X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktreeview.c;h=163b420b4ac7219ac15d8c520c315acfdab3388e;hb=d8d64901c7348e2b092753f5b62b4484fb7a2864;hp=fe30f83d9d5259b09333840f9fa27e2a32cb031a;hpb=3778b86c2e2fe91fc668234f66bf6b88de48c084;p=~andy%2Fgtk diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index fe30f83d9..163b420b4 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -18,12 +18,15 @@ */ +#include +#include +#include + #include "gtktreeview.h" #include "gtkrbtree.h" #include "gtktreednd.h" #include "gtktreeprivate.h" #include "gtkcellrenderer.h" -#include "gtksignal.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkbutton.h" @@ -37,10 +40,6 @@ #include "gtkentry.h" #include "gtktreemodelsort.h" -#include -#include - -#define GTK_TREE_VIEW_SEARCH_DIALOG_KEY "gtk-tree-view-search-dialog" #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5) #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2) #define GTK_TREE_VIEW_NUM_ROWS_PER_IDLE 500 @@ -122,7 +121,8 @@ enum { PROP_REORDERABLE, PROP_RULES_HINT, PROP_ENABLE_SEARCH, - PROP_SEARCH_COLUMN + PROP_SEARCH_COLUMN, + PROP_FIXED_HEIGHT_MODE }; static void gtk_tree_view_class_init (GtkTreeViewClass *klass); @@ -164,6 +164,11 @@ static gboolean gtk_tree_view_button_press (GtkWidget *widget, GdkEventButton *event); static gboolean gtk_tree_view_button_release (GtkWidget *widget, GdkEventButton *event); +#if 0 +static gboolean gtk_tree_view_configure (GtkWidget *widget, + GdkEventConfigure *event); +#endif + static void gtk_tree_view_set_focus_child (GtkContainer *container, GtkWidget *child); static gint gtk_tree_view_focus_out (GtkWidget *widget, @@ -224,16 +229,16 @@ static void gtk_tree_view_set_adjustments (GtkTreeView *tree static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, GtkMovementStep step, gint count); -static void gtk_tree_view_real_select_all (GtkTreeView *tree_view); -static void gtk_tree_view_real_unselect_all (GtkTreeView *tree_view); -static void gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, +static gboolean gtk_tree_view_real_select_all (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_unselect_all (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, gboolean start_editing); -static void gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view); -static void gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, - gboolean logical, - gboolean expand, - gboolean open_all); -static void gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, + gboolean logical, + gboolean expand, + gboolean open_all); +static gboolean gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view); static void gtk_tree_view_row_changed (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, @@ -348,9 +353,17 @@ static void gtk_tree_view_real_set_cursor (GtkTreeView gboolean clear_and_select, gboolean clamp_node); static gboolean gtk_tree_view_has_special_cell (GtkTreeView *tree_view); +static void column_sizing_notify (GObject *object, + GParamSpec *pspec, + gpointer data); +static void gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view, + gboolean enable); + +static gboolean expand_collapse_timeout (gpointer data); +static gboolean do_expand_collapse (GtkTreeView *tree_view); /* interactive search */ -static void gtk_tree_view_search_dialog_destroy (GtkWidget *search_dialog, +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); @@ -402,7 +415,7 @@ static void gtk_tree_view_real_start_editing (GtkTreeView *tree_view, guint flags); static void gtk_tree_view_stop_editing (GtkTreeView *tree_view, gboolean cancel_editing); -static void gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view); +static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view); static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree_view, GtkTreeViewColumn *column, gint drop_position); @@ -416,10 +429,10 @@ static guint tree_view_signals [LAST_SIGNAL] = { 0 }; /* GType Methods */ -GtkType +GType gtk_tree_view_get_type (void) { - static GtkType tree_view_type = 0; + static GType tree_view_type = 0; if (!tree_view_type) { @@ -436,7 +449,9 @@ gtk_tree_view_get_type (void) (GInstanceInitFunc) gtk_tree_view_init }; - tree_view_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTreeView", &tree_view_info, 0); + tree_view_type = + g_type_register_static (GTK_TYPE_CONTAINER, "GtkTreeView", + &tree_view_info, 0); } return tree_view_type; @@ -475,6 +490,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->configure_event = gtk_tree_view_configure;*/ widget_class->motion_notify_event = gtk_tree_view_motion; widget_class->expose_event = gtk_tree_view_expose; widget_class->key_press_event = gtk_tree_view_key_press; @@ -513,85 +529,93 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) g_object_class_install_property (o_class, PROP_MODEL, g_param_spec_object ("model", - _("TreeView Model"), - _("The model for the tree view"), + P_("TreeView Model"), + P_("The model for the tree view"), GTK_TYPE_TREE_MODEL, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_HADJUSTMENT, g_param_spec_object ("hadjustment", - _("Horizontal Adjustment"), - _("Horizontal Adjustment for the widget"), + P_("Horizontal Adjustment"), + P_("Horizontal Adjustment for the widget"), GTK_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_VADJUSTMENT, g_param_spec_object ("vadjustment", - _("Vertical Adjustment"), - _("Vertical Adjustment for the widget"), + P_("Vertical Adjustment"), + P_("Vertical Adjustment for the widget"), GTK_TYPE_ADJUSTMENT, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_HEADERS_VISIBLE, g_param_spec_boolean ("headers_visible", - _("Visible"), - _("Show the column header buttons"), + P_("Visible"), + P_("Show the column header buttons"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_HEADERS_CLICKABLE, g_param_spec_boolean ("headers_clickable", - _("Headers Clickable"), - _("Column headers respond to click events"), + P_("Headers Clickable"), + P_("Column headers respond to click events"), FALSE, G_PARAM_WRITABLE)); g_object_class_install_property (o_class, PROP_EXPANDER_COLUMN, g_param_spec_object ("expander_column", - _("Expander Column"), - _("Set the column for the expander column"), + P_("Expander Column"), + P_("Set the column for the expander column"), GTK_TYPE_TREE_VIEW_COLUMN, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_REORDERABLE, g_param_spec_boolean ("reorderable", - _("Reorderable"), - _("View is reorderable"), + P_("Reorderable"), + P_("View is reorderable"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_RULES_HINT, g_param_spec_boolean ("rules_hint", - _("Rules Hint"), - _("Set a hint to the theme engine to draw rows in alternating colors"), + P_("Rules Hint"), + P_("Set a hint to the theme engine to draw rows in alternating colors"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_ENABLE_SEARCH, g_param_spec_boolean ("enable_search", - _("Enable Search"), - _("View allows user to search through columns interactively"), + P_("Enable Search"), + P_("View allows user to search through columns interactively"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_SEARCH_COLUMN, g_param_spec_int ("search_column", - _("Search Column"), - _("Model column to search through when searching through code"), + P_("Search Column"), + P_("Model column to search through when searching through code"), -1, G_MAXINT, 0, G_PARAM_READWRITE)); + g_object_class_install_property (o_class, + PROP_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)); + /* Style properties */ #define _TREE_VIEW_EXPANDER_SIZE 10 #define _TREE_VIEW_VERTICAL_SEPARATOR 2 @@ -599,8 +623,8 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("expander_size", - _("Expander Size"), - _("Size of the expander arrow."), + P_("Expander Size"), + P_("Size of the expander arrow"), 0, G_MAXINT, _TREE_VIEW_EXPANDER_SIZE, @@ -608,8 +632,8 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("vertical_separator", - _("Vertical Separator Width"), - _("Vertical space between cells. Must be an even number."), + P_("Vertical Separator Width"), + P_("Vertical space between cells. Must be an even number"), 0, G_MAXINT, _TREE_VIEW_VERTICAL_SEPARATOR, @@ -617,8 +641,8 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("horizontal_separator", - _("Horizontal Separator Width"), - _("Horizontal space between cells. Must be an even number."), + P_("Horizontal Separator Width"), + P_("Horizontal space between cells. Must be an even number"), 0, G_MAXINT, _TREE_VIEW_HORIZONTAL_SEPARATOR, @@ -626,178 +650,205 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) gtk_widget_class_install_style_property (widget_class, g_param_spec_boolean ("allow_rules", - _("Allow Rules"), - _("Allow drawing of alternating color rows."), + P_("Allow Rules"), + P_("Allow drawing of alternating color rows"), TRUE, G_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, g_param_spec_boolean ("indent_expanders", - _("Indent Expanders"), - _("Make the expanders indented."), + P_("Indent Expanders"), + P_("Make the expanders indented"), TRUE, G_PARAM_READABLE)); + + gtk_widget_class_install_style_property (widget_class, + 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_widget_class_install_style_property (widget_class, + g_param_spec_boxed ("odd_row_color", + P_("Odd Row Color"), + P_("Color to use for odd rows"), + GDK_TYPE_COLOR, +G_PARAM_READABLE)); + /* Signals */ widget_class->set_scroll_adjustments_signal = - gtk_signal_new ("set_scroll_adjustments", - GTK_RUN_LAST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTreeViewClass, set_scroll_adjustments), - _gtk_marshal_VOID__OBJECT_OBJECT, - GTK_TYPE_NONE, 2, - GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + g_signal_new ("set_scroll_adjustments", + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, set_scroll_adjustments), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_ADJUSTMENT, + GTK_TYPE_ADJUSTMENT); tree_view_signals[ROW_ACTIVATED] = - gtk_signal_new ("row_activated", - GTK_RUN_LAST | GTK_RUN_ACTION, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTreeViewClass, row_activated), - _gtk_marshal_VOID__BOXED_OBJECT, - GTK_TYPE_NONE, 2, - GTK_TYPE_TREE_PATH, - GTK_TYPE_TREE_VIEW_COLUMN); + g_signal_new ("row_activated", + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, row_activated), + NULL, NULL, + _gtk_marshal_VOID__BOXED_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_PATH, + GTK_TYPE_TREE_VIEW_COLUMN); tree_view_signals[TEST_EXPAND_ROW] = g_signal_new ("test_expand_row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row), - _gtk_boolean_handled_accumulator, NULL, - _gtk_marshal_BOOLEAN__BOXED_BOXED, - G_TYPE_BOOLEAN, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__BOXED_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); tree_view_signals[TEST_COLLAPSE_ROW] = g_signal_new ("test_collapse_row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row), - _gtk_boolean_handled_accumulator, NULL, - _gtk_marshal_BOOLEAN__BOXED_BOXED, - G_TYPE_BOOLEAN, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__BOXED_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); tree_view_signals[ROW_EXPANDED] = g_signal_new ("row_expanded", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded), - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - GTK_TYPE_NONE, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded), + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); tree_view_signals[ROW_COLLAPSED] = g_signal_new ("row_collapsed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed), - NULL, NULL, - _gtk_marshal_VOID__BOXED_BOXED, - GTK_TYPE_NONE, 2, - GTK_TYPE_TREE_ITER, - GTK_TYPE_TREE_PATH); + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed), + NULL, NULL, + _gtk_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_TREE_ITER, + GTK_TYPE_TREE_PATH); tree_view_signals[COLUMNS_CHANGED] = g_signal_new ("columns_changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed), - NULL, NULL, - _gtk_marshal_NONE__NONE, - G_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed), + NULL, NULL, + _gtk_marshal_NONE__NONE, + G_TYPE_NONE, 0); tree_view_signals[CURSOR_CHANGED] = g_signal_new ("cursor_changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed), - NULL, NULL, - _gtk_marshal_NONE__NONE, - G_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (o_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed), + NULL, NULL, + _gtk_marshal_NONE__NONE, + G_TYPE_NONE, 0); tree_view_signals[MOVE_CURSOR] = g_signal_new ("move_cursor", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor), - NULL, NULL, - _gtk_marshal_BOOLEAN__ENUM_INT, - GTK_TYPE_BOOL, 2, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT); + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor), + NULL, NULL, + _gtk_marshal_BOOLEAN__ENUM_INT, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_MOVEMENT_STEP, + G_TYPE_INT); tree_view_signals[SELECT_ALL] = g_signal_new ("select_all", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, select_all), - NULL, NULL, - _gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, select_all), + NULL, NULL, + _gtk_marshal_BOOLEAN__NONE, + G_TYPE_BOOLEAN, 0); tree_view_signals[UNSELECT_ALL] = g_signal_new ("unselect_all", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all), NULL, NULL, - _gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); + _gtk_marshal_BOOLEAN__NONE, + G_TYPE_BOOLEAN, 0); tree_view_signals[SELECT_CURSOR_ROW] = g_signal_new ("select_cursor_row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row), - NULL, NULL, - _gtk_marshal_VOID__BOOLEAN, - GTK_TYPE_NONE, 1, + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row), + NULL, NULL, + _gtk_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); tree_view_signals[TOGGLE_CURSOR_ROW] = g_signal_new ("toggle_cursor_row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row), - NULL, NULL, - _gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row), + NULL, NULL, + _gtk_marshal_BOOLEAN__NONE, + G_TYPE_BOOLEAN, 0); tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] = g_signal_new ("expand_collapse_cursor_row", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row), - NULL, NULL, - _gtk_marshal_VOID__BOOLEAN_BOOLEAN_BOOLEAN, - GTK_TYPE_NONE, 3, GTK_TYPE_BOOL, GTK_TYPE_BOOL, GTK_TYPE_BOOL); + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row), + NULL, NULL, + _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN, + G_TYPE_BOOLEAN, 3, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); tree_view_signals[SELECT_CURSOR_PARENT] = g_signal_new ("select_cursor_parent", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent), - NULL, NULL, - _gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent), + NULL, NULL, + _gtk_marshal_BOOLEAN__NONE, + G_TYPE_BOOLEAN, 0); tree_view_signals[START_INTERACTIVE_SEARCH] = g_signal_new ("start_interactive_search", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, - G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search), - NULL, NULL, - _gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search), + NULL, NULL, + _gtk_marshal_BOOLEAN__NONE, + G_TYPE_BOOLEAN, 0); /* Key bindings */ gtk_tree_view_add_move_binding (binding_set, GDK_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1); + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Up, 0, + GTK_MOVEMENT_DISPLAY_LINES, -1); gtk_tree_view_add_move_binding (binding_set, GDK_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1); + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Down, 0, + GTK_MOVEMENT_DISPLAY_LINES, 1); gtk_tree_view_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, -1); @@ -807,39 +858,60 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) gtk_tree_view_add_move_binding (binding_set, GDK_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1); + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Home, 0, + GTK_MOVEMENT_BUFFER_ENDS, -1); gtk_tree_view_add_move_binding (binding_set, GDK_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1); + gtk_tree_view_add_move_binding (binding_set, GDK_KP_End, 0, + GTK_MOVEMENT_BUFFER_ENDS, 1); gtk_tree_view_add_move_binding (binding_set, GDK_Page_Up, 0, GTK_MOVEMENT_PAGES, -1); + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Up, 0, + GTK_MOVEMENT_PAGES, -1); gtk_tree_view_add_move_binding (binding_set, GDK_Page_Down, 0, GTK_MOVEMENT_PAGES, 1); + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Down, 0, + GTK_MOVEMENT_PAGES, 1); + gtk_binding_entry_add_signal (binding_set, GDK_Right, 0, "move_cursor", 2, - GTK_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - GTK_TYPE_INT, 1); + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_Left, 0, "move_cursor", 2, - GTK_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - GTK_TYPE_INT, -1); + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_CONTROL_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - GTK_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0, "move_cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_CONTROL_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - GTK_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0, "move_cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_CONTROL_MASK|GDK_SHIFT_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - GTK_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_CONTROL_MASK, + "move_cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_CONTROL_MASK, + "move_cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); + + gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, + "move_cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_CONTROL_MASK|GDK_SHIFT_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - GTK_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, + "move_cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, "toggle_cursor_row", 0); @@ -850,58 +922,123 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK, "unselect_all", 0); gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_SHIFT_MASK, "select_cursor_row", 1, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE); gtk_binding_entry_add_signal (binding_set, GDK_space, 0, "select_cursor_row", 1, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "select_cursor_row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0, "select_cursor_row", 1, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "select_cursor_row", 1, + G_TYPE_BOOLEAN, TRUE); /* expand and collapse rows */ gtk_binding_entry_add_signal (binding_set, GDK_plus, 0, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, FALSE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE); + + gtk_binding_entry_add_signal (binding_set, GDK_asterisk, 0, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Multiply, 0, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + + gtk_binding_entry_add_signal (binding_set, GDK_slash, 0, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, 0, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); + /* Not doable on US keyboards */ gtk_binding_entry_add_signal (binding_set, GDK_plus, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, 0, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, FALSE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE); gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, TRUE, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_Right, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, TRUE); gtk_binding_entry_add_signal (binding_set, GDK_minus, 0, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); gtk_binding_entry_add_signal (binding_set, GDK_minus, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, 0, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE); gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_SHIFT_MASK, "expand_collapse_cursor_row", 3, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, FALSE, - GTK_TYPE_BOOL, TRUE); + G_TYPE_BOOLEAN, TRUE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_Left, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); + gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, + GDK_CONTROL_MASK | GDK_SHIFT_MASK, + "expand_collapse_cursor_row", 3, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, FALSE, + G_TYPE_BOOLEAN, TRUE); gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, "select_cursor_parent", 0); @@ -918,7 +1055,9 @@ gtk_tree_view_init (GtkTreeView *tree_view) gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE); - tree_view->priv->flags = GTK_TREE_VIEW_IS_LIST | GTK_TREE_VIEW_SHOW_EXPANDERS | GTK_TREE_VIEW_DRAW_KEYFOCUS | GTK_TREE_VIEW_HEADERS_VISIBLE; + tree_view->priv->flags = GTK_TREE_VIEW_SHOW_EXPANDERS + | GTK_TREE_VIEW_DRAW_KEYFOCUS + | GTK_TREE_VIEW_HEADERS_VISIBLE; /* We need some padding */ tree_view->priv->dy = 0; @@ -933,6 +1072,8 @@ gtk_tree_view_init (GtkTreeView *tree_view) tree_view->priv->reorderable = FALSE; tree_view->priv->presize_handler_timer = 0; tree_view->priv->scroll_sync_timer = 0; + tree_view->priv->fixed_height = -1; + tree_view->priv->fixed_height_mode = 0; tree_view->priv->fixed_height_check = 0; gtk_tree_view_set_adjustments (tree_view, NULL, NULL); tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view); @@ -940,6 +1081,8 @@ gtk_tree_view_init (GtkTreeView *tree_view) tree_view->priv->search_column = -1; tree_view->priv->search_dialog_position_func = gtk_tree_view_search_position_func; tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func; + tree_view->priv->init_hadjust_value = TRUE; + tree_view->priv->width = 0; } @@ -989,6 +1132,9 @@ gtk_tree_view_set_property (GObject *object, case PROP_SEARCH_COLUMN: gtk_tree_view_set_search_column (tree_view, g_value_get_int (value)); break; + case PROP_FIXED_HEIGHT_MODE: + gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value)); + break; default: break; } @@ -1033,6 +1179,9 @@ gtk_tree_view_get_property (GObject *object, case PROP_SEARCH_COLUMN: g_value_set_int (value, tree_view->priv->search_column); break; + case PROP_FIXED_HEIGHT_MODE: + g_value_set_boolean (value, tree_view->priv->fixed_height_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1059,7 +1208,6 @@ static void gtk_tree_view_destroy (GtkObject *object) { GtkTreeView *tree_view = GTK_TREE_VIEW (object); - GtkWidget *search_dialog; GList *list; gtk_tree_view_stop_editing (tree_view, TRUE); @@ -1142,13 +1290,14 @@ gtk_tree_view_destroy (GtkObject *object) tree_view->priv->anchor = NULL; /* destroy interactive search dialog */ - search_dialog = gtk_object_get_data (GTK_OBJECT (tree_view), - GTK_TREE_VIEW_SEARCH_DIALOG_KEY); - if (search_dialog) - gtk_tree_view_search_dialog_destroy (search_dialog, - tree_view); + if (tree_view->priv->search_window) + { + gtk_widget_destroy (tree_view->priv->search_window); + tree_view->priv->search_window = NULL; + tree_view->priv->search_entry = NULL; + } - if (tree_view->priv->search_user_data) + if (tree_view->priv->search_destroy) { (* tree_view->priv->search_destroy) (tree_view->priv->search_user_data); tree_view->priv->search_user_data = NULL; @@ -1338,37 +1487,37 @@ gtk_tree_view_unrealize (GtkWidget *widget) if (tree_view->priv->scroll_timeout != 0) { - gtk_timeout_remove (tree_view->priv->scroll_timeout); + g_source_remove (tree_view->priv->scroll_timeout); tree_view->priv->scroll_timeout = 0; } if (tree_view->priv->open_dest_timeout != 0) { - gtk_timeout_remove (tree_view->priv->open_dest_timeout); + g_source_remove (tree_view->priv->open_dest_timeout); tree_view->priv->open_dest_timeout = 0; } if (tree_view->priv->expand_collapse_timeout != 0) { - gtk_timeout_remove (tree_view->priv->expand_collapse_timeout); + g_source_remove (tree_view->priv->expand_collapse_timeout); tree_view->priv->expand_collapse_timeout = 0; } if (tree_view->priv->presize_handler_timer != 0) { - gtk_timeout_remove (tree_view->priv->presize_handler_timer); + g_source_remove (tree_view->priv->presize_handler_timer); tree_view->priv->presize_handler_timer = 0; } if (tree_view->priv->validate_rows_timer != 0) { - gtk_timeout_remove (tree_view->priv->validate_rows_timer); + g_source_remove (tree_view->priv->validate_rows_timer); tree_view->priv->validate_rows_timer = 0; } if (tree_view->priv->scroll_sync_timer != 0) { - gtk_timeout_remove (tree_view->priv->scroll_sync_timer); + g_source_remove (tree_view->priv->scroll_sync_timer); tree_view->priv->scroll_sync_timer = 0; } @@ -1430,6 +1579,7 @@ gtk_tree_view_size_request_columns (GtkTreeView *tree_view) } +/* Called only by ::size_request */ static void gtk_tree_view_update_size (GtkTreeView *tree_view) { @@ -1440,10 +1590,12 @@ gtk_tree_view_update_size (GtkTreeView *tree_view) if (tree_view->priv->model == NULL) { tree_view->priv->width = 0; + tree_view->priv->prev_width = 0; tree_view->priv->height = 0; return; } + tree_view->priv->prev_width = tree_view->priv->width; tree_view->priv->width = 0; /* keep this in sync with size_allocate below */ for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++) @@ -1495,8 +1647,10 @@ gtk_tree_view_size_request (GtkWidget *widget, tree_view = GTK_TREE_VIEW (widget); - /* we validate 50 rows initially just to make sure we have some size */ - /* in practice, with a lot of static lists, this should get a good width */ + /* we validate GTK_TREE_VIEW_NUM_ROWS_PER_IDLE rows initially just to make + * sure we have some size. In practice, with a lot of static lists, this + * should get a good width. + */ validate_rows (tree_view); gtk_tree_view_size_request_columns (tree_view); gtk_tree_view_update_size (GTK_TREE_VIEW (widget)); @@ -1518,16 +1672,109 @@ gtk_tree_view_size_request (GtkWidget *widget, } } + +static void +invalidate_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + gint column_offset = 0; + GList *list; + GtkWidget *widget = GTK_WIDGET (tree_view); + gboolean rtl; + + if (!GTK_WIDGET_REALIZED (widget)) + return; + + 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)) + { + GtkTreeViewColumn *tmpcolumn = list->data; + if (tmpcolumn == column) + { + GdkRectangle invalid_rect; + + invalid_rect.x = column_offset; + invalid_rect.y = 0; + invalid_rect.width = column->width; + invalid_rect.height = widget->allocation.height; + + gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE); + break; + } + + column_offset += tmpcolumn->width; + } +} + +static void +invalidate_last_column (GtkTreeView *tree_view) +{ + GList *last_column; + gboolean rtl; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + + for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns)); + last_column; + last_column = (rtl ? last_column->next : last_column->prev)) + { + if (GTK_TREE_VIEW_COLUMN (last_column->data)->visible) + { + invalidate_column (tree_view, last_column->data); + return; + } + } +} + +static gint +gtk_tree_view_get_real_requested_width_from_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + gint real_requested_width; + + if (column->use_resized_width) + { + real_requested_width = column->resized_width; + } + else if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) + { + real_requested_width = column->fixed_width; + } + else if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) + { + real_requested_width = MAX (column->requested_width, column->button_request); + } + else + { + real_requested_width = column->requested_width; + if (real_requested_width < 0) + real_requested_width = 0; + } + + if (column->min_width != -1) + real_requested_width = MAX (real_requested_width, column->min_width); + if (column->max_width != -1) + real_requested_width = MIN (real_requested_width, column->max_width); + + return real_requested_width; +} + /* GtkWidget::size_allocate helper */ static void gtk_tree_view_size_allocate_columns (GtkWidget *widget) { GtkTreeView *tree_view; - GList *list, *last_column; + GList *list, *first_column, *last_column; GtkTreeViewColumn *column; GtkAllocation allocation; gint width = 0; - + gint extra, extra_per_column; + gint full_requested_width = 0; + gint number_of_expand_columns = 0; + gboolean rtl; + tree_view = GTK_TREE_VIEW (widget); for (last_column = g_list_last (tree_view->priv->columns); @@ -1537,13 +1784,46 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget) if (last_column == NULL) return; + for (first_column = g_list_first (tree_view->priv->columns); + first_column && !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible); + first_column = first_column->next) + ; + allocation.y = 0; allocation.height = tree_view->priv->header_height; + rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + /* find out how many extra space and expandable columns we have */ for (list = tree_view->priv->columns; list != last_column->next; list = list->next) + { + column = (GtkTreeViewColumn *)list->data; + + if (!column->visible) + continue; + + full_requested_width += gtk_tree_view_get_real_requested_width_from_column (tree_view, column); + + if (column->expand) + number_of_expand_columns++; + } + + extra = MAX (widget->allocation.width - full_requested_width, 0); + if (number_of_expand_columns > 0) + extra_per_column = extra/number_of_expand_columns; + else + extra_per_column = 0; + + for (list = (rtl ? last_column : first_column); + list != (rtl ? first_column->prev : last_column->next); + list = (rtl ? list->prev : list->next)) { gint real_requested_width = 0; + gint old_width; + column = list->data; + old_width = column->width; + if (!column->visible) continue; @@ -1552,90 +1832,61 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget) if (column == tree_view->priv->drag_column) { GtkAllocation drag_allocation; - gdk_window_get_size (tree_view->priv->drag_window, - &(drag_allocation.width), &(drag_allocation.height)); + gdk_drawable_get_size (tree_view->priv->drag_window, + &(drag_allocation.width), + &(drag_allocation.height)); drag_allocation.x = 0; drag_allocation.y = 0; - gtk_widget_size_allocate (tree_view->priv->drag_column->button, &drag_allocation); + gtk_widget_size_allocate (tree_view->priv->drag_column->button, + &drag_allocation); width += drag_allocation.width; continue; } - if (column->use_resized_width) - { - real_requested_width = column->resized_width; - } - else if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) - { - real_requested_width = column->fixed_width; - } - else if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) - { - real_requested_width = MAX (column->requested_width, column->button_request); - } - else - { - real_requested_width = column->requested_width; - if (real_requested_width < 0) - real_requested_width = 0; - } - - if (column->min_width != -1) - real_requested_width = MAX (real_requested_width, column->min_width); - if (column->max_width != -1) - real_requested_width = MIN (real_requested_width, column->max_width); + real_requested_width = gtk_tree_view_get_real_requested_width_from_column (tree_view, column); allocation.x = width; column->width = real_requested_width; - if (list == last_column && - width + real_requested_width < widget->allocation.width) + + if (column->expand) { - column->width += (widget->allocation.width - column->width - width); + if (number_of_expand_columns == 1) + { + /* We add the remander to the last column as + * */ + column->width += extra; + } + else + { + column->width += extra_per_column; + extra -= extra_per_column; + number_of_expand_columns --; + } + } + else if (number_of_expand_columns == 0 && + list == last_column) + { + column->width += extra; } + g_object_notify (G_OBJECT (column), "width"); + allocation.width = column->width; width += column->width; + + if (column->width > old_width) + invalidate_column (tree_view, column); + gtk_widget_size_allocate (column->button, &allocation); + if (column->window) gdk_window_move_resize (column->window, - allocation.x + allocation.width - TREE_VIEW_DRAG_WIDTH/2, + allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2, allocation.y, TREE_VIEW_DRAG_WIDTH, allocation.height); } } -static void -invalidate_last_column (GtkTreeView *tree_view) -{ - GList *list, *last_column; - gint last_column_x; - GtkWidget *widget = GTK_WIDGET (tree_view); - - for (last_column = g_list_last (tree_view->priv->columns); - last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible); - last_column = last_column->prev) - ; - - last_column_x = 0; - for (list = tree_view->priv->columns; list; list = list->next) - { - GtkTreeViewColumn *column = list->data; - if (list == last_column) - { - GdkRectangle invalid_rect; - - invalid_rect.x = last_column_x; - invalid_rect.y = 0; - invalid_rect.width = column->width; - invalid_rect.height = widget->allocation.height; - - gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE); - break; - } - - last_column_x += column->width; - } -} static void gtk_tree_view_size_allocate (GtkWidget *widget, @@ -1644,6 +1895,8 @@ 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)); @@ -1678,8 +1931,30 @@ gtk_tree_view_size_allocate (GtkWidget *widget, tree_view->priv->hadjustment->lower = 0; tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width); - if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width) - tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0); + if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) + { + if (allocation->width < tree_view->priv->width) + { + if (tree_view->priv->init_hadjust_value) + { + tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0); + tree_view->priv->init_hadjust_value = FALSE; + } + else if(allocation->width != old_width) + tree_view->priv->hadjustment->value = CLAMP(tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width); + else + tree_view->priv->hadjustment->value = CLAMP(tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width); + } + else + { + tree_view->priv->hadjustment->value = 0; + tree_view->priv->init_hadjust_value = TRUE; + } + } + else + if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width) + tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0); + gtk_adjustment_changed (tree_view->priv->hadjustment); tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view); @@ -1689,8 +1964,14 @@ gtk_tree_view_size_allocate (GtkWidget *widget, 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) - gtk_adjustment_set_value (tree_view->priv->vadjustment, - MAX (tree_view->priv->height - tree_view->priv->vadjustment->page_size, 0)); + { + 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); if (GTK_WIDGET_REALIZED (widget)) @@ -1712,8 +1993,31 @@ gtk_tree_view_size_allocate (GtkWidget *widget, gtk_tree_view_size_allocate_columns (widget); - if (GTK_WIDGET_REALIZED (widget) && width_changed) - invalidate_last_column (tree_view); + if (GTK_WIDGET_REALIZED (widget)) + { + gboolean has_expand_column = FALSE; + for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) + { + if (gtk_tree_view_column_get_expand (GTK_TREE_VIEW_COLUMN (tmp_list->data))) + { + has_expand_column = TRUE; + break; + } + } + + /* This little hack only works if we have an LTR locale, and no column has the */ + if (width_changed) + { + if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR && + ! has_expand_column) + invalidate_last_column (tree_view); + else + gtk_widget_queue_draw (widget); + } + + if (dy_changed) + gtk_widget_queue_draw (widget); + } } static gboolean @@ -1728,17 +2032,28 @@ gtk_tree_view_button_press (GtkWidget *widget, GdkRectangle cell_area; gint vertical_separator; gint horizontal_separator; + gboolean rtl; 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); + 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, NULL); + + /* Because grab_focus can cause reentrancy, we delay grab_focus until after + * we're done handling the button press. + */ + if (event->window == tree_view->priv->bin_window && + tree_view->priv->tree != NULL) + { + } + if (event->window == tree_view->priv->bin_window && tree_view->priv->tree != NULL) { @@ -1754,11 +2069,9 @@ gtk_tree_view_button_press (GtkWidget *widget, GtkTreeViewColumn *column = NULL; GtkCellRenderer *focus_cell = NULL; gint column_handled_click = FALSE; - gboolean emit_row_activated = FALSE; - - if (!GTK_WIDGET_HAS_FOCUS (widget)) - gtk_widget_grab_focus (widget); - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + gboolean row_double_click = FALSE; + gboolean rtl; + GtkWidget *grab_widget; /* are we in an arrow? */ if (tree_view->priv->prelight_node && @@ -1770,11 +2083,14 @@ gtk_tree_view_button_press (GtkWidget *widget, tree_view->priv->button_pressed_node = tree_view->priv->prelight_node; tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree; gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), - tree_view->priv->prelight_tree, + tree_view->priv->prelight_tree, tree_view->priv->prelight_node, event->x, event->y); } + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); return TRUE; } @@ -1785,8 +2101,13 @@ gtk_tree_view_button_press (GtkWidget *widget, y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node); if (node == NULL) - /* We clicked in dead space */ - return TRUE; + { + /* We clicked in dead space */ + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + return TRUE; + } /* Get the path and the node */ path = _gtk_tree_view_find_path (tree_view, tree, node); @@ -1796,8 +2117,11 @@ gtk_tree_view_button_press (GtkWidget *widget, tree_view->priv->expander_size); background_area.x = 0; + /* Let the column have a chance at selecting it. */ - 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)) { column = list->data; @@ -1828,10 +2152,18 @@ gtk_tree_view_button_press (GtkWidget *widget, } if (column == NULL) - return FALSE; + { + gtk_tree_path_free (path); + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + return FALSE; + } + + tree_view->priv->focus_column = column; /* decide if we edit */ - if (event->type == GDK_BUTTON_PRESS && + if (event->type == GDK_BUTTON_PRESS && event->button == 1 && !(event->state & gtk_accelerator_get_default_mod_mask ())) { GtkTreePath *anchor; @@ -1885,6 +2217,7 @@ gtk_tree_view_button_press (GtkWidget *widget, &area, (GdkEvent *)event, flags); + g_free (path_string); gtk_tree_path_free (path); gtk_tree_path_free (anchor); return TRUE; @@ -1900,25 +2233,37 @@ gtk_tree_view_button_press (GtkWidget *widget, /* select */ pre_val = tree_view->priv->vadjustment->value; - tree_view->priv->focus_column = column; - focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, event->x - background_area.x); - if (focus_cell) - gtk_tree_view_column_focus_cell (column, focus_cell); + /* we only handle selection modifications on the first button press + */ + if (event->type == GDK_BUTTON_PRESS) + { + if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + tree_view->priv->ctrl_pressed = TRUE; + if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) + tree_view->priv->shift_pressed = TRUE; - if (event->state & GDK_CONTROL_MASK) - { - gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE); - gtk_tree_view_real_toggle_cursor_row (tree_view); - } - else if (event->state & GDK_SHIFT_MASK) - { - gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE); - gtk_tree_view_real_select_cursor_row (tree_view, FALSE); - } - else - { - gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE); - } + focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, event->x - background_area.x); + if (focus_cell) + gtk_tree_view_column_focus_cell (column, focus_cell); + + if (event->state & GDK_CONTROL_MASK) + { + gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE); + gtk_tree_view_real_toggle_cursor_row (tree_view); + } + else if (event->state & GDK_SHIFT_MASK) + { + gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE); + gtk_tree_view_real_select_cursor_row (tree_view, FALSE); + } + else + { + gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE); + } + + tree_view->priv->ctrl_pressed = FALSE; + tree_view->priv->shift_pressed = FALSE; + } /* the treeview may have been scrolled because of _set_cursor, * correct here @@ -1932,7 +2277,9 @@ gtk_tree_view_button_press (GtkWidget *widget, /* Save press to possibly begin a drag */ - if (!column_handled_click && + grab_widget = gtk_grab_get_current (); + if ((grab_widget == NULL || grab_widget == widget) && + !column_handled_click && tree_view->priv->pressed_button < 0) { tree_view->priv->pressed_button = event->button; @@ -1940,45 +2287,66 @@ gtk_tree_view_button_press (GtkWidget *widget, tree_view->priv->press_start_y = event->y; } - if (event->button == 1 && event->type == GDK_2BUTTON_PRESS && - tree_view->priv->last_button_press) - { - GtkTreePath *lsc; - - lsc = gtk_tree_row_reference_get_path (tree_view->priv->last_button_press); + /* Test if a double click happened on the same row. */ + if (event->button == 1) + { + /* We also handle triple clicks here, because a user could have done + * a first click and a second double click on different rows. + */ + if ((event->type == GDK_2BUTTON_PRESS + || event->type == GDK_3BUTTON_PRESS) + && tree_view->priv->last_button_press) + { + GtkTreePath *lsc; - if (tree_view->priv->last_button_press) - gtk_tree_row_reference_free (tree_view->priv->last_button_press); - if (tree_view->priv->last_button_press_2) - gtk_tree_row_reference_free (tree_view->priv->last_button_press_2); - tree_view->priv->last_button_press = NULL; - tree_view->priv->last_button_press_2 = NULL; + lsc = gtk_tree_row_reference_get_path (tree_view->priv->last_button_press); - if (lsc) - { - if (!gtk_tree_path_compare (lsc, path)) - emit_row_activated = TRUE; - gtk_tree_path_free (lsc); - } - } - else if (event->button == 1 && event->type == GDK_BUTTON_PRESS) - { - if (tree_view->priv->last_button_press) - gtk_tree_row_reference_free (tree_view->priv->last_button_press); - tree_view->priv->last_button_press = tree_view->priv->last_button_press_2; - tree_view->priv->last_button_press_2 = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); - } + if (lsc) + { + row_double_click = !gtk_tree_path_compare (lsc, path); + gtk_tree_path_free (lsc); + } + } - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + if (row_double_click) + { + if (tree_view->priv->last_button_press) + gtk_tree_row_reference_free (tree_view->priv->last_button_press); + if (tree_view->priv->last_button_press_2) + gtk_tree_row_reference_free (tree_view->priv->last_button_press_2); + tree_view->priv->last_button_press = NULL; + tree_view->priv->last_button_press_2 = NULL; + } + else + { + if (tree_view->priv->last_button_press) + gtk_tree_row_reference_free (tree_view->priv->last_button_press); + tree_view->priv->last_button_press = tree_view->priv->last_button_press_2; + tree_view->priv->last_button_press_2 = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); + } + } - if (emit_row_activated) + if (row_double_click) { gtk_grab_remove (widget); gtk_tree_view_row_activated (tree_view, path, column); + + if (tree_view->priv->pressed_button == event->button) + tree_view->priv->pressed_button = -1; } gtk_tree_path_free (path); + /* If we activated the row we don't want to grab focus back, as moving + * focus to another widget is pretty common. + */ + if (!row_double_click) + { + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + } + return TRUE; } @@ -1997,7 +2365,7 @@ gtk_tree_view_button_press (GtkWidget *widget, gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE) { _gtk_tree_view_column_autosize (tree_view, column); - break; + return TRUE; } if (gdk_pointer_grab (column->window, FALSE, @@ -2013,19 +2381,22 @@ gtk_tree_view_button_press (GtkWidget *widget, column->use_resized_width = TRUE; /* block attached dnd signal handler */ - drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); + drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); if (drag_data) - gtk_signal_handler_block_by_data (GTK_OBJECT (widget), drag_data); + g_signal_handlers_block_matched (widget, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + drag_data); if (!GTK_WIDGET_HAS_FOCUS (widget)) gtk_widget_grab_focus (widget); tree_view->priv->drag_pos = i; - tree_view->priv->x_drag = (column->button->allocation.x + column->button->allocation.width); - break; + tree_view->priv->x_drag = column->button->allocation.x + (rtl ? 0 : column->button->allocation.width); + return TRUE; } } - return TRUE; + return FALSE; } /* GtkWidget::button_release_event helper */ @@ -2034,9 +2405,11 @@ gtk_tree_view_button_release_drag_column (GtkWidget *widget, GdkEventButton *event) { GtkTreeView *tree_view; + gboolean rtl; tree_view = GTK_TREE_VIEW (widget); + rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME); gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME); @@ -2057,10 +2430,20 @@ gtk_tree_view_button_release_drag_column (GtkWidget *widget, gtk_widget_grab_focus (tree_view->priv->drag_column->button); - if (tree_view->priv->cur_reorder && - tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column) - gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column, - tree_view->priv->cur_reorder->left_column); + if (rtl) + { + if (tree_view->priv->cur_reorder && + tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column) + gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column, + tree_view->priv->cur_reorder->right_column); + } + else + { + if (tree_view->priv->cur_reorder && + tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column) + gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column, + tree_view->priv->cur_reorder->left_column); + } tree_view->priv->drag_column = NULL; gdk_window_hide (tree_view->priv->drag_window); @@ -2093,9 +2476,12 @@ gtk_tree_view_button_release_column_resize (GtkWidget *widget, tree_view->priv->drag_pos = -1; /* unblock attached dnd signal handler */ - drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); + drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); if (drag_data) - gtk_signal_handler_unblock_by_data (GTK_OBJECT (widget), drag_data); + g_signal_handlers_unblock_matched (widget, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + drag_data); GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); gtk_widget_get_pointer (widget, &x, NULL); @@ -2159,6 +2545,19 @@ gtk_tree_view_button_release (GtkWidget *widget, return TRUE; } +#if 0 +static gboolean +gtk_tree_view_configure (GtkWidget *widget, + GdkEventConfigure *event) +{ + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (widget); + tree_view->priv->search_dialog_position_func (tree_view, tree_view->priv->search_window); + + return FALSE; +} +#endif /* GtkWidget::motion_event function set. */ @@ -2195,61 +2594,98 @@ coords_are_over_arrow (GtkTreeView *tree_view, } static void -do_unprelight (GtkTreeView *tree_view, - /* these are in tree window coords */ - gint x, - gint y) +do_prelight (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node, + /* these are in tree_window coords */ + gint x, + gint y) { - if (tree_view->priv->prelight_node == NULL) - return; + if (tree_view->priv->prelight_tree == tree && + tree_view->priv->prelight_node == node) + { + /* We are still on the same node, + but we might need to take care of the arrow */ + + if (tree && node) + { + gboolean over_arrow; + gboolean flag_set; + + over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y); + flag_set = GTK_TREE_VIEW_FLAG_SET (tree_view, + GTK_TREE_VIEW_ARROW_PRELIT); + + if (over_arrow != flag_set) + { + if (over_arrow) + GTK_TREE_VIEW_SET_FLAG (tree_view, + GTK_TREE_VIEW_ARROW_PRELIT); + else + GTK_TREE_VIEW_UNSET_FLAG (tree_view, + GTK_TREE_VIEW_ARROW_PRELIT); + + gtk_tree_view_draw_arrow (tree_view, tree, node, x, y); + } + } - GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); + return; + } - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT) && - !coords_are_over_arrow (tree_view, - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - x, - y)) - /* We need to unprelight the old arrow. */ + if (tree_view->priv->prelight_tree && tree_view->priv->prelight_node) { - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); + /* Unprelight the old node and arrow */ - gtk_tree_view_draw_arrow (tree_view, - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - x, - y); + GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, + GTK_RBNODE_IS_PRELIT); + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) + { + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); + + gtk_tree_view_draw_arrow (tree_view, + tree_view->priv->prelight_tree, + tree_view->priv->prelight_node, + x, + y); + } + + _gtk_tree_view_queue_draw_node (tree_view, + tree_view->priv->prelight_tree, + tree_view->priv->prelight_node, + NULL); } - tree_view->priv->prelight_node = NULL; - tree_view->priv->prelight_tree = NULL; -} -static void -do_prelight (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkRBNode *node, - /* these are in tree_window coords */ - gint x, - gint y) -{ + /* Set the new prelight values */ + + tree_view->priv->prelight_node = node; + tree_view->priv->prelight_tree = tree; + + if (!node || !tree) + return; + + /* Prelight the new node and arrow */ + if (coords_are_over_arrow (tree_view, tree, node, x, y)) { GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - } - tree_view->priv->prelight_node = node; - tree_view->priv->prelight_tree = tree; + gtk_tree_view_draw_arrow (tree_view, tree, node, x, y); + } GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT); + + _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL); } static void ensure_unprelighted (GtkTreeView *tree_view) { - do_unprelight (tree_view, -1000, -1000); /* coords not possibly over an arrow */ + do_prelight (tree_view, + NULL, NULL, + -1000, -1000); /* coords not possibly over an arrow */ + g_assert (tree_view->priv->prelight_node == NULL); } @@ -2351,11 +2787,11 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view) col.pixel = 0; gdk_gc_set_foreground(gc, &col); gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4); - gdk_gc_destroy (gc); + g_object_unref (gc); gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window, mask, 0, 0); - if (mask) gdk_pixmap_unref (mask); + if (mask) g_object_unref (mask); tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL; } } @@ -2423,10 +2859,10 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view) else j--; } - gdk_gc_destroy (gc); + g_object_unref (gc); gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window, mask, 0, 0); - if (mask) gdk_pixmap_unref (mask); + if (mask) g_object_unref (mask); } tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW; @@ -2499,10 +2935,10 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view) gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j); j--; } - gdk_gc_destroy (gc); + g_object_unref (gc); gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window, mask, 0, 0); - if (mask) gdk_pixmap_unref (mask); + if (mask) g_object_unref (mask); } tree_view->priv->drag_column_window_state = arrow_type; @@ -2559,7 +2995,6 @@ gtk_tree_view_update_current_reorder (GtkTreeView *tree_view) gint mouse_x; gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL); - for (list = tree_view->priv->column_drag_info; list; list = list->next) { reorder = (GtkTreeViewColumnReorder *) list->data; @@ -2650,7 +3085,7 @@ gtk_tree_view_motion_drag_column (GtkWidget *widget, x = CLAMP (x + (gint)event->x - column->drag_x, 0, MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->button->allocation.width); gdk_window_move (tree_view->priv->drag_window, x, y); - + /* autoscroll, if needed */ gtk_tree_view_horizontal_autoscroll (tree_view); /* Update the current reorder position and arrow; */ @@ -2667,65 +3102,29 @@ gtk_tree_view_motion_bin_window (GtkWidget *widget, GtkRBTree *tree; GtkRBNode *node; gint new_y; - GtkRBTree *old_prelight_tree; - GtkRBNode *old_prelight_node; - gboolean old_arrow_prelit; tree_view = (GtkTreeView *) widget; if (tree_view->priv->tree == NULL) return FALSE; - gtk_tree_view_maybe_begin_dragging_row (tree_view, event); - - old_prelight_tree = tree_view->priv->prelight_tree; - old_prelight_node = tree_view->priv->prelight_node; - old_arrow_prelit = GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); + /* only check for an initiated drag when a button is pressed */ + if (tree_view->priv->pressed_button >= 0) + gtk_tree_view_maybe_begin_dragging_row (tree_view, event); new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y); if (new_y < 0) new_y = 0; - do_unprelight (tree_view, event->x, event->y); - _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node); - if (tree == NULL) - return TRUE; + _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node); /* If we are currently pressing down a button, we don't want to prelight anything else. */ if ((tree_view->priv->button_pressed_node != NULL) && (tree_view->priv->button_pressed_node != node)) - return TRUE; - + node = NULL; do_prelight (tree_view, tree, node, event->x, event->y); - if (old_prelight_node != tree_view->priv->prelight_node) - { - if (old_prelight_node) - { - _gtk_tree_view_queue_draw_node (tree_view, - old_prelight_tree, - old_prelight_node, - NULL); - } - if (tree_view->priv->prelight_node) - { - _gtk_tree_view_queue_draw_node (tree_view, - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - NULL); - } - } - else if (old_arrow_prelit != GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) - { - if (tree_view->priv->prelight_node) - { - _gtk_tree_view_queue_draw_node (tree_view, - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - NULL); - } - } return TRUE; } @@ -2789,9 +3188,13 @@ gtk_tree_view_bin_expose (GtkWidget *widget, gint focus_line_width; gboolean allow_rules; gboolean has_special_cell; + gboolean rtl; + gint n_visible_columns; g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + tree_view = GTK_TREE_VIEW (widget); gtk_widget_style_get (widget, @@ -2849,11 +3252,21 @@ gtk_tree_view_bin_expose (GtkWidget *widget, gdk_drawable_get_size (tree_view->priv->bin_window, &bin_window_width, NULL); - for (last_column = g_list_last (tree_view->priv->columns); + + n_visible_columns = 0; + for (list = tree_view->priv->columns; list; list = list->next) + { + if (! GTK_TREE_VIEW_COLUMN (list->data)->visible) + continue; + n_visible_columns ++; + } + + /* Find the last column */ + for (last_column = rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns); last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); - last_column = last_column->prev) + last_column = rtl ? last_column->next : last_column->prev) ; /* Actually process the expose event. To do this, we want to @@ -2886,7 +3299,9 @@ gtk_tree_view_bin_expose (GtkWidget *widget, has_special_cell = gtk_tree_view_has_special_cell (tree_view); - for (list = tree_view->priv->columns; list; list = list->next) + 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; const gchar *detail = NULL; @@ -2903,10 +3318,15 @@ gtk_tree_view_bin_expose (GtkWidget *widget, } if (column->show_sort_indicator) - flags |= GTK_CELL_RENDERER_SORTED; + flags |= GTK_CELL_RENDERER_SORTED; else flags &= ~GTK_CELL_RENDERER_SORTED; + if (cursor == node) + flags |= GTK_CELL_RENDERER_FOCUSED; + else + flags &= ~GTK_CELL_RENDERER_FOCUSED; + background_area.x = cell_offset; background_area.width = column->width; @@ -2934,7 +3354,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, */ if (allow_rules && tree_view->priv->has_rules) { - if (flags & GTK_CELL_RENDERER_SORTED) + if ((flags & GTK_CELL_RENDERER_SORTED) && + n_visible_columns >= 3) { if (parity) detail = "cell_odd_ruled_sorted"; @@ -2951,7 +3372,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, } else { - if (flags & GTK_CELL_RENDERER_SORTED) + if ((flags & GTK_CELL_RENDERER_SORTED) && + n_visible_columns >= 3) { if (parity) detail = "cell_odd_sorted"; @@ -2990,7 +3412,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, if (gtk_tree_view_is_expander_column (tree_view, column) && TREE_VIEW_DRAW_EXPANDERS(tree_view)) { - cell_area.x += depth * tree_view->priv->expander_size; + if (!rtl) + cell_area.x += depth * tree_view->priv->expander_size; cell_area.width -= depth * tree_view->priv->expander_size; /* If we have an expander column, the highlight underline @@ -3447,6 +3870,9 @@ gtk_tree_view_key_press (GtkWidget *widget, { GtkTreeView *tree_view = (GtkTreeView *) widget; GList *list; + gboolean rtl; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG)) { @@ -3458,9 +3884,9 @@ gtk_tree_view_key_press (GtkWidget *widget, return TRUE; } - /* FIXME: this is prolly broken when we go bidi */ if (tree_view->priv->columns && (event->state & GDK_SHIFT_MASK) - && (event->keyval == GDK_Left || event->keyval == GDK_Right)) + && (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) @@ -3471,7 +3897,7 @@ gtk_tree_view_key_press (GtkWidget *widget, if (!column->resizable) return TRUE; - if (event->keyval == GDK_Left) + if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) { column->resized_width = MAX (column->resized_width, column->width); @@ -3491,7 +3917,8 @@ gtk_tree_view_key_press (GtkWidget *widget, gtk_widget_queue_resize (widget); return TRUE; } - else if (event->keyval == GDK_Right) + else if (event->keyval == GDK_Right + || event->keyval == GDK_KP_Right) { column->resized_width = MAX (column->resized_width, column->width); @@ -3509,10 +3936,11 @@ gtk_tree_view_key_press (GtkWidget *widget, } } - /* FIXME: broken when we go bidi? */ if (tree_view->priv->columns && (event->state & GDK_CONTROL_MASK) && - (event->keyval == GDK_Left || event->keyval == GDK_Right - || event->keyval == GDK_Home || event->keyval == GDK_End)) + (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 + || event->keyval == GDK_End || event->keyval == GDK_KP_End)) { list = tree_view->priv->columns; while (list) @@ -3520,7 +3948,8 @@ gtk_tree_view_key_press (GtkWidget *widget, GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data); if (GTK_WIDGET_HAS_FOCUS (column->button)) { - if (event->keyval == GDK_Left) + if (event->keyval == (rtl ? GDK_Right : GDK_Left) + || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left)) { GtkTreeViewColumn *col; col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT); @@ -3528,7 +3957,8 @@ gtk_tree_view_key_press (GtkWidget *widget, gtk_tree_view_move_column_after (tree_view, column, col); return TRUE; } - else if (event->keyval == GDK_Right) + else if (event->keyval == (rtl ? GDK_Left : GDK_Right) + || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right)) { GtkTreeViewColumn *col; col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT); @@ -3536,7 +3966,8 @@ gtk_tree_view_key_press (GtkWidget *widget, gtk_tree_view_move_column_after (tree_view, column, col); return TRUE; } - else if (event->keyval == GDK_Home) + else if (event->keyval == GDK_Home + || event->keyval == GDK_KP_Home) { GtkTreeViewColumn *col; col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME); @@ -3544,7 +3975,7 @@ gtk_tree_view_key_press (GtkWidget *widget, gtk_tree_view_move_column_after (tree_view, column, col); return TRUE; } - else if (event->keyval == GDK_End) + else if (event->keyval == GDK_End || event->keyval == GDK_KP_End) { GtkTreeViewColumn *col; col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END); @@ -3557,9 +3988,10 @@ gtk_tree_view_key_press (GtkWidget *widget, } } - /* FIXME: this is prolly broken when we go bidi */ if (tree_view->priv->columns && - (event->keyval == GDK_Left || event->keyval == GDK_Right)) + GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) && + (event->keyval == GDK_Left || event->keyval == GDK_KP_Left + || event->keyval == GDK_Right || event->keyval == GDK_KP_Right)) { gint width = 0; list = tree_view->priv->columns; @@ -3568,7 +4000,9 @@ gtk_tree_view_key_press (GtkWidget *widget, GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data); if (GTK_WIDGET_HAS_FOCUS (column->button)) { - if (event->keyval == GDK_Left && list->prev) + if ((event->keyval == (rtl ? GDK_Right : GDK_Left) + || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left)) + && list->prev) { GList *tmp; @@ -3585,7 +4019,9 @@ gtk_tree_view_key_press (GtkWidget *widget, gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (width, tree_view->priv->hadjustment->lower, tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size)); return TRUE; } - else if (event->keyval == GDK_Right && list->next) + else if ((event->keyval == (rtl ? GDK_Left : GDK_Right) + || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right)) + && list->next) { GList *tmp; @@ -3609,7 +4045,10 @@ gtk_tree_view_key_press (GtkWidget *widget, } } - return (* GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget, event); + if ((* GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget, event)) + return TRUE; + + return FALSE; } /* FIXME Is this function necessary? Can I get an enter_notify event @@ -3645,17 +4084,8 @@ gtk_tree_view_enter_notify (GtkWidget *widget, new_y = 0; _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node); - if (node == NULL) - return FALSE; - do_prelight (tree_view, tree, node, event->x, event->y); - if (tree_view->priv->prelight_node) - _gtk_tree_view_queue_draw_node (tree_view, - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - NULL); - return TRUE; } @@ -3679,7 +4109,7 @@ gtk_tree_view_leave_notify (GtkWidget *widget, ensure_unprelighted (tree_view); -return TRUE; + return TRUE; } @@ -3687,15 +4117,15 @@ static gint gtk_tree_view_focus_out (GtkWidget *widget, GdkEventFocus *event) { - GtkWidget *search_dialog; + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (widget); gtk_widget_queue_draw (widget); /* destroy interactive search dialog */ - search_dialog = gtk_object_get_data (GTK_OBJECT (widget), - GTK_TREE_VIEW_SEARCH_DIALOG_KEY); - if (search_dialog) - gtk_tree_view_search_dialog_destroy (search_dialog, GTK_TREE_VIEW (widget)); + if (tree_view->priv->search_window) + gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view); return FALSE; } @@ -3748,6 +4178,7 @@ validate_row (GtkTreeView *tree_view, gtk_tree_view_column_cell_get_size (column, NULL, NULL, NULL, &tmp_width, &tmp_height); + height = MAX (height, tmp_height); height = MAX (height, tree_view->priv->expander_size); @@ -3786,7 +4217,7 @@ validate_visible_area (GtkTreeView *tree_view) GtkRBNode *node = NULL; gboolean need_redraw = FALSE; gboolean size_changed = FALSE; - gboolean modify_dy = FALSE; + gboolean update_dy = FALSE; gint total_height; gint area_above = 0; gint area_below = 0; @@ -3796,10 +4227,13 @@ validate_visible_area (GtkTreeView *tree_view) if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID) && tree_view->priv->scroll_to_path == NULL) - return; + return; total_height = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); + if (total_height == 0) + return; + /* First, we check to see if we need to scroll anywhere */ if (tree_view->priv->scroll_to_path) @@ -3815,6 +4249,7 @@ validate_visible_area (GtkTreeView *tree_view) if (validate_row (tree_view, tree, node, &iter, path)) size_changed = TRUE; } + if (tree_view->priv->scroll_to_use_align) { gint height = MAX (GTK_RBNODE_GET_HEIGHT (node), tree_view->priv->expander_size); @@ -3826,10 +4261,62 @@ validate_visible_area (GtkTreeView *tree_view) } else { - /* FIXME: */ - g_warning ("non use_align not implemented yet"); - gtk_tree_path_free (path); - path = NULL; + /* two cases: + * 1) row not visible + * 2) row visible + */ + gint dy; + gint height = MAX (GTK_RBNODE_GET_HEIGHT (node), tree_view->priv->expander_size); + + 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)) + { + /* row visible: keep the row at the same position */ + area_above = dy - tree_view->priv->vadjustment->value; + area_below = (tree_view->priv->vadjustment->value + + tree_view->priv->vadjustment->page_size) + - dy - height; + } + else + { + /* row not visible */ + update_dy = TRUE; + + if (dy >= 0 && dy <= tree_view->priv->vadjustment->page_size) + { + /* row at the beginning -- fixed */ + area_above = dy; + area_below = tree_view->priv->vadjustment->page_size + - area_above - height; + } + else if (dy >= (tree_view->priv->vadjustment->upper - + tree_view->priv->vadjustment->page_size) + && dy <= tree_view->priv->vadjustment->upper) + { + /* row at the end -- fixed */ + area_above = dy - (tree_view->priv->vadjustment->upper - + tree_view->priv->vadjustment->page_size); + area_below = tree_view->priv->vadjustment->page_size - + area_above - height; + + if (area_below < 0) + { + area_above += area_below; + area_below = 0; + } + } + else + { + /* row somewhere in the middle, bring it to the top + * of the view + */ + area_above = 0; + area_below = total_height - height; + } + } } } else @@ -3890,6 +4377,8 @@ validate_visible_area (GtkTreeView *tree_view) */ while (node && area_below > 0) { + gint new_height; + if (node->children) { GtkTreeIter parent = iter; @@ -3943,20 +4432,33 @@ validate_visible_area (GtkTreeView *tree_view) } while (!done); } + + if (!node) + break; + + new_height = GTK_RBNODE_GET_HEIGHT (node); + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID)) { + gint old_height = new_height; + need_redraw = TRUE; if (validate_row (tree_view, tree, node, &iter, path)) - size_changed = TRUE; + { + new_height = GTK_RBNODE_GET_HEIGHT (node); + size_changed = TRUE; + + area_below -= new_height - old_height; + } } - if (node) - area_below -= MAX (GTK_RBNODE_GET_HEIGHT (node), tree_view->priv->expander_size); + + area_below -= MAX (new_height, tree_view->priv->expander_size); } gtk_tree_path_free (path); - /* If we ran out of tree, and have extra area_below left, we need to remove it - * from the area_above */ + /* If we ran out of tree, and have extra area_below left, we need to add it + * to area_above */ if (area_below > 0) area_above += area_below; @@ -3965,6 +4467,8 @@ validate_visible_area (GtkTreeView *tree_view) /* We walk backwards */ while (area_above > 0) { + gint new_height; + _gtk_rbtree_prev_full (tree, node, &tree, &node); if (! gtk_tree_path_prev (above_path) && node != NULL) { @@ -3976,23 +4480,34 @@ validate_visible_area (GtkTreeView *tree_view) if (node == NULL) break; + new_height = GTK_RBNODE_GET_HEIGHT (node); + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID)) { + gint old_height = new_height; + need_redraw = TRUE; if (validate_row (tree_view, tree, node, &iter, above_path)) - size_changed = TRUE; + { + new_height = GTK_RBNODE_GET_HEIGHT (node); + size_changed = TRUE; + + area_above -= new_height - old_height; + } } - area_above -= MAX (GTK_RBNODE_GET_HEIGHT (node), tree_view->priv->expander_size); - modify_dy = TRUE; + area_above -= MAX (new_height, tree_view->priv->expander_size); + update_dy = TRUE; } if (size_changed) { GtkRequisition requisition; + /* 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); gtk_adjustment_changed (tree_view->priv->hadjustment); @@ -4000,8 +4515,20 @@ 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 (modify_dy) + if (update_dy) { gint dy; if (node != NULL) @@ -4012,6 +4539,7 @@ validate_visible_area (GtkTreeView *tree_view) { dy = 0; } + gtk_adjustment_set_value (tree_view->priv->vadjustment, dy); need_redraw = TRUE; } @@ -4033,6 +4561,37 @@ validate_visible_area (GtkTreeView *tree_view) gtk_widget_queue_draw (GTK_WIDGET (tree_view)); } +static void +initialize_fixed_height_mode (GtkTreeView *tree_view) +{ + if (!tree_view->priv->tree) + return; + + if (tree_view->priv->fixed_height < 0) + { + GtkTreeIter iter; + GtkTreePath *path; + + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + + tree = tree_view->priv->tree; + node = tree->root; + + path = _gtk_tree_view_find_path (tree_view, tree, node); + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); + + validate_row (tree_view, tree, node, &iter, path); + + gtk_tree_path_free (path); + + tree_view->priv->fixed_height = MAX (GTK_RBNODE_GET_HEIGHT (node), + tree_view->priv->expander_size); + } + + _gtk_rbtree_set_fixed_height (tree_view->priv->tree, + tree_view->priv->fixed_height); +} /* Our strategy for finding nodes to validate is a little convoluted. We find * the left-most uninvalidated node. We then try walking right, validating @@ -4059,6 +4618,14 @@ do_validate_rows (GtkTreeView *tree_view) if (tree_view->priv->tree == NULL) return FALSE; + if (tree_view->priv->fixed_height_mode) + { + if (tree_view->priv->fixed_height < 0) + initialize_fixed_height_mode (tree_view); + + return FALSE; + } + do { if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID)) @@ -4119,7 +4686,9 @@ do_validate_rows (GtkTreeView *tree_view) path = _gtk_tree_view_find_path (tree_view, tree, node); gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); } - validated_area = validate_row (tree_view, tree, node, &iter, path) | validated_area; + + validated_area = validate_row (tree_view, tree, node, &iter, path) || + validated_area; if (!tree_view->priv->fixed_height_check) { @@ -4181,7 +4750,7 @@ validate_rows (GtkTreeView *tree_view) g_source_remove (tree_view->priv->validate_rows_timer); tree_view->priv->validate_rows_timer = 0; } - + return retval; } @@ -4193,8 +4762,11 @@ validate_rows_handler (GtkTreeView *tree_view) GDK_THREADS_ENTER (); retval = do_validate_rows (tree_view); - if (! retval) - tree_view->priv->validate_rows_timer = 0; + if (! retval && tree_view->priv->validate_rows_timer) + { + g_source_remove (tree_view->priv->validate_rows_timer); + tree_view->priv->validate_rows_timer = 0; + } GDK_THREADS_LEAVE (); @@ -4398,35 +4970,88 @@ set_source_row (GdkDragContext *context, static GtkTreePath* get_source_row (GdkDragContext *context) { - GtkTreeRowReference *ref = - g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); + GtkTreeRowReference *ref = + g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); + + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; +} + +typedef struct +{ + GtkTreeRowReference *dest_row; + gboolean path_down_mode; + gboolean empty_view_drop; + gboolean drop_append_mode; +} +DestRow; + +static void +dest_row_free (gpointer data) +{ + DestRow *dr = (DestRow *)data; - if (ref) - return gtk_tree_row_reference_get_path (ref); - else - return NULL; + gtk_tree_row_reference_free (dr->dest_row); + g_free (dr); } static void set_dest_row (GdkDragContext *context, GtkTreeModel *model, - GtkTreePath *dest_row) + GtkTreePath *dest_row, + gboolean path_down_mode, + gboolean empty_view_drop, + gboolean drop_append_mode) { - g_object_set_data_full (G_OBJECT (context), - "gtk-tree-view-dest-row", - dest_row ? gtk_tree_row_reference_new (model, dest_row) : NULL, - (GDestroyNotify) (dest_row ? gtk_tree_row_reference_free : NULL)); + DestRow *dr; + + if (!dest_row) + { + g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row", + NULL, NULL); + return; + } + + dr = g_new0 (DestRow, 1); + + dr->dest_row = gtk_tree_row_reference_new (model, dest_row); + dr->path_down_mode = path_down_mode; + 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", + dr, (GDestroyNotify) dest_row_free); } static GtkTreePath* -get_dest_row (GdkDragContext *context) +get_dest_row (GdkDragContext *context, + gboolean *path_down_mode) { - GtkTreeRowReference *ref = + DestRow *dr = g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row"); - if (ref) - return gtk_tree_row_reference_get_path (ref); + if (dr) + { + GtkTreePath *path = NULL; + + if (path_down_mode) + *path_down_mode = dr->path_down_mode; + + if (dr->dest_row) + path = gtk_tree_row_reference_get_path (dr->dest_row); + else if (dr->empty_view_drop) + path = gtk_tree_path_new_from_indices (0, -1); + else + path = NULL; + + if (path && dr->drop_append_mode) + gtk_tree_path_next (path); + + return path; + } else return NULL; } @@ -4566,7 +5191,7 @@ remove_scroll_timeout (GtkTreeView *tree_view) { if (tree_view->priv->scroll_timeout != 0) { - gtk_timeout_remove (tree_view->priv->scroll_timeout); + g_source_remove (tree_view->priv->scroll_timeout); tree_view->priv->scroll_timeout = 0; } } @@ -4581,7 +5206,7 @@ check_model_dnd (GtkTreeModel *model, "on GtkTreeView when using models that don't support " "the %s interface and enabling drag-and-drop. The simplest way to do this " "is to connect to '%s' and call " - "gtk_signal_emit_stop_by_name() in your signal handler to prevent " + "g_signal_stop_emission_by_name() in your signal handler to prevent " "the default handler from running. Look at the source code " "for the default handler in gtktreeview.c to get an idea what " "your handler should do. (gtktreeview.c is in the GTK source " @@ -4599,7 +5224,7 @@ remove_open_timeout (GtkTreeView *tree_view) { if (tree_view->priv->open_dest_timeout != 0) { - gtk_timeout_remove (tree_view->priv->open_dest_timeout); + g_source_remove (tree_view->priv->open_dest_timeout); tree_view->priv->open_dest_timeout = 0; } } @@ -4670,6 +5295,7 @@ set_destination_row (GtkTreeView *tree_view, TreeViewDragInfo *di; GtkWidget *widget; GtkTreePath *old_dest_path = NULL; + gboolean can_drop = FALSE; *suggested_action = 0; *target = GDK_NONE; @@ -4678,7 +5304,7 @@ set_destination_row (GtkTreeView *tree_view, di = get_info (tree_view); - if (di == NULL) + if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0) { /* someone unset us as a drag dest, note that if * we return FALSE drag_leave isn't called @@ -4705,18 +5331,34 @@ set_destination_row (GtkTreeView *tree_view, &path, &pos)) { - /* can't drop here */ + gint n_children; + GtkTreeModel *model; + remove_open_timeout (tree_view); - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + /* the row got dropped on empty space, let's setup a special case + */ if (path) gtk_tree_path_free (path); - /* don't propagate to parent though */ - return TRUE; + model = gtk_tree_view_get_model (tree_view); + + n_children = gtk_tree_model_iter_n_children (model, NULL); + if (n_children) + { + pos = GTK_TREE_VIEW_DROP_AFTER; + path = gtk_tree_path_new_from_indices (n_children - 1, -1); + } + else + { + pos = GTK_TREE_VIEW_DROP_BEFORE; + path = gtk_tree_path_new_from_indices (0, -1); + } + + can_drop = TRUE; + + goto out; } g_assert (path); @@ -4738,17 +5380,22 @@ set_destination_row (GtkTreeView *tree_view, gtk_tree_path_free (old_dest_path); if (TRUE /* FIXME if the location droppable predicate */) + { + can_drop = TRUE; + } + +out: + if (can_drop) { GtkWidget *source_widget; *suggested_action = context->suggested_action; - source_widget = gtk_drag_get_source_widget (context); if (source_widget == widget) { /* Default to MOVE, unless the user has - * pressed ctrl or alt to affect available actions + * pressed ctrl or shift to affect available actions */ if ((context->actions & GDK_ACTION_MOVE) != 0) *suggested_action = GDK_ACTION_MOVE; @@ -4772,14 +5419,22 @@ set_destination_row (GtkTreeView *tree_view, return TRUE; } -static GtkTreePath* -get_logical_dest_row (GtkTreeView *tree_view) +static GtkTreePath* +get_logical_dest_row (GtkTreeView *tree_view, + gboolean *path_down_mode, + gboolean *drop_append_mode) { /* adjust path to point to the row the drop goes in front of */ GtkTreePath *path = NULL; GtkTreeViewDropPosition pos; + g_return_val_if_fail (path_down_mode != NULL, NULL); + g_return_val_if_fail (drop_append_mode != NULL, NULL); + + *path_down_mode = FALSE; + *drop_append_mode = 0; + gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); if (path == NULL) @@ -4789,10 +5444,7 @@ get_logical_dest_row (GtkTreeView *tree_view) ; /* do nothing */ else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) - { - /* get first child, drop before it */ - gtk_tree_path_down (path); - } + *path_down_mode = TRUE; else { GtkTreeIter iter; @@ -4800,17 +5452,14 @@ get_logical_dest_row (GtkTreeView *tree_view) g_assert (pos == GTK_TREE_VIEW_DROP_AFTER); - gtk_tree_model_get_iter (model, &iter, path); - - if (!gtk_tree_model_iter_next (model, &iter)) - g_object_set_data (G_OBJECT (model), "gtk-tree-model-drop-append", - GINT_TO_POINTER (1)); + if (!gtk_tree_model_get_iter (model, &iter, path) || + !gtk_tree_model_iter_next (model, &iter)) + *drop_append_mode = 1; else { - g_object_set_data (G_OBJECT (model), "gtk-tree-model-drop-append", - NULL); - gtk_tree_path_next (path); - } + *drop_append_mode = 0; + gtk_tree_path_next (path); + } } return path; @@ -4880,25 +5529,6 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, button, (GdkEvent*)event); - gtk_drag_set_icon_default (context); - - { - GdkPixmap *row_pix; - - row_pix = gtk_tree_view_create_row_drag_icon (tree_view, - path); - - gtk_drag_set_icon_pixmap (context, - gdk_drawable_get_colormap (row_pix), - row_pix, - NULL, - /* the + 1 is for the black border in the icon */ - tree_view->priv->press_start_x + 1, - cell_y + 1); - - gdk_pixmap_unref (row_pix); - } - set_source_row (context, model, path); out: @@ -4913,7 +5543,40 @@ static void gtk_tree_view_drag_begin (GtkWidget *widget, GdkDragContext *context) { - /* do nothing */ + GtkTreeView *tree_view; + GtkTreePath *path = NULL; + gint cell_x, cell_y; + GdkPixmap *row_pix; + + tree_view = GTK_TREE_VIEW (widget); + + /* if the user uses a custom DnD impl, we don't set the icon here */ + if (!get_info (tree_view)) + return; + + gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->press_start_x, + tree_view->priv->press_start_y, + &path, + NULL, + &cell_x, + &cell_y); + + g_return_if_fail (path != NULL); + + row_pix = gtk_tree_view_create_row_drag_icon (tree_view, + path); + + gtk_drag_set_icon_pixmap (context, + gdk_drawable_get_colormap (row_pix), + row_pix, + NULL, + /* the + 1 is for the black border in the icon */ + tree_view->priv->press_start_x + 1, + cell_y + 1); + + g_object_unref (row_pix); + gtk_tree_path_free (path); } static void @@ -5036,7 +5699,9 @@ gtk_tree_view_drag_motion (GtkWidget *widget, gint y, guint time) { + gboolean empty; GtkTreePath *path = NULL; + GtkTreeModel *model; GtkTreeViewDropPosition pos; GtkTreeView *tree_view; GdkDragAction suggested_action = 0; @@ -5049,7 +5714,11 @@ gtk_tree_view_drag_motion (GtkWidget *widget, gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); - if (path == NULL) + /* 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) { /* Can't drop here. */ gdk_drag_status (context, 0, time); @@ -5061,12 +5730,12 @@ gtk_tree_view_drag_motion (GtkWidget *widget, pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) { tree_view->priv->open_dest_timeout = - gtk_timeout_add (500, open_row_timeout, tree_view); + g_timeout_add (500, open_row_timeout, tree_view); } else if (tree_view->priv->scroll_timeout == 0) { tree_view->priv->scroll_timeout = - gtk_timeout_add (150, scroll_row_timeout, tree_view); + g_timeout_add (150, scroll_row_timeout, tree_view); } if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) @@ -5104,6 +5773,8 @@ gtk_tree_view_drag_drop (GtkWidget *widget, GdkAtom target = GDK_NONE; TreeViewDragInfo *di; GtkTreeModel *model; + gboolean path_down_mode; + gboolean drop_append_mode; tree_view = GTK_TREE_VIEW (widget); @@ -5123,7 +5794,7 @@ gtk_tree_view_drag_drop (GtkWidget *widget, if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) return FALSE; - path = get_logical_dest_row (tree_view); + path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode); if (target != GDK_NONE && path != NULL) { @@ -5131,8 +5802,9 @@ gtk_tree_view_drag_drop (GtkWidget *widget, * treat drag data receives as a drop. */ set_status_pending (context, 0); - - set_dest_row (context, model, path); + set_dest_row (context, model, path, + path_down_mode, tree_view->priv->empty_view_drop, + drop_append_mode); } if (path) @@ -5168,6 +5840,8 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, GtkTreeView *tree_view; GtkTreePath *dest_row; GdkDragAction suggested_action; + gboolean path_down_mode; + gboolean drop_append_mode; tree_view = GTK_TREE_VIEW (widget); @@ -5190,17 +5864,33 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, * supposed to call drag_status, not actually paste in the * data. */ - path = get_logical_dest_row (tree_view); + path = get_logical_dest_row (tree_view, &path_down_mode, + &drop_append_mode); if (path == NULL) suggested_action = 0; + else if (path_down_mode) + gtk_tree_path_down (path); if (suggested_action) { if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), path, selection_data)) - suggested_action = 0; + { + if (path_down_mode) + { + path_down_mode = FALSE; + gtk_tree_path_up (path); + + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + path, + selection_data)) + suggested_action = 0; + } + else + suggested_action = 0; + } } gdk_drag_status (context, suggested_action, time); @@ -5217,11 +5907,22 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, return; } - dest_row = get_dest_row (context); + dest_row = get_dest_row (context, &path_down_mode); if (dest_row == NULL) return; + if (selection_data->length >= 0) + { + if (path_down_mode) + { + gtk_tree_path_down (dest_row); + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + dest_row, selection_data)) + gtk_tree_path_up (dest_row); + } + } + if (selection_data->length >= 0) { if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), @@ -5235,10 +5936,18 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, (context->action == GDK_ACTION_MOVE), time); + if (gtk_tree_path_get_depth (dest_row) == 1 + && gtk_tree_path_get_indices (dest_row)[0] == 0) + { + /* special special case drag to "0", scroll to first item */ + if (!tree_view->priv->scroll_to_path) + gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0); + } + gtk_tree_path_free (dest_row); /* drop dest_row */ - set_dest_row (context, NULL, NULL); + set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE); } @@ -5346,6 +6055,59 @@ gtk_tree_view_has_special_cell (GtkTreeView *tree_view) return FALSE; } +static void +column_sizing_notify (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (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); +} + +static void +gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view, + gboolean enable) +{ + GList *l; + + if (tree_view->priv->fixed_height_mode && enable) + return; + + if (!enable) + { + tree_view->priv->fixed_height_mode = 0; + tree_view->priv->fixed_height = -1; + + /* force a revalidation */ + install_presize_handler (tree_view); + return; + } + + /* make sure all columns are of type FIXED */ + for (l = tree_view->priv->columns; l; l = l->next) + { + GtkTreeViewColumn *c = l->data; + + g_return_if_fail (gtk_tree_view_column_get_sizing (c) == GTK_TREE_VIEW_COLUMN_FIXED); + } + + /* yes, we really have to do this is in a separate loop */ + for (l = tree_view->priv->columns; l; l = l->next) + g_signal_connect (l->data, "notify::sizing", + G_CALLBACK (column_sizing_notify), tree_view); + + tree_view->priv->fixed_height_mode = 1; + tree_view->priv->fixed_height = -1; + + if (!tree_view->priv->tree) + return; + + initialize_fixed_height_mode (tree_view); +} + /* Returns TRUE if the focus is within the headers, after the focus operation is * done */ @@ -5358,6 +6120,7 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view, GList *last_column, *first_column; GList *tmp_list; + gboolean rtl; if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) return FALSE; @@ -5393,6 +6156,13 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view, } + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + if (rtl) { + GList *temp = first_column; + first_column = last_column; + last_column = temp; + } + switch (dir) { case GTK_DIR_TAB_BACKWARD: @@ -5604,6 +6374,12 @@ gtk_tree_view_style_set (GtkWidget *widget, 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_style_get (widget, "expander_size", &tree_view->priv->expander_size, NULL); @@ -5615,6 +6391,7 @@ gtk_tree_view_style_set (GtkWidget *widget, _gtk_tree_view_column_cell_set_dirty (column, TRUE); } + tree_view->priv->fixed_height = -1; _gtk_rbtree_mark_invalid (tree_view->priv->tree); gtk_widget_queue_resize (widget); @@ -5660,37 +6437,41 @@ gtk_tree_view_set_adjustments (GtkTreeView *tree_view, if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj)) { - gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->hadjustment), (GtkSignalFunc) gtk_tree_view_adjustment_changed, tree_view); - gtk_object_unref (GTK_OBJECT (tree_view->priv->hadjustment)); + g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment, + gtk_tree_view_adjustment_changed, + tree_view); + g_object_unref (tree_view->priv->hadjustment); } if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj)) { - gtk_signal_disconnect_by_func (GTK_OBJECT (tree_view->priv->vadjustment), (GtkSignalFunc) gtk_tree_view_adjustment_changed, tree_view); - gtk_object_unref (GTK_OBJECT (tree_view->priv->vadjustment)); + g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment, + gtk_tree_view_adjustment_changed, + tree_view); + g_object_unref (tree_view->priv->vadjustment); } if (tree_view->priv->hadjustment != hadj) { tree_view->priv->hadjustment = hadj; - gtk_object_ref (GTK_OBJECT (tree_view->priv->hadjustment)); + g_object_ref (tree_view->priv->hadjustment); gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment)); - gtk_signal_connect (GTK_OBJECT (tree_view->priv->hadjustment), "value_changed", - (GtkSignalFunc) gtk_tree_view_adjustment_changed, - tree_view); + g_signal_connect (tree_view->priv->hadjustment, "value_changed", + G_CALLBACK (gtk_tree_view_adjustment_changed), + tree_view); need_adjust = TRUE; } if (tree_view->priv->vadjustment != vadj) { tree_view->priv->vadjustment = vadj; - gtk_object_ref (GTK_OBJECT (tree_view->priv->vadjustment)); + g_object_ref (tree_view->priv->vadjustment); gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment)); - gtk_signal_connect (GTK_OBJECT (tree_view->priv->vadjustment), "value_changed", - (GtkSignalFunc) gtk_tree_view_adjustment_changed, - tree_view); + g_signal_connect (tree_view->priv->vadjustment, "value_changed", + G_CALLBACK (gtk_tree_view_adjustment_changed), + tree_view); need_adjust = TRUE; } @@ -5704,6 +6485,8 @@ gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, GtkMovementStep step, gint count) { + GdkModifierType state; + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS || step == GTK_MOVEMENT_VISUAL_POSITIONS || @@ -5720,6 +6503,15 @@ gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + if (gtk_get_current_event_state (&state)) + { + if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + tree_view->priv->ctrl_pressed = TRUE; + if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) + tree_view->priv->shift_pressed = TRUE; + } + /* else we assume not pressed */ + switch (step) { /* currently we make no distinction. When we go bi-di, we need to */ @@ -5740,6 +6532,9 @@ gtk_tree_view_real_move_cursor (GtkTreeView *tree_view, g_assert_not_reached (); } + tree_view->priv->ctrl_pressed = FALSE; + tree_view->priv->shift_pressed = FALSE; + return TRUE; } @@ -5854,23 +6649,32 @@ gtk_tree_view_row_changed (GtkTreeModel *model, if (tree == NULL) goto done; - _gtk_rbtree_node_mark_invalid (tree, node); - for (list = tree_view->priv->columns; list; list = list->next) + if (tree_view->priv->fixed_height_mode + && tree_view->priv->fixed_height >= 0) { - GtkTreeViewColumn *column; + _gtk_rbtree_node_set_height (tree, node, tree_view->priv->fixed_height); + } + else + { + _gtk_rbtree_node_mark_invalid (tree, node); + for (list = tree_view->priv->columns; list; list = list->next) + { + GtkTreeViewColumn *column; - column = list->data; - if (! column->visible) - continue; + column = list->data; + if (! column->visible) + continue; - if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - { - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - } + if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + { + _gtk_tree_view_column_cell_set_dirty (column, TRUE); + } + } } done: - install_presize_handler (tree_view); + if (!tree_view->priv->fixed_height_mode) + install_presize_handler (tree_view); if (free_path) gtk_tree_path_free (path); } @@ -5887,6 +6691,7 @@ gtk_tree_view_row_inserted (GtkTreeModel *model, GtkRBNode *tmpnode = NULL; gint depth; gint i = 0; + gint height; gboolean free_path = FALSE; g_return_if_fail (path != NULL || iter != NULL); @@ -5948,18 +6753,24 @@ gtk_tree_view_row_inserted (GtkTreeModel *model, if (tree == NULL) goto done; + if (tree_view->priv->fixed_height_mode + && tree_view->priv->fixed_height >= 0) + height = tree_view->priv->fixed_height; + else + height = 0; + /* ref the node */ gtk_tree_model_ref_node (tree_view->priv->model, iter); if (indices[depth - 1] == 0) { tmpnode = _gtk_rbtree_find_count (tree, 1); - _gtk_rbtree_insert_before (tree, tmpnode, 0, FALSE); + _gtk_rbtree_insert_before (tree, tmpnode, height, FALSE); } else { tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]); - _gtk_rbtree_insert_after (tree, tmpnode, 0, FALSE); - } + _gtk_rbtree_insert_after (tree, tmpnode, height, FALSE); + } done: install_presize_handler (tree_view); @@ -6051,6 +6862,19 @@ count_children_helper (GtkRBTree *tree, (*((gint *)data))++; } +static void +check_selection_helper (GtkRBTree *tree, + GtkRBNode *node, + gpointer data) +{ + gint *value = (gint *)data; + + *value = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED); + + if (node->children && !*value) + _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data); +} + static void gtk_tree_view_row_deleted (GtkTreeModel *model, GtkTreePath *path, @@ -6060,7 +6884,7 @@ gtk_tree_view_row_deleted (GtkTreeModel *model, GtkRBTree *tree; GtkRBNode *node; GList *list; - gint selection_changed; + gint selection_changed = FALSE; g_return_if_fail (path != NULL); @@ -6072,8 +6896,9 @@ gtk_tree_view_row_deleted (GtkTreeModel *model, if (tree == NULL) return; - /* Change the selection */ - selection_changed = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED); + /* check if the selection has been changed */ + _gtk_rbtree_traverse (tree, node, G_POST_ORDER, + check_selection_helper, &selection_changed); for (list = tree_view->priv->columns; list; list = list->next) if (((GtkTreeViewColumn *)list->data)->visible && @@ -6089,7 +6914,7 @@ gtk_tree_view_row_deleted (GtkTreeModel *model, /* If we have a node expanded/collapsed timeout, remove it */ if (tree_view->priv->expand_collapse_timeout != 0) { - gtk_timeout_remove (tree_view->priv->expand_collapse_timeout); + g_source_remove (tree_view->priv->expand_collapse_timeout); tree_view->priv->expand_collapse_timeout = 0; /* Reset node */ @@ -6118,14 +6943,31 @@ gtk_tree_view_row_deleted (GtkTreeModel *model, _gtk_rbtree_remove_node (tree, node); } + if (! gtk_tree_row_reference_valid (tree_view->priv->top_row)) + { + gtk_tree_row_reference_free (tree_view->priv->top_row); + tree_view->priv->top_row = NULL; + } + install_scroll_sync_handler (tree_view); gtk_widget_queue_resize (GTK_WIDGET (tree_view)); if (selection_changed) - g_signal_emit_by_name (G_OBJECT (tree_view->priv->selection), "changed"); + g_signal_emit_by_name (tree_view->priv->selection, "changed"); } +static void +cancel_arrow_animation (GtkTreeView *tree_view) +{ + if (tree_view->priv->expand_collapse_timeout) + { + while (do_expand_collapse (tree_view)); + + g_source_remove (tree_view->priv->expand_collapse_timeout); + tree_view->priv->expand_collapse_timeout = 0; + } +} static void gtk_tree_view_rows_reordered (GtkTreeModel *model, @@ -6164,8 +7006,14 @@ gtk_tree_view_rows_reordered (GtkTreeModel *model, if (tree == NULL) return; + if (tree_view->priv->edited_column) + gtk_tree_view_stop_editing (tree_view, TRUE); + /* we need to be unprelighted */ ensure_unprelighted (tree_view); + + /* clear the timeout */ + cancel_arrow_animation (tree_view); _gtk_rbtree_reorder (tree, new_order, len); @@ -6189,6 +7037,7 @@ gtk_tree_view_get_background_xrange (GtkTreeView *tree_view, GtkTreeViewColumn *tmp_column = NULL; gint total_width; GList *list; + gboolean rtl; if (x1) *x1 = 0; @@ -6196,8 +7045,12 @@ gtk_tree_view_get_background_xrange (GtkTreeView *tree_view, if (x2) *x2 = 0; + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + total_width = 0; - for (list = tree_view->priv->columns; list; list = list->next) + 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; @@ -6236,15 +7089,23 @@ gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, GtkTreeViewColumn *tmp_column = NULL; gint total_width; gboolean indent_expanders; + gboolean rtl; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); total_width = 0; - for (list = tree_view->priv->columns; list; list = list->next) + 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; if (gtk_tree_view_is_expander_column (tree_view, tmp_column)) { - x_offset = total_width; + if (rtl) + x_offset = total_width + tmp_column->width - tree_view->priv->expander_size; + else + x_offset = total_width; break; } @@ -6257,22 +7118,28 @@ gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, NULL); if (indent_expanders) - x_offset += tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree); - + { + if (rtl) + x_offset -= tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree); + 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 = x_offset + tree_view->priv->expander_size + 1; + *x2 = *x1 + tree_view->priv->expander_size + 1; } else { /* return an empty range, the expander column is hidden */ if (x2) - *x2 = x_offset; + *x2 = *x1; } } @@ -6284,11 +7151,16 @@ gtk_tree_view_build_tree (GtkTreeView *tree_view, gboolean recurse) { GtkRBNode *temp = NULL; + gboolean is_list = GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST); do { gtk_tree_model_ref_node (tree_view->priv->model, iter); temp = _gtk_rbtree_insert_after (tree, temp, 0, FALSE); + + if (is_list) + continue; + if (recurse) { GtkTreeIter child; @@ -6305,7 +7177,6 @@ gtk_tree_view_build_tree (GtkTreeView *tree_view, { if ((temp->flags>K_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT) temp->flags ^= GTK_RBNODE_IS_PARENT; - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); } } while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); @@ -6439,28 +7310,21 @@ gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, GtkRBTree *tree, GtkRBNode *node) { - GdkRectangle cell_rect; - GdkRectangle vis_rect; - gint dest_y; + GtkTreePath *path = NULL; - /* We process updates because we want to clear old selected items when we scroll. - * if this is removed, we get a "selection streak" at the bottom. */ if (!GTK_WIDGET_REALIZED (tree_view)) return; - gdk_window_process_updates (tree_view->priv->bin_window, TRUE); - cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, BACKGROUND_FIRST_PIXEL (tree_view, tree, node)); - cell_rect.height = BACKGROUND_HEIGHT (node); - gtk_tree_view_get_visible_rect (tree_view, &vis_rect); - - dest_y = vis_rect.y; - - if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height) - dest_y = cell_rect.y + cell_rect.height - vis_rect.height; - if (cell_rect.y < vis_rect.y) - dest_y = cell_rect.y; + path = _gtk_tree_view_find_path (tree_view, tree, node); - gtk_tree_view_scroll_to_point (tree_view, -1, dest_y); + if (path) + { + /* We process updates because we want to clear old selected items when we scroll. + * if this is removed, we get a "selection streak" at the bottom. */ + gdk_window_process_updates (tree_view->priv->bin_window, TRUE); + gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free (path); + } } static void @@ -6585,7 +7449,9 @@ gtk_tree_view_is_expander_column (GtkTreeView *tree_view, } else { - for (list = tree_view->priv->columns; list; list = list->next) + for (list = tree_view->priv->columns; + list; + list = list->next) if (((GtkTreeViewColumn *)list->data)->visible) break; if (list && list->data == column) @@ -6604,26 +7470,26 @@ gtk_tree_view_add_move_binding (GtkBindingSet *binding_set, gtk_binding_entry_add_signal (binding_set, keyval, modmask, "move_cursor", 2, - GTK_TYPE_ENUM, step, - GTK_TYPE_INT, count); + G_TYPE_ENUM, step, + G_TYPE_INT, count); gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, step, - GTK_TYPE_INT, count); + G_TYPE_ENUM, step, + G_TYPE_INT, count); if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) return; gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, step, - GTK_TYPE_INT, count); + G_TYPE_ENUM, step, + G_TYPE_INT, count); gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, "move_cursor", 2, - GTK_TYPE_ENUM, step, - GTK_TYPE_INT, count); + G_TYPE_ENUM, step, + G_TYPE_INT, count); } static gint @@ -6649,7 +7515,9 @@ gtk_tree_view_unref_tree_helper (GtkTreeModel *model, while (new_node && new_node->left != new_tree->nil) new_node = new_node->left; - g_return_val_if_fail (gtk_tree_model_iter_children (model, &child, iter), FALSE); + if (!gtk_tree_model_iter_children (model, &child, iter)) + return FALSE; + retval = retval || gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node); } @@ -6696,7 +7564,7 @@ gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, GtkTreeViewColumn *left_column; GtkTreeViewColumn *cur_column = NULL; GtkTreeViewColumnReorder *reorder; - + gboolean rtl; GList *tmp_list; gint left; @@ -6704,16 +7572,18 @@ gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, * are available. */ left_column = NULL; + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); /* First, identify all possible drop spots */ - tmp_list = tree_view->priv->columns; + if (rtl) + tmp_list = g_list_last (tree_view->priv->columns); + else + tmp_list = g_list_first (tree_view->priv->columns); while (tmp_list) { - g_assert (tmp_list); - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; + tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list); if (cur_column->visible == FALSE) continue; @@ -6781,7 +7651,7 @@ gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, { gint width; - gdk_window_get_size (tree_view->priv->header_window, &width, NULL); + gdk_drawable_get_size (tree_view->priv->header_window, &width, NULL); reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); } } @@ -6791,7 +7661,7 @@ void _gtk_tree_view_column_start_drag (GtkTreeView *tree_view, GtkTreeViewColumn *column) { - GdkEvent send_event; + GdkEvent *send_event; GtkAllocation allocation; gint x, y, width, height; GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view)); @@ -6827,29 +7697,31 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view, gtk_grab_remove (column->button); - send_event.crossing.type = GDK_LEAVE_NOTIFY; - send_event.crossing.send_event = TRUE; - send_event.crossing.window = column->button->window; - send_event.crossing.subwindow = NULL; - send_event.crossing.detail = GDK_NOTIFY_ANCESTOR; - send_event.crossing.time = GDK_CURRENT_TIME; - - gtk_propagate_event (column->button, &send_event); - - send_event.button.type = GDK_BUTTON_RELEASE; - send_event.button.window = gdk_screen_get_root_window (screen); - send_event.button.send_event = TRUE; - send_event.button.time = GDK_CURRENT_TIME; - send_event.button.x = -1; - send_event.button.y = -1; - send_event.button.axes = NULL; - send_event.button.state = 0; - send_event.button.button = 1; - send_event.button.device = gdk_display_get_core_pointer (display); - send_event.button.x_root = 0; - send_event.button.y_root = 0; - - gtk_propagate_event (column->button, &send_event); + send_event = gdk_event_new (GDK_LEAVE_NOTIFY); + send_event->crossing.send_event = TRUE; + send_event->crossing.window = g_object_ref (column->button->window); + send_event->crossing.subwindow = NULL; + send_event->crossing.detail = GDK_NOTIFY_ANCESTOR; + send_event->crossing.time = GDK_CURRENT_TIME; + + gtk_propagate_event (column->button, send_event); + gdk_event_free (send_event); + + send_event = gdk_event_new (GDK_BUTTON_RELEASE); + send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen)); + send_event->button.send_event = TRUE; + send_event->button.time = GDK_CURRENT_TIME; + send_event->button.x = -1; + send_event->button.y = -1; + send_event->button.axes = NULL; + send_event->button.state = 0; + send_event->button.button = 1; + send_event->button.device = gdk_display_get_core_pointer (display); + send_event->button.x_root = 0; + send_event->button.y_root = 0; + + gtk_propagate_event (column->button, send_event); + gdk_event_free (send_event); gdk_window_move_resize (tree_view->priv->drag_window, column->button->allocation.x, @@ -6874,7 +7746,7 @@ _gtk_tree_view_column_start_drag (GtkTreeView *tree_view, gdk_window_show (tree_view->priv->drag_window); gdk_window_get_origin (tree_view->priv->header_window, &x, &y); - gdk_window_get_size (tree_view->priv->header_window, &width, &height); + gdk_drawable_get_size (tree_view->priv->header_window, &width, &height); gtk_widget_grab_focus (GTK_WIDGET (tree_view)); while (gtk_events_pending ()) @@ -7191,6 +8063,9 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, GtkTreeIter iter; GList *list; gboolean found_column = FALSE; + gboolean rtl; + + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); if (! GTK_WIDGET_HAS_FOCUS (tree_view)) return; @@ -7241,9 +8116,9 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, } loop_end: if (count == 1) - list = list->next; + list = rtl ? list->prev : list->next; else - list = list->prev; + list = rtl ? list->next : list->prev; } if (found_column) @@ -7253,7 +8128,7 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, cursor_tree, cursor_node, NULL); - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[CURSOR_CHANGED], 0); + g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); } gtk_tree_view_clamp_column_visible (tree_view, tree_view->priv->focus_column); } @@ -7300,46 +8175,51 @@ gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view, gtk_tree_path_free (path); } -static void +static gboolean gtk_tree_view_real_select_all (GtkTreeView *tree_view) { if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + return FALSE; if (tree_view->priv->selection->type != GTK_SELECTION_MULTIPLE) - return; + return FALSE; + gtk_tree_selection_select_all (tree_view->priv->selection); + + return TRUE; } -static void +static gboolean gtk_tree_view_real_unselect_all (GtkTreeView *tree_view) { if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + return FALSE; if (tree_view->priv->selection->type != GTK_SELECTION_MULTIPLE) - return; + return FALSE; + gtk_tree_selection_unselect_all (tree_view->priv->selection); + + return TRUE; } -static void +static gboolean gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, gboolean start_editing) { GtkRBTree *cursor_tree = NULL; GtkRBNode *cursor_node = NULL; GtkTreePath *cursor_path = NULL; - GdkModifierType state = 0; - cursor_path = NULL; + GtkTreeSelectMode mode = 0; if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + return FALSE; if (tree_view->priv->cursor) cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); if (cursor_path == NULL) - return; + return FALSE; _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node); @@ -7347,26 +8227,29 @@ gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, if (cursor_tree == NULL) { gtk_tree_path_free (cursor_path); - return; + return FALSE; } - gtk_get_current_event_state (&state); - - if (! (state & GDK_SHIFT_MASK) && - start_editing && + if (!tree_view->priv->shift_pressed && start_editing && tree_view->priv->focus_column) { if (gtk_tree_view_start_editing (tree_view, cursor_path)) { gtk_tree_path_free (cursor_path); - return; + return TRUE; } } + + if (tree_view->priv->ctrl_pressed) + mode |= GTK_TREE_SELECT_MODE_TOGGLE; + if (tree_view->priv->shift_pressed) + mode |= GTK_TREE_SELECT_MODE_EXTEND; + _gtk_tree_selection_internal_select_node (tree_view->priv->selection, cursor_node, cursor_tree, cursor_path, - state, + mode, FALSE); gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); @@ -7374,13 +8257,16 @@ gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view, gtk_widget_grab_focus (GTK_WIDGET (tree_view)); _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL); - if (! (state & GDK_SHIFT_MASK)) - gtk_tree_view_row_activated (tree_view, cursor_path, tree_view->priv->focus_column); + if (!tree_view->priv->shift_pressed) + gtk_tree_view_row_activated (tree_view, cursor_path, + tree_view->priv->focus_column); gtk_tree_path_free (cursor_path); + + return TRUE; } -static void +static gboolean gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view) { GtkRBTree *cursor_tree = NULL; @@ -7388,28 +8274,28 @@ gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view) GtkTreePath *cursor_path = NULL; if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + return FALSE; cursor_path = NULL; if (tree_view->priv->cursor) cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); if (cursor_path == NULL) - return; + return FALSE; _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node); if (cursor_tree == NULL) { gtk_tree_path_free (cursor_path); - return; + return FALSE; } _gtk_tree_selection_internal_select_node (tree_view->priv->selection, cursor_node, cursor_tree, cursor_path, - GDK_CONTROL_MASK, + GTK_TREE_SELECT_MODE_TOGGLE, FALSE); gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); @@ -7417,11 +8303,11 @@ gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view) gtk_widget_grab_focus (GTK_WIDGET (tree_view)); gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); gtk_tree_path_free (cursor_path); -} - + return TRUE; +} -static void +static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, gboolean logical, gboolean expand, @@ -7432,30 +8318,36 @@ gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view, GtkRBNode *node; if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + return FALSE; cursor_path = NULL; if (tree_view->priv->cursor) cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); if (cursor_path == NULL) - return; + return FALSE; if (_gtk_tree_view_find_node (tree_view, cursor_path, &tree, &node)) - return; + return FALSE; gtk_widget_grab_focus (GTK_WIDGET (tree_view)); gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + if (!logical + && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL) + expand = !expand; + if (expand) gtk_tree_view_real_expand_row (tree_view, cursor_path, tree, node, open_all, TRUE); else gtk_tree_view_real_collapse_row (tree_view, cursor_path, tree, node, TRUE); gtk_tree_path_free (cursor_path); + + return TRUE; } -static void +static gboolean gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) { GtkRBTree *cursor_tree = NULL; @@ -7463,21 +8355,21 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) GtkTreePath *cursor_path = NULL; if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + return FALSE; cursor_path = NULL; if (tree_view->priv->cursor) cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); if (cursor_path == NULL) - return; + return FALSE; _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node); if (cursor_tree == NULL) { gtk_tree_path_free (cursor_path); - return; + return FALSE; } if (cursor_tree->parent_node) @@ -7493,7 +8385,7 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) cursor_node, cursor_tree, cursor_path, - 0, + 0, FALSE); } @@ -7502,73 +8394,93 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) gtk_widget_grab_focus (GTK_WIDGET (tree_view)); gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); gtk_tree_path_free (cursor_path); + + return TRUE; } +/* Cut and paste from gtkwindow.c */ static void -gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view) +send_focus_change (GtkWidget *widget, + gboolean in) { - GtkWidget *window; - GtkWidget *entry; - GtkWidget *search_dialog; - GdkEventFocus focus_event; + GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE); - if (! GTK_WIDGET_HAS_FOCUS (tree_view)) - return; + g_object_ref (widget); + + if (in) + GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); - if (tree_view->priv->enable_search == FALSE || - tree_view->priv->search_column < 0) - return; + fevent->focus_change.type = GDK_FOCUS_CHANGE; + fevent->focus_change.window = g_object_ref (widget->window); + fevent->focus_change.in = in; + + gtk_widget_event (widget, fevent); + + g_object_notify (G_OBJECT (widget), "has_focus"); - search_dialog = g_object_get_data (G_OBJECT (tree_view), - GTK_TREE_VIEW_SEARCH_DIALOG_KEY); - if (search_dialog) + g_object_unref (widget); + gdk_event_free (fevent); +} + +static void +gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view) +{ + if (tree_view->priv->search_window != NULL) return; - /* set up window */ - window = gtk_window_new (GTK_WINDOW_POPUP); - gtk_window_set_title (GTK_WINDOW (window), "search dialog"); - gtk_container_set_border_width (GTK_CONTAINER (window), 3); - gtk_window_set_modal (GTK_WINDOW (window), TRUE); - g_signal_connect (G_OBJECT (window), "delete_event", + tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP); + gtk_container_set_border_width (GTK_CONTAINER (tree_view->priv->search_window), 3); + 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), tree_view); - g_signal_connect (G_OBJECT (window), "key_press_event", + g_signal_connect (tree_view->priv->search_window, "key_press_event", G_CALLBACK (gtk_tree_view_search_key_press_event), tree_view); - g_signal_connect (G_OBJECT (window), "button_press_event", + g_signal_connect (tree_view->priv->search_window, "button_press_event", G_CALLBACK (gtk_tree_view_search_button_press_event), tree_view); /* add entry */ - entry = gtk_entry_new (); - gtk_widget_show (entry); - g_signal_connect (G_OBJECT (entry), "changed", - G_CALLBACK (gtk_tree_view_search_init), tree_view); - g_signal_connect (G_OBJECT (entry), "populate_popup", + tree_view->priv->search_entry = gtk_entry_new (); + gtk_widget_show (tree_view->priv->search_entry); + 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, "populate_popup", G_CALLBACK (gtk_tree_view_search_disable_popdown), tree_view); - gtk_container_add (GTK_CONTAINER (window), entry); + gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), + tree_view->priv->search_entry); +} - /* done, show it */ - tree_view->priv->search_dialog_position_func (tree_view, window); - gtk_widget_show_all (window); - gtk_widget_grab_focus (entry); +static gboolean +gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view) +{ + if (! GTK_WIDGET_HAS_FOCUS (tree_view)) + return FALSE; - /* send focus-in event */ - focus_event.type = GDK_FOCUS_CHANGE; - focus_event.in = TRUE; - gtk_widget_event (entry, (GdkEvent *) &focus_event); + if (tree_view->priv->enable_search == FALSE || + tree_view->priv->search_column < 0) + return FALSE; - /* position window */ + gtk_tree_view_ensure_interactive_directory (tree_view); + gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), ""); - /* yes, we point to the entry's private text thing here, a bit evil */ - g_object_set_data (G_OBJECT (window), "gtk-tree-view-text", - (gchar *) gtk_entry_get_text (GTK_ENTRY (entry))); - g_object_set_data (G_OBJECT (tree_view), - GTK_TREE_VIEW_SEARCH_DIALOG_KEY, window); + /* done, show it */ + tree_view->priv->search_dialog_position_func (tree_view, tree_view->priv->search_window); + gtk_widget_show (tree_view->priv->search_window); + gtk_widget_grab_focus (tree_view->priv->search_entry); + + /* send focus-in event */ + send_focus_change (tree_view->priv->search_entry, TRUE); /* search first matching iter */ - gtk_tree_view_search_init (entry, tree_view); + gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view); + + return TRUE; } /* this function returns the new width of the column being resized given @@ -7582,14 +8494,15 @@ gtk_tree_view_new_column_width (GtkTreeView *tree_view, { GtkTreeViewColumn *column; gint width; + gboolean rtl; /* first translate the x position from widget->window * to clist->clist_window */ - + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); column = g_list_nth (tree_view->priv->columns, i)->data; - width = *x - column->button->allocation.x; - + width = rtl ? (column->button->allocation.x + column->button->allocation.width - *x) : (*x - column->button->allocation.x); + /* Clamp down the value */ if (column->min_width == -1) width = MAX (column->button->requisition.width, @@ -7599,8 +8512,9 @@ gtk_tree_view_new_column_width (GtkTreeView *tree_view, width); if (column->max_width != -1) width = MIN (width, column->max_width != -1); - *x = column->button->allocation.x + width; + *x = rtl ? (column->button->allocation.x + column->button->allocation.width - width) : (column->button->allocation.x + width); + return width; } @@ -7621,14 +8535,16 @@ gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, - tree_view->priv->hadjustment->value, 0); dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value; + if (dy && tree_view->priv->edited_column) + { + if (GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget)) + GTK_WIDGET (tree_view->priv->edited_column->editable_widget)->allocation.y += dy; + } gdk_window_scroll (tree_view->priv->bin_window, 0, dy); /* update our dy and top_row */ tree_view->priv->dy = (int) tree_view->priv->vadjustment->value; gtk_tree_view_dy_to_top_row (tree_view); - gdk_window_process_updates (tree_view->priv->bin_window, TRUE); - gdk_window_process_updates (tree_view->priv->header_window, TRUE); - } } @@ -7647,11 +8563,7 @@ gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, GtkWidget * gtk_tree_view_new (void) { - GtkTreeView *tree_view; - - tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ())); - - return GTK_WIDGET (tree_view); + return g_object_new (GTK_TYPE_TREE_VIEW, NULL); } /** @@ -7665,12 +8577,7 @@ gtk_tree_view_new (void) GtkWidget * gtk_tree_view_new_with_model (GtkTreeModel *model) { - GtkTreeView *tree_view; - - tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ())); - gtk_tree_view_set_model (tree_view, model); - - return GTK_WIDGET (tree_view); + return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL); } /* Public Accessors @@ -7713,18 +8620,30 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, if (tree_view->priv->model) { + GList *tmplist = tree_view->priv->columns; + gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree); - g_signal_handlers_disconnect_by_func (G_OBJECT (tree_view->priv->model), - (gpointer) gtk_tree_view_row_changed, tree_view); - g_signal_handlers_disconnect_by_func (G_OBJECT (tree_view->priv->model), - (gpointer) gtk_tree_view_row_inserted, tree_view); - g_signal_handlers_disconnect_by_func (G_OBJECT (tree_view->priv->model), - (gpointer) gtk_tree_view_row_has_child_toggled, tree_view); - g_signal_handlers_disconnect_by_func (G_OBJECT (tree_view->priv->model), - (gpointer) gtk_tree_view_row_deleted, tree_view); - g_signal_handlers_disconnect_by_func (G_OBJECT (tree_view->priv->model), - (gpointer) gtk_tree_view_rows_reordered, tree_view); + g_signal_handlers_disconnect_by_func (tree_view->priv->model, + gtk_tree_view_row_changed, + tree_view); + g_signal_handlers_disconnect_by_func (tree_view->priv->model, + gtk_tree_view_row_inserted, + tree_view); + g_signal_handlers_disconnect_by_func (tree_view->priv->model, + gtk_tree_view_row_has_child_toggled, + tree_view); + g_signal_handlers_disconnect_by_func (tree_view->priv->model, + gtk_tree_view_row_deleted, + tree_view); + g_signal_handlers_disconnect_by_func (tree_view->priv->model, + gtk_tree_view_rows_reordered, + tree_view); + + for (; tmplist; tmplist = tmplist->next) + _gtk_tree_view_column_unset_model (tmplist->data, + tree_view->priv->model); + if (tree_view->priv->tree) { _gtk_rbtree_free (tree_view->priv->tree); @@ -7742,11 +8661,23 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, tree_view->priv->cursor = NULL; gtk_tree_row_reference_free (tree_view->priv->anchor); tree_view->priv->anchor = NULL; + gtk_tree_row_reference_free (tree_view->priv->top_row); + tree_view->priv->top_row = NULL; + gtk_tree_row_reference_free (tree_view->priv->last_button_press); + tree_view->priv->last_button_press = NULL; + gtk_tree_row_reference_free (tree_view->priv->last_button_press_2); + tree_view->priv->last_button_press_2 = NULL; + gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); + tree_view->priv->scroll_to_path = NULL; + + tree_view->priv->scroll_to_column = NULL; g_object_unref (tree_view->priv->model); + tree_view->priv->search_column = -1; - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); tree_view->priv->fixed_height_check = 0; + tree_view->priv->fixed_height = -1; + tree_view->priv->dy = tree_view->priv->top_row_dy = 0; } tree_view->priv->model = model; @@ -7757,19 +8688,22 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, gint i; GtkTreePath *path; GtkTreeIter iter; - + GtkTreeModelFlags flags; if (tree_view->priv->search_column == -1) { for (i = 0; i < gtk_tree_model_get_n_columns (model); i++) { - if (gtk_tree_model_get_column_type (model, i) == G_TYPE_STRING) + GType type = gtk_tree_model_get_column_type (model, i); + + if (g_value_type_transformable (type, G_TYPE_STRING)) { tree_view->priv->search_column = i; break; } } } + g_object_ref (tree_view->priv->model); g_signal_connect (tree_view->priv->model, "row_changed", @@ -7792,6 +8726,12 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, G_CALLBACK (gtk_tree_view_rows_reordered), tree_view); + flags = gtk_tree_model_get_flags (tree_view->priv->model); + if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); + else + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); + path = gtk_tree_path_new_first (); if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path)) { @@ -7978,7 +8918,7 @@ gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2; tree_view->priv->vadjustment->lower = 0; tree_view->priv->vadjustment->upper = tree_view->priv->height; - gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed"); + gtk_adjustment_changed (tree_view->priv->vadjustment); gtk_widget_queue_resize (GTK_WIDGET (tree_view)); @@ -8097,7 +9037,9 @@ gtk_tree_view_get_rules_hint (GtkTreeView *tree_view) * @tree_view: A #GtkTreeView. * @column: The #GtkTreeViewColumn to add. * - * Appends @column to the list of columns. + * Appends @column to the list of columns. If @tree_view has "fixed_height" + * mode enbabled, then @column must have its "sizing" property set to be + * GTK_TREE_VIEW_COLUMN_FIXED. * * Return value: The number of columns in @tree_view after appending. **/ @@ -8130,11 +9072,23 @@ gtk_tree_view_remove_column (GtkTreeView *tree_view, g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1); - _gtk_tree_view_column_unset_tree_view (column); - if (tree_view->priv->focus_column == column) tree_view->priv->focus_column = NULL; + if (tree_view->priv->edited_column == column) + { + gtk_tree_view_stop_editing (tree_view, TRUE); + + /* no need to, but just to be sure ... */ + tree_view->priv->edited_column = NULL; + } + + g_signal_handlers_disconnect_by_func (column, + G_CALLBACK (column_sizing_notify), + tree_view); + + _gtk_tree_view_column_unset_tree_view (column); + tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column); tree_view->priv->n_columns--; @@ -8159,8 +9113,8 @@ gtk_tree_view_remove_column (GtkTreeView *tree_view, gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } - g_object_unref (G_OBJECT (column)); - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLUMNS_CHANGED], 0); + g_object_unref (column); + g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); return tree_view->priv->n_columns; } @@ -8172,7 +9126,9 @@ gtk_tree_view_remove_column (GtkTreeView *tree_view, * @position: The position to insert @column in. * * This inserts the @column into the @tree_view at @position. If @position is - * -1, then the column is inserted at the end. + * -1, then the column is inserted at the end. If @tree_view has + * "fixed_height" mode enabled, then @column must have its "sizing" property + * set to be GTK_TREE_VIEW_COLUMN_FIXED. * * Return value: The number of columns in @tree_view after insertion. **/ @@ -8185,7 +9141,11 @@ gtk_tree_view_insert_column (GtkTreeView *tree_view, g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); g_return_val_if_fail (column->tree_view == NULL, -1); - g_object_ref (G_OBJECT (column)); + if (tree_view->priv->fixed_height_mode) + 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)); if (tree_view->priv->n_columns == 0 && @@ -8195,6 +9155,9 @@ gtk_tree_view_insert_column (GtkTreeView *tree_view, gdk_window_show (tree_view->priv->header_window); } + g_signal_connect (column, "notify::sizing", + G_CALLBACK (column_sizing_notify), tree_view); + tree_view->priv->columns = g_list_insert (tree_view->priv->columns, column, position); tree_view->priv->n_columns++; @@ -8216,7 +9179,7 @@ gtk_tree_view_insert_column (GtkTreeView *tree_view, gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLUMNS_CHANGED], 0); + g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); return tree_view->priv->n_columns; } @@ -8231,7 +9194,9 @@ gtk_tree_view_insert_column (GtkTreeView *tree_view, * * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at * @position. If @position is -1, then the newly created column is inserted at - * the end. The column is initialized with the attributes given. + * the end. The column is initialized with the attributes given. If @tree_view + * has "fixed_height" mode enabled, then @column must have its sizing + * property set to be GTK_TREE_VIEW_COLUMN_FIXED. * * Return value: The number of columns in @tree_view after insertion. **/ @@ -8286,6 +9251,8 @@ gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, * with the given cell renderer and a #GtkCellDataFunc to set cell renderer * attributes (normally using data from the model). See also * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start(). + * If @tree_view has "fixed_height" mode enabled, then @column must have its + * "sizing" property set to be GTK_TREE_VIEW_COLUMN_FIXED. * * Return value: number of columns in the tree view post-insert **/ @@ -8409,7 +9376,7 @@ gtk_tree_view_move_column_after (GtkTreeView *tree_view, gtk_tree_view_size_allocate_columns (GTK_WIDGET (tree_view)); } - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLUMNS_CHANGED], 0); + g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0); } /** @@ -8556,7 +9523,7 @@ gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, * * If @use_align is %FALSE, then the alignment arguments are ignored, and the * tree does the minimum amount of work to scroll the cell onto the screen. - * This means that the cell will be scrolled to the edge closest to it's current + * This means that the cell will be scrolled to the edge closest to its current * position. If the cell is currently visible on the screen, nothing is done. * * This function only works if the model is set, and @path is a valid row on the @@ -8672,10 +9639,39 @@ gtk_tree_view_row_activated (GtkTreeView *tree_view, { g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_signal_emit (G_OBJECT(tree_view), tree_view_signals[ROW_ACTIVATED], 0, path, column); + g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column); } +static void +gtk_tree_view_expand_all_emission_helper (GtkRBTree *tree, + GtkRBNode *node, + gpointer data) +{ + GtkTreeView *tree_view = data; + + if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && + node->children) + { + GtkTreePath *path; + GtkTreeIter iter; + + path = _gtk_tree_view_find_path (tree_view, tree, node); + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); + + g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); + + gtk_tree_path_free (path); + } + + if (node->children) + _gtk_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + gtk_tree_view_expand_all_emission_helper, + tree_view); +} + static void gtk_tree_view_expand_all_helper (GtkRBTree *tree, GtkRBNode *node, @@ -8706,6 +9702,13 @@ gtk_tree_view_expand_all_helper (GtkRBTree *tree, &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); } } @@ -8735,14 +9738,25 @@ gtk_tree_view_expand_all (GtkTreeView *tree_view) static gboolean expand_collapse_timeout (gpointer data) { - GtkTreeView *tree_view = data; + gboolean retval; + + GDK_THREADS_ENTER (); + + retval = do_expand_collapse (data); + + GDK_THREADS_LEAVE (); + + return retval; +} + +static gboolean +do_expand_collapse (GtkTreeView *tree_view) +{ GtkRBNode *node; GtkRBTree *tree; gboolean expanding; gboolean redraw; - GDK_THREADS_ENTER (); - redraw = FALSE; expanding = TRUE; @@ -8791,13 +9805,9 @@ expand_collapse_timeout (gpointer data) { gtk_tree_view_queue_draw_arrow (tree_view, tree, node, NULL); - GDK_THREADS_LEAVE (); - return TRUE; } - GDK_THREADS_LEAVE (); - return FALSE; } @@ -8813,7 +9823,7 @@ gtk_tree_view_collapse_all (GtkTreeView *tree_view) GtkRBTree *tree; GtkRBNode *node; GtkTreePath *path; - guint *indices; + gint *indices; g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); @@ -8847,6 +9857,8 @@ gtk_tree_view_collapse_all (GtkTreeView *tree_view) * * Expands the row at @path. This will also expand all parent rows of * @path as necessary. + * + * Since: 2.2 **/ void gtk_tree_view_expand_to_path (GtkTreeView *tree_view, @@ -8867,11 +9879,11 @@ gtk_tree_view_expand_to_path (GtkTreeView *tree_view, for (i = 0; i < depth; i++) { - gtk_tree_path_append_index (path, indices[i]); - gtk_tree_view_expand_row (tree_view, path, FALSE); + gtk_tree_path_append_index (tmp, indices[i]); + gtk_tree_view_expand_row (tree_view, tmp, FALSE); } - gtk_tree_path_free (path); + gtk_tree_path_free (tmp); } /* FIXME the bool return values for expand_row and collapse_row are @@ -8932,7 +9944,7 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view, return retval; } - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand); + g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand); if (expand) return FALSE; @@ -8951,7 +9963,7 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view, if (tree_view->priv->expand_collapse_timeout) { - gtk_timeout_remove (tree_view->priv->expand_collapse_timeout); + g_source_remove (tree_view->priv->expand_collapse_timeout); tree_view->priv->expand_collapse_timeout = 0; } @@ -8965,7 +9977,7 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view, if (animate) { - tree_view->priv->expand_collapse_timeout = gtk_timeout_add (50, expand_collapse_timeout, tree_view); + tree_view->priv->expand_collapse_timeout = g_timeout_add (50, expand_collapse_timeout, tree_view); tree_view->priv->expanded_collapsed_node = node; tree_view->priv->expanded_collapsed_tree = tree; @@ -8974,7 +9986,7 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view, install_presize_handler (tree_view); - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[ROW_EXPANDED], 0, &iter, path); + g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); return TRUE; } @@ -9025,14 +10037,15 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, gboolean collapse; gint x, y; GList *list; - GdkScreen *screen; + GdkDisplay *display; + GdkWindow *child, *parent; if (node->children == NULL) return FALSE; gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse); + g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse); if (collapse) return FALSE; @@ -9133,14 +10146,14 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, if (gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children)) { _gtk_rbtree_remove (node->children); - g_signal_emit_by_name (G_OBJECT (tree_view->priv->selection), "changed", 0); + g_signal_emit_by_name (tree_view->priv->selection, "changed"); } else _gtk_rbtree_remove (node->children); if (tree_view->priv->expand_collapse_timeout) { - gtk_timeout_remove (tree_view->priv->expand_collapse_timeout); + g_source_remove (tree_view->priv->expand_collapse_timeout); tree_view->priv->expand_collapse_timeout = 0; } @@ -9154,7 +10167,7 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, if (animate) { - tree_view->priv->expand_collapse_timeout = gtk_timeout_add (50, expand_collapse_timeout, tree_view); + tree_view->priv->expand_collapse_timeout = g_timeout_add (50, expand_collapse_timeout, tree_view); tree_view->priv->expanded_collapsed_node = node; tree_view->priv->expanded_collapsed_tree = tree; @@ -9166,24 +10179,35 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[ROW_COLLAPSED], 0, &iter, path); + g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path); - /* 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. */ - - screen = gdk_drawable_get_screen (tree_view->priv->bin_window); - if (gdk_screen_get_window_at_pointer (screen, &x, &y) == tree_view->priv->bin_window) + if (GTK_WIDGET_MAPPED (tree_view)) { - GdkEventMotion event; - event.window = tree_view->priv->bin_window; - event.x = x; - event.y = y; + /* 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); + + if (gdk_window_get_pointer (parent, &x, &y, NULL) == child) + { + GdkEventMotion event; + gint child_x, child_y; + + gdk_window_get_position (child, &child_x, &child_y); - /* despite the fact this isn't a real event, I'm almost positive it will - * never trigger a drag event. maybe_drag is the only function that uses - * more than just event.x and event.y. */ - gtk_tree_view_motion_bin_window (GTK_WIDGET (tree_view), &event); + event.window = tree_view->priv->bin_window; + event.x = x - child_x; + event.y = y - child_y; + + /* despite the fact this isn't a real event, I'm almost positive it will + * never trigger a drag event. maybe_drag is the only function that uses + * more than just event.x and event.y. */ + gtk_tree_view_motion_bin_window (GTK_WIDGET (tree_view), &event); + } } + return TRUE; } @@ -9304,7 +10328,7 @@ gtk_tree_view_row_expanded (GtkTreeView *tree_view, return (node->children != NULL); } -static GtkTargetEntry row_targets[] = { +static const GtkTargetEntry row_targets[] = { { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 } }; @@ -9336,10 +10360,10 @@ gtk_tree_view_get_reorderable (GtkTreeView *tree_view) * #GtkTreeStore and #GtkListStore support these. If @reorderable is %TRUE, then * the user can reorder the model by dragging and dropping rows. The * developer can listen to these changes by connecting to the model's - * signals. + * row_inserted and row_deleted signals. * * This function does not give you any degree of control over the order -- any - * reorderering is allowed. If more control is needed, you should probably + * reordering is allowed. If more control is needed, you should probably * handle drag and drop manually. **/ void @@ -9384,7 +10408,6 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, { GtkRBTree *tree = NULL; GtkRBNode *node = NULL; - GdkModifierType state = 0; if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) { @@ -9395,7 +10418,6 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, } gtk_tree_row_reference_free (tree_view->priv->cursor); - gtk_get_current_event_state (&state); tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, @@ -9403,10 +10425,32 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, _gtk_tree_view_find_node (tree_view, path, &tree, &node); if (tree != NULL) { - if (clear_and_select && !((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)) - _gtk_tree_selection_internal_select_node (tree_view->priv->selection, - node, tree, path, - state, FALSE); + GtkRBTree *new_tree = NULL; + GtkRBNode *new_node = NULL; + + if (clear_and_select && !tree_view->priv->ctrl_pressed) + { + GtkTreeSelectMode mode = 0; + + if (tree_view->priv->ctrl_pressed) + mode |= GTK_TREE_SELECT_MODE_TOGGLE; + if (tree_view->priv->shift_pressed) + mode |= GTK_TREE_SELECT_MODE_EXTEND; + + _gtk_tree_selection_internal_select_node (tree_view->priv->selection, + node, tree, path, mode, + FALSE); + } + + /* We have to re-find tree and node here again, somebody might have + * cleared the node or the whole tree in the GtkTreeSelection::changed + * callback. If the nodes differ we bail out here. + */ + _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node); + + if (tree != new_tree || node != new_node) + return; + if (clamp_node) { gtk_tree_view_clamp_node_visible (tree_view, tree, node); @@ -9414,7 +10458,7 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, } } - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[CURSOR_CHANGED], 0); + g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); } /** @@ -9426,6 +10470,9 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, * Fills in @path and @focus_column with the current path and focus column. If * the cursor isn't currently set, then *@path will be %NULL. If no column * currently has focus, then *@focus_column will be %NULL. + * + * The returned #GtkTreePath must be freed with gtk_tree_path_free() when + * you are done with it. **/ void gtk_tree_view_get_cursor (GtkTreeView *tree_view, @@ -9457,12 +10504,12 @@ gtk_tree_view_get_cursor (GtkTreeView *tree_view, * * Sets the current keyboard focus to be at @path, and selects it. This is * useful when you want to focus the user's attention on a particular row. If - * @column is not %NULL, then focus is given to the column specified by it. - * Additionally, if @column is specified, and @start_editing is %TRUE, then - * editing should be started in the specified cell. This function is often - * followed by @gtk_widget_grab_focus (@tree_view) in order to give keyboard - * focus to the widget. Please note that editing can only happen when the - * widget is realized. + * @focus_column is not %NULL, then focus is given to the column specified by + * it. Additionally, if @focus_column is specified, and @start_editing is + * %TRUE, then editing should be started in the specified cell. + * This function is often followed by @gtk_widget_grab_focus (@tree_view) + * in order to give keyboard focus to the widget. Please note that editing + * can only happen when the widget is realized. **/ void gtk_tree_view_set_cursor (GtkTreeView *tree_view, @@ -9493,6 +10540,8 @@ gtk_tree_view_set_cursor (GtkTreeView *tree_view, * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the * widget. Please note that editing can only happen when the widget is * realized. + * + * Since: 2.2 **/ void gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, @@ -9511,6 +10560,11 @@ gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell)); } + /* cancel the current editing, if it exists */ + if (tree_view->priv->edited_column && + tree_view->priv->edited_column->editable_widget) + gtk_tree_view_stop_editing (tree_view, TRUE); + gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE); if (focus_column && focus_column->visible) @@ -9598,7 +10652,7 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, if (tree_view->priv->tree == NULL) return FALSE; - if (x > tree_view->priv->hadjustment->page_size) + if (x > tree_view->priv->hadjustment->upper) return FALSE; if (x < 0 || y < 0) @@ -9635,14 +10689,24 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, remaining_x -= tmp_column->width; } + /* If found is FALSE and there is a last_column, then it the remainder + * space is in that area + */ if (!found) { - if (column) - *column = last_column; - - if (cell_x) - *cell_x = last_column->width + remaining_x; - } + if (last_column) + { + if (column) + *column = last_column; + + if (cell_x) + *cell_x = last_column->width + remaining_x; + } + else + { + return FALSE; + } + } } y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, @@ -9666,7 +10730,7 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, * gtk_tree_view_get_cell_area: * @tree_view: a #GtkTreeView * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates - * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes + * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordinates * @rect: rectangle to fill with cell rect * * Fills the bounding rectangle in tree window coordinates for the cell at the @@ -9714,9 +10778,10 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view, if (path) { + gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node); + /* Get vertical coords */ - if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) && - tree == NULL) + if ((!ret && tree == NULL) || ret) return; rect->y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator); @@ -9887,6 +10952,17 @@ unset_reorderable (GtkTreeView *tree_view) } } +/** + * gtk_tree_view_enable_model_drag_source: + * @tree_view: a #GtkTreeView + * @start_button_mask: Mask of allowed buttons to start drag + * @targets: the table of targets that the drag will support + * @n_targets: the number of items in @targets + * @actions: the bitmask of possible actions for a drag from this + * widget + * + * Turns @tree_view into a drag source for automatic DND. + **/ void gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, GdkModifierType start_button_mask, @@ -9898,6 +10974,12 @@ gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + gtk_drag_source_set (GTK_WIDGET (tree_view), + 0, + NULL, + 0, + actions); + di = ensure_info (tree_view); clear_source_info (di); @@ -9910,6 +10992,16 @@ gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, unset_reorderable (tree_view); } +/** + * gtk_tree_view_enable_model_drag_dest: + * @tree_view: a #GtkTreeView + * @targets: the table of targets that the drag will support + * @n_targets: the number of items in @targets + * @actions: the bitmask of possible actions for a drag from this + * widget + * + * Turns @tree_view into a drop destination for automatic DND. + **/ void gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, const GtkTargetEntry *targets, @@ -9937,6 +11029,12 @@ gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, unset_reorderable (tree_view); } +/** + * gtk_tree_view_unset_rows_drag_source: + * @tree_view: a #GtkTreeView + * + * Undoes the effect of gtk_tree_view_enable_model_drag_source(). + **/ void gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) { @@ -9950,6 +11048,7 @@ gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) { if (di->source_set) { + gtk_drag_source_unset (GTK_WIDGET (tree_view)); clear_source_info (di); di->source_set = FALSE; } @@ -9961,6 +11060,12 @@ gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) unset_reorderable (tree_view); } +/** + * gtk_tree_view_unset_rows_drag_dest: + * @tree_view: a #GtkTreeView + * + * Undoes the effect of gtk_tree_view_enable_model_drag_dest(). + **/ void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) { @@ -9986,12 +11091,21 @@ gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) unset_reorderable (tree_view); } +/** + * gtk_tree_view_set_drag_dest_row: + * @tree_view: a #GtkTreeView + * @path: The path of the row to highlight, or %NULL. + * @pos: Specifies whether to drop before, after or into the row + * + * Sets the row that is highlighted for feedback. + **/ void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewDropPosition pos) { GtkTreePath *current_dest; + /* Note; this function is exported to allow a custom DND * implementation, so it can't touch TreeViewDragInfo */ @@ -10001,10 +11115,26 @@ gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, current_dest = NULL; if (tree_view->priv->drag_dest_row) - current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + { + current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); + } - if (tree_view->priv->drag_dest_row) - gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); + /* special case a drop on an empty model */ + tree_view->priv->empty_view_drop = 0; + + if (pos == GTK_TREE_VIEW_DROP_BEFORE && path + && gtk_tree_path_get_depth (path) == 1 + && gtk_tree_path_get_indices (path)[0] == 0) + { + gint n_children; + + n_children = gtk_tree_model_iter_n_children (tree_view->priv->model, + NULL); + + if (!n_children) + tree_view->priv->empty_view_drop = 1; + } tree_view->priv->drag_dest_pos = pos; @@ -10039,6 +11169,14 @@ gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, } } +/** + * gtk_tree_view_get_drag_dest_row: + * @tree_view: a #GtkTreeView + * @path: Return location for the path of the highlighted row, or %NULL. + * @pos: Return location for the drop position, or %NULL + * + * Gets information about the row that is highlighted for feedback. + **/ void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, GtkTreePath **path, @@ -10051,13 +11189,30 @@ gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, if (tree_view->priv->drag_dest_row) *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); else - *path = NULL; + { + if (tree_view->priv->empty_view_drop) + *path = gtk_tree_path_new_from_indices (0, -1); + else + *path = NULL; + } } if (pos) *pos = tree_view->priv->drag_dest_pos; } +/** + * gtk_tree_view_get_dest_row_at_pos: + * @tree_view: a #GtkTreeView + * @drag_x: the position to determine the destination row for + * @drag_y: the position to determine the destination row for + * @path: Return location for the path of the highlighted row, or %NULL. + * @pos: Return location for the drop position, or %NULL + * + * Determines the destination row for a given position. + * + * Return value: whether there is a row at the given postiion, + **/ gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, gint drag_x, @@ -10353,10 +11508,11 @@ gtk_tree_view_get_search_column (GtkTreeView *tree_view) /** * gtk_tree_view_set_search_column: * @tree_view: A #GtkTreeView - * @column: the column to search in + * @column: the column of the model to search in * - * Sets @column as the column where the interactive search code should search - * in. Additionally, turns on interactive 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. */ void gtk_tree_view_set_search_column (GtkTreeView *tree_view, @@ -10396,7 +11552,9 @@ gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view) * @search_user_data: user data to pass to @search_equal_func, or %NULL * @search_destroy: Destroy notifier for @search_user_data, or %NULL * - * Sets the compare function for the interactive search capabilities. + * Sets the compare function for the interactive search capabilities; note + * that somewhat like strcmp() returning 0 for equality + * #GtkTreeViewSearchEqualFunc returns %FALSE on matches. **/ void gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, @@ -10418,36 +11576,15 @@ gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, } static void -gtk_tree_view_search_dialog_destroy (GtkWidget *search_dialog, +gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog, GtkTreeView *tree_view) { - GtkEntry *entry = (GtkEntry *)(gtk_container_get_children (GTK_CONTAINER (search_dialog)))->data; - gint *selected_iter; - if (tree_view->priv->disable_popdown) return; - if (entry) - { - GdkEventFocus focus_event; - - focus_event.type = GDK_FOCUS_CHANGE; - focus_event.in = FALSE; - gtk_widget_event (GTK_WIDGET (entry), (GdkEvent *) &focus_event); - } - - /* remove data from tree_view */ - g_object_set_data (G_OBJECT (tree_view), GTK_TREE_VIEW_SEARCH_DIALOG_KEY, - NULL); - - selected_iter = g_object_get_data (G_OBJECT (search_dialog), - "gtk-tree-view-selected-iter"); - if (selected_iter) - g_free (selected_iter); - g_object_set_data (G_OBJECT (search_dialog), "gtk-tree-view-selected-iter", - NULL); - - gtk_widget_destroy (search_dialog); + /* send focus-in event */ + send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE); + gtk_widget_hide (search_dialog); } static void @@ -10464,9 +11601,9 @@ gtk_tree_view_search_position_func (GtkTreeView *tree_view, gtk_widget_realize (search_dialog); gdk_window_get_origin (tree_window, &tree_x, &tree_y); - gdk_window_get_size (tree_window, - &tree_width, - &tree_height); + gdk_drawable_get_size (tree_window, + &tree_width, + &tree_height); gtk_widget_size_request (search_dialog, &requisition); if (tree_x + tree_width - requisition.width > gdk_screen_get_width (screen)) @@ -10494,7 +11631,7 @@ gtk_tree_view_search_disable_popdown (GtkEntry *entry, GtkTreeView *tree_view = (GtkTreeView *)data; tree_view->priv->disable_popdown = 1; - g_signal_connect (G_OBJECT (menu), "hide", + g_signal_connect (menu, "hide", G_CALLBACK (gtk_tree_view_search_enable_popdown), data); } @@ -10526,7 +11663,7 @@ gtk_tree_view_search_delete_event (GtkWidget *widget, { g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - gtk_tree_view_search_dialog_destroy (widget, tree_view); + gtk_tree_view_search_dialog_hide (widget, tree_view); return TRUE; } @@ -10538,7 +11675,7 @@ gtk_tree_view_search_button_press_event (GtkWidget *widget, { g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); - gtk_tree_view_search_dialog_destroy (widget, tree_view); + gtk_tree_view_search_dialog_hide (widget, tree_view); return TRUE; } @@ -10556,19 +11693,19 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget, event->keyval == GDK_Return || event->keyval == GDK_Tab) { - gtk_tree_view_search_dialog_destroy (widget, tree_view); + gtk_tree_view_search_dialog_hide (widget, tree_view); return TRUE; } /* select previous matching iter */ - if (event->keyval == GDK_Up) + if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up) { gtk_tree_view_search_move (widget, tree_view, TRUE); return TRUE; } /* select next matching iter */ - if (event->keyval == GDK_Down) + if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down) { gtk_tree_view_search_move (widget, tree_view, FALSE); return TRUE; @@ -10583,20 +11720,18 @@ gtk_tree_view_search_move (GtkWidget *window, gboolean up) { gboolean ret; - gint *selected_iter; gint len; gint count = 0; - gchar *text; + const gchar *text; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *selection; - text = g_object_get_data (G_OBJECT (window), "gtk-tree-view-text"); - selected_iter = g_object_get_data (G_OBJECT (window), "gtk-tree-view-selected-iter"); + text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)); g_return_if_fail (text != NULL); - if (!selected_iter || (up && *selected_iter == 1)) + if (up && tree_view->priv->selected_iter == 1) return; len = strlen (text); @@ -10612,12 +11747,12 @@ gtk_tree_view_search_move (GtkWidget *window, gtk_tree_model_get_iter_first (model, &iter); ret = gtk_tree_view_search_iter (model, selection, &iter, text, - &count, up?((*selected_iter) - 1):((*selected_iter + 1))); + &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1))); if (ret) { /* found */ - *selected_iter += up?(-1):(1); + tree_view->priv->selected_iter += up?(-1):(1); } else { @@ -10626,7 +11761,7 @@ gtk_tree_view_search_move (GtkWidget *window, gtk_tree_model_get_iter_first (model, &iter); gtk_tree_view_search_iter (model, selection, &iter, text, - &count, *selected_iter); + &count, tree_view->priv->selected_iter); } } @@ -10638,25 +11773,46 @@ gtk_tree_view_search_equal_func (GtkTreeModel *model, gpointer search_data) { gboolean retval = TRUE; + const gchar *str; gchar *normalized_string; gchar *normalized_key; - gchar *case_normalized_string; - gchar *case_normalized_key; + gchar *case_normalized_string = NULL; + gchar *case_normalized_key = NULL; GValue value = {0,}; - gint key_len; + GValue transformed = {0,}; gtk_tree_model_get_value (model, iter, column, &value); - normalized_string = g_utf8_normalize (g_value_get_string (&value), -1, G_NORMALIZE_ALL); - normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); - case_normalized_string = g_utf8_casefold (normalized_string, -1); - case_normalized_key = g_utf8_casefold (normalized_key, -1); - key_len = strlen (case_normalized_key); + g_value_init (&transformed, G_TYPE_STRING); - if (!strncmp (case_normalized_key, case_normalized_string, key_len)) - retval = FALSE; + if (!g_value_transform (&value, &transformed)) + { + g_value_unset (&value); + return TRUE; + } g_value_unset (&value); + + str = g_value_get_string (&transformed); + if (!str) + { + g_value_unset (&transformed); + return TRUE; + } + + normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL); + normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL); + + if (normalized_string && normalized_key) + { + case_normalized_string = g_utf8_casefold (normalized_string, -1); + case_normalized_key = g_utf8_casefold (normalized_key, -1); + + if (strstr (case_normalized_string, case_normalized_key)) + retval = FALSE; + } + + g_value_unset (&transformed); g_free (normalized_key); g_free (normalized_string); g_free (case_normalized_key); @@ -10770,9 +11926,6 @@ gtk_tree_view_search_iter (GtkTreeModel *model, } while (1); - if (path) - gtk_tree_path_free (path); - return FALSE; } @@ -10781,7 +11934,6 @@ gtk_tree_view_search_init (GtkWidget *entry, GtkTreeView *tree_view) { gint ret; - gint *selected_iter; gint len; gint count = 0; const gchar *text; @@ -10801,10 +11953,6 @@ gtk_tree_view_search_init (GtkWidget *entry, /* search */ gtk_tree_selection_unselect_all (selection); - selected_iter = g_object_get_data (G_OBJECT (window), "gtk-tree-view-selected-iter"); - if (selected_iter) - g_free (selected_iter); - g_object_set_data (G_OBJECT (window), "gtk-tree-view-selected-iter", NULL); if (len < 1) return; @@ -10816,12 +11964,7 @@ gtk_tree_view_search_init (GtkWidget *entry, &count, 1); if (ret) - { - selected_iter = g_malloc (sizeof (int)); - *selected_iter = 1; - g_object_set_data (G_OBJECT (window), "gtk-tree-view-selected-iter", - selected_iter); - } + tree_view->priv->selected_iter = 1; } static void @@ -10834,7 +11977,8 @@ gtk_tree_view_remove_widget (GtkCellEditable *cell_editable, _gtk_tree_view_column_stop_editing (tree_view->priv->edited_column); tree_view->priv->edited_column = NULL; - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + if (GTK_WIDGET_HAS_FOCUS (cell_editable)) + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); gtk_container_remove (GTK_CONTAINER (tree_view), GTK_WIDGET (cell_editable)); @@ -10881,6 +12025,13 @@ gtk_tree_view_start_editing (GtkTreeView *tree_view, cursor_path, tree_view->priv->focus_column, &cell_area); + + if (gtk_tree_view_is_expander_column (tree_view, tree_view->priv->focus_column) && TREE_VIEW_DRAW_EXPANDERS (tree_view)) + { + cell_area.x += tree_view->priv->expander_size; + cell_area.width -= tree_view->priv->expander_size; + } + if (_gtk_tree_view_column_cell_event (tree_view->priv->focus_column, &editable_widget, NULL, @@ -10942,7 +12093,8 @@ gtk_tree_view_real_start_editing (GtkTreeView *tree_view, gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable), (GdkEvent *)event); gtk_widget_grab_focus (GTK_WIDGET (cell_editable)); - g_signal_connect (cell_editable, "remove_widget", G_CALLBACK (gtk_tree_view_remove_widget), tree_view); + g_signal_connect (cell_editable, "remove_widget", + G_CALLBACK (gtk_tree_view_remove_widget), tree_view); } static void