2 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include <gdk/gdkkeysyms.h>
26 #include "gtktreeview.h"
27 #include "gtkrbtree.h"
28 #include "gtktreednd.h"
29 #include "gtktreeprivate.h"
30 #include "gtkcellrenderer.h"
32 #include "gtkmarshalers.h"
33 #include "gtkbuildable.h"
34 #include "gtkbutton.h"
35 #include "gtkalignment.h"
41 #include "gtkbindings.h"
42 #include "gtkcontainer.h"
45 #include "gtktreemodelsort.h"
46 #include "gtktooltip.h"
47 #include "gtkscrollable.h"
48 #include "gtkcelllayout.h"
49 #include "gtkprivate.h"
50 #include "gtkwidgetprivate.h"
51 #include "gtkentryprivate.h"
56 * @Short_description: A widget for displaying both trees and lists
58 * @See_also: #GtkTreeViewColumn, #GtkTreeSelection, #GtkTreeDnd, #GtkTreeMode,
59 * #GtkTreeSortable, #GtkTreeModelSort, #GtkListStore, #GtkTreeStore,
60 * #GtkCellRenderer, #GtkCellEditable, #GtkCellRendererPixbuf,
61 * #GtkCellRendererText, #GtkCellRendererToggle
63 * Widget that displays any object that implements the #GtkTreeModel interface.
65 * Please refer to the <link linkend="TreeWidget">tree widget conceptual
66 * overview</link> for an overview of all the objects and data types related
67 * to the tree widget and how they work together.
69 * Several different coordinate systems are exposed in the GtkTreeView API.
72 * <inlinegraphic fileref="tree-view-coordinates.png" format="PNG"></inlinegraphic>
73 * <variablelist><title>Coordinate systems in GtkTreeView API</title>
74 * <varlistentry><term>Widget coordinates</term>
77 * Coordinates relative to the widget (usually <literal>widget->window</literal>).
81 * <varlistentry><term>Bin window coordinates</term>
84 * Coordinates relative to the window that GtkTreeView renders to.
88 * <varlistentry><term>Tree coordinates</term>
91 * Coordinates relative to the entire scrollable area of GtkTreeView. These
92 * coordinates start at (0, 0) for row 0 of the tree.
98 * Several functions are available for converting between the different
99 * coordinate systems. The most common translations are between widget and bin
100 * window coordinates and between bin window and tree coordinates. For the
101 * former you can use gtk_tree_view_convert_widget_to_bin_window_coords()
102 * (and vice versa), for the latter gtk_tree_view_convert_bin_window_to_tree_coords()
105 * <refsect2 id="GtkTreeView-BUILDER-UI">
106 * <title>GtkTreeView as GtkBuildable</title>
107 * The GtkTreeView implementation of the GtkBuildable interface accepts
108 * #GtkTreeViewColumn objects as <child> elements and exposes the
109 * internal #GtkTreeSelection in UI definitions.
111 * <title>A UI definition fragment with GtkTreeView</title>
112 * <programlisting><![CDATA[
113 * <object class="GtkTreeView" id="treeview">
114 * <property name="model">liststore1</property>
116 * <object class="GtkTreeViewColumn" id="test-column">
117 * <property name="title">Test</property>
119 * <object class="GtkCellRendererText" id="test-renderer"/>
121 * <attribute name="text">1</attribute>
126 * <child internal-child="selection">
127 * <object class="GtkTreeSelection" id="selection">
128 * <signal name="changed" handler="on_treeview_selection_changed"/>
132 * ]]></programlisting>
139 DRAG_COLUMN_WINDOW_STATE_UNSET = 0,
140 DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1,
141 DRAG_COLUMN_WINDOW_STATE_ARROW = 2,
142 DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3,
143 DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4
149 RUBBER_BAND_MAYBE_START = 1,
150 RUBBER_BAND_ACTIVE = 2
153 /* This lovely little value is used to determine how far away from the title bar
154 * you can move the mouse and still have a column drag work.
156 #define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*gtk_tree_view_get_effective_header_height(tree_view))
160 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
163 g_log (G_LOG_DOMAIN, \
164 G_LOG_LEVEL_CRITICAL, \
165 "%s (%s): assertion `%s' failed.\n" \
166 "There is a disparity between the internal view of the GtkTreeView,\n" \
167 "and the GtkTreeModel. This generally means that the model has changed\n"\
168 "without letting the view know. Any display from now on is likely to\n" \
176 #define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
179 g_log (G_LOG_DOMAIN, \
180 G_LOG_LEVEL_CRITICAL, \
181 "%s (%s): assertion `%s' failed.\n" \
182 "There is a disparity between the internal view of the GtkTreeView,\n" \
183 "and the GtkTreeModel. This generally means that the model has changed\n"\
184 "without letting the view know. Any display from now on is likely to\n" \
194 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
197 g_log (G_LOG_DOMAIN, \
198 G_LOG_LEVEL_CRITICAL, \
199 "file %s: line %d: assertion `%s' failed.\n" \
200 "There is a disparity between the internal view of the GtkTreeView,\n" \
201 "and the GtkTreeModel. This generally means that the model has changed\n"\
202 "without letting the view know. Any display from now on is likely to\n" \
210 #define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
213 g_log (G_LOG_DOMAIN, \
214 G_LOG_LEVEL_CRITICAL, \
215 "file %s: line %d: assertion '%s' failed.\n" \
216 "There is a disparity between the internal view of the GtkTreeView,\n" \
217 "and the GtkTreeModel. This generally means that the model has changed\n"\
218 "without letting the view know. Any display from now on is likely to\n" \
227 #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
228 #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2)
229 #define GTK_TREE_VIEW_TIME_MS_PER_IDLE 30
230 #define SCROLL_EDGE_SIZE 15
231 #define EXPANDER_EXTRA_PADDING 4
232 #define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000
233 #define AUTO_EXPAND_TIMEOUT 500
235 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
238 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
239 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
241 typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
242 struct _GtkTreeViewColumnReorder
246 GtkTreeViewColumn *left_column;
247 GtkTreeViewColumn *right_column;
250 typedef struct _GtkTreeViewChild GtkTreeViewChild;
251 struct _GtkTreeViewChild
261 typedef struct _TreeViewDragInfo TreeViewDragInfo;
262 struct _TreeViewDragInfo
264 GdkModifierType start_button_mask;
265 GtkTargetList *_unused_source_target_list;
266 GdkDragAction source_actions;
268 GtkTargetList *_unused_dest_target_list;
270 guint source_set : 1;
275 struct _GtkTreeViewPrivate
279 /* tree information */
288 GtkAdjustment *hadjustment;
289 GtkAdjustment *vadjustment;
290 gint min_display_width;
291 gint min_display_height;
294 GdkWindow *bin_window;
295 GdkWindow *header_window;
297 /* Scroll position state keeping */
298 GtkTreeRowReference *top_row;
300 /* dy == y pos of top_row + top_row_dy */
301 /* we cache it for simplicity of the code */
304 guint presize_handler_timer;
305 guint validate_rows_timer;
306 guint scroll_sync_timer;
308 /* Indentation and expander layout */
310 GtkTreeViewColumn *expander_column;
312 gint level_indentation;
314 /* Key navigation (focus), selection */
317 GtkTreeRowReference *anchor;
318 GtkTreeRowReference *cursor;
320 GtkTreeViewColumn *focus_column;
322 /* Current pressed node, previously pressed, prelight */
323 GtkRBNode *button_pressed_node;
324 GtkRBTree *button_pressed_tree;
333 guint last_button_time;
337 GtkRBNode *prelight_node;
338 GtkRBTree *prelight_tree;
341 GtkTreeViewColumn *edited_column;
343 /* The node that's currently being collapsed or expanded */
344 GtkRBNode *expanded_collapsed_node;
345 GtkRBTree *expanded_collapsed_tree;
346 guint expand_collapse_timeout;
348 /* Auto expand/collapse timeout in hover mode */
349 guint auto_expand_timeout;
351 /* Selection information */
352 GtkTreeSelection *selection;
354 /* Header information */
359 GtkTreeViewColumnDropFunc column_drop_func;
360 gpointer column_drop_func_data;
361 GDestroyNotify column_drop_func_data_destroy;
362 GList *column_drag_info;
363 GtkTreeViewColumnReorder *cur_reorder;
365 gint prev_width_before_expander;
367 /* Interactive Header reordering */
368 GdkWindow *drag_window;
369 GdkWindow *drag_highlight_window;
370 GtkTreeViewColumn *drag_column;
373 /* Interactive Header Resizing */
377 /* Non-interactive Header Resizing, expand flag support */
380 gint last_extra_space;
381 gint last_extra_space_per_column;
382 gint last_number_of_expand_columns;
385 GtkTreeDestroyCountFunc destroy_count_func;
386 gpointer destroy_count_data;
387 GDestroyNotify destroy_count_destroy;
389 /* Scroll timeout (e.g. during dnd, rubber banding) */
390 guint scroll_timeout;
392 /* Row drag-and-drop */
393 GtkTreeRowReference *drag_dest_row;
394 GtkTreeViewDropPosition drag_dest_pos;
395 guint open_dest_timeout;
398 gint rubber_band_status;
401 gint rubber_band_shift;
402 gint rubber_band_ctrl;
404 GtkRBNode *rubber_band_start_node;
405 GtkRBTree *rubber_band_start_tree;
407 GtkRBNode *rubber_band_end_node;
408 GtkRBTree *rubber_band_end_tree;
413 /* Scroll-to functionality when unrealized */
414 GtkTreeRowReference *scroll_to_path;
415 GtkTreeViewColumn *scroll_to_column;
416 gfloat scroll_to_row_align;
417 gfloat scroll_to_col_align;
419 /* Interactive search */
422 GtkTreeViewSearchPositionFunc search_position_func;
423 GtkTreeViewSearchEqualFunc search_equal_func;
424 gpointer search_user_data;
425 GDestroyNotify search_destroy;
426 gpointer search_position_user_data;
427 GDestroyNotify search_position_destroy;
428 GtkWidget *search_window;
429 GtkWidget *search_entry;
430 gulong search_entry_changed_id;
431 guint typeselect_flush_timeout;
433 /* Grid and tree lines */
434 GtkTreeViewGridLines grid_lines;
435 double grid_line_dashes[2];
438 gboolean tree_lines_enabled;
439 double tree_line_dashes[2];
443 GtkTreeViewRowSeparatorFunc row_separator_func;
444 gpointer row_separator_data;
445 GDestroyNotify row_separator_destroy;
447 /* Tooltip support */
450 /* Here comes the bitfield */
451 guint scroll_to_use_align : 1;
453 guint fixed_height_mode : 1;
454 guint fixed_height_check : 1;
456 guint reorderable : 1;
457 guint header_has_focus : 1;
458 guint drag_column_window_state : 3;
459 /* hint to display rows in alternating colors */
461 guint mark_rows_col_dirty : 1;
464 guint empty_view_drop : 1;
466 guint ctrl_pressed : 1;
467 guint shift_pressed : 1;
469 guint init_hadjust_value : 1;
471 guint in_top_row_to_dy : 1;
473 /* interactive search */
474 guint enable_search : 1;
475 guint disable_popdown : 1;
476 guint search_custom_entry_set : 1;
478 guint hover_selection : 1;
479 guint hover_expand : 1;
480 guint imcontext_changed : 1;
482 guint rubber_banding_enable : 1;
486 guint post_validation_flag : 1;
488 /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
489 guint search_entry_avoid_unhandled_binding : 1;
491 /* GtkScrollablePolicy needs to be checked when
492 * driving the scrollable adjustment values */
493 guint hscroll_policy : 1;
494 guint vscroll_policy : 1;
496 /* GtkTreeView flags */
498 guint show_expanders : 1;
499 guint in_column_resize : 1;
500 guint arrow_prelit : 1;
501 guint headers_visible : 1;
502 guint draw_keyfocus : 1;
503 guint model_setup : 1;
504 guint in_column_drag : 1;
523 EXPAND_COLLAPSE_CURSOR_ROW,
524 SELECT_CURSOR_PARENT,
525 START_INTERACTIVE_SEARCH,
537 PROP_HEADERS_VISIBLE,
538 PROP_HEADERS_CLICKABLE,
539 PROP_EXPANDER_COLUMN,
544 PROP_FIXED_HEIGHT_MODE,
545 PROP_HOVER_SELECTION,
548 PROP_LEVEL_INDENTATION,
550 PROP_ENABLE_GRID_LINES,
551 PROP_ENABLE_TREE_LINES,
556 static void gtk_tree_view_finalize (GObject *object);
557 static void gtk_tree_view_set_property (GObject *object,
561 static void gtk_tree_view_get_property (GObject *object,
566 /* gtkwidget signals */
567 static void gtk_tree_view_destroy (GtkWidget *widget);
568 static void gtk_tree_view_realize (GtkWidget *widget);
569 static void gtk_tree_view_unrealize (GtkWidget *widget);
570 static void gtk_tree_view_map (GtkWidget *widget);
571 static void gtk_tree_view_get_preferred_width (GtkWidget *widget,
574 static void gtk_tree_view_get_preferred_height (GtkWidget *widget,
577 static void gtk_tree_view_size_request (GtkWidget *widget,
578 GtkRequisition *requisition,
579 gboolean may_validate);
580 static void gtk_tree_view_size_allocate (GtkWidget *widget,
581 GtkAllocation *allocation);
582 static gboolean gtk_tree_view_draw (GtkWidget *widget,
584 static gboolean gtk_tree_view_key_press (GtkWidget *widget,
586 static gboolean gtk_tree_view_key_release (GtkWidget *widget,
588 static gboolean gtk_tree_view_motion (GtkWidget *widget,
589 GdkEventMotion *event);
590 static gboolean gtk_tree_view_enter_notify (GtkWidget *widget,
591 GdkEventCrossing *event);
592 static gboolean gtk_tree_view_leave_notify (GtkWidget *widget,
593 GdkEventCrossing *event);
594 static gboolean gtk_tree_view_button_press (GtkWidget *widget,
595 GdkEventButton *event);
596 static gboolean gtk_tree_view_button_release (GtkWidget *widget,
597 GdkEventButton *event);
598 static gboolean gtk_tree_view_grab_broken (GtkWidget *widget,
599 GdkEventGrabBroken *event);
601 static gboolean gtk_tree_view_configure (GtkWidget *widget,
602 GdkEventConfigure *event);
605 static void gtk_tree_view_set_focus_child (GtkContainer *container,
607 static gint gtk_tree_view_focus_out (GtkWidget *widget,
608 GdkEventFocus *event);
609 static gint gtk_tree_view_focus (GtkWidget *widget,
610 GtkDirectionType direction);
611 static void gtk_tree_view_grab_focus (GtkWidget *widget);
612 static void gtk_tree_view_style_set (GtkWidget *widget,
613 GtkStyle *previous_style);
614 static void gtk_tree_view_grab_notify (GtkWidget *widget,
615 gboolean was_grabbed);
616 static void gtk_tree_view_state_changed (GtkWidget *widget,
617 GtkStateType previous_state);
619 /* container signals */
620 static void gtk_tree_view_remove (GtkContainer *container,
622 static void gtk_tree_view_forall (GtkContainer *container,
623 gboolean include_internals,
624 GtkCallback callback,
625 gpointer callback_data);
627 /* Source side drag signals */
628 static void gtk_tree_view_drag_begin (GtkWidget *widget,
629 GdkDragContext *context);
630 static void gtk_tree_view_drag_end (GtkWidget *widget,
631 GdkDragContext *context);
632 static void gtk_tree_view_drag_data_get (GtkWidget *widget,
633 GdkDragContext *context,
634 GtkSelectionData *selection_data,
637 static void gtk_tree_view_drag_data_delete (GtkWidget *widget,
638 GdkDragContext *context);
640 /* Target side drag signals */
641 static void gtk_tree_view_drag_leave (GtkWidget *widget,
642 GdkDragContext *context,
644 static gboolean gtk_tree_view_drag_motion (GtkWidget *widget,
645 GdkDragContext *context,
649 static gboolean gtk_tree_view_drag_drop (GtkWidget *widget,
650 GdkDragContext *context,
654 static void gtk_tree_view_drag_data_received (GtkWidget *widget,
655 GdkDragContext *context,
658 GtkSelectionData *selection_data,
662 /* tree_model signals */
663 static void gtk_tree_view_set_hadjustment (GtkTreeView *tree_view,
664 GtkAdjustment *adjustment);
665 static void gtk_tree_view_set_vadjustment (GtkTreeView *tree_view,
666 GtkAdjustment *adjustment);
667 static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
668 GtkMovementStep step,
670 static gboolean gtk_tree_view_real_select_all (GtkTreeView *tree_view);
671 static gboolean gtk_tree_view_real_unselect_all (GtkTreeView *tree_view);
672 static gboolean gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
673 gboolean start_editing);
674 static gboolean gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view);
675 static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view,
679 static gboolean gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view);
680 static void gtk_tree_view_row_changed (GtkTreeModel *model,
684 static void gtk_tree_view_row_inserted (GtkTreeModel *model,
688 static void gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
692 static void gtk_tree_view_row_deleted (GtkTreeModel *model,
695 static void gtk_tree_view_rows_reordered (GtkTreeModel *model,
701 /* Incremental reflow */
702 static gboolean validate_row (GtkTreeView *tree_view,
707 static void validate_visible_area (GtkTreeView *tree_view);
708 static gboolean validate_rows_handler (GtkTreeView *tree_view);
709 static gboolean do_validate_rows (GtkTreeView *tree_view,
710 gboolean size_request);
711 static gboolean validate_rows (GtkTreeView *tree_view);
712 static gboolean presize_handler_callback (gpointer data);
713 static void install_presize_handler (GtkTreeView *tree_view);
714 static void install_scroll_sync_handler (GtkTreeView *tree_view);
715 static void gtk_tree_view_set_top_row (GtkTreeView *tree_view,
718 static void gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view);
719 static void gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view);
720 static void invalidate_empty_focus (GtkTreeView *tree_view);
722 /* Internal functions */
723 static gboolean gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
724 GtkTreeViewColumn *column);
725 static inline gboolean gtk_tree_view_draw_expanders (GtkTreeView *tree_view);
726 static void gtk_tree_view_add_move_binding (GtkBindingSet *binding_set,
729 gboolean add_shifted_binding,
730 GtkMovementStep step,
732 static gint gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view,
734 static void gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
736 const GdkRectangle *clip_rect);
737 static void gtk_tree_view_queue_draw_arrow (GtkTreeView *tree_view,
740 static void gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
746 static void gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
750 static gint gtk_tree_view_new_column_width (GtkTreeView *tree_view,
753 static void gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
754 GtkTreeView *tree_view);
755 static void gtk_tree_view_build_tree (GtkTreeView *tree_view,
760 static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
763 static void gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view,
764 GtkTreeViewColumn *column,
765 gboolean focus_to_cell);
766 static gboolean gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view,
767 GdkEventMotion *event);
768 static void gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view);
769 static void gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
771 static void gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
773 static void gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
775 static void gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
777 static gboolean gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
782 static gboolean gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
788 static void gtk_tree_view_real_set_cursor (GtkTreeView *tree_view,
790 gboolean clear_and_select,
791 gboolean clamp_node);
792 static gboolean gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view);
793 static void column_sizing_notify (GObject *object,
796 static gboolean expand_collapse_timeout (gpointer data);
797 static void add_expand_collapse_timeout (GtkTreeView *tree_view,
801 static void remove_expand_collapse_timeout (GtkTreeView *tree_view);
802 static void cancel_arrow_animation (GtkTreeView *tree_view);
803 static gboolean do_expand_collapse (GtkTreeView *tree_view);
804 static void gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view);
805 static void update_prelight (GtkTreeView *tree_view,
809 static inline gint gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view);
811 static inline gint gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view,
814 gint vertical_separator);
815 static inline gint gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view,
817 gint vertical_separator);
819 static inline gint gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view,
822 static inline gint gtk_tree_view_get_row_height (GtkTreeView *tree_view,
825 /* interactive search */
826 static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
827 static void gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
828 GtkTreeView *tree_view,
830 static void gtk_tree_view_search_position_func (GtkTreeView *tree_view,
831 GtkWidget *search_dialog,
833 static void gtk_tree_view_search_disable_popdown (GtkEntry *entry,
836 static void gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
837 GtkTreeView *tree_view);
838 static void gtk_tree_view_search_activate (GtkEntry *entry,
839 GtkTreeView *tree_view);
840 static gboolean gtk_tree_view_real_search_enable_popdown(gpointer data);
841 static void gtk_tree_view_search_enable_popdown (GtkWidget *widget,
843 static gboolean gtk_tree_view_search_delete_event (GtkWidget *widget,
845 GtkTreeView *tree_view);
846 static gboolean gtk_tree_view_search_button_press_event (GtkWidget *widget,
847 GdkEventButton *event,
848 GtkTreeView *tree_view);
849 static gboolean gtk_tree_view_search_scroll_event (GtkWidget *entry,
850 GdkEventScroll *event,
851 GtkTreeView *tree_view);
852 static gboolean gtk_tree_view_search_key_press_event (GtkWidget *entry,
854 GtkTreeView *tree_view);
855 static gboolean gtk_tree_view_search_move (GtkWidget *window,
856 GtkTreeView *tree_view,
858 static gboolean gtk_tree_view_search_equal_func (GtkTreeModel *model,
862 gpointer search_data);
863 static gboolean gtk_tree_view_search_iter (GtkTreeModel *model,
864 GtkTreeSelection *selection,
869 static void gtk_tree_view_search_init (GtkWidget *entry,
870 GtkTreeView *tree_view);
871 static void gtk_tree_view_put (GtkTreeView *tree_view,
872 GtkWidget *child_widget,
877 static gboolean gtk_tree_view_start_editing (GtkTreeView *tree_view,
878 GtkTreePath *cursor_path,
880 static void gtk_tree_view_stop_editing (GtkTreeView *tree_view,
881 gboolean cancel_editing);
882 static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
884 gboolean keybinding);
885 static gboolean gtk_tree_view_start_interactive_search (GtkTreeView *tree_view);
886 static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree_view,
887 GtkTreeViewColumn *column,
891 static void gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
895 static GObject *gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable,
897 const gchar *childname);
898 static void gtk_tree_view_buildable_init (GtkBuildableIface *iface);
901 static gboolean scroll_row_timeout (gpointer data);
902 static void add_scroll_timeout (GtkTreeView *tree_view);
903 static void remove_scroll_timeout (GtkTreeView *tree_view);
905 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
912 G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER,
913 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
914 gtk_tree_view_buildable_init)
915 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
918 gtk_tree_view_class_init (GtkTreeViewClass *class)
920 GObjectClass *o_class;
921 GtkWidgetClass *widget_class;
922 GtkContainerClass *container_class;
923 GtkBindingSet *binding_set;
925 binding_set = gtk_binding_set_by_class (class);
927 o_class = (GObjectClass *) class;
928 widget_class = (GtkWidgetClass *) class;
929 container_class = (GtkContainerClass *) class;
931 /* GObject signals */
932 o_class->set_property = gtk_tree_view_set_property;
933 o_class->get_property = gtk_tree_view_get_property;
934 o_class->finalize = gtk_tree_view_finalize;
936 /* GtkWidget signals */
937 widget_class->destroy = gtk_tree_view_destroy;
938 widget_class->map = gtk_tree_view_map;
939 widget_class->realize = gtk_tree_view_realize;
940 widget_class->unrealize = gtk_tree_view_unrealize;
941 widget_class->get_preferred_width = gtk_tree_view_get_preferred_width;
942 widget_class->get_preferred_height = gtk_tree_view_get_preferred_height;
943 widget_class->size_allocate = gtk_tree_view_size_allocate;
944 widget_class->button_press_event = gtk_tree_view_button_press;
945 widget_class->button_release_event = gtk_tree_view_button_release;
946 widget_class->grab_broken_event = gtk_tree_view_grab_broken;
947 /*widget_class->configure_event = gtk_tree_view_configure;*/
948 widget_class->motion_notify_event = gtk_tree_view_motion;
949 widget_class->draw = gtk_tree_view_draw;
950 widget_class->key_press_event = gtk_tree_view_key_press;
951 widget_class->key_release_event = gtk_tree_view_key_release;
952 widget_class->enter_notify_event = gtk_tree_view_enter_notify;
953 widget_class->leave_notify_event = gtk_tree_view_leave_notify;
954 widget_class->focus_out_event = gtk_tree_view_focus_out;
955 widget_class->drag_begin = gtk_tree_view_drag_begin;
956 widget_class->drag_end = gtk_tree_view_drag_end;
957 widget_class->drag_data_get = gtk_tree_view_drag_data_get;
958 widget_class->drag_data_delete = gtk_tree_view_drag_data_delete;
959 widget_class->drag_leave = gtk_tree_view_drag_leave;
960 widget_class->drag_motion = gtk_tree_view_drag_motion;
961 widget_class->drag_drop = gtk_tree_view_drag_drop;
962 widget_class->drag_data_received = gtk_tree_view_drag_data_received;
963 widget_class->focus = gtk_tree_view_focus;
964 widget_class->grab_focus = gtk_tree_view_grab_focus;
965 widget_class->style_set = gtk_tree_view_style_set;
966 widget_class->grab_notify = gtk_tree_view_grab_notify;
967 widget_class->state_changed = gtk_tree_view_state_changed;
969 /* GtkContainer signals */
970 container_class->remove = gtk_tree_view_remove;
971 container_class->forall = gtk_tree_view_forall;
972 container_class->set_focus_child = gtk_tree_view_set_focus_child;
974 class->move_cursor = gtk_tree_view_real_move_cursor;
975 class->select_all = gtk_tree_view_real_select_all;
976 class->unselect_all = gtk_tree_view_real_unselect_all;
977 class->select_cursor_row = gtk_tree_view_real_select_cursor_row;
978 class->toggle_cursor_row = gtk_tree_view_real_toggle_cursor_row;
979 class->expand_collapse_cursor_row = gtk_tree_view_real_expand_collapse_cursor_row;
980 class->select_cursor_parent = gtk_tree_view_real_select_cursor_parent;
981 class->start_interactive_search = gtk_tree_view_start_interactive_search;
985 g_object_class_install_property (o_class,
987 g_param_spec_object ("model",
988 P_("TreeView Model"),
989 P_("The model for the tree view"),
991 GTK_PARAM_READWRITE));
993 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
994 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
995 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
996 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
998 g_object_class_install_property (o_class,
999 PROP_HEADERS_VISIBLE,
1000 g_param_spec_boolean ("headers-visible",
1001 P_("Headers Visible"),
1002 P_("Show the column header buttons"),
1004 GTK_PARAM_READWRITE));
1006 g_object_class_install_property (o_class,
1007 PROP_HEADERS_CLICKABLE,
1008 g_param_spec_boolean ("headers-clickable",
1009 P_("Headers Clickable"),
1010 P_("Column headers respond to click events"),
1012 GTK_PARAM_READWRITE));
1014 g_object_class_install_property (o_class,
1015 PROP_EXPANDER_COLUMN,
1016 g_param_spec_object ("expander-column",
1017 P_("Expander Column"),
1018 P_("Set the column for the expander column"),
1019 GTK_TYPE_TREE_VIEW_COLUMN,
1020 GTK_PARAM_READWRITE));
1022 g_object_class_install_property (o_class,
1024 g_param_spec_boolean ("reorderable",
1026 P_("View is reorderable"),
1028 GTK_PARAM_READWRITE));
1030 g_object_class_install_property (o_class,
1032 g_param_spec_boolean ("rules-hint",
1034 P_("Set a hint to the theme engine to draw rows in alternating colors"),
1036 GTK_PARAM_READWRITE));
1038 g_object_class_install_property (o_class,
1040 g_param_spec_boolean ("enable-search",
1041 P_("Enable Search"),
1042 P_("View allows user to search through columns interactively"),
1044 GTK_PARAM_READWRITE));
1046 g_object_class_install_property (o_class,
1048 g_param_spec_int ("search-column",
1049 P_("Search Column"),
1050 P_("Model column to search through during interactive search"),
1054 GTK_PARAM_READWRITE));
1057 * GtkTreeView:fixed-height-mode:
1059 * Setting the ::fixed-height-mode property to %TRUE speeds up
1060 * #GtkTreeView by assuming that all rows have the same height.
1061 * Only enable this option if all rows are the same height.
1062 * Please see gtk_tree_view_set_fixed_height_mode() for more
1063 * information on this option.
1067 g_object_class_install_property (o_class,
1068 PROP_FIXED_HEIGHT_MODE,
1069 g_param_spec_boolean ("fixed-height-mode",
1070 P_("Fixed Height Mode"),
1071 P_("Speeds up GtkTreeView by assuming that all rows have the same height"),
1073 GTK_PARAM_READWRITE));
1076 * GtkTreeView:hover-selection:
1078 * Enables or disables the hover selection mode of @tree_view.
1079 * Hover selection makes the selected row follow the pointer.
1080 * Currently, this works only for the selection modes
1081 * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
1083 * This mode is primarily intended for treeviews in popups, e.g.
1084 * in #GtkComboBox or #GtkEntryCompletion.
1088 g_object_class_install_property (o_class,
1089 PROP_HOVER_SELECTION,
1090 g_param_spec_boolean ("hover-selection",
1091 P_("Hover Selection"),
1092 P_("Whether the selection should follow the pointer"),
1094 GTK_PARAM_READWRITE));
1097 * GtkTreeView:hover-expand:
1099 * Enables or disables the hover expansion mode of @tree_view.
1100 * Hover expansion makes rows expand or collapse if the pointer moves
1103 * This mode is primarily intended for treeviews in popups, e.g.
1104 * in #GtkComboBox or #GtkEntryCompletion.
1108 g_object_class_install_property (o_class,
1110 g_param_spec_boolean ("hover-expand",
1112 P_("Whether rows should be expanded/collapsed when the pointer moves over them"),
1114 GTK_PARAM_READWRITE));
1117 * GtkTreeView:show-expanders:
1119 * %TRUE if the view has expanders.
1123 g_object_class_install_property (o_class,
1124 PROP_SHOW_EXPANDERS,
1125 g_param_spec_boolean ("show-expanders",
1126 P_("Show Expanders"),
1127 P_("View has expanders"),
1129 GTK_PARAM_READWRITE));
1132 * GtkTreeView:level-indentation:
1134 * Extra indentation for each level.
1138 g_object_class_install_property (o_class,
1139 PROP_LEVEL_INDENTATION,
1140 g_param_spec_int ("level-indentation",
1141 P_("Level Indentation"),
1142 P_("Extra indentation for each level"),
1146 GTK_PARAM_READWRITE));
1148 g_object_class_install_property (o_class,
1149 PROP_RUBBER_BANDING,
1150 g_param_spec_boolean ("rubber-banding",
1151 P_("Rubber Banding"),
1152 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
1154 GTK_PARAM_READWRITE));
1156 g_object_class_install_property (o_class,
1157 PROP_ENABLE_GRID_LINES,
1158 g_param_spec_enum ("enable-grid-lines",
1159 P_("Enable Grid Lines"),
1160 P_("Whether grid lines should be drawn in the tree view"),
1161 GTK_TYPE_TREE_VIEW_GRID_LINES,
1162 GTK_TREE_VIEW_GRID_LINES_NONE,
1163 GTK_PARAM_READWRITE));
1165 g_object_class_install_property (o_class,
1166 PROP_ENABLE_TREE_LINES,
1167 g_param_spec_boolean ("enable-tree-lines",
1168 P_("Enable Tree Lines"),
1169 P_("Whether tree lines should be drawn in the tree view"),
1171 GTK_PARAM_READWRITE));
1173 g_object_class_install_property (o_class,
1174 PROP_TOOLTIP_COLUMN,
1175 g_param_spec_int ("tooltip-column",
1176 P_("Tooltip Column"),
1177 P_("The column in the model containing the tooltip texts for the rows"),
1181 GTK_PARAM_READWRITE));
1183 /* Style properties */
1184 #define _TREE_VIEW_EXPANDER_SIZE 12
1185 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
1186 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
1188 gtk_widget_class_install_style_property (widget_class,
1189 g_param_spec_int ("expander-size",
1190 P_("Expander Size"),
1191 P_("Size of the expander arrow"),
1194 _TREE_VIEW_EXPANDER_SIZE,
1195 GTK_PARAM_READABLE));
1197 gtk_widget_class_install_style_property (widget_class,
1198 g_param_spec_int ("vertical-separator",
1199 P_("Vertical Separator Width"),
1200 P_("Vertical space between cells. Must be an even number"),
1203 _TREE_VIEW_VERTICAL_SEPARATOR,
1204 GTK_PARAM_READABLE));
1206 gtk_widget_class_install_style_property (widget_class,
1207 g_param_spec_int ("horizontal-separator",
1208 P_("Horizontal Separator Width"),
1209 P_("Horizontal space between cells. Must be an even number"),
1212 _TREE_VIEW_HORIZONTAL_SEPARATOR,
1213 GTK_PARAM_READABLE));
1215 gtk_widget_class_install_style_property (widget_class,
1216 g_param_spec_boolean ("allow-rules",
1218 P_("Allow drawing of alternating color rows"),
1220 GTK_PARAM_READABLE));
1222 gtk_widget_class_install_style_property (widget_class,
1223 g_param_spec_boolean ("indent-expanders",
1224 P_("Indent Expanders"),
1225 P_("Make the expanders indented"),
1227 GTK_PARAM_READABLE));
1229 gtk_widget_class_install_style_property (widget_class,
1230 g_param_spec_boxed ("even-row-color",
1231 P_("Even Row Color"),
1232 P_("Color to use for even rows"),
1234 GTK_PARAM_READABLE));
1236 gtk_widget_class_install_style_property (widget_class,
1237 g_param_spec_boxed ("odd-row-color",
1238 P_("Odd Row Color"),
1239 P_("Color to use for odd rows"),
1241 GTK_PARAM_READABLE));
1243 gtk_widget_class_install_style_property (widget_class,
1244 g_param_spec_int ("grid-line-width",
1245 P_("Grid line width"),
1246 P_("Width, in pixels, of the tree view grid lines"),
1248 GTK_PARAM_READABLE));
1250 gtk_widget_class_install_style_property (widget_class,
1251 g_param_spec_int ("tree-line-width",
1252 P_("Tree line width"),
1253 P_("Width, in pixels, of the tree view lines"),
1255 GTK_PARAM_READABLE));
1257 gtk_widget_class_install_style_property (widget_class,
1258 g_param_spec_string ("grid-line-pattern",
1259 P_("Grid line pattern"),
1260 P_("Dash pattern used to draw the tree view grid lines"),
1262 GTK_PARAM_READABLE));
1264 gtk_widget_class_install_style_property (widget_class,
1265 g_param_spec_string ("tree-line-pattern",
1266 P_("Tree line pattern"),
1267 P_("Dash pattern used to draw the tree view lines"),
1269 GTK_PARAM_READABLE));
1273 * GtkTreeView::row-activated:
1274 * @tree_view: the object on which the signal is emitted
1275 * @path: the #GtkTreePath for the activated row
1276 * @column: the #GtkTreeViewColumn in which the activation occurred
1278 * The "row-activated" signal is emitted when the method
1279 * gtk_tree_view_row_activated() is called or the user double clicks
1280 * a treeview row. It is also emitted when a non-editable row is
1281 * selected and one of the keys: Space, Shift+Space, Return or
1284 * For selection handling refer to the <link linkend="TreeWidget">tree
1285 * widget conceptual overview</link> as well as #GtkTreeSelection.
1287 tree_view_signals[ROW_ACTIVATED] =
1288 g_signal_new (I_("row-activated"),
1289 G_TYPE_FROM_CLASS (o_class),
1290 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1291 G_STRUCT_OFFSET (GtkTreeViewClass, row_activated),
1293 _gtk_marshal_VOID__BOXED_OBJECT,
1296 GTK_TYPE_TREE_VIEW_COLUMN);
1299 * GtkTreeView::test-expand-row:
1300 * @tree_view: the object on which the signal is emitted
1301 * @iter: the tree iter of the row to expand
1302 * @path: a tree path that points to the row
1304 * The given row is about to be expanded (show its children nodes). Use this
1305 * signal if you need to control the expandability of individual rows.
1307 * Returns: %FALSE to allow expansion, %TRUE to reject
1309 tree_view_signals[TEST_EXPAND_ROW] =
1310 g_signal_new (I_("test-expand-row"),
1311 G_TYPE_FROM_CLASS (o_class),
1313 G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row),
1314 _gtk_boolean_handled_accumulator, NULL,
1315 _gtk_marshal_BOOLEAN__BOXED_BOXED,
1318 GTK_TYPE_TREE_PATH);
1321 * GtkTreeView::test-collapse-row:
1322 * @tree_view: the object on which the signal is emitted
1323 * @iter: the tree iter of the row to collapse
1324 * @path: a tree path that points to the row
1326 * The given row is about to be collapsed (hide its children nodes). Use this
1327 * signal if you need to control the collapsibility of individual rows.
1329 * Returns: %FALSE to allow collapsing, %TRUE to reject
1331 tree_view_signals[TEST_COLLAPSE_ROW] =
1332 g_signal_new (I_("test-collapse-row"),
1333 G_TYPE_FROM_CLASS (o_class),
1335 G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row),
1336 _gtk_boolean_handled_accumulator, NULL,
1337 _gtk_marshal_BOOLEAN__BOXED_BOXED,
1340 GTK_TYPE_TREE_PATH);
1343 * GtkTreeView::row-expanded:
1344 * @tree_view: the object on which the signal is emitted
1345 * @iter: the tree iter of the expanded row
1346 * @path: a tree path that points to the row
1348 * The given row has been expanded (child nodes are shown).
1350 tree_view_signals[ROW_EXPANDED] =
1351 g_signal_new (I_("row-expanded"),
1352 G_TYPE_FROM_CLASS (o_class),
1354 G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded),
1356 _gtk_marshal_VOID__BOXED_BOXED,
1359 GTK_TYPE_TREE_PATH);
1362 * GtkTreeView::row-collapsed:
1363 * @tree_view: the object on which the signal is emitted
1364 * @iter: the tree iter of the collapsed row
1365 * @path: a tree path that points to the row
1367 * The given row has been collapsed (child nodes are hidden).
1369 tree_view_signals[ROW_COLLAPSED] =
1370 g_signal_new (I_("row-collapsed"),
1371 G_TYPE_FROM_CLASS (o_class),
1373 G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed),
1375 _gtk_marshal_VOID__BOXED_BOXED,
1378 GTK_TYPE_TREE_PATH);
1381 * GtkTreeView::columns-changed:
1382 * @tree_view: the object on which the signal is emitted
1384 * The number of columns of the treeview has changed.
1386 tree_view_signals[COLUMNS_CHANGED] =
1387 g_signal_new (I_("columns-changed"),
1388 G_TYPE_FROM_CLASS (o_class),
1390 G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed),
1392 _gtk_marshal_VOID__VOID,
1396 * GtkTreeView::cursor-changed:
1397 * @tree_view: the object on which the signal is emitted
1399 * The position of the cursor (focused cell) has changed.
1401 tree_view_signals[CURSOR_CHANGED] =
1402 g_signal_new (I_("cursor-changed"),
1403 G_TYPE_FROM_CLASS (o_class),
1405 G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed),
1407 _gtk_marshal_VOID__VOID,
1410 tree_view_signals[MOVE_CURSOR] =
1411 g_signal_new (I_("move-cursor"),
1412 G_TYPE_FROM_CLASS (o_class),
1413 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1414 G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor),
1416 _gtk_marshal_BOOLEAN__ENUM_INT,
1418 GTK_TYPE_MOVEMENT_STEP,
1421 tree_view_signals[SELECT_ALL] =
1422 g_signal_new (I_("select-all"),
1423 G_TYPE_FROM_CLASS (o_class),
1424 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1425 G_STRUCT_OFFSET (GtkTreeViewClass, select_all),
1427 _gtk_marshal_BOOLEAN__VOID,
1430 tree_view_signals[UNSELECT_ALL] =
1431 g_signal_new (I_("unselect-all"),
1432 G_TYPE_FROM_CLASS (o_class),
1433 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1434 G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all),
1436 _gtk_marshal_BOOLEAN__VOID,
1439 tree_view_signals[SELECT_CURSOR_ROW] =
1440 g_signal_new (I_("select-cursor-row"),
1441 G_TYPE_FROM_CLASS (o_class),
1442 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1443 G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row),
1445 _gtk_marshal_BOOLEAN__BOOLEAN,
1449 tree_view_signals[TOGGLE_CURSOR_ROW] =
1450 g_signal_new (I_("toggle-cursor-row"),
1451 G_TYPE_FROM_CLASS (o_class),
1452 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1453 G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row),
1455 _gtk_marshal_BOOLEAN__VOID,
1458 tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] =
1459 g_signal_new (I_("expand-collapse-cursor-row"),
1460 G_TYPE_FROM_CLASS (o_class),
1461 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1462 G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row),
1464 _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN,
1470 tree_view_signals[SELECT_CURSOR_PARENT] =
1471 g_signal_new (I_("select-cursor-parent"),
1472 G_TYPE_FROM_CLASS (o_class),
1473 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1474 G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent),
1476 _gtk_marshal_BOOLEAN__VOID,
1479 tree_view_signals[START_INTERACTIVE_SEARCH] =
1480 g_signal_new (I_("start-interactive-search"),
1481 G_TYPE_FROM_CLASS (o_class),
1482 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1483 G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search),
1485 _gtk_marshal_BOOLEAN__VOID,
1489 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Up, 0, TRUE,
1490 GTK_MOVEMENT_DISPLAY_LINES, -1);
1491 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Up, 0, TRUE,
1492 GTK_MOVEMENT_DISPLAY_LINES, -1);
1494 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Down, 0, TRUE,
1495 GTK_MOVEMENT_DISPLAY_LINES, 1);
1496 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Down, 0, TRUE,
1497 GTK_MOVEMENT_DISPLAY_LINES, 1);
1499 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_p, GDK_CONTROL_MASK, FALSE,
1500 GTK_MOVEMENT_DISPLAY_LINES, -1);
1502 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_n, GDK_CONTROL_MASK, FALSE,
1503 GTK_MOVEMENT_DISPLAY_LINES, 1);
1505 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Home, 0, TRUE,
1506 GTK_MOVEMENT_BUFFER_ENDS, -1);
1507 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Home, 0, TRUE,
1508 GTK_MOVEMENT_BUFFER_ENDS, -1);
1510 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_End, 0, TRUE,
1511 GTK_MOVEMENT_BUFFER_ENDS, 1);
1512 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_End, 0, TRUE,
1513 GTK_MOVEMENT_BUFFER_ENDS, 1);
1515 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Page_Up, 0, TRUE,
1516 GTK_MOVEMENT_PAGES, -1);
1517 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, TRUE,
1518 GTK_MOVEMENT_PAGES, -1);
1520 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Page_Down, 0, TRUE,
1521 GTK_MOVEMENT_PAGES, 1);
1522 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, TRUE,
1523 GTK_MOVEMENT_PAGES, 1);
1526 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0, "move-cursor", 2,
1527 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1530 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0, "move-cursor", 2,
1531 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1534 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0, "move-cursor", 2,
1535 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1538 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0, "move-cursor", 2,
1539 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1542 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
1544 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1547 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
1549 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1552 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
1554 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1557 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1559 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1562 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1563 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1565 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", 0);
1566 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", 0);
1568 gtk_binding_entry_add_signal (binding_set, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1569 gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1571 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1572 G_TYPE_BOOLEAN, TRUE);
1573 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1574 G_TYPE_BOOLEAN, TRUE);
1576 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, "select-cursor-row", 1,
1577 G_TYPE_BOOLEAN, TRUE);
1578 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, "select-cursor-row", 1,
1579 G_TYPE_BOOLEAN, TRUE);
1580 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "select-cursor-row", 1,
1581 G_TYPE_BOOLEAN, TRUE);
1582 gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, "select-cursor-row", 1,
1583 G_TYPE_BOOLEAN, TRUE);
1584 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "select-cursor-row", 1,
1585 G_TYPE_BOOLEAN, TRUE);
1587 /* expand and collapse rows */
1588 gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, 0, "expand-collapse-cursor-row", 3,
1589 G_TYPE_BOOLEAN, TRUE,
1590 G_TYPE_BOOLEAN, TRUE,
1591 G_TYPE_BOOLEAN, FALSE);
1593 gtk_binding_entry_add_signal (binding_set, GDK_KEY_asterisk, 0,
1594 "expand-collapse-cursor-row", 3,
1595 G_TYPE_BOOLEAN, TRUE,
1596 G_TYPE_BOOLEAN, TRUE,
1597 G_TYPE_BOOLEAN, TRUE);
1598 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Multiply, 0,
1599 "expand-collapse-cursor-row", 3,
1600 G_TYPE_BOOLEAN, TRUE,
1601 G_TYPE_BOOLEAN, TRUE,
1602 G_TYPE_BOOLEAN, TRUE);
1604 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, 0,
1605 "expand-collapse-cursor-row", 3,
1606 G_TYPE_BOOLEAN, TRUE,
1607 G_TYPE_BOOLEAN, FALSE,
1608 G_TYPE_BOOLEAN, FALSE);
1609 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Divide, 0,
1610 "expand-collapse-cursor-row", 3,
1611 G_TYPE_BOOLEAN, TRUE,
1612 G_TYPE_BOOLEAN, FALSE,
1613 G_TYPE_BOOLEAN, FALSE);
1615 /* Not doable on US keyboards */
1616 gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1617 G_TYPE_BOOLEAN, TRUE,
1618 G_TYPE_BOOLEAN, TRUE,
1619 G_TYPE_BOOLEAN, TRUE);
1620 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, 0, "expand-collapse-cursor-row", 3,
1621 G_TYPE_BOOLEAN, TRUE,
1622 G_TYPE_BOOLEAN, TRUE,
1623 G_TYPE_BOOLEAN, FALSE);
1624 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1625 G_TYPE_BOOLEAN, TRUE,
1626 G_TYPE_BOOLEAN, TRUE,
1627 G_TYPE_BOOLEAN, TRUE);
1628 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1629 G_TYPE_BOOLEAN, TRUE,
1630 G_TYPE_BOOLEAN, TRUE,
1631 G_TYPE_BOOLEAN, TRUE);
1632 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_SHIFT_MASK,
1633 "expand-collapse-cursor-row", 3,
1634 G_TYPE_BOOLEAN, FALSE,
1635 G_TYPE_BOOLEAN, TRUE,
1636 G_TYPE_BOOLEAN, TRUE);
1637 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, GDK_SHIFT_MASK,
1638 "expand-collapse-cursor-row", 3,
1639 G_TYPE_BOOLEAN, FALSE,
1640 G_TYPE_BOOLEAN, TRUE,
1641 G_TYPE_BOOLEAN, TRUE);
1642 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right,
1643 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1644 "expand-collapse-cursor-row", 3,
1645 G_TYPE_BOOLEAN, FALSE,
1646 G_TYPE_BOOLEAN, TRUE,
1647 G_TYPE_BOOLEAN, TRUE);
1648 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right,
1649 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1650 "expand-collapse-cursor-row", 3,
1651 G_TYPE_BOOLEAN, FALSE,
1652 G_TYPE_BOOLEAN, TRUE,
1653 G_TYPE_BOOLEAN, TRUE);
1655 gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, 0, "expand-collapse-cursor-row", 3,
1656 G_TYPE_BOOLEAN, TRUE,
1657 G_TYPE_BOOLEAN, FALSE,
1658 G_TYPE_BOOLEAN, FALSE);
1659 gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1660 G_TYPE_BOOLEAN, TRUE,
1661 G_TYPE_BOOLEAN, FALSE,
1662 G_TYPE_BOOLEAN, TRUE);
1663 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, 0, "expand-collapse-cursor-row", 3,
1664 G_TYPE_BOOLEAN, TRUE,
1665 G_TYPE_BOOLEAN, FALSE,
1666 G_TYPE_BOOLEAN, FALSE);
1667 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1668 G_TYPE_BOOLEAN, TRUE,
1669 G_TYPE_BOOLEAN, FALSE,
1670 G_TYPE_BOOLEAN, TRUE);
1671 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK,
1672 "expand-collapse-cursor-row", 3,
1673 G_TYPE_BOOLEAN, FALSE,
1674 G_TYPE_BOOLEAN, FALSE,
1675 G_TYPE_BOOLEAN, TRUE);
1676 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, GDK_SHIFT_MASK,
1677 "expand-collapse-cursor-row", 3,
1678 G_TYPE_BOOLEAN, FALSE,
1679 G_TYPE_BOOLEAN, FALSE,
1680 G_TYPE_BOOLEAN, TRUE);
1681 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left,
1682 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1683 "expand-collapse-cursor-row", 3,
1684 G_TYPE_BOOLEAN, FALSE,
1685 G_TYPE_BOOLEAN, FALSE,
1686 G_TYPE_BOOLEAN, TRUE);
1687 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left,
1688 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1689 "expand-collapse-cursor-row", 3,
1690 G_TYPE_BOOLEAN, FALSE,
1691 G_TYPE_BOOLEAN, FALSE,
1692 G_TYPE_BOOLEAN, TRUE);
1694 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0, "select-cursor-parent", 0);
1695 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1697 gtk_binding_entry_add_signal (binding_set, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1699 gtk_binding_entry_add_signal (binding_set, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1701 g_type_class_add_private (o_class, sizeof (GtkTreeViewPrivate));
1705 gtk_tree_view_init (GtkTreeView *tree_view)
1707 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, GTK_TYPE_TREE_VIEW, GtkTreeViewPrivate);
1709 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1710 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1712 tree_view->priv->show_expanders = TRUE;
1713 tree_view->priv->draw_keyfocus = TRUE;
1714 tree_view->priv->headers_visible = TRUE;
1716 /* We need some padding */
1717 tree_view->priv->dy = 0;
1718 tree_view->priv->cursor_offset = 0;
1719 tree_view->priv->n_columns = 0;
1720 tree_view->priv->header_height = 1;
1721 tree_view->priv->x_drag = 0;
1722 tree_view->priv->drag_pos = -1;
1723 tree_view->priv->header_has_focus = FALSE;
1724 tree_view->priv->pressed_button = -1;
1725 tree_view->priv->press_start_x = -1;
1726 tree_view->priv->press_start_y = -1;
1727 tree_view->priv->reorderable = FALSE;
1728 tree_view->priv->presize_handler_timer = 0;
1729 tree_view->priv->scroll_sync_timer = 0;
1730 tree_view->priv->fixed_height = -1;
1731 tree_view->priv->fixed_height_mode = FALSE;
1732 tree_view->priv->fixed_height_check = 0;
1733 tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view);
1734 tree_view->priv->enable_search = TRUE;
1735 tree_view->priv->search_column = -1;
1736 tree_view->priv->search_position_func = gtk_tree_view_search_position_func;
1737 tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func;
1738 tree_view->priv->search_custom_entry_set = FALSE;
1739 tree_view->priv->typeselect_flush_timeout = 0;
1740 tree_view->priv->init_hadjust_value = TRUE;
1741 tree_view->priv->width = 0;
1743 tree_view->priv->hover_selection = FALSE;
1744 tree_view->priv->hover_expand = FALSE;
1746 tree_view->priv->level_indentation = 0;
1748 tree_view->priv->rubber_banding_enable = FALSE;
1750 tree_view->priv->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE;
1751 tree_view->priv->tree_lines_enabled = FALSE;
1753 tree_view->priv->tooltip_column = -1;
1755 tree_view->priv->post_validation_flag = FALSE;
1757 tree_view->priv->last_button_x = -1;
1758 tree_view->priv->last_button_y = -1;
1760 tree_view->priv->event_last_x = -10000;
1761 tree_view->priv->event_last_y = -10000;
1763 gtk_tree_view_set_vadjustment (tree_view, NULL);
1764 gtk_tree_view_set_hadjustment (tree_view, NULL);
1773 gtk_tree_view_set_property (GObject *object,
1775 const GValue *value,
1778 GtkTreeView *tree_view;
1780 tree_view = GTK_TREE_VIEW (object);
1785 gtk_tree_view_set_model (tree_view, g_value_get_object (value));
1787 case PROP_HADJUSTMENT:
1788 gtk_tree_view_set_hadjustment (tree_view, g_value_get_object (value));
1790 case PROP_VADJUSTMENT:
1791 gtk_tree_view_set_vadjustment (tree_view, g_value_get_object (value));
1793 case PROP_HSCROLL_POLICY:
1794 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1795 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1797 case PROP_VSCROLL_POLICY:
1798 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1799 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1801 case PROP_HEADERS_VISIBLE:
1802 gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1804 case PROP_HEADERS_CLICKABLE:
1805 gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1807 case PROP_EXPANDER_COLUMN:
1808 gtk_tree_view_set_expander_column (tree_view, g_value_get_object (value));
1810 case PROP_REORDERABLE:
1811 gtk_tree_view_set_reorderable (tree_view, g_value_get_boolean (value));
1813 case PROP_RULES_HINT:
1814 gtk_tree_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1816 case PROP_ENABLE_SEARCH:
1817 gtk_tree_view_set_enable_search (tree_view, g_value_get_boolean (value));
1819 case PROP_SEARCH_COLUMN:
1820 gtk_tree_view_set_search_column (tree_view, g_value_get_int (value));
1822 case PROP_FIXED_HEIGHT_MODE:
1823 gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value));
1825 case PROP_HOVER_SELECTION:
1826 tree_view->priv->hover_selection = g_value_get_boolean (value);
1828 case PROP_HOVER_EXPAND:
1829 tree_view->priv->hover_expand = g_value_get_boolean (value);
1831 case PROP_SHOW_EXPANDERS:
1832 gtk_tree_view_set_show_expanders (tree_view, g_value_get_boolean (value));
1834 case PROP_LEVEL_INDENTATION:
1835 tree_view->priv->level_indentation = g_value_get_int (value);
1837 case PROP_RUBBER_BANDING:
1838 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1840 case PROP_ENABLE_GRID_LINES:
1841 gtk_tree_view_set_grid_lines (tree_view, g_value_get_enum (value));
1843 case PROP_ENABLE_TREE_LINES:
1844 gtk_tree_view_set_enable_tree_lines (tree_view, g_value_get_boolean (value));
1846 case PROP_TOOLTIP_COLUMN:
1847 gtk_tree_view_set_tooltip_column (tree_view, g_value_get_int (value));
1850 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1856 gtk_tree_view_get_property (GObject *object,
1861 GtkTreeView *tree_view;
1863 tree_view = GTK_TREE_VIEW (object);
1868 g_value_set_object (value, tree_view->priv->model);
1870 case PROP_HADJUSTMENT:
1871 g_value_set_object (value, tree_view->priv->hadjustment);
1873 case PROP_VADJUSTMENT:
1874 g_value_set_object (value, tree_view->priv->vadjustment);
1876 case PROP_HSCROLL_POLICY:
1877 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1879 case PROP_VSCROLL_POLICY:
1880 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1882 case PROP_HEADERS_VISIBLE:
1883 g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view));
1885 case PROP_HEADERS_CLICKABLE:
1886 g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view));
1888 case PROP_EXPANDER_COLUMN:
1889 g_value_set_object (value, tree_view->priv->expander_column);
1891 case PROP_REORDERABLE:
1892 g_value_set_boolean (value, tree_view->priv->reorderable);
1894 case PROP_RULES_HINT:
1895 g_value_set_boolean (value, tree_view->priv->has_rules);
1897 case PROP_ENABLE_SEARCH:
1898 g_value_set_boolean (value, tree_view->priv->enable_search);
1900 case PROP_SEARCH_COLUMN:
1901 g_value_set_int (value, tree_view->priv->search_column);
1903 case PROP_FIXED_HEIGHT_MODE:
1904 g_value_set_boolean (value, tree_view->priv->fixed_height_mode);
1906 case PROP_HOVER_SELECTION:
1907 g_value_set_boolean (value, tree_view->priv->hover_selection);
1909 case PROP_HOVER_EXPAND:
1910 g_value_set_boolean (value, tree_view->priv->hover_expand);
1912 case PROP_SHOW_EXPANDERS:
1913 g_value_set_boolean (value, tree_view->priv->show_expanders);
1915 case PROP_LEVEL_INDENTATION:
1916 g_value_set_int (value, tree_view->priv->level_indentation);
1918 case PROP_RUBBER_BANDING:
1919 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1921 case PROP_ENABLE_GRID_LINES:
1922 g_value_set_enum (value, tree_view->priv->grid_lines);
1924 case PROP_ENABLE_TREE_LINES:
1925 g_value_set_boolean (value, tree_view->priv->tree_lines_enabled);
1927 case PROP_TOOLTIP_COLUMN:
1928 g_value_set_int (value, tree_view->priv->tooltip_column);
1931 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1937 gtk_tree_view_finalize (GObject *object)
1939 G_OBJECT_CLASS (gtk_tree_view_parent_class)->finalize (object);
1943 static GtkBuildableIface *parent_buildable_iface;
1946 gtk_tree_view_buildable_init (GtkBuildableIface *iface)
1948 parent_buildable_iface = g_type_interface_peek_parent (iface);
1949 iface->add_child = gtk_tree_view_buildable_add_child;
1950 iface->get_internal_child = gtk_tree_view_buildable_get_internal_child;
1954 gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
1955 GtkBuilder *builder,
1959 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child));
1963 gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable,
1964 GtkBuilder *builder,
1965 const gchar *childname)
1967 if (strcmp (childname, "selection") == 0)
1968 return G_OBJECT (GTK_TREE_VIEW (buildable)->priv->selection);
1970 return parent_buildable_iface->get_internal_child (buildable,
1975 /* GtkWidget Methods
1979 gtk_tree_view_free_rbtree (GtkTreeView *tree_view)
1981 _gtk_rbtree_free (tree_view->priv->tree);
1983 tree_view->priv->tree = NULL;
1984 tree_view->priv->button_pressed_node = NULL;
1985 tree_view->priv->button_pressed_tree = NULL;
1986 tree_view->priv->prelight_tree = NULL;
1987 tree_view->priv->prelight_node = NULL;
1988 tree_view->priv->expanded_collapsed_node = NULL;
1989 tree_view->priv->expanded_collapsed_tree = NULL;
1993 gtk_tree_view_destroy (GtkWidget *widget)
1995 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1998 gtk_tree_view_stop_editing (tree_view, TRUE);
2000 if (tree_view->priv->columns != NULL)
2002 list = tree_view->priv->columns;
2005 GtkTreeViewColumn *column;
2006 column = GTK_TREE_VIEW_COLUMN (list->data);
2008 gtk_tree_view_remove_column (tree_view, column);
2010 tree_view->priv->columns = NULL;
2013 if (tree_view->priv->tree != NULL)
2015 gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree);
2017 gtk_tree_view_free_rbtree (tree_view);
2020 if (tree_view->priv->selection != NULL)
2022 _gtk_tree_selection_set_tree_view (tree_view->priv->selection, NULL);
2023 g_object_unref (tree_view->priv->selection);
2024 tree_view->priv->selection = NULL;
2027 if (tree_view->priv->scroll_to_path != NULL)
2029 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
2030 tree_view->priv->scroll_to_path = NULL;
2033 if (tree_view->priv->drag_dest_row != NULL)
2035 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
2036 tree_view->priv->drag_dest_row = NULL;
2039 if (tree_view->priv->top_row != NULL)
2041 gtk_tree_row_reference_free (tree_view->priv->top_row);
2042 tree_view->priv->top_row = NULL;
2045 if (tree_view->priv->column_drop_func_data &&
2046 tree_view->priv->column_drop_func_data_destroy)
2048 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
2049 tree_view->priv->column_drop_func_data = NULL;
2052 if (tree_view->priv->destroy_count_destroy &&
2053 tree_view->priv->destroy_count_data)
2055 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
2056 tree_view->priv->destroy_count_data = NULL;
2059 gtk_tree_row_reference_free (tree_view->priv->cursor);
2060 tree_view->priv->cursor = NULL;
2062 gtk_tree_row_reference_free (tree_view->priv->anchor);
2063 tree_view->priv->anchor = NULL;
2065 /* destroy interactive search dialog */
2066 if (tree_view->priv->search_window)
2068 gtk_widget_destroy (tree_view->priv->search_window);
2069 tree_view->priv->search_window = NULL;
2070 tree_view->priv->search_entry = NULL;
2071 if (tree_view->priv->typeselect_flush_timeout)
2073 g_source_remove (tree_view->priv->typeselect_flush_timeout);
2074 tree_view->priv->typeselect_flush_timeout = 0;
2078 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
2080 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
2081 tree_view->priv->search_user_data = NULL;
2084 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
2086 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
2087 tree_view->priv->search_position_user_data = NULL;
2090 if (tree_view->priv->row_separator_destroy && tree_view->priv->row_separator_data)
2092 tree_view->priv->row_separator_destroy (tree_view->priv->row_separator_data);
2093 tree_view->priv->row_separator_data = NULL;
2096 gtk_tree_view_set_model (tree_view, NULL);
2098 if (tree_view->priv->hadjustment)
2100 g_object_unref (tree_view->priv->hadjustment);
2101 tree_view->priv->hadjustment = NULL;
2103 if (tree_view->priv->vadjustment)
2105 g_object_unref (tree_view->priv->vadjustment);
2106 tree_view->priv->vadjustment = NULL;
2109 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->destroy (widget);
2112 /* GtkWidget::map helper */
2114 gtk_tree_view_map_buttons (GtkTreeView *tree_view)
2118 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
2120 if (tree_view->priv->headers_visible)
2122 GtkTreeViewColumn *column;
2126 for (list = tree_view->priv->columns; list; list = list->next)
2128 column = list->data;
2129 button = gtk_tree_view_column_get_button (column);
2131 if (gtk_tree_view_column_get_visible (column) && button)
2132 gtk_widget_show_now (button);
2134 if (gtk_widget_get_visible (button) &&
2135 !gtk_widget_get_mapped (button))
2136 gtk_widget_map (button);
2138 for (list = tree_view->priv->columns; list; list = list->next)
2140 column = list->data;
2141 if (gtk_tree_view_column_get_visible (column) == FALSE)
2144 window = _gtk_tree_view_column_get_window (column);
2145 if (gtk_tree_view_column_get_resizable (column))
2147 gdk_window_raise (window);
2148 gdk_window_show (window);
2151 gdk_window_hide (window);
2153 gdk_window_show (tree_view->priv->header_window);
2158 gtk_tree_view_map (GtkWidget *widget)
2160 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2163 gtk_widget_set_mapped (widget, TRUE);
2165 tmp_list = tree_view->priv->children;
2168 GtkTreeViewChild *child = tmp_list->data;
2169 tmp_list = tmp_list->next;
2171 if (gtk_widget_get_visible (child->widget))
2173 if (!gtk_widget_get_mapped (child->widget))
2174 gtk_widget_map (child->widget);
2177 gdk_window_show (tree_view->priv->bin_window);
2179 gtk_tree_view_map_buttons (tree_view);
2181 gdk_window_show (gtk_widget_get_window (widget));
2185 gtk_tree_view_realize (GtkWidget *widget)
2187 GtkAllocation allocation;
2189 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2191 GdkWindowAttr attributes;
2193 gint attributes_mask;
2195 gtk_widget_set_realized (widget, TRUE);
2197 gtk_widget_get_allocation (widget, &allocation);
2199 /* Make the main, clipping window */
2200 attributes.window_type = GDK_WINDOW_CHILD;
2201 attributes.x = allocation.x;
2202 attributes.y = allocation.y;
2203 attributes.width = allocation.width;
2204 attributes.height = allocation.height;
2205 attributes.wclass = GDK_INPUT_OUTPUT;
2206 attributes.visual = gtk_widget_get_visual (widget);
2207 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
2209 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2211 window = gdk_window_new (gtk_widget_get_parent_window (widget),
2212 &attributes, attributes_mask);
2213 gtk_widget_set_window (widget, window);
2214 gdk_window_set_user_data (window, widget);
2216 gtk_widget_get_allocation (widget, &allocation);
2218 /* Make the window for the tree */
2220 attributes.y = gtk_tree_view_get_effective_header_height (tree_view);
2221 attributes.width = MAX (tree_view->priv->width, allocation.width);
2222 attributes.height = allocation.height;
2223 attributes.event_mask = (GDK_EXPOSURE_MASK |
2225 GDK_POINTER_MOTION_MASK |
2226 GDK_ENTER_NOTIFY_MASK |
2227 GDK_LEAVE_NOTIFY_MASK |
2228 GDK_BUTTON_PRESS_MASK |
2229 GDK_BUTTON_RELEASE_MASK |
2230 gtk_widget_get_events (widget));
2232 tree_view->priv->bin_window = gdk_window_new (window,
2233 &attributes, attributes_mask);
2234 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
2236 gtk_widget_get_allocation (widget, &allocation);
2238 /* Make the column header window */
2241 attributes.width = MAX (tree_view->priv->width, allocation.width);
2242 attributes.height = tree_view->priv->header_height;
2243 attributes.event_mask = (GDK_EXPOSURE_MASK |
2245 GDK_ENTER_NOTIFY_MASK |
2246 GDK_LEAVE_NOTIFY_MASK |
2247 GDK_BUTTON_PRESS_MASK |
2248 GDK_BUTTON_RELEASE_MASK |
2249 GDK_KEY_PRESS_MASK |
2250 GDK_KEY_RELEASE_MASK |
2251 gtk_widget_get_events (widget));
2253 tree_view->priv->header_window = gdk_window_new (window,
2254 &attributes, attributes_mask);
2255 gdk_window_set_user_data (tree_view->priv->header_window, widget);
2257 /* Add them all up. */
2258 gtk_widget_style_attach (widget);
2259 style = gtk_widget_get_style (widget);
2260 gdk_window_set_background (tree_view->priv->bin_window,
2261 &style->base[gtk_widget_get_state (widget)]);
2262 gtk_style_set_background (style, tree_view->priv->header_window, GTK_STATE_NORMAL);
2264 tmp_list = tree_view->priv->children;
2267 GtkTreeViewChild *child = tmp_list->data;
2268 tmp_list = tmp_list->next;
2270 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
2273 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2274 _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data));
2276 /* Need to call those here, since they create GCs */
2277 gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
2278 gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
2280 install_presize_handler (tree_view);
2284 gtk_tree_view_unrealize (GtkWidget *widget)
2286 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2287 GtkTreeViewPrivate *priv = tree_view->priv;
2290 if (priv->scroll_timeout != 0)
2292 g_source_remove (priv->scroll_timeout);
2293 priv->scroll_timeout = 0;
2296 if (priv->auto_expand_timeout != 0)
2298 g_source_remove (priv->auto_expand_timeout);
2299 priv->auto_expand_timeout = 0;
2302 if (priv->open_dest_timeout != 0)
2304 g_source_remove (priv->open_dest_timeout);
2305 priv->open_dest_timeout = 0;
2308 remove_expand_collapse_timeout (tree_view);
2310 if (priv->presize_handler_timer != 0)
2312 g_source_remove (priv->presize_handler_timer);
2313 priv->presize_handler_timer = 0;
2316 if (priv->validate_rows_timer != 0)
2318 g_source_remove (priv->validate_rows_timer);
2319 priv->validate_rows_timer = 0;
2322 if (priv->scroll_sync_timer != 0)
2324 g_source_remove (priv->scroll_sync_timer);
2325 priv->scroll_sync_timer = 0;
2328 if (priv->typeselect_flush_timeout)
2330 g_source_remove (priv->typeselect_flush_timeout);
2331 priv->typeselect_flush_timeout = 0;
2334 for (list = priv->columns; list; list = list->next)
2335 _gtk_tree_view_column_unrealize_button (GTK_TREE_VIEW_COLUMN (list->data));
2337 gdk_window_set_user_data (priv->bin_window, NULL);
2338 gdk_window_destroy (priv->bin_window);
2339 priv->bin_window = NULL;
2341 gdk_window_set_user_data (priv->header_window, NULL);
2342 gdk_window_destroy (priv->header_window);
2343 priv->header_window = NULL;
2345 if (priv->drag_window)
2347 gdk_window_set_user_data (priv->drag_window, NULL);
2348 gdk_window_destroy (priv->drag_window);
2349 priv->drag_window = NULL;
2352 if (priv->drag_highlight_window)
2354 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
2355 gdk_window_destroy (priv->drag_highlight_window);
2356 priv->drag_highlight_window = NULL;
2359 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unrealize (widget);
2362 /* GtkWidget::size_request helper */
2364 gtk_tree_view_size_request_columns (GtkTreeView *tree_view)
2368 tree_view->priv->header_height = 0;
2370 if (tree_view->priv->model)
2372 for (list = tree_view->priv->columns; list; list = list->next)
2374 GtkRequisition requisition;
2375 GtkTreeViewColumn *column = list->data;
2376 GtkWidget *button = gtk_tree_view_column_get_button (column);
2381 column = list->data;
2383 gtk_widget_get_preferred_size (button, &requisition, NULL);
2384 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
2390 /* Called only by ::size_request */
2392 gtk_tree_view_update_size (GtkTreeView *tree_view)
2395 GtkTreeViewColumn *column;
2398 if (tree_view->priv->model == NULL)
2400 tree_view->priv->width = 0;
2401 tree_view->priv->prev_width = 0;
2402 tree_view->priv->height = 0;
2406 tree_view->priv->prev_width = tree_view->priv->width;
2407 tree_view->priv->width = 0;
2409 /* keep this in sync with size_allocate below */
2410 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
2412 column = list->data;
2413 if (!gtk_tree_view_column_get_visible (column))
2416 tree_view->priv->width += _gtk_tree_view_column_request_width (column);
2419 if (tree_view->priv->tree == NULL)
2420 tree_view->priv->height = 0;
2422 tree_view->priv->height = tree_view->priv->tree->root->offset;
2426 gtk_tree_view_size_request (GtkWidget *widget,
2427 GtkRequisition *requisition,
2428 gboolean may_validate)
2430 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2435 /* we validate some rows initially just to make sure we have some size.
2436 * In practice, with a lot of static lists, this should get a good width.
2438 do_validate_rows (tree_view, FALSE);
2441 gtk_tree_view_size_request_columns (tree_view);
2442 gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
2444 requisition->width = tree_view->priv->width;
2445 requisition->height = tree_view->priv->height + gtk_tree_view_get_effective_header_height (tree_view);
2447 tmp_list = tree_view->priv->children;
2451 gtk_tree_view_get_preferred_width (GtkWidget *widget,
2455 GtkRequisition requisition;
2457 gtk_tree_view_size_request (widget, &requisition, TRUE);
2459 *minimum = *natural = requisition.width;
2463 gtk_tree_view_get_preferred_height (GtkWidget *widget,
2467 GtkRequisition requisition;
2469 gtk_tree_view_size_request (widget, &requisition, TRUE);
2471 *minimum = *natural = requisition.height;
2475 gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view)
2481 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2482 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2483 list->data != tree_view->priv->expander_column;
2484 list = (rtl ? list->prev : list->next))
2486 GtkTreeViewColumn *column = list->data;
2488 width += gtk_tree_view_column_get_width (column);
2495 invalidate_column (GtkTreeView *tree_view,
2496 GtkTreeViewColumn *column)
2498 gint column_offset = 0;
2500 GtkWidget *widget = GTK_WIDGET (tree_view);
2503 if (!gtk_widget_get_realized (widget))
2506 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2507 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2509 list = (rtl ? list->prev : list->next))
2511 GtkTreeViewColumn *tmpcolumn = list->data;
2512 if (tmpcolumn == column)
2514 GtkAllocation allocation;
2515 GdkRectangle invalid_rect;
2517 gtk_widget_get_allocation (widget, &allocation);
2518 invalid_rect.x = column_offset;
2520 invalid_rect.width = gtk_tree_view_column_get_width (column);
2521 invalid_rect.height = allocation.height;
2523 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
2527 column_offset += gtk_tree_view_column_get_width (tmpcolumn);
2532 invalidate_last_column (GtkTreeView *tree_view)
2537 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2539 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
2541 last_column = (rtl ? last_column->next : last_column->prev))
2543 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)))
2545 invalidate_column (tree_view, last_column->data);
2551 /* GtkWidget::size_allocate helper */
2553 gtk_tree_view_size_allocate_columns (GtkWidget *widget,
2554 gboolean *width_changed)
2556 GtkTreeView *tree_view;
2557 GList *list, *first_column, *last_column;
2558 GtkTreeViewColumn *column;
2559 GtkAllocation widget_allocation;
2561 gint extra, extra_per_column, extra_for_last;
2562 gint full_requested_width = 0;
2563 gint number_of_expand_columns = 0;
2564 gboolean column_changed = FALSE;
2566 gboolean update_expand;
2568 tree_view = GTK_TREE_VIEW (widget);
2570 for (last_column = g_list_last (tree_view->priv->columns);
2572 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
2573 last_column = last_column->prev)
2575 if (last_column == NULL)
2578 for (first_column = g_list_first (tree_view->priv->columns);
2580 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
2581 first_column = first_column->next)
2584 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2586 /* find out how many extra space and expandable columns we have */
2587 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
2589 column = (GtkTreeViewColumn *)list->data;
2591 if (!gtk_tree_view_column_get_visible (column))
2594 full_requested_width += _gtk_tree_view_column_request_width (column);
2596 if (gtk_tree_view_column_get_expand (column))
2597 number_of_expand_columns++;
2600 /* Only update the expand value if the width of the widget has changed,
2601 * or the number of expand columns has changed, or if there are no expand
2602 * columns, or if we didn't have an size-allocation yet after the
2603 * last validated node.
2605 update_expand = (width_changed && *width_changed == TRUE)
2606 || number_of_expand_columns != tree_view->priv->last_number_of_expand_columns
2607 || number_of_expand_columns == 0
2608 || tree_view->priv->post_validation_flag == TRUE;
2610 tree_view->priv->post_validation_flag = FALSE;
2612 gtk_widget_get_allocation (widget, &widget_allocation);
2615 extra = tree_view->priv->last_extra_space;
2616 extra_for_last = MAX (widget_allocation.width - full_requested_width - extra, 0);
2620 extra = MAX (widget_allocation.width - full_requested_width, 0);
2623 tree_view->priv->last_extra_space = extra;
2626 if (number_of_expand_columns > 0)
2627 extra_per_column = extra/number_of_expand_columns;
2629 extra_per_column = 0;
2633 tree_view->priv->last_extra_space_per_column = extra_per_column;
2634 tree_view->priv->last_number_of_expand_columns = number_of_expand_columns;
2637 for (list = (rtl ? last_column : first_column);
2638 list != (rtl ? first_column->prev : last_column->next);
2639 list = (rtl ? list->prev : list->next))
2641 gint old_width, column_width;
2643 column = list->data;
2644 old_width = gtk_tree_view_column_get_width (column);
2646 if (!gtk_tree_view_column_get_visible (column))
2649 /* We need to handle the dragged button specially.
2651 if (column == tree_view->priv->drag_column)
2653 GtkAllocation drag_allocation;
2656 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
2658 drag_allocation.x = 0;
2659 drag_allocation.y = 0;
2660 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
2661 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
2662 gtk_widget_size_allocate (button, &drag_allocation);
2663 width += drag_allocation.width;
2667 column_width = _gtk_tree_view_column_request_width (column);
2669 if (gtk_tree_view_column_get_expand (column))
2671 if (number_of_expand_columns == 1)
2673 /* We add the remander to the last column as
2675 column_width += extra;
2679 column_width += extra_per_column;
2680 extra -= extra_per_column;
2681 number_of_expand_columns --;
2684 else if (number_of_expand_columns == 0 &&
2685 list == last_column)
2687 column_width += extra;
2690 /* In addition to expand, the last column can get even more
2691 * extra space so all available space is filled up.
2693 if (extra_for_last > 0 && list == last_column)
2694 column_width += extra_for_last;
2696 _gtk_tree_view_column_allocate (column, width, column_width);
2698 width += column_width;
2700 if (column_width > old_width)
2701 column_changed = TRUE;
2704 /* We change the width here. The user might have been resizing columns,
2705 * so the total width of the tree view changes.
2707 tree_view->priv->width = width;
2709 *width_changed = TRUE;
2712 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2717 gtk_tree_view_size_allocate (GtkWidget *widget,
2718 GtkAllocation *allocation)
2720 GtkAllocation widget_allocation;
2721 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2723 gboolean width_changed = FALSE;
2726 gtk_widget_get_allocation (widget, &widget_allocation);
2727 old_width = widget_allocation.width;
2728 if (allocation->width != widget_allocation.width)
2729 width_changed = TRUE;
2731 gtk_widget_set_allocation (widget, allocation);
2733 tmp_list = tree_view->priv->children;
2737 GtkAllocation allocation;
2739 GtkTreeViewChild *child = tmp_list->data;
2740 tmp_list = tmp_list->next;
2742 /* totally ignore our child's requisition */
2743 allocation.x = child->x;
2744 allocation.y = child->y;
2745 allocation.width = child->width;
2746 allocation.height = child->height;
2747 gtk_widget_size_allocate (child->widget, &allocation);
2750 /* We size-allocate the columns first because the width of the
2751 * tree view (used in updating the adjustments below) might change.
2753 gtk_tree_view_size_allocate_columns (widget, &width_changed);
2755 g_object_freeze_notify (G_OBJECT (tree_view->priv->hadjustment));
2756 gtk_adjustment_set_page_size (tree_view->priv->hadjustment,
2758 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment,
2759 allocation->width * 0.9);
2760 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment,
2761 allocation->width * 0.1);
2762 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2763 gtk_adjustment_set_upper (tree_view->priv->hadjustment,
2764 MAX (tree_view->priv->hadjustment->page_size,
2765 tree_view->priv->width));
2766 g_object_thaw_notify (G_OBJECT (tree_view->priv->hadjustment));
2768 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2770 if (allocation->width < tree_view->priv->width)
2772 if (tree_view->priv->init_hadjust_value)
2774 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2775 MAX (tree_view->priv->width -
2776 allocation->width, 0));
2777 tree_view->priv->init_hadjust_value = FALSE;
2779 else if (allocation->width != old_width)
2781 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2782 CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width,
2784 tree_view->priv->width - allocation->width));
2787 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2788 CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value),
2790 tree_view->priv->width - allocation->width));
2794 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2795 tree_view->priv->init_hadjust_value = TRUE;
2799 if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
2800 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2801 MAX (tree_view->priv->width -
2802 allocation->width, 0));
2804 g_object_freeze_notify (G_OBJECT (tree_view->priv->vadjustment));
2805 gtk_adjustment_set_page_size (tree_view->priv->vadjustment,
2806 allocation->height -
2807 gtk_tree_view_get_effective_header_height (tree_view));
2808 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment,
2809 tree_view->priv->vadjustment->page_size * 0.1);
2810 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment,
2811 tree_view->priv->vadjustment->page_size * 0.9);
2812 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2813 gtk_adjustment_set_upper (tree_view->priv->vadjustment,
2814 MAX (tree_view->priv->vadjustment->page_size,
2815 tree_view->priv->height));
2816 g_object_thaw_notify (G_OBJECT (tree_view->priv->vadjustment));
2818 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2819 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
2820 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2821 else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
2822 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2823 tree_view->priv->height - tree_view->priv->vadjustment->page_size);
2824 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2825 gtk_tree_view_top_row_to_dy (tree_view);
2827 gtk_tree_view_dy_to_top_row (tree_view);
2829 if (gtk_widget_get_realized (widget))
2831 gdk_window_move_resize (gtk_widget_get_window (widget),
2832 allocation->x, allocation->y,
2833 allocation->width, allocation->height);
2834 gdk_window_move_resize (tree_view->priv->header_window,
2835 - (gint) tree_view->priv->hadjustment->value,
2837 MAX (tree_view->priv->width, allocation->width),
2838 tree_view->priv->header_height);
2839 gdk_window_move_resize (tree_view->priv->bin_window,
2840 - (gint) tree_view->priv->hadjustment->value,
2841 gtk_tree_view_get_effective_header_height (tree_view),
2842 MAX (tree_view->priv->width, allocation->width),
2843 allocation->height - gtk_tree_view_get_effective_header_height (tree_view));
2846 if (tree_view->priv->tree == NULL)
2847 invalidate_empty_focus (tree_view);
2849 if (gtk_widget_get_realized (widget))
2851 gboolean has_expand_column = FALSE;
2852 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2854 if (gtk_tree_view_column_get_expand (GTK_TREE_VIEW_COLUMN (tmp_list->data)))
2856 has_expand_column = TRUE;
2861 if (width_changed && tree_view->priv->expander_column)
2863 /* Might seem awkward, but is the best heuristic I could come up
2864 * with. Only if the width of the columns before the expander
2865 * changes, we will update the prelight status. It is this
2866 * width that makes the expander move vertically. Always updating
2867 * prelight status causes trouble with hover selections.
2869 gint width_before_expander;
2871 width_before_expander = gtk_tree_view_calculate_width_before_expander (tree_view);
2873 if (tree_view->priv->prev_width_before_expander
2874 != width_before_expander)
2875 update_prelight (tree_view,
2876 tree_view->priv->event_last_x,
2877 tree_view->priv->event_last_y);
2879 tree_view->priv->prev_width_before_expander = width_before_expander;
2882 /* This little hack only works if we have an LTR locale, and no column has the */
2885 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2886 ! has_expand_column)
2887 invalidate_last_column (tree_view);
2889 gtk_widget_queue_draw (widget);
2894 /* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */
2896 grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view)
2898 GtkWidget *widget = GTK_WIDGET (tree_view);
2900 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2901 gtk_widget_grab_focus (widget);
2902 tree_view->priv->draw_keyfocus = 0;
2905 static inline gboolean
2906 row_is_separator (GtkTreeView *tree_view,
2910 gboolean is_separator = FALSE;
2912 if (tree_view->priv->row_separator_func)
2914 GtkTreeIter tmpiter;
2919 gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, path);
2921 is_separator = tree_view->priv->row_separator_func (tree_view->priv->model,
2923 tree_view->priv->row_separator_data);
2926 return is_separator;
2930 gtk_tree_view_button_press (GtkWidget *widget,
2931 GdkEventButton *event)
2933 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2935 GtkTreeViewColumn *column = NULL;
2937 GdkRectangle background_area;
2938 GdkRectangle cell_area;
2939 gint vertical_separator;
2940 gint horizontal_separator;
2941 gboolean path_is_selectable;
2944 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2945 gtk_tree_view_stop_editing (tree_view, FALSE);
2946 gtk_widget_style_get (widget,
2947 "vertical-separator", &vertical_separator,
2948 "horizontal-separator", &horizontal_separator,
2952 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2953 * we're done handling the button press.
2956 if (event->window == tree_view->priv->bin_window)
2965 gint pre_val, aft_val;
2966 GtkTreeViewColumn *column = NULL;
2967 gint column_handled_click = FALSE;
2968 gboolean row_double_click = FALSE;
2970 gboolean node_selected;
2973 if (tree_view->priv->tree == NULL)
2975 grab_focus_and_unset_draw_keyfocus (tree_view);
2979 /* are we in an arrow? */
2980 if (tree_view->priv->prelight_node &&
2981 tree_view->priv->arrow_prelit &&
2982 gtk_tree_view_draw_expanders (tree_view))
2984 if (event->button == 1)
2986 gtk_grab_add (widget);
2987 tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
2988 tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
2989 gtk_tree_view_queue_draw_arrow (GTK_TREE_VIEW (widget),
2990 tree_view->priv->prelight_tree,
2991 tree_view->priv->prelight_node);
2994 grab_focus_and_unset_draw_keyfocus (tree_view);
2998 /* find the node that was clicked */
2999 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3002 y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
3006 /* We clicked in dead space */
3007 grab_focus_and_unset_draw_keyfocus (tree_view);
3011 /* Get the path and the node */
3012 path = _gtk_tree_view_find_path (tree_view, tree, node);
3013 path_is_selectable = !row_is_separator (tree_view, NULL, path);
3015 if (!path_is_selectable)
3017 gtk_tree_path_free (path);
3018 grab_focus_and_unset_draw_keyfocus (tree_view);
3022 depth = gtk_tree_path_get_depth (path);
3023 background_area.y = y_offset + event->y;
3024 background_area.height = gtk_tree_view_get_row_height (tree_view, node);
3025 background_area.x = 0;
3028 /* Let the column have a chance at selecting it. */
3029 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
3030 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3031 list; list = (rtl ? list->prev : list->next))
3033 GtkTreeViewColumn *candidate = list->data;
3035 if (!gtk_tree_view_column_get_visible (candidate))
3038 background_area.width = gtk_tree_view_column_get_width (candidate);
3039 if ((background_area.x > (gint) event->x) ||
3040 (background_area.x + background_area.width <= (gint) event->x))
3042 background_area.x += background_area.width;
3046 /* we found the focus column */
3048 cell_area = background_area;
3049 cell_area.width -= horizontal_separator;
3050 cell_area.height -= vertical_separator;
3051 cell_area.x += horizontal_separator/2;
3052 cell_area.y += vertical_separator/2;
3053 if (gtk_tree_view_is_expander_column (tree_view, column))
3056 cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
3057 cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
3059 if (gtk_tree_view_draw_expanders (tree_view))
3062 cell_area.x += depth * tree_view->priv->expander_size;
3063 cell_area.width -= depth * tree_view->priv->expander_size;
3071 gtk_tree_path_free (path);
3072 grab_focus_and_unset_draw_keyfocus (tree_view);
3076 tree_view->priv->focus_column = column;
3078 /* decide if we edit */
3079 if (event->type == GDK_BUTTON_PRESS && event->button == 1 &&
3080 !(event->state & gtk_accelerator_get_default_mod_mask ()))
3082 GtkTreePath *anchor;
3085 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
3086 gtk_tree_view_column_cell_set_cell_data (column,
3087 tree_view->priv->model,
3089 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
3090 node->children?TRUE:FALSE);
3092 if (tree_view->priv->anchor)
3093 anchor = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
3097 if ((anchor && !gtk_tree_path_compare (anchor, path))
3098 || !_gtk_tree_view_column_has_editable_cell (column))
3100 GtkCellEditable *cell_editable = NULL;
3102 /* FIXME: get the right flags */
3105 if (_gtk_tree_view_column_cell_event (column,
3109 GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
3110 cell_editable = gtk_cell_area_get_edit_widget (area);
3112 if (cell_editable != NULL)
3114 gtk_tree_path_free (path);
3115 gtk_tree_path_free (anchor);
3118 column_handled_click = TRUE;
3122 gtk_tree_path_free (anchor);
3126 node_selected = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
3127 pre_val = tree_view->priv->vadjustment->value;
3129 /* we only handle selection modifications on the first button press
3131 if (event->type == GDK_BUTTON_PRESS)
3133 GtkCellRenderer *focus_cell;
3135 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
3136 tree_view->priv->ctrl_pressed = TRUE;
3137 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
3138 tree_view->priv->shift_pressed = TRUE;
3140 /* We update the focus cell here, this is also needed if the
3141 * column does not contain an editable cell. In this case,
3142 * GtkCellArea did not receive the event for processing (and
3143 * could not update the focus cell).
3145 focus_cell = _gtk_tree_view_column_get_cell_at_pos (column,
3152 gtk_tree_view_column_focus_cell (column, focus_cell);
3154 if (event->state & GDK_CONTROL_MASK)
3156 gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
3157 gtk_tree_view_real_toggle_cursor_row (tree_view);
3159 else if (event->state & GDK_SHIFT_MASK)
3161 gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
3162 gtk_tree_view_real_select_cursor_row (tree_view, FALSE);
3166 gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
3169 tree_view->priv->ctrl_pressed = FALSE;
3170 tree_view->priv->shift_pressed = FALSE;
3173 /* the treeview may have been scrolled because of _set_cursor,
3177 aft_val = tree_view->priv->vadjustment->value;
3178 dval = pre_val - aft_val;
3180 cell_area.y += dval;
3181 background_area.y += dval;
3183 /* Save press to possibly begin a drag
3185 if (!column_handled_click &&
3186 !tree_view->priv->in_grab &&
3187 tree_view->priv->pressed_button < 0)
3189 tree_view->priv->pressed_button = event->button;
3190 tree_view->priv->press_start_x = event->x;
3191 tree_view->priv->press_start_y = event->y;
3193 if (tree_view->priv->rubber_banding_enable
3195 && gtk_tree_selection_get_mode (tree_view->priv->selection) == GTK_SELECTION_MULTIPLE)
3197 tree_view->priv->press_start_y += tree_view->priv->dy;
3198 tree_view->priv->rubber_band_x = event->x;
3199 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
3200 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
3202 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
3203 tree_view->priv->rubber_band_ctrl = TRUE;
3204 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
3205 tree_view->priv->rubber_band_shift = TRUE;
3209 /* Test if a double click happened on the same row. */
3210 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
3212 int double_click_time, double_click_distance;
3214 g_object_get (gtk_settings_get_default (),
3215 "gtk-double-click-time", &double_click_time,
3216 "gtk-double-click-distance", &double_click_distance,
3219 /* Same conditions as _gdk_event_button_generate */
3220 if (tree_view->priv->last_button_x != -1 &&
3221 (event->time < tree_view->priv->last_button_time + double_click_time) &&
3222 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
3223 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
3225 /* We do no longer compare paths of this row and the
3226 * row clicked previously. We use the double click
3227 * distance to decide whether this is a valid click,
3228 * allowing the mouse to slightly move over another row.
3230 row_double_click = TRUE;
3232 tree_view->priv->last_button_time = 0;
3233 tree_view->priv->last_button_x = -1;
3234 tree_view->priv->last_button_y = -1;
3238 tree_view->priv->last_button_time = event->time;
3239 tree_view->priv->last_button_x = event->x;
3240 tree_view->priv->last_button_y = event->y;
3244 if (row_double_click)
3246 gtk_grab_remove (widget);
3247 gtk_tree_view_row_activated (tree_view, path, column);
3249 if (tree_view->priv->pressed_button == event->button)
3250 tree_view->priv->pressed_button = -1;
3253 gtk_tree_path_free (path);
3255 /* If we activated the row through a double click we don't want to grab
3256 * focus back, as moving focus to another widget is pretty common.
3258 if (!row_double_click)
3259 grab_focus_and_unset_draw_keyfocus (tree_view);
3264 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
3266 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
3268 column = list->data;
3269 if (event->window == _gtk_tree_view_column_get_window (column) &&
3270 gtk_tree_view_column_get_resizable (column) &&
3271 _gtk_tree_view_column_get_window (column))
3274 GtkAllocation button_allocation;
3277 if (event->type == GDK_2BUTTON_PRESS &&
3278 gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE)
3280 _gtk_tree_view_column_set_use_resized_width (column, FALSE);
3281 _gtk_tree_view_column_autosize (tree_view, column);
3285 if (gdk_device_grab (gdk_event_get_device ((GdkEvent*)event),
3286 _gtk_tree_view_column_get_window (column),
3289 GDK_POINTER_MOTION_HINT_MASK
3290 | GDK_BUTTON1_MOTION_MASK
3291 | GDK_BUTTON_RELEASE_MASK,
3293 event->time) != GDK_GRAB_SUCCESS)
3296 gtk_grab_add (widget);
3297 tree_view->priv->in_column_resize = TRUE;
3299 _gtk_tree_view_column_set_resized_width (column, gtk_tree_view_column_get_width (column) -
3300 tree_view->priv->last_extra_space_per_column);
3302 /* block attached dnd signal handler */
3303 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
3305 g_signal_handlers_block_matched (widget,
3306 G_SIGNAL_MATCH_DATA,
3310 button = gtk_tree_view_column_get_button (column);
3311 gtk_widget_get_allocation (button, &button_allocation);
3312 tree_view->priv->drag_pos = i;
3313 tree_view->priv->x_drag = button_allocation.x + (rtl ? 0 : button_allocation.width);
3315 if (!gtk_widget_has_focus (widget))
3316 gtk_widget_grab_focus (widget);
3324 /* GtkWidget::button_release_event helper */
3326 gtk_tree_view_button_release_drag_column (GtkWidget *widget,
3327 GdkEventButton *event)
3329 GtkTreeView *tree_view;
3333 GdkDevice *device, *other;
3335 tree_view = GTK_TREE_VIEW (widget);
3337 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3338 device = gdk_event_get_device ((GdkEvent*)event);
3339 other = gdk_device_get_associated_device (device);
3340 gdk_device_ungrab (device, event->time);
3341 gdk_device_ungrab (other, event->time);
3343 /* Move the button back */
3344 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
3345 g_object_ref (button);
3346 gtk_container_remove (GTK_CONTAINER (tree_view), button);
3347 gtk_widget_set_parent_window (button, tree_view->priv->header_window);
3348 gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
3349 g_object_unref (button);
3350 gtk_widget_queue_resize (widget);
3351 if (gtk_tree_view_column_get_resizable (tree_view->priv->drag_column))
3353 gdk_window_raise (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
3354 gdk_window_show (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
3357 gdk_window_hide (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
3359 gtk_widget_grab_focus (button);
3363 if (tree_view->priv->cur_reorder &&
3364 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
3365 gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column,
3366 tree_view->priv->cur_reorder->right_column);
3370 if (tree_view->priv->cur_reorder &&
3371 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
3372 gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column,
3373 tree_view->priv->cur_reorder->left_column);
3375 tree_view->priv->drag_column = NULL;
3376 gdk_window_hide (tree_view->priv->drag_window);
3378 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
3379 g_slice_free (GtkTreeViewColumnReorder, l->data);
3380 g_list_free (tree_view->priv->column_drag_info);
3381 tree_view->priv->column_drag_info = NULL;
3382 tree_view->priv->cur_reorder = NULL;
3384 if (tree_view->priv->drag_highlight_window)
3385 gdk_window_hide (tree_view->priv->drag_highlight_window);
3387 /* Reset our flags */
3388 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
3389 tree_view->priv->in_column_drag = FALSE;
3394 /* GtkWidget::button_release_event helper */
3396 gtk_tree_view_button_release_column_resize (GtkWidget *widget,
3397 GdkEventButton *event)
3399 GtkTreeView *tree_view;
3402 tree_view = GTK_TREE_VIEW (widget);
3404 tree_view->priv->drag_pos = -1;
3406 /* unblock attached dnd signal handler */
3407 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
3409 g_signal_handlers_unblock_matched (widget,
3410 G_SIGNAL_MATCH_DATA,
3414 tree_view->priv->in_column_resize = FALSE;
3415 gtk_grab_remove (widget);
3416 gdk_device_ungrab (gdk_event_get_device ((GdkEvent*)event), event->time);
3421 gtk_tree_view_button_release (GtkWidget *widget,
3422 GdkEventButton *event)
3424 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
3426 if (tree_view->priv->in_column_drag)
3427 return gtk_tree_view_button_release_drag_column (widget, event);
3429 if (tree_view->priv->rubber_band_status)
3430 gtk_tree_view_stop_rubber_band (tree_view);
3432 if (tree_view->priv->pressed_button == event->button)
3433 tree_view->priv->pressed_button = -1;
3435 if (tree_view->priv->in_column_resize)
3436 return gtk_tree_view_button_release_column_resize (widget, event);
3438 if (tree_view->priv->button_pressed_node == NULL)
3441 if (event->button == 1)
3443 gtk_grab_remove (widget);
3444 if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node &&
3445 tree_view->priv->arrow_prelit)
3447 GtkTreePath *path = NULL;
3449 path = _gtk_tree_view_find_path (tree_view,
3450 tree_view->priv->button_pressed_tree,
3451 tree_view->priv->button_pressed_node);
3452 /* Actually activate the node */
3453 if (tree_view->priv->button_pressed_node->children == NULL)
3454 gtk_tree_view_real_expand_row (tree_view, path,
3455 tree_view->priv->button_pressed_tree,
3456 tree_view->priv->button_pressed_node,
3459 gtk_tree_view_real_collapse_row (GTK_TREE_VIEW (widget), path,
3460 tree_view->priv->button_pressed_tree,
3461 tree_view->priv->button_pressed_node, TRUE);
3462 gtk_tree_path_free (path);
3465 tree_view->priv->button_pressed_tree = NULL;
3466 tree_view->priv->button_pressed_node = NULL;
3473 gtk_tree_view_grab_broken (GtkWidget *widget,
3474 GdkEventGrabBroken *event)
3476 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
3478 if (tree_view->priv->in_column_drag)
3479 gtk_tree_view_button_release_drag_column (widget, (GdkEventButton *)event);
3481 if (tree_view->priv->in_column_resize)
3482 gtk_tree_view_button_release_column_resize (widget, (GdkEventButton *)event);
3489 gtk_tree_view_configure (GtkWidget *widget,
3490 GdkEventConfigure *event)
3492 GtkTreeView *tree_view;
3494 tree_view = GTK_TREE_VIEW (widget);
3495 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window);
3501 /* GtkWidget::motion_event function set.
3505 coords_are_over_arrow (GtkTreeView *tree_view,
3508 /* these are in bin window coords */
3515 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
3518 if ((node->flags & GTK_RBNODE_IS_PARENT) == 0)
3521 arrow.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
3522 arrow.height = gtk_tree_view_get_row_height (tree_view, node);
3524 gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2);
3526 arrow.width = x2 - arrow.x;
3528 return (x >= arrow.x &&
3529 x < (arrow.x + arrow.width) &&
3531 y < (arrow.y + arrow.height));
3535 auto_expand_timeout (gpointer data)
3537 GtkTreeView *tree_view = GTK_TREE_VIEW (data);
3540 if (tree_view->priv->prelight_node)
3542 path = _gtk_tree_view_find_path (tree_view,
3543 tree_view->priv->prelight_tree,
3544 tree_view->priv->prelight_node);
3546 if (tree_view->priv->prelight_node->children)
3547 gtk_tree_view_collapse_row (tree_view, path);
3549 gtk_tree_view_expand_row (tree_view, path, FALSE);
3551 gtk_tree_path_free (path);
3554 tree_view->priv->auto_expand_timeout = 0;
3560 remove_auto_expand_timeout (GtkTreeView *tree_view)
3562 if (tree_view->priv->auto_expand_timeout != 0)
3564 g_source_remove (tree_view->priv->auto_expand_timeout);
3565 tree_view->priv->auto_expand_timeout = 0;
3570 do_prelight (GtkTreeView *tree_view,
3573 /* these are in bin_window coords */
3577 if (tree_view->priv->prelight_tree == tree &&
3578 tree_view->priv->prelight_node == node)
3580 /* We are still on the same node,
3581 but we might need to take care of the arrow */
3583 if (tree && node && gtk_tree_view_draw_expanders (tree_view))
3585 gboolean over_arrow;
3587 over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
3589 if (over_arrow != tree_view->priv->arrow_prelit)
3592 tree_view->priv->arrow_prelit = TRUE;
3594 tree_view->priv->arrow_prelit = FALSE;
3596 gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
3603 if (tree_view->priv->prelight_tree && tree_view->priv->prelight_node)
3605 /* Unprelight the old node and arrow */
3607 GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node,
3608 GTK_RBNODE_IS_PRELIT);
3610 if (tree_view->priv->arrow_prelit
3611 && gtk_tree_view_draw_expanders (tree_view))
3613 tree_view->priv->arrow_prelit = FALSE;
3615 gtk_tree_view_queue_draw_arrow (tree_view,
3616 tree_view->priv->prelight_tree,
3617 tree_view->priv->prelight_node);
3620 _gtk_tree_view_queue_draw_node (tree_view,
3621 tree_view->priv->prelight_tree,
3622 tree_view->priv->prelight_node,
3627 if (tree_view->priv->hover_expand)
3628 remove_auto_expand_timeout (tree_view);
3630 /* Set the new prelight values */
3631 tree_view->priv->prelight_node = node;
3632 tree_view->priv->prelight_tree = tree;
3637 /* Prelight the new node and arrow */
3639 if (gtk_tree_view_draw_expanders (tree_view)
3640 && coords_are_over_arrow (tree_view, tree, node, x, y))
3642 tree_view->priv->arrow_prelit = TRUE;
3644 gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
3647 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
3649 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
3651 if (tree_view->priv->hover_expand)
3653 tree_view->priv->auto_expand_timeout =
3654 gdk_threads_add_timeout (AUTO_EXPAND_TIMEOUT, auto_expand_timeout, tree_view);
3659 prelight_or_select (GtkTreeView *tree_view,
3662 /* these are in bin_window coords */
3666 GtkSelectionMode mode = gtk_tree_selection_get_mode (tree_view->priv->selection);
3668 if (tree_view->priv->hover_selection &&
3669 (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) &&
3670 !(tree_view->priv->edited_column &&
3671 gtk_cell_area_get_edit_widget
3672 (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column)))))
3676 if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
3680 path = _gtk_tree_view_find_path (tree_view, tree, node);
3681 gtk_tree_selection_select_path (tree_view->priv->selection, path);
3682 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
3684 tree_view->priv->draw_keyfocus = FALSE;
3685 gtk_tree_view_real_set_cursor (tree_view, path, FALSE, FALSE);
3687 gtk_tree_path_free (path);
3691 else if (mode == GTK_SELECTION_SINGLE)
3692 gtk_tree_selection_unselect_all (tree_view->priv->selection);
3695 do_prelight (tree_view, tree, node, x, y);
3699 ensure_unprelighted (GtkTreeView *tree_view)
3701 do_prelight (tree_view,
3703 -1000, -1000); /* coords not possibly over an arrow */
3705 g_assert (tree_view->priv->prelight_node == NULL);
3709 update_prelight (GtkTreeView *tree_view,
3717 if (tree_view->priv->tree == NULL)
3722 ensure_unprelighted (tree_view);
3726 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
3730 _gtk_rbtree_find_offset (tree_view->priv->tree,
3731 new_y, &tree, &node);
3734 prelight_or_select (tree_view, tree, node, x, y);
3740 /* Our motion arrow is either a box (in the case of the original spot)
3741 * or an arrow. It is expander_size wide.
3764 gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
3766 GtkTreeViewColumnReorder *reorder = tree_view->priv->cur_reorder;
3767 GtkWidget *widget = GTK_WIDGET (tree_view);
3768 cairo_surface_t *mask_image;
3769 cairo_region_t *mask_region;
3774 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
3775 GdkWindowAttr attributes;
3776 guint attributes_mask;
3780 reorder->left_column == tree_view->priv->drag_column ||
3781 reorder->right_column == tree_view->priv->drag_column)
3782 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3783 else if (reorder->left_column || reorder->right_column)
3785 GtkAllocation left_allocation, right_allocation;
3786 GdkRectangle visible_rect;
3789 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
3790 if (reorder->left_column)
3792 button = gtk_tree_view_column_get_button (reorder->left_column);
3793 gtk_widget_get_allocation (button, &left_allocation);
3794 x = left_allocation.x + left_allocation.width;
3798 button = gtk_tree_view_column_get_button (reorder->right_column);
3799 gtk_widget_get_allocation (button, &right_allocation);
3800 x = right_allocation.x;
3803 if (x < visible_rect.x)
3804 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
3805 else if (x > visible_rect.x + visible_rect.width)
3806 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
3808 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
3811 /* We want to draw the rectangle over the initial location. */
3812 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3814 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3816 GtkAllocation drag_allocation;
3819 if (tree_view->priv->drag_highlight_window)
3821 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3823 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3826 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
3827 attributes.window_type = GDK_WINDOW_CHILD;
3828 attributes.wclass = GDK_INPUT_OUTPUT;
3829 attributes.x = tree_view->priv->drag_column_x;
3831 gtk_widget_get_allocation (button, &drag_allocation);
3832 width = attributes.width = drag_allocation.width;
3833 height = attributes.height = drag_allocation.height;
3834 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3835 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3836 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3837 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3838 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3840 mask_image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
3841 cr = cairo_create (mask_image);
3843 cairo_rectangle (cr, 1, 1, width - 2, height - 2);
3847 mask_region = gdk_cairo_region_create_from_surface (mask_image);
3848 gdk_window_shape_combine_region (tree_view->priv->drag_highlight_window,
3851 cairo_region_destroy (mask_region);
3852 cairo_surface_destroy (mask_image);
3854 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3857 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3859 GtkAllocation button_allocation;
3862 width = tree_view->priv->expander_size;
3864 /* Get x, y, width, height of arrow */
3865 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3866 if (reorder->left_column)
3868 button = gtk_tree_view_column_get_button (reorder->left_column);
3869 gtk_widget_get_allocation (button, &button_allocation);
3870 x += button_allocation.x + button_allocation.width - width/2;
3871 height = button_allocation.height;
3875 button = gtk_tree_view_column_get_button (reorder->right_column);
3876 gtk_widget_get_allocation (button, &button_allocation);
3877 x += button_allocation.x - width/2;
3878 height = button_allocation.height;
3880 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3881 height += tree_view->priv->expander_size;
3883 /* Create the new window */
3884 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3886 if (tree_view->priv->drag_highlight_window)
3888 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3890 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3893 attributes.window_type = GDK_WINDOW_TEMP;
3894 attributes.wclass = GDK_INPUT_OUTPUT;
3895 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3896 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3897 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3900 attributes.width = width;
3901 attributes.height = height;
3902 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3903 &attributes, attributes_mask);
3904 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3906 mask_image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
3908 cr = cairo_create (mask_image);
3909 cairo_move_to (cr, 0, 0);
3910 cairo_line_to (cr, width, 0);
3911 cairo_line_to (cr, width / 2., width / 2);
3912 cairo_move_to (cr, 0, height);
3913 cairo_line_to (cr, width, height);
3914 cairo_line_to (cr, width / 2., height - width / 2.);
3918 mask_region = gdk_cairo_region_create_from_surface (mask_image);
3919 gdk_window_shape_combine_region (tree_view->priv->drag_highlight_window,
3922 cairo_region_destroy (mask_region);
3923 cairo_surface_destroy (mask_image);
3926 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3927 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3929 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3930 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3932 GtkAllocation allocation;
3935 width = tree_view->priv->expander_size;
3937 /* Get x, y, width, height of arrow */
3938 width = width/2; /* remember, the arrow only takes half the available width */
3939 gdk_window_get_origin (gtk_widget_get_window (widget),
3941 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3943 gtk_widget_get_allocation (widget, &allocation);
3944 x += allocation.width - width;
3947 if (reorder->left_column)
3949 button = gtk_tree_view_column_get_button (reorder->left_column);
3950 gtk_widget_get_allocation (button, &allocation);
3951 height = allocation.height;
3955 button = gtk_tree_view_column_get_button (reorder->right_column);
3956 gtk_widget_get_allocation (button, &allocation);
3957 height = allocation.height;
3960 y -= tree_view->priv->expander_size;
3961 height += 2*tree_view->priv->expander_size;
3963 /* Create the new window */
3964 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3965 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3967 if (tree_view->priv->drag_highlight_window)
3969 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3971 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3974 attributes.window_type = GDK_WINDOW_TEMP;
3975 attributes.wclass = GDK_INPUT_OUTPUT;
3976 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3977 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3978 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3981 attributes.width = width;
3982 attributes.height = height;
3983 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3984 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3986 mask_image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
3988 cr = cairo_create (mask_image);
3989 /* mirror if we're on the left */
3990 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3992 cairo_translate (cr, width, 0);
3993 cairo_scale (cr, -1, 1);
3995 cairo_move_to (cr, 0, 0);
3996 cairo_line_to (cr, width, width);
3997 cairo_line_to (cr, 0, tree_view->priv->expander_size);
3998 cairo_move_to (cr, 0, height);
3999 cairo_line_to (cr, width, height - width);
4000 cairo_line_to (cr, 0, height - tree_view->priv->expander_size);
4004 mask_region = gdk_cairo_region_create_from_surface (mask_image);
4005 gdk_window_shape_combine_region (tree_view->priv->drag_highlight_window,
4008 cairo_region_destroy (mask_region);
4009 cairo_surface_destroy (mask_image);
4012 tree_view->priv->drag_column_window_state = arrow_type;
4013 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
4017 g_warning (G_STRLOC"Invalid GtkTreeViewColumnReorder struct");
4018 gdk_window_hide (tree_view->priv->drag_highlight_window);
4022 gdk_window_show (tree_view->priv->drag_highlight_window);
4023 gdk_window_raise (tree_view->priv->drag_highlight_window);
4027 gtk_tree_view_motion_resize_column (GtkWidget *widget,
4028 GdkEventMotion *event)
4032 GtkTreeViewColumn *column;
4033 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
4035 column = gtk_tree_view_get_column (tree_view, tree_view->priv->drag_pos);
4037 if (event->is_hint || event->window != gtk_widget_get_window (widget))
4038 gtk_widget_get_pointer (widget, &x, NULL);
4042 if (tree_view->priv->hadjustment)
4043 x += tree_view->priv->hadjustment->value;
4045 new_width = gtk_tree_view_new_column_width (tree_view,
4046 tree_view->priv->drag_pos, &x);
4047 if (x != tree_view->priv->x_drag &&
4048 (new_width != gtk_tree_view_column_get_fixed_width (column)))
4050 _gtk_tree_view_column_set_use_resized_width (column, TRUE);
4052 if (gtk_tree_view_column_get_expand (column))
4053 new_width -= tree_view->priv->last_extra_space_per_column;
4055 _gtk_tree_view_column_set_resized_width (column, new_width);
4058 gtk_widget_queue_resize (widget);
4066 gtk_tree_view_update_current_reorder (GtkTreeView *tree_view)
4068 GtkTreeViewColumnReorder *reorder = NULL;
4072 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
4073 for (list = tree_view->priv->column_drag_info; list; list = list->next)
4075 reorder = (GtkTreeViewColumnReorder *) list->data;
4076 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
4081 /* if (reorder && reorder == tree_view->priv->cur_reorder)
4084 tree_view->priv->cur_reorder = reorder;
4085 gtk_tree_view_motion_draw_column_motion_arrow (tree_view);
4089 gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view)
4091 GdkRectangle visible_rect;
4095 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
4096 y += tree_view->priv->dy;
4098 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
4100 /* see if we are near the edge. */
4101 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
4104 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
4109 gtk_adjustment_set_value (tree_view->priv->vadjustment,
4110 MAX (tree_view->priv->vadjustment->value + offset, 0.0));
4114 gtk_tree_view_horizontal_autoscroll (GtkTreeView *tree_view)
4116 GdkRectangle visible_rect;
4120 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
4122 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
4124 /* See if we are near the edge. */
4125 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
4128 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
4134 gtk_adjustment_set_value (tree_view->priv->hadjustment,
4135 MAX (tree_view->priv->hadjustment->value + offset, 0.0));
4142 gtk_tree_view_motion_drag_column (GtkWidget *widget,
4143 GdkEventMotion *event)
4145 GtkAllocation allocation, button_allocation;
4146 GtkTreeView *tree_view = (GtkTreeView *) widget;
4147 GtkTreeViewColumn *column = tree_view->priv->drag_column;
4152 if ((column == NULL) ||
4153 (event->window != tree_view->priv->drag_window))
4156 button = gtk_tree_view_column_get_button (column);
4158 /* Handle moving the header */
4159 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
4160 gtk_widget_get_allocation (widget, &allocation);
4161 gtk_widget_get_allocation (button, &button_allocation);
4162 x = CLAMP (x + (gint)event->x - _gtk_tree_view_column_get_drag_x (column), 0,
4163 MAX (tree_view->priv->width, allocation.width) - button_allocation.width);
4164 gdk_window_move (tree_view->priv->drag_window, x, y);
4166 /* autoscroll, if needed */
4167 gtk_tree_view_horizontal_autoscroll (tree_view);
4168 /* Update the current reorder position and arrow; */
4169 gtk_tree_view_update_current_reorder (tree_view);
4175 gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view)
4177 remove_scroll_timeout (tree_view);
4178 gtk_grab_remove (GTK_WIDGET (tree_view));
4180 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4182 GtkTreePath *tmp_path;
4184 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
4186 /* The anchor path should be set to the start path */
4187 tmp_path = _gtk_tree_view_find_path (tree_view,
4188 tree_view->priv->rubber_band_start_tree,
4189 tree_view->priv->rubber_band_start_node);
4191 if (tree_view->priv->anchor)
4192 gtk_tree_row_reference_free (tree_view->priv->anchor);
4194 tree_view->priv->anchor =
4195 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
4196 tree_view->priv->model,
4199 gtk_tree_path_free (tmp_path);
4201 /* ... and the cursor to the end path */
4202 tmp_path = _gtk_tree_view_find_path (tree_view,
4203 tree_view->priv->rubber_band_end_tree,
4204 tree_view->priv->rubber_band_end_node);
4205 gtk_tree_view_real_set_cursor (GTK_TREE_VIEW (tree_view), tmp_path, FALSE, FALSE);
4206 gtk_tree_path_free (tmp_path);
4208 _gtk_tree_selection_emit_changed (tree_view->priv->selection);
4211 /* Clear status variables */
4212 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
4213 tree_view->priv->rubber_band_shift = 0;
4214 tree_view->priv->rubber_band_ctrl = 0;
4216 tree_view->priv->rubber_band_start_node = NULL;
4217 tree_view->priv->rubber_band_start_tree = NULL;
4218 tree_view->priv->rubber_band_end_node = NULL;
4219 tree_view->priv->rubber_band_end_tree = NULL;
4223 gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view,
4224 GtkRBTree *start_tree,
4225 GtkRBNode *start_node,
4226 GtkRBTree *end_tree,
4227 GtkRBNode *end_node,
4229 gboolean skip_start,
4232 if (start_node == end_node)
4235 /* We skip the first node and jump inside the loop */
4241 /* Small optimization by assuming insensitive nodes are never
4244 if (!GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
4247 gboolean selectable;
4249 path = _gtk_tree_view_find_path (tree_view, start_tree, start_node);
4250 selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection, start_node, path);
4251 gtk_tree_path_free (path);
4254 goto node_not_selectable;
4259 if (tree_view->priv->rubber_band_shift)
4260 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4261 else if (tree_view->priv->rubber_band_ctrl)
4263 /* Toggle the selection state */
4264 if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
4265 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4267 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4270 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4274 /* Mirror the above */
4275 if (tree_view->priv->rubber_band_shift)
4276 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4277 else if (tree_view->priv->rubber_band_ctrl)
4279 /* Toggle the selection state */
4280 if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
4281 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4283 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4286 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4289 _gtk_tree_view_queue_draw_node (tree_view, start_tree, start_node, NULL);
4291 node_not_selectable:
4292 if (start_node == end_node)
4297 if (start_node->children)
4299 start_tree = start_node->children;
4300 start_node = start_tree->root;
4301 while (start_node->left != start_tree->nil)
4302 start_node = start_node->left;
4306 _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
4309 /* Ran out of tree */
4313 if (skip_end && start_node == end_node)
4320 gtk_tree_view_update_rubber_band_selection (GtkTreeView *tree_view)
4322 GtkRBTree *start_tree, *end_tree;
4323 GtkRBNode *start_node, *end_node;
4325 _gtk_rbtree_find_offset (tree_view->priv->tree, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_tree, &start_node);
4326 _gtk_rbtree_find_offset (tree_view->priv->tree, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_tree, &end_node);
4328 /* Handle the start area first */
4329 if (!tree_view->priv->rubber_band_start_node)
4331 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4340 else if (_gtk_rbtree_node_find_offset (start_tree, start_node) <
4341 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_start_tree, tree_view->priv->rubber_band_start_node))
4343 /* New node is above the old one; selection became bigger */
4344 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4347 tree_view->priv->rubber_band_start_tree,
4348 tree_view->priv->rubber_band_start_node,
4353 else if (_gtk_rbtree_node_find_offset (start_tree, start_node) >
4354 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_start_tree, tree_view->priv->rubber_band_start_node))
4356 /* New node is below the old one; selection became smaller */
4357 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4358 tree_view->priv->rubber_band_start_tree,
4359 tree_view->priv->rubber_band_start_node,
4367 tree_view->priv->rubber_band_start_tree = start_tree;
4368 tree_view->priv->rubber_band_start_node = start_node;
4370 /* Next, handle the end area */
4371 if (!tree_view->priv->rubber_band_end_node)
4373 /* In the event this happens, start_node was also NULL; this case is
4379 /* Find the last node in the tree */
4380 _gtk_rbtree_find_offset (tree_view->priv->tree, tree_view->priv->height - 1,
4381 &end_tree, &end_node);
4383 /* Selection reached end of the tree */
4384 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4385 tree_view->priv->rubber_band_end_tree,
4386 tree_view->priv->rubber_band_end_node,
4393 else if (_gtk_rbtree_node_find_offset (end_tree, end_node) >
4394 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_end_tree, tree_view->priv->rubber_band_end_node))
4396 /* New node is below the old one; selection became bigger */
4397 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4398 tree_view->priv->rubber_band_end_tree,
4399 tree_view->priv->rubber_band_end_node,
4406 else if (_gtk_rbtree_node_find_offset (end_tree, end_node) <
4407 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_end_tree, tree_view->priv->rubber_band_end_node))
4409 /* New node is above the old one; selection became smaller */
4410 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4413 tree_view->priv->rubber_band_end_tree,
4414 tree_view->priv->rubber_band_end_node,
4420 tree_view->priv->rubber_band_end_tree = end_tree;
4421 tree_view->priv->rubber_band_end_node = end_node;
4425 gtk_tree_view_update_rubber_band (GtkTreeView *tree_view)
4428 GdkRectangle old_area;
4429 GdkRectangle new_area;
4430 GdkRectangle common;
4431 cairo_region_t *invalid_region;
4433 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
4434 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
4435 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
4436 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
4438 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
4441 y = MAX (y, 0) + tree_view->priv->dy;
4443 new_area.x = MIN (tree_view->priv->press_start_x, x);
4444 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
4445 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
4446 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
4448 invalid_region = cairo_region_create_rectangle (&old_area);
4449 cairo_region_union_rectangle (invalid_region, &new_area);
4451 gdk_rectangle_intersect (&old_area, &new_area, &common);
4452 if (common.width > 2 && common.height > 2)
4454 cairo_region_t *common_region;
4456 /* make sure the border is invalidated */
4462 common_region = cairo_region_create_rectangle (&common);
4464 cairo_region_subtract (invalid_region, common_region);
4465 cairo_region_destroy (common_region);
4468 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
4470 cairo_region_destroy (invalid_region);
4472 tree_view->priv->rubber_band_x = x;
4473 tree_view->priv->rubber_band_y = y;
4475 gtk_tree_view_update_rubber_band_selection (tree_view);
4479 gtk_tree_view_paint_rubber_band (GtkTreeView *tree_view,
4487 rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
4488 rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
4489 rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
4490 rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
4492 cairo_set_line_width (cr, 1.0);
4494 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
4496 gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]);
4498 gdk_cairo_rectangle (cr, &rect);
4500 cairo_paint_with_alpha (cr, 0.25);
4502 cairo_rectangle (cr,
4503 rect.x + 0.5, rect.y + 0.5,
4504 rect.width - 1, rect.height - 1);
4511 gtk_tree_view_motion_bin_window (GtkWidget *widget,
4512 GdkEventMotion *event)
4514 GtkTreeView *tree_view;
4519 tree_view = (GtkTreeView *) widget;
4521 if (tree_view->priv->tree == NULL)
4524 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
4526 gtk_grab_add (GTK_WIDGET (tree_view));
4527 gtk_tree_view_update_rubber_band (tree_view);
4529 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
4531 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4533 gtk_tree_view_update_rubber_band (tree_view);
4535 add_scroll_timeout (tree_view);
4538 /* only check for an initiated drag when a button is pressed */
4539 if (tree_view->priv->pressed_button >= 0
4540 && !tree_view->priv->rubber_band_status)
4541 gtk_tree_view_maybe_begin_dragging_row (tree_view, event);
4543 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4547 _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
4549 /* If we are currently pressing down a button, we don't want to prelight anything else. */
4550 if ((tree_view->priv->button_pressed_node != NULL) &&
4551 (tree_view->priv->button_pressed_node != node))
4554 tree_view->priv->event_last_x = event->x;
4555 tree_view->priv->event_last_y = event->y;
4557 prelight_or_select (tree_view, tree, node, event->x, event->y);
4563 gtk_tree_view_motion (GtkWidget *widget,
4564 GdkEventMotion *event)
4566 GtkTreeView *tree_view;
4568 tree_view = (GtkTreeView *) widget;
4570 /* Resizing a column */
4571 if (tree_view->priv->in_column_resize)
4572 return gtk_tree_view_motion_resize_column (widget, event);
4575 if (tree_view->priv->in_column_drag)
4576 return gtk_tree_view_motion_drag_column (widget, event);
4578 /* Sanity check it */
4579 if (event->window == tree_view->priv->bin_window)
4580 return gtk_tree_view_motion_bin_window (widget, event);
4585 /* Invalidate the focus rectangle near the edge of the bin_window; used when
4586 * the tree is empty.
4589 invalidate_empty_focus (GtkTreeView *tree_view)
4593 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
4598 area.width = gdk_window_get_width (tree_view->priv->bin_window);
4599 area.height = gdk_window_get_height (tree_view->priv->bin_window);
4600 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
4603 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
4607 draw_empty_focus (GtkTreeView *tree_view, cairo_t *cr)
4609 GtkWidget *widget = GTK_WIDGET (tree_view);
4612 if (!gtk_widget_has_focus (widget))
4615 w = gdk_window_get_width (tree_view->priv->bin_window) - 2;
4616 h = gdk_window_get_height (tree_view->priv->bin_window) - 2;
4619 gtk_paint_focus (gtk_widget_get_style (widget),
4621 gtk_widget_get_state (widget),
4628 GTK_TREE_VIEW_GRID_LINE,
4629 GTK_TREE_VIEW_TREE_LINE,
4630 GTK_TREE_VIEW_FOREGROUND_LINE
4631 } GtkTreeViewLineType;
4634 gtk_tree_view_draw_line (GtkTreeView *tree_view,
4636 GtkTreeViewLineType type,
4646 case GTK_TREE_VIEW_TREE_LINE:
4647 cairo_set_source_rgb (cr, 0, 0, 0);
4648 cairo_set_line_width (cr, tree_view->priv->tree_line_width);
4649 if (tree_view->priv->tree_line_dashes[0])
4651 tree_view->priv->tree_line_dashes,
4654 case GTK_TREE_VIEW_GRID_LINE:
4655 cairo_set_source_rgb (cr, 0, 0, 0);
4656 cairo_set_line_width (cr, tree_view->priv->grid_line_width);
4657 if (tree_view->priv->grid_line_dashes[0])
4659 tree_view->priv->grid_line_dashes,
4663 g_assert_not_reached ();
4665 case GTK_TREE_VIEW_FOREGROUND_LINE:
4666 cairo_set_line_width (cr, 1.0);
4667 gdk_cairo_set_source_color (cr,
4668 >k_widget_get_style (GTK_WIDGET (tree_view))->fg[gtk_widget_get_state (GTK_WIDGET (tree_view))]);
4672 cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
4673 cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
4680 gtk_tree_view_draw_grid_lines (GtkTreeView *tree_view,
4682 gint n_visible_columns)
4684 GList *list = tree_view->priv->columns;
4688 if (tree_view->priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_VERTICAL
4689 && tree_view->priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_BOTH)
4692 /* Only draw the lines for visible rows and columns */
4693 for (list = tree_view->priv->columns; list; list = list->next, i++)
4695 GtkTreeViewColumn *column = list->data;
4697 /* We don't want a line for the last column */
4698 if (i == n_visible_columns - 1)
4701 if (!gtk_tree_view_column_get_visible (column))
4704 current_x += gtk_tree_view_column_get_width (column);
4706 gtk_tree_view_draw_line (tree_view, cr,
4707 GTK_TREE_VIEW_GRID_LINE,
4709 current_x - 1, tree_view->priv->height);
4713 /* Warning: Very scary function.
4714 * Modify at your own risk
4716 * KEEP IN SYNC WITH gtk_tree_view_create_row_drag_icon()!
4717 * FIXME: It's not...
4720 gtk_tree_view_bin_draw (GtkWidget *widget,
4723 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
4729 GtkRBNode *cursor = NULL;
4730 GtkRBTree *cursor_tree = NULL;
4731 GtkRBNode *drag_highlight = NULL;
4732 GtkRBTree *drag_highlight_tree = NULL;
4735 gint y_offset, cell_offset;
4738 GdkRectangle background_area;
4739 GdkRectangle cell_area;
4743 gint expander_cell_width;
4744 gint bin_window_width;
4745 gint bin_window_height;
4746 GtkTreePath *cursor_path;
4747 GtkTreePath *drag_dest_path;
4748 GList *first_column, *last_column;
4749 gint vertical_separator;
4750 gint horizontal_separator;
4751 gint focus_line_width;
4752 gboolean allow_rules;
4753 gboolean has_can_focus_cell;
4755 gint n_visible_columns;
4756 gint pointer_x, pointer_y;
4757 gint grid_line_width;
4758 gboolean got_pointer = FALSE;
4759 gboolean draw_vgrid_lines, draw_hgrid_lines;
4761 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4763 gtk_widget_style_get (widget,
4764 "horizontal-separator", &horizontal_separator,
4765 "vertical-separator", &vertical_separator,
4766 "allow-rules", &allow_rules,
4767 "focus-line-width", &focus_line_width,
4770 if (tree_view->priv->tree == NULL)
4772 draw_empty_focus (tree_view, cr);
4776 style = gtk_widget_get_style (widget);
4778 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
4779 bin_window_height = gdk_window_get_height (tree_view->priv->bin_window);
4780 cairo_rectangle (cr, 0, 0, bin_window_width, bin_window_height);
4782 if (!gdk_cairo_get_clip_rectangle (cr, &clip))
4785 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, clip.y);
4789 y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
4791 if (tree_view->priv->height < bin_window_height)
4793 gtk_paint_flat_box (style,
4795 gtk_widget_get_state (widget),
4799 0, tree_view->priv->height,
4801 bin_window_height - tree_view->priv->height);
4807 /* find the path for the node */
4808 path = _gtk_tree_view_find_path ((GtkTreeView *)widget,
4811 gtk_tree_model_get_iter (tree_view->priv->model,
4814 depth = gtk_tree_path_get_depth (path);
4815 gtk_tree_path_free (path);
4818 drag_dest_path = NULL;
4820 if (tree_view->priv->cursor)
4821 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
4824 _gtk_tree_view_find_node (tree_view, cursor_path,
4825 &cursor_tree, &cursor);
4827 if (tree_view->priv->drag_dest_row)
4828 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
4831 _gtk_tree_view_find_node (tree_view, drag_dest_path,
4832 &drag_highlight_tree, &drag_highlight);
4835 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
4836 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
4838 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
4839 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
4841 if (draw_vgrid_lines || draw_hgrid_lines)
4842 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
4844 n_visible_columns = 0;
4845 for (list = tree_view->priv->columns; list; list = list->next)
4847 if (!gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
4849 n_visible_columns ++;
4852 /* Find the last column */
4853 for (last_column = g_list_last (tree_view->priv->columns);
4855 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
4856 last_column = last_column->prev)
4860 for (first_column = g_list_first (tree_view->priv->columns);
4862 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
4863 first_column = first_column->next)
4866 /* Actually process the expose event. To do this, we want to
4867 * start at the first node of the event, and walk the tree in
4868 * order, drawing each successive node.
4874 gboolean is_separator = FALSE;
4875 gboolean is_first = FALSE;
4876 gboolean is_last = FALSE;
4878 is_separator = row_is_separator (tree_view, &iter, NULL);
4880 max_height = gtk_tree_view_get_row_height (tree_view, node);
4883 highlight_x = 0; /* should match x coord of first cell */
4884 expander_cell_width = 0;
4886 background_area.y = y_offset + clip.y;
4887 background_area.height = max_height;
4891 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT))
4892 flags |= GTK_CELL_RENDERER_PRELIT;
4894 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
4895 flags |= GTK_CELL_RENDERER_SELECTED;
4897 parity = _gtk_rbtree_node_find_parity (tree, node);
4899 /* we *need* to set cell data on all cells before the call
4900 * to _has_can_focus_cell, else _has_can_focus_cell() does not
4901 * return a correct value.
4903 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4905 list = (rtl ? list->prev : list->next))
4907 GtkTreeViewColumn *column = list->data;
4908 gtk_tree_view_column_cell_set_cell_data (column,
4909 tree_view->priv->model,
4911 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
4912 node->children?TRUE:FALSE);
4915 has_can_focus_cell = gtk_tree_view_has_can_focus_cell (tree_view);
4917 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4919 list = (rtl ? list->prev : list->next))
4921 GtkTreeViewColumn *column = list->data;
4922 const gchar *detail = NULL;
4923 gchar new_detail[128];
4926 gboolean draw_focus;
4928 if (!gtk_tree_view_column_get_visible (column))
4931 width = gtk_tree_view_column_get_width (column);
4933 if (cell_offset > clip.x + clip.width ||
4934 cell_offset + width < clip.x)
4936 cell_offset += width;
4940 if (gtk_tree_view_column_get_sort_indicator (column))
4941 flags |= GTK_CELL_RENDERER_SORTED;
4943 flags &= ~GTK_CELL_RENDERER_SORTED;
4946 flags |= GTK_CELL_RENDERER_FOCUSED;
4948 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4950 background_area.x = cell_offset;
4951 background_area.width = width;
4953 cell_area = background_area;
4954 cell_area.y += vertical_separator / 2;
4955 cell_area.x += horizontal_separator / 2;
4956 cell_area.height -= vertical_separator;
4957 cell_area.width -= horizontal_separator;
4959 if (draw_vgrid_lines)
4961 if (list == first_column)
4963 cell_area.width -= grid_line_width / 2;
4965 else if (list == last_column)
4967 cell_area.x += grid_line_width / 2;
4968 cell_area.width -= grid_line_width / 2;
4972 cell_area.x += grid_line_width / 2;
4973 cell_area.width -= grid_line_width;
4977 if (draw_hgrid_lines)
4979 cell_area.y += grid_line_width / 2;
4980 cell_area.height -= grid_line_width;
4983 if (!gdk_rectangle_intersect (&clip, &background_area, NULL))
4985 cell_offset += gtk_tree_view_column_get_width (column);
4989 gtk_tree_view_column_cell_set_cell_data (column,
4990 tree_view->priv->model,
4992 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
4993 node->children?TRUE:FALSE);
4995 /* Select the detail for drawing the cell. relevant
4996 * factors are parity, sortedness, and whether to
4999 if (allow_rules && tree_view->priv->has_rules)
5001 if ((flags & GTK_CELL_RENDERER_SORTED) &&
5002 n_visible_columns >= 3)
5005 detail = "cell_odd_ruled_sorted";
5007 detail = "cell_even_ruled_sorted";
5012 detail = "cell_odd_ruled";
5014 detail = "cell_even_ruled";
5019 if ((flags & GTK_CELL_RENDERER_SORTED) &&
5020 n_visible_columns >= 3)
5023 detail = "cell_odd_sorted";
5025 detail = "cell_even_sorted";
5030 detail = "cell_odd";
5032 detail = "cell_even";
5038 if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
5039 state = GTK_STATE_INSENSITIVE;
5040 else if (flags & GTK_CELL_RENDERER_SELECTED)
5041 state = GTK_STATE_SELECTED;
5043 state = GTK_STATE_NORMAL;
5045 if (node == cursor && has_can_focus_cell
5046 && ((column == tree_view->priv->focus_column
5047 && tree_view->priv->draw_keyfocus &&
5048 gtk_widget_has_focus (widget))
5049 || (column == tree_view->priv->edited_column)))
5054 /* Draw background */
5055 is_first = (rtl ? !list->next : !list->prev);
5056 is_last = (rtl ? !list->prev : !list->next);
5058 /* (I don't like the snprintfs either, but couldn't find a
5061 if (is_first && is_last)
5062 g_snprintf (new_detail, 127, "%s", detail);
5064 g_snprintf (new_detail, 127, "%s_start", detail);
5066 g_snprintf (new_detail, 127, "%s_end", detail);
5068 g_snprintf (new_detail, 127, "%s_middle", detail);
5070 gtk_paint_flat_box (style,
5078 background_area.width,
5079 background_area.height);
5081 if (gtk_tree_view_is_expander_column (tree_view, column))
5084 cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
5085 cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
5087 if (gtk_tree_view_draw_expanders (tree_view))
5090 cell_area.x += depth * tree_view->priv->expander_size;
5091 cell_area.width -= depth * tree_view->priv->expander_size;
5094 /* If we have an expander column, the highlight underline
5095 * starts with that column, so that it indicates which
5096 * level of the tree we're dropping at.
5098 highlight_x = cell_area.x;
5099 expander_cell_width = cell_area.width;
5102 gtk_paint_hline (style,
5108 cell_area.x + cell_area.width,
5109 cell_area.y + cell_area.height / 2);
5111 _gtk_tree_view_column_cell_render (column,
5117 if (gtk_tree_view_draw_expanders (tree_view)
5118 && (node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
5122 gdk_window_get_pointer (tree_view->priv->bin_window,
5123 &pointer_x, &pointer_y, NULL);
5127 gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
5131 pointer_x, pointer_y);
5137 gtk_paint_hline (style,
5143 cell_area.x + cell_area.width,
5144 cell_area.y + cell_area.height / 2);
5146 _gtk_tree_view_column_cell_render (column,
5154 if (draw_hgrid_lines)
5156 if (background_area.y > 0)
5157 gtk_tree_view_draw_line (tree_view, cr,
5158 GTK_TREE_VIEW_GRID_LINE,
5159 background_area.x, background_area.y,
5160 background_area.x + background_area.width,
5163 if (y_offset + max_height >= clip.height)
5164 gtk_tree_view_draw_line (tree_view, cr,
5165 GTK_TREE_VIEW_GRID_LINE,
5166 background_area.x, background_area.y + max_height,
5167 background_area.x + background_area.width,
5168 background_area.y + max_height);
5171 if (gtk_tree_view_is_expander_column (tree_view, column) &&
5172 tree_view->priv->tree_lines_enabled)
5174 gint x = background_area.x;
5175 gint mult = rtl ? -1 : 1;
5176 gint y0 = background_area.y;
5177 gint y1 = background_area.y + background_area.height/2;
5178 gint y2 = background_area.y + background_area.height;
5181 x += background_area.width - 1;
5183 if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT
5186 gtk_tree_view_draw_line (tree_view, cr,
5187 GTK_TREE_VIEW_TREE_LINE,
5188 x + tree_view->priv->expander_size * (depth - 1.5) * mult,
5190 x + tree_view->priv->expander_size * (depth - 1.1) * mult,
5195 gtk_tree_view_draw_line (tree_view, cr,
5196 GTK_TREE_VIEW_TREE_LINE,
5197 x + tree_view->priv->expander_size * (depth - 1.5) * mult,
5199 x + tree_view->priv->expander_size * (depth - 0.5) * mult,
5206 GtkRBNode *tmp_node;
5207 GtkRBTree *tmp_tree;
5209 if (!_gtk_rbtree_next (tree, node))
5210 gtk_tree_view_draw_line (tree_view, cr,
5211 GTK_TREE_VIEW_TREE_LINE,
5212 x + tree_view->priv->expander_size * (depth - 1.5) * mult,
5214 x + tree_view->priv->expander_size * (depth - 1.5) * mult,
5217 gtk_tree_view_draw_line (tree_view, cr,
5218 GTK_TREE_VIEW_TREE_LINE,
5219 x + tree_view->priv->expander_size * (depth - 1.5) * mult,
5221 x + tree_view->priv->expander_size * (depth - 1.5) * mult,
5224 tmp_node = tree->parent_node;
5225 tmp_tree = tree->parent_tree;
5227 for (i = depth - 2; i > 0; i--)
5229 if (_gtk_rbtree_next (tmp_tree, tmp_node))
5230 gtk_tree_view_draw_line (tree_view, cr,
5231 GTK_TREE_VIEW_TREE_LINE,
5232 x + tree_view->priv->expander_size * (i - 0.5) * mult,
5234 x + tree_view->priv->expander_size * (i - 0.5) * mult,
5237 tmp_node = tmp_tree->parent_node;
5238 tmp_tree = tmp_tree->parent_tree;
5243 cell_offset += gtk_tree_view_column_get_width (column);
5246 if (node == drag_highlight)
5248 /* Draw indicator for the drop
5250 gint highlight_y = -1;
5251 GtkRBTree *tree = NULL;
5252 GtkRBNode *node = NULL;
5254 switch (tree_view->priv->drag_dest_pos)
5256 case GTK_TREE_VIEW_DROP_BEFORE:
5257 highlight_y = background_area.y - 1;
5258 if (highlight_y < 0)
5262 case GTK_TREE_VIEW_DROP_AFTER:
5263 highlight_y = background_area.y + background_area.height - 1;
5266 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
5267 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
5268 _gtk_tree_view_find_node (tree_view, drag_dest_path, &tree, &node);
5273 gtk_paint_focus (style,
5275 gtk_widget_get_state (widget),
5278 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
5279 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
5280 0, gtk_tree_view_get_row_y_offset (tree_view, tree, node)
5281 - focus_line_width / 2,
5282 gdk_window_get_width (tree_view->priv->bin_window),
5283 gtk_tree_view_get_row_height (tree_view, node)
5284 - focus_line_width + 1);
5288 if (highlight_y >= 0)
5290 gtk_tree_view_draw_line (tree_view, cr,
5291 GTK_TREE_VIEW_FOREGROUND_LINE,
5292 rtl ? highlight_x + expander_cell_width : highlight_x,
5294 rtl ? 0 : bin_window_width,
5299 /* draw the big row-spanning focus rectangle, if needed */
5300 if (!has_can_focus_cell && node == cursor &&
5301 tree_view->priv->draw_keyfocus &&
5302 gtk_widget_has_focus (widget))
5304 gint tmp_y, tmp_height;
5305 GtkStateType focus_rect_state;
5308 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
5309 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
5310 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
5313 if (draw_hgrid_lines)
5315 tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node) + grid_line_width / 2;
5316 tmp_height = gtk_tree_view_get_row_height (tree_view, node) - grid_line_width;
5320 tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
5321 tmp_height = gtk_tree_view_get_row_height (tree_view, node);
5324 gtk_paint_focus (style,
5329 ? (is_last ? "treeview" : "treeview-left" )
5330 : (is_last ? "treeview-right" : "treeview-middle" )),
5332 gdk_window_get_width (tree_view->priv->bin_window),
5336 y_offset += max_height;
5339 GtkTreeIter parent = iter;
5342 tree = node->children;
5345 g_assert (node != tree->nil);
5347 while (node->left != tree->nil)
5349 has_child = gtk_tree_model_iter_children (tree_view->priv->model,
5355 TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
5359 gboolean done = FALSE;
5363 node = _gtk_rbtree_next (tree, node);
5366 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5370 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
5374 GtkTreeIter parent_iter = iter;
5375 gboolean has_parent;
5377 node = tree->parent_node;
5378 tree = tree->parent_tree;
5380 /* we should go to done to free some memory */
5382 has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
5388 TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
5394 while (y_offset < clip.height);
5397 gtk_tree_view_draw_grid_lines (tree_view, cr, n_visible_columns);
5399 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5400 gtk_tree_view_paint_rubber_band (tree_view, cr);
5403 gtk_tree_path_free (cursor_path);
5406 gtk_tree_path_free (drag_dest_path);
5412 gtk_tree_view_draw (GtkWidget *widget,
5415 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5418 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
5424 gtk_cairo_transform_to_window (cr, widget, tree_view->priv->bin_window);
5426 gtk_tree_view_bin_draw (widget, cr);
5430 /* We can't just chain up to Container::draw as it will try to send the
5431 * event to the headers, so we handle propagating it to our children
5432 * (eg. widgets being edited) ourselves.
5434 tmp_list = tree_view->priv->children;
5437 GtkTreeViewChild *child = tmp_list->data;
5438 tmp_list = tmp_list->next;
5440 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
5444 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
5448 for (list = tree_view->priv->columns; list != NULL; list = list->next)
5450 GtkTreeViewColumn *column = list->data;
5452 if (column == tree_view->priv->drag_column)
5455 if (gtk_tree_view_column_get_visible (column))
5457 button = gtk_tree_view_column_get_button (column);
5458 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
5464 if (tree_view->priv->drag_window &&
5465 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
5467 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
5468 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
5483 /* returns 0x1 when no column has been found -- yes it's hackish */
5484 static GtkTreeViewColumn *
5485 gtk_tree_view_get_drop_column (GtkTreeView *tree_view,
5486 GtkTreeViewColumn *column,
5489 GtkTreeViewColumn *left_column = NULL;
5490 GtkTreeViewColumn *cur_column = NULL;
5493 if (!gtk_tree_view_column_get_reorderable (column))
5494 return (GtkTreeViewColumn *)0x1;
5496 switch (drop_position)
5499 /* find first column where we can drop */
5500 tmp_list = tree_view->priv->columns;
5501 if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5502 return (GtkTreeViewColumn *)0x1;
5506 g_assert (tmp_list);
5508 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5509 tmp_list = tmp_list->next;
5512 gtk_tree_view_column_get_visible (left_column) == FALSE)
5515 if (!tree_view->priv->column_drop_func)
5518 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5520 left_column = cur_column;
5527 if (!tree_view->priv->column_drop_func)
5530 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
5533 return (GtkTreeViewColumn *)0x1;
5537 /* find first column after column where we can drop */
5538 tmp_list = tree_view->priv->columns;
5540 for (; tmp_list; tmp_list = tmp_list->next)
5541 if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5544 if (!tmp_list || !tmp_list->next)
5545 return (GtkTreeViewColumn *)0x1;
5547 tmp_list = tmp_list->next;
5548 left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5549 tmp_list = tmp_list->next;
5553 g_assert (tmp_list);
5555 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5556 tmp_list = tmp_list->next;
5559 gtk_tree_view_column_get_visible (left_column) == FALSE)
5561 left_column = cur_column;
5563 tmp_list = tmp_list->next;
5567 if (!tree_view->priv->column_drop_func)
5570 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5572 left_column = cur_column;
5579 if (!tree_view->priv->column_drop_func)
5582 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
5585 return (GtkTreeViewColumn *)0x1;
5589 /* find first column before column where we can drop */
5590 tmp_list = tree_view->priv->columns;
5592 for (; tmp_list; tmp_list = tmp_list->next)
5593 if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5596 if (!tmp_list || !tmp_list->prev)
5597 return (GtkTreeViewColumn *)0x1;
5599 tmp_list = tmp_list->prev;
5600 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5601 tmp_list = tmp_list->prev;
5605 g_assert (tmp_list);
5607 left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5610 gtk_tree_view_column_get_visible (left_column) == FALSE)
5612 /*if (!tmp_list->prev)
5613 return (GtkTreeViewColumn *)0x1;
5616 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->prev->data);
5617 tmp_list = tmp_list->prev->prev;
5620 cur_column = left_column;
5622 tmp_list = tmp_list->prev;
5626 if (!tree_view->priv->column_drop_func)
5629 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5632 cur_column = left_column;
5633 tmp_list = tmp_list->prev;
5636 if (!tree_view->priv->column_drop_func)
5639 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
5642 return (GtkTreeViewColumn *)0x1;
5646 /* same as DROP_HOME case, but doing it backwards */
5647 tmp_list = g_list_last (tree_view->priv->columns);
5650 if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5651 return (GtkTreeViewColumn *)0x1;
5655 g_assert (tmp_list);
5657 left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5660 gtk_tree_view_column_get_visible (left_column) == FALSE)
5662 cur_column = left_column;
5663 tmp_list = tmp_list->prev;
5666 if (!tree_view->priv->column_drop_func)
5669 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5672 cur_column = left_column;
5673 tmp_list = tmp_list->prev;
5676 if (!tree_view->priv->column_drop_func)
5679 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
5682 return (GtkTreeViewColumn *)0x1;
5686 return (GtkTreeViewColumn *)0x1;
5690 gtk_tree_view_key_press (GtkWidget *widget,
5693 GtkTreeView *tree_view = (GtkTreeView *) widget;
5696 if (tree_view->priv->rubber_band_status)
5698 if (event->keyval == GDK_KEY_Escape)
5699 gtk_tree_view_stop_rubber_band (tree_view);
5704 if (tree_view->priv->in_column_drag)
5706 if (event->keyval == GDK_KEY_Escape)
5708 tree_view->priv->cur_reorder = NULL;
5709 gtk_tree_view_button_release_drag_column (widget, NULL);
5714 if (tree_view->priv->headers_visible)
5716 GList *focus_column;
5719 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
5721 for (focus_column = tree_view->priv->columns;
5723 focus_column = focus_column->next)
5725 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5727 button = gtk_tree_view_column_get_button (column);
5728 if (gtk_widget_has_focus (button))
5733 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
5734 (event->keyval == GDK_KEY_Left || event->keyval == GDK_KEY_KP_Left
5735 || event->keyval == GDK_KEY_Right || event->keyval == GDK_KEY_KP_Right))
5737 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5738 gint max_width, min_width;
5740 if (!gtk_tree_view_column_get_resizable (column))
5742 gtk_widget_error_bell (widget);
5746 if (event->keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left)
5747 || event->keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
5749 GtkRequisition button_req;
5750 gint old_width = _gtk_tree_view_column_get_resized_width (column);
5753 button = gtk_tree_view_column_get_button (column);
5755 gtk_widget_get_preferred_size (button, &button_req, NULL);
5757 new_width = MAX (old_width, gtk_tree_view_column_get_width (column));
5762 _gtk_tree_view_column_set_resized_width (column, new_width);
5764 min_width = gtk_tree_view_column_get_min_width (column);
5765 if (min_width == -1)
5766 new_width = MAX (button_req.width, new_width);
5769 new_width = MAX (min_width, new_width);
5772 max_width = gtk_tree_view_column_get_max_width (column);
5773 if (max_width != -1)
5774 new_width = MIN (new_width, max_width);
5776 _gtk_tree_view_column_set_use_resized_width (column, TRUE);
5778 if (new_width != old_width)
5780 _gtk_tree_view_column_set_resized_width (column, new_width);
5781 gtk_widget_queue_resize (widget);
5784 gtk_widget_error_bell (widget);
5786 else if (event->keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
5787 || event->keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
5789 gint old_width = _gtk_tree_view_column_get_resized_width (column);
5792 new_width = MAX (old_width, gtk_tree_view_column_get_width (column));
5795 max_width = gtk_tree_view_column_get_max_width (column);
5796 if (max_width != -1)
5797 new_width = MIN (new_width, max_width);
5799 _gtk_tree_view_column_set_use_resized_width (column, TRUE);
5801 if (new_width != old_width)
5803 _gtk_tree_view_column_set_resized_width (column, new_width);
5804 gtk_widget_queue_resize (widget);
5807 gtk_widget_error_bell (widget);
5814 (event->state & GDK_MOD1_MASK) &&
5815 (event->keyval == GDK_KEY_Left || event->keyval == GDK_KEY_KP_Left
5816 || event->keyval == GDK_KEY_Right || event->keyval == GDK_KEY_KP_Right
5817 || event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_KP_Home
5818 || event->keyval == GDK_KEY_End || event->keyval == GDK_KEY_KP_End))
5820 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5822 if (event->keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left)
5823 || event->keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
5825 GtkTreeViewColumn *col;
5826 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT);
5827 if (col != (GtkTreeViewColumn *)0x1)
5828 gtk_tree_view_move_column_after (tree_view, column, col);
5830 gtk_widget_error_bell (widget);
5832 else if (event->keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
5833 || event->keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
5835 GtkTreeViewColumn *col;
5836 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT);
5837 if (col != (GtkTreeViewColumn *)0x1)
5838 gtk_tree_view_move_column_after (tree_view, column, col);
5840 gtk_widget_error_bell (widget);
5842 else if (event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_KP_Home)
5844 GtkTreeViewColumn *col;
5845 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME);
5846 if (col != (GtkTreeViewColumn *)0x1)
5847 gtk_tree_view_move_column_after (tree_view, column, col);
5849 gtk_widget_error_bell (widget);
5851 else if (event->keyval == GDK_KEY_End || event->keyval == GDK_KEY_KP_End)
5853 GtkTreeViewColumn *col;
5854 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END);
5855 if (col != (GtkTreeViewColumn *)0x1)
5856 gtk_tree_view_move_column_after (tree_view, column, col);
5858 gtk_widget_error_bell (widget);
5865 /* Chain up to the parent class. It handles the keybindings. */
5866 if (GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->key_press_event (widget, event))
5869 if (tree_view->priv->search_entry_avoid_unhandled_binding)
5871 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
5875 /* We pass the event to the search_entry. If its text changes, then we start
5876 * the typeahead find capabilities. */
5877 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
5878 && tree_view->priv->enable_search
5879 && !tree_view->priv->search_custom_entry_set)
5881 GdkEvent *new_event;
5883 const char *new_text;
5886 gboolean text_modified;
5887 gulong popup_menu_id;
5889 gtk_tree_view_ensure_interactive_directory (tree_view);
5891 /* Make a copy of the current text */
5892 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
5893 new_event = gdk_event_copy ((GdkEvent *) event);
5894 g_object_unref (((GdkEventKey *) new_event)->window);
5895 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
5896 gtk_widget_realize (tree_view->priv->search_window);
5898 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
5899 "popup-menu", G_CALLBACK (gtk_true),
5902 /* Move the entry off screen */
5903 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
5904 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
5905 gdk_screen_get_width (screen) + 1,
5906 gdk_screen_get_height (screen) + 1);
5907 gtk_widget_show (tree_view->priv->search_window);
5909 /* Send the event to the window. If the preedit_changed signal is emitted
5910 * during this event, we will set priv->imcontext_changed */
5911 tree_view->priv->imcontext_changed = FALSE;
5912 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
5913 gdk_event_free (new_event);
5914 gtk_widget_hide (tree_view->priv->search_window);
5916 g_signal_handler_disconnect (tree_view->priv->search_entry,
5919 /* We check to make sure that the entry tried to handle the text, and that
5920 * the text has changed.
5922 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
5923 text_modified = strcmp (old_text, new_text) != 0;
5925 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
5926 (retval && text_modified)) /* ...or the text was modified */
5928 if (gtk_tree_view_real_start_interactive_search (tree_view,
5929 gdk_event_get_device ((GdkEvent *) event),
5932 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
5937 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
5947 gtk_tree_view_key_release (GtkWidget *widget,
5950 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5952 if (tree_view->priv->rubber_band_status)
5955 return GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->key_release_event (widget, event);
5958 /* FIXME Is this function necessary? Can I get an enter_notify event
5959 * w/o either an expose event or a mouse motion event?
5962 gtk_tree_view_enter_notify (GtkWidget *widget,
5963 GdkEventCrossing *event)
5965 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5970 /* Sanity check it */
5971 if (event->window != tree_view->priv->bin_window)
5974 if (tree_view->priv->tree == NULL)
5977 if (event->mode == GDK_CROSSING_GRAB ||
5978 event->mode == GDK_CROSSING_GTK_GRAB ||
5979 event->mode == GDK_CROSSING_GTK_UNGRAB ||
5980 event->mode == GDK_CROSSING_STATE_CHANGED)
5983 /* find the node internally */
5984 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5987 _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
5989 tree_view->priv->event_last_x = event->x;
5990 tree_view->priv->event_last_y = event->y;
5992 if ((tree_view->priv->button_pressed_node == NULL) ||
5993 (tree_view->priv->button_pressed_node == node))
5994 prelight_or_select (tree_view, tree, node, event->x, event->y);
6000 gtk_tree_view_leave_notify (GtkWidget *widget,
6001 GdkEventCrossing *event)
6003 GtkTreeView *tree_view;
6005 if (event->mode == GDK_CROSSING_GRAB)
6008 tree_view = GTK_TREE_VIEW (widget);
6010 if (tree_view->priv->prelight_node)
6011 _gtk_tree_view_queue_draw_node (tree_view,
6012 tree_view->priv->prelight_tree,
6013 tree_view->priv->prelight_node,
6016 tree_view->priv->event_last_x = -10000;
6017 tree_view->priv->event_last_y = -10000;
6019 prelight_or_select (tree_view,
6021 -1000, -1000); /* coords not possibly over an arrow */
6028 gtk_tree_view_focus_out (GtkWidget *widget,
6029 GdkEventFocus *event)
6031 GtkTreeView *tree_view;
6033 tree_view = GTK_TREE_VIEW (widget);
6035 gtk_widget_queue_draw (widget);
6037 /* destroy interactive search dialog */
6038 if (tree_view->priv->search_window)
6039 gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view,
6040 gdk_event_get_device ((GdkEvent *) event));
6046 /* Incremental Reflow
6050 gtk_tree_view_node_queue_redraw (GtkTreeView *tree_view,
6054 GtkAllocation allocation;
6057 y = _gtk_rbtree_node_find_offset (tree, node)
6058 - tree_view->priv->vadjustment->value
6059 + gtk_tree_view_get_effective_header_height (tree_view);
6061 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
6062 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
6065 GTK_RBNODE_GET_HEIGHT (node));
6069 node_is_visible (GtkTreeView *tree_view,
6076 y = _gtk_rbtree_node_find_offset (tree, node);
6077 height = gtk_tree_view_get_row_height (tree_view, node);
6079 if (y >= tree_view->priv->vadjustment->value &&
6080 y + height <= (tree_view->priv->vadjustment->value
6081 + tree_view->priv->vadjustment->page_size))
6087 /* Returns TRUE if it updated the size
6090 validate_row (GtkTreeView *tree_view,
6096 GtkTreeViewColumn *column;
6097 GList *list, *first_column, *last_column;
6099 gint horizontal_separator;
6100 gint vertical_separator;
6101 gint depth = gtk_tree_path_get_depth (path);
6102 gboolean retval = FALSE;
6103 gboolean is_separator = FALSE;
6104 gboolean draw_vgrid_lines, draw_hgrid_lines;
6106 gint grid_line_width;
6107 gboolean wide_separators;
6108 gint separator_height;
6110 /* double check the row needs validating */
6111 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) &&
6112 ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6115 is_separator = row_is_separator (tree_view, iter, NULL);
6117 gtk_widget_style_get (GTK_WIDGET (tree_view),
6118 "focus-padding", &focus_pad,
6119 "horizontal-separator", &horizontal_separator,
6120 "vertical-separator", &vertical_separator,
6121 "grid-line-width", &grid_line_width,
6122 "wide-separators", &wide_separators,
6123 "separator-height", &separator_height,
6127 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
6128 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
6130 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
6131 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
6133 for (last_column = g_list_last (tree_view->priv->columns);
6135 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
6136 last_column = last_column->prev)
6139 for (first_column = g_list_first (tree_view->priv->columns);
6141 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
6142 first_column = first_column->next)
6145 for (list = tree_view->priv->columns; list; list = list->next)
6148 gint original_width;
6152 column = list->data;
6154 if (!gtk_tree_view_column_get_visible (column))
6157 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) &&
6158 !_gtk_tree_view_column_cell_get_dirty (column))
6161 original_width = _gtk_tree_view_column_get_requested_width (column);
6163 gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter,
6164 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
6165 node->children?TRUE:FALSE);
6166 gtk_tree_view_column_cell_get_size (column,
6172 row_height += vertical_separator;
6173 height = MAX (height, row_height);
6174 height = MAX (height, tree_view->priv->expander_size);
6178 if (wide_separators)
6179 height = separator_height + 2 * focus_pad;
6181 height = 2 + 2 * focus_pad;
6184 if (gtk_tree_view_is_expander_column (tree_view, column))
6186 padding += horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
6188 if (gtk_tree_view_draw_expanders (tree_view))
6189 padding += depth * tree_view->priv->expander_size;
6192 padding += horizontal_separator;
6194 if (draw_vgrid_lines)
6196 if (list->data == first_column || list->data == last_column)
6197 padding += grid_line_width / 2.0;
6199 padding += grid_line_width;
6202 /* Update the padding for the column */
6203 _gtk_tree_view_column_push_padding (column, padding);
6204 new_width = _gtk_tree_view_column_get_requested_width (column);
6206 if (new_width > original_width)
6210 if (draw_hgrid_lines)
6211 height += grid_line_width;
6213 if (height != GTK_RBNODE_GET_HEIGHT (node))
6216 _gtk_rbtree_node_set_height (tree, node, height);
6218 _gtk_rbtree_node_mark_valid (tree, node);
6219 tree_view->priv->post_validation_flag = TRUE;
6226 validate_visible_area (GtkTreeView *tree_view)
6228 GtkAllocation allocation;
6229 GtkTreePath *path = NULL;
6230 GtkTreePath *above_path = NULL;
6232 GtkRBTree *tree = NULL;
6233 GtkRBNode *node = NULL;
6234 gboolean need_redraw = FALSE;
6235 gboolean size_changed = FALSE;
6237 gint area_above = 0;
6238 gint area_below = 0;
6240 if (tree_view->priv->tree == NULL)
6243 if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID) &&
6244 tree_view->priv->scroll_to_path == NULL)
6247 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
6248 total_height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
6250 if (total_height == 0)
6253 /* First, we check to see if we need to scroll anywhere
6255 if (tree_view->priv->scroll_to_path)
6257 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
6258 if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node))
6260 /* we are going to scroll, and will update dy */
6261 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6262 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6263 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6265 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6266 if (validate_row (tree_view, tree, node, &iter, path))
6267 size_changed = TRUE;
6270 if (tree_view->priv->scroll_to_use_align)
6272 gint height = gtk_tree_view_get_row_height (tree_view, node);
6273 area_above = (total_height - height) *
6274 tree_view->priv->scroll_to_row_align;
6275 area_below = total_height - area_above - height;
6276 area_above = MAX (area_above, 0);
6277 area_below = MAX (area_below, 0);
6282 * 1) row not visible
6286 gint height = gtk_tree_view_get_row_height (tree_view, node);
6288 dy = _gtk_rbtree_node_find_offset (tree, node);
6290 if (dy >= tree_view->priv->vadjustment->value &&
6291 dy + height <= (tree_view->priv->vadjustment->value
6292 + tree_view->priv->vadjustment->page_size))
6294 /* row visible: keep the row at the same position */
6295 area_above = dy - tree_view->priv->vadjustment->value;
6296 area_below = (tree_view->priv->vadjustment->value +
6297 tree_view->priv->vadjustment->page_size)
6302 /* row not visible */
6304 && dy + height <= tree_view->priv->vadjustment->page_size)
6306 /* row at the beginning -- fixed */
6308 area_below = tree_view->priv->vadjustment->page_size
6309 - area_above - height;
6311 else if (dy >= (tree_view->priv->vadjustment->upper -
6312 tree_view->priv->vadjustment->page_size))
6314 /* row at the end -- fixed */
6315 area_above = dy - (tree_view->priv->vadjustment->upper -
6316 tree_view->priv->vadjustment->page_size);
6317 area_below = tree_view->priv->vadjustment->page_size -
6318 area_above - height;
6322 area_above = tree_view->priv->vadjustment->page_size - height;
6328 /* row somewhere in the middle, bring it to the top
6332 area_below = total_height - height;
6338 /* the scroll to isn't valid; ignore it.
6341 if (tree_view->priv->scroll_to_path && !path)
6343 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
6344 tree_view->priv->scroll_to_path = NULL;
6347 gtk_tree_path_free (path);
6352 /* We didn't have a scroll_to set, so we just handle things normally
6358 offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
6359 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
6363 /* In this case, nothing has been validated */
6364 path = gtk_tree_path_new_first ();
6365 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
6369 path = _gtk_tree_view_find_path (tree_view, tree, node);
6370 total_height += offset;
6373 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6375 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6376 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6378 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6379 if (validate_row (tree_view, tree, node, &iter, path))
6380 size_changed = TRUE;
6383 area_below = total_height - gtk_tree_view_get_row_height (tree_view, node);
6386 above_path = gtk_tree_path_copy (path);
6388 /* if we do not validate any row above the new top_row, we will make sure
6389 * that the row immediately above top_row has been validated. (if we do not
6390 * do this, _gtk_rbtree_find_offset will find the row above top_row, because
6391 * when invalidated that row's height will be zero. and this will mess up
6394 if (area_above == 0)
6399 _gtk_tree_view_find_node (tree_view, above_path, &tmptree, &tmpnode);
6400 _gtk_rbtree_prev_full (tmptree, tmpnode, &tmptree, &tmpnode);
6404 GtkTreePath *tmppath;
6405 GtkTreeIter tmpiter;
6407 tmppath = _gtk_tree_view_find_path (tree_view, tmptree, tmpnode);
6408 gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, tmppath);
6410 if (GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_INVALID) ||
6411 GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_COLUMN_INVALID))
6413 _gtk_tree_view_queue_draw_node (tree_view, tmptree, tmpnode, NULL);
6414 if (validate_row (tree_view, tmptree, tmpnode, &tmpiter, tmppath))
6415 size_changed = TRUE;
6418 gtk_tree_path_free (tmppath);
6422 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
6423 * backwards is much slower then forward, as there is no iter_prev function.
6424 * We go forwards first in case we run out of tree. Then we go backwards to
6427 while (node && area_below > 0)
6431 GtkTreeIter parent = iter;
6434 tree = node->children;
6437 g_assert (node != tree->nil);
6439 while (node->left != tree->nil)
6441 has_child = gtk_tree_model_iter_children (tree_view->priv->model,
6444 TREE_VIEW_INTERNAL_ASSERT_VOID (has_child);
6445 gtk_tree_path_down (path);
6449 gboolean done = FALSE;
6452 node = _gtk_rbtree_next (tree, node);
6455 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
6457 gtk_tree_path_next (path);
6460 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
6464 GtkTreeIter parent_iter = iter;
6465 gboolean has_parent;
6467 node = tree->parent_node;
6468 tree = tree->parent_tree;
6471 has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
6474 gtk_tree_path_up (path);
6477 TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
6486 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6487 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6489 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6490 if (validate_row (tree_view, tree, node, &iter, path))
6491 size_changed = TRUE;
6494 area_below -= gtk_tree_view_get_row_height (tree_view, node);
6496 gtk_tree_path_free (path);
6498 /* If we ran out of tree, and have extra area_below left, we need to add it
6501 area_above += area_below;
6503 _gtk_tree_view_find_node (tree_view, above_path, &tree, &node);
6505 /* We walk backwards */
6506 while (area_above > 0)
6508 _gtk_rbtree_prev_full (tree, node, &tree, &node);
6510 /* Always find the new path in the tree. We cannot just assume
6511 * a gtk_tree_path_prev() is enough here, as there might be children
6512 * in between this node and the previous sibling node. If this
6513 * appears to be a performance hotspot in profiles, we can look into
6514 * intrigate logic for keeping path, node and iter in sync like
6515 * we do for forward walks. (Which will be hard because of the lacking
6522 gtk_tree_path_free (above_path);
6523 above_path = _gtk_tree_view_find_path (tree_view, tree, node);
6525 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
6527 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6528 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6530 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6531 if (validate_row (tree_view, tree, node, &iter, above_path))
6532 size_changed = TRUE;
6534 area_above -= gtk_tree_view_get_row_height (tree_view, node);
6537 /* if we scrolled to a path, we need to set the dy here,
6538 * and sync the top row accordingly
6540 if (tree_view->priv->scroll_to_path)
6542 gtk_tree_view_set_top_row (tree_view, above_path, -area_above);
6543 gtk_tree_view_top_row_to_dy (tree_view);
6547 else if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
6549 /* when we are not scrolling, we should never set dy to something
6550 * else than zero. we update top_row to be in sync with dy = 0.
6552 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
6553 gtk_tree_view_dy_to_top_row (tree_view);
6555 else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
6557 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), tree_view->priv->height - tree_view->priv->vadjustment->page_size);
6558 gtk_tree_view_dy_to_top_row (tree_view);
6561 gtk_tree_view_top_row_to_dy (tree_view);
6563 /* update width/height and queue a resize */
6566 GtkRequisition requisition;
6568 /* We temporarily guess a size, under the assumption that it will be the
6569 * same when we get our next size_allocate. If we don't do this, we'll be
6570 * in an inconsistent state if we call top_row_to_dy. */
6572 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view),
6573 &requisition, NULL);
6574 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
6575 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
6576 gtk_adjustment_changed (tree_view->priv->hadjustment);
6577 gtk_adjustment_changed (tree_view->priv->vadjustment);
6578 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6581 if (tree_view->priv->scroll_to_path)
6583 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
6584 tree_view->priv->scroll_to_path = NULL;
6588 gtk_tree_path_free (above_path);
6590 if (tree_view->priv->scroll_to_column)
6592 tree_view->priv->scroll_to_column = NULL;
6595 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
6599 initialize_fixed_height_mode (GtkTreeView *tree_view)
6601 if (!tree_view->priv->tree)
6604 if (tree_view->priv->fixed_height < 0)
6609 GtkRBTree *tree = NULL;
6610 GtkRBNode *node = NULL;
6612 tree = tree_view->priv->tree;
6615 path = _gtk_tree_view_find_path (tree_view, tree, node);
6616 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6618 validate_row (tree_view, tree, node, &iter, path);
6620 gtk_tree_path_free (path);
6622 tree_view->priv->fixed_height = gtk_tree_view_get_row_height (tree_view, node);
6625 _gtk_rbtree_set_fixed_height (tree_view->priv->tree,
6626 tree_view->priv->fixed_height, TRUE);
6629 /* Our strategy for finding nodes to validate is a little convoluted. We find
6630 * the left-most uninvalidated node. We then try walking right, validating
6631 * nodes. Once we find a valid node, we repeat the previous process of finding
6632 * the first invalid node.
6636 do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize)
6638 GtkRBTree *tree = NULL;
6639 GtkRBNode *node = NULL;
6640 gboolean validated_area = FALSE;
6642 GtkTreePath *path = NULL;
6647 gint prev_height = -1;
6648 gboolean fixed_height = TRUE;
6650 g_assert (tree_view);
6652 if (tree_view->priv->tree == NULL)
6655 if (tree_view->priv->fixed_height_mode)
6657 if (tree_view->priv->fixed_height < 0)
6658 initialize_fixed_height_mode (tree_view);
6663 timer = g_timer_new ();
6664 g_timer_start (timer);
6668 if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID))
6676 node = _gtk_rbtree_next (tree, node);
6679 TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_next (tree_view->priv->model, &iter), FALSE);
6680 gtk_tree_path_next (path);
6684 gtk_tree_path_free (path);
6691 tree = tree_view->priv->tree;
6692 node = tree_view->priv->tree->root;
6694 g_assert (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_DESCENDANTS_INVALID));
6698 if (node->left != tree->nil &&
6699 GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID))
6703 else if (node->right != tree->nil &&
6704 GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID))
6708 else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6709 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6713 else if (node->children != NULL)
6715 tree = node->children;
6719 /* RBTree corruption! All bad */
6720 g_assert_not_reached ();
6723 path = _gtk_tree_view_find_path (tree_view, tree, node);
6724 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6727 validated_area = validate_row (tree_view, tree, node, &iter, path) ||
6730 if (!tree_view->priv->fixed_height_check)
6734 height = gtk_tree_view_get_row_height (tree_view, node);
6735 if (prev_height < 0)
6736 prev_height = height;
6737 else if (prev_height != height)
6738 fixed_height = FALSE;
6743 while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.);
6745 if (!tree_view->priv->fixed_height_check)
6748 _gtk_rbtree_set_fixed_height (tree_view->priv->tree, prev_height, FALSE);
6750 tree_view->priv->fixed_height_check = 1;
6756 GtkRequisition requisition;
6758 /* We temporarily guess a size, under the assumption that it will be the
6759 * same when we get our next size_allocate. If we don't do this, we'll be
6760 * in an inconsistent state when we call top_row_to_dy. */
6762 /* FIXME: This is called from size_request, for some reason it is not infinitely
6763 * recursing, we cannot call gtk_widget_get_preferred_size() here because that's
6764 * not allowed (from inside ->get_preferred_width/height() implementations, one
6765 * should call the vfuncs directly). However what is desired here is the full
6766 * size including any margins and limited by any alignment (i.e. after
6767 * GtkWidget:adjust_size_request() is called).
6769 * Currently bypassing this but the real solution is to not update the scroll adjustments
6770 * untill we've recieved an allocation (never update scroll adjustments from size-requests).
6772 gtk_tree_view_size_request (GTK_WIDGET (tree_view), &requisition, FALSE);
6774 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
6775 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
6776 gtk_adjustment_changed (tree_view->priv->hadjustment);
6777 gtk_adjustment_changed (tree_view->priv->vadjustment);
6780 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6783 if (path) gtk_tree_path_free (path);
6784 g_timer_destroy (timer);
6790 validate_rows (GtkTreeView *tree_view)
6794 retval = do_validate_rows (tree_view, TRUE);
6796 if (! retval && tree_view->priv->validate_rows_timer)
6798 g_source_remove (tree_view->priv->validate_rows_timer);
6799 tree_view->priv->validate_rows_timer = 0;
6806 validate_rows_handler (GtkTreeView *tree_view)
6810 retval = do_validate_rows (tree_view, TRUE);
6811 if (! retval && tree_view->priv->validate_rows_timer)
6813 g_source_remove (tree_view->priv->validate_rows_timer);
6814 tree_view->priv->validate_rows_timer = 0;
6821 do_presize_handler (GtkTreeView *tree_view)
6823 if (tree_view->priv->mark_rows_col_dirty)
6825 if (tree_view->priv->tree)
6826 _gtk_rbtree_column_invalid (tree_view->priv->tree);
6827 tree_view->priv->mark_rows_col_dirty = FALSE;
6829 validate_visible_area (tree_view);
6830 tree_view->priv->presize_handler_timer = 0;
6832 if (tree_view->priv->fixed_height_mode)
6834 GtkRequisition requisition;
6836 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view),
6837 &requisition, NULL);
6839 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
6840 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
6841 gtk_adjustment_changed (tree_view->priv->hadjustment);
6842 gtk_adjustment_changed (tree_view->priv->vadjustment);
6843 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6850 presize_handler_callback (gpointer data)
6852 do_presize_handler (GTK_TREE_VIEW (data));
6858 install_presize_handler (GtkTreeView *tree_view)
6860 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
6863 if (! tree_view->priv->presize_handler_timer)
6865 tree_view->priv->presize_handler_timer =
6866 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
6868 if (! tree_view->priv->validate_rows_timer)
6870 tree_view->priv->validate_rows_timer =
6871 gdk_threads_add_idle_full (GTK_TREE_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
6876 gtk_tree_view_bin_process_updates (GtkTreeView *tree_view)
6878 /* Prior to drawing, we make sure the visible area is validated. */
6879 if (tree_view->priv->presize_handler_timer)
6881 g_source_remove (tree_view->priv->presize_handler_timer);
6882 tree_view->priv->presize_handler_timer = 0;
6884 do_presize_handler (tree_view);
6887 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
6891 scroll_sync_handler (GtkTreeView *tree_view)
6893 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
6894 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
6895 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
6896 gtk_tree_view_top_row_to_dy (tree_view);
6898 gtk_tree_view_dy_to_top_row (tree_view);
6900 tree_view->priv->scroll_sync_timer = 0;
6906 install_scroll_sync_handler (GtkTreeView *tree_view)
6908 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
6911 if (!tree_view->priv->scroll_sync_timer)
6913 tree_view->priv->scroll_sync_timer =
6914 gdk_threads_add_idle_full (GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
6919 gtk_tree_view_set_top_row (GtkTreeView *tree_view,
6923 gtk_tree_row_reference_free (tree_view->priv->top_row);
6927 tree_view->priv->top_row = NULL;
6928 tree_view->priv->top_row_dy = 0;
6932 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
6933 tree_view->priv->top_row_dy = offset;
6937 /* Always call this iff dy is in the visible range. If the tree is empty, then
6938 * it's set to be NULL, and top_row_dy is 0;
6941 gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view)
6948 if (tree_view->priv->tree == NULL)
6950 gtk_tree_view_set_top_row (tree_view, NULL, 0);
6954 offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
6955 tree_view->priv->dy,
6960 gtk_tree_view_set_top_row (tree_view, NULL, 0);
6964 path = _gtk_tree_view_find_path (tree_view, tree, node);
6965 gtk_tree_view_set_top_row (tree_view, path, offset);
6966 gtk_tree_path_free (path);
6972 gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view)
6979 /* Avoid recursive calls */
6980 if (tree_view->priv->in_top_row_to_dy)
6983 if (tree_view->priv->top_row)
6984 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
6991 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
6994 gtk_tree_path_free (path);
6998 /* keep dy and set new toprow */
6999 gtk_tree_row_reference_free (tree_view->priv->top_row);
7000 tree_view->priv->top_row = NULL;
7001 tree_view->priv->top_row_dy = 0;
7002 /* DO NOT install the idle handler */
7003 gtk_tree_view_dy_to_top_row (tree_view);
7007 if (gtk_tree_view_get_row_height (tree_view, node)
7008 < tree_view->priv->top_row_dy)
7010 /* new top row -- do NOT install the idle handler */
7011 gtk_tree_view_dy_to_top_row (tree_view);
7015 new_dy = _gtk_rbtree_node_find_offset (tree, node);
7016 new_dy += tree_view->priv->top_row_dy;
7018 if (new_dy + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
7019 new_dy = tree_view->priv->height - tree_view->priv->vadjustment->page_size;
7021 new_dy = MAX (0, new_dy);
7023 tree_view->priv->in_top_row_to_dy = TRUE;
7024 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
7025 tree_view->priv->in_top_row_to_dy = FALSE;
7030 _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view,
7031 gboolean install_handler)
7033 tree_view->priv->mark_rows_col_dirty = TRUE;
7035 if (install_handler)
7036 install_presize_handler (tree_view);
7040 * This function works synchronously (due to the while (validate_rows...)
7043 * There was a check for column_type != GTK_TREE_VIEW_COLUMN_AUTOSIZE
7044 * here. You now need to check that yourself.
7047 _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
7048 GtkTreeViewColumn *column)
7050 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
7051 g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
7053 _gtk_tree_view_column_cell_set_dirty (column, FALSE);
7055 do_presize_handler (tree_view);
7056 while (validate_rows (tree_view));
7058 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7064 set_source_row (GdkDragContext *context,
7065 GtkTreeModel *model,
7066 GtkTreePath *source_row)
7068 g_object_set_data_full (G_OBJECT (context),
7069 I_("gtk-tree-view-source-row"),
7070 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
7071 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
7075 get_source_row (GdkDragContext *context)
7077 GtkTreeRowReference *ref =
7078 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
7081 return gtk_tree_row_reference_get_path (ref);
7088 GtkTreeRowReference *dest_row;
7089 guint path_down_mode : 1;
7090 guint empty_view_drop : 1;
7091 guint drop_append_mode : 1;
7096 dest_row_free (gpointer data)
7098 DestRow *dr = (DestRow *)data;
7100 gtk_tree_row_reference_free (dr->dest_row);
7101 g_slice_free (DestRow, dr);
7105 set_dest_row (GdkDragContext *context,
7106 GtkTreeModel *model,
7107 GtkTreePath *dest_row,
7108 gboolean path_down_mode,
7109 gboolean empty_view_drop,
7110 gboolean drop_append_mode)
7116 g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"),
7121 dr = g_slice_new (DestRow);
7123 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
7124 dr->path_down_mode = path_down_mode != FALSE;
7125 dr->empty_view_drop = empty_view_drop != FALSE;
7126 dr->drop_append_mode = drop_append_mode != FALSE;
7128 g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"),
7129 dr, (GDestroyNotify) dest_row_free);
7133 get_dest_row (GdkDragContext *context,
7134 gboolean *path_down_mode)
7137 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
7141 GtkTreePath *path = NULL;
7144 *path_down_mode = dr->path_down_mode;
7147 path = gtk_tree_row_reference_get_path (dr->dest_row);
7148 else if (dr->empty_view_drop)
7149 path = gtk_tree_path_new_from_indices (0, -1);
7153 if (path && dr->drop_append_mode)
7154 gtk_tree_path_next (path);
7162 /* Get/set whether drag_motion requested the drag data and
7163 * drag_data_received should thus not actually insert the data,
7164 * since the data doesn't result from a drop.
7167 set_status_pending (GdkDragContext *context,
7168 GdkDragAction suggested_action)
7170 g_object_set_data (G_OBJECT (context),
7171 I_("gtk-tree-view-status-pending"),
7172 GINT_TO_POINTER (suggested_action));
7175 static GdkDragAction
7176 get_status_pending (GdkDragContext *context)
7178 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
7179 "gtk-tree-view-status-pending"));
7182 static TreeViewDragInfo*
7183 get_info (GtkTreeView *tree_view)
7185 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
7189 destroy_info (TreeViewDragInfo *di)
7191 g_slice_free (TreeViewDragInfo, di);
7194 static TreeViewDragInfo*
7195 ensure_info (GtkTreeView *tree_view)
7197 TreeViewDragInfo *di;
7199 di = get_info (tree_view);
7203 di = g_slice_new0 (TreeViewDragInfo);
7205 g_object_set_data_full (G_OBJECT (tree_view),
7206 I_("gtk-tree-view-drag-info"),
7208 (GDestroyNotify) destroy_info);
7215 remove_info (GtkTreeView *tree_view)
7217 g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL);
7222 drag_scan_timeout (gpointer data)
7224 GtkTreeView *tree_view;
7226 GdkModifierType state;
7227 GtkTreePath *path = NULL;
7228 GtkTreeViewColumn *column = NULL;
7229 GdkRectangle visible_rect;
7231 GDK_THREADS_ENTER ();
7233 tree_view = GTK_TREE_VIEW (data);
7235 gdk_window_get_pointer (tree_view->priv->bin_window,
7238 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
7240 /* See if we are near the edge. */
7241 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
7242 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
7243 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
7244 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
7246 gtk_tree_view_get_path_at_pos (tree_view,
7247 tree_view->priv->bin_window,
7256 gtk_tree_view_scroll_to_cell (tree_view,
7262 gtk_tree_path_free (path);
7266 GDK_THREADS_LEAVE ();
7273 add_scroll_timeout (GtkTreeView *tree_view)
7275 if (tree_view->priv->scroll_timeout == 0)
7277 tree_view->priv->scroll_timeout =
7278 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
7283 remove_scroll_timeout (GtkTreeView *tree_view)
7285 if (tree_view->priv->scroll_timeout != 0)
7287 g_source_remove (tree_view->priv->scroll_timeout);
7288 tree_view->priv->scroll_timeout = 0;
7293 check_model_dnd (GtkTreeModel *model,
7294 GType required_iface,
7295 const gchar *signal)
7297 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
7299 g_warning ("You must override the default '%s' handler "
7300 "on GtkTreeView when using models that don't support "
7301 "the %s interface and enabling drag-and-drop. The simplest way to do this "
7302 "is to connect to '%s' and call "
7303 "g_signal_stop_emission_by_name() in your signal handler to prevent "
7304 "the default handler from running. Look at the source code "
7305 "for the default handler in gtktreeview.c to get an idea what "
7306 "your handler should do. (gtktreeview.c is in the GTK source "
7307 "code.) If you're using GTK from a language other than C, "
7308 "there may be a more natural way to override default handlers, e.g. via derivation.",
7309 signal, g_type_name (required_iface), signal);
7317 remove_open_timeout (GtkTreeView *tree_view)
7319 if (tree_view->priv->open_dest_timeout != 0)
7321 g_source_remove (tree_view->priv->open_dest_timeout);
7322 tree_view->priv->open_dest_timeout = 0;
7328 open_row_timeout (gpointer data)
7330 GtkTreeView *tree_view = data;
7331 GtkTreePath *dest_path = NULL;
7332 GtkTreeViewDropPosition pos;
7333 gboolean result = FALSE;
7335 gtk_tree_view_get_drag_dest_row (tree_view,
7340 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7341 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
7343 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
7344 tree_view->priv->open_dest_timeout = 0;
7346 gtk_tree_path_free (dest_path);
7351 gtk_tree_path_free (dest_path);
7360 scroll_row_timeout (gpointer data)
7362 GtkTreeView *tree_view = data;
7364 gtk_tree_view_vertical_autoscroll (tree_view);
7366 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
7367 gtk_tree_view_update_rubber_band (tree_view);
7372 /* Returns TRUE if event should not be propagated to parent widgets */
7374 set_destination_row (GtkTreeView *tree_view,
7375 GdkDragContext *context,
7376 /* coordinates relative to the widget */
7379 GdkDragAction *suggested_action,
7382 GtkTreePath *path = NULL;
7383 GtkTreeViewDropPosition pos;
7384 GtkTreeViewDropPosition old_pos;
7385 TreeViewDragInfo *di;
7387 GtkTreePath *old_dest_path = NULL;
7388 gboolean can_drop = FALSE;
7390 *suggested_action = 0;
7393 widget = GTK_WIDGET (tree_view);
7395 di = get_info (tree_view);
7397 if (di == NULL || y - gtk_tree_view_get_effective_header_height (tree_view) < 0)
7399 /* someone unset us as a drag dest, note that if
7400 * we return FALSE drag_leave isn't called
7403 gtk_tree_view_set_drag_dest_row (tree_view,
7405 GTK_TREE_VIEW_DROP_BEFORE);
7407 remove_scroll_timeout (GTK_TREE_VIEW (widget));
7408 remove_open_timeout (GTK_TREE_VIEW (widget));
7410 return FALSE; /* no longer a drop site */
7413 *target = gtk_drag_dest_find_target (widget, context,
7414 gtk_drag_dest_get_target_list (widget));
7415 if (*target == GDK_NONE)
7420 if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
7426 GtkTreeModel *model;
7428 remove_open_timeout (tree_view);
7430 /* the row got dropped on empty space, let's setup a special case
7434 gtk_tree_path_free (path);
7436 model = gtk_tree_view_get_model (tree_view);
7438 n_children = gtk_tree_model_iter_n_children (model, NULL);
7441 pos = GTK_TREE_VIEW_DROP_AFTER;
7442 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
7446 pos = GTK_TREE_VIEW_DROP_BEFORE;
7447 path = gtk_tree_path_new_from_indices (0, -1);
7457 /* If we left the current row's "open" zone, unset the timeout for
7460 gtk_tree_view_get_drag_dest_row (tree_view,
7464 if (old_dest_path &&
7465 (gtk_tree_path_compare (path, old_dest_path) != 0 ||
7466 !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7467 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)))
7468 remove_open_timeout (tree_view);
7471 gtk_tree_path_free (old_dest_path);
7473 if (TRUE /* FIXME if the location droppable predicate */)
7481 GtkWidget *source_widget;
7483 *suggested_action = gdk_drag_context_get_suggested_action (context);
7484 source_widget = gtk_drag_get_source_widget (context);
7486 if (source_widget == widget)
7488 /* Default to MOVE, unless the user has
7489 * pressed ctrl or shift to affect available actions
7491 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
7492 *suggested_action = GDK_ACTION_MOVE;
7495 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7500 /* can't drop here */
7501 remove_open_timeout (tree_view);
7503 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7505 GTK_TREE_VIEW_DROP_BEFORE);
7509 gtk_tree_path_free (path);
7515 get_logical_dest_row (GtkTreeView *tree_view,
7516 gboolean *path_down_mode,
7517 gboolean *drop_append_mode)
7519 /* adjust path to point to the row the drop goes in front of */
7520 GtkTreePath *path = NULL;
7521 GtkTreeViewDropPosition pos;
7523 g_return_val_if_fail (path_down_mode != NULL, NULL);
7524 g_return_val_if_fail (drop_append_mode != NULL, NULL);
7526 *path_down_mode = FALSE;
7527 *drop_append_mode = 0;
7529 gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
7534 if (pos == GTK_TREE_VIEW_DROP_BEFORE)
7536 else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
7537 pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
7538 *path_down_mode = TRUE;
7542 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
7544 g_assert (pos == GTK_TREE_VIEW_DROP_AFTER);
7546 if (!gtk_tree_model_get_iter (model, &iter, path) ||
7547 !gtk_tree_model_iter_next (model, &iter))
7548 *drop_append_mode = 1;
7551 *drop_append_mode = 0;
7552 gtk_tree_path_next (path);
7560 gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view,
7561 GdkEventMotion *event)
7563 GtkWidget *widget = GTK_WIDGET (tree_view);
7564 GdkDragContext *context;
7565 TreeViewDragInfo *di;
7566 GtkTreePath *path = NULL;
7568 gint cell_x, cell_y;
7569 GtkTreeModel *model;
7570 gboolean retval = FALSE;
7572 di = get_info (tree_view);
7574 if (di == NULL || !di->source_set)
7577 if (tree_view->priv->pressed_button < 0)
7580 if (!gtk_drag_check_threshold (widget,
7581 tree_view->priv->press_start_x,
7582 tree_view->priv->press_start_y,
7583 event->x, event->y))
7586 model = gtk_tree_view_get_model (tree_view);
7591 button = tree_view->priv->pressed_button;
7592 tree_view->priv->pressed_button = -1;
7594 gtk_tree_view_get_path_at_pos (tree_view,
7595 tree_view->priv->press_start_x,
7596 tree_view->priv->press_start_y,
7605 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7606 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7610 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
7613 /* Now we can begin the drag */
7617 context = gtk_drag_begin (widget,
7618 gtk_drag_source_get_target_list (widget),
7623 set_source_row (context, model, path);
7627 gtk_tree_path_free (path);
7634 gtk_tree_view_drag_begin (GtkWidget *widget,
7635 GdkDragContext *context)
7637 GtkTreeView *tree_view;
7638 GtkTreePath *path = NULL;
7639 gint cell_x, cell_y;
7640 cairo_surface_t *row_pix;
7641 TreeViewDragInfo *di;
7643 tree_view = GTK_TREE_VIEW (widget);
7645 /* if the user uses a custom DND source impl, we don't set the icon here */
7646 di = get_info (tree_view);
7648 if (di == NULL || !di->source_set)
7651 gtk_tree_view_get_path_at_pos (tree_view,
7652 tree_view->priv->press_start_x,
7653 tree_view->priv->press_start_y,
7659 g_return_if_fail (path != NULL);
7661 row_pix = gtk_tree_view_create_row_drag_icon (tree_view,
7663 cairo_surface_set_device_offset (row_pix,
7664 /* the + 1 is for the black border in the icon */
7665 - (tree_view->priv->press_start_x + 1),
7668 gtk_drag_set_icon_surface (context, row_pix);
7670 cairo_surface_destroy (row_pix);
7671 gtk_tree_path_free (path);
7675 gtk_tree_view_drag_end (GtkWidget *widget,
7676 GdkDragContext *context)
7681 /* Default signal implementations for the drag signals */
7683 gtk_tree_view_drag_data_get (GtkWidget *widget,
7684 GdkDragContext *context,
7685 GtkSelectionData *selection_data,
7689 GtkTreeView *tree_view;
7690 GtkTreeModel *model;
7691 TreeViewDragInfo *di;
7692 GtkTreePath *source_row;
7694 tree_view = GTK_TREE_VIEW (widget);
7696 model = gtk_tree_view_get_model (tree_view);
7701 di = get_info (GTK_TREE_VIEW (widget));
7706 source_row = get_source_row (context);
7708 if (source_row == NULL)
7711 /* We can implement the GTK_TREE_MODEL_ROW target generically for
7712 * any model; for DragSource models there are some other targets
7716 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
7717 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
7722 /* If drag_data_get does nothing, try providing row data. */
7723 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
7725 gtk_tree_set_row_drag_data (selection_data,
7731 gtk_tree_path_free (source_row);
7736 gtk_tree_view_drag_data_delete (GtkWidget *widget,
7737 GdkDragContext *context)
7739 TreeViewDragInfo *di;
7740 GtkTreeModel *model;
7741 GtkTreeView *tree_view;
7742 GtkTreePath *source_row;
7744 tree_view = GTK_TREE_VIEW (widget);
7745 model = gtk_tree_view_get_model (tree_view);
7747 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
7750 di = get_info (tree_view);
7755 source_row = get_source_row (context);
7757 if (source_row == NULL)
7760 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
7763 gtk_tree_path_free (source_row);
7765 set_source_row (context, NULL, NULL);
7769 gtk_tree_view_drag_leave (GtkWidget *widget,
7770 GdkDragContext *context,
7773 /* unset any highlight row */
7774 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7776 GTK_TREE_VIEW_DROP_BEFORE);
7778 remove_scroll_timeout (GTK_TREE_VIEW (widget));
7779 remove_open_timeout (GTK_TREE_VIEW (widget));
7784 gtk_tree_view_drag_motion (GtkWidget *widget,
7785 GdkDragContext *context,
7786 /* coordinates relative to the widget */
7792 GtkTreePath *path = NULL;
7793 GtkTreeViewDropPosition pos;
7794 GtkTreeView *tree_view;
7795 GdkDragAction suggested_action = 0;
7798 tree_view = GTK_TREE_VIEW (widget);
7800 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
7803 gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
7805 /* we only know this *after* set_desination_row */
7806 empty = tree_view->priv->empty_view_drop;
7808 if (path == NULL && !empty)
7810 /* Can't drop here. */
7811 gdk_drag_status (context, 0, time);
7815 if (tree_view->priv->open_dest_timeout == 0 &&
7816 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7817 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
7819 tree_view->priv->open_dest_timeout =
7820 gdk_threads_add_timeout (AUTO_EXPAND_TIMEOUT, open_row_timeout, tree_view);
7824 add_scroll_timeout (tree_view);
7827 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
7829 /* Request data so we can use the source row when
7830 * determining whether to accept the drop
7832 set_status_pending (context, suggested_action);
7833 gtk_drag_get_data (widget, context, target, time);
7837 set_status_pending (context, 0);
7838 gdk_drag_status (context, suggested_action, time);
7843 gtk_tree_path_free (path);
7850 gtk_tree_view_drag_drop (GtkWidget *widget,
7851 GdkDragContext *context,
7852 /* coordinates relative to the widget */
7857 GtkTreeView *tree_view;
7859 GdkDragAction suggested_action = 0;
7860 GdkAtom target = GDK_NONE;
7861 TreeViewDragInfo *di;
7862 GtkTreeModel *model;
7863 gboolean path_down_mode;
7864 gboolean drop_append_mode;
7866 tree_view = GTK_TREE_VIEW (widget);
7868 model = gtk_tree_view_get_model (tree_view);
7870 remove_scroll_timeout (GTK_TREE_VIEW (widget));
7871 remove_open_timeout (GTK_TREE_VIEW (widget));
7873 di = get_info (tree_view);
7878 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
7881 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
7884 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
7886 if (target != GDK_NONE && path != NULL)
7888 /* in case a motion had requested drag data, change things so we
7889 * treat drag data receives as a drop.
7891 set_status_pending (context, 0);
7892 set_dest_row (context, model, path,
7893 path_down_mode, tree_view->priv->empty_view_drop,
7898 gtk_tree_path_free (path);
7900 /* Unset this thing */
7901 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7903 GTK_TREE_VIEW_DROP_BEFORE);
7905 if (target != GDK_NONE)
7907 gtk_drag_get_data (widget, context, target, time);
7915 gtk_tree_view_drag_data_received (GtkWidget *widget,
7916 GdkDragContext *context,
7917 /* coordinates relative to the widget */
7920 GtkSelectionData *selection_data,
7925 TreeViewDragInfo *di;
7926 gboolean accepted = FALSE;
7927 GtkTreeModel *model;
7928 GtkTreeView *tree_view;
7929 GtkTreePath *dest_row;
7930 GdkDragAction suggested_action;
7931 gboolean path_down_mode;
7932 gboolean drop_append_mode;
7934 tree_view = GTK_TREE_VIEW (widget);
7936 model = gtk_tree_view_get_model (tree_view);
7938 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
7941 di = get_info (tree_view);
7946 suggested_action = get_status_pending (context);
7948 if (suggested_action)
7950 /* We are getting this data due to a request in drag_motion,
7951 * rather than due to a request in drag_drop, so we are just
7952 * supposed to call drag_status, not actually paste in the
7955 path = get_logical_dest_row (tree_view, &path_down_mode,
7959 suggested_action = 0;
7960 else if (path_down_mode)
7961 gtk_tree_path_down (path);
7963 if (suggested_action)
7965 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7971 path_down_mode = FALSE;
7972 gtk_tree_path_up (path);
7974 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7977 suggested_action = 0;
7980 suggested_action = 0;
7984 gdk_drag_status (context, suggested_action, time);
7987 gtk_tree_path_free (path);
7989 /* If you can't drop, remove user drop indicator until the next motion */
7990 if (suggested_action == 0)
7991 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7993 GTK_TREE_VIEW_DROP_BEFORE);
7998 dest_row = get_dest_row (context, &path_down_mode);
8000 if (dest_row == NULL)
8003 if (gtk_selection_data_get_length (selection_data) >= 0)
8007 gtk_tree_path_down (dest_row);
8008 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
8009 dest_row, selection_data))
8010 gtk_tree_path_up (dest_row);
8014 if (gtk_selection_data_get_length (selection_data) >= 0)
8016 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
8022 gtk_drag_finish (context,
8024 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
8027 if (gtk_tree_path_get_depth (dest_row) == 1
8028 && gtk_tree_path_get_indices (dest_row)[0] == 0)
8030 /* special special case drag to "0", scroll to first item */
8031 if (!tree_view->priv->scroll_to_path)
8032 gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
8035 gtk_tree_path_free (dest_row);
8038 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
8043 /* GtkContainer Methods
8048 gtk_tree_view_remove (GtkContainer *container,
8051 GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8052 GtkTreeViewChild *child = NULL;
8055 tmp_list = tree_view->priv->children;
8058 child = tmp_list->data;
8059 if (child->widget == widget)
8061 gtk_widget_unparent (widget);
8063 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
8064 g_list_free_1 (tmp_list);
8065 g_slice_free (GtkTreeViewChild, child);
8069 tmp_list = tmp_list->next;
8072 tmp_list = tree_view->priv->columns;
8076 GtkTreeViewColumn *column;
8079 column = tmp_list->data;
8080 button = gtk_tree_view_column_get_button (column);
8082 if (button == widget)
8084 gtk_widget_unparent (widget);
8087 tmp_list = tmp_list->next;
8092 gtk_tree_view_forall (GtkContainer *container,
8093 gboolean include_internals,
8094 GtkCallback callback,
8095 gpointer callback_data)
8097 GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8098 GtkTreeViewChild *child = NULL;
8099 GtkTreeViewColumn *column;
8103 tmp_list = tree_view->priv->children;
8106 child = tmp_list->data;
8107 tmp_list = tmp_list->next;
8109 (* callback) (child->widget, callback_data);
8111 if (include_internals == FALSE)
8114 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
8116 column = tmp_list->data;
8117 button = gtk_tree_view_column_get_button (column);
8120 (* callback) (button, callback_data);
8124 /* Returns TRUE is any of the columns contains a cell that can-focus.
8125 * If this is not the case, a column-spanning focus rectangle will be
8129 gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view)
8133 for (list = tree_view->priv->columns; list; list = list->next)
8135 GtkTreeViewColumn *column = list->data;
8137 if (!gtk_tree_view_column_get_visible (column))
8139 if (gtk_cell_area_is_activatable (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column))))
8147 column_sizing_notify (GObject *object,
8151 GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (object);
8153 if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED)
8154 /* disable fixed height mode */
8155 g_object_set (data, "fixed-height-mode", FALSE, NULL);
8159 * gtk_tree_view_set_fixed_height_mode:
8160 * @tree_view: a #GtkTreeView
8161 * @enable: %TRUE to enable fixed height mode
8163 * Enables or disables the fixed height mode of @tree_view.
8164 * Fixed height mode speeds up #GtkTreeView by assuming that all
8165 * rows have the same height.
8166 * Only enable this option if all rows are the same height and all
8167 * columns are of type %GTK_TREE_VIEW_COLUMN_FIXED.
8172 gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
8177 enable = enable != FALSE;
8179 if (enable == tree_view->priv->fixed_height_mode)
8184 tree_view->priv->fixed_height_mode = 0;
8185 tree_view->priv->fixed_height = -1;
8187 /* force a revalidation */
8188 install_presize_handler (tree_view);
8192 /* make sure all columns are of type FIXED */
8193 for (l = tree_view->priv->columns; l; l = l->next)
8195 GtkTreeViewColumn *c = l->data;
8197 g_return_if_fail (gtk_tree_view_column_get_sizing (c) == GTK_TREE_VIEW_COLUMN_FIXED);
8200 /* yes, we really have to do this is in a separate loop */
8201 for (l = tree_view->priv->columns; l; l = l->next)
8202 g_signal_connect (l->data, "notify::sizing",
8203 G_CALLBACK (column_sizing_notify), tree_view);
8205 tree_view->priv->fixed_height_mode = 1;
8206 tree_view->priv->fixed_height = -1;
8208 if (tree_view->priv->tree)
8209 initialize_fixed_height_mode (tree_view);
8212 g_object_notify (G_OBJECT (tree_view), "fixed-height-mode");
8216 * gtk_tree_view_get_fixed_height_mode:
8217 * @tree_view: a #GtkTreeView
8219 * Returns whether fixed height mode is turned on for @tree_view.
8221 * Return value: %TRUE if @tree_view is in fixed height mode
8226 gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view)
8228 return tree_view->priv->fixed_height_mode;
8231 /* Returns TRUE if the focus is within the headers, after the focus operation is
8235 gtk_tree_view_header_focus (GtkTreeView *tree_view,
8236 GtkDirectionType dir,
8237 gboolean clamp_column_visible)
8239 GtkTreeViewColumn *column;
8240 GtkWidget *focus_child;
8242 GList *last_column, *first_column;
8246 if (! tree_view->priv->headers_visible)
8249 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
8251 first_column = tree_view->priv->columns;
8252 while (first_column)
8254 column = GTK_TREE_VIEW_COLUMN (first_column->data);
8255 button = gtk_tree_view_column_get_button (column);
8257 if (gtk_widget_get_can_focus (button) &&
8258 gtk_tree_view_column_get_visible (column) &&
8259 (gtk_tree_view_column_get_clickable (column) ||
8260 gtk_tree_view_column_get_reorderable (column)))
8262 first_column = first_column->next;
8265 /* No headers are visible, or are focusable. We can't focus in or out.
8267 if (first_column == NULL)
8270 last_column = g_list_last (tree_view->priv->columns);
8273 column = GTK_TREE_VIEW_COLUMN (last_column->data);
8274 button = gtk_tree_view_column_get_button (column);
8276 if (gtk_widget_get_can_focus (button) &&
8277 gtk_tree_view_column_get_visible (column) &&
8278 (gtk_tree_view_column_get_clickable (column) ||
8279 gtk_tree_view_column_get_reorderable (column)))
8281 last_column = last_column->prev;
8285 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8289 case GTK_DIR_TAB_BACKWARD:
8290 case GTK_DIR_TAB_FORWARD:
8293 if (focus_child == NULL)
8295 if (tree_view->priv->focus_column != NULL)
8296 button = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
8300 if (button && gtk_widget_get_can_focus (button))
8301 focus_child = button;
8303 focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
8305 gtk_widget_grab_focus (focus_child);
8312 if (focus_child == NULL)
8314 if (tree_view->priv->focus_column != NULL)
8315 focus_child = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
8316 else if (dir == GTK_DIR_LEFT)
8317 focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data));
8319 focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
8321 gtk_widget_grab_focus (focus_child);
8325 if (gtk_widget_child_focus (focus_child, dir))
8327 /* The focus moves inside the button. */
8328 /* This is probably a great example of bad UI */
8332 /* We need to move the focus among the row of buttons. */
8333 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
8334 if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
8337 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
8338 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
8340 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8346 GtkTreeViewColumn *column;
8349 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
8350 tmp_list = tmp_list->next;
8352 tmp_list = tmp_list->prev;
8354 if (tmp_list == NULL)
8356 g_warning ("Internal button not found");
8359 column = tmp_list->data;
8360 button = gtk_tree_view_column_get_button (column);
8362 gtk_tree_view_column_get_visible (column) &&
8363 gtk_widget_get_can_focus (button))
8365 focus_child = button;
8366 gtk_widget_grab_focus (button);
8372 g_assert_not_reached ();
8376 /* if focus child is non-null, we assume it's been set to the current focus child
8380 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
8381 if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
8383 tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
8387 if (clamp_column_visible)
8389 gtk_tree_view_clamp_column_visible (tree_view,
8390 tree_view->priv->focus_column,
8395 return (focus_child != NULL);
8398 /* This function returns in 'path' the first focusable path, if the given path
8399 * is already focusable, it's the returned one.
8402 search_first_focusable_path (GtkTreeView *tree_view,
8404 gboolean search_forward,
8405 GtkRBTree **new_tree,
8406 GtkRBNode **new_node)
8408 GtkRBTree *tree = NULL;
8409 GtkRBNode *node = NULL;
8411 if (!path || !*path)
8414 _gtk_tree_view_find_node (tree_view, *path, &tree, &node);
8419 while (node && row_is_separator (tree_view, NULL, *path))
8422 _gtk_rbtree_next_full (tree, node, &tree, &node);
8424 _gtk_rbtree_prev_full (tree, node, &tree, &node);
8427 gtk_tree_path_free (*path);
8430 *path = _gtk_tree_view_find_path (tree_view, tree, node);
8441 return (*path != NULL);
8445 gtk_tree_view_focus (GtkWidget *widget,
8446 GtkDirectionType direction)
8448 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8449 GtkContainer *container = GTK_CONTAINER (widget);
8450 GtkWidget *focus_child;
8452 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
8455 focus_child = gtk_container_get_focus_child (container);
8457 gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE);
8458 /* Case 1. Headers currently have focus. */
8465 gtk_tree_view_header_focus (tree_view, direction, TRUE);
8467 case GTK_DIR_TAB_BACKWARD:
8470 case GTK_DIR_TAB_FORWARD:
8472 gtk_widget_grab_focus (widget);
8475 g_assert_not_reached ();
8480 /* Case 2. We don't have focus at all. */
8481 if (!gtk_widget_has_focus (widget))
8483 gtk_widget_grab_focus (widget);
8487 /* Case 3. We have focus already. */
8488 if (direction == GTK_DIR_TAB_BACKWARD)
8489 return (gtk_tree_view_header_focus (tree_view, direction, FALSE));
8490 else if (direction == GTK_DIR_TAB_FORWARD)
8493 /* Other directions caught by the keybindings */
8494 gtk_widget_grab_focus (widget);
8499 gtk_tree_view_grab_focus (GtkWidget *widget)
8501 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->grab_focus (widget);
8503 gtk_tree_view_focus_to_cursor (GTK_TREE_VIEW (widget));
8507 gtk_tree_view_style_set (GtkWidget *widget,
8508 GtkStyle *previous_style)
8510 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8513 GtkTreeViewColumn *column;
8515 if (gtk_widget_get_realized (widget))
8517 style = gtk_widget_get_style (widget);
8518 gdk_window_set_background (tree_view->priv->bin_window,
8519 &style->base[gtk_widget_get_state (widget)]);
8520 gtk_style_set_background (style, tree_view->priv->header_window, GTK_STATE_NORMAL);
8522 gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
8523 gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
8526 gtk_widget_style_get (widget,
8527 "expander-size", &tree_view->priv->expander_size,
8529 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
8531 for (list = tree_view->priv->columns; list; list = list->next)
8533 column = list->data;
8534 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
8537 tree_view->priv->fixed_height = -1;
8538 _gtk_rbtree_mark_invalid (tree_view->priv->tree);
8540 gtk_widget_queue_resize (widget);
8545 gtk_tree_view_set_focus_child (GtkContainer *container,
8548 GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8551 for (list = tree_view->priv->columns; list; list = list->next)
8553 if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (list->data)) == child)
8555 tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
8560 GTK_CONTAINER_CLASS (gtk_tree_view_parent_class)->set_focus_child (container, child);
8564 gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
8565 GtkMovementStep step,
8568 GdkModifierType state;
8570 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
8571 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
8572 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
8573 step == GTK_MOVEMENT_DISPLAY_LINES ||
8574 step == GTK_MOVEMENT_PAGES ||
8575 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
8577 if (tree_view->priv->tree == NULL)
8579 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8582 gtk_tree_view_stop_editing (tree_view, FALSE);
8583 tree_view->priv->draw_keyfocus = TRUE;
8584 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8586 if (gtk_get_current_event_state (&state))
8588 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
8589 tree_view->priv->ctrl_pressed = TRUE;
8590 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
8591 tree_view->priv->shift_pressed = TRUE;
8593 /* else we assume not pressed */
8597 /* currently we make no distinction. When we go bi-di, we need to */
8598 case GTK_MOVEMENT_LOGICAL_POSITIONS:
8599 case GTK_MOVEMENT_VISUAL_POSITIONS:
8600 gtk_tree_view_move_cursor_left_right (tree_view, count);
8602 case GTK_MOVEMENT_DISPLAY_LINES:
8603 gtk_tree_view_move_cursor_up_down (tree_view, count);
8605 case GTK_MOVEMENT_PAGES:
8606 gtk_tree_view_move_cursor_page_up_down (tree_view, count);
8608 case GTK_MOVEMENT_BUFFER_ENDS:
8609 gtk_tree_view_move_cursor_start_end (tree_view, count);
8612 g_assert_not_reached ();
8615 tree_view->priv->ctrl_pressed = FALSE;
8616 tree_view->priv->shift_pressed = FALSE;
8622 gtk_tree_view_put (GtkTreeView *tree_view,
8623 GtkWidget *child_widget,
8624 /* in bin_window coordinates */
8630 GtkTreeViewChild *child;
8632 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
8633 g_return_if_fail (GTK_IS_WIDGET (child_widget));
8635 child = g_slice_new (GtkTreeViewChild);
8637 child->widget = child_widget;
8640 child->width = width;
8641 child->height = height;
8643 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
8645 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8646 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
8648 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
8652 _gtk_tree_view_child_move_resize (GtkTreeView *tree_view,
8654 /* in tree coordinates */
8660 GtkTreeViewChild *child = NULL;
8662 GdkRectangle allocation;
8664 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
8665 g_return_if_fail (GTK_IS_WIDGET (widget));
8667 for (list = tree_view->priv->children; list; list = list->next)
8669 if (((GtkTreeViewChild *)list->data)->widget == widget)
8678 allocation.x = child->x = x;
8679 allocation.y = child->y = y;
8680 allocation.width = child->width = width;
8681 allocation.height = child->height = height;
8683 if (gtk_widget_get_realized (widget))
8684 gtk_widget_size_allocate (widget, &allocation);
8688 /* TreeModel Callbacks
8692 gtk_tree_view_row_changed (GtkTreeModel *model,
8697 GtkTreeView *tree_view = (GtkTreeView *)data;
8700 gboolean free_path = FALSE;
8702 GtkTreePath *cursor_path;
8704 g_return_if_fail (path != NULL || iter != NULL);
8706 if (tree_view->priv->cursor != NULL)
8707 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8711 if (tree_view->priv->edited_column &&
8712 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
8713 gtk_tree_view_stop_editing (tree_view, TRUE);
8715 if (cursor_path != NULL)
8716 gtk_tree_path_free (cursor_path);
8720 path = gtk_tree_model_get_path (model, iter);
8723 else if (iter == NULL)
8724 gtk_tree_model_get_iter (model, iter, path);
8726 if (_gtk_tree_view_find_node (tree_view,
8730 /* We aren't actually showing the node */
8736 if (tree_view->priv->fixed_height_mode
8737 && tree_view->priv->fixed_height >= 0)
8739 _gtk_rbtree_node_set_height (tree, node, tree_view->priv->fixed_height);
8740 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8741 gtk_tree_view_node_queue_redraw (tree_view, tree, node);
8745 _gtk_rbtree_node_mark_invalid (tree, node);
8746 for (list = tree_view->priv->columns; list; list = list->next)
8748 GtkTreeViewColumn *column;
8750 column = list->data;
8751 if (!gtk_tree_view_column_get_visible (column))
8754 if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
8756 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
8762 if (!tree_view->priv->fixed_height_mode &&
8763 gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8764 install_presize_handler (tree_view);
8766 gtk_tree_path_free (path);
8770 gtk_tree_view_row_inserted (GtkTreeModel *model,
8775 GtkTreeView *tree_view = (GtkTreeView *) data;
8777 GtkRBTree *tmptree, *tree;
8778 GtkRBNode *tmpnode = NULL;
8782 gboolean free_path = FALSE;
8783 gboolean node_visible = TRUE;
8785 g_return_if_fail (path != NULL || iter != NULL);
8787 if (tree_view->priv->fixed_height_mode
8788 && tree_view->priv->fixed_height >= 0)
8789 height = tree_view->priv->fixed_height;
8795 path = gtk_tree_model_get_path (model, iter);
8798 else if (iter == NULL)
8799 gtk_tree_model_get_iter (model, iter, path);
8801 if (tree_view->priv->tree == NULL)
8802 tree_view->priv->tree = _gtk_rbtree_new ();
8804 tmptree = tree = tree_view->priv->tree;
8806 /* Update all row-references */
8807 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
8808 depth = gtk_tree_path_get_depth (path);
8809 indices = gtk_tree_path_get_indices (path);
8811 /* First, find the parent tree */
8812 while (i < depth - 1)
8814 if (tmptree == NULL)
8816 /* We aren't showing the node */
8817 node_visible = FALSE;
8821 tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
8822 if (tmpnode == NULL)
8824 g_warning ("A node was inserted with a parent that's not in the tree.\n" \
8825 "This possibly means that a GtkTreeModel inserted a child node\n" \
8826 "before the parent was inserted.");
8829 else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT))
8831 /* FIXME enforce correct behavior on model, probably */
8832 /* In theory, the model should have emitted has_child_toggled here. We
8833 * try to catch it anyway, just to be safe, in case the model hasn't.
8835 GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view,
8838 gtk_tree_view_row_has_child_toggled (model, tmppath, NULL, data);
8839 gtk_tree_path_free (tmppath);
8843 tmptree = tmpnode->children;
8850 node_visible = FALSE;
8855 gtk_tree_model_ref_node (tree_view->priv->model, iter);
8856 if (indices[depth - 1] == 0)
8858 tmpnode = _gtk_rbtree_find_count (tree, 1);
8859 tmpnode = _gtk_rbtree_insert_before (tree, tmpnode, height, FALSE);
8863 tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]);
8864 tmpnode = _gtk_rbtree_insert_after (tree, tmpnode, height, FALSE);
8871 _gtk_rbtree_node_mark_valid (tree, tmpnode);
8873 if (node_visible && node_is_visible (tree_view, tree, tmpnode))
8874 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8876 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
8879 install_presize_handler (tree_view);
8881 gtk_tree_path_free (path);
8885 gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
8890 GtkTreeView *tree_view = (GtkTreeView *)data;
8891 GtkTreeIter real_iter;
8895 gboolean free_path = FALSE;
8897 g_return_if_fail (path != NULL || iter != NULL);
8904 path = gtk_tree_model_get_path (model, iter);
8907 else if (iter == NULL)
8908 gtk_tree_model_get_iter (model, &real_iter, path);
8910 if (_gtk_tree_view_find_node (tree_view,
8914 /* We aren't actually showing the node */
8920 has_child = gtk_tree_model_iter_has_child (model, &real_iter);
8923 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child)
8927 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT);
8929 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
8931 if (has_child && tree_view->priv->is_list)
8933 tree_view->priv->is_list = FALSE;
8934 if (tree_view->priv->show_expanders)
8938 for (list = tree_view->priv->columns; list; list = list->next)
8939 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
8941 _gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE);
8945 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8949 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
8954 gtk_tree_path_free (path);
8958 count_children_helper (GtkRBTree *tree,
8963 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, data);
8964 (*((gint *)data))++;
8968 check_selection_helper (GtkRBTree *tree,
8972 gint *value = (gint *)data;
8974 *value = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
8976 if (node->children && !*value)
8977 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data);
8981 gtk_tree_view_row_deleted (GtkTreeModel *model,
8985 GtkTreeView *tree_view = (GtkTreeView *)data;
8989 gint selection_changed = FALSE;
8991 g_return_if_fail (path != NULL);
8993 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
8995 if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
9001 /* check if the selection has been changed */
9002 _gtk_rbtree_traverse (tree, node, G_POST_ORDER,
9003 check_selection_helper, &selection_changed);
9005 for (list = tree_view->priv->columns; list; list = list->next)
9006 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)) &&
9007 gtk_tree_view_column_get_sizing (GTK_TREE_VIEW_COLUMN (list->data)) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
9008 _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE);
9010 /* Ensure we don't have a dangling pointer to a dead node */
9011 ensure_unprelighted (tree_view);
9013 /* Cancel editting if we've started */
9014 gtk_tree_view_stop_editing (tree_view, TRUE);
9016 /* If we have a node expanded/collapsed timeout, remove it */
9017 remove_expand_collapse_timeout (tree_view);
9019 if (tree_view->priv->destroy_count_func)
9021 gint child_count = 0;
9023 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, &child_count);
9024 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
9027 if (tree->root->count == 1)
9029 if (tree_view->priv->tree == tree)
9030 tree_view->priv->tree = NULL;
9032 _gtk_rbtree_remove (tree);
9036 _gtk_rbtree_remove_node (tree, node);
9039 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
9041 gtk_tree_row_reference_free (tree_view->priv->top_row);
9042 tree_view->priv->top_row = NULL;
9045 install_scroll_sync_handler (tree_view);
9047 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9049 if (selection_changed)
9050 g_signal_emit_by_name (tree_view->priv->selection, "changed");
9054 gtk_tree_view_rows_reordered (GtkTreeModel *model,
9055 GtkTreePath *parent,
9060 GtkTreeView *tree_view = GTK_TREE_VIEW (data);
9065 len = gtk_tree_model_iter_n_children (model, iter);
9070 gtk_tree_row_reference_reordered (G_OBJECT (data),
9075 if (_gtk_tree_view_find_node (tree_view,
9081 /* We need to special case the parent path */
9083 tree = tree_view->priv->tree;
9085 tree = node->children;
9090 if (tree_view->priv->edited_column)
9091 gtk_tree_view_stop_editing (tree_view, TRUE);
9093 /* we need to be unprelighted */
9094 ensure_unprelighted (tree_view);
9096 /* clear the timeout */
9097 cancel_arrow_animation (tree_view);
9099 _gtk_rbtree_reorder (tree, new_order, len);
9101 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9103 gtk_tree_view_dy_to_top_row (tree_view);
9107 /* Internal tree functions
9112 gtk_tree_view_get_background_xrange (GtkTreeView *tree_view,
9114 GtkTreeViewColumn *column,
9118 GtkTreeViewColumn *tmp_column = NULL;
9129 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9132 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
9134 list = (rtl ? list->prev : list->next))
9136 tmp_column = list->data;
9138 if (tmp_column == column)
9141 if (gtk_tree_view_column_get_visible (tmp_column))
9142 total_width += gtk_tree_view_column_get_width (tmp_column);
9145 if (tmp_column != column)
9147 g_warning (G_STRLOC": passed-in column isn't in the tree");
9156 if (gtk_tree_view_column_get_visible (column))
9157 *x2 = total_width + gtk_tree_view_column_get_width (column);
9159 *x2 = total_width; /* width of 0 */
9164 gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
9171 GtkTreeViewColumn *tmp_column = NULL;
9173 gboolean indent_expanders;
9176 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9179 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
9181 list = (rtl ? list->prev : list->next))
9183 tmp_column = list->data;
9185 if (gtk_tree_view_is_expander_column (tree_view, tmp_column))
9188 x_offset = total_width + gtk_tree_view_column_get_width (tmp_column) - tree_view->priv->expander_size;
9190 x_offset = total_width;
9194 if (gtk_tree_view_column_get_visible (tmp_column))
9195 total_width += gtk_tree_view_column_get_width (tmp_column);
9198 gtk_widget_style_get (GTK_WIDGET (tree_view),
9199 "indent-expanders", &indent_expanders,
9202 if (indent_expanders)
9205 x_offset -= tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree);
9207 x_offset += tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree);
9213 gtk_tree_view_column_get_visible (tmp_column))
9214 /* +1 because x2 isn't included in the range. */
9215 *x2 = *x1 + tree_view->priv->expander_size + 1;
9221 gtk_tree_view_build_tree (GtkTreeView *tree_view,
9227 GtkRBNode *temp = NULL;
9228 GtkTreePath *path = NULL;
9232 gtk_tree_model_ref_node (tree_view->priv->model, iter);
9233 temp = _gtk_rbtree_insert_after (tree, temp, 0, FALSE);
9235 if (tree_view->priv->fixed_height > 0)
9237 if (GTK_RBNODE_FLAG_SET (temp, GTK_RBNODE_INVALID))
9239 _gtk_rbtree_node_set_height (tree, temp, tree_view->priv->fixed_height);
9240 _gtk_rbtree_node_mark_valid (tree, temp);
9244 if (tree_view->priv->is_list)
9252 path = gtk_tree_model_get_path (tree_view->priv->model, iter);
9254 gtk_tree_path_next (path);
9256 if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter))
9260 g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, iter, path, &expand);
9262 if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter)
9265 temp->children = _gtk_rbtree_new ();
9266 temp->children->parent_tree = tree;
9267 temp->children->parent_node = temp;
9268 gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse);
9273 if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter))
9275 if ((temp->flags>K_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT)
9276 temp->flags ^= GTK_RBNODE_IS_PARENT;
9279 while (gtk_tree_model_iter_next (tree_view->priv->model, iter));
9282 gtk_tree_path_free (path);
9285 /* Make sure the node is visible vertically */
9287 gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
9291 gint node_dy, height;
9292 GtkTreePath *path = NULL;
9294 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9297 /* just return if the node is visible, avoiding a costly expose */
9298 node_dy = _gtk_rbtree_node_find_offset (tree, node);
9299 height = gtk_tree_view_get_row_height (tree_view, node);
9300 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID)
9301 && node_dy >= tree_view->priv->vadjustment->value
9302 && node_dy + height <= (tree_view->priv->vadjustment->value
9303 + tree_view->priv->vadjustment->page_size))
9306 path = _gtk_tree_view_find_path (tree_view, tree, node);
9309 /* We process updates because we want to clear old selected items when we scroll.
9310 * if this is removed, we get a "selection streak" at the bottom. */
9311 gtk_tree_view_bin_process_updates (tree_view);
9313 gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
9314 gtk_tree_path_free (path);
9319 gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view,
9320 GtkTreeViewColumn *column,
9321 gboolean focus_to_cell)
9323 GtkAllocation allocation;
9329 gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
9331 width = allocation.width;
9333 if (width > tree_view->priv->hadjustment->page_size)
9335 /* The column is larger than the horizontal page size. If the
9336 * column has cells which can be focussed individually, then we make
9337 * sure the cell which gets focus is fully visible (if even the
9338 * focus cell is bigger than the page size, we make sure the
9339 * left-hand side of the cell is visible).
9341 * If the column does not have an activatable cell, we
9342 * make sure the left-hand side of the column is visible.
9345 if (focus_to_cell && gtk_tree_view_has_can_focus_cell (tree_view))
9347 GtkCellArea *cell_area;
9348 GtkCellRenderer *focus_cell;
9350 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
9351 focus_cell = gtk_cell_area_get_focus_cell (cell_area);
9353 if (gtk_tree_view_column_cell_get_position (column, focus_cell,
9356 if (width < tree_view->priv->hadjustment->page_size)
9358 if (tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size < x + width)
9359 gtk_adjustment_set_value (tree_view->priv->hadjustment,
9360 x + width - tree_view->priv->hadjustment->page_size);
9361 else if (tree_view->priv->hadjustment->value > x)
9362 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9367 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9371 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
9372 gtk_adjustment_set_value (tree_view->priv->hadjustment,
9373 x + width - tree_view->priv->hadjustment->page_size);
9374 else if (tree_view->priv->hadjustment->value > x)
9375 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9379 /* This function could be more efficient. I'll optimize it if profiling seems
9380 * to imply that it is important */
9382 _gtk_tree_view_find_path (GtkTreeView *tree_view,
9387 GtkRBTree *tmp_tree;
9388 GtkRBNode *tmp_node, *last;
9391 path = gtk_tree_path_new ();
9393 g_return_val_if_fail (node != NULL, path);
9394 g_return_val_if_fail (node != tree->nil, path);
9396 count = 1 + node->left->count;
9399 tmp_node = node->parent;
9403 while (tmp_node != tmp_tree->nil)
9405 if (tmp_node->right == last)
9406 count += 1 + tmp_node->left->count;
9408 tmp_node = tmp_node->parent;
9410 gtk_tree_path_prepend_index (path, count - 1);
9411 last = tmp_tree->parent_node;
9412 tmp_tree = tmp_tree->parent_tree;
9415 count = 1 + last->left->count;
9416 tmp_node = last->parent;
9422 /* Returns TRUE if we ran out of tree before finding the path. If the path is
9423 * invalid (ie. points to a node that's not in the tree), *tree and *node are
9427 _gtk_tree_view_find_node (GtkTreeView *tree_view,
9432 GtkRBNode *tmpnode = NULL;
9433 GtkRBTree *tmptree = tree_view->priv->tree;
9434 gint *indices = gtk_tree_path_get_indices (path);
9435 gint depth = gtk_tree_path_get_depth (path);
9441 if (depth == 0 || tmptree == NULL)
9445 tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
9447 if (tmpnode == NULL)
9461 tmptree = tmpnode->children;
9462 if (tmptree == NULL)
9469 gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
9470 GtkTreeViewColumn *column)
9474 if (tree_view->priv->is_list)
9477 if (tree_view->priv->expander_column != NULL)
9479 if (tree_view->priv->expander_column == column)
9485 for (list = tree_view->priv->columns;
9488 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
9490 if (list && list->data == column)
9496 static inline gboolean
9497 gtk_tree_view_draw_expanders (GtkTreeView *tree_view)
9499 if (!tree_view->priv->is_list && tree_view->priv->show_expanders)
9506 gtk_tree_view_add_move_binding (GtkBindingSet *binding_set,
9509 gboolean add_shifted_binding,
9510 GtkMovementStep step,
9514 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
9519 if (add_shifted_binding)
9520 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
9525 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
9528 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
9533 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
9540 gtk_tree_view_unref_tree_helper (GtkTreeModel *model,
9545 gint retval = FALSE;
9548 g_return_val_if_fail (node != NULL, FALSE);
9553 GtkRBTree *new_tree;
9554 GtkRBNode *new_node;
9556 new_tree = node->children;
9557 new_node = new_tree->root;
9559 while (new_node && new_node->left != new_tree->nil)
9560 new_node = new_node->left;
9562 if (!gtk_tree_model_iter_children (model, &child, iter))
9565 retval = retval || gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node);
9568 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
9570 gtk_tree_model_unref_node (model, iter);
9571 node = _gtk_rbtree_next (tree, node);
9573 while (gtk_tree_model_iter_next (model, iter));
9579 gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view,
9591 while (node && node->left != tree->nil)
9594 g_return_val_if_fail (node != NULL, FALSE);
9595 path = _gtk_tree_view_find_path (tree_view, tree, node);
9596 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model),
9598 retval = gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (tree_view->priv->model), &iter, tree, node);
9599 gtk_tree_path_free (path);
9605 gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view,
9606 GtkTreeViewColumn *column)
9608 GtkTreeViewColumn *left_column;
9609 GtkTreeViewColumn *cur_column = NULL;
9610 GtkTreeViewColumnReorder *reorder;
9615 /* We want to precalculate the motion list such that we know what column slots
9619 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9621 /* First, identify all possible drop spots */
9623 tmp_list = g_list_last (tree_view->priv->columns);
9625 tmp_list = g_list_first (tree_view->priv->columns);
9629 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
9630 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
9632 if (gtk_tree_view_column_get_visible (cur_column) == FALSE)
9635 /* If it's not the column moving and func tells us to skip over the column, we continue. */
9636 if (left_column != column && cur_column != column &&
9637 tree_view->priv->column_drop_func &&
9638 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
9640 left_column = cur_column;
9643 reorder = g_slice_new0 (GtkTreeViewColumnReorder);
9644 reorder->left_column = left_column;
9645 left_column = reorder->right_column = cur_column;
9647 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
9650 /* Add the last one */
9651 if (tree_view->priv->column_drop_func == NULL ||
9652 ((left_column != column) &&
9653 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
9655 reorder = g_slice_new0 (GtkTreeViewColumnReorder);
9656 reorder->left_column = left_column;
9657 reorder->right_column = NULL;
9658 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
9661 /* We quickly check to see if it even makes sense to reorder columns. */
9662 /* If there is nothing that can be moved, then we return */
9664 if (tree_view->priv->column_drag_info == NULL)
9667 /* We know there are always 2 slots possbile, as you can always return column. */
9668 /* If that's all there is, return */
9669 if (tree_view->priv->column_drag_info->next == NULL ||
9670 (tree_view->priv->column_drag_info->next->next == NULL &&
9671 ((GtkTreeViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
9672 ((GtkTreeViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
9674 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
9675 g_slice_free (GtkTreeViewColumnReorder, tmp_list->data);
9676 g_list_free (tree_view->priv->column_drag_info);
9677 tree_view->priv->column_drag_info = NULL;
9680 /* We fill in the ranges for the columns, now that we've isolated them */
9681 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
9683 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
9685 reorder = (GtkTreeViewColumnReorder *) tmp_list->data;
9687 reorder->left_align = left;
9688 if (tmp_list->next != NULL)
9690 GtkAllocation right_allocation, left_allocation;
9691 GtkWidget *left_button, *right_button;
9693 g_assert (tmp_list->next->data);
9695 right_button = gtk_tree_view_column_get_button (reorder->right_column);
9696 left_button = gtk_tree_view_column_get_button
9697 (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column);
9699 gtk_widget_get_allocation (right_button, &right_allocation);
9700 gtk_widget_get_allocation (left_button, &left_allocation);
9701 left = reorder->right_align = (right_allocation.x + right_allocation.width + left_allocation.x) / 2;
9705 reorder->right_align = gdk_window_get_width (tree_view->priv->header_window)
9706 + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
9712 _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
9713 GtkTreeViewColumn *column,
9716 GdkEvent *send_event;
9717 GtkAllocation allocation;
9718 GtkAllocation button_allocation;
9719 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
9721 GdkDevice *pointer, *keyboard;
9723 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
9724 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
9726 gtk_tree_view_set_column_drag_info (tree_view, column);
9728 if (tree_view->priv->column_drag_info == NULL)
9731 button = gtk_tree_view_column_get_button (column);
9733 if (tree_view->priv->drag_window == NULL)
9735 GdkWindowAttr attributes;
9736 guint attributes_mask;
9738 gtk_widget_get_allocation (button, &button_allocation);
9740 attributes.window_type = GDK_WINDOW_CHILD;
9741 attributes.wclass = GDK_INPUT_OUTPUT;
9742 attributes.x = button_allocation.x;
9744 attributes.width = button_allocation.width;
9745 attributes.height = button_allocation.height;
9746 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
9747 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
9748 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
9750 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
9753 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
9756 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
9759 pointer = gdk_device_get_associated_device (device);
9764 keyboard = gdk_device_get_associated_device (device);
9767 gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
9768 gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
9770 gtk_grab_remove (button);
9772 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
9773 send_event->crossing.send_event = TRUE;
9774 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (button)));
9775 send_event->crossing.subwindow = NULL;
9776 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
9777 send_event->crossing.time = GDK_CURRENT_TIME;
9778 gdk_event_set_device (send_event, device);
9780 gtk_propagate_event (button, send_event);
9781 gdk_event_free (send_event);
9783 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
9784 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
9785 send_event->button.send_event = TRUE;
9786 send_event->button.time = GDK_CURRENT_TIME;
9787 send_event->button.x = -1;
9788 send_event->button.y = -1;
9789 send_event->button.axes = NULL;
9790 send_event->button.state = 0;
9791 send_event->button.button = 1;
9792 send_event->button.x_root = 0;
9793 send_event->button.y_root = 0;
9794 gdk_event_set_device (send_event, device);
9796 gtk_propagate_event (button, send_event);
9797 gdk_event_free (send_event);
9799 /* Kids, don't try this at home */
9800 g_object_ref (button);
9801 gtk_container_remove (GTK_CONTAINER (tree_view), button);
9802 gtk_widget_set_parent_window (button, tree_view->priv->drag_window);
9803 gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
9804 g_object_unref (button);
9806 gtk_widget_get_allocation (button, &button_allocation);
9807 tree_view->priv->drag_column_x = button_allocation.x;
9808 allocation = button_allocation;
9810 gtk_widget_size_allocate (button, &allocation);
9811 gtk_widget_set_parent_window (button, tree_view->priv->drag_window);
9813 tree_view->priv->drag_column = column;
9814 gdk_window_show (tree_view->priv->drag_window);
9816 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9817 while (gtk_events_pending ())
9818 gtk_main_iteration ();
9820 tree_view->priv->in_column_drag = TRUE;
9822 gdk_device_grab (pointer,
9823 tree_view->priv->drag_window,
9826 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
9829 gdk_device_grab (keyboard,
9830 tree_view->priv->drag_window,
9833 GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK,
9839 gtk_tree_view_queue_draw_arrow (GtkTreeView *tree_view,
9843 GtkAllocation allocation;
9846 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9849 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9851 rect.width = MAX (tree_view->priv->expander_size, MAX (tree_view->priv->width, allocation.width));
9853 rect.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
9854 rect.height = gtk_tree_view_get_row_height (tree_view, node);
9856 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
9860 _gtk_tree_view_queue_draw_node (GtkTreeView *tree_view,
9863 const GdkRectangle *clip_rect)
9865 GtkAllocation allocation;
9868 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9871 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9873 rect.width = MAX (tree_view->priv->width, allocation.width);
9875 rect.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
9876 rect.height = gtk_tree_view_get_row_height (tree_view, node);
9880 GdkRectangle new_rect;
9882 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
9884 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
9888 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
9893 gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view)
9895 if (tree_view->priv->headers_visible)
9896 return tree_view->priv->header_height;
9902 _gtk_tree_view_get_header_height (GtkTreeView *tree_view)
9904 return tree_view->priv->header_height;
9908 _gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view,
9909 GtkTreeViewRowSeparatorFunc *func,
9912 *func = tree_view->priv->row_separator_func;
9913 *data = tree_view->priv->row_separator_data;
9917 _gtk_tree_view_get_anchor_path (GtkTreeView *tree_view)
9919 if (tree_view->priv->anchor)
9920 return gtk_tree_row_reference_get_path (tree_view->priv->anchor);
9926 _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view,
9927 GtkTreePath *anchor_path)
9929 if (tree_view->priv->anchor)
9931 gtk_tree_row_reference_free (tree_view->priv->anchor);
9932 tree_view->priv->anchor = NULL;
9935 if (anchor_path && tree_view->priv->model)
9936 tree_view->priv->anchor =
9937 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9938 tree_view->priv->model, anchor_path);
9942 _gtk_tree_view_get_rbtree (GtkTreeView *tree_view)
9944 return tree_view->priv->tree;
9948 _gtk_tree_view_get_header_window (GtkTreeView *tree_view)
9950 return tree_view->priv->header_window;
9954 _gtk_tree_view_set_focus_column (GtkTreeView *tree_view,
9955 GtkTreeViewColumn *column)
9957 tree_view->priv->focus_column = column;
9962 gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
9964 const GdkRectangle *clip_rect)
9966 GtkRBTree *tree = NULL;
9967 GtkRBNode *node = NULL;
9969 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
9972 _gtk_tree_view_queue_draw_node (tree_view, tree, node, clip_rect);
9975 /* x and y are the mouse position
9978 gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
9982 /* in bin_window coordinates */
9991 gint vertical_separator;
9993 GtkExpanderStyle expander_style;
9995 widget = GTK_WIDGET (tree_view);
9997 gtk_widget_style_get (widget,
9998 "vertical-separator", &vertical_separator,
10000 expander_size = tree_view->priv->expander_size - EXPANDER_EXTRA_PADDING;
10002 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
10005 gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2);
10008 area.y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node,
10009 vertical_separator);
10010 area.width = expander_size + 2;
10011 area.height = gtk_tree_view_get_cell_area_height (tree_view, node,
10012 vertical_separator);
10014 if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
10016 state = GTK_STATE_INSENSITIVE;
10018 else if (node == tree_view->priv->button_pressed_node)
10020 if (x >= area.x && x <= (area.x + area.width) &&
10021 y >= area.y && y <= (area.y + area.height))
10022 state = GTK_STATE_ACTIVE;
10024 state = GTK_STATE_NORMAL;
10028 if (node == tree_view->priv->prelight_node &&
10029 tree_view->priv->arrow_prelit)
10030 state = GTK_STATE_PRELIGHT;
10032 state = GTK_STATE_NORMAL;
10035 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_EXPANDED))
10036 expander_style = GTK_EXPANDER_SEMI_EXPANDED;
10037 else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_COLLAPSED))
10038 expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
10039 else if (node->children != NULL)
10040 expander_style = GTK_EXPANDER_EXPANDED;
10042 expander_style = GTK_EXPANDER_COLLAPSED;
10044 gtk_paint_expander (gtk_widget_get_style (widget),
10049 area.x + area.width / 2,
10050 area.y + area.height / 2,
10055 gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view)
10058 GtkTreePath *cursor_path;
10060 if ((tree_view->priv->tree == NULL) ||
10061 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
10064 cursor_path = NULL;
10065 if (tree_view->priv->cursor)
10066 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10068 if (cursor_path == NULL)
10070 /* Consult the selection before defaulting to the
10071 * first focusable element
10073 GList *selected_rows;
10074 GtkTreeModel *model;
10075 GtkTreeSelection *selection;
10077 selection = gtk_tree_view_get_selection (tree_view);
10078 selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
10082 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
10083 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
10084 g_list_free (selected_rows);
10088 cursor_path = gtk_tree_path_new_first ();
10089 search_first_focusable_path (tree_view, &cursor_path,
10093 gtk_tree_row_reference_free (tree_view->priv->cursor);
10094 tree_view->priv->cursor = NULL;
10098 if (gtk_tree_selection_get_mode (tree_view->priv->selection) == GTK_SELECTION_MULTIPLE)
10099 gtk_tree_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
10101 gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
10107 tree_view->priv->draw_keyfocus = TRUE;
10109 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10110 gtk_tree_path_free (cursor_path);
10112 if (tree_view->priv->focus_column == NULL)
10115 for (list = tree_view->priv->columns; list; list = list->next)
10117 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
10119 GtkCellArea *cell_area;
10121 tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
10123 /* This happens when the treeview initially grabs focus and there
10124 * is no column in focus, here we explicitly focus into the first cell */
10125 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
10126 if (!gtk_cell_area_get_focus_cell (cell_area))
10130 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10131 gtk_cell_area_focus (cell_area,
10132 rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT);
10143 gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
10146 gint selection_count;
10147 GtkRBTree *cursor_tree = NULL;
10148 GtkRBNode *cursor_node = NULL;
10149 GtkRBTree *new_cursor_tree = NULL;
10150 GtkRBNode *new_cursor_node = NULL;
10151 GtkTreePath *cursor_path = NULL;
10152 gboolean grab_focus = TRUE;
10153 gboolean selectable;
10154 GtkDirectionType direction;
10155 GtkCellArea *cell_area = NULL;
10156 GtkCellRenderer *last_focus_cell = NULL;
10159 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10162 cursor_path = NULL;
10163 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
10164 /* FIXME: we lost the cursor; should we get the first? */
10167 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10168 _gtk_tree_view_find_node (tree_view, cursor_path,
10169 &cursor_tree, &cursor_node);
10171 if (cursor_tree == NULL)
10172 /* FIXME: we lost the cursor; should we get the first? */
10175 direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
10177 if (tree_view->priv->focus_column)
10178 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
10180 /* If focus stays in the area for this row, then just return for this round */
10181 if (cell_area && (count == -1 || count == 1) &&
10182 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path))
10184 gtk_tree_view_column_cell_set_cell_data (tree_view->priv->focus_column,
10185 tree_view->priv->model,
10187 GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
10188 cursor_node->children?TRUE:FALSE);
10190 /* Save the last cell that had focus, if we hit the end of the view we'll give
10191 * focus back to it. */
10192 last_focus_cell = gtk_cell_area_get_focus_cell (cell_area);
10194 /* If focus stays in the area, no need to change the cursor row */
10195 if (gtk_cell_area_focus (cell_area, direction))
10199 selection_count = gtk_tree_selection_count_selected_rows (tree_view->priv->selection);
10200 selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
10204 if (selection_count == 0
10205 && gtk_tree_selection_get_mode (tree_view->priv->selection) != GTK_SELECTION_NONE
10206 && !tree_view->priv->ctrl_pressed
10209 /* Don't move the cursor, but just select the current node */
10210 new_cursor_tree = cursor_tree;
10211 new_cursor_node = cursor_node;
10216 _gtk_rbtree_prev_full (cursor_tree, cursor_node,
10217 &new_cursor_tree, &new_cursor_node);
10219 _gtk_rbtree_next_full (cursor_tree, cursor_node,
10220 &new_cursor_tree, &new_cursor_node);
10223 gtk_tree_path_free (cursor_path);
10225 if (new_cursor_node)
10227 cursor_path = _gtk_tree_view_find_path (tree_view,
10228 new_cursor_tree, new_cursor_node);
10230 search_first_focusable_path (tree_view, &cursor_path,
10236 gtk_tree_path_free (cursor_path);
10240 * If the list has only one item and multi-selection is set then select
10241 * the row (if not yet selected).
10243 if (gtk_tree_selection_get_mode (tree_view->priv->selection) == GTK_SELECTION_MULTIPLE &&
10244 new_cursor_node == NULL)
10247 _gtk_rbtree_next_full (cursor_tree, cursor_node,
10248 &new_cursor_tree, &new_cursor_node);
10250 _gtk_rbtree_prev_full (cursor_tree, cursor_node,
10251 &new_cursor_tree, &new_cursor_node);
10253 if (new_cursor_node == NULL
10254 && !GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_SELECTED))
10256 new_cursor_node = cursor_node;
10257 new_cursor_tree = cursor_tree;
10261 new_cursor_node = NULL;
10265 if (new_cursor_node)
10267 cursor_path = _gtk_tree_view_find_path (tree_view, new_cursor_tree, new_cursor_node);
10268 gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
10269 gtk_tree_path_free (cursor_path);
10271 /* Give focus to the area in the new row */
10273 gtk_cell_area_focus (cell_area, direction);
10277 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10279 if (!tree_view->priv->shift_pressed)
10281 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
10283 GTK_DIR_UP : GTK_DIR_DOWN))
10285 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
10288 gtk_widget_child_focus (toplevel,
10290 GTK_DIR_TAB_BACKWARD :
10291 GTK_DIR_TAB_FORWARD);
10293 grab_focus = FALSE;
10298 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10302 gtk_cell_area_set_focus_cell (cell_area, last_focus_cell);
10306 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10310 gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
10313 GtkRBTree *cursor_tree = NULL;
10314 GtkRBNode *cursor_node = NULL;
10315 GtkTreePath *old_cursor_path = NULL;
10316 GtkTreePath *cursor_path = NULL;
10317 GtkRBTree *start_cursor_tree = NULL;
10318 GtkRBNode *start_cursor_node = NULL;
10321 gint vertical_separator;
10323 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10326 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10327 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10329 /* This is sorta weird. Focus in should give us a cursor */
10332 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
10333 _gtk_tree_view_find_node (tree_view, old_cursor_path,
10334 &cursor_tree, &cursor_node);
10336 if (cursor_tree == NULL)
10338 /* FIXME: we lost the cursor. Should we try to get one? */
10339 gtk_tree_path_free (old_cursor_path);
10342 g_return_if_fail (cursor_node != NULL);
10344 y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
10345 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
10346 y += tree_view->priv->cursor_offset;
10347 y += count * (int)tree_view->priv->vadjustment->page_increment;
10348 y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower, (gint)tree_view->priv->vadjustment->upper - vertical_separator);
10350 if (y >= tree_view->priv->height)
10351 y = tree_view->priv->height - 1;
10353 tree_view->priv->cursor_offset =
10354 _gtk_rbtree_find_offset (tree_view->priv->tree, y,
10355 &cursor_tree, &cursor_node);
10357 if (cursor_tree == NULL)
10359 /* FIXME: we lost the cursor. Should we try to get one? */
10360 gtk_tree_path_free (old_cursor_path);
10364 if (tree_view->priv->cursor_offset
10365 > gtk_tree_view_get_row_height (tree_view, cursor_node))
10367 _gtk_rbtree_next_full (cursor_tree, cursor_node,
10368 &cursor_tree, &cursor_node);
10369 tree_view->priv->cursor_offset -= gtk_tree_view_get_row_height (tree_view, cursor_node);
10372 y -= tree_view->priv->cursor_offset;
10373 cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
10375 start_cursor_tree = cursor_tree;
10376 start_cursor_node = cursor_node;
10378 if (! search_first_focusable_path (tree_view, &cursor_path,
10380 &cursor_tree, &cursor_node))
10382 /* It looks like we reached the end of the view without finding
10383 * a focusable row. We will step backwards to find the last
10386 cursor_tree = start_cursor_tree;
10387 cursor_node = start_cursor_node;
10388 cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
10390 search_first_focusable_path (tree_view, &cursor_path,
10392 &cursor_tree, &cursor_node);
10399 y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
10401 gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
10404 gtk_tree_view_scroll_to_point (tree_view, -1, y);
10405 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10406 _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
10408 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
10409 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10411 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10414 gtk_tree_path_free (old_cursor_path);
10415 gtk_tree_path_free (cursor_path);
10419 gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
10422 GtkRBTree *cursor_tree = NULL;
10423 GtkRBNode *cursor_node = NULL;
10424 GtkTreePath *cursor_path = NULL;
10425 GtkTreeViewColumn *column;
10428 gboolean found_column = FALSE;
10430 GtkDirectionType direction;
10431 GtkCellArea *cell_area;
10432 GtkCellRenderer *last_focus_cell = NULL;
10433 GtkCellArea *last_focus_area = NULL;
10435 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10437 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10440 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10441 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10445 _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node);
10446 if (cursor_tree == NULL)
10448 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
10450 gtk_tree_path_free (cursor_path);
10453 gtk_tree_path_free (cursor_path);
10455 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
10456 if (tree_view->priv->focus_column)
10458 /* Save the cell/area we are moving focus from, if moving the cursor
10459 * by one step hits the end we'll set focus back here */
10460 last_focus_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
10461 last_focus_cell = gtk_cell_area_get_focus_cell (last_focus_area);
10463 for (; list; list = (rtl ? list->prev : list->next))
10465 if (list->data == tree_view->priv->focus_column)
10470 direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
10474 column = list->data;
10475 if (gtk_tree_view_column_get_visible (column) == FALSE)
10478 gtk_tree_view_column_cell_set_cell_data (column,
10479 tree_view->priv->model,
10481 GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
10482 cursor_node->children?TRUE:FALSE);
10484 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
10485 if (gtk_cell_area_focus (cell_area, direction))
10487 tree_view->priv->focus_column = column;
10488 found_column = TRUE;
10494 list = rtl ? list->prev : list->next;
10496 list = rtl ? list->next : list->prev;
10501 if (!gtk_tree_view_has_can_focus_cell (tree_view))
10502 _gtk_tree_view_queue_draw_node (tree_view,
10506 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10507 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10511 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10513 if (last_focus_area)
10514 gtk_cell_area_set_focus_cell (last_focus_area, last_focus_cell);
10517 gtk_tree_view_clamp_column_visible (tree_view,
10518 tree_view->priv->focus_column, TRUE);
10522 gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
10525 GtkRBTree *cursor_tree;
10526 GtkRBNode *cursor_node;
10528 GtkTreePath *old_path;
10530 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10533 g_return_if_fail (tree_view->priv->tree != NULL);
10535 gtk_tree_view_get_cursor (tree_view, &old_path, NULL);
10537 cursor_tree = tree_view->priv->tree;
10538 cursor_node = cursor_tree->root;
10542 while (cursor_node && cursor_node->left != cursor_tree->nil)
10543 cursor_node = cursor_node->left;
10545 /* Now go forward to find the first focusable row. */
10546 path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
10547 search_first_focusable_path (tree_view, &path,
10548 TRUE, &cursor_tree, &cursor_node);
10554 while (cursor_node && cursor_node->right != cursor_tree->nil)
10555 cursor_node = cursor_node->right;
10556 if (cursor_node->children == NULL)
10559 cursor_tree = cursor_node->children;
10560 cursor_node = cursor_tree->root;
10564 /* Now go backwards to find last focusable row. */
10565 path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
10566 search_first_focusable_path (tree_view, &path,
10567 FALSE, &cursor_tree, &cursor_node);
10573 if (gtk_tree_path_compare (old_path, path))
10575 gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
10576 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10580 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10584 gtk_tree_path_free (old_path);
10585 gtk_tree_path_free (path);
10589 gtk_tree_view_real_select_all (GtkTreeView *tree_view)
10591 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10594 if (gtk_tree_selection_get_mode (tree_view->priv->selection) != GTK_SELECTION_MULTIPLE)
10597 gtk_tree_selection_select_all (tree_view->priv->selection);
10603 gtk_tree_view_real_unselect_all (GtkTreeView *tree_view)
10605 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10608 if (gtk_tree_selection_get_mode (tree_view->priv->selection) != GTK_SELECTION_MULTIPLE)
10611 gtk_tree_selection_unselect_all (tree_view->priv->selection);
10617 gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
10618 gboolean start_editing)
10620 GtkRBTree *new_tree = NULL;
10621 GtkRBNode *new_node = NULL;
10622 GtkRBTree *cursor_tree = NULL;
10623 GtkRBNode *cursor_node = NULL;
10624 GtkTreePath *cursor_path = NULL;
10625 GtkTreeSelectMode mode = 0;
10627 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10630 if (tree_view->priv->cursor)
10631 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10633 if (cursor_path == NULL)
10636 _gtk_tree_view_find_node (tree_view, cursor_path,
10637 &cursor_tree, &cursor_node);
10639 if (cursor_tree == NULL)
10641 gtk_tree_path_free (cursor_path);
10645 if (!tree_view->priv->shift_pressed && start_editing &&
10646 tree_view->priv->focus_column)
10648 if (gtk_tree_view_start_editing (tree_view, cursor_path, FALSE))
10650 gtk_tree_path_free (cursor_path);
10655 if (tree_view->priv->ctrl_pressed)
10656 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
10657 if (tree_view->priv->shift_pressed)
10658 mode |= GTK_TREE_SELECT_MODE_EXTEND;
10660 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
10667 /* We bail out if the original (tree, node) don't exist anymore after
10668 * handling the selection-changed callback. We do return TRUE because
10669 * the key press has been handled at this point.
10671 _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
10673 if (cursor_tree != new_tree || cursor_node != new_node)
10676 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10678 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10679 _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
10681 if (!tree_view->priv->shift_pressed)
10682 gtk_tree_view_row_activated (tree_view, cursor_path,
10683 tree_view->priv->focus_column);
10685 gtk_tree_path_free (cursor_path);
10691 gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view)
10693 GtkRBTree *new_tree = NULL;
10694 GtkRBNode *new_node = NULL;
10695 GtkRBTree *cursor_tree = NULL;
10696 GtkRBNode *cursor_node = NULL;
10697 GtkTreePath *cursor_path = NULL;
10699 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10702 cursor_path = NULL;
10703 if (tree_view->priv->cursor)
10704 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10706 if (cursor_path == NULL)
10709 _gtk_tree_view_find_node (tree_view, cursor_path,
10710 &cursor_tree, &cursor_node);
10711 if (cursor_tree == NULL)
10713 gtk_tree_path_free (cursor_path);
10717 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
10721 GTK_TREE_SELECT_MODE_TOGGLE,
10724 /* We bail out if the original (tree, node) don't exist anymore after
10725 * handling the selection-changed callback. We do return TRUE because
10726 * the key press has been handled at this point.
10728 _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
10730 if (cursor_tree != new_tree || cursor_node != new_node)
10733 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10735 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10736 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10737 gtk_tree_path_free (cursor_path);
10743 gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view,
10748 GtkTreePath *cursor_path = NULL;
10752 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10755 cursor_path = NULL;
10756 if (tree_view->priv->cursor)
10757 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10759 if (cursor_path == NULL)
10762 if (_gtk_tree_view_find_node (tree_view, cursor_path, &tree, &node))
10765 /* Don't handle the event if we aren't an expander */
10766 if (!((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT))
10770 && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
10774 gtk_tree_view_real_expand_row (tree_view, cursor_path, tree, node, open_all, TRUE);
10776 gtk_tree_view_real_collapse_row (tree_view, cursor_path, tree, node, TRUE);
10778 gtk_tree_path_free (cursor_path);
10784 gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
10786 GtkRBTree *cursor_tree = NULL;
10787 GtkRBNode *cursor_node = NULL;
10788 GtkTreePath *cursor_path = NULL;
10789 GdkModifierType state;
10791 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10794 cursor_path = NULL;
10795 if (tree_view->priv->cursor)
10796 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10798 if (cursor_path == NULL)
10801 _gtk_tree_view_find_node (tree_view, cursor_path,
10802 &cursor_tree, &cursor_node);
10803 if (cursor_tree == NULL)
10805 gtk_tree_path_free (cursor_path);
10809 if (cursor_tree->parent_node)
10811 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10812 cursor_node = cursor_tree->parent_node;
10813 cursor_tree = cursor_tree->parent_tree;
10815 gtk_tree_path_up (cursor_path);
10817 if (gtk_get_current_event_state (&state))
10819 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
10820 tree_view->priv->ctrl_pressed = TRUE;
10823 gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
10824 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10826 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10827 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10828 gtk_tree_path_free (cursor_path);
10830 tree_view->priv->ctrl_pressed = FALSE;
10837 tree_view->priv->search_entry_avoid_unhandled_binding = TRUE;
10842 gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
10844 gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view, NULL);
10845 tree_view->priv->typeselect_flush_timeout = 0;
10850 /* Cut and paste from gtkwindow.c */
10852 send_focus_change (GtkWidget *widget,
10856 GdkDeviceManager *device_manager;
10857 GList *devices, *d;
10859 device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
10860 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
10861 devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
10862 devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
10864 for (d = devices; d; d = d->next)
10866 GdkDevice *dev = d->data;
10870 if (gdk_device_get_source (dev) != GDK_SOURCE_KEYBOARD)
10873 window = gtk_widget_get_window (widget);
10875 /* Skip non-master keyboards that haven't
10876 * selected for events from this window
10878 if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
10879 !gdk_window_get_device_events (window, dev))
10882 fevent = gdk_event_new (GDK_FOCUS_CHANGE);
10884 fevent->focus_change.type = GDK_FOCUS_CHANGE;
10885 fevent->focus_change.window = g_object_ref (window);
10886 fevent->focus_change.in = in;
10887 gdk_event_set_device (fevent, device);
10889 gtk_widget_send_focus_change (widget, fevent);
10891 gdk_event_free (fevent);
10896 gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
10898 GtkWidget *frame, *vbox, *toplevel;
10901 if (tree_view->priv->search_custom_entry_set)
10904 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
10905 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
10907 if (tree_view->priv->search_window != NULL)
10909 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
10910 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
10911 GTK_WINDOW (tree_view->priv->search_window));
10912 else if (gtk_window_has_group (GTK_WINDOW (tree_view->priv->search_window)))
10913 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
10914 GTK_WINDOW (tree_view->priv->search_window));
10916 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
10921 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
10922 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
10924 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
10925 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
10926 GTK_WINDOW (tree_view->priv->search_window));
10928 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
10929 GDK_WINDOW_TYPE_HINT_UTILITY);
10930 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
10931 g_signal_connect (tree_view->priv->search_window, "delete-event",
10932 G_CALLBACK (gtk_tree_view_search_delete_event),
10934 g_signal_connect (tree_view->priv->search_window, "key-press-event",
10935 G_CALLBACK (gtk_tree_view_search_key_press_event),
10937 g_signal_connect (tree_view->priv->search_window, "button-press-event",
10938 G_CALLBACK (gtk_tree_view_search_button_press_event),
10940 g_signal_connect (tree_view->priv->search_window, "scroll-event",
10941 G_CALLBACK (gtk_tree_view_search_scroll_event),
10944 frame = gtk_frame_new (NULL);
10945 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
10946 gtk_widget_show (frame);
10947 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
10949 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
10950 gtk_widget_show (vbox);
10951 gtk_container_add (GTK_CONTAINER (frame), vbox);
10952 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
10955 tree_view->priv->search_entry = gtk_entry_new ();
10956 gtk_widget_show (tree_view->priv->search_entry);
10957 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
10958 G_CALLBACK (gtk_tree_view_search_disable_popdown),
10960 g_signal_connect (tree_view->priv->search_entry,
10961 "activate", G_CALLBACK (gtk_tree_view_search_activate),
10964 g_signal_connect (_gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry)),
10966 G_CALLBACK (gtk_tree_view_search_preedit_changed),
10969 gtk_container_add (GTK_CONTAINER (vbox),
10970 tree_view->priv->search_entry);
10972 gtk_widget_realize (tree_view->priv->search_entry);
10975 /* Pops up the interactive search entry. If keybinding is TRUE then the user
10976 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
10979 gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
10981 gboolean keybinding)
10983 /* We only start interactive search if we have focus or the columns
10984 * have focus. If one of our children have focus, we don't want to
10985 * start the search.
10988 gboolean found_focus = FALSE;
10989 GtkWidgetClass *entry_parent_class;
10991 if (!tree_view->priv->enable_search && !keybinding)
10994 if (tree_view->priv->search_custom_entry_set)
10997 if (tree_view->priv->search_window != NULL &&
10998 gtk_widget_get_visible (tree_view->priv->search_window))
11001 for (list = tree_view->priv->columns; list; list = list->next)
11003 GtkTreeViewColumn *column;
11006 column = list->data;
11007 if (!gtk_tree_view_column_get_visible (column))
11010 button = gtk_tree_view_column_get_button (column);
11011 if (gtk_widget_has_focus (button))
11013 found_focus = TRUE;
11018 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
11019 found_focus = TRUE;
11024 if (tree_view->priv->search_column < 0)
11027 gtk_tree_view_ensure_interactive_directory (tree_view);
11030 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11032 /* done, show it */
11033 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
11034 gtk_widget_show (tree_view->priv->search_window);
11035 if (tree_view->priv->search_entry_changed_id == 0)
11037 tree_view->priv->search_entry_changed_id =
11038 g_signal_connect (tree_view->priv->search_entry, "changed",
11039 G_CALLBACK (gtk_tree_view_search_init),
11043 tree_view->priv->typeselect_flush_timeout =
11044 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
11045 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
11048 /* Grab focus will select all the text. We don't want that to happen, so we
11049 * call the parent instance and bypass the selection change. This is probably
11050 * really non-kosher. */
11051 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
11052 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
11054 /* send focus-in event */
11055 send_focus_change (tree_view->priv->search_entry, device, TRUE);
11057 /* search first matching iter */
11058 gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
11064 gtk_tree_view_start_interactive_search (GtkTreeView *tree_view)
11066 return gtk_tree_view_real_start_interactive_search (tree_view,
11067 gtk_get_current_event_device (),
11071 /* this function returns the new width of the column being resized given
11072 * the column and x position of the cursor; the x cursor position is passed
11073 * in as a pointer and automagicly corrected if it's beyond min/max limits
11076 gtk_tree_view_new_column_width (GtkTreeView *tree_view,
11080 GtkAllocation allocation;
11081 GtkTreeViewColumn *column;
11082 GtkRequisition button_req;
11083 gint max_width, min_width;
11087 /* first translate the x position from widget->window
11088 * to clist->clist_window
11090 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
11091 column = g_list_nth (tree_view->priv->columns, i)->data;
11092 gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
11093 width = rtl ? (allocation.x + allocation.width - *x) : (*x - allocation.x);
11095 /* Clamp down the value */
11096 min_width = gtk_tree_view_column_get_min_width (column);
11097 if (min_width == -1)
11099 gtk_widget_get_preferred_size (gtk_tree_view_column_get_button (column), &button_req, NULL);
11100 width = MAX (button_req.width, width);
11103 width = MAX (min_width, width);
11105 max_width = gtk_tree_view_column_get_max_width (column);
11106 if (max_width != -1)
11107 width = MIN (width, max_width);
11109 *x = rtl ? (allocation.x + allocation.width - width) : (allocation.x + width);
11115 /* FIXME this adjust_allocation is a big cut-and-paste from
11116 * GtkCList, needs to be some "official" way to do this
11126 /* The window to which widget->window is relative */
11127 #define ALLOCATION_WINDOW(widget) \
11128 (!gtk_widget_get_has_window (widget) ? \
11129 gtk_widget_get_window (widget) : \
11130 gdk_window_get_parent (gtk_widget_get_window (widget)))
11133 adjust_allocation_recurse (GtkWidget *widget,
11136 GtkAllocation allocation;
11137 ScrollData *scroll_data = data;
11139 /* Need to really size allocate instead of just poking
11140 * into widget->allocation if the widget is not realized.
11141 * FIXME someone figure out why this was.
11143 gtk_widget_get_allocation (widget, &allocation);
11144 if (!gtk_widget_get_realized (widget))
11146 if (gtk_widget_get_visible (widget))
11148 GdkRectangle tmp_rectangle = allocation;
11149 tmp_rectangle.x += scroll_data->dx;
11150 tmp_rectangle.y += scroll_data->dy;
11152 gtk_widget_size_allocate (widget, &tmp_rectangle);
11157 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
11159 allocation.x += scroll_data->dx;
11160 allocation.y += scroll_data->dy;
11161 gtk_widget_set_allocation (widget, &allocation);
11163 if (GTK_IS_CONTAINER (widget))
11164 gtk_container_forall (GTK_CONTAINER (widget),
11165 adjust_allocation_recurse,
11172 adjust_allocation (GtkWidget *widget,
11176 ScrollData scroll_data;
11178 if (gtk_widget_get_realized (widget))
11179 scroll_data.window = ALLOCATION_WINDOW (widget);
11181 scroll_data.window = NULL;
11183 scroll_data.dx = dx;
11184 scroll_data.dy = dy;
11186 adjust_allocation_recurse (widget, &scroll_data);
11191 gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
11192 GtkTreeView *tree_view)
11194 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11198 gdk_window_move (tree_view->priv->bin_window,
11199 - tree_view->priv->hadjustment->value,
11200 gtk_tree_view_get_effective_header_height (tree_view));
11201 gdk_window_move (tree_view->priv->header_window,
11202 - tree_view->priv->hadjustment->value,
11204 dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
11207 update_prelight (tree_view,
11208 tree_view->priv->event_last_x,
11209 tree_view->priv->event_last_y - dy);
11211 if (tree_view->priv->edited_column)
11214 GtkTreeViewChild *child = NULL;
11215 GtkCellEditable *edit_widget;
11218 area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column));
11219 edit_widget = gtk_cell_area_get_edit_widget (area);
11220 if (GTK_IS_WIDGET (edit_widget))
11222 adjust_allocation (GTK_WIDGET (edit_widget), 0, dy);
11224 for (list = tree_view->priv->children; list; list = list->next)
11226 child = (GtkTreeViewChild *)list->data;
11227 if (child->widget == GTK_WIDGET (edit_widget))
11236 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
11238 if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
11240 /* update our dy and top_row */
11241 tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
11243 if (!tree_view->priv->in_top_row_to_dy)
11244 gtk_tree_view_dy_to_top_row (tree_view);
11247 gdk_window_process_updates (tree_view->priv->header_window, TRUE);
11248 gtk_tree_view_bin_process_updates (tree_view);
11258 * gtk_tree_view_new:
11260 * Creates a new #GtkTreeView widget.
11262 * Return value: A newly created #GtkTreeView widget.
11265 gtk_tree_view_new (void)
11267 return g_object_new (GTK_TYPE_TREE_VIEW, NULL);
11271 * gtk_tree_view_new_with_model:
11272 * @model: the model.
11274 * Creates a new #GtkTreeView widget with the model initialized to @model.
11276 * Return value: A newly created #GtkTreeView widget.
11279 gtk_tree_view_new_with_model (GtkTreeModel *model)
11281 return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL);
11284 /* Public Accessors
11288 * gtk_tree_view_get_model:
11289 * @tree_view: a #GtkTreeView
11291 * Returns the model the #GtkTreeView is based on. Returns %NULL if the
11294 * Return value: (transfer none): A #GtkTreeModel, or %NULL if none is currently being used.
11297 gtk_tree_view_get_model (GtkTreeView *tree_view)
11299 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11301 return tree_view->priv->model;
11305 * gtk_tree_view_set_model:
11306 * @tree_view: A #GtkTreeNode.
11307 * @model: (allow-none): The model.
11309 * Sets the model for a #GtkTreeView. If the @tree_view already has a model
11310 * set, it will remove it before setting the new model. If @model is %NULL,
11311 * then it will unset the old model.
11314 gtk_tree_view_set_model (GtkTreeView *tree_view,
11315 GtkTreeModel *model)
11317 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11318 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
11320 if (model == tree_view->priv->model)
11323 if (tree_view->priv->scroll_to_path)
11325 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
11326 tree_view->priv->scroll_to_path = NULL;
11329 if (tree_view->priv->model)
11331 GList *tmplist = tree_view->priv->columns;
11333 gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree);
11334 gtk_tree_view_stop_editing (tree_view, TRUE);
11336 remove_expand_collapse_timeout (tree_view);
11338 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11339 gtk_tree_view_row_changed,
11341 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11342 gtk_tree_view_row_inserted,
11344 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11345 gtk_tree_view_row_has_child_toggled,
11347 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11348 gtk_tree_view_row_deleted,
11350 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11351 gtk_tree_view_rows_reordered,
11354 for (; tmplist; tmplist = tmplist->next)
11355 _gtk_tree_view_column_unset_model (tmplist->data,
11356 tree_view->priv->model);
11358 if (tree_view->priv->tree)
11359 gtk_tree_view_free_rbtree (tree_view);
11361 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11362 tree_view->priv->drag_dest_row = NULL;
11363 gtk_tree_row_reference_free (tree_view->priv->cursor);
11364 tree_view->priv->cursor = NULL;
11365 gtk_tree_row_reference_free (tree_view->priv->anchor);
11366 tree_view->priv->anchor = NULL;
11367 gtk_tree_row_reference_free (tree_view->priv->top_row);
11368 tree_view->priv->top_row = NULL;
11369 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
11370 tree_view->priv->scroll_to_path = NULL;
11372 tree_view->priv->scroll_to_column = NULL;
11374 g_object_unref (tree_view->priv->model);
11376 tree_view->priv->search_column = -1;
11377 tree_view->priv->fixed_height_check = 0;
11378 tree_view->priv->fixed_height = -1;
11379 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
11380 tree_view->priv->last_button_x = -1;
11381 tree_view->priv->last_button_y = -1;
11384 tree_view->priv->model = model;
11386 if (tree_view->priv->model)
11391 GtkTreeModelFlags flags;
11393 if (tree_view->priv->search_column == -1)
11395 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
11397 GType type = gtk_tree_model_get_column_type (model, i);
11399 if (g_value_type_transformable (type, G_TYPE_STRING))
11401 tree_view->priv->search_column = i;
11407 g_object_ref (tree_view->priv->model);
11408 g_signal_connect (tree_view->priv->model,
11410 G_CALLBACK (gtk_tree_view_row_changed),
11412 g_signal_connect (tree_view->priv->model,
11414 G_CALLBACK (gtk_tree_view_row_inserted),
11416 g_signal_connect (tree_view->priv->model,
11417 "row-has-child-toggled",
11418 G_CALLBACK (gtk_tree_view_row_has_child_toggled),
11420 g_signal_connect (tree_view->priv->model,
11422 G_CALLBACK (gtk_tree_view_row_deleted),
11424 g_signal_connect (tree_view->priv->model,
11426 G_CALLBACK (gtk_tree_view_rows_reordered),
11429 flags = gtk_tree_model_get_flags (tree_view->priv->model);
11430 if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
11431 tree_view->priv->is_list = TRUE;
11433 tree_view->priv->is_list = FALSE;
11435 path = gtk_tree_path_new_first ();
11436 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path))
11438 tree_view->priv->tree = _gtk_rbtree_new ();
11439 gtk_tree_view_build_tree (tree_view, tree_view->priv->tree, &iter, 1, FALSE);
11441 gtk_tree_path_free (path);
11443 /* FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */
11444 install_presize_handler (tree_view);
11447 g_object_notify (G_OBJECT (tree_view), "model");
11449 if (tree_view->priv->selection)
11450 _gtk_tree_selection_emit_changed (tree_view->priv->selection);
11452 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11453 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11457 * gtk_tree_view_get_selection:
11458 * @tree_view: A #GtkTreeView.
11460 * Gets the #GtkTreeSelection associated with @tree_view.
11462 * Return value: (transfer none): A #GtkTreeSelection object.
11465 gtk_tree_view_get_selection (GtkTreeView *tree_view)
11467 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11469 return tree_view->priv->selection;
11473 * gtk_tree_view_get_hadjustment:
11474 * @tree_view: A #GtkTreeView
11476 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
11478 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
11479 * if none is currently being used.
11481 * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
11484 gtk_tree_view_get_hadjustment (GtkTreeView *tree_view)
11486 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11488 return tree_view->priv->hadjustment;
11492 * gtk_tree_view_set_hadjustment:
11493 * @tree_view: A #GtkTreeView
11494 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
11496 * Sets the #GtkAdjustment for the current horizontal aspect.
11498 * Deprecated: 3.0: Use gtk_scrollable_set_hadjustment()
11501 gtk_tree_view_set_hadjustment (GtkTreeView *tree_view,
11502 GtkAdjustment *adjustment)
11504 GtkTreeViewPrivate *priv = tree_view->priv;
11506 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11507 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
11509 if (adjustment && priv->hadjustment == adjustment)
11512 if (priv->hadjustment != NULL)
11514 g_signal_handlers_disconnect_by_func (priv->hadjustment,
11515 gtk_tree_view_adjustment_changed,
11517 g_object_unref (priv->hadjustment);
11520 if (adjustment == NULL)
11521 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
11524 g_signal_connect (adjustment, "value-changed",
11525 G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view);
11526 priv->hadjustment = g_object_ref_sink (adjustment);
11527 /* FIXME: Adjustment should probably be populated here with fresh values, but
11528 * internal details are too complicated for me to decipher right now.
11530 gtk_tree_view_adjustment_changed (NULL, tree_view);
11532 g_object_notify (G_OBJECT (tree_view), "hadjustment");
11536 * gtk_tree_view_get_vadjustment:
11537 * @tree_view: A #GtkTreeView
11539 * Gets the #GtkAdjustment currently being used for the vertical aspect.
11541 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
11542 * if none is currently being used.
11544 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
11547 gtk_tree_view_get_vadjustment (GtkTreeView *tree_view)
11549 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11551 return tree_view->priv->vadjustment;
11555 * gtk_tree_view_set_vadjustment:
11556 * @tree_view: A #GtkTreeView
11557 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
11559 * Sets the #GtkAdjustment for the current vertical aspect.
11561 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
11564 gtk_tree_view_set_vadjustment (GtkTreeView *tree_view,
11565 GtkAdjustment *adjustment)
11567 GtkTreeViewPrivate *priv = tree_view->priv;
11569 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11570 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
11572 if (adjustment && priv->vadjustment == adjustment)
11575 if (priv->vadjustment != NULL)
11577 g_signal_handlers_disconnect_by_func (priv->vadjustment,
11578 gtk_tree_view_adjustment_changed,
11580 g_object_unref (priv->vadjustment);
11583 if (adjustment == NULL)
11584 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
11587 g_signal_connect (adjustment, "value-changed",
11588 G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view);
11589 priv->vadjustment = g_object_ref_sink (adjustment);
11590 /* FIXME: Adjustment should probably be populated here with fresh values, but
11591 * internal details are too complicated for me to decipher right now.
11593 gtk_tree_view_adjustment_changed (NULL, tree_view);
11594 g_object_notify (G_OBJECT (tree_view), "vadjustment");
11597 /* Column and header operations */
11600 * gtk_tree_view_get_headers_visible:
11601 * @tree_view: A #GtkTreeView.
11603 * Returns %TRUE if the headers on the @tree_view are visible.
11605 * Return value: Whether the headers are visible or not.
11608 gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
11610 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11612 return tree_view->priv->headers_visible;
11616 * gtk_tree_view_set_headers_visible:
11617 * @tree_view: A #GtkTreeView.
11618 * @headers_visible: %TRUE if the headers are visible
11620 * Sets the visibility state of the headers.
11623 gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
11624 gboolean headers_visible)
11628 GtkTreeViewColumn *column;
11629 GtkAllocation allocation;
11632 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11634 headers_visible = !! headers_visible;
11636 if (tree_view->priv->headers_visible == headers_visible)
11639 tree_view->priv->headers_visible = headers_visible == TRUE;
11641 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11643 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
11644 if (headers_visible)
11646 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
11647 gdk_window_move_resize (tree_view->priv->bin_window,
11648 x, y + gtk_tree_view_get_effective_header_height (tree_view),
11649 tree_view->priv->width, allocation.height - + gtk_tree_view_get_effective_header_height (tree_view));
11651 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
11652 gtk_tree_view_map_buttons (tree_view);
11656 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
11658 for (list = tree_view->priv->columns; list; list = list->next)
11660 column = list->data;
11661 button = gtk_tree_view_column_get_button (column);
11663 gtk_widget_hide (button);
11664 gtk_widget_unmap (button);
11666 gdk_window_hide (tree_view->priv->header_window);
11670 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
11671 tree_view->priv->vadjustment->page_size = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
11672 tree_view->priv->vadjustment->page_increment = (allocation.height - gtk_tree_view_get_effective_header_height (tree_view)) / 2;
11673 tree_view->priv->vadjustment->lower = 0;
11674 tree_view->priv->vadjustment->upper = tree_view->priv->height;
11675 gtk_adjustment_changed (tree_view->priv->vadjustment);
11677 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11679 g_object_notify (G_OBJECT (tree_view), "headers-visible");
11683 * gtk_tree_view_columns_autosize:
11684 * @tree_view: A #GtkTreeView.
11686 * Resizes all columns to their optimal width. Only works after the
11687 * treeview has been realized.
11690 gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
11692 gboolean dirty = FALSE;
11694 GtkTreeViewColumn *column;
11696 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11698 for (list = tree_view->priv->columns; list; list = list->next)
11700 column = list->data;
11701 if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
11703 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
11708 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11712 * gtk_tree_view_set_headers_clickable:
11713 * @tree_view: A #GtkTreeView.
11714 * @setting: %TRUE if the columns are clickable.
11716 * Allow the column title buttons to be clicked.
11719 gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view,
11724 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11726 for (list = tree_view->priv->columns; list; list = list->next)
11727 gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting);
11729 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
11734 * gtk_tree_view_get_headers_clickable:
11735 * @tree_view: A #GtkTreeView.
11737 * Returns whether all header columns are clickable.
11739 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
11744 gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view)
11748 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11750 for (list = tree_view->priv->columns; list; list = list->next)
11751 if (!gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)))
11758 * gtk_tree_view_set_rules_hint
11759 * @tree_view: a #GtkTreeView
11760 * @setting: %TRUE if the tree requires reading across rows
11762 * This function tells GTK+ that the user interface for your
11763 * application requires users to read across tree rows and associate
11764 * cells with one another. By default, GTK+ will then render the tree
11765 * with alternating row colors. Do <emphasis>not</emphasis> use it
11766 * just because you prefer the appearance of the ruled tree; that's a
11767 * question for the theme. Some themes will draw tree rows in
11768 * alternating colors even when rules are turned off, and users who
11769 * prefer that appearance all the time can choose those themes. You
11770 * should call this function only as a <emphasis>semantic</emphasis>
11771 * hint to the theme engine that your tree makes alternating colors
11772 * useful from a functional standpoint (since it has lots of columns,
11777 gtk_tree_view_set_rules_hint (GtkTreeView *tree_view,
11780 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11782 setting = setting != FALSE;
11784 if (tree_view->priv->has_rules != setting)
11786 tree_view->priv->has_rules = setting;
11787 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
11790 g_object_notify (G_OBJECT (tree_view), "rules-hint");
11794 * gtk_tree_view_get_rules_hint
11795 * @tree_view: a #GtkTreeView
11797 * Gets the setting set by gtk_tree_view_set_rules_hint().
11799 * Return value: %TRUE if rules are useful for the user of this tree
11802 gtk_tree_view_get_rules_hint (GtkTreeView *tree_view)
11804 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11806 return tree_view->priv->has_rules;
11809 /* Public Column functions
11813 * gtk_tree_view_append_column:
11814 * @tree_view: A #GtkTreeView.
11815 * @column: The #GtkTreeViewColumn to add.
11817 * Appends @column to the list of columns. If @tree_view has "fixed_height"
11818 * mode enabled, then @column must have its "sizing" property set to be
11819 * GTK_TREE_VIEW_COLUMN_FIXED.
11821 * Return value: The number of columns in @tree_view after appending.
11824 gtk_tree_view_append_column (GtkTreeView *tree_view,
11825 GtkTreeViewColumn *column)
11827 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11828 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
11829 g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
11831 return gtk_tree_view_insert_column (tree_view, column, -1);
11836 * gtk_tree_view_remove_column:
11837 * @tree_view: A #GtkTreeView.
11838 * @column: The #GtkTreeViewColumn to remove.
11840 * Removes @column from @tree_view.
11842 * Return value: The number of columns in @tree_view after removing.
11845 gtk_tree_view_remove_column (GtkTreeView *tree_view,
11846 GtkTreeViewColumn *column)
11848 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11849 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
11850 g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view), -1);
11852 if (tree_view->priv->focus_column == column)
11853 tree_view->priv->focus_column = NULL;
11855 if (tree_view->priv->edited_column == column)
11857 gtk_tree_view_stop_editing (tree_view, TRUE);
11859 /* no need to, but just to be sure ... */
11860 tree_view->priv->edited_column = NULL;
11863 if (tree_view->priv->expander_column == column)
11864 tree_view->priv->expander_column = NULL;
11866 g_signal_handlers_disconnect_by_func (column,
11867 G_CALLBACK (column_sizing_notify),
11870 _gtk_tree_view_column_unset_tree_view (column);
11872 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
11873 tree_view->priv->n_columns--;
11875 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11879 _gtk_tree_view_column_unrealize_button (column);
11880 for (list = tree_view->priv->columns; list; list = list->next)
11882 GtkTreeViewColumn *tmp_column;
11884 tmp_column = GTK_TREE_VIEW_COLUMN (list->data);
11885 if (gtk_tree_view_column_get_visible (tmp_column))
11886 _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE);
11889 if (tree_view->priv->n_columns == 0 &&
11890 gtk_tree_view_get_headers_visible (tree_view))
11891 gdk_window_hide (tree_view->priv->header_window);
11893 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11896 g_object_unref (column);
11897 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
11899 return tree_view->priv->n_columns;
11903 * gtk_tree_view_insert_column:
11904 * @tree_view: A #GtkTreeView.
11905 * @column: The #GtkTreeViewColumn to be inserted.
11906 * @position: The position to insert @column in.
11908 * This inserts the @column into the @tree_view at @position. If @position is
11909 * -1, then the column is inserted at the end. If @tree_view has
11910 * "fixed_height" mode enabled, then @column must have its "sizing" property
11911 * set to be GTK_TREE_VIEW_COLUMN_FIXED.
11913 * Return value: The number of columns in @tree_view after insertion.
11916 gtk_tree_view_insert_column (GtkTreeView *tree_view,
11917 GtkTreeViewColumn *column,
11920 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11921 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
11922 g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
11924 if (tree_view->priv->fixed_height_mode)
11925 g_return_val_if_fail (gtk_tree_view_column_get_sizing (column)
11926 == GTK_TREE_VIEW_COLUMN_FIXED, -1);
11928 g_object_ref_sink (column);
11930 if (tree_view->priv->n_columns == 0 &&
11931 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
11932 gtk_tree_view_get_headers_visible (tree_view))
11934 gdk_window_show (tree_view->priv->header_window);
11937 g_signal_connect (column, "notify::sizing",
11938 G_CALLBACK (column_sizing_notify), tree_view);
11940 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
11942 tree_view->priv->n_columns++;
11944 _gtk_tree_view_column_set_tree_view (column, tree_view);
11946 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11950 _gtk_tree_view_column_realize_button (column);
11952 for (list = tree_view->priv->columns; list; list = list->next)
11954 column = GTK_TREE_VIEW_COLUMN (list->data);
11955 if (gtk_tree_view_column_get_visible (column))
11956 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
11958 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11961 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
11963 return tree_view->priv->n_columns;
11967 * gtk_tree_view_insert_column_with_attributes:
11968 * @tree_view: A #GtkTreeView
11969 * @position: The position to insert the new column in.
11970 * @title: The title to set the header to.
11971 * @cell: The #GtkCellRenderer.
11972 * @Varargs: A %NULL-terminated list of attributes.
11974 * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at
11975 * @position. If @position is -1, then the newly created column is inserted at
11976 * the end. The column is initialized with the attributes given. If @tree_view
11977 * has "fixed_height" mode enabled, then the new column will have its sizing
11978 * property set to be GTK_TREE_VIEW_COLUMN_FIXED.
11980 * Return value: The number of columns in @tree_view after insertion.
11983 gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view,
11985 const gchar *title,
11986 GtkCellRenderer *cell,
11989 GtkTreeViewColumn *column;
11994 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11996 column = gtk_tree_view_column_new ();
11997 if (tree_view->priv->fixed_height_mode)
11998 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
12000 gtk_tree_view_column_set_title (column, title);
12001 gtk_tree_view_column_pack_start (column, cell, TRUE);
12003 va_start (args, cell);
12005 attribute = va_arg (args, gchar *);
12007 while (attribute != NULL)
12009 column_id = va_arg (args, gint);
12010 gtk_tree_view_column_add_attribute (column, cell, attribute, column_id);
12011 attribute = va_arg (args, gchar *);
12016 gtk_tree_view_insert_column (tree_view, column, position);
12018 return tree_view->priv->n_columns;
12022 * gtk_tree_view_insert_column_with_data_func:
12023 * @tree_view: a #GtkTreeView
12024 * @position: Position to insert, -1 for append
12025 * @title: column title
12026 * @cell: cell renderer for column
12027 * @func: function to set attributes of cell renderer
12028 * @data: data for @func
12029 * @dnotify: destroy notifier for @data
12031 * Convenience function that inserts a new column into the #GtkTreeView
12032 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
12033 * attributes (normally using data from the model). See also
12034 * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start().
12035 * If @tree_view has "fixed_height" mode enabled, then the new column will have its
12036 * "sizing" property set to be GTK_TREE_VIEW_COLUMN_FIXED.
12038 * Return value: number of columns in the tree view post-insert
12041 gtk_tree_view_insert_column_with_data_func (GtkTreeView *tree_view,
12043 const gchar *title,
12044 GtkCellRenderer *cell,
12045 GtkTreeCellDataFunc func,
12047 GDestroyNotify dnotify)
12049 GtkTreeViewColumn *column;
12051 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
12053 column = gtk_tree_view_column_new ();
12054 if (tree_view->priv->fixed_height_mode)
12055 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
12057 gtk_tree_view_column_set_title (column, title);
12058 gtk_tree_view_column_pack_start (column, cell, TRUE);
12059 gtk_tree_view_column_set_cell_data_func (column, cell, func, data, dnotify);
12061 gtk_tree_view_insert_column (tree_view, column, position);
12063 return tree_view->priv->n_columns;
12067 * gtk_tree_view_get_column:
12068 * @tree_view: A #GtkTreeView.
12069 * @n: The position of the column, counting from 0.
12071 * Gets the #GtkTreeViewColumn at the given position in the #tree_view.
12073 * Return value: (transfer none): The #GtkTreeViewColumn, or %NULL if the
12074 * position is outside the range of columns.
12076 GtkTreeViewColumn *
12077 gtk_tree_view_get_column (GtkTreeView *tree_view,
12080 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12082 if (n < 0 || n >= tree_view->priv->n_columns)
12085 if (tree_view->priv->columns == NULL)
12088 return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
12092 * gtk_tree_view_get_columns:
12093 * @tree_view: A #GtkTreeView
12095 * Returns a #GList of all the #GtkTreeViewColumn s currently in @tree_view.
12096 * The returned list must be freed with g_list_free ().
12098 * Return value: (element-type GtkTreeViewColumn) (transfer container): A list of #GtkTreeViewColumn s
12101 gtk_tree_view_get_columns (GtkTreeView *tree_view)
12103 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12105 return g_list_copy (tree_view->priv->columns);
12109 * gtk_tree_view_move_column_after:
12110 * @tree_view: A #GtkTreeView
12111 * @column: The #GtkTreeViewColumn to be moved.
12112 * @base_column: (allow-none): The #GtkTreeViewColumn to be moved relative to, or %NULL.
12114 * Moves @column to be after to @base_column. If @base_column is %NULL, then
12115 * @column is placed in the first position.
12118 gtk_tree_view_move_column_after (GtkTreeView *tree_view,
12119 GtkTreeViewColumn *column,
12120 GtkTreeViewColumn *base_column)
12122 GList *column_list_el, *base_el = NULL;
12124 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12126 column_list_el = g_list_find (tree_view->priv->columns, column);
12127 g_return_if_fail (column_list_el != NULL);
12131 base_el = g_list_find (tree_view->priv->columns, base_column);
12132 g_return_if_fail (base_el != NULL);
12135 if (column_list_el->prev == base_el)
12138 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
12139 if (base_el == NULL)
12141 column_list_el->prev = NULL;
12142 column_list_el->next = tree_view->priv->columns;
12143 if (column_list_el->next)
12144 column_list_el->next->prev = column_list_el;
12145 tree_view->priv->columns = column_list_el;
12149 column_list_el->prev = base_el;
12150 column_list_el->next = base_el->next;
12151 if (column_list_el->next)
12152 column_list_el->next->prev = column_list_el;
12153 base_el->next = column_list_el;
12156 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12158 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
12159 gtk_tree_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
12162 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
12166 * gtk_tree_view_set_expander_column:
12167 * @tree_view: A #GtkTreeView
12168 * @column: %NULL, or the column to draw the expander arrow at.
12170 * Sets the column to draw the expander arrow at. It must be in @tree_view.
12171 * If @column is %NULL, then the expander arrow is always at the first
12174 * If you do not want expander arrow to appear in your tree, set the
12175 * expander column to a hidden column.
12178 gtk_tree_view_set_expander_column (GtkTreeView *tree_view,
12179 GtkTreeViewColumn *column)
12181 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12182 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
12184 if (tree_view->priv->expander_column != column)
12190 /* Confirm that column is in tree_view */
12191 for (list = tree_view->priv->columns; list; list = list->next)
12192 if (list->data == column)
12194 g_return_if_fail (list != NULL);
12197 tree_view->priv->expander_column = column;
12198 g_object_notify (G_OBJECT (tree_view), "expander-column");
12203 * gtk_tree_view_get_expander_column:
12204 * @tree_view: A #GtkTreeView
12206 * Returns the column that is the current expander column.
12207 * This column has the expander arrow drawn next to it.
12209 * Return value: (transfer none): The expander column.
12211 GtkTreeViewColumn *
12212 gtk_tree_view_get_expander_column (GtkTreeView *tree_view)
12216 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12218 for (list = tree_view->priv->columns; list; list = list->next)
12219 if (gtk_tree_view_is_expander_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data)))
12220 return (GtkTreeViewColumn *) list->data;
12226 * gtk_tree_view_set_column_drag_function:
12227 * @tree_view: A #GtkTreeView.
12228 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
12229 * @user_data: (allow-none): User data to be passed to @func, or %NULL
12230 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
12232 * Sets a user function for determining where a column may be dropped when
12233 * dragged. This function is called on every column pair in turn at the
12234 * beginning of a column drag to determine where a drop can take place. The
12235 * arguments passed to @func are: the @tree_view, the #GtkTreeViewColumn being
12236 * dragged, the two #GtkTreeViewColumn s determining the drop spot, and
12237 * @user_data. If either of the #GtkTreeViewColumn arguments for the drop spot
12238 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
12239 * @tree_view reverts to the default behavior of allowing all columns to be
12240 * dropped everywhere.
12243 gtk_tree_view_set_column_drag_function (GtkTreeView *tree_view,
12244 GtkTreeViewColumnDropFunc func,
12245 gpointer user_data,
12246 GDestroyNotify destroy)
12248 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12250 if (tree_view->priv->column_drop_func_data_destroy)
12251 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
12253 tree_view->priv->column_drop_func = func;
12254 tree_view->priv->column_drop_func_data = user_data;
12255 tree_view->priv->column_drop_func_data_destroy = destroy;
12259 * gtk_tree_view_scroll_to_point:
12260 * @tree_view: a #GtkTreeView
12261 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
12262 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
12264 * Scrolls the tree view such that the top-left corner of the visible
12265 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
12266 * in tree coordinates. The @tree_view must be realized before
12267 * this function is called. If it isn't, you probably want to be
12268 * using gtk_tree_view_scroll_to_cell().
12270 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
12273 gtk_tree_view_scroll_to_point (GtkTreeView *tree_view,
12277 GtkAdjustment *hadj;
12278 GtkAdjustment *vadj;
12280 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12281 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
12283 hadj = tree_view->priv->hadjustment;
12284 vadj = tree_view->priv->vadjustment;
12287 gtk_adjustment_set_value (hadj, tree_x);
12289 gtk_adjustment_set_value (vadj, tree_y);
12293 * gtk_tree_view_scroll_to_cell:
12294 * @tree_view: A #GtkTreeView.
12295 * @path: (allow-none): The path of the row to move to, or %NULL.
12296 * @column: (allow-none): The #GtkTreeViewColumn to move horizontally to, or %NULL.
12297 * @use_align: whether to use alignment arguments, or %FALSE.
12298 * @row_align: The vertical alignment of the row specified by @path.
12299 * @col_align: The horizontal alignment of the column specified by @column.
12301 * Moves the alignments of @tree_view to the position specified by @column and
12302 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
12303 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
12304 * or @path need to be non-%NULL. @row_align determines where the row is
12305 * placed, and @col_align determines where @column is placed. Both are expected
12306 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
12307 * right/bottom alignment, 0.5 means center.
12309 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
12310 * tree does the minimum amount of work to scroll the cell onto the screen.
12311 * This means that the cell will be scrolled to the edge closest to its current
12312 * position. If the cell is currently visible on the screen, nothing is done.
12314 * This function only works if the model is set, and @path is a valid row on the
12315 * model. If the model changes before the @tree_view is realized, the centered
12316 * path will be modified to reflect this change.
12319 gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view,
12321 GtkTreeViewColumn *column,
12322 gboolean use_align,
12326 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12327 g_return_if_fail (tree_view->priv->model != NULL);
12328 g_return_if_fail (tree_view->priv->tree != NULL);
12329 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
12330 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
12331 g_return_if_fail (path != NULL || column != NULL);
12334 g_print ("gtk_tree_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
12335 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
12337 row_align = CLAMP (row_align, 0.0, 1.0);
12338 col_align = CLAMP (col_align, 0.0, 1.0);
12341 /* Note: Despite the benefits that come from having one code path for the
12342 * scrolling code, we short-circuit validate_visible_area's immplementation as
12343 * it is much slower than just going to the point.
12345 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
12346 !gtk_widget_get_realized (GTK_WIDGET (tree_view)) ||
12347 _gtk_widget_get_alloc_needed (GTK_WIDGET (tree_view)) ||
12348 GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID))
12350 if (tree_view->priv->scroll_to_path)
12351 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
12353 tree_view->priv->scroll_to_path = NULL;
12354 tree_view->priv->scroll_to_column = NULL;
12357 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
12359 tree_view->priv->scroll_to_column = column;
12360 tree_view->priv->scroll_to_use_align = use_align;
12361 tree_view->priv->scroll_to_row_align = row_align;
12362 tree_view->priv->scroll_to_col_align = col_align;
12364 install_presize_handler (tree_view);
12368 GdkRectangle cell_rect;
12369 GdkRectangle vis_rect;
12370 gint dest_x, dest_y;
12372 gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect);
12373 gtk_tree_view_get_visible_rect (tree_view, &vis_rect);
12375 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
12377 dest_x = vis_rect.x;
12378 dest_y = vis_rect.y;
12384 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
12388 if (cell_rect.x < vis_rect.x)
12389 dest_x = cell_rect.x;
12390 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
12391 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
12399 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
12400 dest_y = MAX (dest_y, 0);
12404 if (cell_rect.y < vis_rect.y)
12405 dest_y = cell_rect.y;
12406 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
12407 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
12411 gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y);
12416 * gtk_tree_view_row_activated:
12417 * @tree_view: A #GtkTreeView
12418 * @path: The #GtkTreePath to be activated.
12419 * @column: The #GtkTreeViewColumn to be activated.
12421 * Activates the cell determined by @path and @column.
12424 gtk_tree_view_row_activated (GtkTreeView *tree_view,
12426 GtkTreeViewColumn *column)
12428 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12430 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
12435 gtk_tree_view_expand_all_emission_helper (GtkRBTree *tree,
12439 GtkTreeView *tree_view = data;
12441 if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT &&
12447 path = _gtk_tree_view_find_path (tree_view, tree, node);
12448 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12450 g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
12452 gtk_tree_path_free (path);
12455 if (node->children)
12456 _gtk_rbtree_traverse (node->children,
12457 node->children->root,
12459 gtk_tree_view_expand_all_emission_helper,
12464 * gtk_tree_view_expand_all:
12465 * @tree_view: A #GtkTreeView.
12467 * Recursively expands all nodes in the @tree_view.
12470 gtk_tree_view_expand_all (GtkTreeView *tree_view)
12476 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12478 if (tree_view->priv->tree == NULL)
12481 path = gtk_tree_path_new_first ();
12482 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
12486 gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE, FALSE);
12487 node = _gtk_rbtree_next (tree, node);
12488 gtk_tree_path_next (path);
12491 gtk_tree_path_free (path);
12494 /* Timeout to animate the expander during expands and collapses */
12496 expand_collapse_timeout (gpointer data)
12498 return do_expand_collapse (data);
12502 add_expand_collapse_timeout (GtkTreeView *tree_view,
12507 if (tree_view->priv->expand_collapse_timeout != 0)
12510 tree_view->priv->expand_collapse_timeout =
12511 gdk_threads_add_timeout (50, expand_collapse_timeout, tree_view);
12512 tree_view->priv->expanded_collapsed_tree = tree;
12513 tree_view->priv->expanded_collapsed_node = node;
12516 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
12518 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
12522 remove_expand_collapse_timeout (GtkTreeView *tree_view)
12524 if (tree_view->priv->expand_collapse_timeout)
12526 g_source_remove (tree_view->priv->expand_collapse_timeout);
12527 tree_view->priv->expand_collapse_timeout = 0;
12530 if (tree_view->priv->expanded_collapsed_node != NULL)
12532 GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_EXPANDED);
12533 GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_COLLAPSED);
12535 tree_view->priv->expanded_collapsed_node = NULL;
12540 cancel_arrow_animation (GtkTreeView *tree_view)
12542 if (tree_view->priv->expand_collapse_timeout)
12544 while (do_expand_collapse (tree_view));
12546 remove_expand_collapse_timeout (tree_view);
12551 do_expand_collapse (GtkTreeView *tree_view)
12555 gboolean expanding;
12561 node = tree_view->priv->expanded_collapsed_node;
12562 tree = tree_view->priv->expanded_collapsed_tree;
12564 if (node->children == NULL)
12569 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_COLLAPSED))
12571 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
12572 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
12577 else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_EXPANDED))
12579 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
12586 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_EXPANDED))
12588 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
12589 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
12593 else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_COLLAPSED))
12595 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
12604 gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
12613 * gtk_tree_view_collapse_all:
12614 * @tree_view: A #GtkTreeView.
12616 * Recursively collapses all visible, expanded nodes in @tree_view.
12619 gtk_tree_view_collapse_all (GtkTreeView *tree_view)
12626 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12628 if (tree_view->priv->tree == NULL)
12631 path = gtk_tree_path_new ();
12632 gtk_tree_path_down (path);
12633 indices = gtk_tree_path_get_indices (path);
12635 tree = tree_view->priv->tree;
12637 while (node && node->left != tree->nil)
12642 if (node->children)
12643 gtk_tree_view_real_collapse_row (tree_view, path, tree, node, FALSE);
12645 node = _gtk_rbtree_next (tree, node);
12648 gtk_tree_path_free (path);
12652 * gtk_tree_view_expand_to_path:
12653 * @tree_view: A #GtkTreeView.
12654 * @path: path to a row.
12656 * Expands the row at @path. This will also expand all parent rows of
12657 * @path as necessary.
12662 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
12669 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12670 g_return_if_fail (path != NULL);
12672 depth = gtk_tree_path_get_depth (path);
12673 indices = gtk_tree_path_get_indices (path);
12675 tmp = gtk_tree_path_new ();
12676 g_return_if_fail (tmp != NULL);
12678 for (i = 0; i < depth; i++)
12680 gtk_tree_path_append_index (tmp, indices[i]);
12681 gtk_tree_view_expand_row (tree_view, tmp, FALSE);
12684 gtk_tree_path_free (tmp);
12687 /* FIXME the bool return values for expand_row and collapse_row are
12688 * not analagous; they should be TRUE if the row had children and
12689 * was not already in the requested state.
12694 gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
12706 g_object_get (gtk_widget_get_settings (GTK_WIDGET (tree_view)),
12707 "gtk-enable-animations", &animate,
12710 remove_auto_expand_timeout (tree_view);
12712 if (node->children && !open_all)
12715 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
12718 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12719 if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
12723 if (node->children && open_all)
12725 gboolean retval = FALSE;
12726 GtkTreePath *tmp_path = gtk_tree_path_copy (path);
12728 gtk_tree_path_append_index (tmp_path, 0);
12729 tree = node->children;
12731 while (node->left != tree->nil)
12733 /* try to expand the children */
12737 t = gtk_tree_view_real_expand_row (tree_view, tmp_path, tree, node,
12742 gtk_tree_path_next (tmp_path);
12743 node = _gtk_rbtree_next (tree, node);
12745 while (node != NULL);
12747 gtk_tree_path_free (tmp_path);
12752 g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand);
12754 if (!gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
12760 node->children = _gtk_rbtree_new ();
12761 node->children->parent_tree = tree;
12762 node->children->parent_node = node;
12764 gtk_tree_model_iter_children (tree_view->priv->model, &temp, &iter);
12766 gtk_tree_view_build_tree (tree_view,
12769 gtk_tree_path_get_depth (path) + 1,
12772 remove_expand_collapse_timeout (tree_view);
12775 add_expand_collapse_timeout (tree_view, tree, node, TRUE);
12777 install_presize_handler (tree_view);
12779 g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
12780 if (open_all && node->children)
12782 _gtk_rbtree_traverse (node->children,
12783 node->children->root,
12785 gtk_tree_view_expand_all_emission_helper,
12793 * gtk_tree_view_expand_row:
12794 * @tree_view: a #GtkTreeView
12795 * @path: path to a row
12796 * @open_all: whether to recursively expand, or just expand immediate children
12798 * Opens the row so its children are visible.
12800 * Return value: %TRUE if the row existed and had children
12803 gtk_tree_view_expand_row (GtkTreeView *tree_view,
12810 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12811 g_return_val_if_fail (tree_view->priv->model != NULL, FALSE);
12812 g_return_val_if_fail (path != NULL, FALSE);
12814 if (_gtk_tree_view_find_node (tree_view,
12821 return gtk_tree_view_real_expand_row (tree_view, path, tree, node, open_all, FALSE);
12827 gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
12834 GtkTreeIter children;
12838 GdkWindow *child, *parent;
12841 g_object_get (gtk_widget_get_settings (GTK_WIDGET (tree_view)),
12842 "gtk-enable-animations", &animate,
12845 remove_auto_expand_timeout (tree_view);
12847 if (node->children == NULL)
12850 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12852 g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse);
12857 /* if the prelighted node is a child of us, we want to unprelight it. We have
12858 * a chance to prelight the correct node below */
12860 if (tree_view->priv->prelight_tree)
12862 GtkRBTree *parent_tree;
12863 GtkRBNode *parent_node;
12865 parent_tree = tree_view->priv->prelight_tree->parent_tree;
12866 parent_node = tree_view->priv->prelight_tree->parent_node;
12867 while (parent_tree)
12869 if (parent_tree == tree && parent_node == node)
12871 ensure_unprelighted (tree_view);
12874 parent_node = parent_tree->parent_node;
12875 parent_tree = parent_tree->parent_tree;
12879 TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_children (tree_view->priv->model, &children, &iter), FALSE);
12881 for (list = tree_view->priv->columns; list; list = list->next)
12883 GtkTreeViewColumn *column = list->data;
12885 if (gtk_tree_view_column_get_visible (column) == FALSE)
12887 if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
12888 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
12891 if (tree_view->priv->destroy_count_func)
12893 GtkTreePath *child_path;
12894 gint child_count = 0;
12895 child_path = gtk_tree_path_copy (path);
12896 gtk_tree_path_down (child_path);
12897 if (node->children)
12898 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, &child_count);
12899 tree_view->priv->destroy_count_func (tree_view, child_path, child_count, tree_view->priv->destroy_count_data);
12900 gtk_tree_path_free (child_path);
12903 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
12905 GtkTreePath *cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
12907 if (gtk_tree_path_is_ancestor (path, cursor_path))
12909 gtk_tree_row_reference_free (tree_view->priv->cursor);
12910 tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
12911 tree_view->priv->model,
12914 gtk_tree_path_free (cursor_path);
12917 if (gtk_tree_row_reference_valid (tree_view->priv->anchor))
12919 GtkTreePath *anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
12920 if (gtk_tree_path_is_ancestor (path, anchor_path))
12922 gtk_tree_row_reference_free (tree_view->priv->anchor);
12923 tree_view->priv->anchor = NULL;
12925 gtk_tree_path_free (anchor_path);
12928 /* Stop a pending double click */
12929 tree_view->priv->last_button_x = -1;
12930 tree_view->priv->last_button_y = -1;
12932 remove_expand_collapse_timeout (tree_view);
12934 if (gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children))
12936 _gtk_rbtree_remove (node->children);
12937 g_signal_emit_by_name (tree_view->priv->selection, "changed");
12940 _gtk_rbtree_remove (node->children);
12943 add_expand_collapse_timeout (tree_view, tree, node, FALSE);
12945 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
12947 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
12950 g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path);
12952 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
12954 /* now that we've collapsed all rows, we want to try to set the prelight
12955 * again. To do this, we fake a motion event and send it to ourselves. */
12957 child = tree_view->priv->bin_window;
12958 parent = gdk_window_get_parent (child);
12960 if (gdk_window_get_pointer (parent, &x, &y, NULL) == child)
12962 GdkEventMotion event;
12963 gint child_x, child_y;
12965 gdk_window_get_position (child, &child_x, &child_y);
12967 event.window = tree_view->priv->bin_window;
12968 event.x = x - child_x;
12969 event.y = y - child_y;
12971 /* despite the fact this isn't a real event, I'm almost positive it will
12972 * never trigger a drag event. maybe_drag is the only function that uses
12973 * more than just event.x and event.y. */
12974 gtk_tree_view_motion_bin_window (GTK_WIDGET (tree_view), &event);
12982 * gtk_tree_view_collapse_row:
12983 * @tree_view: a #GtkTreeView
12984 * @path: path to a row in the @tree_view
12986 * Collapses a row (hides its child rows, if they exist).
12988 * Return value: %TRUE if the row was collapsed.
12991 gtk_tree_view_collapse_row (GtkTreeView *tree_view,
12997 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12998 g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE);
12999 g_return_val_if_fail (path != NULL, FALSE);
13001 if (_gtk_tree_view_find_node (tree_view,
13007 if (tree == NULL || node->children == NULL)
13010 return gtk_tree_view_real_collapse_row (tree_view, path, tree, node, FALSE);
13014 gtk_tree_view_map_expanded_rows_helper (GtkTreeView *tree_view,
13017 GtkTreeViewMappingFunc func,
13018 gpointer user_data)
13022 if (tree == NULL || tree->root == NULL)
13027 while (node && node->left != tree->nil)
13032 if (node->children)
13034 (* func) (tree_view, path, user_data);
13035 gtk_tree_path_down (path);
13036 gtk_tree_view_map_expanded_rows_helper (tree_view, node->children, path, func, user_data);
13037 gtk_tree_path_up (path);
13039 gtk_tree_path_next (path);
13040 node = _gtk_rbtree_next (tree, node);
13045 * gtk_tree_view_map_expanded_rows:
13046 * @tree_view: A #GtkTreeView
13047 * @func: (scope call): A function to be called
13048 * @data: User data to be passed to the function.
13050 * Calls @func on all expanded rows.
13053 gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view,
13054 GtkTreeViewMappingFunc func,
13055 gpointer user_data)
13059 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13060 g_return_if_fail (func != NULL);
13062 path = gtk_tree_path_new_first ();
13064 gtk_tree_view_map_expanded_rows_helper (tree_view,
13065 tree_view->priv->tree,
13066 path, func, user_data);
13068 gtk_tree_path_free (path);
13072 * gtk_tree_view_row_expanded:
13073 * @tree_view: A #GtkTreeView.
13074 * @path: A #GtkTreePath to test expansion state.
13076 * Returns %TRUE if the node pointed to by @path is expanded in @tree_view.
13078 * Return value: %TRUE if #path is expanded.
13081 gtk_tree_view_row_expanded (GtkTreeView *tree_view,
13087 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13088 g_return_val_if_fail (path != NULL, FALSE);
13090 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
13095 return (node->children != NULL);
13099 * gtk_tree_view_get_reorderable:
13100 * @tree_view: a #GtkTreeView
13102 * Retrieves whether the user can reorder the tree via drag-and-drop. See
13103 * gtk_tree_view_set_reorderable().
13105 * Return value: %TRUE if the tree can be reordered.
13108 gtk_tree_view_get_reorderable (GtkTreeView *tree_view)
13110 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13112 return tree_view->priv->reorderable;
13116 * gtk_tree_view_set_reorderable:
13117 * @tree_view: A #GtkTreeView.
13118 * @reorderable: %TRUE, if the tree can be reordered.
13120 * This function is a convenience function to allow you to reorder
13121 * models that support the #GtkDragSourceIface and the
13122 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
13123 * these. If @reorderable is %TRUE, then the user can reorder the
13124 * model by dragging and dropping rows. The developer can listen to
13125 * these changes by connecting to the model's row_inserted and
13126 * row_deleted signals. The reordering is implemented by setting up
13127 * the tree view as a drag source and destination. Therefore, drag and
13128 * drop can not be used in a reorderable view for any other purpose.
13130 * This function does not give you any degree of control over the order -- any
13131 * reordering is allowed. If more control is needed, you should probably
13132 * handle drag and drop manually.
13135 gtk_tree_view_set_reorderable (GtkTreeView *tree_view,
13136 gboolean reorderable)
13138 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13140 reorderable = reorderable != FALSE;
13142 if (tree_view->priv->reorderable == reorderable)
13147 const GtkTargetEntry row_targets[] = {
13148 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
13151 gtk_tree_view_enable_model_drag_source (tree_view,
13154 G_N_ELEMENTS (row_targets),
13156 gtk_tree_view_enable_model_drag_dest (tree_view,
13158 G_N_ELEMENTS (row_targets),
13163 gtk_tree_view_unset_rows_drag_source (tree_view);
13164 gtk_tree_view_unset_rows_drag_dest (tree_view);
13167 tree_view->priv->reorderable = reorderable;
13169 g_object_notify (G_OBJECT (tree_view), "reorderable");
13173 gtk_tree_view_real_set_cursor (GtkTreeView *tree_view,
13175 gboolean clear_and_select,
13176 gboolean clamp_node)
13178 GtkRBTree *tree = NULL;
13179 GtkRBNode *node = NULL;
13181 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
13183 GtkTreePath *cursor_path;
13184 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
13185 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
13186 gtk_tree_path_free (cursor_path);
13189 gtk_tree_row_reference_free (tree_view->priv->cursor);
13190 tree_view->priv->cursor = NULL;
13192 /* One cannot set the cursor on a separator. Also, if
13193 * _gtk_tree_view_find_node returns TRUE, it ran out of tree
13194 * before finding the tree and node belonging to path. The
13195 * path maps to a non-existing path and we will silently bail out.
13196 * We unset tree and node to avoid further processing.
13198 if (!row_is_separator (tree_view, NULL, path)
13199 && _gtk_tree_view_find_node (tree_view, path, &tree, &node) == FALSE)
13201 tree_view->priv->cursor =
13202 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
13203 tree_view->priv->model,
13214 GtkRBTree *new_tree = NULL;
13215 GtkRBNode *new_node = NULL;
13217 if (clear_and_select && !tree_view->priv->ctrl_pressed)
13219 GtkTreeSelectMode mode = 0;
13221 if (tree_view->priv->ctrl_pressed)
13222 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
13223 if (tree_view->priv->shift_pressed)
13224 mode |= GTK_TREE_SELECT_MODE_EXTEND;
13226 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
13227 node, tree, path, mode,
13231 /* We have to re-find tree and node here again, somebody might have
13232 * cleared the node or the whole tree in the GtkTreeSelection::changed
13233 * callback. If the nodes differ we bail out here.
13235 _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node);
13237 if (tree != new_tree || node != new_node)
13242 gtk_tree_view_clamp_node_visible (tree_view, tree, node);
13243 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
13247 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
13251 * gtk_tree_view_get_cursor:
13252 * @tree_view: A #GtkTreeView
13253 * @path: (out) (allow-none): A pointer to be filled with the current cursor path, or %NULL
13254 * @focus_column: (out) (allow-none): A pointer to be filled with the current focus column, or %NULL
13256 * Fills in @path and @focus_column with the current path and focus column. If
13257 * the cursor isn't currently set, then *@path will be %NULL. If no column
13258 * currently has focus, then *@focus_column will be %NULL.
13260 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
13261 * you are done with it.
13264 gtk_tree_view_get_cursor (GtkTreeView *tree_view,
13265 GtkTreePath **path,
13266 GtkTreeViewColumn **focus_column)
13268 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13272 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
13273 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
13280 *focus_column = tree_view->priv->focus_column;
13285 * gtk_tree_view_set_cursor:
13286 * @tree_view: A #GtkTreeView
13287 * @path: A #GtkTreePath
13288 * @focus_column: (allow-none): A #GtkTreeViewColumn, or %NULL
13289 * @start_editing: %TRUE if the specified cell should start being edited.
13291 * Sets the current keyboard focus to be at @path, and selects it. This is
13292 * useful when you want to focus the user's attention on a particular row. If
13293 * @focus_column is not %NULL, then focus is given to the column specified by
13294 * it. Additionally, if @focus_column is specified, and @start_editing is
13295 * %TRUE, then editing should be started in the specified cell.
13296 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
13297 * in order to give keyboard focus to the widget. Please note that editing
13298 * can only happen when the widget is realized.
13300 * If @path is invalid for @model, the current cursor (if any) will be unset
13301 * and the function will return without failing.
13304 gtk_tree_view_set_cursor (GtkTreeView *tree_view,
13306 GtkTreeViewColumn *focus_column,
13307 gboolean start_editing)
13309 gtk_tree_view_set_cursor_on_cell (tree_view, path, focus_column,
13310 NULL, start_editing);
13314 * gtk_tree_view_set_cursor_on_cell:
13315 * @tree_view: A #GtkTreeView
13316 * @path: A #GtkTreePath
13317 * @focus_column: (allow-none): A #GtkTreeViewColumn, or %NULL
13318 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
13319 * @start_editing: %TRUE if the specified cell should start being edited.
13321 * Sets the current keyboard focus to be at @path, and selects it. This is
13322 * useful when you want to focus the user's attention on a particular row. If
13323 * @focus_column is not %NULL, then focus is given to the column specified by
13324 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
13325 * contains 2 or more editable or activatable cells, then focus is given to
13326 * the cell specified by @focus_cell. Additionally, if @focus_column is
13327 * specified, and @start_editing is %TRUE, then editing should be started in
13328 * the specified cell. This function is often followed by
13329 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
13330 * widget. Please note that editing can only happen when the widget is
13333 * If @path is invalid for @model, the current cursor (if any) will be unset
13334 * and the function will return without failing.
13339 gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view,
13341 GtkTreeViewColumn *focus_column,
13342 GtkCellRenderer *focus_cell,
13343 gboolean start_editing)
13345 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13346 g_return_if_fail (path != NULL);
13347 g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column));
13349 if (!tree_view->priv->model)
13354 g_return_if_fail (focus_column);
13355 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
13358 /* cancel the current editing, if it exists */
13359 if (tree_view->priv->edited_column &&
13360 gtk_cell_area_get_edit_widget
13361 (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column))))
13362 gtk_tree_view_stop_editing (tree_view, TRUE);
13364 gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
13366 if (focus_column &&
13367 gtk_tree_view_column_get_visible (focus_column))
13370 gboolean column_in_tree = FALSE;
13372 for (list = tree_view->priv->columns; list; list = list->next)
13373 if (list->data == focus_column)
13375 column_in_tree = TRUE;
13378 g_return_if_fail (column_in_tree);
13379 tree_view->priv->focus_column = focus_column;
13381 gtk_tree_view_column_focus_cell (focus_column, focus_cell);
13383 gtk_tree_view_start_editing (tree_view, path, TRUE);
13388 * gtk_tree_view_get_bin_window:
13389 * @tree_view: A #GtkTreeView
13391 * Returns the window that @tree_view renders to.
13392 * This is used primarily to compare to <literal>event->window</literal>
13393 * to confirm that the event on @tree_view is on the right window.
13395 * Return value: (transfer none): A #GdkWindow, or %NULL when @tree_view
13396 * hasn't been realized yet
13399 gtk_tree_view_get_bin_window (GtkTreeView *tree_view)
13401 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
13403 return tree_view->priv->bin_window;
13407 * gtk_tree_view_get_path_at_pos:
13408 * @tree_view: A #GtkTreeView.
13409 * @x: The x position to be identified (relative to bin_window).
13410 * @y: The y position to be identified (relative to bin_window).
13411 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
13412 * @column: (out) (allow-none): A pointer to a #GtkTreeViewColumn pointer to be filled in, or %NULL
13413 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
13414 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
13416 * Finds the path at the point (@x, @y), relative to bin_window coordinates
13417 * (please see gtk_tree_view_get_bin_window()).
13418 * That is, @x and @y are relative to an events coordinates. @x and @y must
13419 * come from an event on the @tree_view only where <literal>event->window ==
13420 * gtk_tree_view_get_bin_window (<!-- -->)</literal>. It is primarily for
13421 * things like popup menus. If @path is non-%NULL, then it will be filled
13422 * with the #GtkTreePath at that point. This path should be freed with
13423 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
13424 * with the column at that point. @cell_x and @cell_y return the coordinates
13425 * relative to the cell background (i.e. the @background_area passed to
13426 * gtk_cell_renderer_render()). This function is only meaningful if
13427 * @tree_view is realized. Therefore this function will always return %FALSE
13428 * if @tree_view is not realized or does not have a model.
13430 * For converting widget coordinates (eg. the ones you get from
13431 * GtkWidget::query-tooltip), please see
13432 * gtk_tree_view_convert_widget_to_bin_window_coords().
13434 * Return value: %TRUE if a row exists at that coordinate.
13437 gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view,
13440 GtkTreePath **path,
13441 GtkTreeViewColumn **column,
13449 g_return_val_if_fail (tree_view != NULL, FALSE);
13456 if (tree_view->priv->bin_window == NULL)
13459 if (tree_view->priv->tree == NULL)
13462 if (x > tree_view->priv->hadjustment->upper)
13465 if (x < 0 || y < 0)
13468 if (column || cell_x)
13470 GtkTreeViewColumn *tmp_column;
13471 GtkTreeViewColumn *last_column = NULL;
13473 gint remaining_x = x;
13474 gboolean found = FALSE;
13478 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
13479 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
13481 list = (rtl ? list->prev : list->next))
13483 tmp_column = list->data;
13485 if (gtk_tree_view_column_get_visible (tmp_column) == FALSE)
13488 last_column = tmp_column;
13489 width = gtk_tree_view_column_get_width (tmp_column);
13490 if (remaining_x <= width)
13495 *column = tmp_column;
13498 *cell_x = remaining_x;
13502 remaining_x -= width;
13505 /* If found is FALSE and there is a last_column, then it the remainder
13506 * space is in that area
13513 *column = last_column;
13516 *cell_x = gtk_tree_view_column_get_width (last_column) + remaining_x;
13525 y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
13526 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
13533 *cell_y = y_offset;
13536 *path = _gtk_tree_view_find_path (tree_view, tree, node);
13543 gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view,
13545 gint vertical_separator)
13549 /* The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
13550 * i.e. just the cells, no spacing.
13552 * The cell area height is at least expander_size - vertical_separator.
13553 * For regular nodes, the height is then at least expander_size. We should
13554 * be able to enforce the expander_size minimum here, because this
13555 * function will not be called for irregular (e.g. separator) rows.
13557 height = gtk_tree_view_get_row_height (tree_view, node);
13558 if (height < tree_view->priv->expander_size)
13559 height = tree_view->priv->expander_size;
13561 return height - vertical_separator;
13565 gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view,
13568 gint vertical_separator)
13572 offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
13573 offset += vertical_separator / 2;
13579 * gtk_tree_view_get_cell_area:
13580 * @tree_view: a #GtkTreeView
13581 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
13582 * @column: (allow-none): a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordinates
13583 * @rect: rectangle to fill with cell rect
13585 * Fills the bounding rectangle in bin_window coordinates for the cell at the
13586 * row specified by @path and the column specified by @column. If @path is
13587 * %NULL, or points to a path not currently displayed, the @y and @height fields
13588 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
13589 * fields will be filled with 0. The sum of all cell rects does not cover the
13590 * entire tree; there are extra pixels in between rows, for example. The
13591 * returned rectangle is equivalent to the @cell_area passed to
13592 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
13596 gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
13598 GtkTreeViewColumn *column,
13599 GdkRectangle *rect)
13601 GtkAllocation allocation;
13602 GtkRBTree *tree = NULL;
13603 GtkRBNode *node = NULL;
13604 gint vertical_separator;
13605 gint horizontal_separator;
13607 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13608 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
13609 g_return_if_fail (rect != NULL);
13610 g_return_if_fail (!column || gtk_tree_view_column_get_tree_view (column) == (GtkWidget *) tree_view);
13611 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
13613 gtk_widget_style_get (GTK_WIDGET (tree_view),
13614 "vertical-separator", &vertical_separator,
13615 "horizontal-separator", &horizontal_separator,
13625 gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
13626 rect->x = allocation.x + horizontal_separator/2;
13627 rect->width = allocation.width - horizontal_separator;
13632 gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node);
13634 /* Get vertical coords */
13635 if ((!ret && tree == NULL) || ret)
13638 if (row_is_separator (tree_view, NULL, path))
13640 /* There isn't really a "cell area" for separator, so we
13641 * return the y, height values for background area instead.
13643 rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
13644 rect->height = gtk_tree_view_get_row_height (tree_view, node);
13648 rect->y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node,
13649 vertical_separator);
13650 rect->height = gtk_tree_view_get_cell_area_height (tree_view, node,
13651 vertical_separator);
13655 gtk_tree_view_is_expander_column (tree_view, column))
13657 gint depth = gtk_tree_path_get_depth (path);
13660 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
13663 rect->x += (depth - 1) * tree_view->priv->level_indentation;
13664 rect->width -= (depth - 1) * tree_view->priv->level_indentation;
13666 if (gtk_tree_view_draw_expanders (tree_view))
13669 rect->x += depth * tree_view->priv->expander_size;
13670 rect->width -= depth * tree_view->priv->expander_size;
13673 rect->width = MAX (rect->width, 0);
13679 gtk_tree_view_get_row_height (GtkTreeView *tree_view,
13684 /* The "background" areas of all rows/cells add up to cover the entire tree.
13685 * The background includes all inter-row and inter-cell spacing.
13687 * If the row pointed at by node does not have a height set, we default
13688 * to expander_size, which is the minimum height for regular nodes.
13689 * Non-regular nodes (e.g. separators) can have a height set smaller
13690 * than expander_size and should not be overruled here.
13692 height = GTK_RBNODE_GET_HEIGHT (node);
13694 height = tree_view->priv->expander_size;
13700 gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view,
13706 offset = _gtk_rbtree_node_find_offset (tree, node);
13708 return RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, offset);
13712 * gtk_tree_view_get_background_area:
13713 * @tree_view: a #GtkTreeView
13714 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
13715 * @column: (allow-none): a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes
13716 * @rect: rectangle to fill with cell background rect
13718 * Fills the bounding rectangle in bin_window coordinates for the cell at the
13719 * row specified by @path and the column specified by @column. If @path is
13720 * %NULL, or points to a node not found in the tree, the @y and @height fields of
13721 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
13722 * fields will be filled with 0. The returned rectangle is equivalent to the
13723 * @background_area passed to gtk_cell_renderer_render(). These background
13724 * areas tile to cover the entire bin window. Contrast with the @cell_area,
13725 * returned by gtk_tree_view_get_cell_area(), which returns only the cell
13726 * itself, excluding surrounding borders and the tree expander area.
13730 gtk_tree_view_get_background_area (GtkTreeView *tree_view,
13732 GtkTreeViewColumn *column,
13733 GdkRectangle *rect)
13735 GtkRBTree *tree = NULL;
13736 GtkRBNode *node = NULL;
13738 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13739 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
13740 g_return_if_fail (rect != NULL);
13749 /* Get vertical coords */
13751 if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) &&
13755 rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
13756 rect->height = gtk_tree_view_get_row_height (tree_view, node);
13763 gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2);
13764 rect->width = x2 - rect->x;
13769 * gtk_tree_view_get_visible_rect:
13770 * @tree_view: a #GtkTreeView
13771 * @visible_rect: rectangle to fill
13773 * Fills @visible_rect with the currently-visible region of the
13774 * buffer, in tree coordinates. Convert to bin_window coordinates with
13775 * gtk_tree_view_convert_tree_to_bin_window_coords().
13776 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
13777 * scrollable area of the tree.
13780 gtk_tree_view_get_visible_rect (GtkTreeView *tree_view,
13781 GdkRectangle *visible_rect)
13783 GtkAllocation allocation;
13786 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13788 widget = GTK_WIDGET (tree_view);
13792 gtk_widget_get_allocation (widget, &allocation);
13793 visible_rect->x = tree_view->priv->hadjustment->value;
13794 visible_rect->y = tree_view->priv->vadjustment->value;
13795 visible_rect->width = allocation.width;
13796 visible_rect->height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
13801 * gtk_tree_view_convert_widget_to_tree_coords:
13802 * @tree_view: a #GtkTreeView
13803 * @wx: X coordinate relative to the widget
13804 * @wy: Y coordinate relative to the widget
13805 * @tx: return location for tree X coordinate
13806 * @ty: return location for tree Y coordinate
13808 * Converts widget coordinates to coordinates for the
13809 * tree (the full scrollable area of the tree).
13814 gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view,
13822 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13824 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
13827 gtk_tree_view_convert_bin_window_to_tree_coords (tree_view,
13833 * gtk_tree_view_convert_tree_to_widget_coords:
13834 * @tree_view: a #GtkTreeView
13835 * @tx: X coordinate relative to the tree
13836 * @ty: Y coordinate relative to the tree
13837 * @wx: return location for widget X coordinate
13838 * @wy: return location for widget Y coordinate
13840 * Converts tree coordinates (coordinates in full scrollable area of the tree)
13841 * to widget coordinates.
13846 gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view,
13854 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13856 gtk_tree_view_convert_tree_to_bin_window_coords (tree_view,
13859 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
13865 * gtk_tree_view_convert_widget_to_bin_window_coords:
13866 * @tree_view: a #GtkTreeView
13867 * @wx: X coordinate relative to the widget
13868 * @wy: Y coordinate relative to the widget
13869 * @bx: return location for bin_window X coordinate
13870 * @by: return location for bin_window Y coordinate
13872 * Converts widget coordinates to coordinates for the bin_window
13873 * (see gtk_tree_view_get_bin_window()).
13878 gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view,
13884 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13887 *bx = wx + tree_view->priv->hadjustment->value;
13889 *by = wy - gtk_tree_view_get_effective_header_height (tree_view);
13893 * gtk_tree_view_convert_bin_window_to_widget_coords:
13894 * @tree_view: a #GtkTreeView
13895 * @bx: bin_window X coordinate
13896 * @by: bin_window Y coordinate
13897 * @wx: return location for widget X coordinate
13898 * @wy: return location for widget Y coordinate
13900 * Converts bin_window coordinates (see gtk_tree_view_get_bin_window())
13901 * to widget relative coordinates.
13906 gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view,
13912 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13915 *wx = bx - tree_view->priv->hadjustment->value;
13917 *wy = by + gtk_tree_view_get_effective_header_height (tree_view);
13921 * gtk_tree_view_convert_tree_to_bin_window_coords:
13922 * @tree_view: a #GtkTreeView
13923 * @tx: tree X coordinate
13924 * @ty: tree Y coordinate
13925 * @bx: return location for X coordinate relative to bin_window
13926 * @by: return location for Y coordinate relative to bin_window
13928 * Converts tree coordinates (coordinates in full scrollable area of the tree)
13929 * to bin_window coordinates.
13934 gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view,
13940 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13945 *by = ty - tree_view->priv->dy;
13949 * gtk_tree_view_convert_bin_window_to_tree_coords:
13950 * @tree_view: a #GtkTreeView
13951 * @bx: X coordinate relative to bin_window
13952 * @by: Y coordinate relative to bin_window
13953 * @tx: return location for tree X coordinate
13954 * @ty: return location for tree Y coordinate
13956 * Converts bin_window coordinates to coordinates for the
13957 * tree (the full scrollable area of the tree).
13962 gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view,
13968 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13973 *ty = by + tree_view->priv->dy;
13979 * gtk_tree_view_get_visible_range:
13980 * @tree_view: A #GtkTreeView
13981 * @start_path: (allow-none): Return location for start of region, or %NULL.
13982 * @end_path: (allow-none): Return location for end of region, or %NULL.
13984 * Sets @start_path and @end_path to be the first and last visible path.
13985 * Note that there may be invisible paths in between.
13987 * The paths should be freed with gtk_tree_path_free() after use.
13989 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
13994 gtk_tree_view_get_visible_range (GtkTreeView *tree_view,
13995 GtkTreePath **start_path,
13996 GtkTreePath **end_path)
14002 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14004 if (!tree_view->priv->tree)
14011 _gtk_rbtree_find_offset (tree_view->priv->tree,
14012 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
14015 *start_path = _gtk_tree_view_find_path (tree_view, tree, node);
14024 if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
14025 y = tree_view->priv->height - 1;
14027 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
14029 _gtk_rbtree_find_offset (tree_view->priv->tree, y, &tree, &node);
14031 *end_path = _gtk_tree_view_find_path (tree_view, tree, node);
14040 unset_reorderable (GtkTreeView *tree_view)
14042 if (tree_view->priv->reorderable)
14044 tree_view->priv->reorderable = FALSE;
14045 g_object_notify (G_OBJECT (tree_view), "reorderable");
14050 * gtk_tree_view_enable_model_drag_source:
14051 * @tree_view: a #GtkTreeView
14052 * @start_button_mask: Mask of allowed buttons to start drag
14053 * @targets: (array): the table of targets that the drag will support
14054 * @n_targets: the number of items in @targets
14055 * @actions: the bitmask of possible actions for a drag from this
14058 * Turns @tree_view into a drag source for automatic DND. Calling this
14059 * method sets #GtkTreeView:reorderable to %FALSE.
14062 gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view,
14063 GdkModifierType start_button_mask,
14064 const GtkTargetEntry *targets,
14066 GdkDragAction actions)
14068 TreeViewDragInfo *di;
14070 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14072 gtk_drag_source_set (GTK_WIDGET (tree_view),
14078 di = ensure_info (tree_view);
14080 di->start_button_mask = start_button_mask;
14081 di->source_actions = actions;
14082 di->source_set = TRUE;
14084 unset_reorderable (tree_view);
14088 * gtk_tree_view_enable_model_drag_dest:
14089 * @tree_view: a #GtkTreeView
14090 * @targets: (array): the table of targets that the drag will support
14091 * @n_targets: the number of items in @targets
14092 * @actions: the bitmask of possible actions for a drag from this
14095 * Turns @tree_view into a drop destination for automatic DND. Calling
14096 * this method sets #GtkTreeView:reorderable to %FALSE.
14099 gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view,
14100 const GtkTargetEntry *targets,
14102 GdkDragAction actions)
14104 TreeViewDragInfo *di;
14106 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14108 gtk_drag_dest_set (GTK_WIDGET (tree_view),
14114 di = ensure_info (tree_view);
14115 di->dest_set = TRUE;
14117 unset_reorderable (tree_view);
14121 * gtk_tree_view_unset_rows_drag_source:
14122 * @tree_view: a #GtkTreeView
14124 * Undoes the effect of
14125 * gtk_tree_view_enable_model_drag_source(). Calling this method sets
14126 * #GtkTreeView:reorderable to %FALSE.
14129 gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view)
14131 TreeViewDragInfo *di;
14133 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14135 di = get_info (tree_view);
14139 if (di->source_set)
14141 gtk_drag_source_unset (GTK_WIDGET (tree_view));
14142 di->source_set = FALSE;
14145 if (!di->dest_set && !di->source_set)
14146 remove_info (tree_view);
14149 unset_reorderable (tree_view);
14153 * gtk_tree_view_unset_rows_drag_dest:
14154 * @tree_view: a #GtkTreeView
14156 * Undoes the effect of
14157 * gtk_tree_view_enable_model_drag_dest(). Calling this method sets
14158 * #GtkTreeView:reorderable to %FALSE.
14161 gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view)
14163 TreeViewDragInfo *di;
14165 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14167 di = get_info (tree_view);
14173 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
14174 di->dest_set = FALSE;
14177 if (!di->dest_set && !di->source_set)
14178 remove_info (tree_view);
14181 unset_reorderable (tree_view);
14185 * gtk_tree_view_set_drag_dest_row:
14186 * @tree_view: a #GtkTreeView
14187 * @path: (allow-none): The path of the row to highlight, or %NULL.
14188 * @pos: Specifies whether to drop before, after or into the row
14190 * Sets the row that is highlighted for feedback.
14193 gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view,
14195 GtkTreeViewDropPosition pos)
14197 GtkTreePath *current_dest;
14199 /* Note; this function is exported to allow a custom DND
14200 * implementation, so it can't touch TreeViewDragInfo
14203 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14205 current_dest = NULL;
14207 if (tree_view->priv->drag_dest_row)
14209 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
14210 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
14213 /* special case a drop on an empty model */
14214 tree_view->priv->empty_view_drop = 0;
14216 if (pos == GTK_TREE_VIEW_DROP_BEFORE && path
14217 && gtk_tree_path_get_depth (path) == 1
14218 && gtk_tree_path_get_indices (path)[0] == 0)
14222 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
14226 tree_view->priv->empty_view_drop = 1;
14229 tree_view->priv->drag_dest_pos = pos;
14233 tree_view->priv->drag_dest_row =
14234 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
14235 gtk_tree_view_queue_draw_path (tree_view, path, NULL);
14238 tree_view->priv->drag_dest_row = NULL;
14242 GtkRBTree *tree, *new_tree;
14243 GtkRBNode *node, *new_node;
14245 _gtk_tree_view_find_node (tree_view, current_dest, &tree, &node);
14246 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
14250 _gtk_rbtree_next_full (tree, node, &new_tree, &new_node);
14251 if (new_tree && new_node)
14252 _gtk_tree_view_queue_draw_node (tree_view, new_tree, new_node, NULL);
14254 _gtk_rbtree_prev_full (tree, node, &new_tree, &new_node);
14255 if (new_tree && new_node)
14256 _gtk_tree_view_queue_draw_node (tree_view, new_tree, new_node, NULL);
14258 gtk_tree_path_free (current_dest);
14263 * gtk_tree_view_get_drag_dest_row:
14264 * @tree_view: a #GtkTreeView
14265 * @path: (out) (allow-none): Return location for the path of the highlighted row, or %NULL.
14266 * @pos: (out) (allow-none): Return location for the drop position, or %NULL
14268 * Gets information about the row that is highlighted for feedback.
14271 gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view,
14272 GtkTreePath **path,
14273 GtkTreeViewDropPosition *pos)
14275 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14279 if (tree_view->priv->drag_dest_row)
14280 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
14283 if (tree_view->priv->empty_view_drop)
14284 *path = gtk_tree_path_new_from_indices (0, -1);
14291 *pos = tree_view->priv->drag_dest_pos;
14295 * gtk_tree_view_get_dest_row_at_pos:
14296 * @tree_view: a #GtkTreeView
14297 * @drag_x: the position to determine the destination row for
14298 * @drag_y: the position to determine the destination row for
14299 * @path: (out) (allow-none): Return location for the path of the highlighted row, or %NULL.
14300 * @pos: (out) (allow-none): Return location for the drop position, or %NULL
14302 * Determines the destination row for a given position. @drag_x and
14303 * @drag_y are expected to be in widget coordinates. This function is only
14304 * meaningful if @tree_view is realized. Therefore this function will always
14305 * return %FALSE if @tree_view is not realized or does not have a model.
14307 * Return value: whether there is a row at the given position, %TRUE if this
14308 * is indeed the case.
14311 gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view,
14314 GtkTreePath **path,
14315 GtkTreeViewDropPosition *pos)
14319 gdouble offset_into_row;
14322 GtkTreeViewColumn *column = NULL;
14323 GtkTreePath *tmp_path = NULL;
14325 /* Note; this function is exported to allow a custom DND
14326 * implementation, so it can't touch TreeViewDragInfo
14329 g_return_val_if_fail (tree_view != NULL, FALSE);
14330 g_return_val_if_fail (drag_x >= 0, FALSE);
14331 g_return_val_if_fail (drag_y >= 0, FALSE);
14336 if (tree_view->priv->bin_window == NULL)
14339 if (tree_view->priv->tree == NULL)
14342 /* If in the top third of a row, we drop before that row; if
14343 * in the bottom third, drop after that row; if in the middle,
14344 * and the row has children, drop into the row.
14346 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
14349 if (!gtk_tree_view_get_path_at_pos (tree_view,
14358 gtk_tree_view_get_background_area (tree_view, tmp_path, column,
14361 offset_into_row = cell_y;
14366 gtk_tree_path_free (tmp_path);
14370 third = cell.height / 3.0;
14374 if (offset_into_row < third)
14376 *pos = GTK_TREE_VIEW_DROP_BEFORE;
14378 else if (offset_into_row < (cell.height / 2.0))
14380 *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
14382 else if (offset_into_row < third * 2.0)
14384 *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
14388 *pos = GTK_TREE_VIEW_DROP_AFTER;
14397 /* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */
14399 * gtk_tree_view_create_row_drag_icon:
14400 * @tree_view: a #GtkTreeView
14401 * @path: a #GtkTreePath in @tree_view
14403 * Creates a #cairo_surface_t representation of the row at @path.
14404 * This image is used for a drag icon.
14406 * Return value: (transfer full): a newly-allocated surface of the drag icon.
14409 gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view,
14418 GdkRectangle background_area;
14421 /* start drawing inside the black outline */
14423 cairo_surface_t *surface;
14424 gint bin_window_width;
14425 gboolean is_separator = FALSE;
14429 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14430 g_return_val_if_fail (path != NULL, NULL);
14432 widget = GTK_WIDGET (tree_view);
14434 if (!gtk_widget_get_realized (widget))
14437 depth = gtk_tree_path_get_depth (path);
14439 _gtk_tree_view_find_node (tree_view,
14447 if (!gtk_tree_model_get_iter (tree_view->priv->model,
14452 style = gtk_widget_get_style (widget);
14454 is_separator = row_is_separator (tree_view, &iter, NULL);
14458 background_area.y = y;
14459 background_area.height = gtk_tree_view_get_row_height (tree_view, node);
14461 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
14463 surface = gdk_window_create_similar_surface (tree_view->priv->bin_window,
14464 CAIRO_CONTENT_COLOR,
14465 bin_window_width + 2,
14466 background_area.height + 2);
14468 cr = cairo_create (surface);
14469 gdk_cairo_set_source_color (cr, &style->base [gtk_widget_get_state (widget)]);
14472 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
14474 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
14476 list = (rtl ? list->prev : list->next))
14478 GtkTreeViewColumn *column = list->data;
14479 GdkRectangle cell_area;
14480 gint vertical_separator;
14482 if (!gtk_tree_view_column_get_visible (column))
14485 gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter,
14486 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
14487 node->children?TRUE:FALSE);
14489 background_area.x = cell_offset;
14490 background_area.width = gtk_tree_view_column_get_width (column);
14492 gtk_widget_style_get (widget,
14493 "vertical-separator", &vertical_separator,
14496 cell_area = background_area;
14498 cell_area.y += vertical_separator / 2;
14499 cell_area.height -= vertical_separator;
14501 if (gtk_tree_view_is_expander_column (tree_view, column))
14504 cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
14505 cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
14507 if (gtk_tree_view_draw_expanders (tree_view))
14510 cell_area.x += depth * tree_view->priv->expander_size;
14511 cell_area.width -= depth * tree_view->priv->expander_size;
14515 if (gtk_tree_view_column_cell_is_visible (column))
14518 gtk_paint_hline (style,
14524 cell_area.x + cell_area.width,
14525 cell_area.y + cell_area.height / 2);
14527 _gtk_tree_view_column_cell_render (column,
14533 cell_offset += gtk_tree_view_column_get_width (column);
14536 cairo_set_source_rgb (cr, 0, 0, 0);
14537 cairo_rectangle (cr,
14539 bin_window_width + 1,
14540 background_area.height + 1);
14541 cairo_set_line_width (cr, 1.0);
14544 cairo_destroy (cr);
14546 cairo_surface_set_device_offset (surface, 2, 2);
14553 * gtk_tree_view_set_destroy_count_func:
14554 * @tree_view: A #GtkTreeView
14555 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
14556 * @data: (allow-none): User data to be passed to @func, or %NULL
14557 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
14559 * This function should almost never be used. It is meant for private use by
14560 * ATK for determining the number of visible children that are removed when the
14561 * user collapses a row, or a row is deleted.
14564 gtk_tree_view_set_destroy_count_func (GtkTreeView *tree_view,
14565 GtkTreeDestroyCountFunc func,
14567 GDestroyNotify destroy)
14569 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14571 if (tree_view->priv->destroy_count_destroy)
14572 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
14574 tree_view->priv->destroy_count_func = func;
14575 tree_view->priv->destroy_count_data = data;
14576 tree_view->priv->destroy_count_destroy = destroy;
14581 * Interactive search
14585 * gtk_tree_view_set_enable_search:
14586 * @tree_view: A #GtkTreeView
14587 * @enable_search: %TRUE, if the user can search interactively
14589 * If @enable_search is set, then the user can type in text to search through
14590 * the tree interactively (this is sometimes called "typeahead find").
14592 * Note that even if this is %FALSE, the user can still initiate a search
14593 * using the "start-interactive-search" key binding.
14596 gtk_tree_view_set_enable_search (GtkTreeView *tree_view,
14597 gboolean enable_search)
14599 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14601 enable_search = !!enable_search;
14603 if (tree_view->priv->enable_search != enable_search)
14605 tree_view->priv->enable_search = enable_search;
14606 g_object_notify (G_OBJECT (tree_view), "enable-search");
14611 * gtk_tree_view_get_enable_search:
14612 * @tree_view: A #GtkTreeView
14614 * Returns whether or not the tree allows to start interactive searching
14615 * by typing in text.
14617 * Return value: whether or not to let the user search interactively
14620 gtk_tree_view_get_enable_search (GtkTreeView *tree_view)
14622 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14624 return tree_view->priv->enable_search;
14629 * gtk_tree_view_get_search_column:
14630 * @tree_view: A #GtkTreeView
14632 * Gets the column searched on by the interactive search code.
14634 * Return value: the column the interactive search code searches in.
14637 gtk_tree_view_get_search_column (GtkTreeView *tree_view)
14639 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
14641 return (tree_view->priv->search_column);
14645 * gtk_tree_view_set_search_column:
14646 * @tree_view: A #GtkTreeView
14647 * @column: the column of the model to search in, or -1 to disable searching
14649 * Sets @column as the column where the interactive search code should
14650 * search in for the current model.
14652 * If the search column is set, users can use the "start-interactive-search"
14653 * key binding to bring up search popup. The enable-search property controls
14654 * whether simply typing text will also start an interactive search.
14656 * Note that @column refers to a column of the current model. The search
14657 * column is reset to -1 when the model is changed.
14660 gtk_tree_view_set_search_column (GtkTreeView *tree_view,
14663 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14664 g_return_if_fail (column >= -1);
14666 if (tree_view->priv->search_column == column)
14669 tree_view->priv->search_column = column;
14670 g_object_notify (G_OBJECT (tree_view), "search-column");
14674 * gtk_tree_view_get_search_equal_func:
14675 * @tree_view: A #GtkTreeView
14677 * Returns the compare function currently in use.
14679 * Return value: the currently used compare function for the search code.
14682 GtkTreeViewSearchEqualFunc
14683 gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view)
14685 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14687 return tree_view->priv->search_equal_func;
14691 * gtk_tree_view_set_search_equal_func:
14692 * @tree_view: A #GtkTreeView
14693 * @search_equal_func: the compare function to use during the search
14694 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
14695 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
14697 * Sets the compare function for the interactive search capabilities; note
14698 * that somewhat like strcmp() returning 0 for equality
14699 * #GtkTreeViewSearchEqualFunc returns %FALSE on matches.
14702 gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view,
14703 GtkTreeViewSearchEqualFunc search_equal_func,
14704 gpointer search_user_data,
14705 GDestroyNotify search_destroy)
14707 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14708 g_return_if_fail (search_equal_func != NULL);
14710 if (tree_view->priv->search_destroy)
14711 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
14713 tree_view->priv->search_equal_func = search_equal_func;
14714 tree_view->priv->search_user_data = search_user_data;
14715 tree_view->priv->search_destroy = search_destroy;
14716 if (tree_view->priv->search_equal_func == NULL)
14717 tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func;
14721 * gtk_tree_view_get_search_entry:
14722 * @tree_view: A #GtkTreeView
14724 * Returns the #GtkEntry which is currently in use as interactive search
14725 * entry for @tree_view. In case the built-in entry is being used, %NULL
14726 * will be returned.
14728 * Return value: (transfer none): the entry currently in use as search entry.
14733 gtk_tree_view_get_search_entry (GtkTreeView *tree_view)
14735 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14737 if (tree_view->priv->search_custom_entry_set)
14738 return GTK_ENTRY (tree_view->priv->search_entry);
14744 * gtk_tree_view_set_search_entry:
14745 * @tree_view: A #GtkTreeView
14746 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
14748 * Sets the entry which the interactive search code will use for this
14749 * @tree_view. This is useful when you want to provide a search entry
14750 * in our interface at all time at a fixed position. Passing %NULL for
14751 * @entry will make the interactive search code use the built-in popup
14757 gtk_tree_view_set_search_entry (GtkTreeView *tree_view,
14760 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14761 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
14763 if (tree_view->priv->search_custom_entry_set)
14765 if (tree_view->priv->search_entry_changed_id)
14767 g_signal_handler_disconnect (tree_view->priv->search_entry,
14768 tree_view->priv->search_entry_changed_id);
14769 tree_view->priv->search_entry_changed_id = 0;
14771 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
14772 G_CALLBACK (gtk_tree_view_search_key_press_event),
14775 g_object_unref (tree_view->priv->search_entry);
14777 else if (tree_view->priv->search_window)
14779 gtk_widget_destroy (tree_view->priv->search_window);
14781 tree_view->priv->search_window = NULL;
14786 tree_view->priv->search_entry = g_object_ref (entry);
14787 tree_view->priv->search_custom_entry_set = TRUE;
14789 if (tree_view->priv->search_entry_changed_id == 0)
14791 tree_view->priv->search_entry_changed_id =
14792 g_signal_connect (tree_view->priv->search_entry, "changed",
14793 G_CALLBACK (gtk_tree_view_search_init),
14797 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
14798 G_CALLBACK (gtk_tree_view_search_key_press_event),
14801 gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
14805 tree_view->priv->search_entry = NULL;
14806 tree_view->priv->search_custom_entry_set = FALSE;
14811 * gtk_tree_view_set_search_position_func:
14812 * @tree_view: A #GtkTreeView
14813 * @func: (allow-none): the function to use to position the search dialog, or %NULL
14814 * to use the default search position function
14815 * @data: (allow-none): user data to pass to @func, or %NULL
14816 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
14818 * Sets the function to use when positioning the search dialog.
14823 gtk_tree_view_set_search_position_func (GtkTreeView *tree_view,
14824 GtkTreeViewSearchPositionFunc func,
14825 gpointer user_data,
14826 GDestroyNotify destroy)
14828 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14830 if (tree_view->priv->search_position_destroy)
14831 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
14833 tree_view->priv->search_position_func = func;
14834 tree_view->priv->search_position_user_data = user_data;
14835 tree_view->priv->search_position_destroy = destroy;
14836 if (tree_view->priv->search_position_func == NULL)
14837 tree_view->priv->search_position_func = gtk_tree_view_search_position_func;
14841 * gtk_tree_view_get_search_position_func:
14842 * @tree_view: A #GtkTreeView
14844 * Returns the positioning function currently in use.
14846 * Return value: the currently used function for positioning the search dialog.
14850 GtkTreeViewSearchPositionFunc
14851 gtk_tree_view_get_search_position_func (GtkTreeView *tree_view)
14853 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14855 return tree_view->priv->search_position_func;
14860 gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog,
14861 GtkTreeView *tree_view,
14864 if (tree_view->priv->disable_popdown)
14867 if (tree_view->priv->search_entry_changed_id)
14869 g_signal_handler_disconnect (tree_view->priv->search_entry,
14870 tree_view->priv->search_entry_changed_id);
14871 tree_view->priv->search_entry_changed_id = 0;
14873 if (tree_view->priv->typeselect_flush_timeout)
14875 g_source_remove (tree_view->priv->typeselect_flush_timeout);
14876 tree_view->priv->typeselect_flush_timeout = 0;
14879 if (gtk_widget_get_visible (search_dialog))
14881 /* send focus-in event */
14882 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), device, FALSE);
14883 gtk_widget_hide (search_dialog);
14884 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
14885 send_focus_change (GTK_WIDGET (tree_view), device, TRUE);
14890 gtk_tree_view_search_position_func (GtkTreeView *tree_view,
14891 GtkWidget *search_dialog,
14892 gpointer user_data)
14895 gint tree_x, tree_y;
14896 gint tree_width, tree_height;
14897 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
14898 GdkScreen *screen = gdk_window_get_screen (tree_window);
14899 GtkRequisition requisition;
14901 GdkRectangle monitor;
14903 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
14904 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
14906 gtk_widget_realize (search_dialog);
14908 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
14909 tree_width = gdk_window_get_width (tree_window);
14910 tree_height = gdk_window_get_height (tree_window);
14911 gtk_widget_get_preferred_size (search_dialog, &requisition, NULL);
14913 if (tree_x + tree_width > gdk_screen_get_width (screen))
14914 x = gdk_screen_get_width (screen) - requisition.width;
14915 else if (tree_x + tree_width - requisition.width < 0)
14918 x = tree_x + tree_width - requisition.width;
14920 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
14921 y = gdk_screen_get_height (screen) - requisition.height;
14922 else if (tree_y + tree_height < 0) /* isn't really possible ... */
14925 y = tree_y + tree_height;
14927 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
14931 gtk_tree_view_search_disable_popdown (GtkEntry *entry,
14935 GtkTreeView *tree_view = (GtkTreeView *)data;
14937 tree_view->priv->disable_popdown = 1;
14938 g_signal_connect (menu, "hide",
14939 G_CALLBACK (gtk_tree_view_search_enable_popdown), data);
14942 /* Because we're visible but offscreen, we just set a flag in the preedit
14946 gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
14947 GtkTreeView *tree_view)
14949 tree_view->priv->imcontext_changed = 1;
14950 if (tree_view->priv->typeselect_flush_timeout)
14952 g_source_remove (tree_view->priv->typeselect_flush_timeout);
14953 tree_view->priv->typeselect_flush_timeout =
14954 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
14955 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
14962 gtk_tree_view_search_activate (GtkEntry *entry,
14963 GtkTreeView *tree_view)
14969 gtk_tree_view_search_dialog_hide (tree_view->priv->search_window,
14971 gtk_get_current_event_device ());
14973 /* If we have a row selected and it's the cursor row, we activate
14975 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
14977 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
14979 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
14981 if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
14982 gtk_tree_view_row_activated (tree_view, path, tree_view->priv->focus_column);
14984 gtk_tree_path_free (path);
14989 gtk_tree_view_real_search_enable_popdown (gpointer data)
14991 GtkTreeView *tree_view = (GtkTreeView *)data;
14993 tree_view->priv->disable_popdown = 0;
14999 gtk_tree_view_search_enable_popdown (GtkWidget *widget,
15002 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, gtk_tree_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
15006 gtk_tree_view_search_delete_event (GtkWidget *widget,
15007 GdkEventAny *event,
15008 GtkTreeView *tree_view)
15010 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
15012 gtk_tree_view_search_dialog_hide (widget, tree_view, NULL);
15018 gtk_tree_view_search_button_press_event (GtkWidget *widget,
15019 GdkEventButton *event,
15020 GtkTreeView *tree_view)
15022 GdkDevice *keyb_device;
15024 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
15026 keyb_device = gdk_device_get_associated_device (event->device);
15027 gtk_tree_view_search_dialog_hide (widget, tree_view, keyb_device);
15029 if (event->window == tree_view->priv->bin_window)
15030 gtk_tree_view_button_press (GTK_WIDGET (tree_view), event);
15036 gtk_tree_view_search_scroll_event (GtkWidget *widget,
15037 GdkEventScroll *event,
15038 GtkTreeView *tree_view)
15040 gboolean retval = FALSE;
15042 if (event->direction == GDK_SCROLL_UP)
15044 gtk_tree_view_search_move (widget, tree_view, TRUE);
15047 else if (event->direction == GDK_SCROLL_DOWN)
15049 gtk_tree_view_search_move (widget, tree_view, FALSE);
15053 /* renew the flush timeout */
15054 if (retval && tree_view->priv->typeselect_flush_timeout
15055 && !tree_view->priv->search_custom_entry_set)
15057 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15058 tree_view->priv->typeselect_flush_timeout =
15059 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15060 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15068 gtk_tree_view_search_key_press_event (GtkWidget *widget,
15069 GdkEventKey *event,
15070 GtkTreeView *tree_view)
15072 gboolean retval = FALSE;
15074 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
15075 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15077 /* close window and cancel the search */
15078 if (!tree_view->priv->search_custom_entry_set
15079 && (event->keyval == GDK_KEY_Escape ||
15080 event->keyval == GDK_KEY_Tab ||
15081 event->keyval == GDK_KEY_KP_Tab ||
15082 event->keyval == GDK_KEY_ISO_Left_Tab))
15084 gtk_tree_view_search_dialog_hide (widget, tree_view,
15085 gdk_event_get_device ((GdkEvent *) event));
15089 /* select previous matching iter */
15090 if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
15092 if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
15093 gtk_widget_error_bell (widget);
15098 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
15099 && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
15101 if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
15102 gtk_widget_error_bell (widget);
15107 /* select next matching iter */
15108 if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
15110 if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
15111 gtk_widget_error_bell (widget);
15116 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
15117 && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
15119 if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
15120 gtk_widget_error_bell (widget);
15125 /* renew the flush timeout */
15126 if (retval && tree_view->priv->typeselect_flush_timeout
15127 && !tree_view->priv->search_custom_entry_set)
15129 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15130 tree_view->priv->typeselect_flush_timeout =
15131 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15132 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15139 /* this function returns FALSE if there is a search string but
15140 * nothing was found, and TRUE otherwise.
15143 gtk_tree_view_search_move (GtkWidget *window,
15144 GtkTreeView *tree_view,
15152 GtkTreeModel *model;
15153 GtkTreeSelection *selection;
15155 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
15157 g_return_val_if_fail (text != NULL, FALSE);
15159 len = strlen (text);
15161 if (up && tree_view->priv->selected_iter == 1)
15162 return strlen (text) < 1;
15164 len = strlen (text);
15169 model = gtk_tree_view_get_model (tree_view);
15170 selection = gtk_tree_view_get_selection (tree_view);
15173 gtk_tree_selection_unselect_all (selection);
15174 if (!gtk_tree_model_get_iter_first (model, &iter))
15177 ret = gtk_tree_view_search_iter (model, selection, &iter, text,
15178 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
15183 tree_view->priv->selected_iter += up?(-1):(1);
15188 /* return to old iter */
15190 gtk_tree_model_get_iter_first (model, &iter);
15191 gtk_tree_view_search_iter (model, selection,
15193 &count, tree_view->priv->selected_iter);
15199 gtk_tree_view_search_equal_func (GtkTreeModel *model,
15203 gpointer search_data)
15205 gboolean retval = TRUE;
15207 gchar *normalized_string;
15208 gchar *normalized_key;
15209 gchar *case_normalized_string = NULL;
15210 gchar *case_normalized_key = NULL;
15211 GValue value = {0,};
15212 GValue transformed = {0,};
15214 gtk_tree_model_get_value (model, iter, column, &value);
15216 g_value_init (&transformed, G_TYPE_STRING);
15218 if (!g_value_transform (&value, &transformed))
15220 g_value_unset (&value);
15224 g_value_unset (&value);
15226 str = g_value_get_string (&transformed);
15229 g_value_unset (&transformed);
15233 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
15234 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
15236 if (normalized_string && normalized_key)
15238 case_normalized_string = g_utf8_casefold (normalized_string, -1);
15239 case_normalized_key = g_utf8_casefold (normalized_key, -1);
15241 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
15245 g_value_unset (&transformed);
15246 g_free (normalized_key);
15247 g_free (normalized_string);
15248 g_free (case_normalized_key);
15249 g_free (case_normalized_string);
15255 gtk_tree_view_search_iter (GtkTreeModel *model,
15256 GtkTreeSelection *selection,
15262 GtkRBTree *tree = NULL;
15263 GtkRBNode *node = NULL;
15266 GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
15268 path = gtk_tree_model_get_path (model, iter);
15269 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
15273 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
15278 gtk_tree_view_scroll_to_cell (tree_view, path, NULL,
15280 gtk_tree_selection_select_iter (selection, iter);
15281 gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
15284 gtk_tree_path_free (path);
15290 if (node->children)
15292 gboolean has_child;
15295 tree = node->children;
15298 while (node->left != tree->nil)
15302 has_child = gtk_tree_model_iter_children (model, iter, &tmp);
15303 gtk_tree_path_down (path);
15306 TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
15310 gboolean done = FALSE;
15314 node = _gtk_rbtree_next (tree, node);
15320 has_next = gtk_tree_model_iter_next (model, iter);
15323 gtk_tree_path_next (path);
15326 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
15330 gboolean has_parent;
15331 GtkTreeIter tmp_iter = *iter;
15333 node = tree->parent_node;
15334 tree = tree->parent_tree;
15339 gtk_tree_path_free (path);
15341 /* we've run out of tree, done with this func */
15345 has_parent = gtk_tree_model_iter_parent (model,
15348 gtk_tree_path_up (path);
15351 TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
15363 gtk_tree_view_search_init (GtkWidget *entry,
15364 GtkTreeView *tree_view)
15370 GtkTreeModel *model;
15371 GtkTreeSelection *selection;
15373 g_return_if_fail (GTK_IS_ENTRY (entry));
15374 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15376 text = gtk_entry_get_text (GTK_ENTRY (entry));
15378 model = gtk_tree_view_get_model (tree_view);
15379 selection = gtk_tree_view_get_selection (tree_view);
15382 gtk_tree_selection_unselect_all (selection);
15383 if (tree_view->priv->typeselect_flush_timeout
15384 && !tree_view->priv->search_custom_entry_set)
15386 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15387 tree_view->priv->typeselect_flush_timeout =
15388 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15389 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15396 if (!gtk_tree_model_get_iter_first (model, &iter))
15399 ret = gtk_tree_view_search_iter (model, selection,
15404 tree_view->priv->selected_iter = 1;
15408 _gtk_tree_view_remove_editable (GtkTreeView *tree_view,
15409 GtkTreeViewColumn *column,
15410 GtkCellEditable *cell_editable)
15412 if (tree_view->priv->edited_column == NULL)
15415 g_return_if_fail (column == tree_view->priv->edited_column);
15417 tree_view->priv->edited_column = NULL;
15419 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
15420 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
15422 gtk_container_remove (GTK_CONTAINER (tree_view),
15423 GTK_WIDGET (cell_editable));
15425 /* FIXME should only redraw a single node */
15426 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15430 gtk_tree_view_start_editing (GtkTreeView *tree_view,
15431 GtkTreePath *cursor_path,
15432 gboolean edit_only)
15435 GdkRectangle cell_area;
15436 GtkTreeViewColumn *focus_column;
15437 gchar *path_string;
15438 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
15439 gint retval = FALSE;
15440 GtkRBTree *cursor_tree;
15441 GtkRBNode *cursor_node;
15443 g_assert (tree_view->priv->focus_column);
15444 focus_column = tree_view->priv->focus_column;
15446 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
15449 if (_gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node) ||
15450 cursor_node == NULL)
15453 path_string = gtk_tree_path_to_string (cursor_path);
15454 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
15456 validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path);
15458 gtk_tree_view_column_cell_set_cell_data (focus_column,
15459 tree_view->priv->model,
15461 GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
15462 cursor_node->children?TRUE:FALSE);
15463 gtk_tree_view_get_cell_area (tree_view,
15468 if (gtk_cell_area_activate (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (focus_column)),
15469 _gtk_tree_view_column_get_context (focus_column),
15470 GTK_WIDGET (tree_view),
15479 _gtk_tree_view_add_editable (GtkTreeView *tree_view,
15480 GtkTreeViewColumn *column,
15482 GtkCellEditable *cell_editable,
15483 GdkRectangle *cell_area)
15485 gint pre_val = tree_view->priv->vadjustment->value;
15486 GtkRequisition requisition;
15488 tree_view->priv->edited_column = column;
15490 gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
15491 cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
15493 gtk_widget_get_preferred_size (GTK_WIDGET (cell_editable),
15494 &requisition, NULL);
15496 tree_view->priv->draw_keyfocus = TRUE;
15498 if (requisition.height < cell_area->height)
15500 gint diff = cell_area->height - requisition.height;
15501 gtk_tree_view_put (tree_view,
15502 GTK_WIDGET (cell_editable),
15503 cell_area->x, cell_area->y + diff/2,
15504 cell_area->width, requisition.height);
15508 gtk_tree_view_put (tree_view,
15509 GTK_WIDGET (cell_editable),
15510 cell_area->x, cell_area->y,
15511 cell_area->width, cell_area->height);
15516 gtk_tree_view_stop_editing (GtkTreeView *tree_view,
15517 gboolean cancel_editing)
15519 GtkTreeViewColumn *column;
15521 if (tree_view->priv->edited_column == NULL)
15525 * This is very evil. We need to do this, because
15526 * gtk_cell_editable_editing_done may trigger gtk_tree_view_row_changed
15527 * later on. If gtk_tree_view_row_changed notices
15528 * tree_view->priv->edited_column != NULL, it'll call
15529 * gtk_tree_view_stop_editing again. Bad things will happen then.
15531 * Please read that again if you intend to modify anything here.
15534 column = tree_view->priv->edited_column;
15535 gtk_cell_area_stop_editing (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)), cancel_editing);
15536 tree_view->priv->edited_column = NULL;
15541 * gtk_tree_view_set_hover_selection:
15542 * @tree_view: a #GtkTreeView
15543 * @hover: %TRUE to enable hover selection mode
15545 * Enables of disables the hover selection mode of @tree_view.
15546 * Hover selection makes the selected row follow the pointer.
15547 * Currently, this works only for the selection modes
15548 * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
15553 gtk_tree_view_set_hover_selection (GtkTreeView *tree_view,
15556 hover = hover != FALSE;
15558 if (hover != tree_view->priv->hover_selection)
15560 tree_view->priv->hover_selection = hover;
15562 g_object_notify (G_OBJECT (tree_view), "hover-selection");
15567 * gtk_tree_view_get_hover_selection:
15568 * @tree_view: a #GtkTreeView
15570 * Returns whether hover selection mode is turned on for @tree_view.
15572 * Return value: %TRUE if @tree_view is in hover selection mode
15577 gtk_tree_view_get_hover_selection (GtkTreeView *tree_view)
15579 return tree_view->priv->hover_selection;
15583 * gtk_tree_view_set_hover_expand:
15584 * @tree_view: a #GtkTreeView
15585 * @expand: %TRUE to enable hover selection mode
15587 * Enables of disables the hover expansion mode of @tree_view.
15588 * Hover expansion makes rows expand or collapse if the pointer
15594 gtk_tree_view_set_hover_expand (GtkTreeView *tree_view,
15597 expand = expand != FALSE;
15599 if (expand != tree_view->priv->hover_expand)
15601 tree_view->priv->hover_expand = expand;
15603 g_object_notify (G_OBJECT (tree_view), "hover-expand");
15608 * gtk_tree_view_get_hover_expand:
15609 * @tree_view: a #GtkTreeView
15611 * Returns whether hover expansion mode is turned on for @tree_view.
15613 * Return value: %TRUE if @tree_view is in hover expansion mode
15618 gtk_tree_view_get_hover_expand (GtkTreeView *tree_view)
15620 return tree_view->priv->hover_expand;
15624 * gtk_tree_view_set_rubber_banding:
15625 * @tree_view: a #GtkTreeView
15626 * @enable: %TRUE to enable rubber banding
15628 * Enables or disables rubber banding in @tree_view. If the selection mode
15629 * is #GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select
15630 * multiple rows by dragging the mouse.
15635 gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view,
15638 enable = enable != FALSE;
15640 if (enable != tree_view->priv->rubber_banding_enable)
15642 tree_view->priv->rubber_banding_enable = enable;
15644 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
15649 * gtk_tree_view_get_rubber_banding:
15650 * @tree_view: a #GtkTreeView
15652 * Returns whether rubber banding is turned on for @tree_view. If the
15653 * selection mode is #GTK_SELECTION_MULTIPLE, rubber banding will allow the
15654 * user to select multiple rows by dragging the mouse.
15656 * Return value: %TRUE if rubber banding in @tree_view is enabled.
15661 gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view)
15663 return tree_view->priv->rubber_banding_enable;
15667 * gtk_tree_view_is_rubber_banding_active:
15668 * @tree_view: a #GtkTreeView
15670 * Returns whether a rubber banding operation is currently being done
15673 * Return value: %TRUE if a rubber banding operation is currently being
15674 * done in @tree_view.
15679 gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view)
15681 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15683 if (tree_view->priv->rubber_banding_enable
15684 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
15691 * gtk_tree_view_get_row_separator_func:
15692 * @tree_view: a #GtkTreeView
15694 * Returns the current row separator function.
15696 * Return value: the current row separator function.
15700 GtkTreeViewRowSeparatorFunc
15701 gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view)
15703 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
15705 return tree_view->priv->row_separator_func;
15709 * gtk_tree_view_set_row_separator_func:
15710 * @tree_view: a #GtkTreeView
15711 * @func: (allow-none): a #GtkTreeViewRowSeparatorFunc
15712 * @data: (allow-none): user data to pass to @func, or %NULL
15713 * @destroy: (allow-none): destroy notifier for @data, or %NULL
15715 * Sets the row separator function, which is used to determine
15716 * whether a row should be drawn as a separator. If the row separator
15717 * function is %NULL, no separators are drawn. This is the default value.
15722 gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view,
15723 GtkTreeViewRowSeparatorFunc func,
15725 GDestroyNotify destroy)
15727 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15729 if (tree_view->priv->row_separator_destroy)
15730 tree_view->priv->row_separator_destroy (tree_view->priv->row_separator_data);
15732 tree_view->priv->row_separator_func = func;
15733 tree_view->priv->row_separator_data = data;
15734 tree_view->priv->row_separator_destroy = destroy;
15736 /* Have the tree recalculate heights */
15737 _gtk_rbtree_mark_invalid (tree_view->priv->tree);
15738 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
15743 gtk_tree_view_grab_notify (GtkWidget *widget,
15744 gboolean was_grabbed)
15746 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
15748 tree_view->priv->in_grab = !was_grabbed;
15752 tree_view->priv->pressed_button = -1;
15754 if (tree_view->priv->rubber_band_status)
15755 gtk_tree_view_stop_rubber_band (tree_view);
15760 gtk_tree_view_state_changed (GtkWidget *widget,
15761 GtkStateType previous_state)
15763 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
15765 if (gtk_widget_get_realized (widget))
15767 gdk_window_set_background (tree_view->priv->bin_window,
15768 >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
15771 gtk_widget_queue_draw (widget);
15775 * gtk_tree_view_get_grid_lines:
15776 * @tree_view: a #GtkTreeView
15778 * Returns which grid lines are enabled in @tree_view.
15780 * Return value: a #GtkTreeViewGridLines value indicating which grid lines
15785 GtkTreeViewGridLines
15786 gtk_tree_view_get_grid_lines (GtkTreeView *tree_view)
15788 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
15790 return tree_view->priv->grid_lines;
15794 * gtk_tree_view_set_grid_lines:
15795 * @tree_view: a #GtkTreeView
15796 * @grid_lines: a #GtkTreeViewGridLines value indicating which grid lines to
15799 * Sets which grid lines to draw in @tree_view.
15804 gtk_tree_view_set_grid_lines (GtkTreeView *tree_view,
15805 GtkTreeViewGridLines grid_lines)
15807 GtkTreeViewPrivate *priv;
15809 GtkTreeViewGridLines old_grid_lines;
15811 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15813 priv = tree_view->priv;
15814 widget = GTK_WIDGET (tree_view);
15816 old_grid_lines = priv->grid_lines;
15817 priv->grid_lines = grid_lines;
15819 if (gtk_widget_get_realized (widget))
15821 if (grid_lines == GTK_TREE_VIEW_GRID_LINES_NONE &&
15822 priv->grid_line_width)
15824 priv->grid_line_width = 0;
15827 if (grid_lines != GTK_TREE_VIEW_GRID_LINES_NONE &&
15828 !priv->grid_line_width)
15832 gtk_widget_style_get (widget,
15833 "grid-line-width", &priv->grid_line_width,
15834 "grid-line-pattern", (gchar *)&dash_list,
15839 priv->grid_line_dashes[0] = dash_list[0];
15841 priv->grid_line_dashes[1] = dash_list[1];
15843 g_free (dash_list);
15847 priv->grid_line_dashes[0] = 1;
15848 priv->grid_line_dashes[1] = 1;
15853 if (old_grid_lines != grid_lines)
15855 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15857 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
15862 * gtk_tree_view_get_enable_tree_lines:
15863 * @tree_view: a #GtkTreeView.
15865 * Returns whether or not tree lines are drawn in @tree_view.
15867 * Return value: %TRUE if tree lines are drawn in @tree_view, %FALSE
15873 gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view)
15875 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15877 return tree_view->priv->tree_lines_enabled;
15881 * gtk_tree_view_set_enable_tree_lines:
15882 * @tree_view: a #GtkTreeView
15883 * @enabled: %TRUE to enable tree line drawing, %FALSE otherwise.
15885 * Sets whether to draw lines interconnecting the expanders in @tree_view.
15886 * This does not have any visible effects for lists.
15891 gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view,
15894 GtkTreeViewPrivate *priv;
15896 gboolean was_enabled;
15898 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15900 enabled = enabled != FALSE;
15902 priv = tree_view->priv;
15903 widget = GTK_WIDGET (tree_view);
15905 was_enabled = priv->tree_lines_enabled;
15907 priv->tree_lines_enabled = enabled;
15909 if (gtk_widget_get_realized (widget))
15911 if (!enabled && priv->tree_line_width)
15913 priv->tree_line_width = 0;
15916 if (enabled && !priv->tree_line_width)
15919 gtk_widget_style_get (widget,
15920 "tree-line-width", &priv->tree_line_width,
15921 "tree-line-pattern", (gchar *)&dash_list,
15926 priv->tree_line_dashes[0] = dash_list[0];
15928 priv->tree_line_dashes[1] = dash_list[1];
15930 g_free (dash_list);
15934 priv->tree_line_dashes[0] = 1;
15935 priv->tree_line_dashes[1] = 1;
15940 if (was_enabled != enabled)
15942 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15944 g_object_notify (G_OBJECT (tree_view), "enable-tree-lines");
15950 * gtk_tree_view_set_show_expanders:
15951 * @tree_view: a #GtkTreeView
15952 * @enabled: %TRUE to enable expander drawing, %FALSE otherwise.
15954 * Sets whether to draw and enable expanders and indent child rows in
15955 * @tree_view. When disabled there will be no expanders visible in trees
15956 * and there will be no way to expand and collapse rows by default. Also
15957 * note that hiding the expanders will disable the default indentation. You
15958 * can set a custom indentation in this case using
15959 * gtk_tree_view_set_level_indentation().
15960 * This does not have any visible effects for lists.
15965 gtk_tree_view_set_show_expanders (GtkTreeView *tree_view,
15968 gboolean was_enabled;
15970 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15972 enabled = enabled != FALSE;
15973 was_enabled = tree_view->priv->show_expanders;
15975 tree_view->priv->show_expanders = enabled == TRUE;
15977 if (enabled != was_enabled)
15978 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15982 * gtk_tree_view_get_show_expanders:
15983 * @tree_view: a #GtkTreeView.
15985 * Returns whether or not expanders are drawn in @tree_view.
15987 * Return value: %TRUE if expanders are drawn in @tree_view, %FALSE
15993 gtk_tree_view_get_show_expanders (GtkTreeView *tree_view)
15995 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15997 return tree_view->priv->show_expanders;
16001 * gtk_tree_view_set_level_indentation:
16002 * @tree_view: a #GtkTreeView
16003 * @indentation: the amount, in pixels, of extra indentation in @tree_view.
16005 * Sets the amount of extra indentation for child levels to use in @tree_view
16006 * in addition to the default indentation. The value should be specified in
16007 * pixels, a value of 0 disables this feature and in this case only the default
16008 * indentation will be used.
16009 * This does not have any visible effects for lists.
16014 gtk_tree_view_set_level_indentation (GtkTreeView *tree_view,
16017 tree_view->priv->level_indentation = indentation;
16019 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
16023 * gtk_tree_view_get_level_indentation:
16024 * @tree_view: a #GtkTreeView.
16026 * Returns the amount, in pixels, of extra indentation for child levels
16029 * Return value: the amount of extra indentation for child levels in
16030 * @tree_view. A return value of 0 means that this feature is disabled.
16035 gtk_tree_view_get_level_indentation (GtkTreeView *tree_view)
16037 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
16039 return tree_view->priv->level_indentation;
16043 * gtk_tree_view_set_tooltip_row:
16044 * @tree_view: a #GtkTreeView
16045 * @tooltip: a #GtkTooltip
16046 * @path: a #GtkTreePath
16048 * Sets the tip area of @tooltip to be the area covered by the row at @path.
16049 * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
16050 * See also gtk_tooltip_set_tip_area().
16055 gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view,
16056 GtkTooltip *tooltip,
16059 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16060 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
16062 gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
16066 * gtk_tree_view_set_tooltip_cell:
16067 * @tree_view: a #GtkTreeView
16068 * @tooltip: a #GtkTooltip
16069 * @path: (allow-none): a #GtkTreePath or %NULL
16070 * @column: (allow-none): a #GtkTreeViewColumn or %NULL
16071 * @cell: (allow-none): a #GtkCellRenderer or %NULL
16073 * Sets the tip area of @tooltip to the area @path, @column and @cell have
16074 * in common. For example if @path is %NULL and @column is set, the tip
16075 * area will be set to the full area covered by @column. See also
16076 * gtk_tooltip_set_tip_area().
16078 * Note that if @path is not specified and @cell is set and part of a column
16079 * containing the expander, the tooltip might not show and hide at the correct
16080 * position. In such cases @path must be set to the current node under the
16081 * mouse cursor for this function to operate correctly.
16083 * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
16088 gtk_tree_view_set_tooltip_cell (GtkTreeView *tree_view,
16089 GtkTooltip *tooltip,
16091 GtkTreeViewColumn *column,
16092 GtkCellRenderer *cell)
16094 GtkAllocation allocation;
16097 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16098 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
16099 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
16100 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
16102 /* Determine x values. */
16103 if (column && cell)
16108 /* We always pass in path here, whether it is NULL or not.
16109 * For cells in expander columns path must be specified so that
16110 * we can correctly account for the indentation. This also means
16111 * that the tooltip is constrained vertically by the "Determine y
16112 * values" code below; this is not a real problem since cells actually
16113 * don't stretch vertically in constrast to columns.
16115 gtk_tree_view_get_cell_area (tree_view, path, column, &tmp);
16116 gtk_tree_view_column_cell_get_position (column, cell, &start, &width);
16118 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
16121 rect.width = width;
16127 gtk_tree_view_get_background_area (tree_view, NULL, column, &tmp);
16128 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
16131 rect.width = tmp.width;
16135 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
16137 rect.width = allocation.width;
16140 /* Determine y values. */
16145 gtk_tree_view_get_background_area (tree_view, path, NULL, &tmp);
16146 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
16149 rect.height = tmp.height;
16154 rect.height = tree_view->priv->vadjustment->page_size;
16157 gtk_tooltip_set_tip_area (tooltip, &rect);
16161 * gtk_tree_view_get_tooltip_context:
16162 * @tree_view: a #GtkTreeView
16163 * @x: the x coordinate (relative to widget coordinates)
16164 * @y: the y coordinate (relative to widget coordinates)
16165 * @keyboard_tip: whether this is a keyboard tooltip or not
16166 * @model: (out) (allow-none): a pointer to receive a #GtkTreeModel or %NULL
16167 * @path: (out) (allow-none): a pointer to receive a #GtkTreePath or %NULL
16168 * @iter: (out) (allow-none): a pointer to receive a #GtkTreeIter or %NULL
16170 * This function is supposed to be used in a #GtkWidget::query-tooltip
16171 * signal handler for #GtkTreeView. The @x, @y and @keyboard_tip values
16172 * which are received in the signal handler, should be passed to this
16173 * function without modification.
16175 * The return value indicates whether there is a tree view row at the given
16176 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
16177 * tooltips the row returned will be the cursor row. When %TRUE, then any of
16178 * @model, @path and @iter which have been provided will be set to point to
16179 * that row and the corresponding model. @x and @y will always be converted
16180 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
16182 * Return value: whether or not the given tooltip context points to a row.
16187 gtk_tree_view_get_tooltip_context (GtkTreeView *tree_view,
16190 gboolean keyboard_tip,
16191 GtkTreeModel **model,
16192 GtkTreePath **path,
16195 GtkTreePath *tmppath = NULL;
16197 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
16198 g_return_val_if_fail (x != NULL, FALSE);
16199 g_return_val_if_fail (y != NULL, FALSE);
16203 gtk_tree_view_get_cursor (tree_view, &tmppath, NULL);
16210 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
16213 if (!gtk_tree_view_get_path_at_pos (tree_view, *x, *y,
16214 &tmppath, NULL, NULL, NULL))
16219 *model = gtk_tree_view_get_model (tree_view);
16222 gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view),
16228 gtk_tree_path_free (tmppath);
16234 gtk_tree_view_set_tooltip_query_cb (GtkWidget *widget,
16237 gboolean keyboard_tip,
16238 GtkTooltip *tooltip,
16241 GValue value = { 0, };
16242 GValue transformed = { 0, };
16245 GtkTreeModel *model;
16246 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
16248 if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
16251 &model, &path, &iter))
16254 gtk_tree_model_get_value (model, &iter,
16255 tree_view->priv->tooltip_column, &value);
16257 g_value_init (&transformed, G_TYPE_STRING);
16259 if (!g_value_transform (&value, &transformed))
16261 g_value_unset (&value);
16262 gtk_tree_path_free (path);
16267 g_value_unset (&value);
16269 if (!g_value_get_string (&transformed))
16271 g_value_unset (&transformed);
16272 gtk_tree_path_free (path);
16277 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
16278 gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
16280 gtk_tree_path_free (path);
16281 g_value_unset (&transformed);
16287 * gtk_tree_view_set_tooltip_column:
16288 * @tree_view: a #GtkTreeView
16289 * @column: an integer, which is a valid column number for @tree_view's model
16291 * If you only plan to have simple (text-only) tooltips on full rows, you
16292 * can use this function to have #GtkTreeView handle these automatically
16293 * for you. @column should be set to the column in @tree_view's model
16294 * containing the tooltip texts, or -1 to disable this feature.
16296 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
16297 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
16299 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
16300 * so &, <, etc have to be escaped in the text.
16305 gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view,
16308 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16310 if (column == tree_view->priv->tooltip_column)
16315 g_signal_handlers_disconnect_by_func (tree_view,
16316 gtk_tree_view_set_tooltip_query_cb,
16318 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
16322 if (tree_view->priv->tooltip_column == -1)
16324 g_signal_connect (tree_view, "query-tooltip",
16325 G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL);
16326 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
16330 tree_view->priv->tooltip_column = column;
16331 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
16335 * gtk_tree_view_get_tooltip_column:
16336 * @tree_view: a #GtkTreeView
16338 * Returns the column of @tree_view's model which is being used for
16339 * displaying tooltips on @tree_view's rows.
16341 * Return value: the index of the tooltip column that is currently being
16342 * used, or -1 if this is disabled.
16347 gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view)
16349 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
16351 return tree_view->priv->tooltip_column;