1 /* gtktreeview.c
2  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
3  *
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.
8  *
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.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 #include "config.h"
20 
21 #include "gtktreeview.h"
22 
23 #include "gtkadjustmentprivate.h"
24 #include "gtkbox.h"
25 #include "gtkbuildable.h"
26 #include "gtkbutton.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrenderer.h"
29 #include "gtkcssnumbervalueprivate.h"
30 #include "gtkcsscolorvalueprivate.h"
31 #include "gtkdragsourceprivate.h"
32 #include "gtkdragicon.h"
33 #include "gtkdroptargetasync.h"
34 #include "gtkentryprivate.h"
35 #include "gtksearchentryprivate.h"
36 #include "gtkeventcontrollerkey.h"
37 #include "gtkeventcontrollerfocus.h"
38 #include "gtkeventcontrollermotion.h"
39 #include "gtkeventcontrollerscroll.h"
40 #include "gtkframe.h"
41 #include "gtkgesturedrag.h"
42 #include "gtkgestureclick.h"
43 #include "gtkgesturesingle.h"
44 #include "gtkintl.h"
45 #include "gtklabel.h"
46 #include "gtkmain.h"
47 #include "gtkmarshalers.h"
48 #include "gtkprivate.h"
49 #include "gtktext.h"
50 #include "gtktreerbtreeprivate.h"
51 #include "gtkrendericonprivate.h"
52 #include "gtkscrollable.h"
53 #include "gtksettingsprivate.h"
54 #include "gtkshortcutcontroller.h"
55 #include "gtksnapshot.h"
56 #include "gtkstylecontextprivate.h"
57 #include "gtktooltip.h"
58 #include "gtktreednd.h"
59 #include "gtktreemodelsort.h"
60 #include "gtktreeprivate.h"
61 #include "gtktypebuiltins.h"
62 #include "gtkwidgetprivate.h"
63 #include "gtkwindowgroup.h"
64 #include "gtknative.h"
65 #include "gtkpopover.h"
66 
67 #include "gdk/gdkeventsprivate.h"
68 #include "gdk/gdktextureprivate.h"
69 
70 #include <math.h>
71 #include <string.h>
72 
73 /**
74  * GtkTreeView:
75  *
76  * A widget for displaying both trees and lists
77  *
78  * Widget that displays any object that implements the [iface@Gtk.TreeModel] interface.
79  *
80  * Please refer to the [tree widget conceptual overview](section-tree-widget.html)
81  * for an overview of all the objects and data types related to the tree
82  * widget and how they work together.
83  *
84  * ## Coordinate systems in GtkTreeView API
85  *
86  * Several different coordinate systems are exposed in the `GtkTreeView` API.
87  * These are:
88  *
89  * ![](tree-view-coordinates.png)
90  *
91  * - Widget coordinates: Coordinates relative to the widget (usually `widget->window`).
92  *
93  * - Bin window coordinates: Coordinates relative to the window that GtkTreeView renders to.
94  *
95  * - Tree coordinates: Coordinates relative to the entire scrollable area of GtkTreeView. These
96  *   coordinates start at (0, 0) for row 0 of the tree.
97  *
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 [method@Gtk.TreeView.convert_widget_to_bin_window_coords]
102  * (and vice versa), for the latter [method@Gtk.TreeView.convert_bin_window_to_tree_coords]
103  * (and vice versa).
104  *
105  * ## `GtkTreeView` as `GtkBuildable`
106  *
107  * The `GtkTreeView` implementation of the `GtkBuildable` interface accepts
108  * [class@Gtk.TreeViewColumn] objects as `<child>` elements and exposes the
109  * internal [class@Gtk.TreeSelection] in UI definitions.
110  *
111  * An example of a UI definition fragment with `GtkTreeView`:
112  *
113  * ```xml
114  * <object class="GtkTreeView" id="treeview">
115  *   <property name="model">liststore1</property>
116  *   <child>
117  *     <object class="GtkTreeViewColumn" id="test-column">
118  *       <property name="title">Test</property>
119  *       <child>
120  *         <object class="GtkCellRendererText" id="test-renderer"/>
121  *         <attributes>
122  *           <attribute name="text">1</attribute>
123  *         </attributes>
124  *       </child>
125  *     </object>
126  *   </child>
127  *   <child internal-child="selection">
128  *     <object class="GtkTreeSelection" id="selection">
129  *       <signal name="changed" handler="on_treeview_selection_changed"/>
130  *     </object>
131  *   </child>
132  * </object>
133  * ```
134  *
135  * ## CSS nodes
136  *
137  * ```
138  * treeview.view
139  * ├── header
140  * │   ├── button
141  * │   │   ╰── [sort-indicator]
142  * ┊   ┊
143  * │   ╰── button
144  * │       ╰── [sort-indicator]
145  * │
146  * ├── [rubberband]
147  * ╰── [dndtarget]
148  * ```
149  *
150  * `GtkTreeView` has a main CSS node with name `treeview` and style class `.view`.
151  * It has a subnode with name `header`, which is the parent for all the column
152  * header widgets' CSS nodes.
153  *
154  * Each column header consists of a `button`, which among other content, has a
155  * child with name `sort-indicator`, which carries the `.ascending` or `.descending`
156  * style classes when the column header should show a sort indicator. The CSS
157  * is expected to provide a suitable image using the `-gtk-icon-source` property.
158  *
159  * For rubberband selection, a subnode with name `rubberband` is used.
160  *
161  * For the drop target location during DND, a subnode with name `dndtarget` is used.
162  */
163 
164 enum
165 {
166   DRAG_COLUMN_WINDOW_STATE_UNSET = 0,
167   DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1,
168   DRAG_COLUMN_WINDOW_STATE_ARROW = 2,
169   DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3,
170   DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4
171 };
172 
173 enum
174 {
175   RUBBER_BAND_OFF = 0,
176   RUBBER_BAND_MAYBE_START = 1,
177   RUBBER_BAND_ACTIVE = 2
178 };
179 
180 typedef enum {
181   CLEAR_AND_SELECT = (1 << 0),
182   CLAMP_NODE       = (1 << 1),
183   CURSOR_INVALID   = (1 << 2)
184 } SetCursorFlags;
185 
186  /* This lovely little value is used to determine how far away from the title bar
187   * you can move the mouse and still have a column drag work.
188   */
189 #define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*gtk_tree_view_get_effective_header_height(tree_view))
190 
191 #ifdef __GNUC__
192 
193 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret)     G_STMT_START{          \
194      if (!(expr))                                                       \
195        {                                                                \
196          g_log (G_LOG_DOMAIN,                                           \
197                 G_LOG_LEVEL_CRITICAL,                                   \
198 		"%s (%s): assertion `%s' failed.\n"                     \
199 	        "There is a disparity between the internal view of the GtkTreeView,\n"    \
200 		"and the GtkTreeModel.  This generally means that the model has changed\n"\
201 		"without letting the view know.  Any display from now on is likely to\n"  \
202 		"be incorrect.\n",                                                        \
203                 G_STRLOC,                                               \
204                 G_STRFUNC,                                              \
205                 #expr);                                                 \
206          return ret;                                                    \
207        };                               }G_STMT_END
208 
209 #define TREE_VIEW_INTERNAL_ASSERT_VOID(expr)     G_STMT_START{          \
210      if (!(expr))                                                       \
211        {                                                                \
212          g_log (G_LOG_DOMAIN,                                           \
213                 G_LOG_LEVEL_CRITICAL,                                   \
214 		"%s (%s): assertion `%s' failed.\n"                     \
215 	        "There is a disparity between the internal view of the GtkTreeView,\n"    \
216 		"and the GtkTreeModel.  This generally means that the model has changed\n"\
217 		"without letting the view know.  Any display from now on is likely to\n"  \
218 		"be incorrect.\n",                                                        \
219                 G_STRLOC,                                               \
220                 G_STRFUNC,                                              \
221                 #expr);                                                 \
222          return;                                                        \
223        };                               }G_STMT_END
224 
225 #else
226 
227 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret)     G_STMT_START{          \
228      if (!(expr))                                                       \
229        {                                                                \
230          g_log (G_LOG_DOMAIN,                                           \
231                 G_LOG_LEVEL_CRITICAL,                                   \
232 		"file %s: line %d: assertion `%s' failed.\n"       \
233 	        "There is a disparity between the internal view of the GtkTreeView,\n"    \
234 		"and the GtkTreeModel.  This generally means that the model has changed\n"\
235 		"without letting the view know.  Any display from now on is likely to\n"  \
236 		"be incorrect.\n",                                                        \
237                 __FILE__,                                               \
238                 __LINE__,                                               \
239                 #expr);                                                 \
240          return ret;                                                    \
241        };                               }G_STMT_END
242 
243 #define TREE_VIEW_INTERNAL_ASSERT_VOID(expr)     G_STMT_START{          \
244      if (!(expr))                                                       \
245        {                                                                \
246          g_log (G_LOG_DOMAIN,                                           \
247                 G_LOG_LEVEL_CRITICAL,                                   \
248 		"file %s: line %d: assertion '%s' failed.\n"            \
249 	        "There is a disparity between the internal view of the GtkTreeView,\n"    \
250 		"and the GtkTreeModel.  This generally means that the model has changed\n"\
251 		"without letting the view know.  Any display from now on is likely to\n"  \
252 		"be incorrect.\n",                                                        \
253                 __FILE__,                                               \
254                 __LINE__,                                               \
255                 #expr);                                                 \
256          return;                                                        \
257        };                               }G_STMT_END
258 #endif
259 
260 #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
261 #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2)
262 /* 3/5 of gdkframeclockidle.c's FRAME_INTERVAL (16667 microsecs) */
263 #define GTK_TREE_VIEW_TIME_MS_PER_IDLE 10
264 #define SCROLL_EDGE_SIZE 15
265 #define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000
266 #define AUTO_EXPAND_TIMEOUT 500
267 
268 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
269  * vice versa.
270  */
271 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->dy)
272 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->dy)
273 
274 /* Vertical separator width. Must be an even number. */
275 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
276 /* Horizontal separator width. Must be an even number.  */
277 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 4
278 /* Tree view grid line width, in pixels */
279 #define _TREE_VIEW_GRID_LINE_WIDTH 1
280 /* Tree view line width, in pixels */
281 #define _TREE_VIEW_TREE_LINE_WIDTH 1
282 
283 typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
284 struct _GtkTreeViewColumnReorder
285 {
286   int left_align;
287   int right_align;
288   GtkTreeViewColumn *left_column;
289   GtkTreeViewColumn *right_column;
290 };
291 
292 typedef struct _GtkTreeViewChild GtkTreeViewChild;
293 struct _GtkTreeViewChild
294 {
295   GtkWidget *widget;
296   GtkTreeRBNode *node;
297   GtkTreeRBTree *tree;
298   GtkTreeViewColumn *column;
299   GtkBorder border;
300 };
301 
302 
303 typedef struct _TreeViewDragInfo TreeViewDragInfo;
304 struct _TreeViewDragInfo
305 {
306   GdkContentFormats *source_formats;
307   GdkDragAction source_actions;
308   GdkDrag *drag;
309   GtkTreeRowReference *source_item;
310 
311   GtkCssNode *cssnode;
312   GtkDropTargetAsync *dest;
313   GdkModifierType start_button_mask;
314 
315   guint source_set : 1;
316   guint dest_set : 1;
317 };
318 
319 
320 typedef struct
321 {
322   GtkTreeModel *model;
323 
324   /* tree information */
325   GtkTreeRBTree *tree;
326 
327   /* Container info */
328   GList *children;
329   int width;
330 
331   guint presize_handler_tick_cb;
332 
333   /* Adjustments */
334   GtkAdjustment *hadjustment;
335   GtkAdjustment *vadjustment;
336   int            min_display_width;
337   int            min_display_height;
338 
339   /* CSS nodes */
340   GtkCssNode *header_node;
341 
342   /* Scroll position state keeping */
343   GtkTreeRowReference *top_row;
344   int top_row_dy;
345   /* dy == y pos of top_row + top_row_dy */
346   /* we cache it for simplicity of the code */
347   int dy;
348 
349   guint validate_rows_timer;
350   guint scroll_sync_timer;
351 
352   /* Indentation and expander layout */
353   GtkTreeViewColumn *expander_column;
354 
355   int level_indentation;
356 
357   /* Key navigation (focus), selection */
358   int cursor_offset;
359 
360   GtkTreeRowReference *anchor;
361   GtkTreeRBNode *cursor_node;
362   GtkTreeRBTree *cursor_tree;
363 
364   GtkTreeViewColumn *focus_column;
365 
366   /* Current pressed node, previously pressed, prelight */
367   GtkTreeRBNode *button_pressed_node;
368   GtkTreeRBTree *button_pressed_tree;
369 
370   int press_start_x;
371   int press_start_y;
372 
373   int event_last_x;
374   int event_last_y;
375 
376   GtkTreeRBNode *prelight_node;
377   GtkTreeRBTree *prelight_tree;
378 
379   /* Cell Editing */
380   GtkTreeViewColumn *edited_column;
381 
382   /* Auto expand/collapse timeout in hover mode */
383   guint auto_expand_timeout;
384 
385   /* Selection information */
386   GtkTreeSelection *selection;
387 
388   /* Header information */
389   int header_height;
390   int n_columns;
391   GList *columns;
392 
393   GtkTreeViewColumnDropFunc column_drop_func;
394   gpointer column_drop_func_data;
395   GDestroyNotify column_drop_func_data_destroy;
396   GList *column_drag_info;
397   GtkTreeViewColumnReorder *cur_reorder;
398 
399   int prev_width_before_expander;
400 
401   /* Scroll timeout (e.g. during dnd, rubber banding) */
402   guint scroll_timeout;
403 
404   /* Interactive Header reordering */
405   GtkTreeViewColumn *drag_column;
406   int drag_column_x;
407   int drag_column_y;
408 
409   /* Interactive Header Resizing */
410   int drag_pos;
411   int x_drag;
412 
413   /* Row drag-and-drop */
414   GtkTreeRowReference *drag_dest_row;
415   GtkTreeViewDropPosition drag_dest_pos;
416   guint open_dest_timeout;
417 
418   /* Rubber banding */
419   int rubber_band_status;
420   int rubber_band_x;
421   int rubber_band_y;
422   int rubber_band_extend;
423   int rubber_band_modify;
424 
425   /* fixed height */
426   int fixed_height;
427 
428   GtkTreeRBNode *rubber_band_start_node;
429   GtkTreeRBTree *rubber_band_start_tree;
430 
431   GtkTreeRBNode *rubber_band_end_node;
432   GtkTreeRBTree *rubber_band_end_tree;
433   GtkCssNode *rubber_band_cssnode;
434 
435   /* Scroll-to functionality when unrealized */
436   GtkTreeRowReference *scroll_to_path;
437   GtkTreeViewColumn *scroll_to_column;
438   float scroll_to_row_align;
439   float scroll_to_col_align;
440 
441   /* Interactive search */
442   int selected_iter;
443   int search_column;
444   GtkTreeViewSearchEqualFunc search_equal_func;
445   gpointer search_user_data;
446   GDestroyNotify search_destroy;
447   gpointer search_position_user_data;
448   GDestroyNotify search_position_destroy;
449   GtkWidget *search_popover;
450   GtkWidget *search_entry;
451   gulong search_entry_changed_id;
452   guint typeselect_flush_timeout;
453 
454   /* Grid and tree lines */
455   GtkTreeViewGridLines grid_lines;
456   gboolean tree_lines_enabled;
457 
458   /* Row separators */
459   GtkTreeViewRowSeparatorFunc row_separator_func;
460   gpointer row_separator_data;
461   GDestroyNotify row_separator_destroy;
462 
463   /* Gestures */
464   GtkGesture *click_gesture;
465   GtkGesture *drag_gesture; /* Rubberbanding, row DnD */
466   GtkGesture *column_drag_gesture; /* Column reordering, resizing */
467 
468   /* Tooltip support */
469   int tooltip_column;
470 
471   int expander_size;
472 
473   GdkRGBA grid_line_color; /* Color used in the textures */
474   GdkTexture *horizontal_grid_line_texture;
475   GdkTexture *vertical_grid_line_texture;
476 
477   GdkRGBA tree_line_color; /* Color used in the textures */
478   GdkTexture *horizontal_tree_line_texture;
479   GdkTexture *vertical_tree_line_texture;
480 
481   /* Here comes the bitfield */
482   guint scroll_to_use_align : 1;
483 
484   guint fixed_height_mode : 1;
485   guint fixed_height_check : 1;
486 
487   guint activate_on_single_click : 1;
488   guint reorderable : 1;
489   guint header_has_focus : 1;
490   guint drag_column_surface_state : 3;
491   guint mark_rows_col_dirty : 1;
492 
493   /* for DnD */
494   guint empty_view_drop : 1;
495 
496   guint modify_selection_pressed : 1;
497   guint extend_selection_pressed : 1;
498 
499   guint in_top_row_to_dy : 1;
500 
501   /* interactive search */
502   guint enable_search : 1;
503   guint disable_popdown : 1;
504   guint search_custom_entry_set : 1;
505 
506   guint hover_selection : 1;
507   guint hover_expand : 1;
508   guint imcontext_changed : 1;
509 
510   guint in_scroll : 1;
511 
512   guint rubber_banding_enable : 1;
513 
514   guint in_grab : 1;
515 
516   /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
517   guint search_entry_avoid_unhandled_binding : 1;
518 
519   /* GtkScrollablePolicy needs to be checked when
520    * driving the scrollable adjustment values */
521   guint hscroll_policy : 1;
522   guint vscroll_policy : 1;
523 
524   /* GtkTreeView flags */
525   guint is_list : 1;
526   guint show_expanders : 1;
527   guint in_column_resize : 1;
528   guint arrow_prelit : 1;
529   guint headers_visible : 1;
530   guint draw_keyfocus : 1;
531   guint model_setup : 1;
532   guint in_column_drag : 1;
533 } GtkTreeViewPrivate;
534 
535 
536 /* Signals */
537 enum
538 {
539   ROW_ACTIVATED,
540   TEST_EXPAND_ROW,
541   TEST_COLLAPSE_ROW,
542   ROW_EXPANDED,
543   ROW_COLLAPSED,
544   COLUMNS_CHANGED,
545   CURSOR_CHANGED,
546   MOVE_CURSOR,
547   SELECT_ALL,
548   UNSELECT_ALL,
549   SELECT_CURSOR_ROW,
550   TOGGLE_CURSOR_ROW,
551   EXPAND_COLLAPSE_CURSOR_ROW,
552   SELECT_CURSOR_PARENT,
553   START_INTERACTIVE_SEARCH,
554   LAST_SIGNAL
555 };
556 
557 /* Properties */
558 enum {
559   PROP_0,
560   PROP_MODEL,
561   PROP_HEADERS_VISIBLE,
562   PROP_HEADERS_CLICKABLE,
563   PROP_EXPANDER_COLUMN,
564   PROP_REORDERABLE,
565   PROP_ENABLE_SEARCH,
566   PROP_SEARCH_COLUMN,
567   PROP_FIXED_HEIGHT_MODE,
568   PROP_HOVER_SELECTION,
569   PROP_HOVER_EXPAND,
570   PROP_SHOW_EXPANDERS,
571   PROP_LEVEL_INDENTATION,
572   PROP_RUBBER_BANDING,
573   PROP_ENABLE_GRID_LINES,
574   PROP_ENABLE_TREE_LINES,
575   PROP_TOOLTIP_COLUMN,
576   PROP_ACTIVATE_ON_SINGLE_CLICK,
577   LAST_PROP,
578   /* overridden */
579   PROP_HADJUSTMENT = LAST_PROP,
580   PROP_VADJUSTMENT,
581   PROP_HSCROLL_POLICY,
582   PROP_VSCROLL_POLICY,
583 };
584 
585 /* object signals */
586 static void     gtk_tree_view_finalize             (GObject          *object);
587 static void     gtk_tree_view_dispose              (GObject          *object);
588 static void     gtk_tree_view_set_property         (GObject         *object,
589 						    guint            prop_id,
590 						    const GValue    *value,
591 						    GParamSpec      *pspec);
592 static void     gtk_tree_view_get_property         (GObject         *object,
593 						    guint            prop_id,
594 						    GValue          *value,
595 						    GParamSpec      *pspec);
596 
597 /* gtkwidget signals */
598 static void     gtk_tree_view_realize              (GtkWidget        *widget);
599 static void     gtk_tree_view_unrealize            (GtkWidget        *widget);
600 static void     gtk_tree_view_unroot               (GtkWidget        *widget);
601 static void     gtk_tree_view_map                  (GtkWidget        *widget);
602 static void     gtk_tree_view_measure              (GtkWidget        *widget,
603                                                     GtkOrientation  orientation,
604                                                     int             for_size,
605                                                     int            *minimum,
606                                                     int            *natural,
607                                                     int            *minimum_baseline,
608                                                     int            *natural_baseline);
609 static void     gtk_tree_view_size_allocate        (GtkWidget      *widget,
610                                                     int             width,
611                                                     int             height,
612                                                     int             baseline);
613 static void     gtk_tree_view_snapshot             (GtkWidget        *widget,
614                                                     GtkSnapshot      *snapshot);
615 
616 static gboolean gtk_tree_view_forward_controller_key_pressed  (GtkEventControllerKey *key,
617                                                                guint                  keyval,
618                                                                guint                  keycode,
619                                                                GdkModifierType        state,
620                                                                GtkTreeView           *tree_view);
621 static gboolean gtk_tree_view_key_controller_key_pressed  (GtkEventControllerKey *key,
622                                                            guint                  keyval,
623                                                            guint                  keycode,
624                                                            GdkModifierType        state,
625                                                            GtkTreeView           *tree_view);
626 static void     gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key,
627                                                            guint                  keyval,
628                                                            guint                  keycode,
629                                                            GdkModifierType        state,
630                                                            GtkTreeView           *tree_view);
631 static void     gtk_tree_view_focus_controller_focus_out  (GtkEventController    *focus,
632                                                            GtkTreeView           *tree_view);
633 
634 static int      gtk_tree_view_focus                (GtkWidget        *widget,
635 						    GtkDirectionType  direction);
636 static gboolean gtk_tree_view_grab_focus           (GtkWidget        *widget);
637 static void     gtk_tree_view_css_changed          (GtkWidget        *widget,
638                                                     GtkCssStyleChange *change);
639 
640 static void     gtk_tree_view_remove               (GtkTreeView      *tree_view,
641                                                     GtkWidget        *widget);
642 
643 /* Source side drag signals */
644 static void gtk_tree_view_dnd_finished_cb  (GdkDrag          *drag,
645                                             GtkWidget        *widget);
646 static GdkContentProvider * gtk_tree_view_drag_data_get   (GtkTreeView           *tree_view,
647                                                            GtkTreePath           *source_row);
648 
649 /* Target side drag signals */
650 static void     gtk_tree_view_drag_leave                  (GtkDropTargetAsync    *dest,
651                                                            GdkDrop               *drop,
652                                                            GtkTreeView           *tree_view);
653 static GdkDragAction gtk_tree_view_drag_motion            (GtkDropTargetAsync    *dest,
654                                                            GdkDrop               *drop,
655                                                            double                 x,
656                                                            double                 y,
657                                                            GtkTreeView           *tree_view);
658 static gboolean gtk_tree_view_drag_drop                   (GtkDropTargetAsync    *dest,
659                                                            GdkDrop               *drop,
660                                                            double                 x,
661                                                            double                 y,
662                                                            GtkTreeView           *tree_view);
663 static void     gtk_tree_view_drag_data_received          (GObject               *source,
664                                                            GAsyncResult          *result,
665                                                            gpointer               data);
666 
667 /* tree_model signals */
668 static gboolean gtk_tree_view_real_move_cursor            (GtkTreeView     *tree_view,
669 							   GtkMovementStep  step,
670 							   int              count,
671                                                            gboolean         extend,
672                                                            gboolean         modify);
673 static gboolean gtk_tree_view_real_select_all             (GtkTreeView     *tree_view);
674 static gboolean gtk_tree_view_real_unselect_all           (GtkTreeView     *tree_view);
675 static gboolean gtk_tree_view_real_select_cursor_row      (GtkTreeView     *tree_view,
676 							   gboolean         start_editing);
677 static gboolean gtk_tree_view_real_toggle_cursor_row      (GtkTreeView     *tree_view);
678 static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView     *tree_view,
679 							       gboolean         logical,
680 							       gboolean         expand,
681 							       gboolean         open_all);
682 static gboolean gtk_tree_view_real_select_cursor_parent   (GtkTreeView     *tree_view);
683 static void gtk_tree_view_row_changed                     (GtkTreeModel    *model,
684 							   GtkTreePath     *path,
685 							   GtkTreeIter     *iter,
686 							   gpointer         data);
687 static void gtk_tree_view_row_inserted                    (GtkTreeModel    *model,
688 							   GtkTreePath     *path,
689 							   GtkTreeIter     *iter,
690 							   gpointer         data);
691 static void gtk_tree_view_row_has_child_toggled           (GtkTreeModel    *model,
692 							   GtkTreePath     *path,
693 							   GtkTreeIter     *iter,
694 							   gpointer         data);
695 static void gtk_tree_view_row_deleted                     (GtkTreeModel    *model,
696 							   GtkTreePath     *path,
697 							   gpointer         data);
698 static void gtk_tree_view_rows_reordered                  (GtkTreeModel    *model,
699 							   GtkTreePath     *parent,
700 							   GtkTreeIter     *iter,
701 							   int             *new_order,
702 							   gpointer         data);
703 
704 /* Incremental reflow */
705 static gboolean validate_row                              (GtkTreeView     *tree_view,
706                                                            GtkTreeRBTree   *tree,
707                                                            GtkTreeRBNode   *node,
708                                                            GtkTreeIter     *iter,
709                                                            GtkTreePath     *path);
710 static void     validate_visible_area    (GtkTreeView *tree_view);
711 static gboolean do_validate_rows         (GtkTreeView *tree_view,
712 					  gboolean     queue_resize);
713 static gboolean validate_rows            (GtkTreeView *tree_view);
714 static void     install_presize_handler  (GtkTreeView *tree_view);
715 static void     install_scroll_sync_handler (GtkTreeView *tree_view);
716 static void     gtk_tree_view_set_top_row   (GtkTreeView *tree_view,
717 					     GtkTreePath *path,
718 					     int          offset);
719 static void	gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view);
720 static void     gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view);
721 static void     invalidate_empty_focus      (GtkTreeView *tree_view);
722 
723 /* Internal functions */
724 static gboolean gtk_tree_view_is_expander_column             (GtkTreeView        *tree_view,
725 							      GtkTreeViewColumn  *column);
726 static inline gboolean gtk_tree_view_draw_expanders          (GtkTreeView        *tree_view);
727 static void     gtk_tree_view_add_move_binding               (GtkWidgetClass     *widget_class,
728 							      guint               keyval,
729 							      guint               modmask,
730 							      gboolean            add_shifted_binding,
731 							      GtkMovementStep     step,
732 							      int                 count);
733 static int      gtk_tree_view_unref_and_check_selection_tree (GtkTreeView        *tree_view,
734 							      GtkTreeRBTree      *tree);
735 static void     gtk_tree_view_snapshot_arrow                 (GtkTreeView        *tree_view,
736                                                               GtkSnapshot        *snapshot,
737 							      GtkTreeRBTree      *tree,
738 							      GtkTreeRBNode      *node);
739 static void     gtk_tree_view_get_arrow_xrange               (GtkTreeView        *tree_view,
740 							      GtkTreeRBTree      *tree,
741 							      int                *x1,
742 							      int                *x2);
743 static void     gtk_tree_view_adjustment_changed             (GtkAdjustment      *adjustment,
744 							      GtkTreeView        *tree_view);
745 static void     gtk_tree_view_build_tree                     (GtkTreeView        *tree_view,
746 							      GtkTreeRBTree          *tree,
747 							      GtkTreeIter        *iter,
748 							      int                 depth,
749 							      gboolean            recurse);
750 static void     gtk_tree_view_clamp_node_visible             (GtkTreeView        *tree_view,
751 							      GtkTreeRBTree      *tree,
752 							      GtkTreeRBNode      *node);
753 static void     gtk_tree_view_clamp_column_visible           (GtkTreeView        *tree_view,
754 							      GtkTreeViewColumn  *column,
755 							      gboolean            focus_to_cell);
756 static gboolean gtk_tree_view_maybe_begin_dragging_row       (GtkTreeView        *tree_view);
757 static void     gtk_tree_view_focus_to_cursor                (GtkTreeView        *tree_view);
758 static void     gtk_tree_view_move_cursor_up_down            (GtkTreeView        *tree_view,
759 							      int                 count);
760 static void     gtk_tree_view_move_cursor_page_up_down       (GtkTreeView        *tree_view,
761 							      int                 count);
762 static void     gtk_tree_view_move_cursor_left_right         (GtkTreeView        *tree_view,
763 							      int                 count);
764 static void     gtk_tree_view_move_cursor_start_end          (GtkTreeView        *tree_view,
765 							      int                 count);
766 static gboolean gtk_tree_view_real_collapse_row              (GtkTreeView        *tree_view,
767 							      GtkTreePath        *path,
768 							      GtkTreeRBTree      *tree,
769 							      GtkTreeRBNode      *node);
770 static gboolean gtk_tree_view_real_expand_row                (GtkTreeView        *tree_view,
771 							      GtkTreePath        *path,
772 							      GtkTreeRBTree      *tree,
773 							      GtkTreeRBNode      *node,
774 							      gboolean            open_all);
775 static void     gtk_tree_view_real_set_cursor                (GtkTreeView        *tree_view,
776 							      GtkTreePath        *path,
777                                                               SetCursorFlags      flags);
778 static gboolean gtk_tree_view_has_can_focus_cell             (GtkTreeView        *tree_view);
779 static void     column_sizing_notify                         (GObject            *object,
780                                                               GParamSpec         *pspec,
781                                                               gpointer            data);
782 static void     gtk_tree_view_stop_rubber_band               (GtkTreeView        *tree_view);
783 static void     ensure_unprelighted                          (GtkTreeView        *tree_view);
784 static void     update_prelight                              (GtkTreeView        *tree_view,
785                                                               int                 x,
786                                                               int                 y);
787 
788 static inline int gtk_tree_view_get_effective_header_height (GtkTreeView        *tree_view);
789 
790 static inline int gtk_tree_view_get_cell_area_y_offset      (GtkTreeView        *tree_view,
791                                                               GtkTreeRBTree      *tree,
792                                                               GtkTreeRBNode      *node);
793 static inline int gtk_tree_view_get_cell_area_height        (GtkTreeView        *tree_view,
794                                                               GtkTreeRBNode      *node);
795 
796 static inline int gtk_tree_view_get_row_y_offset            (GtkTreeView        *tree_view,
797                                                               GtkTreeRBTree      *tree,
798                                                               GtkTreeRBNode      *node);
799 static inline int gtk_tree_view_get_row_height              (GtkTreeView        *tree_view,
800                                                               GtkTreeRBNode      *node);
801 static TreeViewDragInfo* get_info (GtkTreeView *tree_view);
802 
803 /* interactive search */
804 static void     gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
805 static void     gtk_tree_view_search_popover_hide       (GtkWidget        *search_popover,
806                                                          GtkTreeView      *tree_view);
807 static void     gtk_tree_view_search_preedit_changed    (GtkText          *text,
808                                                          const char       *preedit,
809 							 GtkTreeView      *tree_view);
810 static void     gtk_tree_view_search_changed            (GtkEditable      *editable,
811                                                          GtkTreeView      *tree_view);
812 static void     gtk_tree_view_search_activate           (GtkEntry         *entry,
813 							 GtkTreeView      *tree_view);
814 static void     gtk_tree_view_search_pressed_cb         (GtkGesture       *gesture,
815                                                          int               n_press,
816                                                          double            x,
817                                                          double            y,
818 							 GtkTreeView      *tree_view);
819 static gboolean gtk_tree_view_search_scroll_event       (GtkWidget        *entry,
820 							 double            dx,
821                                                          double            dy,
822 							 GtkTreeView      *tree_view);
823 static gboolean gtk_tree_view_search_key_pressed        (GtkEventControllerKey *key,
824                                                          guint                  keyval,
825                                                          guint                  keycode,
826                                                          GdkModifierType        state,
827                                                          GtkTreeView           *tree_view);
828 static gboolean gtk_tree_view_search_move               (GtkWidget        *window,
829 							 GtkTreeView      *tree_view,
830 							 gboolean          up);
831 static gboolean gtk_tree_view_search_equal_func         (GtkTreeModel     *model,
832 							 int               column,
833 							 const char       *key,
834 							 GtkTreeIter      *iter,
835 							 gpointer          search_data);
836 static gboolean gtk_tree_view_search_iter               (GtkTreeModel     *model,
837 							 GtkTreeSelection *selection,
838 							 GtkTreeIter      *iter,
839 							 const char       *text,
840 							 int              *count,
841 							 int               n);
842 static void     gtk_tree_view_search_init               (GtkWidget        *entry,
843 							 GtkTreeView      *tree_view);
844 static void     gtk_tree_view_put                       (GtkTreeView      *tree_view,
845 							 GtkWidget        *child_widget,
846                                                          GtkTreePath      *path,
847                                                          GtkTreeViewColumn*column,
848                                                          const GtkBorder  *border);
849 static gboolean gtk_tree_view_start_editing             (GtkTreeView      *tree_view,
850 							 GtkTreePath      *cursor_path,
851 							 gboolean          edit_only);
852 static void gtk_tree_view_stop_editing                  (GtkTreeView *tree_view,
853 							 gboolean     cancel_editing);
854 static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
855 							     gboolean     keybinding);
856 static gboolean gtk_tree_view_start_interactive_search      (GtkTreeView *tree_view);
857 static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView       *tree_view,
858 							 GtkTreeViewColumn *column,
859 							 int                drop_position);
860 
861 /* GtkBuildable */
862 static void     gtk_tree_view_buildable_add_child          (GtkBuildable      *tree_view,
863 							    GtkBuilder        *builder,
864 							    GObject           *child,
865 							    const char        *type);
866 static GObject *gtk_tree_view_buildable_get_internal_child (GtkBuildable      *buildable,
867 							    GtkBuilder        *builder,
868 							    const char        *childname);
869 static void     gtk_tree_view_buildable_init               (GtkBuildableIface *iface);
870 
871 /* GtkScrollable */
872 static void     gtk_tree_view_scrollable_init              (GtkScrollableInterface *iface);
873 
874 static void           gtk_tree_view_do_set_hadjustment (GtkTreeView   *tree_view,
875                                                         GtkAdjustment *adjustment);
876 static void           gtk_tree_view_do_set_vadjustment (GtkTreeView   *tree_view,
877                                                         GtkAdjustment *adjustment);
878 
879 static gboolean scroll_row_timeout                   (gpointer     data);
880 static void     add_scroll_timeout                   (GtkTreeView *tree_view);
881 static void     remove_scroll_timeout                (GtkTreeView *tree_view);
882 
883 static void     grab_focus_and_unset_draw_keyfocus   (GtkTreeView *tree_view);
884 
885 /* Gestures */
886 static void gtk_tree_view_column_click_gesture_pressed (GtkGestureClick *gesture,
887                                                              int                   n_press,
888                                                              double                x,
889                                                              double                y,
890                                                              GtkTreeView          *tree_view);
891 
892 static void gtk_tree_view_click_gesture_pressed        (GtkGestureClick *gesture,
893                                                              int                   n_press,
894                                                              double                x,
895                                                              double                y,
896                                                              GtkTreeView          *tree_view);
897 static void gtk_tree_view_click_gesture_released       (GtkGestureClick *gesture,
898                                                              int                   n_press,
899                                                              double                x,
900                                                              double                y,
901                                                              GtkTreeView          *tree_view);
902 
903 static void gtk_tree_view_column_drag_gesture_begin         (GtkGestureDrag *gesture,
904                                                              double          start_x,
905                                                              double          start_y,
906                                                              GtkTreeView    *tree_view);
907 static void gtk_tree_view_column_drag_gesture_update        (GtkGestureDrag *gesture,
908                                                              double          offset_x,
909                                                              double          offset_y,
910                                                              GtkTreeView    *tree_view);
911 static void gtk_tree_view_column_drag_gesture_end           (GtkGestureDrag *gesture,
912                                                              double          offset_x,
913                                                              double          offset_y,
914                                                              GtkTreeView    *tree_view);
915 
916 static void gtk_tree_view_drag_gesture_begin                (GtkGestureDrag *gesture,
917                                                              double          start_x,
918                                                              double          start_y,
919                                                              GtkTreeView    *tree_view);
920 static void gtk_tree_view_drag_gesture_update               (GtkGestureDrag *gesture,
921                                                              double          offset_x,
922                                                              double          offset_y,
923                                                              GtkTreeView    *tree_view);
924 static void gtk_tree_view_drag_gesture_end                  (GtkGestureDrag *gesture,
925                                                              double          offset_x,
926                                                              double          offset_y,
927                                                              GtkTreeView    *tree_view);
928 static void gtk_tree_view_motion_controller_enter           (GtkEventControllerMotion *controller,
929                                                              double                    x,
930                                                              double                    y,
931                                                              GtkTreeView              *tree_view);
932 static void gtk_tree_view_motion_controller_leave           (GtkEventControllerMotion *controller,
933                                                              GtkTreeView              *tree_view);
934 static void gtk_tree_view_motion_controller_motion          (GtkEventControllerMotion *controller,
935                                                              double                    x,
936                                                              double                    y,
937                                                              GtkTreeView              *tree_view);
938 
939 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
940 static GParamSpec *tree_view_props [LAST_PROP] = { NULL };
941 
942 
943 
944 /* GType Methods
945  */
946 
G_DEFINE_TYPE_WITH_CODE(GtkTreeView,gtk_tree_view,GTK_TYPE_WIDGET,G_ADD_PRIVATE (GtkTreeView)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_tree_view_buildable_init)G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,gtk_tree_view_scrollable_init))947 G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_WIDGET,
948                          G_ADD_PRIVATE (GtkTreeView)
949                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
950                                                 gtk_tree_view_buildable_init)
951                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,
952                                                 gtk_tree_view_scrollable_init))
953 
954 static void
955 gtk_tree_view_class_init (GtkTreeViewClass *class)
956 {
957   GObjectClass *o_class = G_OBJECT_CLASS (class);
958   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
959 
960   /* GObject signals */
961   o_class->set_property = gtk_tree_view_set_property;
962   o_class->get_property = gtk_tree_view_get_property;
963   o_class->finalize = gtk_tree_view_finalize;
964   o_class->dispose = gtk_tree_view_dispose;
965 
966   /* GtkWidget signals */
967   widget_class->map = gtk_tree_view_map;
968   widget_class->realize = gtk_tree_view_realize;
969   widget_class->unrealize = gtk_tree_view_unrealize;
970   widget_class->unroot = gtk_tree_view_unroot;
971   widget_class->measure = gtk_tree_view_measure;
972   widget_class->size_allocate = gtk_tree_view_size_allocate;
973   widget_class->snapshot = gtk_tree_view_snapshot;
974   widget_class->focus = gtk_tree_view_focus;
975   widget_class->grab_focus = gtk_tree_view_grab_focus;
976   widget_class->css_changed = gtk_tree_view_css_changed;
977 
978   class->move_cursor = gtk_tree_view_real_move_cursor;
979   class->select_all = gtk_tree_view_real_select_all;
980   class->unselect_all = gtk_tree_view_real_unselect_all;
981   class->select_cursor_row = gtk_tree_view_real_select_cursor_row;
982   class->toggle_cursor_row = gtk_tree_view_real_toggle_cursor_row;
983   class->expand_collapse_cursor_row = gtk_tree_view_real_expand_collapse_cursor_row;
984   class->select_cursor_parent = gtk_tree_view_real_select_cursor_parent;
985   class->start_interactive_search = gtk_tree_view_start_interactive_search;
986 
987   /* Properties */
988 
989   g_object_class_override_property (o_class, PROP_HADJUSTMENT,    "hadjustment");
990   g_object_class_override_property (o_class, PROP_VADJUSTMENT,    "vadjustment");
991   g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
992   g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
993 
994   tree_view_props[PROP_MODEL] =
995       g_param_spec_object ("model",
996                            P_("TreeView Model"),
997                            P_("The model for the tree view"),
998                            GTK_TYPE_TREE_MODEL,
999                            GTK_PARAM_READWRITE);
1000 
1001   tree_view_props[PROP_HEADERS_VISIBLE] =
1002       g_param_spec_boolean ("headers-visible",
1003                             P_("Headers Visible"),
1004                             P_("Show the column header buttons"),
1005                             TRUE,
1006                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1007 
1008   tree_view_props[PROP_HEADERS_CLICKABLE] =
1009       g_param_spec_boolean ("headers-clickable",
1010                             P_("Headers Clickable"),
1011                             P_("Column headers respond to click events"),
1012                             TRUE,
1013                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1014 
1015   tree_view_props[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);
1021 
1022   tree_view_props[PROP_REORDERABLE] =
1023       g_param_spec_boolean ("reorderable",
1024                             P_("Reorderable"),
1025                             P_("View is reorderable"),
1026                             FALSE,
1027                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1028 
1029   tree_view_props[PROP_ENABLE_SEARCH] =
1030       g_param_spec_boolean ("enable-search",
1031                             P_("Enable Search"),
1032                             P_("View allows user to search through columns interactively"),
1033                             TRUE,
1034                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1035 
1036   tree_view_props[PROP_SEARCH_COLUMN] =
1037       g_param_spec_int ("search-column",
1038                         P_("Search Column"),
1039                         P_("Model column to search through during interactive search"),
1040                         -1, G_MAXINT,
1041                         -1,
1042                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1043 
1044   /**
1045    * GtkTreeView:fixed-height-mode:
1046    *
1047    * Setting the ::fixed-height-mode property to %TRUE speeds up
1048    * `GtkTreeView` by assuming that all rows have the same height.
1049    * Only enable this option if all rows are the same height.
1050    * Please see gtk_tree_view_set_fixed_height_mode() for more
1051    * information on this option.
1052    */
1053   tree_view_props[PROP_FIXED_HEIGHT_MODE] =
1054       g_param_spec_boolean ("fixed-height-mode",
1055                             P_("Fixed Height Mode"),
1056                             P_("Speeds up GtkTreeView by assuming that all rows have the same height"),
1057                             FALSE,
1058                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1059 
1060   /**
1061    * GtkTreeView:hover-selection:
1062    *
1063    * Enables or disables the hover selection mode of @tree_view.
1064    * Hover selection makes the selected row follow the pointer.
1065    * Currently, this works only for the selection modes
1066    * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
1067    *
1068    * This mode is primarily intended for treeviews in popups, e.g.
1069    * in `GtkComboBox` or `GtkEntryCompletion`.
1070    */
1071   tree_view_props[PROP_HOVER_SELECTION] =
1072       g_param_spec_boolean ("hover-selection",
1073                             P_("Hover Selection"),
1074                             P_("Whether the selection should follow the pointer"),
1075                             FALSE,
1076                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1077 
1078   /**
1079    * GtkTreeView:hover-expand:
1080    *
1081    * Enables or disables the hover expansion mode of @tree_view.
1082    * Hover expansion makes rows expand or collapse if the pointer moves
1083    * over them.
1084    *
1085    * This mode is primarily intended for treeviews in popups, e.g.
1086    * in `GtkComboBox` or `GtkEntryCompletion`.
1087    */
1088   tree_view_props[PROP_HOVER_EXPAND] =
1089       g_param_spec_boolean ("hover-expand",
1090                             P_("Hover Expand"),
1091                             P_("Whether rows should be expanded/collapsed when the pointer moves over them"),
1092                             FALSE,
1093                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1094 
1095   /**
1096    * GtkTreeView:show-expanders:
1097    *
1098    * %TRUE if the view has expanders.
1099    */
1100   tree_view_props[PROP_SHOW_EXPANDERS] =
1101       g_param_spec_boolean ("show-expanders",
1102                             P_("Show Expanders"),
1103                             P_("View has expanders"),
1104                             TRUE,
1105                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1106 
1107   /**
1108    * GtkTreeView:level-indentation:
1109    *
1110    * Extra indentation for each level.
1111    */
1112   tree_view_props[PROP_LEVEL_INDENTATION] =
1113       g_param_spec_int ("level-indentation",
1114                         P_("Level Indentation"),
1115                         P_("Extra indentation for each level"),
1116                         0, G_MAXINT,
1117                         0,
1118                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1119 
1120   tree_view_props[PROP_RUBBER_BANDING] =
1121       g_param_spec_boolean ("rubber-banding",
1122                             P_("Rubber Banding"),
1123                             P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
1124                             FALSE,
1125                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1126 
1127   tree_view_props[PROP_ENABLE_GRID_LINES] =
1128       g_param_spec_enum ("enable-grid-lines",
1129                          P_("Enable Grid Lines"),
1130                          P_("Whether grid lines should be drawn in the tree view"),
1131                          GTK_TYPE_TREE_VIEW_GRID_LINES,
1132                          GTK_TREE_VIEW_GRID_LINES_NONE,
1133                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1134 
1135   tree_view_props[PROP_ENABLE_TREE_LINES] =
1136       g_param_spec_boolean ("enable-tree-lines",
1137                             P_("Enable Tree Lines"),
1138                             P_("Whether tree lines should be drawn in the tree view"),
1139                             FALSE,
1140                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1141 
1142   tree_view_props[PROP_TOOLTIP_COLUMN] =
1143       g_param_spec_int ("tooltip-column",
1144                         P_("Tooltip Column"),
1145                         P_("The column in the model containing the tooltip texts for the rows"),
1146                         -1, G_MAXINT,
1147                         -1,
1148                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1149 
1150   /**
1151    * GtkTreeView:activate-on-single-click:
1152    *
1153    * The activate-on-single-click property specifies whether the "row-activated" signal
1154    * will be emitted after a single click.
1155    */
1156   tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK] =
1157       g_param_spec_boolean ("activate-on-single-click",
1158                             P_("Activate on Single Click"),
1159                             P_("Activate row on a single click"),
1160                             FALSE,
1161                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1162 
1163   g_object_class_install_properties (o_class, LAST_PROP, tree_view_props);
1164 
1165   /* Signals */
1166   /**
1167    * GtkTreeView::row-activated:
1168    * @tree_view: the object on which the signal is emitted
1169    * @path: the `GtkTreePath` for the activated row
1170    * @column: (nullable): the `GtkTreeViewColumn` in which the activation occurred
1171    *
1172    * The "row-activated" signal is emitted when the method
1173    * gtk_tree_view_row_activated() is called.
1174    *
1175    * This signal is emitted when the user double-clicks a treeview row with the
1176    * [property@Gtk.TreeView:activate-on-single-click] property set to %FALSE,
1177    * or when the user single-clicks a row when that property set to %TRUE.
1178    *
1179    * This signal is also emitted when a non-editable row is selected and one
1180    * of the keys: <kbd>Space</kbd>, <kbd>Shift</kbd>+<kbd>Space</kbd>, <kbd>Return</kbd> or <kbd>Enter</kbd> is pressed.
1181    *
1182    * For selection handling refer to the
1183    * [tree widget conceptual overview][TreeWidget]
1184    * as well as `GtkTreeSelection`.
1185    */
1186   tree_view_signals[ROW_ACTIVATED] =
1187     g_signal_new (I_("row-activated"),
1188 		  G_TYPE_FROM_CLASS (o_class),
1189 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1190 		  G_STRUCT_OFFSET (GtkTreeViewClass, row_activated),
1191 		  NULL, NULL,
1192 		  _gtk_marshal_VOID__BOXED_OBJECT,
1193 		  G_TYPE_NONE, 2,
1194 		  GTK_TYPE_TREE_PATH,
1195 		  GTK_TYPE_TREE_VIEW_COLUMN);
1196   g_signal_set_va_marshaller (tree_view_signals[ROW_ACTIVATED],
1197                               G_TYPE_FROM_CLASS (o_class),
1198                               _gtk_marshal_VOID__BOXED_OBJECTv);
1199 
1200   /**
1201    * GtkTreeView::test-expand-row:
1202    * @tree_view: the object on which the signal is emitted
1203    * @iter: the tree iter of the row to expand
1204    * @path: a tree path that points to the row
1205    *
1206    * The given row is about to be expanded (show its children nodes). Use this
1207    * signal if you need to control the expandability of individual rows.
1208    *
1209    * Returns: %FALSE to allow expansion, %TRUE to reject
1210    */
1211   tree_view_signals[TEST_EXPAND_ROW] =
1212     g_signal_new (I_("test-expand-row"),
1213 		  G_TYPE_FROM_CLASS (o_class),
1214 		  G_SIGNAL_RUN_LAST,
1215 		  G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row),
1216 		  _gtk_boolean_handled_accumulator, NULL,
1217 		  _gtk_marshal_BOOLEAN__BOXED_BOXED,
1218 		  G_TYPE_BOOLEAN, 2,
1219 		  GTK_TYPE_TREE_ITER,
1220 		  GTK_TYPE_TREE_PATH);
1221   g_signal_set_va_marshaller (tree_view_signals[TEST_EXPAND_ROW],
1222                               G_TYPE_FROM_CLASS (o_class),
1223                               _gtk_marshal_BOOLEAN__BOXED_BOXEDv);
1224 
1225   /**
1226    * GtkTreeView::test-collapse-row:
1227    * @tree_view: the object on which the signal is emitted
1228    * @iter: the tree iter of the row to collapse
1229    * @path: a tree path that points to the row
1230    *
1231    * The given row is about to be collapsed (hide its children nodes). Use this
1232    * signal if you need to control the collapsibility of individual rows.
1233    *
1234    * Returns: %FALSE to allow collapsing, %TRUE to reject
1235    */
1236   tree_view_signals[TEST_COLLAPSE_ROW] =
1237     g_signal_new (I_("test-collapse-row"),
1238 		  G_TYPE_FROM_CLASS (o_class),
1239 		  G_SIGNAL_RUN_LAST,
1240 		  G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row),
1241 		  _gtk_boolean_handled_accumulator, NULL,
1242 		  _gtk_marshal_BOOLEAN__BOXED_BOXED,
1243 		  G_TYPE_BOOLEAN, 2,
1244 		  GTK_TYPE_TREE_ITER,
1245 		  GTK_TYPE_TREE_PATH);
1246   g_signal_set_va_marshaller (tree_view_signals[TEST_COLLAPSE_ROW],
1247                               G_TYPE_FROM_CLASS (o_class),
1248                               _gtk_marshal_BOOLEAN__BOXED_BOXEDv);
1249 
1250   /**
1251    * GtkTreeView::row-expanded:
1252    * @tree_view: the object on which the signal is emitted
1253    * @iter: the tree iter of the expanded row
1254    * @path: a tree path that points to the row
1255    *
1256    * The given row has been expanded (child nodes are shown).
1257    */
1258   tree_view_signals[ROW_EXPANDED] =
1259     g_signal_new (I_("row-expanded"),
1260 		  G_TYPE_FROM_CLASS (o_class),
1261 		  G_SIGNAL_RUN_LAST,
1262 		  G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded),
1263 		  NULL, NULL,
1264 		  _gtk_marshal_VOID__BOXED_BOXED,
1265 		  G_TYPE_NONE, 2,
1266 		  GTK_TYPE_TREE_ITER,
1267 		  GTK_TYPE_TREE_PATH);
1268   g_signal_set_va_marshaller (tree_view_signals[ROW_EXPANDED],
1269                               G_TYPE_FROM_CLASS (o_class),
1270                               _gtk_marshal_VOID__BOXED_BOXEDv);
1271 
1272   /**
1273    * GtkTreeView::row-collapsed:
1274    * @tree_view: the object on which the signal is emitted
1275    * @iter: the tree iter of the collapsed row
1276    * @path: a tree path that points to the row
1277    *
1278    * The given row has been collapsed (child nodes are hidden).
1279    */
1280   tree_view_signals[ROW_COLLAPSED] =
1281     g_signal_new (I_("row-collapsed"),
1282 		  G_TYPE_FROM_CLASS (o_class),
1283 		  G_SIGNAL_RUN_LAST,
1284 		  G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed),
1285 		  NULL, NULL,
1286 		  _gtk_marshal_VOID__BOXED_BOXED,
1287 		  G_TYPE_NONE, 2,
1288 		  GTK_TYPE_TREE_ITER,
1289 		  GTK_TYPE_TREE_PATH);
1290   g_signal_set_va_marshaller (tree_view_signals[ROW_COLLAPSED],
1291                               G_TYPE_FROM_CLASS (o_class),
1292                               _gtk_marshal_VOID__BOXED_BOXEDv);
1293 
1294   /**
1295    * GtkTreeView::columns-changed:
1296    * @tree_view: the object on which the signal is emitted
1297    *
1298    * The number of columns of the treeview has changed.
1299    */
1300   tree_view_signals[COLUMNS_CHANGED] =
1301     g_signal_new (I_("columns-changed"),
1302 		  G_TYPE_FROM_CLASS (o_class),
1303 		  G_SIGNAL_RUN_LAST,
1304 		  G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed),
1305 		  NULL, NULL,
1306 		  NULL,
1307 		  G_TYPE_NONE, 0);
1308 
1309   /**
1310    * GtkTreeView::cursor-changed:
1311    * @tree_view: the object on which the signal is emitted
1312    *
1313    * The position of the cursor (focused cell) has changed.
1314    */
1315   tree_view_signals[CURSOR_CHANGED] =
1316     g_signal_new (I_("cursor-changed"),
1317 		  G_TYPE_FROM_CLASS (o_class),
1318 		  G_SIGNAL_RUN_LAST,
1319 		  G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed),
1320 		  NULL, NULL,
1321 		  NULL,
1322 		  G_TYPE_NONE, 0);
1323 
1324   /**
1325    * GtkTreeView::move-cursor:
1326    * @tree_view: the object on which the signal is emitted.
1327    * @step: the granularity of the move, as a `GtkMovementStep`.
1328    *     %GTK_MOVEMENT_LOGICAL_POSITIONS, %GTK_MOVEMENT_VISUAL_POSITIONS,
1329    *     %GTK_MOVEMENT_DISPLAY_LINES, %GTK_MOVEMENT_PAGES and
1330    *     %GTK_MOVEMENT_BUFFER_ENDS are supported.
1331    *     %GTK_MOVEMENT_LOGICAL_POSITIONS and %GTK_MOVEMENT_VISUAL_POSITIONS
1332    *     are treated identically.
1333    * @direction: the direction to move: +1 to move forwards; -1 to move
1334    *     backwards. The resulting movement is undefined for all other values.
1335    * @extend: whether to extend the selection
1336    * @modify: whether to modify the selection
1337    *
1338    * The `GtkTreeView`::move-cursor signal is a [keybinding
1339    * signal][class@Gtk.SignalAction] which gets emitted when the user
1340    * presses one of the cursor keys.
1341    *
1342    * Applications should not connect to it, but may emit it with
1343    * g_signal_emit_by_name() if they need to control the cursor
1344    * programmatically. In contrast to gtk_tree_view_set_cursor() and
1345    * gtk_tree_view_set_cursor_on_cell() when moving horizontally
1346    * `GtkTreeView`::move-cursor does not reset the current selection.
1347    *
1348    * Returns: %TRUE if @step is supported, %FALSE otherwise.
1349    */
1350   tree_view_signals[MOVE_CURSOR] =
1351     g_signal_new (I_("move-cursor"),
1352 		  G_TYPE_FROM_CLASS (o_class),
1353 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1354 		  G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor),
1355 		  NULL, NULL,
1356 		  _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEAN,
1357 		  G_TYPE_BOOLEAN, 4,
1358 		  GTK_TYPE_MOVEMENT_STEP,
1359 		  G_TYPE_INT,
1360                   G_TYPE_BOOLEAN,
1361                   G_TYPE_BOOLEAN);
1362   g_signal_set_va_marshaller (tree_view_signals[MOVE_CURSOR],
1363                               G_TYPE_FROM_CLASS (o_class),
1364                               _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEANv);
1365 
1366   tree_view_signals[SELECT_ALL] =
1367     g_signal_new (I_("select-all"),
1368 		  G_TYPE_FROM_CLASS (o_class),
1369 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1370 		  G_STRUCT_OFFSET (GtkTreeViewClass, select_all),
1371 		  NULL, NULL,
1372 		  _gtk_marshal_BOOLEAN__VOID,
1373 		  G_TYPE_BOOLEAN, 0);
1374   g_signal_set_va_marshaller (tree_view_signals[SELECT_ALL],
1375                               G_TYPE_FROM_CLASS (o_class),
1376                               _gtk_marshal_BOOLEAN__VOIDv);
1377 
1378   tree_view_signals[UNSELECT_ALL] =
1379     g_signal_new (I_("unselect-all"),
1380 		  G_TYPE_FROM_CLASS (o_class),
1381 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1382 		  G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all),
1383 		  NULL, NULL,
1384 		  _gtk_marshal_BOOLEAN__VOID,
1385 		  G_TYPE_BOOLEAN, 0);
1386   g_signal_set_va_marshaller (tree_view_signals[UNSELECT_ALL],
1387                               G_TYPE_FROM_CLASS (o_class),
1388                               _gtk_marshal_BOOLEAN__VOIDv);
1389 
1390   tree_view_signals[SELECT_CURSOR_ROW] =
1391     g_signal_new (I_("select-cursor-row"),
1392 		  G_TYPE_FROM_CLASS (o_class),
1393 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1394 		  G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row),
1395 		  NULL, NULL,
1396 		  _gtk_marshal_BOOLEAN__BOOLEAN,
1397 		  G_TYPE_BOOLEAN, 1,
1398 		  G_TYPE_BOOLEAN);
1399   g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_ROW],
1400                               G_TYPE_FROM_CLASS (o_class),
1401                               _gtk_marshal_BOOLEAN__BOOLEANv);
1402 
1403   tree_view_signals[TOGGLE_CURSOR_ROW] =
1404     g_signal_new (I_("toggle-cursor-row"),
1405 		  G_TYPE_FROM_CLASS (o_class),
1406 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1407 		  G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row),
1408 		  NULL, NULL,
1409 		  _gtk_marshal_BOOLEAN__VOID,
1410 		  G_TYPE_BOOLEAN, 0);
1411   g_signal_set_va_marshaller (tree_view_signals[TOGGLE_CURSOR_ROW],
1412                               G_TYPE_FROM_CLASS (o_class),
1413                               _gtk_marshal_BOOLEAN__VOIDv);
1414 
1415   tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] =
1416     g_signal_new (I_("expand-collapse-cursor-row"),
1417 		  G_TYPE_FROM_CLASS (o_class),
1418 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1419 		  G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row),
1420 		  NULL, NULL,
1421 		  _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN,
1422 		  G_TYPE_BOOLEAN, 3,
1423 		  G_TYPE_BOOLEAN,
1424 		  G_TYPE_BOOLEAN,
1425 		  G_TYPE_BOOLEAN);
1426   g_signal_set_va_marshaller (tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW],
1427                               G_TYPE_FROM_CLASS (o_class),
1428                               _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEANv);
1429 
1430   tree_view_signals[SELECT_CURSOR_PARENT] =
1431     g_signal_new (I_("select-cursor-parent"),
1432 		  G_TYPE_FROM_CLASS (o_class),
1433 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1434 		  G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent),
1435 		  NULL, NULL,
1436 		  _gtk_marshal_BOOLEAN__VOID,
1437 		  G_TYPE_BOOLEAN, 0);
1438   g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_PARENT],
1439                               G_TYPE_FROM_CLASS (o_class),
1440                               _gtk_marshal_BOOLEAN__VOIDv);
1441 
1442   tree_view_signals[START_INTERACTIVE_SEARCH] =
1443     g_signal_new (I_("start-interactive-search"),
1444 		  G_TYPE_FROM_CLASS (o_class),
1445 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1446 		  G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search),
1447 		  NULL, NULL,
1448 		  _gtk_marshal_BOOLEAN__VOID,
1449 		  G_TYPE_BOOLEAN, 0);
1450   g_signal_set_va_marshaller (tree_view_signals[START_INTERACTIVE_SEARCH],
1451                               G_TYPE_FROM_CLASS (o_class),
1452                               _gtk_marshal_BOOLEAN__VOIDv);
1453 
1454   /* Key bindings */
1455   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Up, 0, TRUE,
1456 				  GTK_MOVEMENT_DISPLAY_LINES, -1);
1457   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Up, 0, TRUE,
1458 				  GTK_MOVEMENT_DISPLAY_LINES, -1);
1459 
1460   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Down, 0, TRUE,
1461 				  GTK_MOVEMENT_DISPLAY_LINES, 1);
1462   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Down, 0, TRUE,
1463 				  GTK_MOVEMENT_DISPLAY_LINES, 1);
1464 
1465   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_p, GDK_CONTROL_MASK, FALSE,
1466 				  GTK_MOVEMENT_DISPLAY_LINES, -1);
1467 
1468   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_n, GDK_CONTROL_MASK, FALSE,
1469 				  GTK_MOVEMENT_DISPLAY_LINES, 1);
1470 
1471   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Home, 0, TRUE,
1472 				  GTK_MOVEMENT_BUFFER_ENDS, -1);
1473   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Home, 0, TRUE,
1474 				  GTK_MOVEMENT_BUFFER_ENDS, -1);
1475 
1476   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_End, 0, TRUE,
1477 				  GTK_MOVEMENT_BUFFER_ENDS, 1);
1478   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_End, 0, TRUE,
1479 				  GTK_MOVEMENT_BUFFER_ENDS, 1);
1480 
1481   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Up, 0, TRUE,
1482 				  GTK_MOVEMENT_PAGES, -1);
1483   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0, TRUE,
1484 				  GTK_MOVEMENT_PAGES, -1);
1485 
1486   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Page_Down, 0, TRUE,
1487 				  GTK_MOVEMENT_PAGES, 1);
1488   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0, TRUE,
1489 				  GTK_MOVEMENT_PAGES, 1);
1490 
1491   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Right, 0, FALSE,
1492                                   GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1493   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_Left, 0, FALSE,
1494                                   GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1495 
1496   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Right, 0, FALSE,
1497                                   GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1498   gtk_tree_view_add_move_binding (widget_class, GDK_KEY_KP_Left, 0, FALSE,
1499                                   GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1500 
1501   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL);
1502   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", NULL);
1503 
1504   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", NULL);
1505   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", NULL);
1506 
1507   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", NULL);
1508   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", NULL);
1509 
1510   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE);
1511   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", "(b)", TRUE);
1512 
1513   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_space, 0, "select-cursor-row", "(b)", TRUE);
1514   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Space, 0, "select-cursor-row", "(b)", TRUE);
1515   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0, "select-cursor-row", "(b)", TRUE);
1516   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0, "select-cursor-row", "(b)", TRUE);
1517   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0, "select-cursor-row", "(b)", TRUE);
1518 
1519   /* expand and collapse rows */
1520   gtk_widget_class_add_binding_signal (widget_class,
1521                                        GDK_KEY_plus, 0,
1522                                        "expand-collapse-cursor-row",
1523                                        "(bbb)", TRUE, TRUE, FALSE);
1524 
1525   gtk_widget_class_add_binding_signal (widget_class,
1526                                        GDK_KEY_asterisk, 0,
1527                                        "expand-collapse-cursor-row",
1528                                        "(bbb)", TRUE, TRUE, TRUE);
1529   gtk_widget_class_add_binding_signal (widget_class,
1530                                        GDK_KEY_KP_Multiply, 0,
1531                                        "expand-collapse-cursor-row",
1532                                        "(bbb)", TRUE, TRUE, TRUE);
1533 
1534   gtk_widget_class_add_binding_signal (widget_class,
1535                                        GDK_KEY_slash, 0,
1536                                        "expand-collapse-cursor-row",
1537                                        "(bbb)", TRUE, FALSE, FALSE);
1538   gtk_widget_class_add_binding_signal (widget_class,
1539                                        GDK_KEY_KP_Divide, 0,
1540                                        "expand-collapse-cursor-row",
1541                                        "(bbb)", TRUE, FALSE, FALSE);
1542 
1543   /* Not doable on US keyboards */
1544   gtk_widget_class_add_binding_signal (widget_class,
1545                                        GDK_KEY_plus, GDK_SHIFT_MASK,
1546                                        "expand-collapse-cursor-row",
1547                                        "(bbb)", TRUE, TRUE, TRUE);
1548   gtk_widget_class_add_binding_signal (widget_class,
1549                                        GDK_KEY_KP_Add, 0,
1550                                        "expand-collapse-cursor-row",
1551                                        "(bbb)", TRUE, TRUE, FALSE);
1552   gtk_widget_class_add_binding_signal (widget_class,
1553                                        GDK_KEY_KP_Add, GDK_SHIFT_MASK,
1554                                        "expand-collapse-cursor-row",
1555                                        "(bbb)", TRUE, TRUE, TRUE);
1556   gtk_widget_class_add_binding_signal (widget_class,
1557                                        GDK_KEY_KP_Add, GDK_SHIFT_MASK,
1558                                        "expand-collapse-cursor-row",
1559                                        "(bbb)", TRUE, TRUE, TRUE);
1560   gtk_widget_class_add_binding_signal (widget_class,
1561                                        GDK_KEY_Right, GDK_SHIFT_MASK,
1562                                        "expand-collapse-cursor-row",
1563                                        "(bbb)", FALSE, TRUE, TRUE);
1564   gtk_widget_class_add_binding_signal (widget_class,
1565                                        GDK_KEY_KP_Right, GDK_SHIFT_MASK,
1566                                        "expand-collapse-cursor-row",
1567                                        "(bbb)", FALSE, TRUE, TRUE);
1568   gtk_widget_class_add_binding_signal (widget_class,
1569                                        GDK_KEY_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1570                                        "expand-collapse-cursor-row",
1571                                        "(bbb)", FALSE, TRUE, TRUE);
1572   gtk_widget_class_add_binding_signal (widget_class,
1573                                        GDK_KEY_KP_Right, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1574                                        "expand-collapse-cursor-row",
1575                                        "(bbb)", FALSE, TRUE, TRUE);
1576 
1577   gtk_widget_class_add_binding_signal (widget_class,
1578                                        GDK_KEY_minus, 0,
1579                                        "expand-collapse-cursor-row",
1580                                        "(bbb)", TRUE, FALSE, FALSE);
1581   gtk_widget_class_add_binding_signal (widget_class,
1582                                        GDK_KEY_minus, GDK_SHIFT_MASK,
1583                                        "expand-collapse-cursor-row",
1584                                        "(bbb)", TRUE, FALSE, TRUE);
1585   gtk_widget_class_add_binding_signal (widget_class,
1586                                        GDK_KEY_KP_Subtract, 0,
1587                                        "expand-collapse-cursor-row",
1588                                        "(bbb)", TRUE, FALSE, FALSE);
1589   gtk_widget_class_add_binding_signal (widget_class,
1590                                        GDK_KEY_KP_Subtract, GDK_SHIFT_MASK,
1591                                        "expand-collapse-cursor-row",
1592                                        "(bbb)", TRUE, FALSE, TRUE);
1593   gtk_widget_class_add_binding_signal (widget_class,
1594                                        GDK_KEY_Left, GDK_SHIFT_MASK,
1595                                        "expand-collapse-cursor-row",
1596                                        "(bbb)", FALSE, FALSE, TRUE);
1597   gtk_widget_class_add_binding_signal (widget_class,
1598                                        GDK_KEY_KP_Left, GDK_SHIFT_MASK,
1599                                        "expand-collapse-cursor-row",
1600                                        "(bbb)", FALSE, FALSE, TRUE);
1601   gtk_widget_class_add_binding_signal (widget_class,
1602                                        GDK_KEY_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1603                                        "expand-collapse-cursor-row",
1604                                        "(bbb)", FALSE, FALSE, TRUE);
1605   gtk_widget_class_add_binding_signal (widget_class,
1606                                        GDK_KEY_KP_Left, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1607                                        "expand-collapse-cursor-row",
1608                                        "(bbb)", FALSE, FALSE, TRUE);
1609 
1610   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, 0, "select-cursor-parent", NULL);
1611   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", NULL);
1612 
1613   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", NULL);
1614 
1615   gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", NULL);
1616 
1617   gtk_widget_class_set_css_name (widget_class, I_("treeview"));
1618 }
1619 
1620 static void
gtk_tree_view_init(GtkTreeView * tree_view)1621 gtk_tree_view_init (GtkTreeView *tree_view)
1622 {
1623   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
1624   GtkCssNode *widget_node;
1625   GtkGesture *gesture;
1626   GtkEventController *controller;
1627   GtkEventController **controllers;
1628   guint n_controllers, i;
1629 
1630   gtk_widget_set_overflow (GTK_WIDGET (tree_view), GTK_OVERFLOW_HIDDEN);
1631   gtk_widget_set_focusable (GTK_WIDGET (tree_view), TRUE);
1632 
1633   priv->show_expanders = TRUE;
1634   priv->draw_keyfocus = TRUE;
1635   priv->headers_visible = TRUE;
1636   priv->activate_on_single_click = FALSE;
1637 
1638   /* We need some padding */
1639   priv->dy = 0;
1640   priv->cursor_offset = 0;
1641   priv->n_columns = 0;
1642   priv->header_height = 1;
1643   priv->x_drag = 0;
1644   priv->drag_pos = -1;
1645   priv->header_has_focus = FALSE;
1646   priv->press_start_x = -1;
1647   priv->press_start_y = -1;
1648   priv->reorderable = FALSE;
1649   priv->presize_handler_tick_cb = 0;
1650   priv->scroll_sync_timer = 0;
1651   priv->fixed_height = -1;
1652   priv->fixed_height_mode = FALSE;
1653   priv->fixed_height_check = 0;
1654   priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view);
1655   priv->enable_search = TRUE;
1656   priv->search_column = -1;
1657   priv->search_equal_func = gtk_tree_view_search_equal_func;
1658   priv->search_custom_entry_set = FALSE;
1659   priv->typeselect_flush_timeout = 0;
1660   priv->width = 0;
1661   priv->expander_size = -1;
1662 
1663   priv->hover_selection = FALSE;
1664   priv->hover_expand = FALSE;
1665 
1666   priv->level_indentation = 0;
1667 
1668   priv->rubber_banding_enable = FALSE;
1669 
1670   priv->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE;
1671   priv->tree_lines_enabled = FALSE;
1672 
1673   priv->tooltip_column = -1;
1674 
1675   priv->event_last_x = -10000;
1676   priv->event_last_y = -10000;
1677 
1678   gtk_tree_view_do_set_vadjustment (tree_view, NULL);
1679   gtk_tree_view_do_set_hadjustment (tree_view, NULL);
1680 
1681   gtk_widget_add_css_class (GTK_WIDGET (tree_view), "view");
1682 
1683   widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view));
1684   priv->header_node = gtk_css_node_new ();
1685   gtk_css_node_set_name (priv->header_node, g_quark_from_static_string ("header"));
1686   gtk_css_node_set_parent (priv->header_node, widget_node);
1687   gtk_css_node_set_state (priv->header_node, gtk_css_node_get_state (widget_node));
1688   g_object_unref (priv->header_node);
1689 
1690   controller = gtk_event_controller_key_new ();
1691   g_signal_connect (controller, "key-pressed",
1692                     G_CALLBACK (gtk_tree_view_forward_controller_key_pressed), tree_view);
1693   gtk_widget_add_controller (GTK_WIDGET (tree_view), controller);
1694 
1695   controllers = gtk_widget_list_controllers (GTK_WIDGET (tree_view), GTK_PHASE_BUBBLE, &n_controllers);
1696   for (i = 0; i < n_controllers; i ++)
1697     {
1698       controller = controllers[i];
1699       if (GTK_IS_SHORTCUT_CONTROLLER (controller))
1700         {
1701           g_object_ref (controller);
1702           gtk_widget_remove_controller (GTK_WIDGET (tree_view), controller);
1703           gtk_widget_add_controller (GTK_WIDGET (tree_view), controller);
1704           break;
1705         }
1706     }
1707   g_free (controllers);
1708 
1709   priv->click_gesture = gtk_gesture_click_new ();
1710   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->click_gesture), 0);
1711   g_signal_connect (priv->click_gesture, "pressed",
1712                     G_CALLBACK (gtk_tree_view_click_gesture_pressed), tree_view);
1713   g_signal_connect (priv->click_gesture, "released",
1714                     G_CALLBACK (gtk_tree_view_click_gesture_released), tree_view);
1715   gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->click_gesture));
1716 
1717   gesture = gtk_gesture_click_new ();
1718   g_signal_connect (gesture, "pressed",
1719                     G_CALLBACK (gtk_tree_view_column_click_gesture_pressed), tree_view);
1720   gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
1721                                               GTK_PHASE_CAPTURE);
1722   gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (gesture));
1723 
1724   priv->drag_gesture = gtk_gesture_drag_new ();
1725   g_signal_connect (priv->drag_gesture, "drag-begin",
1726                     G_CALLBACK (gtk_tree_view_drag_gesture_begin), tree_view);
1727   g_signal_connect (priv->drag_gesture, "drag-update",
1728                     G_CALLBACK (gtk_tree_view_drag_gesture_update), tree_view);
1729   g_signal_connect (priv->drag_gesture, "drag-end",
1730                     G_CALLBACK (gtk_tree_view_drag_gesture_end), tree_view);
1731   gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->drag_gesture));
1732 
1733   priv->column_drag_gesture = gtk_gesture_drag_new ();
1734   g_signal_connect (priv->column_drag_gesture, "drag-begin",
1735                     G_CALLBACK (gtk_tree_view_column_drag_gesture_begin), tree_view);
1736   g_signal_connect (priv->column_drag_gesture, "drag-update",
1737                     G_CALLBACK (gtk_tree_view_column_drag_gesture_update), tree_view);
1738   g_signal_connect (priv->column_drag_gesture, "drag-end",
1739                     G_CALLBACK (gtk_tree_view_column_drag_gesture_end), tree_view);
1740   gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->column_drag_gesture),
1741                                               GTK_PHASE_CAPTURE);
1742   gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (priv->column_drag_gesture));
1743 
1744   controller = gtk_event_controller_motion_new ();
1745   g_signal_connect (controller, "enter",
1746                     G_CALLBACK (gtk_tree_view_motion_controller_enter), tree_view);
1747   g_signal_connect (controller, "leave",
1748                     G_CALLBACK (gtk_tree_view_motion_controller_leave), tree_view);
1749   g_signal_connect (controller, "motion",
1750                     G_CALLBACK (gtk_tree_view_motion_controller_motion), tree_view);
1751   gtk_widget_add_controller (GTK_WIDGET (tree_view), controller);
1752 
1753   controller = gtk_event_controller_key_new ();
1754   g_signal_connect (controller, "key-pressed",
1755                     G_CALLBACK (gtk_tree_view_key_controller_key_pressed), tree_view);
1756   g_signal_connect (controller, "key-released",
1757                     G_CALLBACK (gtk_tree_view_key_controller_key_released), tree_view);
1758   gtk_widget_add_controller (GTK_WIDGET (tree_view), controller);
1759 
1760   controller = gtk_event_controller_focus_new ();
1761   g_signal_connect (controller, "leave",
1762                     G_CALLBACK (gtk_tree_view_focus_controller_focus_out), tree_view);
1763   gtk_widget_add_controller (GTK_WIDGET (tree_view), controller);
1764 }
1765 
1766 
1767 
1768 /* GObject Methods
1769  */
1770 
1771 static void
gtk_tree_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1772 gtk_tree_view_set_property (GObject         *object,
1773 			    guint            prop_id,
1774 			    const GValue    *value,
1775 			    GParamSpec      *pspec)
1776 {
1777   GtkTreeView *tree_view = GTK_TREE_VIEW (object);
1778   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
1779 
1780   switch (prop_id)
1781     {
1782     case PROP_MODEL:
1783       gtk_tree_view_set_model (tree_view, g_value_get_object (value));
1784       break;
1785     case PROP_HADJUSTMENT:
1786       gtk_tree_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1787       break;
1788     case PROP_VADJUSTMENT:
1789       gtk_tree_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1790       break;
1791     case PROP_HSCROLL_POLICY:
1792       if (priv->hscroll_policy != g_value_get_enum (value))
1793         {
1794           priv->hscroll_policy = g_value_get_enum (value);
1795           gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1796           g_object_notify_by_pspec (object, pspec);
1797         }
1798       break;
1799     case PROP_VSCROLL_POLICY:
1800       if (priv->vscroll_policy != g_value_get_enum (value))
1801         {
1802           priv->vscroll_policy = g_value_get_enum (value);
1803           gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1804           g_object_notify_by_pspec (object, pspec);
1805         }
1806       break;
1807     case PROP_HEADERS_VISIBLE:
1808       gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1809       break;
1810     case PROP_HEADERS_CLICKABLE:
1811       gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1812       break;
1813     case PROP_EXPANDER_COLUMN:
1814       gtk_tree_view_set_expander_column (tree_view, g_value_get_object (value));
1815       break;
1816     case PROP_REORDERABLE:
1817       gtk_tree_view_set_reorderable (tree_view, g_value_get_boolean (value));
1818       break;
1819     case PROP_ENABLE_SEARCH:
1820       gtk_tree_view_set_enable_search (tree_view, g_value_get_boolean (value));
1821       break;
1822     case PROP_SEARCH_COLUMN:
1823       gtk_tree_view_set_search_column (tree_view, g_value_get_int (value));
1824       break;
1825     case PROP_FIXED_HEIGHT_MODE:
1826       gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value));
1827       break;
1828     case PROP_HOVER_SELECTION:
1829       if (priv->hover_selection != g_value_get_boolean (value))
1830         {
1831           priv->hover_selection = g_value_get_boolean (value);
1832           g_object_notify_by_pspec (object, pspec);
1833         }
1834       break;
1835     case PROP_HOVER_EXPAND:
1836       if (priv->hover_expand != g_value_get_boolean (value))
1837         {
1838           priv->hover_expand = g_value_get_boolean (value);
1839           g_object_notify_by_pspec (object, pspec);
1840         }
1841       break;
1842     case PROP_SHOW_EXPANDERS:
1843       gtk_tree_view_set_show_expanders (tree_view, g_value_get_boolean (value));
1844       break;
1845     case PROP_LEVEL_INDENTATION:
1846       if (priv->level_indentation != g_value_get_int (value))
1847         {
1848           priv->level_indentation = g_value_get_int (value);
1849           g_object_notify_by_pspec (object, pspec);
1850         }
1851       break;
1852     case PROP_RUBBER_BANDING:
1853       if (priv->rubber_banding_enable != g_value_get_boolean (value))
1854         {
1855           priv->rubber_banding_enable = g_value_get_boolean (value);
1856           g_object_notify_by_pspec (object, pspec);
1857         }
1858       break;
1859     case PROP_ENABLE_GRID_LINES:
1860       gtk_tree_view_set_grid_lines (tree_view, g_value_get_enum (value));
1861       break;
1862     case PROP_ENABLE_TREE_LINES:
1863       gtk_tree_view_set_enable_tree_lines (tree_view, g_value_get_boolean (value));
1864       break;
1865     case PROP_TOOLTIP_COLUMN:
1866       gtk_tree_view_set_tooltip_column (tree_view, g_value_get_int (value));
1867       break;
1868     case PROP_ACTIVATE_ON_SINGLE_CLICK:
1869       gtk_tree_view_set_activate_on_single_click (tree_view, g_value_get_boolean (value));
1870       break;
1871     default:
1872       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1873       break;
1874     }
1875 }
1876 
1877 static void
gtk_tree_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1878 gtk_tree_view_get_property (GObject    *object,
1879 			    guint       prop_id,
1880 			    GValue     *value,
1881 			    GParamSpec *pspec)
1882 {
1883   GtkTreeView *tree_view = GTK_TREE_VIEW (object);
1884   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
1885 
1886   switch (prop_id)
1887     {
1888     case PROP_MODEL:
1889       g_value_set_object (value, priv->model);
1890       break;
1891     case PROP_HADJUSTMENT:
1892       g_value_set_object (value, priv->hadjustment);
1893       break;
1894     case PROP_VADJUSTMENT:
1895       g_value_set_object (value, priv->vadjustment);
1896       break;
1897     case PROP_HSCROLL_POLICY:
1898       g_value_set_enum (value, priv->hscroll_policy);
1899       break;
1900     case PROP_VSCROLL_POLICY:
1901       g_value_set_enum (value, priv->vscroll_policy);
1902       break;
1903     case PROP_HEADERS_VISIBLE:
1904       g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view));
1905       break;
1906     case PROP_HEADERS_CLICKABLE:
1907       g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view));
1908       break;
1909     case PROP_EXPANDER_COLUMN:
1910       g_value_set_object (value, priv->expander_column);
1911       break;
1912     case PROP_REORDERABLE:
1913       g_value_set_boolean (value, priv->reorderable);
1914       break;
1915     case PROP_ENABLE_SEARCH:
1916       g_value_set_boolean (value, priv->enable_search);
1917       break;
1918     case PROP_SEARCH_COLUMN:
1919       g_value_set_int (value, priv->search_column);
1920       break;
1921     case PROP_FIXED_HEIGHT_MODE:
1922       g_value_set_boolean (value, priv->fixed_height_mode);
1923       break;
1924     case PROP_HOVER_SELECTION:
1925       g_value_set_boolean (value, priv->hover_selection);
1926       break;
1927     case PROP_HOVER_EXPAND:
1928       g_value_set_boolean (value, priv->hover_expand);
1929       break;
1930     case PROP_SHOW_EXPANDERS:
1931       g_value_set_boolean (value, priv->show_expanders);
1932       break;
1933     case PROP_LEVEL_INDENTATION:
1934       g_value_set_int (value, priv->level_indentation);
1935       break;
1936     case PROP_RUBBER_BANDING:
1937       g_value_set_boolean (value, priv->rubber_banding_enable);
1938       break;
1939     case PROP_ENABLE_GRID_LINES:
1940       g_value_set_enum (value, priv->grid_lines);
1941       break;
1942     case PROP_ENABLE_TREE_LINES:
1943       g_value_set_boolean (value, priv->tree_lines_enabled);
1944       break;
1945     case PROP_TOOLTIP_COLUMN:
1946       g_value_set_int (value, priv->tooltip_column);
1947       break;
1948     case PROP_ACTIVATE_ON_SINGLE_CLICK:
1949       g_value_set_boolean (value, priv->activate_on_single_click);
1950       break;
1951     default:
1952       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1953       break;
1954     }
1955 }
1956 
1957 static void
gtk_tree_view_finalize(GObject * object)1958 gtk_tree_view_finalize (GObject *object)
1959 {
1960   G_OBJECT_CLASS (gtk_tree_view_parent_class)->finalize (object);
1961 }
1962 
1963 
1964 static GtkBuildableIface *parent_buildable_iface;
1965 
1966 static void
gtk_tree_view_buildable_init(GtkBuildableIface * iface)1967 gtk_tree_view_buildable_init (GtkBuildableIface *iface)
1968 {
1969   parent_buildable_iface = g_type_interface_peek_parent (iface);
1970   iface->add_child = gtk_tree_view_buildable_add_child;
1971   iface->get_internal_child = gtk_tree_view_buildable_get_internal_child;
1972 }
1973 
1974 static void
gtk_tree_view_buildable_add_child(GtkBuildable * tree_view,GtkBuilder * builder,GObject * child,const char * type)1975 gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
1976 				   GtkBuilder  *builder,
1977 				   GObject     *child,
1978 				   const char *type)
1979 {
1980   if (GTK_IS_TREE_VIEW_COLUMN (child))
1981     gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child));
1982   else
1983     parent_buildable_iface->add_child (tree_view, builder, child, type);
1984 }
1985 
1986 static GObject *
gtk_tree_view_buildable_get_internal_child(GtkBuildable * buildable,GtkBuilder * builder,const char * childname)1987 gtk_tree_view_buildable_get_internal_child (GtkBuildable      *buildable,
1988 					    GtkBuilder        *builder,
1989 					    const char        *childname)
1990 {
1991   GtkTreeView *tree_view = GTK_TREE_VIEW (buildable);
1992   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
1993 
1994   if (strcmp (childname, "selection") == 0)
1995     return G_OBJECT (priv->selection);
1996 
1997   return parent_buildable_iface->get_internal_child (buildable,
1998                                                      builder,
1999                                                      childname);
2000 }
2001 
2002 /* GtkWidget Methods
2003  */
2004 
2005 static void
gtk_tree_view_free_rbtree(GtkTreeView * tree_view)2006 gtk_tree_view_free_rbtree (GtkTreeView *tree_view)
2007 {
2008   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2009 
2010   gtk_tree_rbtree_free (priv->tree);
2011 
2012   priv->tree = NULL;
2013   priv->button_pressed_node = NULL;
2014   priv->button_pressed_tree = NULL;
2015   priv->prelight_tree = NULL;
2016   priv->prelight_node = NULL;
2017 }
2018 
2019 static void
gtk_tree_view_destroy_search_popover(GtkTreeView * tree_view)2020 gtk_tree_view_destroy_search_popover (GtkTreeView *tree_view)
2021 {
2022   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2023 
2024   gtk_widget_unparent (priv->search_popover);
2025 
2026   priv->search_popover = NULL;
2027   priv->search_entry = NULL;
2028   priv->search_entry_changed_id = 0;
2029 }
2030 
2031 static void
gtk_tree_view_dispose(GObject * object)2032 gtk_tree_view_dispose (GObject *object)
2033 {
2034   GtkTreeView *tree_view = GTK_TREE_VIEW (object);
2035   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2036   GList *list;
2037 
2038   gtk_tree_view_stop_editing (tree_view, TRUE);
2039   gtk_tree_view_stop_rubber_band (tree_view);
2040 
2041   if (priv->columns != NULL)
2042     {
2043       list = priv->columns;
2044       while (list)
2045 	{
2046 	  GtkTreeViewColumn *column;
2047 	  column = GTK_TREE_VIEW_COLUMN (list->data);
2048 	  list = list->next;
2049 	  gtk_tree_view_remove_column (tree_view, column);
2050 	}
2051       priv->columns = NULL;
2052     }
2053 
2054   if (priv->tree != NULL)
2055     {
2056       gtk_tree_view_unref_and_check_selection_tree (tree_view, priv->tree);
2057 
2058       gtk_tree_view_free_rbtree (tree_view);
2059     }
2060 
2061   if (priv->selection != NULL)
2062     {
2063       _gtk_tree_selection_set_tree_view (priv->selection, NULL);
2064       g_object_unref (priv->selection);
2065       priv->selection = NULL;
2066     }
2067 
2068   g_clear_pointer (&priv->scroll_to_path, gtk_tree_row_reference_free);
2069   g_clear_pointer (&priv->drag_dest_row, gtk_tree_row_reference_free);
2070   g_clear_pointer (&priv->top_row, gtk_tree_row_reference_free);
2071 
2072   if (priv->column_drop_func_data &&
2073       priv->column_drop_func_data_destroy)
2074     {
2075       priv->column_drop_func_data_destroy (priv->column_drop_func_data);
2076       priv->column_drop_func_data = NULL;
2077     }
2078 
2079   gtk_tree_row_reference_free (priv->anchor);
2080   priv->anchor = NULL;
2081 
2082   /* destroy interactive search dialog */
2083   if (priv->search_popover)
2084     {
2085       gtk_tree_view_destroy_search_popover (tree_view);
2086       if (priv->typeselect_flush_timeout)
2087 	{
2088 	  g_source_remove (priv->typeselect_flush_timeout);
2089 	  priv->typeselect_flush_timeout = 0;
2090 	}
2091     }
2092 
2093   if (priv->search_custom_entry_set)
2094     {
2095       GtkEventController *controller;
2096 
2097       g_signal_handlers_disconnect_by_func (priv->search_entry,
2098                                             G_CALLBACK (gtk_tree_view_search_init),
2099                                             tree_view);
2100 
2101       if (GTK_IS_ENTRY (priv->search_entry))
2102         controller = gtk_entry_get_key_controller (GTK_ENTRY (priv->search_entry));
2103       else
2104         controller = gtk_search_entry_get_key_controller (GTK_SEARCH_ENTRY (priv->search_entry));
2105       g_signal_handlers_disconnect_by_func (controller,
2106                                             G_CALLBACK (gtk_tree_view_search_key_pressed),
2107                                             tree_view);
2108 
2109       g_object_unref (priv->search_entry);
2110 
2111       priv->search_entry = NULL;
2112       priv->search_custom_entry_set = FALSE;
2113     }
2114 
2115   if (priv->search_destroy && priv->search_user_data)
2116     {
2117       priv->search_destroy (priv->search_user_data);
2118       priv->search_user_data = NULL;
2119     }
2120 
2121   if (priv->search_position_destroy && priv->search_position_user_data)
2122     {
2123       priv->search_position_destroy (priv->search_position_user_data);
2124       priv->search_position_user_data = NULL;
2125     }
2126 
2127   if (priv->row_separator_destroy && priv->row_separator_data)
2128     {
2129       priv->row_separator_destroy (priv->row_separator_data);
2130       priv->row_separator_data = NULL;
2131     }
2132 
2133   gtk_tree_view_set_model (tree_view, NULL);
2134 
2135   g_clear_object (&priv->hadjustment);
2136   g_clear_object (&priv->vadjustment);
2137   g_clear_object (&priv->horizontal_grid_line_texture);
2138   g_clear_object (&priv->vertical_grid_line_texture);
2139   g_clear_object (&priv->horizontal_tree_line_texture);
2140   g_clear_object (&priv->vertical_tree_line_texture);
2141 
2142   G_OBJECT_CLASS (gtk_tree_view_parent_class)->dispose (object);
2143 }
2144 
2145 /* GtkWidget::map helper */
2146 static void
gtk_tree_view_map_buttons(GtkTreeView * tree_view)2147 gtk_tree_view_map_buttons (GtkTreeView *tree_view)
2148 {
2149   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2150   GList *list;
2151 
2152   g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
2153 
2154   if (priv->headers_visible)
2155     {
2156       GtkTreeViewColumn *column;
2157       GtkWidget         *button;
2158 
2159       for (list = priv->columns; list; list = list->next)
2160 	{
2161 	  column = list->data;
2162 	  button = gtk_tree_view_column_get_button (column);
2163 
2164           if (gtk_tree_view_column_get_visible (column) && button)
2165             gtk_widget_show (button);
2166 
2167           if (gtk_widget_get_visible (button) &&
2168               !gtk_widget_get_mapped (button))
2169             gtk_widget_map (button);
2170 	}
2171     }
2172 }
2173 
2174 static void
gtk_tree_view_map(GtkWidget * widget)2175 gtk_tree_view_map (GtkWidget *widget)
2176 {
2177   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2178   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2179   GList *tmp_list;
2180 
2181   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->map (widget);
2182 
2183   tmp_list = priv->children;
2184   while (tmp_list)
2185     {
2186       GtkTreeViewChild *child = tmp_list->data;
2187       tmp_list = tmp_list->next;
2188 
2189       if (gtk_widget_get_visible (child->widget))
2190 	{
2191 	  if (!gtk_widget_get_mapped (child->widget))
2192 	    gtk_widget_map (child->widget);
2193 	}
2194     }
2195 
2196   gtk_tree_view_map_buttons (tree_view);
2197 }
2198 
2199 static void
gtk_tree_view_realize(GtkWidget * widget)2200 gtk_tree_view_realize (GtkWidget *widget)
2201 {
2202   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2203   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2204   GList *tmp_list;
2205 
2206   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->realize (widget);
2207 
2208   for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next)
2209     _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data));
2210 
2211   /* Need to call those here, since they create GCs */
2212   gtk_tree_view_set_grid_lines (tree_view, priv->grid_lines);
2213   gtk_tree_view_set_enable_tree_lines (tree_view, priv->tree_lines_enabled);
2214 
2215   install_presize_handler (tree_view);
2216 }
2217 
2218 static void
gtk_tree_view_unrealize(GtkWidget * widget)2219 gtk_tree_view_unrealize (GtkWidget *widget)
2220 {
2221   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2222   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2223 
2224   if (priv->scroll_timeout != 0)
2225     {
2226       g_source_remove (priv->scroll_timeout);
2227       priv->scroll_timeout = 0;
2228     }
2229 
2230   if (priv->auto_expand_timeout != 0)
2231     {
2232       g_source_remove (priv->auto_expand_timeout);
2233       priv->auto_expand_timeout = 0;
2234     }
2235 
2236   if (priv->open_dest_timeout != 0)
2237     {
2238       g_source_remove (priv->open_dest_timeout);
2239       priv->open_dest_timeout = 0;
2240     }
2241 
2242   if (priv->presize_handler_tick_cb != 0)
2243     {
2244       gtk_widget_remove_tick_callback (widget, priv->presize_handler_tick_cb);
2245       priv->presize_handler_tick_cb = 0;
2246     }
2247 
2248   if (priv->validate_rows_timer != 0)
2249     {
2250       g_source_remove (priv->validate_rows_timer);
2251       priv->validate_rows_timer = 0;
2252     }
2253 
2254   if (priv->scroll_sync_timer != 0)
2255     {
2256       g_source_remove (priv->scroll_sync_timer);
2257       priv->scroll_sync_timer = 0;
2258     }
2259 
2260   if (priv->typeselect_flush_timeout)
2261     {
2262       g_source_remove (priv->typeselect_flush_timeout);
2263       priv->typeselect_flush_timeout = 0;
2264     }
2265 
2266   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unrealize (widget);
2267 }
2268 
2269 static void
gtk_tree_view_unroot(GtkWidget * widget)2270 gtk_tree_view_unroot (GtkWidget *widget)
2271 {
2272   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2273   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2274 
2275   /* break ref cycles */
2276   g_clear_pointer (&priv->scroll_to_path, gtk_tree_row_reference_free);
2277   g_clear_pointer (&priv->drag_dest_row, gtk_tree_row_reference_free);
2278   g_clear_pointer (&priv->top_row, gtk_tree_row_reference_free);
2279 
2280   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unroot (widget);
2281 }
2282 
2283 /* GtkWidget::get_preferred_height helper */
2284 static void
gtk_tree_view_update_height(GtkTreeView * tree_view)2285 gtk_tree_view_update_height (GtkTreeView *tree_view)
2286 {
2287   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2288   GList *list;
2289 
2290   priv->header_height = 0;
2291 
2292   for (list = priv->columns; list; list = list->next)
2293     {
2294       GtkRequisition     requisition;
2295       GtkTreeViewColumn *column = list->data;
2296       GtkWidget         *button = gtk_tree_view_column_get_button (column);
2297 
2298       if (button == NULL)
2299         continue;
2300 
2301       gtk_widget_get_preferred_size (button, &requisition, NULL);
2302       priv->header_height = MAX (priv->header_height, requisition.height);
2303     }
2304 }
2305 
2306 static int
gtk_tree_view_get_height(GtkTreeView * tree_view)2307 gtk_tree_view_get_height (GtkTreeView *tree_view)
2308 {
2309   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2310 
2311   if (priv->tree == NULL)
2312     return 0;
2313   else
2314     return priv->tree->root->offset;
2315 }
2316 
2317 static void
gtk_tree_view_measure(GtkWidget * widget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline)2318 gtk_tree_view_measure (GtkWidget        *widget,
2319                        GtkOrientation  orientation,
2320                        int             for_size,
2321                        int            *minimum,
2322                        int            *natural,
2323                        int            *minimum_baseline,
2324                        int            *natural_baseline)
2325 {
2326   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2327   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2328 
2329   if (orientation == GTK_ORIENTATION_HORIZONTAL)
2330     {
2331       GList *list;
2332       GtkTreeViewColumn *column;
2333       int width = 0;
2334 
2335       /* we validate some rows initially just to make sure we have some size.
2336        * In practice, with a lot of static lists, this should get a good width.
2337        */
2338       do_validate_rows (tree_view, FALSE);
2339 
2340       /* keep this in sync with size_allocate below */
2341       for (list = priv->columns; list; list = list->next)
2342         {
2343           column = list->data;
2344           if (!gtk_tree_view_column_get_visible (column) || column == priv->drag_column)
2345             continue;
2346 
2347           width += _gtk_tree_view_column_request_width (column);
2348         }
2349 
2350       *minimum = *natural = width;
2351     }
2352   else /* VERTICAL */
2353     {
2354       int height;
2355 
2356       gtk_tree_view_update_height (tree_view);
2357       height = gtk_tree_view_get_height (tree_view) + gtk_tree_view_get_effective_header_height (tree_view);
2358 
2359       *minimum = *natural = height;
2360     }
2361 }
2362 
2363 static int
gtk_tree_view_calculate_width_before_expander(GtkTreeView * tree_view)2364 gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view)
2365 {
2366   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2367   int width = 0;
2368   GList *list;
2369   gboolean rtl;
2370 
2371   rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2372   for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
2373        list->data != priv->expander_column;
2374        list = (rtl ? list->prev : list->next))
2375     {
2376       GtkTreeViewColumn *column = list->data;
2377 
2378       width += gtk_tree_view_column_get_width (column);
2379     }
2380 
2381   return width;
2382 }
2383 
2384 /* GtkWidget::size_allocate helper */
2385 static void
gtk_tree_view_size_allocate_columns(GtkWidget * widget)2386 gtk_tree_view_size_allocate_columns (GtkWidget *widget)
2387 {
2388   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2389   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2390   const int x_offset = - gtk_adjustment_get_value (priv->hadjustment);
2391   GList *list, *first_column, *last_column;
2392   GtkTreeViewColumn *column;
2393   int widget_width, width = 0;
2394   int extra, extra_per_column;
2395   int full_requested_width = 0;
2396   int number_of_expand_columns = 0;
2397   gboolean rtl;
2398 
2399   for (last_column = g_list_last (priv->columns);
2400        last_column &&
2401        !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
2402        last_column = last_column->prev)
2403     ;
2404   if (last_column == NULL)
2405     return;
2406 
2407   for (first_column = g_list_first (priv->columns);
2408        first_column &&
2409        !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
2410        first_column = first_column->next)
2411     ;
2412 
2413   if (first_column == NULL)
2414     return;
2415 
2416   rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2417 
2418   /* find out how many extra space and expandable columns we have */
2419   for (list = priv->columns; list != last_column->next; list = list->next)
2420     {
2421       column = (GtkTreeViewColumn *)list->data;
2422 
2423       if (!gtk_tree_view_column_get_visible (column) || column == priv->drag_column)
2424 	continue;
2425 
2426       full_requested_width += _gtk_tree_view_column_request_width (column);
2427 
2428       if (gtk_tree_view_column_get_expand (column))
2429 	number_of_expand_columns++;
2430     }
2431 
2432   widget_width = gtk_widget_get_width (widget);
2433   extra = MAX (widget_width - full_requested_width, 0);
2434 
2435   if (number_of_expand_columns > 0)
2436     extra_per_column = extra/number_of_expand_columns;
2437   else
2438     extra_per_column = 0;
2439 
2440   for (list = first_column;
2441        list != last_column->next;
2442        list = list->next)
2443     {
2444       int column_width;
2445 
2446       column = list->data;
2447       column_width = _gtk_tree_view_column_request_width (column);
2448 
2449       if (!gtk_tree_view_column_get_visible (column))
2450 	continue;
2451 
2452       if (column == priv->drag_column)
2453         goto next;
2454 
2455       if (gtk_tree_view_column_get_expand (column))
2456 	{
2457 	  if (number_of_expand_columns == 1)
2458 	    {
2459 	      /* We add the remander to the last column as
2460 	       * */
2461 	      column_width += extra;
2462 	    }
2463 	  else
2464 	    {
2465 	      column_width += extra_per_column;
2466 	      extra -= extra_per_column;
2467 	      number_of_expand_columns --;
2468 	    }
2469 	}
2470       else if (number_of_expand_columns == 0 &&
2471 	       list == last_column)
2472 	{
2473 	  column_width += extra;
2474 	}
2475 
2476       if (rtl)
2477         _gtk_tree_view_column_allocate (column, widget_width - width - column_width + x_offset,
2478                                         column_width, priv->header_height);
2479       else
2480         _gtk_tree_view_column_allocate (column, width + x_offset,
2481                                         column_width, priv->header_height);
2482   next:
2483       width += column_width;
2484     }
2485 
2486   /* We change the width here.  The user might have been resizing columns,
2487    * which changes the total width of the tree view.  This is of
2488    * importance for getting the horizontal scroll bar right.
2489    */
2490   priv->width = width;
2491 }
2492 
2493 /* GtkWidget::size_allocate helper */
2494 static void
gtk_tree_view_size_allocate_drag_column(GtkWidget * widget)2495 gtk_tree_view_size_allocate_drag_column (GtkWidget *widget)
2496 {
2497   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2498   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2499   GtkAllocation allocation;
2500   int baseline;
2501   GtkWidget *button;
2502 
2503   if (priv->drag_column == NULL)
2504     return;
2505 
2506   button = gtk_tree_view_column_get_button (priv->drag_column);
2507 
2508   allocation.x = priv->drag_column_x;
2509   allocation.y = priv->drag_column_y;
2510   allocation.width = gtk_widget_get_allocated_width (button);
2511   allocation.height = gtk_widget_get_allocated_height (button);
2512   baseline = gtk_widget_get_allocated_baseline (button);
2513 
2514   gtk_widget_size_allocate (button, &allocation, baseline);
2515 }
2516 
2517 static void
gtk_tree_view_size_allocate(GtkWidget * widget,int width,int height,int baseline)2518 gtk_tree_view_size_allocate (GtkWidget *widget,
2519                              int        width,
2520                              int        height,
2521                              int        baseline)
2522 {
2523   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2524   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2525   GList *tmp_list;
2526   double page_size;
2527 
2528   /* We allocate the columns first because the width of the
2529    * tree view (used in updating the adjustments below) might change.
2530    */
2531   gtk_tree_view_size_allocate_columns (widget);
2532   gtk_tree_view_size_allocate_drag_column (widget);
2533 
2534   page_size = gtk_adjustment_get_page_size (priv->vadjustment);
2535   gtk_adjustment_configure (priv->hadjustment,
2536                             gtk_adjustment_get_value (priv->hadjustment) +
2537                             (_gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL ? width - page_size : 0),
2538                             0,
2539                             MAX (width, priv->width),
2540                             width * 0.1,
2541                             width * 0.9,
2542                             width);
2543 
2544   page_size = height - gtk_tree_view_get_effective_header_height (tree_view);
2545   gtk_adjustment_configure (priv->vadjustment,
2546                             gtk_adjustment_get_value (priv->vadjustment),
2547                             0,
2548                             MAX (page_size, gtk_tree_view_get_height (tree_view)),
2549                             page_size * 0.1,
2550                             page_size * 0.9,
2551                             page_size);
2552 
2553   /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2554   if (gtk_tree_row_reference_valid (priv->top_row))
2555     gtk_tree_view_top_row_to_dy (tree_view);
2556   else
2557     gtk_tree_view_dy_to_top_row (tree_view);
2558 
2559   if (gtk_widget_get_realized (widget))
2560     {
2561       if (priv->tree == NULL)
2562         invalidate_empty_focus (tree_view);
2563 
2564       if (priv->expander_column)
2565         {
2566           /* Might seem awkward, but is the best heuristic I could come up
2567            * with.  Only if the width of the columns before the expander
2568            * changes, we will update the prelight status.  It is this
2569            * width that makes the expander move vertically.  Always updating
2570            * prelight status causes trouble with hover selections.
2571            */
2572           int width_before_expander;
2573 
2574           width_before_expander = gtk_tree_view_calculate_width_before_expander (tree_view);
2575 
2576           if (priv->prev_width_before_expander
2577               != width_before_expander)
2578               update_prelight (tree_view,
2579                                priv->event_last_x,
2580                                priv->event_last_y);
2581 
2582           priv->prev_width_before_expander = width_before_expander;
2583         }
2584     }
2585 
2586   for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
2587     {
2588       GtkTreeViewChild *child = tmp_list->data;
2589       GtkTreePath *path;
2590       GdkRectangle child_rect;
2591       int min_x, max_x, min_y, max_y;
2592       int size;
2593       GtkTextDirection direction;
2594 
2595       direction = _gtk_widget_get_direction (child->widget);
2596       path = _gtk_tree_path_new_from_rbtree (child->tree, child->node);
2597       gtk_tree_view_get_cell_area (tree_view, path, child->column, &child_rect);
2598       child_rect.x += child->border.left;
2599       child_rect.y += child->border.top;
2600       child_rect.width -= child->border.left + child->border.right;
2601       child_rect.height -= child->border.top + child->border.bottom;
2602 
2603       gtk_widget_measure (GTK_WIDGET (child->widget), GTK_ORIENTATION_HORIZONTAL, -1,
2604                           &size, NULL, NULL, NULL);
2605 
2606       if (size > child_rect.width)
2607         {
2608           /* Enlarge the child, extending it to the left (RTL) */
2609           if (direction == GTK_TEXT_DIR_RTL)
2610             child_rect.x -= (size - child_rect.width);
2611           /* or to the right (LTR) */
2612           else
2613             child_rect.x += 0;
2614 
2615           child_rect.width = size;
2616         }
2617 
2618       gtk_widget_measure (GTK_WIDGET (child->widget), GTK_ORIENTATION_VERTICAL,
2619                           child_rect.width,
2620                           &size, NULL,
2621                           NULL, NULL);
2622       if (size > child_rect.height)
2623         {
2624           /* Enlarge the child, extending in both directions equally */
2625           child_rect.y -= (size - child_rect.height) / 2;
2626           child_rect.height = size;
2627         }
2628 
2629       /* push the rect back in the visible area if needed,
2630        * preferring the top left corner (for RTL)
2631        * or top right corner (for LTR)
2632        */
2633       min_x = 0;
2634       max_x = min_x + width - child_rect.width;
2635       min_y = 0;
2636       max_y = min_y + height - gtk_tree_view_get_effective_header_height (tree_view) - child_rect.height;
2637 
2638       if (direction == GTK_TEXT_DIR_LTR)
2639         /* Ensure that child's right edge is not sticking to the right
2640          * (if (child_rect.x > max_x) child_rect.x = max_x),
2641          * then ensure that child's left edge is visible and is not sticking to the left
2642          * (if (child_rect.x < min_x) child_rect.x = min_x).
2643          */
2644         child_rect.x = MAX (min_x, MIN (max_x, child_rect.x));
2645       else
2646         /* Ensure that child's left edge is not sticking to the left
2647          * (if (child_rect.x < min_x) child_rect.x = min_x),
2648          * then ensure that child's right edge is visible and is not sticking to the right
2649          * (if (child_rect.x > max_x) child_rect.x = max_x).
2650          */
2651         child_rect.x = MIN (max_x, MAX (min_x, child_rect.x));
2652 
2653       child_rect.y = MAX (min_y, MIN (max_y, child_rect.y));
2654 
2655       gtk_tree_path_free (path);
2656       gtk_widget_size_allocate (child->widget, &child_rect, -1);
2657     }
2658 
2659   if (priv->search_popover)
2660     gtk_popover_present (GTK_POPOVER (priv->search_popover));
2661 }
2662 
2663 /* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */
2664 static void
grab_focus_and_unset_draw_keyfocus(GtkTreeView * tree_view)2665 grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view)
2666 {
2667   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2668   GtkWidget *widget = GTK_WIDGET (tree_view);
2669 
2670   if (gtk_widget_get_focusable (widget) &&
2671       !gtk_widget_has_focus (widget))
2672     gtk_widget_grab_focus (widget);
2673 
2674   priv->draw_keyfocus = 0;
2675 }
2676 
2677 static inline gboolean
row_is_separator(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)2678 row_is_separator (GtkTreeView *tree_view,
2679 		  GtkTreeIter *iter,
2680 		  GtkTreePath *path)
2681 {
2682   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2683   gboolean is_separator = FALSE;
2684 
2685   if (priv->row_separator_func)
2686     {
2687       GtkTreeIter tmpiter;
2688 
2689       if (iter)
2690         tmpiter = *iter;
2691       else
2692         {
2693           if (!gtk_tree_model_get_iter (priv->model, &tmpiter, path))
2694             return FALSE;
2695         }
2696 
2697       is_separator = priv->row_separator_func (priv->model,
2698                                                &tmpiter,
2699                                                priv->row_separator_data);
2700     }
2701 
2702   return is_separator;
2703 }
2704 
2705 static int
gtk_tree_view_get_expander_size(GtkTreeView * tree_view)2706 gtk_tree_view_get_expander_size (GtkTreeView *tree_view)
2707 {
2708   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2709   GtkStyleContext *context;
2710   GtkCssStyle *style;
2711   int min_width;
2712   int min_height;
2713   int expander_size;
2714 
2715   if (priv->expander_size != -1)
2716     return priv->expander_size;
2717 
2718   context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
2719   gtk_style_context_save (context);
2720   gtk_style_context_add_class (context, "expander");
2721 
2722   style = gtk_style_context_lookup_style (context);
2723   min_width = _gtk_css_number_value_get (style->size->min_width, 100);
2724   min_height = _gtk_css_number_value_get (style->size->min_height, 100);
2725 
2726   gtk_style_context_restore (context);
2727 
2728   expander_size = MAX (min_width, min_height);
2729 
2730   priv->expander_size = expander_size + (_TREE_VIEW_HORIZONTAL_SEPARATOR / 2);
2731 
2732   return priv->expander_size;
2733 }
2734 
2735 static void
get_current_selection_modifiers(GtkEventController * controller,gboolean * modify,gboolean * extend)2736 get_current_selection_modifiers (GtkEventController *controller,
2737                                  gboolean           *modify,
2738                                  gboolean           *extend)
2739 {
2740   GdkModifierType state;
2741 
2742   state = gtk_event_controller_get_current_event_state (controller);
2743   *modify = (state & GDK_CONTROL_MASK) != 0;
2744   *extend = (state & GDK_SHIFT_MASK) != 0;
2745 }
2746 
2747 static void
gtk_tree_view_click_gesture_pressed(GtkGestureClick * gesture,int n_press,double x,double y,GtkTreeView * tree_view)2748 gtk_tree_view_click_gesture_pressed (GtkGestureClick *gesture,
2749                                      int              n_press,
2750                                      double           x,
2751                                      double           y,
2752                                      GtkTreeView     *tree_view)
2753 {
2754   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
2755   GtkWidget *widget = GTK_WIDGET (tree_view);
2756   GdkRectangle background_area, cell_area;
2757   GtkTreeViewColumn *column = NULL;
2758   GdkEventSequence *sequence;
2759   GdkModifierType modifiers;
2760   GdkEvent *event;
2761   int new_y, y_offset;
2762   int bin_x, bin_y;
2763   GtkTreePath *path;
2764   GtkTreeRBNode *node;
2765   GtkTreeRBTree *tree;
2766   int depth;
2767   guint button;
2768   GList *list;
2769   gboolean rtl;
2770   GtkWidget *target;
2771 
2772   /* check if this is a click in a child widget */
2773   target = gtk_event_controller_get_target (GTK_EVENT_CONTROLLER (gesture));
2774   if (gtk_widget_is_ancestor (target, widget))
2775     return;
2776 
2777   gtk_tree_view_stop_editing (tree_view, FALSE);
2778   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
2779   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2780 
2781   if (button > 3)
2782     {
2783       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
2784       return;
2785     }
2786 
2787   /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2788    * we're done handling the button press.
2789    */
2790   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y,
2791                                                      &bin_x, &bin_y);
2792   gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
2793 
2794   if (n_press > 1)
2795     gtk_gesture_set_state (priv->drag_gesture,
2796                            GTK_EVENT_SEQUENCE_DENIED);
2797 
2798   /* Empty tree? */
2799   if (priv->tree == NULL)
2800     {
2801       grab_focus_and_unset_draw_keyfocus (tree_view);
2802       return;
2803     }
2804 
2805   if (sequence)
2806     update_prelight (tree_view, x, y);
2807 
2808   /* are we in an arrow? */
2809   if (priv->prelight_node &&
2810       priv->arrow_prelit &&
2811       gtk_tree_view_draw_expanders (tree_view))
2812     {
2813       if (button == GDK_BUTTON_PRIMARY)
2814         {
2815           priv->button_pressed_node = priv->prelight_node;
2816           priv->button_pressed_tree = priv->prelight_tree;
2817           gtk_widget_queue_draw (widget);
2818         }
2819 
2820       grab_focus_and_unset_draw_keyfocus (tree_view);
2821       return;
2822     }
2823 
2824   /* find the node that was clicked */
2825   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(priv, bin_y);
2826   if (new_y < 0)
2827     new_y = 0;
2828   y_offset = -gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node);
2829 
2830   if (node == NULL)
2831     {
2832       /* We clicked in dead space */
2833       grab_focus_and_unset_draw_keyfocus (tree_view);
2834       return;
2835     }
2836 
2837   /* Get the path and the node */
2838   path = _gtk_tree_path_new_from_rbtree (tree, node);
2839 
2840   if (row_is_separator (tree_view, NULL, path))
2841     {
2842       gtk_tree_path_free (path);
2843       grab_focus_and_unset_draw_keyfocus (tree_view);
2844       return;
2845     }
2846 
2847   depth = gtk_tree_path_get_depth (path);
2848   background_area.y = y_offset + bin_y;
2849   background_area.height = gtk_tree_view_get_row_height (tree_view, node);
2850   background_area.x = 0;
2851 
2852   gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
2853                                                      background_area.x,
2854                                                      background_area.y,
2855                                                      &background_area.x,
2856                                                      &background_area.y);
2857 
2858   /* Let the column have a chance at selecting it. */
2859   rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2860   for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
2861        list; list = (rtl ? list->prev : list->next))
2862     {
2863       GtkTreeViewColumn *candidate = list->data;
2864 
2865       if (!gtk_tree_view_column_get_visible (candidate))
2866         continue;
2867 
2868       background_area.width = gtk_tree_view_column_get_width (candidate);
2869       if ((background_area.x > bin_x) ||
2870           (background_area.x + background_area.width <= bin_x))
2871         {
2872           background_area.x += background_area.width;
2873           continue;
2874         }
2875 
2876       /* we found the focus column */
2877       column = candidate;
2878       cell_area = background_area;
2879       cell_area.width -= _TREE_VIEW_HORIZONTAL_SEPARATOR;
2880       cell_area.x += _TREE_VIEW_HORIZONTAL_SEPARATOR / 2;
2881       if (gtk_tree_view_is_expander_column (tree_view, column))
2882         {
2883           if (!rtl)
2884             cell_area.x += (depth - 1) * priv->level_indentation;
2885           cell_area.width -= (depth - 1) * priv->level_indentation;
2886 
2887           if (gtk_tree_view_draw_expanders (tree_view))
2888             {
2889               int expander_size = gtk_tree_view_get_expander_size (tree_view);
2890               if (!rtl)
2891                 cell_area.x += depth * expander_size;
2892               cell_area.width -= depth * expander_size;
2893             }
2894         }
2895       break;
2896     }
2897 
2898   if (column == NULL)
2899     {
2900       gtk_tree_path_free (path);
2901       grab_focus_and_unset_draw_keyfocus (tree_view);
2902       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
2903       return;
2904     }
2905 
2906   _gtk_tree_view_set_focus_column (tree_view, column);
2907 
2908   event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2909   modifiers = gdk_event_get_modifier_state (event);
2910 
2911   /* decide if we edit */
2912   if (button == GDK_BUTTON_PRIMARY &&
2913       !(modifiers & gtk_accelerator_get_default_mod_mask ()))
2914     {
2915       GtkTreePath *anchor;
2916       GtkTreeIter iter;
2917 
2918       gtk_tree_model_get_iter (priv->model, &iter, path);
2919       gtk_tree_view_column_cell_set_cell_data (column,
2920                                                priv->model,
2921                                                &iter,
2922                                                GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT),
2923                                                node->children?TRUE:FALSE);
2924 
2925       if (priv->anchor)
2926         anchor = gtk_tree_row_reference_get_path (priv->anchor);
2927       else
2928         anchor = NULL;
2929 
2930       if ((anchor && !gtk_tree_path_compare (anchor, path))
2931           || !_gtk_tree_view_column_has_editable_cell (column))
2932         {
2933           GtkCellEditable *cell_editable = NULL;
2934 
2935           /* FIXME: get the right flags */
2936           guint flags = 0;
2937 
2938           if (_gtk_tree_view_column_cell_event (column,
2939                                                 (GdkEvent *)event,
2940                                                 &cell_area, flags))
2941             {
2942               GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
2943               cell_editable = gtk_cell_area_get_edit_widget (area);
2944 
2945               if (cell_editable != NULL)
2946                 {
2947                   gtk_tree_path_free (path);
2948                   gtk_tree_path_free (anchor);
2949                   return;
2950                 }
2951             }
2952         }
2953       if (anchor)
2954         gtk_tree_path_free (anchor);
2955     }
2956 
2957   /* we only handle selection modifications on the first button press
2958    */
2959   if (n_press == 1)
2960     {
2961       GtkCellRenderer *focus_cell;
2962       gboolean modify, extend;
2963 
2964       get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend);
2965       priv->modify_selection_pressed = modify;
2966       priv->extend_selection_pressed = extend;
2967 
2968       /* We update the focus cell here, this is also needed if the
2969        * column does not contain an editable cell.  In this case,
2970        * GtkCellArea did not receive the event for processing (and
2971        * could not update the focus cell).
2972        */
2973       focus_cell = _gtk_tree_view_column_get_cell_at_pos (column,
2974                                                           &cell_area,
2975                                                           &background_area,
2976                                                           bin_x, bin_y);
2977 
2978       if (focus_cell)
2979         gtk_tree_view_column_focus_cell (column, focus_cell);
2980 
2981       if (modify)
2982         {
2983           gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
2984           gtk_tree_view_real_toggle_cursor_row (tree_view);
2985         }
2986       else if (extend)
2987         {
2988           gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
2989           gtk_tree_view_real_select_cursor_row (tree_view, FALSE);
2990         }
2991       else
2992         {
2993           gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE);
2994         }
2995 
2996       priv->modify_selection_pressed = FALSE;
2997       priv->extend_selection_pressed = FALSE;
2998     }
2999 
3000   if (button == GDK_BUTTON_PRIMARY && n_press == 2)
3001     gtk_tree_view_row_activated (tree_view, path, column);
3002   else
3003     {
3004       if (n_press == 1)
3005         {
3006           priv->button_pressed_node = priv->prelight_node;
3007           priv->button_pressed_tree = priv->prelight_tree;
3008         }
3009 
3010       grab_focus_and_unset_draw_keyfocus (tree_view);
3011     }
3012 
3013   gtk_tree_path_free (path);
3014 
3015   if (n_press >= 2)
3016     gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
3017 }
3018 
3019 static void
gtk_tree_view_drag_gesture_begin(GtkGestureDrag * gesture,double start_x,double start_y,GtkTreeView * tree_view)3020 gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture,
3021                                   double          start_x,
3022                                   double          start_y,
3023                                   GtkTreeView    *tree_view)
3024 {
3025   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3026   int bin_x, bin_y;
3027   GtkTreeRBTree *tree;
3028   GtkTreeRBNode *node;
3029 
3030   if (priv->tree == NULL)
3031     {
3032       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
3033       return;
3034     }
3035 
3036   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
3037                                                      &bin_x, &bin_y);
3038   priv->press_start_x = priv->rubber_band_x = bin_x;
3039   priv->press_start_y = priv->rubber_band_y = bin_y;
3040   gtk_tree_rbtree_find_offset (priv->tree, bin_y + priv->dy,
3041                            &tree, &node);
3042 
3043   if (priv->rubber_banding_enable
3044       && !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)
3045       && gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE)
3046     {
3047       gboolean modify, extend;
3048 
3049       priv->press_start_y += priv->dy;
3050       priv->rubber_band_y += priv->dy;
3051       priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
3052 
3053       get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend);
3054       priv->rubber_band_modify = modify;
3055       priv->rubber_band_extend = extend;
3056     }
3057 }
3058 
3059 static void
gtk_tree_view_column_click_gesture_pressed(GtkGestureClick * gesture,int n_press,double x,double y,GtkTreeView * tree_view)3060 gtk_tree_view_column_click_gesture_pressed (GtkGestureClick *gesture,
3061                                             int              n_press,
3062                                             double           x,
3063                                             double           y,
3064                                             GtkTreeView     *tree_view)
3065 {
3066   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3067   GtkTreeViewColumn *column;
3068   GList *list;
3069 
3070   if (n_press != 2)
3071     return;
3072 
3073   for (list = priv->columns; list; list = list->next)
3074     {
3075       column = list->data;
3076 
3077       if (!_gtk_tree_view_column_coords_in_resize_rect (column, x, y) ||
3078           !gtk_tree_view_column_get_resizable (column))
3079         continue;
3080 
3081       if (gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE)
3082         {
3083           gtk_tree_view_column_set_fixed_width (column, -1);
3084           gtk_tree_view_column_set_expand (column, FALSE);
3085           _gtk_tree_view_column_autosize (tree_view, column);
3086         }
3087 
3088       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
3089       break;
3090     }
3091 }
3092 
3093 static void
gtk_tree_view_column_drag_gesture_begin(GtkGestureDrag * gesture,double start_x,double start_y,GtkTreeView * tree_view)3094 gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture,
3095                                          double          start_x,
3096                                          double          start_y,
3097                                          GtkTreeView    *tree_view)
3098 {
3099   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3100   GtkTreeViewColumn *column;
3101   gboolean rtl;
3102   GList *list;
3103   int i;
3104 
3105   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
3106 
3107   for (i = 0, list = priv->columns; list; list = list->next, i++)
3108     {
3109       gpointer drag_data;
3110       int column_width;
3111 
3112       column = list->data;
3113 
3114       if (!_gtk_tree_view_column_coords_in_resize_rect (column, start_x, start_y))
3115         continue;
3116 
3117       if (!gtk_tree_view_column_get_resizable (column))
3118         break;
3119 
3120       priv->in_column_resize = TRUE;
3121 
3122       /* block attached dnd signal handler */
3123       drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data");
3124       if (drag_data)
3125         g_signal_handlers_block_matched (tree_view,
3126                                          G_SIGNAL_MATCH_DATA,
3127                                          0, 0, NULL, NULL,
3128                                          drag_data);
3129 
3130       column_width = gtk_tree_view_column_get_width (column);
3131       gtk_tree_view_column_set_fixed_width (column, column_width);
3132       gtk_tree_view_column_set_expand (column, FALSE);
3133 
3134       priv->drag_pos = i;
3135       priv->x_drag = start_x + (rtl ? column_width : -column_width);
3136 
3137       if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3138         gtk_widget_grab_focus (GTK_WIDGET (tree_view));
3139 
3140       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
3141       return;
3142     }
3143 }
3144 
3145 static void
gtk_tree_view_update_button_position(GtkTreeView * tree_view,GtkTreeViewColumn * column)3146 gtk_tree_view_update_button_position (GtkTreeView       *tree_view,
3147                                       GtkTreeViewColumn *column)
3148 {
3149   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3150   GList *column_el;
3151 
3152   column_el = g_list_find (priv->columns, column);
3153   g_return_if_fail (column_el != NULL);
3154 
3155   gtk_css_node_insert_after (priv->header_node,
3156                              gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)),
3157                              column_el->prev ? gtk_widget_get_css_node (
3158                                 gtk_tree_view_column_get_button (column_el->prev->data)) : NULL);
3159 }
3160 
3161 /* column drag gesture helper */
3162 static gboolean
gtk_tree_view_button_release_drag_column(GtkTreeView * tree_view)3163 gtk_tree_view_button_release_drag_column (GtkTreeView *tree_view)
3164 {
3165   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3166   GtkWidget *button, *widget = GTK_WIDGET (tree_view);
3167   GList *l;
3168   gboolean rtl;
3169   GtkStyleContext *context;
3170 
3171   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3172 
3173   /* Move the button back */
3174   button = gtk_tree_view_column_get_button (priv->drag_column);
3175 
3176   context = gtk_widget_get_style_context (button);
3177   gtk_style_context_remove_class (context, "dnd");
3178 
3179   gtk_tree_view_update_button_position (tree_view, priv->drag_column);
3180   gtk_widget_queue_allocate (widget);
3181 
3182   gtk_widget_grab_focus (button);
3183 
3184   if (rtl)
3185     {
3186       if (priv->cur_reorder &&
3187           priv->cur_reorder->right_column != priv->drag_column)
3188         gtk_tree_view_move_column_after (tree_view, priv->drag_column,
3189                                          priv->cur_reorder->right_column);
3190     }
3191   else
3192     {
3193       if (priv->cur_reorder &&
3194           priv->cur_reorder->left_column != priv->drag_column)
3195         gtk_tree_view_move_column_after (tree_view, priv->drag_column,
3196                                          priv->cur_reorder->left_column);
3197     }
3198   priv->drag_column = NULL;
3199 
3200   for (l = priv->column_drag_info; l != NULL; l = l->next)
3201     g_slice_free (GtkTreeViewColumnReorder, l->data);
3202   g_list_free (priv->column_drag_info);
3203   priv->column_drag_info = NULL;
3204   priv->cur_reorder = NULL;
3205 
3206   /* Reset our flags */
3207   priv->drag_column_surface_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
3208   priv->in_column_drag = FALSE;
3209 
3210   return TRUE;
3211 }
3212 
3213 /* column drag gesture helper */
3214 static gboolean
gtk_tree_view_button_release_column_resize(GtkTreeView * tree_view)3215 gtk_tree_view_button_release_column_resize (GtkTreeView *tree_view)
3216 {
3217   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3218   gpointer drag_data;
3219 
3220   priv->drag_pos = -1;
3221 
3222   /* unblock attached dnd signal handler */
3223   drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data");
3224   if (drag_data)
3225     g_signal_handlers_unblock_matched (tree_view,
3226 				       G_SIGNAL_MATCH_DATA,
3227 				       0, 0, NULL, NULL,
3228 				       drag_data);
3229 
3230   priv->in_column_resize = FALSE;
3231   return TRUE;
3232 }
3233 
3234 static void
gtk_tree_view_column_drag_gesture_end(GtkGestureDrag * gesture,double offset_x,double offset_y,GtkTreeView * tree_view)3235 gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture,
3236                                        double          offset_x,
3237                                        double          offset_y,
3238                                        GtkTreeView    *tree_view)
3239 {
3240   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3241   GdkEventSequence *sequence;
3242 
3243   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
3244 
3245   /* Cancel reorder if the drag got cancelled */
3246   if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
3247     priv->cur_reorder = NULL;
3248 
3249   if (priv->in_column_drag)
3250     gtk_tree_view_button_release_drag_column (tree_view);
3251   else if (priv->in_column_resize)
3252     gtk_tree_view_button_release_column_resize (tree_view);
3253 }
3254 
3255 static void
gtk_tree_view_drag_gesture_end(GtkGestureDrag * gesture,double offset_x,double offset_y,GtkTreeView * tree_view)3256 gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture,
3257                                 double          offset_x,
3258                                 double          offset_y,
3259                                 GtkTreeView    *tree_view)
3260 {
3261   gtk_tree_view_stop_rubber_band (tree_view);
3262 }
3263 
3264 static void
gtk_tree_view_click_gesture_released(GtkGestureClick * gesture,int n_press,double x,double y,GtkTreeView * tree_view)3265 gtk_tree_view_click_gesture_released (GtkGestureClick *gesture,
3266                                       int              n_press,
3267                                       double           x,
3268                                       double           y,
3269                                       GtkTreeView     *tree_view)
3270 {
3271   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3272   GdkEventSequence *sequence;
3273   gboolean modify, extend;
3274   guint button;
3275 
3276   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
3277   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
3278 
3279   if (button != GDK_BUTTON_PRIMARY ||
3280       priv->button_pressed_node == NULL ||
3281       priv->button_pressed_node != priv->prelight_node)
3282     return;
3283 
3284   get_current_selection_modifiers (GTK_EVENT_CONTROLLER (gesture), &modify, &extend);
3285 
3286   if (priv->arrow_prelit)
3287     {
3288       GtkTreePath *path = NULL;
3289 
3290       path = _gtk_tree_path_new_from_rbtree (priv->button_pressed_tree,
3291                                              priv->button_pressed_node);
3292       /* Actually activate the node */
3293       if (priv->button_pressed_node->children == NULL)
3294         gtk_tree_view_real_expand_row (tree_view, path,
3295                                        priv->button_pressed_tree,
3296                                        priv->button_pressed_node,
3297                                        FALSE);
3298       else
3299         gtk_tree_view_real_collapse_row (tree_view, path,
3300                                          priv->button_pressed_tree,
3301                                          priv->button_pressed_node);
3302       gtk_tree_path_free (path);
3303     }
3304   else if (priv->activate_on_single_click && !modify && !extend)
3305     {
3306       GtkTreePath *path = NULL;
3307 
3308       path = _gtk_tree_path_new_from_rbtree (priv->button_pressed_tree,
3309                                              priv->button_pressed_node);
3310       gtk_tree_view_row_activated (tree_view, path, priv->focus_column);
3311       gtk_tree_path_free (path);
3312     }
3313 
3314   priv->button_pressed_tree = NULL;
3315   priv->button_pressed_node = NULL;
3316 
3317   if (sequence)
3318     ensure_unprelighted (tree_view);
3319 }
3320 
3321 /* GtkWidget::motion_event function set.
3322  */
3323 
3324 static gboolean
coords_are_over_arrow(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node,int x,int y)3325 coords_are_over_arrow (GtkTreeView   *tree_view,
3326                        GtkTreeRBTree *tree,
3327                        GtkTreeRBNode *node,
3328                        /* these are in bin window coords */
3329                        int            x,
3330                        int            y)
3331 {
3332   GdkRectangle arrow;
3333   int x2;
3334 
3335   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
3336     return FALSE;
3337 
3338   if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == 0)
3339     return FALSE;
3340 
3341   arrow.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
3342   arrow.height = gtk_tree_view_get_row_height (tree_view, node);
3343 
3344   gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2);
3345 
3346   arrow.width = x2 - arrow.x;
3347 
3348   return (x >= arrow.x &&
3349           x < (arrow.x + arrow.width) &&
3350 	  y >= arrow.y &&
3351 	  y < (arrow.y + arrow.height));
3352 }
3353 
3354 static gboolean
auto_expand_timeout(gpointer data)3355 auto_expand_timeout (gpointer data)
3356 {
3357   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
3358   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3359   GtkTreePath *path;
3360 
3361   if (priv->prelight_node)
3362     {
3363       path = _gtk_tree_path_new_from_rbtree (priv->prelight_tree,
3364 				             priv->prelight_node);
3365 
3366       if (priv->prelight_node->children)
3367         gtk_tree_view_collapse_row (tree_view, path);
3368       else
3369         gtk_tree_view_expand_row (tree_view, path, FALSE);
3370 
3371       gtk_tree_path_free (path);
3372     }
3373 
3374   priv->auto_expand_timeout = 0;
3375 
3376   return FALSE;
3377 }
3378 
3379 static void
remove_auto_expand_timeout(GtkTreeView * tree_view)3380 remove_auto_expand_timeout (GtkTreeView *tree_view)
3381 {
3382   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3383 
3384   g_clear_handle_id (&priv->auto_expand_timeout, g_source_remove);
3385 }
3386 
3387 static void
do_prelight(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node,int x,int y)3388 do_prelight (GtkTreeView   *tree_view,
3389              GtkTreeRBTree *tree,
3390              GtkTreeRBNode *node,
3391 	     /* these are in bin_window coords */
3392              int            x,
3393              int            y)
3394 {
3395   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3396 
3397   if (priv->prelight_tree == tree &&
3398       priv->prelight_node == node)
3399     {
3400       /*  We are still on the same node,
3401 	  but we might need to take care of the arrow  */
3402 
3403       if (tree && node && gtk_tree_view_draw_expanders (tree_view))
3404 	{
3405 	  gboolean over_arrow;
3406 
3407 	  over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
3408 
3409 	  if (over_arrow != priv->arrow_prelit)
3410 	    {
3411 	      if (over_arrow)
3412           priv->arrow_prelit = TRUE;
3413         else
3414           priv->arrow_prelit = FALSE;
3415 
3416 	      gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3417 	    }
3418 	}
3419 
3420       return;
3421     }
3422 
3423   if (priv->prelight_tree && priv->prelight_node)
3424     {
3425       /*  Unprelight the old node and arrow  */
3426 
3427       GTK_TREE_RBNODE_UNSET_FLAG (priv->prelight_node,
3428                                   GTK_TREE_RBNODE_IS_PRELIT);
3429 
3430       if (priv->arrow_prelit
3431           && gtk_tree_view_draw_expanders (tree_view))
3432         {
3433           priv->arrow_prelit = FALSE;
3434 
3435           gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3436         }
3437 
3438       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3439     }
3440 
3441 
3442   if (priv->hover_expand)
3443     remove_auto_expand_timeout (tree_view);
3444 
3445   /*  Set the new prelight values  */
3446   priv->prelight_node = node;
3447   priv->prelight_tree = tree;
3448 
3449   if (!node || !tree)
3450     return;
3451 
3452   /*  Prelight the new node and arrow  */
3453 
3454   if (gtk_tree_view_draw_expanders (tree_view)
3455       && coords_are_over_arrow (tree_view, tree, node, x, y))
3456     {
3457       priv->arrow_prelit = TRUE;
3458 
3459       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3460     }
3461 
3462   GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_PRELIT);
3463 
3464   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3465 
3466   if (priv->hover_expand)
3467     {
3468       priv->auto_expand_timeout =
3469         g_timeout_add (AUTO_EXPAND_TIMEOUT, auto_expand_timeout, tree_view);
3470       gdk_source_set_static_name_by_id (priv->auto_expand_timeout, "[gtk] auto_expand_timeout");
3471     }
3472 }
3473 
3474 static void
prelight_or_select(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node,int x,int y)3475 prelight_or_select (GtkTreeView   *tree_view,
3476                     GtkTreeRBTree *tree,
3477                     GtkTreeRBNode *node,
3478                     /* these are in bin_window coords */
3479                     int            x,
3480                     int            y)
3481 {
3482   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3483   GtkSelectionMode mode = gtk_tree_selection_get_mode (priv->selection);
3484 
3485   if (priv->hover_selection &&
3486       (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) &&
3487       !(priv->edited_column &&
3488         gtk_cell_area_get_edit_widget
3489         (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->edited_column)))))
3490     {
3491       if (node)
3492         {
3493           if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
3494             {
3495               GtkTreePath *path;
3496 
3497               path = _gtk_tree_path_new_from_rbtree (tree, node);
3498               gtk_tree_selection_select_path (priv->selection, path);
3499               if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
3500                 {
3501                   priv->draw_keyfocus = FALSE;
3502                   gtk_tree_view_real_set_cursor (tree_view, path, 0);
3503                 }
3504               gtk_tree_path_free (path);
3505             }
3506         }
3507 
3508       else if (mode == GTK_SELECTION_SINGLE)
3509         gtk_tree_selection_unselect_all (priv->selection);
3510     }
3511 
3512   do_prelight (tree_view, tree, node, x, y);
3513 }
3514 
3515 static void
ensure_unprelighted(GtkTreeView * tree_view)3516 ensure_unprelighted (GtkTreeView *tree_view)
3517 {
3518   GtkTreeViewPrivate *priv G_GNUC_UNUSED = gtk_tree_view_get_instance_private (tree_view);
3519 
3520   do_prelight (tree_view,
3521 	       NULL, NULL,
3522 	       -1000, -1000); /* coords not possibly over an arrow */
3523 
3524   g_assert (priv->prelight_node == NULL);
3525 }
3526 
3527 static void
update_prelight(GtkTreeView * tree_view,int x,int y)3528 update_prelight (GtkTreeView *tree_view,
3529                  int          x,
3530                  int          y)
3531 {
3532   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3533   int new_y;
3534   GtkTreeRBTree *tree;
3535   GtkTreeRBNode *node;
3536 
3537   if (priv->tree == NULL)
3538     return;
3539 
3540   if (x == -10000)
3541     {
3542       ensure_unprelighted (tree_view);
3543       return;
3544     }
3545 
3546   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, y);
3547   if (new_y < 0)
3548     new_y = 0;
3549 
3550   gtk_tree_rbtree_find_offset (priv->tree,
3551                                new_y, &tree, &node);
3552 
3553   if (node)
3554     prelight_or_select (tree_view, tree, node, x, y);
3555 }
3556 
3557 static gboolean
gtk_tree_view_motion_resize_column(GtkTreeView * tree_view,double x,double y)3558 gtk_tree_view_motion_resize_column (GtkTreeView *tree_view,
3559                                     double       x,
3560                                     double       y)
3561 {
3562   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3563   int new_width;
3564   GtkTreeViewColumn *column;
3565 
3566   column = gtk_tree_view_get_column (tree_view, priv->drag_pos);
3567 
3568   if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
3569     new_width = MAX (priv->x_drag - x, 0);
3570   else
3571     new_width = MAX (x - priv->x_drag, 0);
3572 
3573   if (new_width != gtk_tree_view_column_get_fixed_width (column))
3574     gtk_tree_view_column_set_fixed_width (column, new_width);
3575 
3576   return FALSE;
3577 }
3578 
3579 static void
gtk_tree_view_update_current_reorder(GtkTreeView * tree_view)3580 gtk_tree_view_update_current_reorder (GtkTreeView *tree_view)
3581 {
3582   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3583   GtkTreeViewColumnReorder *reorder = NULL;
3584   GdkEventSequence *sequence;
3585   GList *list;
3586   double x;
3587 
3588   sequence = gtk_gesture_single_get_current_sequence
3589     (GTK_GESTURE_SINGLE (priv->column_drag_gesture));
3590   gtk_gesture_get_point (priv->column_drag_gesture,
3591                          sequence, &x, NULL);
3592   x += gtk_adjustment_get_value (priv->hadjustment);
3593 
3594   for (list = priv->column_drag_info; list; list = list->next)
3595     {
3596       reorder = (GtkTreeViewColumnReorder *) list->data;
3597       if (x >= reorder->left_align && x < reorder->right_align)
3598 	break;
3599       reorder = NULL;
3600     }
3601 
3602   priv->cur_reorder = reorder;
3603 }
3604 
3605 static void
gtk_tree_view_vertical_autoscroll(GtkTreeView * tree_view)3606 gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view)
3607 {
3608   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3609   GdkRectangle visible_rect;
3610   int y;
3611   int offset;
3612 
3613   if (gtk_gesture_is_recognized (priv->drag_gesture))
3614     {
3615       GdkEventSequence *sequence;
3616       double py;
3617 
3618       sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (priv->drag_gesture));
3619       gtk_gesture_get_point (priv->drag_gesture, sequence, NULL, &py);
3620       gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, py,
3621                                                          NULL, &y);
3622     }
3623   else
3624     {
3625       y = priv->event_last_y;
3626       gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, y, NULL, &y);
3627     }
3628 
3629   y += priv->dy;
3630   gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
3631 
3632   /* see if we are near the edge. */
3633   offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3634   if (offset > 0)
3635     {
3636       offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3637       if (offset < 0)
3638 	return;
3639     }
3640 
3641   gtk_adjustment_set_value (priv->vadjustment,
3642                             MAX (gtk_adjustment_get_value (priv->vadjustment) + offset, 0.0));
3643 }
3644 
3645 static void
gtk_tree_view_horizontal_autoscroll(GtkTreeView * tree_view)3646 gtk_tree_view_horizontal_autoscroll (GtkTreeView *tree_view)
3647 {
3648   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3649   GdkEventSequence *sequence;
3650   GdkRectangle visible_rect;
3651   double x;
3652   int offset;
3653 
3654   sequence = gtk_gesture_single_get_current_sequence
3655     (GTK_GESTURE_SINGLE (priv->column_drag_gesture));
3656   gtk_gesture_get_point (priv->column_drag_gesture,
3657                          sequence, &x, NULL);
3658   gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
3659 
3660   x += gtk_adjustment_get_value (priv->hadjustment);
3661 
3662   /* See if we are near the edge. */
3663   offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3664   if (offset > 0)
3665     {
3666       offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3667       if (offset < 0)
3668 	return;
3669     }
3670   offset = offset/3;
3671 
3672   gtk_adjustment_set_value (priv->hadjustment,
3673                             MAX (gtk_adjustment_get_value (priv->hadjustment) + offset, 0.0));
3674 }
3675 
3676 static void
gtk_tree_view_motion_drag_column(GtkTreeView * tree_view,double x,double y)3677 gtk_tree_view_motion_drag_column (GtkTreeView *tree_view,
3678                                   double       x,
3679                                   double       y)
3680 {
3681   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3682   GtkTreeViewColumn *column = priv->drag_column;
3683   GtkWidget *button;
3684   int width, button_width;
3685 
3686   button = gtk_tree_view_column_get_button (column);
3687   x += gtk_adjustment_get_value (priv->hadjustment);
3688 
3689   /* Handle moving the header */
3690   width = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view));
3691   button_width = gtk_widget_get_allocated_width (button);
3692   priv->drag_column_x = CLAMP (x - _gtk_tree_view_column_get_drag_x (column), 0,
3693                                MAX (priv->width, width) - button_width);
3694 
3695   /* autoscroll, if needed */
3696   gtk_tree_view_horizontal_autoscroll (tree_view);
3697   /* Update the current reorder position and arrow; */
3698   gtk_tree_view_update_current_reorder (tree_view);
3699   gtk_widget_queue_allocate (GTK_WIDGET (tree_view));
3700 }
3701 
3702 static void
gtk_tree_view_stop_rubber_band(GtkTreeView * tree_view)3703 gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view)
3704 {
3705   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3706 
3707   remove_scroll_timeout (tree_view);
3708 
3709   if (priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3710     {
3711       GtkTreePath *tmp_path;
3712 
3713       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3714 
3715       /* The anchor path should be set to the start path */
3716       if (priv->rubber_band_start_node)
3717         {
3718           tmp_path = _gtk_tree_path_new_from_rbtree (priv->rubber_band_start_tree,
3719                                                      priv->rubber_band_start_node);
3720 
3721           if (priv->anchor)
3722             gtk_tree_row_reference_free (priv->anchor);
3723 
3724           priv->anchor = gtk_tree_row_reference_new (priv->model, tmp_path);
3725 
3726           gtk_tree_path_free (tmp_path);
3727         }
3728 
3729       /* ... and the cursor to the end path */
3730       if (priv->rubber_band_end_node)
3731         {
3732           tmp_path = _gtk_tree_path_new_from_rbtree (priv->rubber_band_end_tree,
3733                                                      priv->rubber_band_end_node);
3734           gtk_tree_view_real_set_cursor (GTK_TREE_VIEW (tree_view), tmp_path, 0);
3735           gtk_tree_path_free (tmp_path);
3736         }
3737 
3738       _gtk_tree_selection_emit_changed (priv->selection);
3739 
3740       gtk_css_node_set_parent (priv->rubber_band_cssnode, NULL);
3741       priv->rubber_band_cssnode = NULL;
3742     }
3743 
3744   /* Clear status variables */
3745   priv->rubber_band_status = RUBBER_BAND_OFF;
3746   priv->rubber_band_extend = FALSE;
3747   priv->rubber_band_modify = FALSE;
3748 
3749   priv->rubber_band_start_node = NULL;
3750   priv->rubber_band_start_tree = NULL;
3751   priv->rubber_band_end_node = NULL;
3752   priv->rubber_band_end_tree = NULL;
3753 }
3754 
3755 static void
gtk_tree_view_update_rubber_band_selection_range(GtkTreeView * tree_view,GtkTreeRBTree * start_tree,GtkTreeRBNode * start_node,GtkTreeRBTree * end_tree,GtkTreeRBNode * end_node,gboolean select,gboolean skip_start,gboolean skip_end)3756 gtk_tree_view_update_rubber_band_selection_range (GtkTreeView  *tree_view,
3757 						 GtkTreeRBTree *start_tree,
3758 						 GtkTreeRBNode *start_node,
3759 						 GtkTreeRBTree *end_tree,
3760 						 GtkTreeRBNode *end_node,
3761 						 gboolean       select,
3762 						 gboolean       skip_start,
3763 						 gboolean       skip_end)
3764 {
3765   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3766 
3767   if (start_node == end_node)
3768     return;
3769 
3770   /* We skip the first node and jump inside the loop */
3771   if (skip_start)
3772     goto skip_first;
3773 
3774   do
3775     {
3776       /* Small optimization by assuming insensitive nodes are never
3777        * selected.
3778        */
3779       if (!GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED))
3780         {
3781           GtkTreePath *path;
3782           gboolean selectable;
3783 
3784           path = _gtk_tree_path_new_from_rbtree (start_tree, start_node);
3785           selectable = _gtk_tree_selection_row_is_selectable (priv->selection, start_node, path);
3786           gtk_tree_path_free (path);
3787 
3788           if (!selectable)
3789             goto node_not_selectable;
3790         }
3791 
3792       if (select)
3793         {
3794           if (priv->rubber_band_extend)
3795             GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3796           else if (priv->rubber_band_modify)
3797             {
3798               /* Toggle the selection state */
3799               if (GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED))
3800                 GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3801               else
3802                 GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3803             }
3804           else
3805             GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3806         }
3807       else
3808         {
3809           /* Mirror the above */
3810           if (priv->rubber_band_extend)
3811             GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3812           else if (priv->rubber_band_modify)
3813             {
3814               /* Toggle the selection state */
3815               if (GTK_TREE_RBNODE_FLAG_SET (start_node, GTK_TREE_RBNODE_IS_SELECTED))
3816                 GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3817               else
3818                 GTK_TREE_RBNODE_SET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3819             }
3820           else
3821             GTK_TREE_RBNODE_UNSET_FLAG (start_node, GTK_TREE_RBNODE_IS_SELECTED);
3822         }
3823 
3824       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3825 
3826 node_not_selectable:
3827       if (start_node == end_node)
3828 	break;
3829 
3830 skip_first:
3831 
3832       if (start_node->children)
3833         {
3834 	  start_tree = start_node->children;
3835           start_node = gtk_tree_rbtree_first (start_tree);
3836 	}
3837       else
3838         {
3839 	  gtk_tree_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
3840 
3841 	  if (!start_tree)
3842 	    /* Ran out of tree */
3843 	    break;
3844 	}
3845 
3846       if (skip_end && start_node == end_node)
3847 	break;
3848     }
3849   while (TRUE);
3850 }
3851 
3852 static void
gtk_tree_view_update_rubber_band_selection(GtkTreeView * tree_view)3853 gtk_tree_view_update_rubber_band_selection (GtkTreeView *tree_view)
3854 {
3855   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3856   GtkTreeRBTree *start_tree, *end_tree;
3857   GtkTreeRBNode *start_node, *end_node;
3858   double start_y, offset_y;
3859   int bin_y;
3860 
3861   if (!gtk_gesture_is_active (priv->drag_gesture))
3862     return;
3863 
3864   gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture),
3865                                NULL, &offset_y);
3866   gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture),
3867                                     NULL, &start_y);
3868   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, start_y,
3869 						      NULL, &bin_y);
3870   bin_y = MAX (0, bin_y + offset_y + priv->dy);
3871 
3872   gtk_tree_rbtree_find_offset (priv->tree, MIN (priv->press_start_y, bin_y), &start_tree, &start_node);
3873   gtk_tree_rbtree_find_offset (priv->tree, MAX (priv->press_start_y, bin_y), &end_tree, &end_node);
3874 
3875   /* Handle the start area first */
3876   if (!start_node && !end_node)
3877     {
3878       if (priv->rubber_band_start_node)
3879         {
3880           GtkTreeRBNode *node = priv->rubber_band_start_node;
3881 
3882 	  if (priv->rubber_band_modify)
3883 	    {
3884 	      /* Toggle the selection state */
3885 	      if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
3886 		GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED);
3887 	      else
3888 		GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED);
3889 	    }
3890           else
3891             GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED);
3892 
3893           gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3894         }
3895     }
3896   if (!priv->rubber_band_start_node || !start_node)
3897     {
3898       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3899 						       start_tree,
3900 						       start_node,
3901 						       end_tree,
3902 						       end_node,
3903 						       TRUE,
3904 						       FALSE,
3905 						       FALSE);
3906     }
3907   else if (gtk_tree_rbtree_node_find_offset (start_tree, start_node) <
3908            gtk_tree_rbtree_node_find_offset (priv->rubber_band_start_tree, priv->rubber_band_start_node))
3909     {
3910       /* New node is above the old one; selection became bigger */
3911       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3912 						       start_tree,
3913 						       start_node,
3914 						       priv->rubber_band_start_tree,
3915 						       priv->rubber_band_start_node,
3916 						       TRUE,
3917 						       FALSE,
3918 						       TRUE);
3919     }
3920   else if (gtk_tree_rbtree_node_find_offset (start_tree, start_node) >
3921            gtk_tree_rbtree_node_find_offset (priv->rubber_band_start_tree, priv->rubber_band_start_node))
3922     {
3923       /* New node is below the old one; selection became smaller */
3924       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3925 						       priv->rubber_band_start_tree,
3926 						       priv->rubber_band_start_node,
3927 						       start_tree,
3928 						       start_node,
3929 						       FALSE,
3930 						       FALSE,
3931 						       TRUE);
3932     }
3933 
3934   priv->rubber_band_start_tree = start_tree;
3935   priv->rubber_band_start_node = start_node;
3936 
3937   /* Next, handle the end area */
3938   if (!priv->rubber_band_end_node)
3939     {
3940       /* In the event this happens, start_node was also NULL; this case is
3941        * handled above.
3942        */
3943     }
3944   else if (!end_node)
3945     {
3946       /* Find the last node in the tree */
3947       gtk_tree_rbtree_find_offset (priv->tree, gtk_tree_view_get_height (tree_view) - 1,
3948 			       &end_tree, &end_node);
3949 
3950       /* Selection reached end of the tree */
3951       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3952                                                         priv->rubber_band_end_tree,
3953                                                         priv->rubber_band_end_node,
3954                                                         end_tree,
3955                                                         end_node,
3956                                                         TRUE,
3957                                                         TRUE,
3958                                                         FALSE);
3959     }
3960   else if (gtk_tree_rbtree_node_find_offset (end_tree, end_node) >
3961            gtk_tree_rbtree_node_find_offset (priv->rubber_band_end_tree, priv->rubber_band_end_node))
3962     {
3963       /* New node is below the old one; selection became bigger */
3964       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3965                                                         priv->rubber_band_end_tree,
3966                                                         priv->rubber_band_end_node,
3967                                                         end_tree,
3968                                                         end_node,
3969                                                         TRUE,
3970                                                         TRUE,
3971                                                         FALSE);
3972     }
3973   else if (gtk_tree_rbtree_node_find_offset (end_tree, end_node) <
3974            gtk_tree_rbtree_node_find_offset (priv->rubber_band_end_tree, priv->rubber_band_end_node))
3975     {
3976       /* New node is above the old one; selection became smaller */
3977       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3978 						       end_tree,
3979 						       end_node,
3980 						       priv->rubber_band_end_tree,
3981 						       priv->rubber_band_end_node,
3982 						       FALSE,
3983 						       TRUE,
3984 						       FALSE);
3985     }
3986 
3987   priv->rubber_band_end_tree = end_tree;
3988   priv->rubber_band_end_node = end_node;
3989 }
3990 
3991 static void
gtk_tree_view_update_rubber_band(GtkTreeView * tree_view)3992 gtk_tree_view_update_rubber_band (GtkTreeView *tree_view)
3993 {
3994   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
3995   double start_x, start_y, offset_x, offset_y, x, y;
3996   int bin_x, bin_y;
3997 
3998   if (!gtk_gesture_is_recognized (priv->drag_gesture))
3999     return;
4000 
4001   gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture),
4002                                &offset_x, &offset_y);
4003   gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture),
4004                                     &start_x, &start_y);
4005   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
4006                                                      &bin_x, &bin_y);
4007   bin_y += priv->dy;
4008 
4009   x = MAX (bin_x + offset_x, 0);
4010   y = MAX (bin_y + offset_y, 0);
4011 
4012   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
4013 
4014   priv->rubber_band_x = x;
4015   priv->rubber_band_y = y;
4016 
4017   gtk_tree_view_update_rubber_band_selection (tree_view);
4018 }
4019 
4020 static void
gtk_tree_view_snapshot_rubber_band(GtkTreeView * tree_view,GtkSnapshot * snapshot)4021 gtk_tree_view_snapshot_rubber_band (GtkTreeView *tree_view,
4022                                     GtkSnapshot *snapshot)
4023 {
4024   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4025   double start_x, start_y, offset_x, offset_y;
4026   GdkRectangle rect;
4027   GtkStyleContext *context;
4028   int bin_x, bin_y;
4029 
4030   if (!gtk_gesture_is_recognized (priv->drag_gesture))
4031     return;
4032 
4033   gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture),
4034                                &offset_x, &offset_y);
4035   gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture),
4036                                     &start_x, &start_y);
4037   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
4038                                                      &bin_x, &bin_y);
4039   bin_x = MAX (0, bin_x + offset_x);
4040   bin_y = MAX (0, bin_y + offset_y + priv->dy);
4041 
4042   context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
4043 
4044   gtk_style_context_save_to_node (context, priv->rubber_band_cssnode);
4045 
4046   rect.x = MIN (priv->press_start_x, bin_x);
4047   rect.y = MIN (priv->press_start_y, bin_y) - priv->dy;
4048   rect.width = ABS (priv->press_start_x - bin_x) + 1;
4049   rect.height = ABS (priv->press_start_y - bin_y) + 1;
4050 
4051   gtk_snapshot_render_background (snapshot, context,
4052                                   rect.x, rect.y,
4053                                   rect.width, rect.height);
4054   gtk_snapshot_render_frame (snapshot, context,
4055                              rect.x, rect.y,
4056                              rect.width, rect.height);
4057 
4058   gtk_style_context_restore (context);
4059 }
4060 
4061 static void
gtk_tree_view_column_drag_gesture_update(GtkGestureDrag * gesture,double offset_x,double offset_y,GtkTreeView * tree_view)4062 gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture,
4063                                           double          offset_x,
4064                                           double          offset_y,
4065                                           GtkTreeView    *tree_view)
4066 {
4067   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4068   double start_x, start_y, x, y;
4069   GdkEventSequence *sequence;
4070 
4071   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
4072 
4073   if (gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence) != GTK_EVENT_SEQUENCE_CLAIMED)
4074     return;
4075 
4076   gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
4077   x = start_x + offset_x;
4078   y = start_y + offset_y;
4079 
4080   if (priv->in_column_resize)
4081     gtk_tree_view_motion_resize_column (tree_view, x, y);
4082   else if (priv->in_column_drag)
4083     gtk_tree_view_motion_drag_column (tree_view, x, y);
4084 }
4085 
4086 static void
gtk_tree_view_drag_gesture_update(GtkGestureDrag * gesture,double offset_x,double offset_y,GtkTreeView * tree_view)4087 gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture,
4088                                    double          offset_x,
4089                                    double          offset_y,
4090                                    GtkTreeView    *tree_view)
4091 {
4092   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4093 
4094   if (priv->tree == NULL)
4095     {
4096       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
4097       return;
4098     }
4099 
4100   if (priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
4101     {
4102       GtkCssNode *widget_node;
4103 
4104       widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view));
4105       priv->rubber_band_cssnode = gtk_css_node_new ();
4106       gtk_css_node_set_name (priv->rubber_band_cssnode, g_quark_from_static_string ("rubberband"));
4107       gtk_css_node_set_parent (priv->rubber_band_cssnode, widget_node);
4108       gtk_css_node_set_state (priv->rubber_band_cssnode, gtk_css_node_get_state (widget_node));
4109       g_object_unref (priv->rubber_band_cssnode);
4110 
4111       gtk_tree_view_update_rubber_band (tree_view);
4112 
4113       priv->rubber_band_status = RUBBER_BAND_ACTIVE;
4114       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
4115     }
4116   else if (priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4117     {
4118       gtk_tree_view_update_rubber_band (tree_view);
4119 
4120       add_scroll_timeout (tree_view);
4121     }
4122   else if (!priv->rubber_band_status)
4123     {
4124       if (gtk_tree_view_maybe_begin_dragging_row (tree_view))
4125         gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
4126     }
4127 }
4128 
4129 static void
gtk_tree_view_motion_controller_motion(GtkEventControllerMotion * controller,double x,double y,GtkTreeView * tree_view)4130 gtk_tree_view_motion_controller_motion (GtkEventControllerMotion *controller,
4131                                         double                    x,
4132                                         double                    y,
4133                                         GtkTreeView              *tree_view)
4134 {
4135   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4136   GtkTreeRBTree *tree;
4137   GtkTreeRBNode *node;
4138   int new_y;
4139   GList *list;
4140   gboolean cursor_set = FALSE;
4141 
4142   if (priv->tree)
4143     {
4144       int bin_x, bin_y;
4145 
4146       /* If we are currently pressing down a button, we don't want to prelight anything else. */
4147       if (gtk_gesture_is_active (priv->drag_gesture) ||
4148           gtk_gesture_is_active (priv->click_gesture))
4149         node = NULL;
4150 
4151       gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y,
4152 							  &bin_x, &bin_y);
4153       new_y = MAX (0, TREE_WINDOW_Y_TO_RBTREE_Y (priv, bin_y));
4154 
4155       gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node);
4156 
4157       priv->event_last_x = bin_x;
4158       priv->event_last_y = bin_y;
4159       prelight_or_select (tree_view, tree, node, bin_x, bin_y);
4160     }
4161 
4162   for (list = priv->columns; list; list = list->next)
4163     {
4164       GtkTreeViewColumn *column = list->data;
4165 
4166       if (_gtk_tree_view_column_coords_in_resize_rect (column, x, y))
4167         {
4168           gtk_widget_set_cursor_from_name (GTK_WIDGET (tree_view), "col-resize");
4169           cursor_set = TRUE;
4170           break;
4171         }
4172     }
4173 
4174   if (!cursor_set)
4175     gtk_widget_set_cursor (GTK_WIDGET (tree_view), NULL);
4176 }
4177 
4178 /* Invalidate the focus rectangle near the edge of the bin_window; used when
4179  * the tree is empty.
4180  */
4181 static void
invalidate_empty_focus(GtkTreeView * tree_view)4182 invalidate_empty_focus (GtkTreeView *tree_view)
4183 {
4184   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
4185     return;
4186 
4187   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
4188 }
4189 
4190 static void
gtk_tree_view_snapshot_grid_line(GtkTreeView * tree_view,GtkSnapshot * snapshot,GtkOrientation orientation,const graphene_point_t * start,float size)4191 gtk_tree_view_snapshot_grid_line (GtkTreeView            *tree_view,
4192                                   GtkSnapshot            *snapshot,
4193                                   GtkOrientation          orientation,
4194                                   const graphene_point_t *start,
4195                                   float                   size)
4196 {
4197   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4198   GtkStyleContext *context;
4199   const GdkRGBA *grid_line_color;
4200 
4201   context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
4202   grid_line_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context,
4203                                                                                     GTK_CSS_PROPERTY_BORDER_TOP_COLOR));
4204 
4205   if (!gdk_rgba_equal (grid_line_color, &priv->grid_line_color) ||
4206       (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->horizontal_grid_line_texture) ||
4207       (orientation == GTK_ORIENTATION_VERTICAL && !priv->vertical_grid_line_texture))
4208     {
4209       cairo_surface_t *surface;
4210       unsigned char *data;
4211 
4212       g_clear_object (&priv->horizontal_grid_line_texture);
4213       g_clear_object (&priv->vertical_grid_line_texture);
4214       priv->grid_line_color = *grid_line_color;
4215 
4216       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2, 1);
4217       data = cairo_image_surface_get_data (surface);
4218       /* just color the first pixel... */
4219       data[0] = round (grid_line_color->blue  * 255);
4220       data[1] = round (grid_line_color->green * 255);
4221       data[2] = round (grid_line_color->red   * 255);
4222       data[3] = round (grid_line_color->alpha * 255);
4223 
4224       priv->horizontal_grid_line_texture = gdk_texture_new_for_surface (surface);
4225       cairo_surface_destroy (surface);
4226 
4227       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 2);
4228       data = cairo_image_surface_get_data (surface);
4229       data[0] = round (grid_line_color->blue  * 255);
4230       data[1] = round (grid_line_color->green * 255);
4231       data[2] = round (grid_line_color->red   * 255);
4232       data[3] = round (grid_line_color->alpha * 255);
4233 
4234       priv->vertical_grid_line_texture = gdk_texture_new_for_surface (surface);
4235       cairo_surface_destroy (surface);
4236     }
4237 
4238   g_assert (priv->horizontal_grid_line_texture);
4239   g_assert (priv->vertical_grid_line_texture);
4240 
4241   if (orientation == GTK_ORIENTATION_HORIZONTAL)
4242     {
4243       gtk_snapshot_push_repeat (snapshot,
4244                                 &GRAPHENE_RECT_INIT (
4245                                   start->x, start->y,
4246                                   size, 1),
4247                                 NULL);
4248       gtk_snapshot_append_texture (snapshot, priv->horizontal_grid_line_texture,
4249                                    &GRAPHENE_RECT_INIT (0, 0, 2, 1));
4250       gtk_snapshot_pop (snapshot);
4251     }
4252   else /* VERTICAL */
4253     {
4254       gtk_snapshot_push_repeat (snapshot,
4255                                 &GRAPHENE_RECT_INIT (
4256                                   start->x, start->y,
4257                                   1, size),
4258                                 NULL);
4259       gtk_snapshot_append_texture (snapshot, priv->vertical_grid_line_texture,
4260                                    &GRAPHENE_RECT_INIT (0, 0, 1, 2));
4261       gtk_snapshot_pop (snapshot);
4262     }
4263 }
4264 
4265 static void
gtk_tree_view_snapshot_tree_line(GtkTreeView * tree_view,GtkSnapshot * snapshot,GtkOrientation orientation,const graphene_point_t * start,float size)4266 gtk_tree_view_snapshot_tree_line (GtkTreeView            *tree_view,
4267                                   GtkSnapshot            *snapshot,
4268                                   GtkOrientation          orientation,
4269                                   const graphene_point_t *start,
4270                                   float                   size)
4271 {
4272   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4273   GtkStyleContext *context;
4274   const GdkRGBA *tree_line_color;
4275 
4276   context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
4277   tree_line_color = gtk_css_color_value_get_rgba (_gtk_style_context_peek_property (context,
4278                                                                                     GTK_CSS_PROPERTY_BORDER_LEFT_COLOR));
4279 
4280   if (!gdk_rgba_equal (tree_line_color, &priv->tree_line_color) ||
4281       (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->horizontal_tree_line_texture) ||
4282       (orientation == GTK_ORIENTATION_VERTICAL && !priv->vertical_tree_line_texture))
4283     {
4284       cairo_surface_t *surface;
4285       unsigned char *data;
4286 
4287       g_clear_object (&priv->horizontal_tree_line_texture);
4288       g_clear_object (&priv->vertical_tree_line_texture);
4289       priv->tree_line_color = *tree_line_color;
4290 
4291       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2, 1);
4292       data = cairo_image_surface_get_data (surface);
4293       /* just color the first pixel... */
4294       data[0] = round (tree_line_color->blue  * 255);
4295       data[1] = round (tree_line_color->green * 255);
4296       data[2] = round (tree_line_color->red   * 255);
4297       data[3] = round (tree_line_color->alpha * 255);
4298 
4299       priv->horizontal_tree_line_texture = gdk_texture_new_for_surface (surface);
4300       cairo_surface_destroy (surface);
4301 
4302       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 2);
4303       data = cairo_image_surface_get_data (surface);
4304       data[0] = round (tree_line_color->blue  * 255);
4305       data[1] = round (tree_line_color->green * 255);
4306       data[2] = round (tree_line_color->red   * 255);
4307       data[3] = round (tree_line_color->alpha * 255);
4308 
4309       priv->vertical_tree_line_texture = gdk_texture_new_for_surface (surface);
4310       cairo_surface_destroy (surface);
4311     }
4312 
4313   g_assert (priv->horizontal_tree_line_texture);
4314   g_assert (priv->vertical_tree_line_texture);
4315 
4316   if (orientation == GTK_ORIENTATION_HORIZONTAL)
4317     {
4318       gtk_snapshot_push_repeat (snapshot,
4319                                 &GRAPHENE_RECT_INIT (
4320                                   start->x, start->y,
4321                                   size, 1),
4322                                 NULL);
4323       gtk_snapshot_append_texture (snapshot, priv->horizontal_tree_line_texture,
4324                                    &GRAPHENE_RECT_INIT (0, 0, 2, 1));
4325       gtk_snapshot_pop (snapshot);
4326     }
4327   else /* VERTICAL */
4328     {
4329       gtk_snapshot_push_repeat (snapshot,
4330                                 &GRAPHENE_RECT_INIT (
4331                                   start->x, start->y,
4332                                   1, size),
4333                                 NULL);
4334       gtk_snapshot_append_texture (snapshot, priv->vertical_tree_line_texture,
4335                                    &GRAPHENE_RECT_INIT (0, 0, 1, 2));
4336       gtk_snapshot_pop (snapshot);
4337     }
4338 }
4339 
4340 static void
gtk_tree_view_snapshot_grid_lines(GtkTreeView * tree_view,GtkSnapshot * snapshot)4341 gtk_tree_view_snapshot_grid_lines (GtkTreeView *tree_view,
4342                                    GtkSnapshot *snapshot)
4343 {
4344   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4345   GList *list, *first, *last;
4346   gboolean rtl;
4347   int current_x = 0;
4348   int tree_view_height;
4349 
4350   if (priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_VERTICAL &&
4351       priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_BOTH)
4352     return;
4353 
4354   rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4355 
4356   first = g_list_first (priv->columns);
4357   last = g_list_last (priv->columns);
4358   tree_view_height = gtk_tree_view_get_height (tree_view);
4359 
4360   for (list = (rtl ? last : first);
4361        list;
4362        list = (rtl ? list->prev : list->next))
4363     {
4364       GtkTreeViewColumn *column = list->data;
4365 
4366       /* We don't want a line for the last column */
4367       if (column == (rtl ? first->data : last->data))
4368         break;
4369 
4370       if (!gtk_tree_view_column_get_visible (column))
4371         continue;
4372 
4373       current_x += gtk_tree_view_column_get_width (column);
4374 
4375       gtk_tree_view_snapshot_grid_line (tree_view,
4376                                         snapshot,
4377                                         GTK_ORIENTATION_VERTICAL,
4378                                         &(graphene_point_t) { current_x - 1, 0 },
4379                                         tree_view_height);
4380     }
4381 }
4382 
4383 /* Warning: Very scary function.
4384  * Modify at your own risk
4385  *
4386  * KEEP IN SYNC WITH gtk_tree_view_create_row_drag_icon()!
4387  * FIXME: It’s not...
4388  */
4389 static void
gtk_tree_view_bin_snapshot(GtkWidget * widget,GtkSnapshot * snapshot)4390 gtk_tree_view_bin_snapshot (GtkWidget   *widget,
4391 			    GtkSnapshot *snapshot)
4392 {
4393   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
4394   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
4395   const int x_scroll_offset = - gtk_adjustment_get_value (priv->hadjustment);
4396   GtkTreePath *path;
4397   GtkTreeRBTree *tree;
4398   GList *list;
4399   GtkTreeRBNode *node;
4400   GtkTreeRBNode *drag_highlight = NULL;
4401   GtkTreeRBTree *drag_highlight_tree = NULL;
4402   GtkTreeIter iter;
4403   int new_y;
4404   int y_offset, cell_offset;
4405   int max_height;
4406   int depth;
4407   GdkRectangle background_area;
4408   GdkRectangle cell_area;
4409   GdkRectangle clip;
4410   guint flags;
4411   int bin_window_width;
4412   int bin_window_height;
4413   GtkTreePath *drag_dest_path;
4414   GList *first_column, *last_column;
4415   gboolean has_can_focus_cell;
4416   gboolean rtl;
4417   int n_visible_columns;
4418   int expander_size;
4419   gboolean draw_vgrid_lines, draw_hgrid_lines;
4420   GtkStyleContext *context;
4421   gboolean parity;
4422 
4423   rtl = (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4424   context = gtk_widget_get_style_context (widget);
4425 
4426   if (priv->tree == NULL)
4427     return;
4428 
4429   bin_window_width = gtk_widget_get_width (GTK_WIDGET (tree_view));
4430   bin_window_height = gtk_widget_get_height(GTK_WIDGET (tree_view));
4431 
4432   clip = (GdkRectangle) { 0, 0, bin_window_width, bin_window_height };
4433   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, clip.y);
4434   y_offset = -gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node);
4435 
4436   if (gtk_tree_view_get_height (tree_view) < bin_window_height)
4437     {
4438       gtk_style_context_save (context);
4439       gtk_style_context_add_class (context, "cell");
4440 
4441       gtk_snapshot_render_background (snapshot, context,
4442                                       0, gtk_tree_view_get_height (tree_view),
4443                                       bin_window_width,
4444                                       bin_window_height - gtk_tree_view_get_height (tree_view));
4445 
4446       gtk_style_context_restore (context);
4447     }
4448 
4449   if (node == NULL)
4450     return;
4451 
4452   /* find the path for the node */
4453   path = _gtk_tree_path_new_from_rbtree (tree, node);
4454   gtk_tree_model_get_iter (priv->model,
4455 			   &iter,
4456 			   path);
4457   depth = gtk_tree_path_get_depth (path);
4458   gtk_tree_path_free (path);
4459 
4460   drag_dest_path = NULL;
4461 
4462   if (priv->drag_dest_row)
4463     drag_dest_path = gtk_tree_row_reference_get_path (priv->drag_dest_row);
4464 
4465   if (drag_dest_path)
4466     _gtk_tree_view_find_node (tree_view, drag_dest_path,
4467                               &drag_highlight_tree, &drag_highlight);
4468 
4469   draw_vgrid_lines =
4470     priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
4471     || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
4472   draw_hgrid_lines =
4473     priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
4474     || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
4475   expander_size = gtk_tree_view_get_expander_size (tree_view);
4476 
4477   n_visible_columns = 0;
4478   for (list = priv->columns; list; list = list->next)
4479     {
4480       if (!gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
4481 	continue;
4482       n_visible_columns ++;
4483     }
4484 
4485   /* Find the last column */
4486   for (last_column = g_list_last (priv->columns);
4487        last_column &&
4488        !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
4489        last_column = last_column->prev)
4490     ;
4491 
4492   /* and the first */
4493   for (first_column = g_list_first (priv->columns);
4494        first_column &&
4495        !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
4496        first_column = first_column->next)
4497     ;
4498 
4499   /* Actually process the expose event.  To do this, we want to
4500    * start at the first node of the event, and walk the tree in
4501    * order, drawing each successive node.
4502    */
4503 
4504   parity = !(gtk_tree_rbtree_node_get_index (tree, node) % 2);
4505 
4506   do
4507     {
4508       gboolean is_separator = FALSE;
4509       int n_col = 0;
4510 
4511       parity = !parity;
4512       is_separator = row_is_separator (tree_view, &iter, NULL);
4513 
4514       max_height = gtk_tree_view_get_row_height (tree_view, node);
4515 
4516       cell_offset = x_scroll_offset;
4517 
4518       background_area.y = y_offset + clip.y;
4519       background_area.height = max_height;
4520 
4521       flags = 0;
4522 
4523       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PRELIT))
4524 	flags |= GTK_CELL_RENDERER_PRELIT;
4525 
4526       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
4527         flags |= GTK_CELL_RENDERER_SELECTED;
4528 
4529       /* we *need* to set cell data on all cells before the call
4530        * to _has_can_focus_cell, else _has_can_focus_cell() does not
4531        * return a correct value.
4532        */
4533       for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
4534 	   list;
4535 	   list = (rtl ? list->prev : list->next))
4536         {
4537 	  GtkTreeViewColumn *column = list->data;
4538 	  gtk_tree_view_column_cell_set_cell_data (column,
4539 						   priv->model,
4540 						   &iter,
4541 						   GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT),
4542 						   node->children?TRUE:FALSE);
4543         }
4544 
4545       has_can_focus_cell = gtk_tree_view_has_can_focus_cell (tree_view);
4546 
4547       for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
4548 	   list;
4549 	   list = (rtl ? list->prev : list->next))
4550 	{
4551 	  GtkTreeViewColumn *column = list->data;
4552 	  GtkStateFlags state = 0;
4553           int width;
4554           gboolean draw_focus;
4555 
4556 	  if (!gtk_tree_view_column_get_visible (column))
4557             continue;
4558 
4559           n_col++;
4560           width = gtk_tree_view_column_get_width (column);
4561 
4562 	  if (cell_offset > clip.x + clip.width ||
4563 	      cell_offset + width < clip.x)
4564 	    {
4565 	      cell_offset += width;
4566 	      continue;
4567 	    }
4568 
4569           if (gtk_tree_view_column_get_sort_indicator (column))
4570 	    flags |= GTK_CELL_RENDERER_SORTED;
4571           else
4572             flags &= ~GTK_CELL_RENDERER_SORTED;
4573 
4574 	  if (priv->cursor_node == node)
4575             flags |= GTK_CELL_RENDERER_FOCUSED;
4576           else
4577             flags &= ~GTK_CELL_RENDERER_FOCUSED;
4578 
4579           if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT))
4580             flags |= GTK_CELL_RENDERER_EXPANDABLE;
4581           else
4582             flags &= ~GTK_CELL_RENDERER_EXPANDABLE;
4583 
4584           if (node->children)
4585             flags |= GTK_CELL_RENDERER_EXPANDED;
4586           else
4587             flags &= ~GTK_CELL_RENDERER_EXPANDED;
4588 
4589           background_area.x = cell_offset;
4590 	  background_area.width = width;
4591 
4592           cell_area = background_area;
4593           cell_area.x += _TREE_VIEW_HORIZONTAL_SEPARATOR /2;
4594           cell_area.width -= _TREE_VIEW_HORIZONTAL_SEPARATOR;
4595 
4596 	  if (draw_vgrid_lines)
4597 	    {
4598 	      if (list == first_column)
4599 	        {
4600 		  cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH / 2;
4601 		}
4602 	      else if (list == last_column)
4603 	        {
4604 		  cell_area.x += _TREE_VIEW_GRID_LINE_WIDTH / 2;
4605 		  cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH / 2;
4606 		}
4607 	      else
4608 	        {
4609 	          cell_area.x += _TREE_VIEW_GRID_LINE_WIDTH / 2;
4610 	          cell_area.width -= _TREE_VIEW_GRID_LINE_WIDTH;
4611 		}
4612 	    }
4613 
4614 	  if (draw_hgrid_lines)
4615 	    {
4616 	      cell_area.y += _TREE_VIEW_GRID_LINE_WIDTH / 2;
4617 	      cell_area.height -= _TREE_VIEW_GRID_LINE_WIDTH;
4618 	    }
4619 
4620 	  if (!gdk_rectangle_intersect (&clip, &background_area, NULL))
4621 	    {
4622 	      cell_offset += gtk_tree_view_column_get_width (column);
4623 	      continue;
4624 	    }
4625 
4626           background_area.x -= x_scroll_offset;
4627           cell_area.x -= x_scroll_offset;
4628 
4629 	  gtk_tree_view_column_cell_set_cell_data (column,
4630 						   priv->model,
4631 						   &iter,
4632 						   GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT),
4633 						   node->children?TRUE:FALSE);
4634 
4635           gtk_style_context_save (context);
4636 
4637           state = gtk_cell_renderer_get_state (NULL, widget, flags);
4638           gtk_style_context_set_state (context, state);
4639 
4640           gtk_style_context_add_class (context, "cell");
4641 
4642 	  if (node == priv->cursor_node && has_can_focus_cell
4643               && ((column == priv->focus_column
4644                    && priv->draw_keyfocus &&
4645                    gtk_widget_has_visible_focus (widget))
4646                   || (column == priv->edited_column)))
4647             draw_focus = TRUE;
4648           else
4649             draw_focus = FALSE;
4650 
4651 	  /* Draw background */
4652           gtk_snapshot_render_background (snapshot, context,
4653                                           background_area.x,
4654                                           background_area.y,
4655                                           background_area.width,
4656                                           background_area.height);
4657 
4658           /* Draw frame */
4659           gtk_snapshot_render_frame (snapshot, context,
4660                                      background_area.x,
4661                                      background_area.y,
4662                                      background_area.width,
4663                                      background_area.height);
4664 
4665 	  if (gtk_tree_view_is_expander_column (tree_view, column))
4666 	    {
4667 	      if (!rtl)
4668 		cell_area.x += (depth - 1) * priv->level_indentation;
4669 	      cell_area.width -= (depth - 1) * priv->level_indentation;
4670 
4671               if (gtk_tree_view_draw_expanders (tree_view))
4672 	        {
4673 	          if (!rtl)
4674 		    cell_area.x += depth * expander_size;
4675 		  cell_area.width -= depth * expander_size;
4676 		}
4677 
4678 	      if (is_separator)
4679                 {
4680                   GdkRGBA color;
4681 
4682                   gtk_style_context_save (context);
4683                   gtk_style_context_add_class (context, "separator");
4684 
4685                   gtk_style_context_get_color (context, &color);
4686                   gtk_snapshot_append_color (snapshot,
4687                                              &color,
4688                                              &GRAPHENE_RECT_INIT(
4689                                                  cell_area.x,
4690                                                  cell_area.y + cell_area.height / 2,
4691                                                  cell_area.x + cell_area.width,
4692                                                  1
4693                                              ));
4694 
4695                   gtk_style_context_restore (context);
4696                 }
4697 	      else
4698                 {
4699                   gtk_tree_view_column_cell_snapshot (column,
4700                                                       snapshot,
4701                                                       &background_area,
4702                                                       &cell_area,
4703                                                       flags,
4704                                                       draw_focus);
4705                 }
4706 
4707 	      if (gtk_tree_view_draw_expanders (tree_view)
4708 		  && (node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT)
4709 		{
4710 		  gtk_tree_view_snapshot_arrow (GTK_TREE_VIEW (widget),
4711                                                 snapshot,
4712                                                 tree,
4713                                                 node);
4714 		}
4715 	    }
4716 	  else
4717 	    {
4718 	      if (is_separator)
4719                 {
4720                   GdkRGBA color;
4721 
4722                   gtk_style_context_save (context);
4723                   gtk_style_context_add_class (context, "separator");
4724 
4725                   gtk_style_context_get_color (context, &color);
4726                   gtk_snapshot_append_color (snapshot,
4727                                              &color,
4728                                              &GRAPHENE_RECT_INIT(
4729                                                  cell_area.x,
4730                                                  cell_area.y + cell_area.height / 2,
4731                                                  cell_area.x + cell_area.width,
4732                                                  1
4733                                              ));
4734 
4735                   gtk_style_context_restore (context);
4736                 }
4737 	      else
4738 		gtk_tree_view_column_cell_snapshot (column,
4739                                                     snapshot,
4740                                                     &background_area,
4741                                                     &cell_area,
4742                                                     flags,
4743                                                     draw_focus);
4744 	    }
4745 
4746           if (draw_hgrid_lines)
4747             {
4748               if (background_area.y >= clip.y)
4749                 gtk_tree_view_snapshot_grid_line (tree_view,
4750                                                   snapshot,
4751                                                   GTK_ORIENTATION_HORIZONTAL,
4752                                                   &(graphene_point_t) {
4753                                                     background_area.x, background_area.y
4754                                                   },
4755                                                   background_area.width);
4756 
4757               if (background_area.y + max_height < clip.y + clip.height)
4758                 gtk_tree_view_snapshot_grid_line (tree_view,
4759                                                   snapshot,
4760                                                   GTK_ORIENTATION_HORIZONTAL,
4761                                                   &(graphene_point_t) {
4762                                                     background_area.x, background_area.y + max_height
4763                                                   },
4764                                                   background_area.width);
4765             }
4766 
4767 	  if (gtk_tree_view_is_expander_column (tree_view, column) &&
4768 	      priv->tree_lines_enabled)
4769 	    {
4770 	      int x = background_area.x;
4771 	      int mult = rtl ? -1 : 1;
4772 	      int y0 = background_area.y;
4773 	      int y1 = background_area.y + background_area.height/2;
4774 	      int y2 = background_area.y + background_area.height;
4775 
4776               if (rtl)
4777                 x += background_area.width - 1;
4778 
4779               if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT &&
4780                   depth > 1)
4781                 {
4782                   gtk_tree_view_snapshot_tree_line (tree_view,
4783                                                     snapshot,
4784                                                     GTK_ORIENTATION_HORIZONTAL,
4785                                                     &(graphene_point_t) {
4786                                                       x + expander_size * (depth - 1.5) * mult,
4787                                                       y1
4788                                                     },
4789                                                     mult * expander_size * 0.4);
4790 
4791                 }
4792               else if (depth > 1)
4793                 {
4794                   gtk_tree_view_snapshot_tree_line (tree_view,
4795                                                     snapshot,
4796                                                     GTK_ORIENTATION_HORIZONTAL,
4797                                                     &(graphene_point_t) {
4798                                                       x + expander_size * (depth - 1.5) * mult,
4799                                                       y1
4800                                                     },
4801                                                     mult * expander_size);
4802                 }
4803 
4804               if (depth > 1)
4805                 {
4806                   int i;
4807                   GtkTreeRBNode *tmp_node;
4808                   GtkTreeRBTree *tmp_tree;
4809 
4810                   if (!gtk_tree_rbtree_next (tree, node))
4811                     gtk_tree_view_snapshot_tree_line (tree_view,
4812                                                       snapshot,
4813                                                       GTK_ORIENTATION_VERTICAL,
4814                                                       &(graphene_point_t) {
4815                                                         x + expander_size * (depth - 1.5) * mult,
4816                                                         y0
4817                                                       },
4818                                                       y1 - y0);
4819                   else
4820                     gtk_tree_view_snapshot_tree_line (tree_view,
4821                                                       snapshot,
4822                                                       GTK_ORIENTATION_VERTICAL,
4823                                                       &(graphene_point_t) {
4824                                                         x + expander_size * (depth - 1.5) * mult,
4825                                                         y0
4826                                                       },
4827                                                       y2 - y0);
4828 
4829                   tmp_node = tree->parent_node;
4830                   tmp_tree = tree->parent_tree;
4831 
4832                   for (i = depth - 2; i > 0; i--)
4833                     {
4834                       if (gtk_tree_rbtree_next (tmp_tree, tmp_node))
4835                         gtk_tree_view_snapshot_tree_line (tree_view,
4836                                                           snapshot,
4837                                                           GTK_ORIENTATION_VERTICAL,
4838                                                           &(graphene_point_t) {
4839                                                             x + expander_size * (i - 0.5) * mult,
4840                                                             y0
4841                                                           },
4842                                                           y2 - y0);
4843                       tmp_node = tmp_tree->parent_node;
4844                       tmp_tree = tmp_tree->parent_tree;
4845                     }
4846                 }
4847 	    }
4848 
4849           gtk_style_context_restore (context);
4850 	  cell_offset += gtk_tree_view_column_get_width (column);
4851 	}
4852 
4853       if (node == drag_highlight)
4854         {
4855 	  GtkTreeRBTree *drag_tree = NULL;
4856 	  GtkTreeRBNode *drag_node = NULL;
4857 
4858           _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node);
4859           if (drag_tree != NULL)
4860             {
4861               TreeViewDragInfo *di;
4862 
4863               di = get_info (tree_view);
4864               /* Draw indicator for the drop
4865                */
4866 
4867               switch (priv->drag_dest_pos)
4868                 {
4869                 case GTK_TREE_VIEW_DROP_BEFORE:
4870                   gtk_css_node_set_classes (di->cssnode, (const char *[]){"before", NULL});
4871                   break;
4872 
4873                 case GTK_TREE_VIEW_DROP_AFTER:
4874                   gtk_css_node_set_classes (di->cssnode, (const char *[]){"after", NULL});
4875                   break;
4876 
4877                 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
4878                 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
4879                   gtk_css_node_set_classes (di->cssnode, (const char *[]){"into", NULL});
4880                   break;
4881 
4882                 default:
4883                  break;
4884                 }
4885 
4886              gtk_style_context_save_to_node (context, di->cssnode);
4887              gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE);
4888 
4889              gtk_snapshot_render_frame (snapshot, context,
4890                                         0, gtk_tree_view_get_row_y_offset (tree_view, drag_tree, drag_node),
4891                                         bin_window_width,
4892                                         gtk_tree_view_get_row_height (tree_view, drag_node));
4893 
4894              gtk_style_context_restore (context);
4895           }
4896         }
4897 
4898       /* draw the big row-spanning focus rectangle, if needed */
4899       if (!has_can_focus_cell && node == priv->cursor_node &&
4900           priv->draw_keyfocus &&
4901 	  gtk_widget_has_visible_focus (widget))
4902         {
4903 	  int tmp_y, tmp_height;
4904 	  GtkStateFlags focus_rect_state = 0;
4905 
4906           gtk_style_context_save (context);
4907 
4908           focus_rect_state = gtk_cell_renderer_get_state (NULL, widget, flags);
4909           gtk_style_context_set_state (context, focus_rect_state);
4910 
4911 	  if (draw_hgrid_lines)
4912 	    {
4913 	      tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node) + _TREE_VIEW_GRID_LINE_WIDTH / 2;
4914               tmp_height = gtk_tree_view_get_row_height (tree_view, node) - _TREE_VIEW_GRID_LINE_WIDTH;
4915 	    }
4916 	  else
4917 	    {
4918 	      tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
4919               tmp_height = gtk_tree_view_get_row_height (tree_view, node);
4920 	    }
4921 
4922           gtk_snapshot_render_focus (snapshot, context,
4923                                      0, tmp_y,
4924                                      bin_window_width,
4925                                      tmp_height);
4926 
4927           gtk_style_context_restore (context);
4928         }
4929 
4930       y_offset += max_height;
4931       if (node->children)
4932 	{
4933 	  GtkTreeIter parent = iter;
4934 	  gboolean has_child;
4935 
4936 	  tree = node->children;
4937           node = gtk_tree_rbtree_first (tree);
4938 
4939 	  has_child = gtk_tree_model_iter_children (priv->model,
4940 						    &iter,
4941 						    &parent);
4942 	  depth++;
4943 
4944 	  /* Sanity Check! */
4945 	  TREE_VIEW_INTERNAL_ASSERT_VOID (has_child);
4946 	}
4947       else
4948 	{
4949 	  gboolean done = FALSE;
4950 
4951 	  do
4952 	    {
4953 	      node = gtk_tree_rbtree_next (tree, node);
4954 	      if (node != NULL)
4955 		{
4956 		  gboolean has_next = gtk_tree_model_iter_next (priv->model, &iter);
4957 		  done = TRUE;
4958 
4959 		  /* Sanity Check! */
4960 		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4961 		}
4962 	      else
4963 		{
4964 		  GtkTreeIter parent_iter = iter;
4965 		  gboolean has_parent;
4966 
4967 		  node = tree->parent_node;
4968 		  tree = tree->parent_tree;
4969 		  if (tree == NULL)
4970 		    /* we should go to done to free some memory */
4971 		    goto done;
4972 		  has_parent = gtk_tree_model_iter_parent (priv->model,
4973 							   &iter,
4974 							   &parent_iter);
4975 		  depth--;
4976 
4977 		  /* Sanity check */
4978 		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
4979 		}
4980 	    }
4981 	  while (!done);
4982 	}
4983     }
4984   while (y_offset < clip.height);
4985 
4986 done:
4987   gtk_tree_view_snapshot_grid_lines (tree_view, snapshot);
4988 
4989   if (priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4990     gtk_tree_view_snapshot_rubber_band (tree_view, snapshot);
4991 
4992   if (drag_dest_path)
4993     gtk_tree_path_free (drag_dest_path);
4994 }
4995 
4996 static void
gtk_tree_view_snapshot(GtkWidget * widget,GtkSnapshot * snapshot)4997 gtk_tree_view_snapshot (GtkWidget   *widget,
4998                         GtkSnapshot *snapshot)
4999 {
5000   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5001   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5002   GtkWidget *button;
5003   GtkStyleContext *context;
5004   GList *list;
5005   int width, height;
5006 
5007   context = gtk_widget_get_style_context (widget);
5008   width = gtk_widget_get_width (widget);
5009   height = gtk_widget_get_height (widget);
5010 
5011   gtk_snapshot_push_clip (snapshot,
5012                           &GRAPHENE_RECT_INIT(
5013                               0, gtk_tree_view_get_effective_header_height (tree_view),
5014                               width,
5015                               height - gtk_tree_view_get_effective_header_height (tree_view)
5016                           ));
5017 
5018   gtk_snapshot_save (snapshot);
5019   gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (
5020                           - (int) gtk_adjustment_get_value (priv->hadjustment),
5021                           gtk_tree_view_get_effective_header_height (tree_view)));
5022   gtk_tree_view_bin_snapshot (widget, snapshot);
5023   gtk_snapshot_restore (snapshot);
5024 
5025   /* We can't just chain up to Container::draw as it will try to send the
5026    * event to the headers, so we handle propagating it to our children
5027    * (eg. widgets being edited) ourselves.
5028    */
5029   for (list = priv->children; list; list = list->next)
5030     {
5031       GtkTreeViewChild *child = list->data;
5032 
5033       gtk_widget_snapshot_child (widget, child->widget, snapshot);
5034     }
5035 
5036   gtk_snapshot_pop (snapshot);
5037 
5038   gtk_snapshot_push_clip (snapshot,
5039                           &GRAPHENE_RECT_INIT(
5040                               0, 0,
5041                               width,
5042                               gtk_tree_view_get_effective_header_height (tree_view)
5043                           ));
5044 
5045   gtk_style_context_save (context);
5046   gtk_style_context_remove_class (context, "view");
5047 
5048   for (list = priv->columns; list != NULL; list = list->next)
5049     {
5050       GtkTreeViewColumn *column = list->data;
5051 
5052       if (column == priv->drag_column)
5053         continue;
5054 
5055       if (gtk_tree_view_column_get_visible (column))
5056         {
5057           button = gtk_tree_view_column_get_button (column);
5058           gtk_widget_snapshot_child (widget, button, snapshot);
5059         }
5060     }
5061 
5062   if (priv->drag_column)
5063     {
5064       button = gtk_tree_view_column_get_button (priv->drag_column);
5065       gtk_widget_snapshot_child (widget, button, snapshot);
5066     }
5067 
5068   gtk_style_context_restore (context);
5069 
5070   gtk_snapshot_pop (snapshot);
5071 }
5072 
5073 enum
5074 {
5075   DROP_HOME,
5076   DROP_RIGHT,
5077   DROP_LEFT,
5078   DROP_END
5079 };
5080 
5081 /* returns 0x1 when no column has been found -- yes it's hackish */
5082 static GtkTreeViewColumn *
gtk_tree_view_get_drop_column(GtkTreeView * tree_view,GtkTreeViewColumn * column,int drop_position)5083 gtk_tree_view_get_drop_column (GtkTreeView       *tree_view,
5084 			       GtkTreeViewColumn *column,
5085 			       int                drop_position)
5086 {
5087   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5088   GtkTreeViewColumn *left_column = NULL;
5089   GtkTreeViewColumn *cur_column = NULL;
5090   GList *tmp_list;
5091 
5092   if (!gtk_tree_view_column_get_reorderable (column))
5093     return (GtkTreeViewColumn *)0x1;
5094 
5095   switch (drop_position)
5096     {
5097       case DROP_HOME:
5098 	/* find first column where we can drop */
5099 	tmp_list = priv->columns;
5100 	if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5101 	  return (GtkTreeViewColumn *)0x1;
5102 
5103 	while (tmp_list)
5104 	  {
5105 	    g_assert (tmp_list);
5106 
5107 	    cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5108 	    tmp_list = tmp_list->next;
5109 
5110 	    if (left_column &&
5111                 gtk_tree_view_column_get_visible (left_column) == FALSE)
5112 	      continue;
5113 
5114 	    if (!priv->column_drop_func)
5115 	      return left_column;
5116 
5117 	    if (!priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data))
5118 	      {
5119 		left_column = cur_column;
5120 		continue;
5121 	      }
5122 
5123 	    return left_column;
5124 	  }
5125 
5126 	if (!priv->column_drop_func)
5127 	  return left_column;
5128 
5129 	if (priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data))
5130 	  return left_column;
5131 	else
5132 	  return (GtkTreeViewColumn *)0x1;
5133 	break;
5134 
5135       case DROP_RIGHT:
5136 	/* find first column after column where we can drop */
5137 	tmp_list = priv->columns;
5138 
5139 	for (; tmp_list; tmp_list = tmp_list->next)
5140 	  if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5141 	    break;
5142 
5143 	if (!tmp_list || !tmp_list->next)
5144 	  return (GtkTreeViewColumn *)0x1;
5145 
5146 	tmp_list = tmp_list->next;
5147 	left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5148 	tmp_list = tmp_list->next;
5149 
5150 	while (tmp_list)
5151 	  {
5152 	    g_assert (tmp_list);
5153 
5154 	    cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5155 	    tmp_list = tmp_list->next;
5156 
5157 	    if (left_column &&
5158                 gtk_tree_view_column_get_visible (left_column) == FALSE)
5159 	      {
5160 		left_column = cur_column;
5161 		if (tmp_list)
5162 		  tmp_list = tmp_list->next;
5163 	        continue;
5164 	      }
5165 
5166 	    if (!priv->column_drop_func)
5167 	      return left_column;
5168 
5169 	    if (!priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data))
5170 	      {
5171 		left_column = cur_column;
5172 		continue;
5173 	      }
5174 
5175 	    return left_column;
5176 	  }
5177 
5178 	if (!priv->column_drop_func)
5179 	  return left_column;
5180 
5181 	if (priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data))
5182 	  return left_column;
5183 	else
5184 	  return (GtkTreeViewColumn *)0x1;
5185 	break;
5186 
5187       case DROP_LEFT:
5188 	/* find first column before column where we can drop */
5189 	tmp_list = priv->columns;
5190 
5191 	for (; tmp_list; tmp_list = tmp_list->next)
5192 	  if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5193 	    break;
5194 
5195 	if (!tmp_list || !tmp_list->prev)
5196 	  return (GtkTreeViewColumn *)0x1;
5197 
5198 	tmp_list = tmp_list->prev;
5199 	cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5200 	tmp_list = tmp_list->prev;
5201 
5202 	while (tmp_list)
5203 	  {
5204 	    g_assert (tmp_list);
5205 
5206 	    left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5207 
5208 	    if (left_column &&
5209                 gtk_tree_view_column_get_visible (left_column) == FALSE)
5210 	      {
5211 		/*if (!tmp_list->prev)
5212 		  return (GtkTreeViewColumn *)0x1;
5213 		  */
5214 /*
5215 		cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->prev->data);
5216 		tmp_list = tmp_list->prev->prev;
5217 		continue;*/
5218 
5219 		cur_column = left_column;
5220 		if (tmp_list)
5221 		  tmp_list = tmp_list->prev;
5222 		continue;
5223 	      }
5224 
5225 	    if (!priv->column_drop_func)
5226 	      return left_column;
5227 
5228 	    if (priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data))
5229 	      return left_column;
5230 
5231 	    cur_column = left_column;
5232 	    tmp_list = tmp_list->prev;
5233 	  }
5234 
5235 	if (!priv->column_drop_func)
5236 	  return NULL;
5237 
5238 	if (priv->column_drop_func (tree_view, column, NULL, cur_column, priv->column_drop_func_data))
5239 	  return NULL;
5240 	else
5241 	  return (GtkTreeViewColumn *)0x1;
5242 	break;
5243 
5244       case DROP_END:
5245 	/* same as DROP_HOME case, but doing it backwards */
5246 	tmp_list = g_list_last (priv->columns);
5247 	cur_column = NULL;
5248 
5249 	if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5250 	  return (GtkTreeViewColumn *)0x1;
5251 
5252 	while (tmp_list)
5253 	  {
5254 	    g_assert (tmp_list);
5255 
5256 	    left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5257 
5258 	    if (left_column &&
5259                 gtk_tree_view_column_get_visible (left_column) == FALSE)
5260 	      {
5261 		cur_column = left_column;
5262 		tmp_list = tmp_list->prev;
5263 	      }
5264 
5265 	    if (!priv->column_drop_func)
5266 	      return left_column;
5267 
5268 	    if (priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data))
5269 	      return left_column;
5270 
5271 	    cur_column = left_column;
5272 	    tmp_list = tmp_list->prev;
5273 	  }
5274 
5275 	if (!priv->column_drop_func)
5276 	  return NULL;
5277 
5278 	if (priv->column_drop_func (tree_view, column, NULL, cur_column, priv->column_drop_func_data))
5279 	  return NULL;
5280 	else
5281 	  return (GtkTreeViewColumn *)0x1;
5282 	break;
5283 
5284       default:
5285         return (GtkTreeViewColumn *)0x1;
5286         break;
5287     }
5288 }
5289 
5290 static gboolean
gtk_tree_view_search_key_cancels_search(guint keyval)5291 gtk_tree_view_search_key_cancels_search (guint keyval)
5292 {
5293   return keyval == GDK_KEY_Escape
5294       || keyval == GDK_KEY_Tab
5295       || keyval == GDK_KEY_KP_Tab
5296       || keyval == GDK_KEY_ISO_Left_Tab;
5297 }
5298 
5299 static gboolean
gtk_tree_view_key_controller_key_pressed(GtkEventControllerKey * key,guint keyval,guint keycode,GdkModifierType state,GtkTreeView * tree_view)5300 gtk_tree_view_key_controller_key_pressed (GtkEventControllerKey *key,
5301                                           guint                  keyval,
5302                                           guint                  keycode,
5303                                           GdkModifierType        state,
5304                                           GtkTreeView           *tree_view)
5305 {
5306   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5307   GtkWidget *widget = GTK_WIDGET (tree_view);
5308   GtkWidget *button;
5309 
5310   if (priv->rubber_band_status)
5311     {
5312       if (keyval == GDK_KEY_Escape)
5313 	gtk_tree_view_stop_rubber_band (tree_view);
5314 
5315       return TRUE;
5316     }
5317 
5318   if (priv->in_column_drag)
5319     {
5320       if (keyval == GDK_KEY_Escape)
5321         gtk_gesture_set_state (GTK_GESTURE (priv->column_drag_gesture),
5322                                GTK_EVENT_SEQUENCE_DENIED);
5323       return TRUE;
5324     }
5325 
5326   if (priv->headers_visible)
5327     {
5328       GList *focus_column;
5329       gboolean rtl;
5330 
5331       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
5332 
5333       for (focus_column = priv->columns;
5334            focus_column;
5335            focus_column = focus_column->next)
5336         {
5337           GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5338 
5339 	  button = gtk_tree_view_column_get_button (column);
5340           if (gtk_widget_has_focus (button))
5341             break;
5342         }
5343 
5344       if (focus_column &&
5345           (state & GDK_SHIFT_MASK) && (state & GDK_ALT_MASK) &&
5346           (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left
5347            || keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right))
5348         {
5349           GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5350           int column_width;
5351 
5352           if (!gtk_tree_view_column_get_resizable (column))
5353             {
5354               gtk_widget_error_bell (widget);
5355               return TRUE;
5356             }
5357 
5358 	  column_width = gtk_tree_view_column_get_width (column);
5359 
5360           if (keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left)
5361               || keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
5362             {
5363 	      column_width = MAX (column_width - 2, 0);
5364             }
5365           else if (keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
5366                    || keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
5367             {
5368 	      column_width = column_width + 2;
5369             }
5370 
5371 	  gtk_tree_view_column_set_fixed_width (column, column_width);
5372 	  gtk_tree_view_column_set_expand (column, FALSE);
5373           return TRUE;
5374         }
5375 
5376       if (focus_column &&
5377           (state & GDK_ALT_MASK) &&
5378           (keyval == GDK_KEY_Left || keyval == GDK_KEY_KP_Left
5379            || keyval == GDK_KEY_Right || keyval == GDK_KEY_KP_Right
5380            || keyval == GDK_KEY_Home || keyval == GDK_KEY_KP_Home
5381            || keyval == GDK_KEY_End || keyval == GDK_KEY_KP_End))
5382         {
5383           GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5384 
5385           if (keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left)
5386               || keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
5387             {
5388               GtkTreeViewColumn *col;
5389               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT);
5390               if (col != (GtkTreeViewColumn *)0x1)
5391                 gtk_tree_view_move_column_after (tree_view, column, col);
5392               else
5393                 gtk_widget_error_bell (widget);
5394             }
5395           else if (keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
5396                    || keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
5397             {
5398               GtkTreeViewColumn *col;
5399               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT);
5400               if (col != (GtkTreeViewColumn *)0x1)
5401                 gtk_tree_view_move_column_after (tree_view, column, col);
5402               else
5403                 gtk_widget_error_bell (widget);
5404             }
5405           else if (keyval == GDK_KEY_Home || keyval == GDK_KEY_KP_Home)
5406             {
5407               GtkTreeViewColumn *col;
5408               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME);
5409               if (col != (GtkTreeViewColumn *)0x1)
5410                 gtk_tree_view_move_column_after (tree_view, column, col);
5411               else
5412                 gtk_widget_error_bell (widget);
5413             }
5414           else if (keyval == GDK_KEY_End || keyval == GDK_KEY_KP_End)
5415             {
5416               GtkTreeViewColumn *col;
5417               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END);
5418               if (col != (GtkTreeViewColumn *)0x1)
5419                 gtk_tree_view_move_column_after (tree_view, column, col);
5420               else
5421                 gtk_widget_error_bell (widget);
5422             }
5423 
5424           return TRUE;
5425         }
5426     }
5427 
5428   return FALSE;
5429 }
5430 
5431 static gboolean
gtk_tree_view_forward_controller_key_pressed(GtkEventControllerKey * key,guint keyval,guint keycode,GdkModifierType state,GtkTreeView * tree_view)5432 gtk_tree_view_forward_controller_key_pressed (GtkEventControllerKey *key,
5433                                               guint                  keyval,
5434                                               guint                  keycode,
5435                                               GdkModifierType        state,
5436                                               GtkTreeView           *tree_view)
5437 {
5438   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5439 
5440   if (priv->search_entry_avoid_unhandled_binding)
5441     {
5442       priv->search_entry_avoid_unhandled_binding = FALSE;
5443       return FALSE;
5444     }
5445 
5446   /* Initially, before the search window is visible, we pass the event to the
5447    * IM context of the search entry box. If it triggers a commit or a preedit,
5448    * we then show the search window without losing tree view focus.
5449    * If the search window is already visible, we forward the events to it,
5450    * keeping the focus on the tree view.
5451    */
5452   if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
5453       && priv->enable_search
5454       && !priv->search_custom_entry_set
5455       && !gtk_tree_view_search_key_cancels_search (keyval))
5456     {
5457       gtk_tree_view_ensure_interactive_directory (tree_view);
5458 
5459       if (!gtk_widget_is_visible (priv->search_popover))
5460         {
5461           priv->imcontext_changed = FALSE;
5462 
5463           gtk_event_controller_key_forward (key, priv->search_entry);
5464 
5465           if (priv->imcontext_changed)
5466             return gtk_tree_view_real_start_interactive_search (tree_view, FALSE);
5467         }
5468     }
5469 
5470   return FALSE;
5471 }
5472 
5473 static void
gtk_tree_view_key_controller_key_released(GtkEventControllerKey * key,guint keyval,guint keycode,GdkModifierType state,GtkTreeView * tree_view)5474 gtk_tree_view_key_controller_key_released (GtkEventControllerKey *key,
5475                                            guint                  keyval,
5476                                            guint                  keycode,
5477                                            GdkModifierType        state,
5478                                            GtkTreeView           *tree_view)
5479 {
5480 }
5481 
5482 static void
gtk_tree_view_motion_controller_enter(GtkEventControllerMotion * controller,double x,double y,GtkTreeView * tree_view)5483 gtk_tree_view_motion_controller_enter (GtkEventControllerMotion *controller,
5484                                        double                    x,
5485                                        double                    y,
5486                                        GtkTreeView              *tree_view)
5487 {
5488   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5489   GtkTreeRBTree *tree;
5490   GtkTreeRBNode *node;
5491   int new_y;
5492 
5493   if (priv->tree == NULL)
5494     return;
5495 
5496   /* find the node internally */
5497   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(priv, y);
5498   if (new_y < 0)
5499     new_y = 0;
5500   gtk_tree_rbtree_find_offset (priv->tree, new_y, &tree, &node);
5501 
5502   priv->event_last_x = x;
5503   priv->event_last_y = y;
5504 
5505   if ((priv->button_pressed_node == NULL) ||
5506       (priv->button_pressed_node == node))
5507     prelight_or_select (tree_view, tree, node, x, y);
5508 }
5509 
5510 static void
gtk_tree_view_motion_controller_leave(GtkEventControllerMotion * controller,GtkTreeView * tree_view)5511 gtk_tree_view_motion_controller_leave (GtkEventControllerMotion *controller,
5512                                        GtkTreeView              *tree_view)
5513 {
5514   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5515 
5516   if (priv->prelight_node)
5517     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5518 
5519   priv->event_last_x = -10000;
5520   priv->event_last_y = -10000;
5521 
5522   prelight_or_select (tree_view, NULL, NULL, -1000, -1000); /* not possibly over an arrow */
5523 }
5524 
5525 static void
gtk_tree_view_focus_controller_focus_out(GtkEventController * focus,GtkTreeView * tree_view)5526 gtk_tree_view_focus_controller_focus_out (GtkEventController   *focus,
5527                                           GtkTreeView          *tree_view)
5528 {
5529   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5530 
5531   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5532 
5533   if (priv->search_popover &&
5534       !gtk_event_controller_focus_contains_focus (GTK_EVENT_CONTROLLER_FOCUS (focus)))
5535     gtk_tree_view_search_popover_hide (priv->search_popover, tree_view);
5536 }
5537 
5538 /* Incremental Reflow
5539  */
5540 
5541 static int
get_separator_height(GtkTreeView * tree_view)5542 get_separator_height (GtkTreeView *tree_view)
5543 {
5544   GtkStyleContext *context;
5545   GtkCssStyle *style;
5546   double d;
5547   int min_size;
5548 
5549   context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
5550   gtk_style_context_save (context);
5551   gtk_style_context_add_class (context, "separator");
5552 
5553   style = gtk_style_context_lookup_style (context);
5554   d = _gtk_css_number_value_get (style->size->min_height, 100);
5555 
5556   if (d < 1)
5557     min_size = ceil (d);
5558   else
5559     min_size = floor (d);
5560 
5561   gtk_style_context_restore (context);
5562 
5563   return min_size;
5564 }
5565 
5566 /* Returns TRUE if it updated the size
5567  */
5568 static gboolean
validate_row(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node,GtkTreeIter * iter,GtkTreePath * path)5569 validate_row (GtkTreeView   *tree_view,
5570 	      GtkTreeRBTree *tree,
5571 	      GtkTreeRBNode *node,
5572 	      GtkTreeIter   *iter,
5573 	      GtkTreePath *path)
5574 {
5575   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5576   GtkTreeViewColumn *column;
5577   GtkStyleContext *context;
5578   GList *list, *first_column, *last_column;
5579   int height = 0;
5580   int depth = gtk_tree_path_get_depth (path);
5581   gboolean retval = FALSE;
5582   gboolean is_separator = FALSE;
5583   gboolean draw_vgrid_lines, draw_hgrid_lines;
5584   int expander_size;
5585   int separator_height;
5586 
5587   /* double check the row needs validating */
5588   if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) &&
5589       ! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))
5590     return FALSE;
5591 
5592   is_separator = row_is_separator (tree_view, iter, NULL);
5593 
5594   draw_vgrid_lines =
5595     priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
5596     || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
5597   draw_hgrid_lines =
5598     priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
5599     || priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
5600   expander_size = gtk_tree_view_get_expander_size (tree_view);
5601 
5602   for (last_column = g_list_last (priv->columns);
5603        last_column &&
5604        !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
5605        last_column = last_column->prev)
5606     ;
5607 
5608   for (first_column = g_list_first (priv->columns);
5609        first_column &&
5610        !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
5611        first_column = first_column->next)
5612     ;
5613 
5614   separator_height = get_separator_height (tree_view);
5615 
5616   context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
5617   gtk_style_context_save (context);
5618   gtk_style_context_add_class (context, "cell");
5619 
5620   for (list = priv->columns; list; list = list->next)
5621     {
5622       int padding = 0;
5623       int original_width;
5624       int new_width;
5625       int row_height;
5626 
5627       column = list->data;
5628 
5629       if (!gtk_tree_view_column_get_visible (column))
5630 	continue;
5631 
5632       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) &&
5633 	  !_gtk_tree_view_column_cell_get_dirty (column))
5634 	continue;
5635 
5636       original_width = _gtk_tree_view_column_get_requested_width (column);
5637 
5638       gtk_tree_view_column_cell_set_cell_data (column, priv->model, iter,
5639 					       GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT),
5640 					       node->children?TRUE:FALSE);
5641       gtk_tree_view_column_cell_get_size (column,
5642                                           NULL, NULL,
5643                                           NULL, &row_height);
5644 
5645       if (is_separator)
5646         {
5647           height = separator_height;
5648           /* gtk_tree_view_get_row_height() assumes separator nodes are > 0 */
5649           height = MAX (height, 1);
5650         }
5651       else
5652         {
5653           height = MAX (height, row_height);
5654           height = MAX (height, expander_size);
5655         }
5656 
5657       if (gtk_tree_view_is_expander_column (tree_view, column))
5658         {
5659 	  padding += _TREE_VIEW_HORIZONTAL_SEPARATOR + (depth - 1) * priv->level_indentation;
5660 
5661 	  if (gtk_tree_view_draw_expanders (tree_view))
5662 	    padding += depth * expander_size;
5663 	}
5664       else
5665         padding += _TREE_VIEW_HORIZONTAL_SEPARATOR;
5666 
5667       if (draw_vgrid_lines)
5668         {
5669 	  if (list->data == first_column || list->data == last_column)
5670 	    padding += _TREE_VIEW_GRID_LINE_WIDTH / 2.0;
5671 	  else
5672 	    padding += _TREE_VIEW_GRID_LINE_WIDTH;
5673 	}
5674 
5675       /* Update the padding for the column */
5676       _gtk_tree_view_column_push_padding (column, padding);
5677       new_width = _gtk_tree_view_column_get_requested_width (column);
5678 
5679       if (new_width > original_width)
5680 	retval = TRUE;
5681     }
5682 
5683   gtk_style_context_restore (context);
5684 
5685   if (draw_hgrid_lines)
5686     height += _TREE_VIEW_GRID_LINE_WIDTH;
5687 
5688   if (height != GTK_TREE_RBNODE_GET_HEIGHT (node))
5689     {
5690       retval = TRUE;
5691       gtk_tree_rbtree_node_set_height (tree, node, height);
5692     }
5693   gtk_tree_rbtree_node_mark_valid (tree, node);
5694 
5695   return retval;
5696 }
5697 
5698 
5699 static void
validate_visible_area(GtkTreeView * tree_view)5700 validate_visible_area (GtkTreeView *tree_view)
5701 {
5702   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
5703   GtkTreePath *path = NULL;
5704   GtkTreePath *above_path = NULL;
5705   GtkTreeIter iter;
5706   GtkTreeRBTree *tree = NULL;
5707   GtkTreeRBNode *node = NULL;
5708   gboolean need_redraw = FALSE;
5709   gboolean size_changed = FALSE;
5710   int total_height;
5711   int area_above = 0;
5712   int area_below = 0;
5713 
5714   if (priv->tree == NULL)
5715     return;
5716 
5717   if (! GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID) &&
5718       priv->scroll_to_path == NULL)
5719     return;
5720 
5721   total_height = gtk_widget_get_height (GTK_WIDGET (tree_view))
5722                  - gtk_tree_view_get_effective_header_height (tree_view);
5723 
5724   if (total_height == 0)
5725     return;
5726 
5727   /* First, we check to see if we need to scroll anywhere
5728    */
5729   if (priv->scroll_to_path)
5730     {
5731       path = gtk_tree_row_reference_get_path (priv->scroll_to_path);
5732       if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node))
5733 	{
5734           /* we are going to scroll, and will update dy */
5735 	  gtk_tree_model_get_iter (priv->model, &iter, path);
5736 	  if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) ||
5737 	      GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))
5738 	    {
5739               gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5740 	      if (validate_row (tree_view, tree, node, &iter, path))
5741 		size_changed = TRUE;
5742 	    }
5743 
5744 	  if (priv->scroll_to_use_align)
5745 	    {
5746 	      int height = gtk_tree_view_get_row_height (tree_view, node);
5747 	      area_above = (total_height - height) *
5748 		priv->scroll_to_row_align;
5749 	      area_below = total_height - area_above - height;
5750 	      area_above = MAX (area_above, 0);
5751 	      area_below = MAX (area_below, 0);
5752 	    }
5753 	  else
5754 	    {
5755 	      /* two cases:
5756 	       * 1) row not visible
5757 	       * 2) row visible
5758 	       */
5759 	      int dy;
5760 	      int height = gtk_tree_view_get_row_height (tree_view, node);
5761 
5762 	      dy = gtk_tree_rbtree_node_find_offset (tree, node);
5763 
5764 	      if (dy >= gtk_adjustment_get_value (priv->vadjustment) &&
5765 		  dy + height <= (gtk_adjustment_get_value (priv->vadjustment)
5766 		                  + gtk_adjustment_get_page_size (priv->vadjustment)))
5767 	        {
5768 		  /* row visible: keep the row at the same position */
5769 		  area_above = dy - gtk_adjustment_get_value (priv->vadjustment);
5770 		  area_below = (gtk_adjustment_get_value (priv->vadjustment) +
5771 		                gtk_adjustment_get_page_size (priv->vadjustment))
5772 		               - dy - height;
5773 		}
5774 	      else
5775 	        {
5776 		  /* row not visible */
5777 		  if (dy >= 0
5778 		      && dy + height <= gtk_adjustment_get_page_size (priv->vadjustment))
5779 		    {
5780 		      /* row at the beginning -- fixed */
5781 		      area_above = dy;
5782 		      area_below = gtk_adjustment_get_page_size (priv->vadjustment)
5783 				   - area_above - height;
5784 		    }
5785 		  else if (dy >= (gtk_adjustment_get_upper (priv->vadjustment) -
5786 			          gtk_adjustment_get_page_size (priv->vadjustment)))
5787 		    {
5788 		      /* row at the end -- fixed */
5789 		      area_above = dy - (gtk_adjustment_get_upper (priv->vadjustment) -
5790 			           gtk_adjustment_get_page_size (priv->vadjustment));
5791                       area_below = gtk_adjustment_get_page_size (priv->vadjustment) -
5792                                    area_above - height;
5793 
5794                       if (area_below < 0)
5795                         {
5796 			  area_above = gtk_adjustment_get_page_size (priv->vadjustment) - height;
5797                           area_below = 0;
5798                         }
5799 		    }
5800 		  else
5801 		    {
5802 		      /* row somewhere in the middle, bring it to the top
5803 		       * of the view
5804 		       */
5805 		      area_above = 0;
5806 		      area_below = total_height - height;
5807 		    }
5808 		}
5809 	    }
5810 	}
5811       else
5812 	/* the scroll to isn't valid; ignore it.
5813 	 */
5814 	{
5815 	  if (priv->scroll_to_path && !path)
5816 	    {
5817 	      gtk_tree_row_reference_free (priv->scroll_to_path);
5818 	      priv->scroll_to_path = NULL;
5819 	    }
5820 	  if (path)
5821 	    gtk_tree_path_free (path);
5822 	  path = NULL;
5823 	}
5824     }
5825 
5826   /* We didn't have a scroll_to set, so we just handle things normally
5827    */
5828   if (path == NULL)
5829     {
5830       int offset;
5831 
5832       offset = gtk_tree_rbtree_find_offset (priv->tree,
5833                                             TREE_WINDOW_Y_TO_RBTREE_Y (priv, 0),
5834                                             &tree, &node);
5835       if (node == NULL)
5836 	{
5837 	  /* In this case, nothing has been validated */
5838 	  path = gtk_tree_path_new_first ();
5839 	  _gtk_tree_view_find_node (tree_view, path, &tree, &node);
5840 	}
5841       else
5842 	{
5843 	  path = _gtk_tree_path_new_from_rbtree (tree, node);
5844 	  total_height += offset;
5845 	}
5846 
5847       gtk_tree_model_get_iter (priv->model, &iter, path);
5848 
5849       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) ||
5850 	  GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))
5851 	{
5852           gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5853 	  if (validate_row (tree_view, tree, node, &iter, path))
5854 	    size_changed = TRUE;
5855 	}
5856       area_above = 0;
5857       area_below = total_height - gtk_tree_view_get_row_height (tree_view, node);
5858     }
5859 
5860   above_path = gtk_tree_path_copy (path);
5861 
5862   /* if we do not validate any row above the new top_row, we will make sure
5863    * that the row immediately above top_row has been validated. (if we do not
5864    * do this, gtk_tree_rbtree_find_offset will find the row above top_row, because
5865    * when invalidated that row's height will be zero. and this will mess up
5866    * scrolling).
5867    */
5868   if (area_above == 0)
5869     {
5870       GtkTreeRBTree *tmptree;
5871       GtkTreeRBNode *tmpnode;
5872 
5873       _gtk_tree_view_find_node (tree_view, above_path, &tmptree, &tmpnode);
5874       gtk_tree_rbtree_prev_full (tmptree, tmpnode, &tmptree, &tmpnode);
5875 
5876       if (tmpnode)
5877         {
5878 	  GtkTreePath *tmppath;
5879 	  GtkTreeIter tmpiter;
5880 
5881 	  tmppath = _gtk_tree_path_new_from_rbtree (tmptree, tmpnode);
5882 	  gtk_tree_model_get_iter (priv->model, &tmpiter, tmppath);
5883 
5884 	  if (GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_INVALID) ||
5885 	      GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_COLUMN_INVALID))
5886 	    {
5887               gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5888 	      if (validate_row (tree_view, tmptree, tmpnode, &tmpiter, tmppath))
5889 		size_changed = TRUE;
5890 	    }
5891 
5892 	  gtk_tree_path_free (tmppath);
5893 	}
5894     }
5895 
5896   /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5897    * backwards is much slower then forward, as there is no iter_prev function.
5898    * We go forwards first in case we run out of tree.  Then we go backwards to
5899    * fill out the top.
5900    */
5901   while (node && area_below > 0)
5902     {
5903       if (node->children)
5904 	{
5905 	  GtkTreeIter parent = iter;
5906 	  gboolean has_child;
5907 
5908 	  tree = node->children;
5909           node = gtk_tree_rbtree_first (tree);
5910 
5911 	  has_child = gtk_tree_model_iter_children (priv->model,
5912 						    &iter,
5913 						    &parent);
5914 	  TREE_VIEW_INTERNAL_ASSERT_VOID (has_child);
5915 	  gtk_tree_path_down (path);
5916 	}
5917       else
5918 	{
5919 	  gboolean done = FALSE;
5920 	  do
5921 	    {
5922 	      node = gtk_tree_rbtree_next (tree, node);
5923 	      if (node != NULL)
5924 		{
5925 		  gboolean has_next = gtk_tree_model_iter_next (priv->model, &iter);
5926 		  done = TRUE;
5927 		  gtk_tree_path_next (path);
5928 
5929 		  /* Sanity Check! */
5930 		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5931 		}
5932 	      else
5933 		{
5934 		  GtkTreeIter parent_iter = iter;
5935 		  gboolean has_parent;
5936 
5937 		  node = tree->parent_node;
5938 		  tree = tree->parent_tree;
5939 		  if (tree == NULL)
5940 		    break;
5941 		  has_parent = gtk_tree_model_iter_parent (priv->model,
5942 							   &iter,
5943 							   &parent_iter);
5944 		  gtk_tree_path_up (path);
5945 
5946 		  /* Sanity check */
5947 		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
5948 		}
5949 	    }
5950 	  while (!done);
5951 	}
5952 
5953       if (!node)
5954         break;
5955 
5956       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) ||
5957 	  GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))
5958 	{
5959           gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5960 	  if (validate_row (tree_view, tree, node, &iter, path))
5961 	      size_changed = TRUE;
5962 	}
5963 
5964       area_below -= gtk_tree_view_get_row_height (tree_view, node);
5965     }
5966   gtk_tree_path_free (path);
5967 
5968   /* If we ran out of tree, and have extra area_below left, we need to add it
5969    * to area_above */
5970   if (area_below > 0)
5971     area_above += area_below;
5972 
5973   _gtk_tree_view_find_node (tree_view, above_path, &tree, &node);
5974 
5975   /* We walk backwards */
5976   while (area_above > 0)
5977     {
5978       gtk_tree_rbtree_prev_full (tree, node, &tree, &node);
5979 
5980       /* Always find the new path in the tree.  We cannot just assume
5981        * a gtk_tree_path_prev() is enough here, as there might be children
5982        * in between this node and the previous sibling node.  If this
5983        * appears to be a performance hotspot in profiles, we can look into
5984        * intrigate logic for keeping path, node and iter in sync like
5985        * we do for forward walks.  (Which will be hard because of the lacking
5986        * iter_prev).
5987        */
5988 
5989       if (node == NULL)
5990 	break;
5991 
5992       gtk_tree_path_free (above_path);
5993       above_path = _gtk_tree_path_new_from_rbtree (tree, node);
5994 
5995       gtk_tree_model_get_iter (priv->model, &iter, above_path);
5996 
5997       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) ||
5998 	  GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))
5999 	{
6000           gtk_widget_queue_draw (GTK_WIDGET (tree_view));
6001 	  if (validate_row (tree_view, tree, node, &iter, above_path))
6002 	    size_changed = TRUE;
6003 	}
6004       area_above -= gtk_tree_view_get_row_height (tree_view, node);
6005     }
6006 
6007   /* if we scrolled to a path, we need to set the dy here,
6008    * and sync the top row accordingly
6009    */
6010   if (priv->scroll_to_path)
6011     {
6012       gtk_tree_view_set_top_row (tree_view, above_path, -area_above);
6013       gtk_tree_view_top_row_to_dy (tree_view);
6014 
6015       need_redraw = TRUE;
6016     }
6017   else if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (priv->vadjustment))
6018     {
6019       /* when we are not scrolling, we should never set dy to something
6020        * else than zero. we update top_row to be in sync with dy = 0.
6021        */
6022       gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), 0);
6023       gtk_tree_view_dy_to_top_row (tree_view);
6024     }
6025   else if (gtk_adjustment_get_value (priv->vadjustment) + gtk_adjustment_get_page_size (priv->vadjustment) > gtk_tree_view_get_height (tree_view))
6026     {
6027       gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (priv->vadjustment));
6028       gtk_tree_view_dy_to_top_row (tree_view);
6029     }
6030   else
6031     gtk_tree_view_top_row_to_dy (tree_view);
6032 
6033   /* update width/height and queue a resize */
6034   if (size_changed)
6035     {
6036       GtkRequisition requisition;
6037 
6038       /* We temporarily guess a size, under the assumption that it will be the
6039        * same when we get our next size_allocate.  If we don't do this, we'll be
6040        * in an inconsistent state if we call top_row_to_dy. */
6041 
6042       gtk_widget_get_preferred_size (GTK_WIDGET (tree_view),
6043                                      &requisition, NULL);
6044       gtk_adjustment_set_upper (priv->hadjustment,
6045                                 MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width));
6046       gtk_adjustment_set_upper (priv->vadjustment,
6047                                 MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height));
6048       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6049     }
6050 
6051   if (priv->scroll_to_path)
6052     {
6053       gtk_tree_row_reference_free (priv->scroll_to_path);
6054       priv->scroll_to_path = NULL;
6055     }
6056 
6057   if (above_path)
6058     gtk_tree_path_free (above_path);
6059 
6060   if (priv->scroll_to_column)
6061     {
6062       priv->scroll_to_column = NULL;
6063     }
6064   if (need_redraw)
6065     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
6066 }
6067 
6068 static void
initialize_fixed_height_mode(GtkTreeView * tree_view)6069 initialize_fixed_height_mode (GtkTreeView *tree_view)
6070 {
6071   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6072 
6073   if (!priv->tree)
6074     return;
6075 
6076   if (priv->fixed_height < 0)
6077     {
6078       GtkTreeIter iter;
6079       GtkTreePath *path;
6080 
6081       GtkTreeRBTree *tree = NULL;
6082       GtkTreeRBNode *node = NULL;
6083 
6084       tree = priv->tree;
6085       node = tree->root;
6086 
6087       path = _gtk_tree_path_new_from_rbtree (tree, node);
6088       gtk_tree_model_get_iter (priv->model, &iter, path);
6089 
6090       validate_row (tree_view, tree, node, &iter, path);
6091 
6092       gtk_tree_path_free (path);
6093 
6094       priv->fixed_height = gtk_tree_view_get_row_height (tree_view, node);
6095     }
6096 
6097    gtk_tree_rbtree_set_fixed_height (priv->tree,
6098                                  priv->fixed_height, TRUE);
6099 }
6100 
6101 /* Our strategy for finding nodes to validate is a little convoluted.  We find
6102  * the left-most uninvalidated node.  We then try walking right, validating
6103  * nodes.  Once we find a valid node, we repeat the previous process of finding
6104  * the first invalid node.
6105  */
6106 
6107 static gboolean
do_validate_rows(GtkTreeView * tree_view,gboolean queue_resize)6108 do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize)
6109 {
6110   static gboolean prevent_recursion_hack = FALSE;
6111 
6112   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6113   GtkTreeRBTree *tree = NULL;
6114   GtkTreeRBNode *node = NULL;
6115   gboolean validated_area = FALSE;
6116   int retval = TRUE;
6117   GtkTreePath *path = NULL;
6118   GtkTreeIter iter;
6119   GTimer *timer;
6120   int i = 0;
6121 
6122   int y = -1;
6123   int prev_height = -1;
6124   gboolean fixed_height = TRUE;
6125 
6126   g_assert (tree_view);
6127 
6128   /* prevent infinite recursion via get_preferred_width() */
6129   if (prevent_recursion_hack)
6130     return FALSE;
6131 
6132   if (priv->tree == NULL)
6133       return FALSE;
6134 
6135   if (priv->fixed_height_mode)
6136     {
6137       if (priv->fixed_height < 0)
6138         initialize_fixed_height_mode (tree_view);
6139 
6140       return FALSE;
6141     }
6142 
6143   timer = g_timer_new ();
6144   g_timer_start (timer);
6145 
6146   do
6147     {
6148       gboolean changed = FALSE;
6149 
6150       if (! GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID))
6151 	{
6152 	  retval = FALSE;
6153 	  goto done;
6154 	}
6155 
6156       if (path != NULL)
6157 	{
6158 	  node = gtk_tree_rbtree_next (tree, node);
6159 	  if (node != NULL)
6160 	    {
6161 	      TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_next (priv->model, &iter), FALSE);
6162 	      gtk_tree_path_next (path);
6163 	    }
6164 	  else
6165 	    {
6166 	      gtk_tree_path_free (path);
6167 	      path = NULL;
6168 	    }
6169 	}
6170 
6171       if (path == NULL)
6172 	{
6173 	  tree = priv->tree;
6174 	  node = priv->tree->root;
6175 
6176 	  g_assert (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
6177 
6178 	  do
6179 	    {
6180 	      if (!gtk_tree_rbtree_is_nil (node->left) &&
6181 		  GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID))
6182 		{
6183 		  node = node->left;
6184 		}
6185               else if (!gtk_tree_rbtree_is_nil (node->right) &&
6186 		       GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID))
6187 		{
6188 		  node = node->right;
6189 		}
6190 	      else if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) ||
6191 		       GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))
6192 		{
6193 		  break;
6194 		}
6195 	      else if (node->children != NULL)
6196 		{
6197 		  tree = node->children;
6198 		  node = tree->root;
6199 		}
6200 	      else
6201 		/* RBTree corruption!  All bad */
6202 		g_assert_not_reached ();
6203 	    }
6204 	  while (TRUE);
6205 	  path = _gtk_tree_path_new_from_rbtree (tree, node);
6206 	  gtk_tree_model_get_iter (priv->model, &iter, path);
6207 	}
6208 
6209       changed = validate_row (tree_view, tree, node, &iter, path);
6210       validated_area = changed || validated_area;
6211 
6212       if (changed)
6213         {
6214           int offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
6215 
6216           if (y == -1 || y > offset)
6217             y = offset;
6218         }
6219 
6220       if (!priv->fixed_height_check)
6221         {
6222 	  int height;
6223 
6224 	  height = gtk_tree_view_get_row_height (tree_view, node);
6225 	  if (prev_height < 0)
6226 	    prev_height = height;
6227 	  else if (prev_height != height)
6228 	    fixed_height = FALSE;
6229 	}
6230 
6231       i++;
6232     }
6233   while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.);
6234 
6235   if (!priv->fixed_height_check)
6236    {
6237      if (fixed_height)
6238        gtk_tree_rbtree_set_fixed_height (priv->tree, prev_height, FALSE);
6239 
6240      priv->fixed_height_check = 1;
6241    }
6242 
6243  done:
6244   if (validated_area)
6245     {
6246       GtkRequisition requisition;
6247       int dummy;
6248 
6249       /* We temporarily guess a size, under the assumption that it will be the
6250        * same when we get our next size_allocate.  If we don't do this, we'll be
6251        * in an inconsistent state when we call top_row_to_dy. */
6252 
6253       /* FIXME: This is called from size_request, for some reason it is not infinitely
6254        * recursing, we cannot call gtk_widget_get_preferred_size() here because that's
6255        * not allowed (from inside ->get_preferred_width/height() implementations, one
6256        * should call the vfuncs directly). However what is desired here is the full
6257        * size including any margins and limited by any alignment (i.e. after
6258        * GtkWidget:adjust_size_request() is called).
6259        *
6260        * Currently bypassing this but the real solution is to not update the scroll adjustments
6261        * until we've received an allocation (never update scroll adjustments from size-requests).
6262        */
6263       prevent_recursion_hack = TRUE;
6264       gtk_tree_view_measure (GTK_WIDGET (tree_view),
6265                              GTK_ORIENTATION_HORIZONTAL,
6266                              -1,
6267                              &requisition.width, &dummy,
6268                              NULL, NULL);
6269       gtk_tree_view_measure (GTK_WIDGET (tree_view),
6270                              GTK_ORIENTATION_VERTICAL,
6271                              -1,
6272                              &requisition.height, &dummy,
6273                              NULL, NULL);
6274       prevent_recursion_hack = FALSE;
6275 
6276       /* If rows above the current position have changed height, this has
6277        * affected the current view and thus needs a redraw.
6278        */
6279       if (y != -1 && y < gtk_adjustment_get_value (priv->vadjustment))
6280         gtk_widget_queue_draw (GTK_WIDGET (tree_view));
6281 
6282       gtk_adjustment_set_upper (priv->hadjustment,
6283                                 MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width));
6284       gtk_adjustment_set_upper (priv->vadjustment,
6285                                 MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height));
6286 
6287       if (queue_resize)
6288         gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6289     }
6290 
6291   if (path) gtk_tree_path_free (path);
6292   g_timer_destroy (timer);
6293 
6294   if (!retval && gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
6295     update_prelight (tree_view,
6296                      priv->event_last_x,
6297                      priv->event_last_y);
6298 
6299   return retval;
6300 }
6301 
6302 static void
disable_adjustment_animation(GtkTreeView * tree_view)6303 disable_adjustment_animation (GtkTreeView *tree_view)
6304 {
6305   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6306 
6307   gtk_adjustment_enable_animation (priv->vadjustment,
6308                                    NULL,
6309                                    gtk_adjustment_get_animation_duration (priv->vadjustment));
6310 }
6311 
6312 static void
maybe_reenable_adjustment_animation(GtkTreeView * tree_view)6313 maybe_reenable_adjustment_animation (GtkTreeView *tree_view)
6314 {
6315   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6316 
6317   if (priv->presize_handler_tick_cb != 0 ||
6318       priv->validate_rows_timer != 0)
6319     return;
6320 
6321   gtk_adjustment_enable_animation (priv->vadjustment,
6322                                    gtk_widget_get_frame_clock (GTK_WIDGET (tree_view)),
6323                                    gtk_adjustment_get_animation_duration (priv->vadjustment));
6324 }
6325 
6326 static gboolean
do_presize_handler(GtkTreeView * tree_view)6327 do_presize_handler (GtkTreeView *tree_view)
6328 {
6329   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6330 
6331   if (priv->mark_rows_col_dirty)
6332    {
6333       if (priv->tree)
6334 	gtk_tree_rbtree_column_invalid (priv->tree);
6335       priv->mark_rows_col_dirty = FALSE;
6336     }
6337   validate_visible_area (tree_view);
6338   if (priv->presize_handler_tick_cb != 0)
6339     {
6340       gtk_widget_remove_tick_callback (GTK_WIDGET (tree_view), priv->presize_handler_tick_cb);
6341       priv->presize_handler_tick_cb = 0;
6342     }
6343 
6344   if (priv->fixed_height_mode)
6345     {
6346       GtkRequisition requisition;
6347 
6348       gtk_widget_get_preferred_size (GTK_WIDGET (tree_view),
6349                                      &requisition, NULL);
6350 
6351       gtk_adjustment_set_upper (priv->hadjustment,
6352                                 MAX (gtk_adjustment_get_upper (priv->hadjustment), requisition.width));
6353       gtk_adjustment_set_upper (priv->vadjustment,
6354                                 MAX (gtk_adjustment_get_upper (priv->vadjustment), requisition.height));
6355       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6356     }
6357 
6358   maybe_reenable_adjustment_animation (tree_view);
6359 
6360   return FALSE;
6361 }
6362 
6363 static gboolean
presize_handler_callback(GtkWidget * widget,GdkFrameClock * clock,gpointer unused)6364 presize_handler_callback (GtkWidget     *widget,
6365                           GdkFrameClock *clock,
6366                           gpointer       unused)
6367 {
6368   do_presize_handler (GTK_TREE_VIEW (widget));
6369 
6370   return G_SOURCE_REMOVE;
6371 }
6372 
6373 static gboolean
validate_rows(GtkTreeView * tree_view)6374 validate_rows (GtkTreeView *tree_view)
6375 {
6376   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6377   gboolean retval;
6378 
6379   if (priv->presize_handler_tick_cb)
6380     {
6381       do_presize_handler (tree_view);
6382       return G_SOURCE_CONTINUE;
6383     }
6384 
6385   retval = do_validate_rows (tree_view, TRUE);
6386 
6387   if (! retval && priv->validate_rows_timer)
6388     {
6389       g_source_remove (priv->validate_rows_timer);
6390       priv->validate_rows_timer = 0;
6391       maybe_reenable_adjustment_animation (tree_view);
6392     }
6393 
6394   return retval;
6395 }
6396 
6397 static void
install_presize_handler(GtkTreeView * tree_view)6398 install_presize_handler (GtkTreeView *tree_view)
6399 {
6400   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6401 
6402   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
6403     return;
6404 
6405   disable_adjustment_animation (tree_view);
6406 
6407   if (! priv->presize_handler_tick_cb)
6408     {
6409       priv->presize_handler_tick_cb =
6410 	gtk_widget_add_tick_callback (GTK_WIDGET (tree_view), presize_handler_callback, NULL, NULL);
6411     }
6412   if (! priv->validate_rows_timer)
6413     {
6414       priv->validate_rows_timer =
6415 	g_idle_add_full (GTK_TREE_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows, tree_view, NULL);
6416       gdk_source_set_static_name_by_id (priv->validate_rows_timer, "[gtk] validate_rows");
6417     }
6418 }
6419 
6420 static gboolean
scroll_sync_handler(GtkTreeView * tree_view)6421 scroll_sync_handler (GtkTreeView *tree_view)
6422 {
6423   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6424 
6425   if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (priv->vadjustment))
6426     gtk_adjustment_set_value (GTK_ADJUSTMENT (priv->vadjustment), 0);
6427   else if (gtk_tree_row_reference_valid (priv->top_row))
6428     gtk_tree_view_top_row_to_dy (tree_view);
6429   else
6430     gtk_tree_view_dy_to_top_row (tree_view);
6431 
6432   priv->scroll_sync_timer = 0;
6433 
6434   return FALSE;
6435 }
6436 
6437 static void
install_scroll_sync_handler(GtkTreeView * tree_view)6438 install_scroll_sync_handler (GtkTreeView *tree_view)
6439 {
6440   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6441 
6442   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
6443     return;
6444 
6445   if (!priv->scroll_sync_timer)
6446     {
6447       priv->scroll_sync_timer =
6448 	g_idle_add_full (GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
6449       gdk_source_set_static_name_by_id (priv->scroll_sync_timer, "[gtk] scroll_sync_handler");
6450     }
6451 }
6452 
6453 static void
gtk_tree_view_set_top_row(GtkTreeView * tree_view,GtkTreePath * path,int offset)6454 gtk_tree_view_set_top_row (GtkTreeView *tree_view,
6455 			   GtkTreePath *path,
6456 			   int          offset)
6457 {
6458   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6459 
6460   gtk_tree_row_reference_free (priv->top_row);
6461 
6462   if (!path)
6463     {
6464       priv->top_row = NULL;
6465       priv->top_row_dy = 0;
6466     }
6467   else
6468     {
6469       priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path);
6470       priv->top_row_dy = offset;
6471     }
6472 }
6473 
6474 /* Always call this iff dy is in the visible range.  If the tree is empty, then
6475  * it’s set to be NULL, and top_row_dy is 0;
6476  */
6477 static void
gtk_tree_view_dy_to_top_row(GtkTreeView * tree_view)6478 gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view)
6479 {
6480   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6481   int offset;
6482   GtkTreePath *path;
6483   GtkTreeRBTree *tree;
6484   GtkTreeRBNode *node;
6485 
6486   if (priv->tree == NULL)
6487     {
6488       gtk_tree_view_set_top_row (tree_view, NULL, 0);
6489     }
6490   else
6491     {
6492       offset = gtk_tree_rbtree_find_offset (priv->tree,
6493 					priv->dy,
6494 					&tree, &node);
6495 
6496       if (tree == NULL)
6497         {
6498 	  gtk_tree_view_set_top_row (tree_view, NULL, 0);
6499 	}
6500       else
6501         {
6502 	  path = _gtk_tree_path_new_from_rbtree (tree, node);
6503 	  gtk_tree_view_set_top_row (tree_view, path, offset);
6504 	  gtk_tree_path_free (path);
6505 	}
6506     }
6507 }
6508 
6509 static void
gtk_tree_view_top_row_to_dy(GtkTreeView * tree_view)6510 gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view)
6511 {
6512   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6513   GtkTreePath *path;
6514   GtkTreeRBTree *tree;
6515   GtkTreeRBNode *node;
6516   int new_dy;
6517 
6518   /* Avoid recursive calls */
6519   if (priv->in_top_row_to_dy)
6520     return;
6521 
6522   if (priv->top_row)
6523     path = gtk_tree_row_reference_get_path (priv->top_row);
6524   else
6525     path = NULL;
6526 
6527   if (!path)
6528     tree = NULL;
6529   else
6530     _gtk_tree_view_find_node (tree_view, path, &tree, &node);
6531 
6532   if (path)
6533     gtk_tree_path_free (path);
6534 
6535   if (tree == NULL)
6536     {
6537       /* keep dy and set new toprow */
6538       gtk_tree_row_reference_free (priv->top_row);
6539       priv->top_row = NULL;
6540       priv->top_row_dy = 0;
6541       /* DO NOT install the idle handler */
6542       gtk_tree_view_dy_to_top_row (tree_view);
6543       return;
6544     }
6545 
6546   if (gtk_tree_view_get_row_height (tree_view, node)
6547       < priv->top_row_dy)
6548     {
6549       /* new top row -- do NOT install the idle handler */
6550       gtk_tree_view_dy_to_top_row (tree_view);
6551       return;
6552     }
6553 
6554   new_dy = gtk_tree_rbtree_node_find_offset (tree, node);
6555   new_dy += priv->top_row_dy;
6556 
6557   if (new_dy + gtk_adjustment_get_page_size (priv->vadjustment) > gtk_tree_view_get_height (tree_view))
6558     new_dy = gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (priv->vadjustment);
6559 
6560   new_dy = MAX (0, new_dy);
6561 
6562   priv->in_top_row_to_dy = TRUE;
6563   gtk_adjustment_set_value (priv->vadjustment, (double)new_dy);
6564   priv->in_top_row_to_dy = FALSE;
6565 }
6566 
6567 
6568 void
_gtk_tree_view_install_mark_rows_col_dirty(GtkTreeView * tree_view,gboolean install_handler)6569 _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view,
6570 					    gboolean     install_handler)
6571 {
6572   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6573 
6574   priv->mark_rows_col_dirty = TRUE;
6575 
6576   if (install_handler)
6577     install_presize_handler (tree_view);
6578 }
6579 
6580 /*
6581  * This function works synchronously (due to the while (validate_rows...)
6582  * loop).
6583  *
6584  * There was a check for column_type != GTK_TREE_VIEW_COLUMN_AUTOSIZE
6585  * here. You now need to check that yourself.
6586  */
6587 void
_gtk_tree_view_column_autosize(GtkTreeView * tree_view,GtkTreeViewColumn * column)6588 _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
6589 			        GtkTreeViewColumn *column)
6590 {
6591   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
6592   g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
6593 
6594   _gtk_tree_view_column_cell_set_dirty (column, FALSE);
6595 
6596   do_presize_handler (tree_view);
6597   while (validate_rows (tree_view));
6598 
6599   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6600 }
6601 
6602 /* Drag-and-drop */
6603 
6604 typedef struct
6605 {
6606   GtkTreeRowReference *dest_row;
6607   guint                path_down_mode   : 1;
6608   guint                empty_view_drop  : 1;
6609   guint                drop_append_mode : 1;
6610 }
6611 DestRow;
6612 
6613 static void
dest_row_free(gpointer data)6614 dest_row_free (gpointer data)
6615 {
6616   DestRow *dr = (DestRow *)data;
6617 
6618   gtk_tree_row_reference_free (dr->dest_row);
6619   g_slice_free (DestRow, dr);
6620 }
6621 
6622 static void
set_dest_row(GdkDrop * drop,GtkTreeModel * model,GtkTreePath * dest_row,gboolean path_down_mode,gboolean empty_view_drop,gboolean drop_append_mode)6623 set_dest_row (GdkDrop      *drop,
6624               GtkTreeModel *model,
6625               GtkTreePath  *dest_row,
6626               gboolean      path_down_mode,
6627               gboolean      empty_view_drop,
6628               gboolean      drop_append_mode)
6629 {
6630   DestRow *dr;
6631 
6632   if (!dest_row)
6633     {
6634       g_object_set_data_full (G_OBJECT (drop), I_("gtk-tree-view-dest-row"),
6635                               NULL, NULL);
6636       return;
6637     }
6638 
6639   dr = g_slice_new (DestRow);
6640 
6641   dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
6642   dr->path_down_mode = path_down_mode != FALSE;
6643   dr->empty_view_drop = empty_view_drop != FALSE;
6644   dr->drop_append_mode = drop_append_mode != FALSE;
6645 
6646   g_object_set_data_full (G_OBJECT (drop), I_("gtk-tree-view-dest-row"),
6647                           dr, (GDestroyNotify) dest_row_free);
6648 }
6649 
6650 static GtkTreePath*
get_dest_row(GdkDrop * drop,gboolean * path_down_mode)6651 get_dest_row (GdkDrop  *drop,
6652               gboolean *path_down_mode)
6653 {
6654   DestRow *dr =
6655     g_object_get_data (G_OBJECT (drop), "gtk-tree-view-dest-row");
6656 
6657   if (dr)
6658     {
6659       GtkTreePath *path = NULL;
6660 
6661       if (path_down_mode)
6662         *path_down_mode = dr->path_down_mode;
6663 
6664       if (dr->dest_row)
6665         path = gtk_tree_row_reference_get_path (dr->dest_row);
6666       else if (dr->empty_view_drop)
6667         path = gtk_tree_path_new_from_indices (0, -1);
6668       else
6669         path = NULL;
6670 
6671       if (path && dr->drop_append_mode)
6672         gtk_tree_path_next (path);
6673 
6674       return path;
6675     }
6676   else
6677     return NULL;
6678 }
6679 
6680 /* Get/set whether drag_motion requested the drag data and
6681  * drag_data_received should thus not actually insert the data,
6682  * since the data doesn’t result from a drop.
6683  */
6684 static void
set_status_pending(GdkDrop * drop,GdkDragAction suggested_action)6685 set_status_pending (GdkDrop       *drop,
6686                     GdkDragAction  suggested_action)
6687 {
6688   g_object_set_data (G_OBJECT (drop),
6689                      I_("gtk-tree-view-status-pending"),
6690                      GINT_TO_POINTER (suggested_action));
6691 }
6692 
6693 static GdkDragAction
get_status_pending(GdkDrop * drop)6694 get_status_pending (GdkDrop *drop)
6695 {
6696   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop),
6697                                              "gtk-tree-view-status-pending"));
6698 }
6699 
6700 static TreeViewDragInfo*
get_info(GtkTreeView * tree_view)6701 get_info (GtkTreeView *tree_view)
6702 {
6703   return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
6704 }
6705 
6706 static void
destroy_info(TreeViewDragInfo * di)6707 destroy_info (TreeViewDragInfo *di)
6708 {
6709   g_clear_pointer (&di->source_formats, gdk_content_formats_unref);
6710   g_clear_pointer (&di->source_item, gtk_tree_row_reference_free);
6711   g_clear_object (&di->dest);
6712 
6713   g_slice_free (TreeViewDragInfo, di);
6714 }
6715 
6716 static TreeViewDragInfo*
ensure_info(GtkTreeView * tree_view)6717 ensure_info (GtkTreeView *tree_view)
6718 {
6719   TreeViewDragInfo *di;
6720 
6721   di = get_info (tree_view);
6722 
6723   if (di == NULL)
6724     {
6725       di = g_slice_new0 (TreeViewDragInfo);
6726 
6727       g_object_set_data_full (G_OBJECT (tree_view),
6728                               I_("gtk-tree-view-drag-info"),
6729                               di,
6730                               (GDestroyNotify) destroy_info);
6731     }
6732 
6733   return di;
6734 }
6735 
6736 static void
remove_info(GtkTreeView * tree_view)6737 remove_info (GtkTreeView *tree_view)
6738 {
6739   TreeViewDragInfo *di;
6740 
6741   di = get_info (tree_view);
6742   if (di && di->dest)
6743     gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
6744   g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL);
6745 }
6746 
6747 static void
add_scroll_timeout(GtkTreeView * tree_view)6748 add_scroll_timeout (GtkTreeView *tree_view)
6749 {
6750   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6751 
6752   if (priv->scroll_timeout == 0)
6753     {
6754       priv->scroll_timeout = g_timeout_add (150, scroll_row_timeout, tree_view);
6755       gdk_source_set_static_name_by_id (priv->scroll_timeout, "[gtk] scroll_row_timeout");
6756     }
6757 }
6758 
6759 static void
remove_scroll_timeout(GtkTreeView * tree_view)6760 remove_scroll_timeout (GtkTreeView *tree_view)
6761 {
6762   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6763 
6764   g_clear_handle_id (&priv->scroll_timeout, g_source_remove);
6765 }
6766 
6767 static gboolean
check_model_dnd(GtkTreeModel * model,GType required_iface,const char * signal)6768 check_model_dnd (GtkTreeModel *model,
6769                  GType         required_iface,
6770                  const char   *signal)
6771 {
6772   if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
6773     {
6774       g_warning ("You must override the default '%s' handler "
6775                  "on GtkTreeView when using models that don't support "
6776                  "the %s interface and enabling drag-and-drop. The simplest way to do this "
6777                  "is to connect to '%s' and call "
6778                  "g_signal_stop_emission_by_name() in your signal handler to prevent "
6779                  "the default handler from running. Look at the source code "
6780                  "for the default handler in gtktreeview.c to get an idea what "
6781                  "your handler should do. (gtktreeview.c is in the GTK source "
6782                  "code.) If you're using GTK from a language other than C, "
6783                  "there may be a more natural way to override default handlers, e.g. via derivation.",
6784                  signal, g_type_name (required_iface), signal);
6785       return FALSE;
6786     }
6787   else
6788     return TRUE;
6789 }
6790 
6791 static void
remove_open_timeout(GtkTreeView * tree_view)6792 remove_open_timeout (GtkTreeView *tree_view)
6793 {
6794   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6795 
6796   g_clear_handle_id (&priv->open_dest_timeout, g_source_remove);
6797 }
6798 
6799 
6800 static int
open_row_timeout(gpointer data)6801 open_row_timeout (gpointer data)
6802 {
6803   GtkTreeView *tree_view = data;
6804   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6805   GtkTreePath *dest_path = NULL;
6806   GtkTreeViewDropPosition pos;
6807   gboolean result = FALSE;
6808 
6809   gtk_tree_view_get_drag_dest_row (tree_view,
6810                                    &dest_path,
6811                                    &pos);
6812 
6813   if (dest_path &&
6814       (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
6815        pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
6816     {
6817       gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
6818       priv->open_dest_timeout = 0;
6819 
6820       gtk_tree_path_free (dest_path);
6821     }
6822   else
6823     {
6824       if (dest_path)
6825         gtk_tree_path_free (dest_path);
6826 
6827       result = TRUE;
6828     }
6829 
6830   return result;
6831 }
6832 
6833 static gboolean
scroll_row_timeout(gpointer data)6834 scroll_row_timeout (gpointer data)
6835 {
6836   GtkTreeView *tree_view = data;
6837   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
6838 
6839   gtk_tree_view_vertical_autoscroll (tree_view);
6840 
6841   if (priv->rubber_band_status == RUBBER_BAND_ACTIVE)
6842     gtk_tree_view_update_rubber_band (tree_view);
6843 
6844   return TRUE;
6845 }
6846 
6847 /* Returns TRUE if event should not be propagated to parent widgets */
6848 static gboolean
set_destination_row(GtkTreeView * tree_view,GtkDropTargetAsync * dest,int x,int y,GdkDragAction * suggested_action,GType * target)6849 set_destination_row (GtkTreeView         *tree_view,
6850                      GtkDropTargetAsync  *dest,
6851                      /* coordinates relative to the widget */
6852                      int                  x,
6853                      int                  y,
6854                      GdkDragAction       *suggested_action,
6855                      GType               *target)
6856 {
6857   GtkTreePath *path = NULL;
6858   GtkTreeViewDropPosition pos;
6859   GtkTreeViewDropPosition old_pos;
6860   TreeViewDragInfo *di;
6861   GtkWidget *widget;
6862   GtkTreePath *old_dest_path = NULL;
6863   gboolean can_drop = FALSE;
6864   GdkContentFormats *formats;
6865 
6866   *suggested_action = 0;
6867   *target = G_TYPE_INVALID;
6868 
6869   widget = GTK_WIDGET (tree_view);
6870 
6871   di = get_info (tree_view);
6872 
6873   if (di == NULL || y - gtk_tree_view_get_effective_header_height (tree_view) < 0)
6874     {
6875       /* someone unset us as a drag dest, note that if
6876        * we return FALSE drag_leave isn't called
6877        */
6878 
6879       gtk_tree_view_set_drag_dest_row (tree_view,
6880                                        NULL,
6881                                        GTK_TREE_VIEW_DROP_BEFORE);
6882 
6883       remove_scroll_timeout (GTK_TREE_VIEW (widget));
6884       remove_open_timeout (GTK_TREE_VIEW (widget));
6885 
6886       return FALSE; /* no longer a drop site */
6887     }
6888 
6889   formats = gtk_drop_target_async_get_formats (dest);
6890   *target = gdk_content_formats_match_gtype (formats, formats);
6891   if (*target == G_TYPE_INVALID)
6892     return FALSE;
6893 
6894   if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
6895                                           x, y,
6896                                           &path,
6897                                           &pos))
6898     {
6899       int n_children;
6900       GtkTreeModel *model;
6901 
6902       remove_open_timeout (tree_view);
6903 
6904       /* the row got dropped on empty space, let's setup a special case
6905        */
6906 
6907       if (path)
6908 	gtk_tree_path_free (path);
6909 
6910       model = gtk_tree_view_get_model (tree_view);
6911 
6912       n_children = gtk_tree_model_iter_n_children (model, NULL);
6913       if (n_children)
6914         {
6915           pos = GTK_TREE_VIEW_DROP_AFTER;
6916           path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6917         }
6918       else
6919         {
6920           pos = GTK_TREE_VIEW_DROP_BEFORE;
6921           path = gtk_tree_path_new_from_indices (0, -1);
6922         }
6923 
6924       can_drop = TRUE;
6925 
6926       goto out;
6927     }
6928 
6929   g_assert (path);
6930 
6931   /* If we left the current row's "open" zone, unset the timeout for
6932    * opening the row
6933    */
6934   gtk_tree_view_get_drag_dest_row (tree_view,
6935                                    &old_dest_path,
6936                                    &old_pos);
6937 
6938   if (old_dest_path &&
6939       (gtk_tree_path_compare (path, old_dest_path) != 0 ||
6940        !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
6941          pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)))
6942     remove_open_timeout (tree_view);
6943 
6944   if (old_dest_path)
6945     gtk_tree_path_free (old_dest_path);
6946 
6947   if (TRUE /* FIXME if the location droppable predicate */)
6948     {
6949       can_drop = TRUE;
6950     }
6951 
6952 out:
6953   if (can_drop)
6954     {
6955       *suggested_action = GDK_ACTION_COPY | GDK_ACTION_MOVE;
6956 
6957       gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
6958                                        path, pos);
6959     }
6960   else
6961     {
6962       /* can't drop here */
6963       remove_open_timeout (tree_view);
6964 
6965       gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
6966                                        NULL,
6967                                        GTK_TREE_VIEW_DROP_BEFORE);
6968     }
6969 
6970   if (path)
6971     gtk_tree_path_free (path);
6972 
6973   return TRUE;
6974 }
6975 
6976 static GtkTreePath*
get_logical_dest_row(GtkTreeView * tree_view,gboolean * path_down_mode,gboolean * drop_append_mode)6977 get_logical_dest_row (GtkTreeView *tree_view,
6978                       gboolean    *path_down_mode,
6979                       gboolean    *drop_append_mode)
6980 {
6981   /* adjust path to point to the row the drop goes in front of */
6982   GtkTreePath *path = NULL;
6983   GtkTreeViewDropPosition pos;
6984 
6985   g_return_val_if_fail (path_down_mode != NULL, NULL);
6986   g_return_val_if_fail (drop_append_mode != NULL, NULL);
6987 
6988   *path_down_mode = FALSE;
6989   *drop_append_mode = 0;
6990 
6991   gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
6992 
6993   if (path == NULL)
6994     return NULL;
6995 
6996   if (pos == GTK_TREE_VIEW_DROP_BEFORE)
6997     ; /* do nothing */
6998   else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
6999            pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
7000     *path_down_mode = TRUE;
7001   else
7002     {
7003       GtkTreeIter iter;
7004       GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
7005 
7006       g_assert (pos == GTK_TREE_VIEW_DROP_AFTER);
7007 
7008       if (!gtk_tree_model_get_iter (model, &iter, path) ||
7009           !gtk_tree_model_iter_next (model, &iter))
7010         *drop_append_mode = 1;
7011       else
7012         {
7013           *drop_append_mode = 0;
7014           gtk_tree_path_next (path);
7015         }
7016     }
7017 
7018   return path;
7019 }
7020 
7021 static gboolean
gtk_tree_view_maybe_begin_dragging_row(GtkTreeView * tree_view)7022 gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
7023 {
7024   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7025   GtkWidget *widget = GTK_WIDGET (tree_view);
7026   double start_x, start_y, offset_x, offset_y;
7027   TreeViewDragInfo *di;
7028   GtkTreePath *path = NULL;
7029   int button;
7030   GtkTreeModel *model;
7031   gboolean retval = FALSE;
7032   int bin_x, bin_y;
7033   GdkSurface *surface;
7034   GdkDevice *device;
7035   GdkContentProvider *content;
7036   GdkDrag *drag;
7037   GdkPaintable *icon;
7038 
7039   di = get_info (tree_view);
7040 
7041   if (di == NULL || !di->source_set)
7042     goto out;
7043 
7044   if (!gtk_gesture_is_recognized (priv->drag_gesture))
7045     goto out;
7046 
7047   gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (priv->drag_gesture),
7048                                     &start_x, &start_y);
7049   gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (priv->drag_gesture),
7050                                &offset_x, &offset_y);
7051 
7052   if (!gtk_drag_check_threshold_double (widget, 0, 0, offset_x, offset_y))
7053     goto out;
7054 
7055   model = gtk_tree_view_get_model (tree_view);
7056 
7057   if (model == NULL)
7058     goto out;
7059 
7060   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (priv->drag_gesture));
7061 
7062   /* Deny the click gesture */
7063   gtk_gesture_set_state (GTK_GESTURE (priv->click_gesture),
7064                          GTK_EVENT_SEQUENCE_DENIED);
7065 
7066   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
7067 						      &bin_x, &bin_y);
7068   gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, &path,
7069                                  NULL, NULL, NULL);
7070 
7071   if (path == NULL)
7072     goto out;
7073 
7074   if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7075       !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7076 					   path))
7077     goto out;
7078 
7079   if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
7080     goto out;
7081 
7082   /* Now we can begin the drag */
7083   gtk_gesture_set_state (GTK_GESTURE (priv->drag_gesture),
7084                          GTK_EVENT_SEQUENCE_CLAIMED);
7085 
7086   surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (tree_view)));
7087   device = gtk_gesture_get_device (GTK_GESTURE (priv->drag_gesture)),
7088   content = gtk_tree_view_drag_data_get (tree_view, path);
7089   if (content == NULL)
7090     goto out;
7091 
7092   retval = TRUE;
7093 
7094   drag = gdk_drag_begin (surface, device, content, di->source_actions, start_x, start_y);
7095 
7096   g_object_unref (content);
7097 
7098   g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_tree_view_dnd_finished_cb), tree_view);
7099 
7100   icon = gtk_tree_view_create_row_drag_icon (tree_view, path);
7101   gtk_drag_icon_set_from_paintable (drag, icon, priv->press_start_x + 1, 1);
7102   g_object_unref (icon);
7103 
7104   di->drag = drag;
7105 
7106   g_object_unref (drag);
7107 
7108   di->source_item = gtk_tree_row_reference_new (model, path);
7109 
7110  out:
7111   if (path)
7112     gtk_tree_path_free (path);
7113 
7114   return retval;
7115 }
7116 
7117 static void
gtk_tree_view_dnd_finished_cb(GdkDrag * drag,GtkWidget * widget)7118 gtk_tree_view_dnd_finished_cb (GdkDrag   *drag,
7119                                GtkWidget *widget)
7120 {
7121   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
7122   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7123   TreeViewDragInfo *di;
7124   GtkTreeModel *model;
7125   GtkTreePath *source_row;
7126 
7127   priv->event_last_x = -10000;
7128   priv->event_last_y = -10000;
7129 
7130   if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE)
7131     return;
7132 
7133   tree_view = GTK_TREE_VIEW (widget);
7134   model = gtk_tree_view_get_model (tree_view);
7135 
7136   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
7137     return;
7138 
7139   di = get_info (tree_view);
7140 
7141   if (di == NULL || di->source_item == NULL)
7142     return;
7143 
7144   source_row = gtk_tree_row_reference_get_path (di->source_item);
7145 
7146   if (source_row == NULL)
7147     return;
7148 
7149   gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row);
7150 
7151   gtk_tree_path_free (source_row);
7152 
7153   g_clear_pointer (&di->source_item, gtk_tree_row_reference_free);
7154 }
7155 
7156 /* Default signal implementations for the drag signals */
7157 static GdkContentProvider *
gtk_tree_view_drag_data_get(GtkTreeView * tree_view,GtkTreePath * source_row)7158 gtk_tree_view_drag_data_get (GtkTreeView *tree_view,
7159                              GtkTreePath *source_row)
7160 {
7161   GtkTreeModel *model;
7162   GdkContentProvider *content;
7163 
7164   model = gtk_tree_view_get_model (tree_view);
7165 
7166   if (model == NULL)
7167     return NULL;
7168 
7169   /* We can implement the GTK_TREE_MODEL_ROW target generically for
7170    * any model; for DragSource models there are some other targets
7171    * we also support.
7172    */
7173 
7174   if (GTK_IS_TREE_DRAG_SOURCE (model))
7175     content = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row);
7176   else
7177     content = NULL;
7178 
7179   /* If drag_data_get does nothing, try providing row data. */
7180   if (!content)
7181     content = gtk_tree_create_row_drag_content (model, source_row);
7182 
7183   return content;
7184 }
7185 
7186 static void
gtk_tree_view_drag_leave(GtkDropTargetAsync * dest,GdkDrop * drop,GtkTreeView * tree_view)7187 gtk_tree_view_drag_leave (GtkDropTargetAsync *dest,
7188                           GdkDrop            *drop,
7189                           GtkTreeView        *tree_view)
7190 {
7191   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7192 
7193   /* unset any highlight row */
7194   gtk_tree_view_set_drag_dest_row (tree_view,
7195                                    NULL,
7196                                    GTK_TREE_VIEW_DROP_BEFORE);
7197 
7198   remove_scroll_timeout (tree_view);
7199   remove_open_timeout (tree_view);
7200 
7201   priv->event_last_x = -10000;
7202   priv->event_last_y = -10000;
7203 }
7204 
7205 
7206 static GdkDragAction
gtk_tree_view_drag_motion(GtkDropTargetAsync * dest,GdkDrop * drop,double x,double y,GtkTreeView * tree_view)7207 gtk_tree_view_drag_motion (GtkDropTargetAsync *dest,
7208                            GdkDrop            *drop,
7209                            double              x,
7210                            double              y,
7211                            GtkTreeView        *tree_view)
7212 {
7213   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7214   gboolean empty;
7215   GtkTreePath *path = NULL;
7216   GtkTreeViewDropPosition pos;
7217   GdkDragAction suggested_action = 0;
7218   GType target;
7219 
7220   if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target))
7221     return 0;
7222 
7223   priv->event_last_x = x;
7224   priv->event_last_y = y;
7225 
7226   gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
7227 
7228   /* we only know this *after* set_desination_row */
7229   empty = priv->empty_view_drop;
7230 
7231   if (path == NULL && !empty)
7232     {
7233       suggested_action = 0;
7234     }
7235   else
7236     {
7237       if (priv->open_dest_timeout == 0 &&
7238           (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7239            pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
7240         {
7241           priv->open_dest_timeout =
7242             g_timeout_add (AUTO_EXPAND_TIMEOUT, open_row_timeout, tree_view);
7243           gdk_source_set_static_name_by_id (priv->open_dest_timeout, "[gtk] open_row_timeout");
7244         }
7245       else
7246         {
7247 	  add_scroll_timeout (tree_view);
7248 	}
7249 
7250       if (target == GTK_TYPE_TREE_ROW_DATA)
7251         {
7252           /* Request data so we can use the source row when
7253            * determining whether to accept the drop
7254            */
7255           set_status_pending (drop, suggested_action);
7256           gdk_drop_read_value_async (drop, GTK_TYPE_TREE_ROW_DATA, G_PRIORITY_DEFAULT, NULL, gtk_tree_view_drag_data_received, tree_view);
7257         }
7258       else
7259         {
7260           set_status_pending (drop, 0);
7261         }
7262     }
7263 
7264   if (path)
7265     gtk_tree_path_free (path);
7266 
7267   return suggested_action;
7268 }
7269 
7270 
7271 static gboolean
gtk_tree_view_drag_drop(GtkDropTargetAsync * dest,GdkDrop * drop,double x,double y,GtkTreeView * tree_view)7272 gtk_tree_view_drag_drop (GtkDropTargetAsync *dest,
7273                          GdkDrop            *drop,
7274                          double              x,
7275                          double              y,
7276                          GtkTreeView        *tree_view)
7277 {
7278   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7279   GtkTreePath *path;
7280   GdkDragAction suggested_action = 0;
7281   GType target = G_TYPE_INVALID;
7282   TreeViewDragInfo *di;
7283   GtkTreeModel *model;
7284   gboolean path_down_mode;
7285   gboolean drop_append_mode;
7286 
7287   model = gtk_tree_view_get_model (tree_view);
7288 
7289   remove_scroll_timeout (tree_view);
7290   remove_open_timeout (tree_view);
7291 
7292   di = get_info (tree_view);
7293 
7294   if (di == NULL)
7295     return FALSE;
7296 
7297   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
7298     return FALSE;
7299 
7300   if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target))
7301     return FALSE;
7302 
7303   path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
7304 
7305   if (target != G_TYPE_INVALID && path != NULL)
7306     {
7307       /* in case a motion had requested drag data, change things so we
7308        * treat drag data receives as a drop.
7309        */
7310       set_status_pending (drop, 0);
7311       set_dest_row (drop, model, path,
7312                     path_down_mode, priv->empty_view_drop,
7313                     drop_append_mode);
7314     }
7315 
7316   if (path)
7317     gtk_tree_path_free (path);
7318 
7319   /* Unset this thing */
7320   gtk_tree_view_set_drag_dest_row (tree_view,
7321                                    NULL,
7322                                    GTK_TREE_VIEW_DROP_BEFORE);
7323 
7324   if (target != G_TYPE_INVALID)
7325     {
7326       gdk_drop_read_value_async (drop, GTK_TYPE_TREE_ROW_DATA, G_PRIORITY_DEFAULT, NULL, gtk_tree_view_drag_data_received, tree_view);
7327       return TRUE;
7328     }
7329   else
7330     return FALSE;
7331 }
7332 
7333 static GdkDragAction
gtk_tree_view_get_action(GtkWidget * widget,GdkDrop * drop)7334 gtk_tree_view_get_action (GtkWidget *widget,
7335                           GdkDrop   *drop)
7336 {
7337   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
7338   TreeViewDragInfo *di;
7339   GdkDrag *drag = gdk_drop_get_drag (drop);
7340   GdkDragAction actions;
7341 
7342   di = get_info (tree_view);
7343 
7344   actions = gdk_drop_get_actions (drop);
7345 
7346   if (di && di->drag == drag &&
7347       actions & GDK_ACTION_MOVE)
7348     return GDK_ACTION_MOVE;
7349 
7350   if (actions & GDK_ACTION_COPY)
7351     return GDK_ACTION_COPY;
7352 
7353   if (actions & GDK_ACTION_MOVE)
7354     return GDK_ACTION_MOVE;
7355 
7356   return 0;
7357 }
7358 
7359 static void
gtk_tree_view_drag_data_received(GObject * source,GAsyncResult * result,gpointer data)7360 gtk_tree_view_drag_data_received (GObject      *source,
7361                                   GAsyncResult *result,
7362                                   gpointer      data)
7363 {
7364   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
7365   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7366   GdkDrop *drop = GDK_DROP (source);
7367   GtkTreePath *path;
7368   TreeViewDragInfo *di;
7369   GtkTreeModel *model;
7370   GtkTreePath *dest_row;
7371   GdkDragAction suggested_action;
7372   gboolean path_down_mode;
7373   gboolean drop_append_mode;
7374   const GValue *value;
7375 
7376   value = gdk_drop_read_value_finish (drop, result, NULL);
7377   if (value == NULL)
7378     return;
7379 
7380   model = gtk_tree_view_get_model (tree_view);
7381 
7382   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
7383     return;
7384 
7385   di = get_info (tree_view);
7386 
7387   if (di == NULL)
7388     return;
7389 
7390   suggested_action = get_status_pending (drop);
7391 
7392   if (suggested_action)
7393     {
7394       /* We are getting this data due to a request in drag_motion,
7395        * rather than due to a request in drag_drop, so we are just
7396        * supposed to call drag_status, not actually paste in the
7397        * data.
7398        */
7399       path = get_logical_dest_row (tree_view, &path_down_mode,
7400                                    &drop_append_mode);
7401 
7402       if (path == NULL)
7403         suggested_action = 0;
7404       else if (path_down_mode)
7405         gtk_tree_path_down (path);
7406 
7407       if (suggested_action)
7408         {
7409 	  if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7410 						     path,
7411 						     value))
7412             {
7413               if (path_down_mode)
7414                 {
7415                   path_down_mode = FALSE;
7416                   gtk_tree_path_up (path);
7417 
7418                   if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7419                                                              path,
7420                                                              value))
7421                     suggested_action = 0;
7422                 }
7423               else
7424 	        suggested_action = 0;
7425             }
7426         }
7427 
7428       if (path)
7429         gtk_tree_path_free (path);
7430 
7431       /* If you can't drop, remove user drop indicator until the next motion */
7432       if (suggested_action == 0)
7433         gtk_tree_view_set_drag_dest_row (tree_view,
7434                                          NULL,
7435                                          GTK_TREE_VIEW_DROP_BEFORE);
7436 
7437       return;
7438     }
7439 
7440   dest_row = get_dest_row (drop, &path_down_mode);
7441 
7442   if (dest_row == NULL)
7443     return;
7444 
7445   if (path_down_mode)
7446     {
7447       gtk_tree_path_down (dest_row);
7448       if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7449                                                  dest_row, value))
7450         gtk_tree_path_up (dest_row);
7451     }
7452 
7453   suggested_action = gtk_tree_view_get_action (GTK_WIDGET (tree_view), drop);
7454 
7455   if (suggested_action &&
7456       !gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
7457                                               dest_row,
7458                                               value))
7459     suggested_action = 0;
7460 
7461   gdk_drop_finish (drop, suggested_action);
7462 
7463   if (gtk_tree_path_get_depth (dest_row) == 1 &&
7464       gtk_tree_path_get_indices (dest_row)[0] == 0 &&
7465       gtk_tree_model_iter_n_children (priv->model, NULL) != 0)
7466     {
7467       /* special case drag to "0", scroll to first item */
7468       if (!priv->scroll_to_path)
7469         gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
7470     }
7471 
7472   gtk_tree_path_free (dest_row);
7473 
7474   /* drop dest_row */
7475   set_dest_row (drop, NULL, NULL, FALSE, FALSE, FALSE);
7476 }
7477 
7478 static void
gtk_tree_view_remove(GtkTreeView * tree_view,GtkWidget * widget)7479 gtk_tree_view_remove (GtkTreeView  *tree_view,
7480                       GtkWidget    *widget)
7481 {
7482   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7483   GtkTreeViewChild *child = NULL;
7484   GList *tmp_list;
7485 
7486   tmp_list = priv->children;
7487   while (tmp_list)
7488     {
7489       child = tmp_list->data;
7490       if (child->widget == widget)
7491 	{
7492 	  gtk_widget_unparent (widget);
7493 
7494 	  priv->children = g_list_remove_link (priv->children, tmp_list);
7495 	  g_list_free_1 (tmp_list);
7496 	  g_slice_free (GtkTreeViewChild, child);
7497 	  return;
7498 	}
7499 
7500       tmp_list = tmp_list->next;
7501     }
7502 
7503   tmp_list = priv->columns;
7504 
7505   while (tmp_list)
7506     {
7507       GtkTreeViewColumn *column;
7508       GtkWidget         *button;
7509 
7510       column = tmp_list->data;
7511       button = gtk_tree_view_column_get_button (column);
7512 
7513       if (button == widget)
7514 	{
7515 	  gtk_widget_unparent (widget);
7516 	  return;
7517 	}
7518       tmp_list = tmp_list->next;
7519     }
7520 }
7521 
7522 /* Returns TRUE is any of the columns contains a cell that can-focus.
7523  * If this is not the case, a column-spanning focus rectangle will be
7524  * drawn.
7525  */
7526 static gboolean
gtk_tree_view_has_can_focus_cell(GtkTreeView * tree_view)7527 gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view)
7528 {
7529   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7530   GList *list;
7531 
7532   for (list = priv->columns; list; list = list->next)
7533     {
7534       GtkTreeViewColumn *column = list->data;
7535 
7536       if (!gtk_tree_view_column_get_visible (column))
7537 	continue;
7538       if (gtk_cell_area_is_activatable (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column))))
7539 	return TRUE;
7540     }
7541 
7542   return FALSE;
7543 }
7544 
7545 static void
column_sizing_notify(GObject * object,GParamSpec * pspec,gpointer data)7546 column_sizing_notify (GObject    *object,
7547                       GParamSpec *pspec,
7548                       gpointer    data)
7549 {
7550   GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (object);
7551 
7552   if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED)
7553     /* disable fixed height mode */
7554     g_object_set (data, "fixed-height-mode", FALSE, NULL);
7555 }
7556 
7557 /**
7558  * gtk_tree_view_set_fixed_height_mode:
7559  * @tree_view: a `GtkTreeView`
7560  * @enable: %TRUE to enable fixed height mode
7561  *
7562  * Enables or disables the fixed height mode of @tree_view.
7563  * Fixed height mode speeds up `GtkTreeView` by assuming that all
7564  * rows have the same height.
7565  * Only enable this option if all rows are the same height and all
7566  * columns are of type %GTK_TREE_VIEW_COLUMN_FIXED.
7567  **/
7568 void
gtk_tree_view_set_fixed_height_mode(GtkTreeView * tree_view,gboolean enable)7569 gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
7570                                      gboolean     enable)
7571 {
7572   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7573   GList *l;
7574 
7575   enable = enable != FALSE;
7576 
7577   if (enable == priv->fixed_height_mode)
7578     return;
7579 
7580   if (!enable)
7581     {
7582       priv->fixed_height_mode = 0;
7583       priv->fixed_height = -1;
7584     }
7585   else
7586     {
7587       /* make sure all columns are of type FIXED */
7588       for (l = priv->columns; l; l = l->next)
7589 	{
7590 	  g_return_if_fail (gtk_tree_view_column_get_sizing (l->data) == GTK_TREE_VIEW_COLUMN_FIXED);
7591 	}
7592 
7593       /* yes, we really have to do this is in a separate loop */
7594       for (l = priv->columns; l; l = l->next)
7595 	g_signal_connect (l->data, "notify::sizing",
7596 			  G_CALLBACK (column_sizing_notify), tree_view);
7597 
7598       priv->fixed_height_mode = 1;
7599       priv->fixed_height = -1;
7600     }
7601 
7602   /* force a revalidation */
7603   install_presize_handler (tree_view);
7604 
7605   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_FIXED_HEIGHT_MODE]);
7606 }
7607 
7608 /**
7609  * gtk_tree_view_get_fixed_height_mode:
7610  * @tree_view: a `GtkTreeView`
7611  *
7612  * Returns whether fixed height mode is turned on for @tree_view.
7613  *
7614  * Returns: %TRUE if @tree_view is in fixed height mode
7615  **/
7616 gboolean
gtk_tree_view_get_fixed_height_mode(GtkTreeView * tree_view)7617 gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view)
7618 {
7619   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7620 
7621   return priv->fixed_height_mode;
7622 }
7623 
7624 /* Returns TRUE if the focus is within the headers, after the focus operation is
7625  * done
7626  */
7627 static gboolean
gtk_tree_view_header_focus(GtkTreeView * tree_view,GtkDirectionType dir,gboolean clamp_column_visible)7628 gtk_tree_view_header_focus (GtkTreeView      *tree_view,
7629 			    GtkDirectionType  dir,
7630 			    gboolean          clamp_column_visible)
7631 {
7632   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7633   GtkTreeViewColumn *column;
7634   GtkWidget *button;
7635   GtkWidget *focus_child;
7636   GList *last_column, *first_column;
7637   GList *tmp_list;
7638   gboolean rtl;
7639 
7640   if (! priv->headers_visible)
7641     return FALSE;
7642 
7643   focus_child = gtk_widget_get_focus_child (GTK_WIDGET (tree_view));
7644 
7645   first_column = priv->columns;
7646   while (first_column)
7647     {
7648       column = GTK_TREE_VIEW_COLUMN (first_column->data);
7649       button = gtk_tree_view_column_get_button (column);
7650 
7651       if (gtk_widget_get_focusable (button) &&
7652           gtk_tree_view_column_get_visible (column) &&
7653           (gtk_tree_view_column_get_clickable (column) ||
7654            gtk_tree_view_column_get_reorderable (column)))
7655 	break;
7656       first_column = first_column->next;
7657     }
7658 
7659   /* No headers are visible, or are focusable.  We can't focus in or out.
7660    */
7661   if (first_column == NULL)
7662     return FALSE;
7663 
7664   last_column = g_list_last (priv->columns);
7665   while (last_column)
7666     {
7667       column = GTK_TREE_VIEW_COLUMN (last_column->data);
7668       button = gtk_tree_view_column_get_button (column);
7669 
7670       if (gtk_widget_get_focusable (button) &&
7671           gtk_tree_view_column_get_visible (column) &&
7672           (gtk_tree_view_column_get_clickable (column) ||
7673            gtk_tree_view_column_get_reorderable (column)))
7674 	break;
7675       last_column = last_column->prev;
7676     }
7677 
7678 
7679   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7680 
7681   switch (dir)
7682     {
7683     case GTK_DIR_TAB_BACKWARD:
7684     case GTK_DIR_TAB_FORWARD:
7685     case GTK_DIR_UP:
7686     case GTK_DIR_DOWN:
7687       if (focus_child == NULL)
7688 	{
7689 	  if (priv->focus_column != NULL)
7690 	    button = gtk_tree_view_column_get_button (priv->focus_column);
7691 	  else
7692 	    button = NULL;
7693 
7694 	  if (button && gtk_widget_get_focusable (button))
7695 	    focus_child = button;
7696 	  else
7697 	    focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
7698 
7699 	  gtk_widget_grab_focus (focus_child);
7700 	  break;
7701 	}
7702       return FALSE;
7703 
7704     case GTK_DIR_LEFT:
7705     case GTK_DIR_RIGHT:
7706       if (focus_child == NULL)
7707 	{
7708 	  if (priv->focus_column != NULL)
7709 	    focus_child = gtk_tree_view_column_get_button (priv->focus_column);
7710 	  else if (dir == GTK_DIR_LEFT)
7711 	    focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data));
7712 	  else
7713 	    focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
7714 
7715 	  gtk_widget_grab_focus (focus_child);
7716 	  break;
7717 	}
7718 
7719       if (gtk_widget_child_focus (focus_child, dir))
7720 	{
7721 	  /* The focus moves inside the button. */
7722 	  /* This is probably a great example of bad UI */
7723 	  break;
7724 	}
7725 
7726       /* We need to move the focus among the row of buttons. */
7727       for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next)
7728 	if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
7729 	  break;
7730 
7731       if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
7732 	  || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
7733         {
7734 	  gtk_widget_error_bell (GTK_WIDGET (tree_view));
7735 	  break;
7736 	}
7737 
7738       while (tmp_list)
7739 	{
7740 	  if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
7741 	    tmp_list = tmp_list->next;
7742 	  else
7743 	    tmp_list = tmp_list->prev;
7744 
7745 	  if (tmp_list == NULL)
7746 	    {
7747 	      g_warning ("Internal button not found");
7748 	      break;
7749 	    }
7750 	  column = tmp_list->data;
7751 	  button = gtk_tree_view_column_get_button (column);
7752 	  if (button &&
7753 	      gtk_tree_view_column_get_visible (column) &&
7754 	      gtk_widget_get_focusable (button))
7755 	    {
7756 	      focus_child = button;
7757 	      gtk_widget_grab_focus (button);
7758 	      break;
7759 	    }
7760 	}
7761       break;
7762     default:
7763       g_assert_not_reached ();
7764       break;
7765     }
7766 
7767   /* if focus child is non-null, we assume it's been set to the current focus child
7768    */
7769   if (focus_child)
7770     {
7771       for (tmp_list = priv->columns; tmp_list; tmp_list = tmp_list->next)
7772 	if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
7773 	  {
7774             _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (tmp_list->data));
7775 	    break;
7776 	  }
7777 
7778       if (clamp_column_visible)
7779         {
7780 	  gtk_tree_view_clamp_column_visible (tree_view,
7781 					      priv->focus_column,
7782 					      FALSE);
7783 	}
7784     }
7785 
7786   return (focus_child != NULL);
7787 }
7788 
7789 /* This function returns in 'path' the first focusable path, if the given path
7790  * is already focusable, it’s the returned one.
7791  */
7792 static gboolean
search_first_focusable_path(GtkTreeView * tree_view,GtkTreePath ** path,gboolean search_forward,GtkTreeRBTree ** new_tree,GtkTreeRBNode ** new_node)7793 search_first_focusable_path (GtkTreeView    *tree_view,
7794 			     GtkTreePath   **path,
7795 			     gboolean        search_forward,
7796 			     GtkTreeRBTree **new_tree,
7797 			     GtkTreeRBNode **new_node)
7798 {
7799   GtkTreeRBTree *tree = NULL;
7800   GtkTreeRBNode *node = NULL;
7801 
7802   if (!path || !*path)
7803     return FALSE;
7804 
7805   _gtk_tree_view_find_node (tree_view, *path, &tree, &node);
7806 
7807   if (!tree || !node)
7808     return FALSE;
7809 
7810   while (node && row_is_separator (tree_view, NULL, *path))
7811     {
7812       if (search_forward)
7813 	gtk_tree_rbtree_next_full (tree, node, &tree, &node);
7814       else
7815 	gtk_tree_rbtree_prev_full (tree, node, &tree, &node);
7816 
7817       if (*path)
7818 	gtk_tree_path_free (*path);
7819 
7820       if (node)
7821 	*path = _gtk_tree_path_new_from_rbtree (tree, node);
7822       else
7823 	*path = NULL;
7824     }
7825 
7826   if (new_tree)
7827     *new_tree = tree;
7828 
7829   if (new_node)
7830     *new_node = node;
7831 
7832   return (*path != NULL);
7833 }
7834 
7835 static int
gtk_tree_view_focus(GtkWidget * widget,GtkDirectionType direction)7836 gtk_tree_view_focus (GtkWidget        *widget,
7837 		     GtkDirectionType  direction)
7838 {
7839   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
7840   GtkWidget *focus_child;
7841 
7842   focus_child = gtk_widget_get_focus_child (widget);
7843 
7844   gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE);
7845   /* Case 1.  Headers currently have focus. */
7846   if (focus_child)
7847     {
7848       switch (direction)
7849 	{
7850 	case GTK_DIR_LEFT:
7851 	case GTK_DIR_RIGHT:
7852 	  gtk_tree_view_header_focus (tree_view, direction, TRUE);
7853 	  return TRUE;
7854 	case GTK_DIR_TAB_BACKWARD:
7855 	case GTK_DIR_UP:
7856 	  return FALSE;
7857 	case GTK_DIR_TAB_FORWARD:
7858 	case GTK_DIR_DOWN:
7859 	  return gtk_widget_grab_focus (widget);
7860 	default:
7861 	  g_assert_not_reached ();
7862 	  return FALSE;
7863 	}
7864     }
7865 
7866   /* Case 2. We don't have focus at all. */
7867   if (!gtk_widget_has_focus (widget))
7868     {
7869       return gtk_widget_grab_focus (widget);
7870     }
7871 
7872   /* Case 3. We have focus already. */
7873   if (direction == GTK_DIR_TAB_BACKWARD)
7874     return (gtk_tree_view_header_focus (tree_view, direction, FALSE));
7875   else if (direction == GTK_DIR_TAB_FORWARD)
7876     return FALSE;
7877 
7878   /* Other directions caught by the keybindings */
7879   return gtk_widget_grab_focus (widget);
7880 }
7881 
7882 static gboolean
gtk_tree_view_grab_focus(GtkWidget * widget)7883 gtk_tree_view_grab_focus (GtkWidget *widget)
7884 {
7885   if (!gtk_widget_grab_focus_self (widget))
7886     return FALSE;
7887 
7888   gtk_tree_view_focus_to_cursor (GTK_TREE_VIEW (widget));
7889   return TRUE;
7890 }
7891 
7892 static void
gtk_tree_view_css_changed(GtkWidget * widget,GtkCssStyleChange * change)7893 gtk_tree_view_css_changed (GtkWidget         *widget,
7894                            GtkCssStyleChange *change)
7895 {
7896   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
7897   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7898   GList *list;
7899   GtkTreeViewColumn *column;
7900 
7901   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->css_changed (widget, change);
7902 
7903   if (gtk_widget_get_realized (widget))
7904     {
7905       gtk_tree_view_set_grid_lines (tree_view, priv->grid_lines);
7906       gtk_tree_view_set_enable_tree_lines (tree_view, priv->tree_lines_enabled);
7907     }
7908 
7909   if (change == NULL || gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE))
7910     {
7911       for (list = priv->columns; list; list = list->next)
7912 	{
7913 	  column = list->data;
7914 	  _gtk_tree_view_column_cell_set_dirty (column, TRUE);
7915 	}
7916 
7917       priv->fixed_height = -1;
7918       gtk_tree_rbtree_mark_invalid (priv->tree);
7919     }
7920 
7921   /* Invalidate expander size */
7922   priv->expander_size = -1;
7923 }
7924 
7925 static gboolean
gtk_tree_view_real_move_cursor(GtkTreeView * tree_view,GtkMovementStep step,int count,gboolean extend,gboolean modify)7926 gtk_tree_view_real_move_cursor (GtkTreeView       *tree_view,
7927 				GtkMovementStep    step,
7928 				int                count,
7929                                 gboolean           extend,
7930                                 gboolean           modify)
7931 {
7932   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7933 
7934   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
7935   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7936 			step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7937 			step == GTK_MOVEMENT_DISPLAY_LINES ||
7938 			step == GTK_MOVEMENT_PAGES ||
7939 			step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
7940 
7941   if (priv->tree == NULL)
7942     return FALSE;
7943   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7944     return FALSE;
7945 
7946   gtk_tree_view_stop_editing (tree_view, FALSE);
7947   priv->draw_keyfocus = TRUE;
7948   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7949 
7950   priv->modify_selection_pressed = modify;
7951   priv->extend_selection_pressed = extend;
7952 
7953   switch (step)
7954     {
7955       /* currently we make no distinction.  When we go bi-di, we need to */
7956     case GTK_MOVEMENT_LOGICAL_POSITIONS:
7957     case GTK_MOVEMENT_VISUAL_POSITIONS:
7958       gtk_tree_view_move_cursor_left_right (tree_view, count);
7959       break;
7960     case GTK_MOVEMENT_DISPLAY_LINES:
7961       gtk_tree_view_move_cursor_up_down (tree_view, count);
7962       break;
7963     case GTK_MOVEMENT_PAGES:
7964       gtk_tree_view_move_cursor_page_up_down (tree_view, count);
7965       break;
7966     case GTK_MOVEMENT_BUFFER_ENDS:
7967       gtk_tree_view_move_cursor_start_end (tree_view, count);
7968       break;
7969     case GTK_MOVEMENT_WORDS:
7970     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7971     case GTK_MOVEMENT_PARAGRAPHS:
7972     case GTK_MOVEMENT_PARAGRAPH_ENDS:
7973     case GTK_MOVEMENT_HORIZONTAL_PAGES:
7974     default:
7975       g_assert_not_reached ();
7976     }
7977 
7978   priv->modify_selection_pressed = FALSE;
7979   priv->extend_selection_pressed = FALSE;
7980 
7981   return TRUE;
7982 }
7983 
7984 static void
gtk_tree_view_put(GtkTreeView * tree_view,GtkWidget * child_widget,GtkTreePath * path,GtkTreeViewColumn * column,const GtkBorder * border)7985 gtk_tree_view_put (GtkTreeView       *tree_view,
7986 		   GtkWidget         *child_widget,
7987                    GtkTreePath       *path,
7988                    GtkTreeViewColumn *column,
7989                    const GtkBorder   *border)
7990 {
7991   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
7992   GtkTreeViewChild *child;
7993 
7994   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
7995   g_return_if_fail (GTK_IS_WIDGET (child_widget));
7996 
7997   child = g_slice_new (GtkTreeViewChild);
7998 
7999   child->widget = child_widget;
8000   if (_gtk_tree_view_find_node (tree_view,
8001 				path,
8002 				&child->tree,
8003 				&child->node))
8004     {
8005       g_assert_not_reached ();
8006     }
8007   child->column = column;
8008   child->border = *border;
8009 
8010   priv->children = g_list_append (priv->children, child);
8011 
8012   gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (tree_view)),
8013                              gtk_widget_get_css_node (child_widget),
8014                              priv->header_node);
8015   gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
8016 }
8017 
8018 /* TreeModel Callbacks
8019  */
8020 
8021 static void
gtk_tree_view_row_changed(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8022 gtk_tree_view_row_changed (GtkTreeModel *model,
8023 			   GtkTreePath  *path,
8024 			   GtkTreeIter  *iter,
8025 			   gpointer      data)
8026 {
8027   GtkTreeView *tree_view = (GtkTreeView *)data;
8028   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8029   GtkTreeRBTree *tree;
8030   GtkTreeRBNode *node;
8031   gboolean free_path = FALSE;
8032   GList *list;
8033   GtkTreePath *cursor_path;
8034 
8035   g_return_if_fail (path != NULL || iter != NULL);
8036 
8037   if (priv->cursor_node != NULL)
8038     cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
8039                                                   priv->cursor_node);
8040   else
8041     cursor_path = NULL;
8042 
8043   if (priv->edited_column &&
8044       (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
8045     gtk_tree_view_stop_editing (tree_view, TRUE);
8046 
8047   if (cursor_path != NULL)
8048     gtk_tree_path_free (cursor_path);
8049 
8050   if (path == NULL)
8051     {
8052       path = gtk_tree_model_get_path (model, iter);
8053       free_path = TRUE;
8054     }
8055   else if (iter == NULL)
8056     gtk_tree_model_get_iter (model, iter, path);
8057 
8058   if (_gtk_tree_view_find_node (tree_view,
8059 				path,
8060 				&tree,
8061 				&node))
8062     /* We aren't actually showing the node */
8063     goto done;
8064 
8065   if (tree == NULL)
8066     goto done;
8067 
8068   if (priv->fixed_height_mode
8069       && priv->fixed_height >= 0)
8070     {
8071       gtk_tree_rbtree_node_set_height (tree, node, priv->fixed_height);
8072       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
8073     }
8074   else
8075     {
8076       gtk_tree_rbtree_node_mark_invalid (tree, node);
8077       for (list = priv->columns; list; list = list->next)
8078         {
8079           GtkTreeViewColumn *column;
8080 
8081           column = list->data;
8082           if (!gtk_tree_view_column_get_visible (column))
8083             continue;
8084 
8085           if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
8086             {
8087               _gtk_tree_view_column_cell_set_dirty (column, TRUE);
8088             }
8089         }
8090     }
8091 
8092  done:
8093   if (!priv->fixed_height_mode &&
8094       gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8095     install_presize_handler (tree_view);
8096   if (free_path)
8097     gtk_tree_path_free (path);
8098 }
8099 
8100 static void
gtk_tree_view_row_inserted(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8101 gtk_tree_view_row_inserted (GtkTreeModel *model,
8102 			    GtkTreePath  *path,
8103 			    GtkTreeIter  *iter,
8104 			    gpointer      data)
8105 {
8106   GtkTreeView *tree_view = (GtkTreeView *) data;
8107   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8108   int *indices;
8109   GtkTreeRBTree *tree;
8110   GtkTreeRBNode *tmpnode = NULL;
8111   int depth;
8112   int i = 0;
8113   int height;
8114   gboolean free_path = FALSE;
8115 
8116   g_return_if_fail (path != NULL || iter != NULL);
8117 
8118   if (priv->fixed_height_mode
8119       && priv->fixed_height >= 0)
8120     height = priv->fixed_height;
8121   else
8122     height = 0;
8123 
8124   if (path == NULL)
8125     {
8126       path = gtk_tree_model_get_path (model, iter);
8127       free_path = TRUE;
8128     }
8129   else if (iter == NULL)
8130     gtk_tree_model_get_iter (model, iter, path);
8131 
8132   if (priv->tree == NULL)
8133     priv->tree = gtk_tree_rbtree_new ();
8134 
8135   tree = priv->tree;
8136 
8137   /* Update all row-references */
8138   gtk_tree_row_reference_inserted (G_OBJECT (data), path);
8139   depth = gtk_tree_path_get_depth (path);
8140   indices = gtk_tree_path_get_indices (path);
8141 
8142   /* First, find the parent tree */
8143   while (i < depth - 1)
8144     {
8145       if (tree == NULL)
8146 	{
8147 	  /* We aren't showing the node */
8148           goto done;
8149 	}
8150 
8151       tmpnode = gtk_tree_rbtree_find_count (tree, indices[i] + 1);
8152       if (tmpnode == NULL)
8153 	{
8154 	  g_warning ("A node was inserted with a parent that's not in the tree.\n" \
8155 		     "This possibly means that a GtkTreeModel inserted a child node\n" \
8156 		     "before the parent was inserted.");
8157           goto done;
8158 	}
8159       else if (!GTK_TREE_RBNODE_FLAG_SET (tmpnode, GTK_TREE_RBNODE_IS_PARENT))
8160 	{
8161           /* FIXME enforce correct behavior on model, probably */
8162 	  /* In theory, the model should have emitted has_child_toggled here.  We
8163 	   * try to catch it anyway, just to be safe, in case the model hasn't.
8164 	   */
8165 	  GtkTreePath *tmppath = _gtk_tree_path_new_from_rbtree (tree, tmpnode);
8166 	  gtk_tree_view_row_has_child_toggled (model, tmppath, NULL, data);
8167 	  gtk_tree_path_free (tmppath);
8168           goto done;
8169 	}
8170 
8171       tree = tmpnode->children;
8172       i++;
8173     }
8174 
8175   if (tree == NULL)
8176     {
8177       goto done;
8178     }
8179 
8180   /* ref the node */
8181   gtk_tree_model_ref_node (priv->model, iter);
8182   if (indices[depth - 1] == 0)
8183     {
8184       tmpnode = gtk_tree_rbtree_find_count (tree, 1);
8185       tmpnode = gtk_tree_rbtree_insert_before (tree, tmpnode, height, FALSE);
8186     }
8187   else
8188     {
8189       tmpnode = gtk_tree_rbtree_find_count (tree, indices[depth - 1]);
8190       tmpnode = gtk_tree_rbtree_insert_after (tree, tmpnode, height, FALSE);
8191     }
8192 
8193  done:
8194   if (height > 0)
8195     {
8196       if (tree)
8197         gtk_tree_rbtree_node_mark_valid (tree, tmpnode);
8198 
8199       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8200     }
8201   else
8202     install_presize_handler (tree_view);
8203   if (free_path)
8204     gtk_tree_path_free (path);
8205 }
8206 
8207 static void
gtk_tree_view_row_has_child_toggled(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8208 gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
8209 				     GtkTreePath  *path,
8210 				     GtkTreeIter  *iter,
8211 				     gpointer      data)
8212 {
8213   GtkTreeView *tree_view = (GtkTreeView *)data;
8214   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8215   GtkTreeIter real_iter;
8216   gboolean has_child;
8217   GtkTreeRBTree *tree;
8218   GtkTreeRBNode *node;
8219   gboolean free_path = FALSE;
8220 
8221   g_return_if_fail (path != NULL || iter != NULL);
8222 
8223   if (iter)
8224     real_iter = *iter;
8225 
8226   if (path == NULL)
8227     {
8228       path = gtk_tree_model_get_path (model, iter);
8229       free_path = TRUE;
8230     }
8231   else if (iter == NULL)
8232     gtk_tree_model_get_iter (model, &real_iter, path);
8233 
8234   if (_gtk_tree_view_find_node (tree_view,
8235 				path,
8236 				&tree,
8237 				&node))
8238     /* We aren't actually showing the node */
8239     goto done;
8240 
8241   if (tree == NULL)
8242     goto done;
8243 
8244   has_child = gtk_tree_model_iter_has_child (model, &real_iter);
8245   /* Sanity check.
8246    */
8247   if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT) == has_child)
8248     goto done;
8249 
8250   if (has_child)
8251     {
8252       GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_PARENT);
8253     }
8254   else
8255     {
8256       GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_PARENT);
8257     }
8258 
8259   if (has_child && priv->is_list)
8260     {
8261       priv->is_list = FALSE;
8262       if (priv->show_expanders)
8263 	{
8264 	  GList *list;
8265 
8266 	  for (list = priv->columns; list; list = list->next)
8267 	    if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
8268 	      {
8269 		_gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE);
8270 		break;
8271 	      }
8272 	}
8273       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8274     }
8275   else
8276     {
8277       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
8278     }
8279 
8280  done:
8281   if (free_path)
8282     gtk_tree_path_free (path);
8283 }
8284 
8285 static void
check_selection_helper(GtkTreeRBTree * tree,GtkTreeRBNode * node,gpointer data)8286 check_selection_helper (GtkTreeRBTree *tree,
8287                         GtkTreeRBNode *node,
8288                         gpointer       data)
8289 {
8290   int *value = (int *)data;
8291 
8292   *value |= GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED);
8293 
8294   if (node->children && !*value)
8295     gtk_tree_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data);
8296 }
8297 
8298 static void
gtk_tree_view_row_deleted(GtkTreeModel * model,GtkTreePath * path,gpointer data)8299 gtk_tree_view_row_deleted (GtkTreeModel *model,
8300 			   GtkTreePath  *path,
8301 			   gpointer      data)
8302 {
8303   GtkTreeView *tree_view = (GtkTreeView *)data;
8304   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8305   GtkTreeRBTree *tree;
8306   GtkTreeRBNode *node;
8307   GList *list;
8308   gboolean selection_changed = FALSE, cursor_changed = FALSE;
8309   GtkTreeRBTree *cursor_tree = NULL;
8310   GtkTreeRBNode *cursor_node = NULL;
8311 
8312   g_return_if_fail (path != NULL);
8313 
8314   gtk_tree_row_reference_deleted (G_OBJECT (data), path);
8315 
8316   if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
8317     return;
8318 
8319   if (tree == NULL)
8320     return;
8321 
8322   /* check if the selection has been changed */
8323   gtk_tree_rbtree_traverse (tree, node, G_POST_ORDER,
8324                         check_selection_helper, &selection_changed);
8325 
8326   for (list = priv->columns; list; list = list->next)
8327     if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)) &&
8328 	gtk_tree_view_column_get_sizing (GTK_TREE_VIEW_COLUMN (list->data)) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
8329       _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE);
8330 
8331   /* Ensure we don't have a dangling pointer to a dead node */
8332   ensure_unprelighted (tree_view);
8333 
8334   /* Cancel editing if we've started */
8335   gtk_tree_view_stop_editing (tree_view, TRUE);
8336 
8337   /* If the cursor row got deleted, move the cursor to the next row */
8338   if (priv->cursor_node &&
8339       (priv->cursor_node == node ||
8340        (node->children && (priv->cursor_tree == node->children ||
8341                            gtk_tree_rbtree_contains (node->children, priv->cursor_tree)))))
8342     {
8343       GtkTreePath *cursor_path;
8344 
8345       cursor_tree = tree;
8346       cursor_node = gtk_tree_rbtree_next (tree, node);
8347       /* find the first node that is not going to be deleted */
8348       while (cursor_node == NULL && cursor_tree->parent_tree)
8349         {
8350           cursor_node = gtk_tree_rbtree_next (cursor_tree->parent_tree,
8351                                           cursor_tree->parent_node);
8352           cursor_tree = cursor_tree->parent_tree;
8353         }
8354 
8355       if (cursor_node != NULL)
8356         cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
8357       else
8358         cursor_path = NULL;
8359 
8360       if (cursor_path == NULL ||
8361           ! search_first_focusable_path (tree_view, &cursor_path, TRUE,
8362                                          &cursor_tree, &cursor_node))
8363         {
8364           /* It looks like we reached the end of the view without finding
8365            * a focusable row.  We will step backwards to find the last
8366            * focusable row.
8367            */
8368           gtk_tree_rbtree_prev_full (tree, node, &cursor_tree, &cursor_node);
8369           if (cursor_node)
8370             {
8371               cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
8372               if (! search_first_focusable_path (tree_view, &cursor_path, FALSE,
8373                                                  &cursor_tree, &cursor_node))
8374                 cursor_node = NULL;
8375               gtk_tree_path_free (cursor_path);
8376             }
8377         }
8378       else if (cursor_path)
8379         gtk_tree_path_free (cursor_path);
8380 
8381       cursor_changed = TRUE;
8382     }
8383 
8384   if (tree->root->count == 1)
8385     {
8386       if (priv->tree == tree)
8387 	priv->tree = NULL;
8388 
8389       gtk_tree_rbtree_remove (tree);
8390     }
8391   else
8392     {
8393       gtk_tree_rbtree_remove_node (tree, node);
8394     }
8395 
8396   if (! gtk_tree_row_reference_valid (priv->top_row))
8397     {
8398       gtk_tree_row_reference_free (priv->top_row);
8399       priv->top_row = NULL;
8400     }
8401 
8402   install_scroll_sync_handler (tree_view);
8403 
8404   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8405 
8406   if (cursor_changed)
8407     {
8408       if (cursor_node)
8409         {
8410           GtkTreePath *cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
8411           gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CURSOR_INVALID);
8412           gtk_tree_path_free (cursor_path);
8413         }
8414       else
8415         gtk_tree_view_real_set_cursor (tree_view, NULL, CLEAR_AND_SELECT | CURSOR_INVALID);
8416     }
8417   if (selection_changed)
8418     g_signal_emit_by_name (priv->selection, "changed");
8419 }
8420 
8421 static void
gtk_tree_view_rows_reordered(GtkTreeModel * model,GtkTreePath * parent,GtkTreeIter * iter,int * new_order,gpointer data)8422 gtk_tree_view_rows_reordered (GtkTreeModel *model,
8423 			      GtkTreePath  *parent,
8424 			      GtkTreeIter  *iter,
8425 			      int          *new_order,
8426 			      gpointer      data)
8427 {
8428   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
8429   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8430   GtkTreeRBTree *tree;
8431   GtkTreeRBNode *node;
8432   int len;
8433 
8434   len = gtk_tree_model_iter_n_children (model, iter);
8435 
8436   if (len < 2)
8437     return;
8438 
8439   gtk_tree_row_reference_reordered (G_OBJECT (data),
8440 				    parent,
8441 				    iter,
8442 				    new_order);
8443 
8444   if (_gtk_tree_view_find_node (tree_view,
8445 				parent,
8446 				&tree,
8447 				&node))
8448     return;
8449 
8450   /* We need to special case the parent path */
8451   if (tree == NULL)
8452     tree = priv->tree;
8453   else
8454     tree = node->children;
8455 
8456   if (tree == NULL)
8457     return;
8458 
8459   if (priv->edited_column)
8460     gtk_tree_view_stop_editing (tree_view, TRUE);
8461 
8462   /* we need to be unprelighted */
8463   ensure_unprelighted (tree_view);
8464 
8465   gtk_tree_rbtree_reorder (tree, new_order, len);
8466 
8467   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
8468 
8469   gtk_tree_view_dy_to_top_row (tree_view);
8470 }
8471 
8472 
8473 /* Internal tree functions
8474  */
8475 
8476 
8477 static void
gtk_tree_view_get_background_xrange(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeViewColumn * column,int * x1,int * x2)8478 gtk_tree_view_get_background_xrange (GtkTreeView       *tree_view,
8479                                      GtkTreeRBTree     *tree,
8480                                      GtkTreeViewColumn *column,
8481                                      int               *x1,
8482                                      int               *x2)
8483 {
8484   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8485   GtkTreeViewColumn *tmp_column = NULL;
8486   int total_width;
8487   GList *list;
8488   gboolean rtl;
8489 
8490   if (x1)
8491     *x1 = 0;
8492 
8493   if (x2)
8494     *x2 = 0;
8495 
8496   rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8497 
8498   total_width = 0;
8499   for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
8500        list;
8501        list = (rtl ? list->prev : list->next))
8502     {
8503       tmp_column = list->data;
8504 
8505       if (tmp_column == column)
8506         break;
8507 
8508       if (gtk_tree_view_column_get_visible (tmp_column))
8509         total_width += gtk_tree_view_column_get_width (tmp_column);
8510     }
8511 
8512   if (tmp_column != column)
8513     {
8514       g_warning (G_STRLOC": passed-in column isn't in the tree");
8515       return;
8516     }
8517 
8518   if (x1)
8519     *x1 = total_width;
8520 
8521   if (x2)
8522     {
8523       if (gtk_tree_view_column_get_visible (column))
8524         *x2 = total_width + gtk_tree_view_column_get_width (column);
8525       else
8526         *x2 = total_width; /* width of 0 */
8527     }
8528 }
8529 
8530 static void
gtk_tree_view_get_arrow_xrange(GtkTreeView * tree_view,GtkTreeRBTree * tree,int * x1,int * x2)8531 gtk_tree_view_get_arrow_xrange (GtkTreeView   *tree_view,
8532 				GtkTreeRBTree *tree,
8533                                 int           *x1,
8534                                 int           *x2)
8535 {
8536   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8537   int x_offset = 0;
8538   GList *list;
8539   GtkTreeViewColumn *tmp_column = NULL;
8540   int total_width;
8541   int expander_size, expander_render_size;
8542   gboolean rtl;
8543 
8544   rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8545   expander_size = gtk_tree_view_get_expander_size (tree_view);
8546   expander_render_size = expander_size - (_TREE_VIEW_HORIZONTAL_SEPARATOR / 2);
8547 
8548   total_width = 0;
8549   for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
8550        list;
8551        list = (rtl ? list->prev : list->next))
8552     {
8553       tmp_column = list->data;
8554 
8555       if (gtk_tree_view_is_expander_column (tree_view, tmp_column))
8556         {
8557 	  if (rtl)
8558 	    x_offset = total_width + gtk_tree_view_column_get_width (tmp_column) - expander_size;
8559 	  else
8560 	    x_offset = total_width;
8561           break;
8562         }
8563 
8564       if (gtk_tree_view_column_get_visible (tmp_column))
8565         total_width += gtk_tree_view_column_get_width (tmp_column);
8566     }
8567 
8568   x_offset += (expander_size - expander_render_size);
8569 
8570   if (rtl)
8571     x_offset -= expander_size * gtk_tree_rbtree_get_depth (tree);
8572   else
8573     x_offset += expander_size * gtk_tree_rbtree_get_depth (tree);
8574 
8575   *x1 = x_offset;
8576 
8577   if (tmp_column &&
8578       gtk_tree_view_column_get_visible (tmp_column))
8579     /* +1 because x2 isn't included in the range. */
8580     *x2 = *x1 + expander_render_size + 1;
8581   else
8582     *x2 = *x1;
8583 }
8584 
8585 static void
gtk_tree_view_build_tree(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeIter * iter,int depth,gboolean recurse)8586 gtk_tree_view_build_tree (GtkTreeView   *tree_view,
8587 			  GtkTreeRBTree *tree,
8588 			  GtkTreeIter   *iter,
8589 			  int            depth,
8590 			  gboolean       recurse)
8591 {
8592   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8593   GtkTreeRBNode *temp = NULL;
8594   GtkTreePath *path = NULL;
8595 
8596   do
8597     {
8598       gtk_tree_model_ref_node (priv->model, iter);
8599       temp = gtk_tree_rbtree_insert_after (tree, temp, 0, FALSE);
8600 
8601       if (priv->fixed_height > 0)
8602         {
8603           if (GTK_TREE_RBNODE_FLAG_SET (temp, GTK_TREE_RBNODE_INVALID))
8604 	    {
8605               gtk_tree_rbtree_node_set_height (tree, temp, priv->fixed_height);
8606 	      gtk_tree_rbtree_node_mark_valid (tree, temp);
8607 	    }
8608         }
8609 
8610       if (priv->is_list)
8611         continue;
8612 
8613       if (recurse)
8614 	{
8615 	  GtkTreeIter child;
8616 
8617 	  if (!path)
8618 	    path = gtk_tree_model_get_path (priv->model, iter);
8619 	  else
8620 	    gtk_tree_path_next (path);
8621 
8622 	  if (gtk_tree_model_iter_has_child (priv->model, iter))
8623 	    {
8624 	      gboolean expand;
8625 
8626 	      g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, iter, path, &expand);
8627 
8628 	      if (gtk_tree_model_iter_children (priv->model, &child, iter)
8629 		  && !expand)
8630 	        {
8631 	          temp->children = gtk_tree_rbtree_new ();
8632 	          temp->children->parent_tree = tree;
8633 	          temp->children->parent_node = temp;
8634 	          gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse);
8635 		}
8636 	    }
8637 	}
8638 
8639       if (gtk_tree_model_iter_has_child (priv->model, iter))
8640 	{
8641 	  if ((temp->flags&GTK_TREE_RBNODE_IS_PARENT) != GTK_TREE_RBNODE_IS_PARENT)
8642 	    temp->flags ^= GTK_TREE_RBNODE_IS_PARENT;
8643 	}
8644     }
8645   while (gtk_tree_model_iter_next (priv->model, iter));
8646 
8647   if (path)
8648     gtk_tree_path_free (path);
8649 }
8650 
8651 /* Make sure the node is visible vertically */
8652 static void
gtk_tree_view_clamp_node_visible(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node)8653 gtk_tree_view_clamp_node_visible (GtkTreeView   *tree_view,
8654 				  GtkTreeRBTree *tree,
8655 				  GtkTreeRBNode *node)
8656 {
8657   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8658   int node_dy, height;
8659   GtkTreePath *path = NULL;
8660 
8661   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8662     return;
8663 
8664   /* just return if the node is visible, avoiding a costly expose */
8665   node_dy = gtk_tree_rbtree_node_find_offset (tree, node);
8666   height = gtk_tree_view_get_row_height (tree_view, node);
8667   if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID)
8668       && node_dy >= gtk_adjustment_get_value (priv->vadjustment)
8669       && node_dy + height <= (gtk_adjustment_get_value (priv->vadjustment)
8670                               + gtk_adjustment_get_page_size (priv->vadjustment)))
8671     return;
8672 
8673   path = _gtk_tree_path_new_from_rbtree (tree, node);
8674   if (path)
8675     {
8676       gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
8677       gtk_tree_path_free (path);
8678     }
8679 }
8680 
8681 static void
gtk_tree_view_clamp_column_visible(GtkTreeView * tree_view,GtkTreeViewColumn * column,gboolean focus_to_cell)8682 gtk_tree_view_clamp_column_visible (GtkTreeView       *tree_view,
8683 				    GtkTreeViewColumn *column,
8684 				    gboolean           focus_to_cell)
8685 {
8686   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8687   GtkAllocation allocation;
8688   int x, width;
8689 
8690   if (column == NULL)
8691     return;
8692 
8693   gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
8694   x = allocation.x;
8695   width = allocation.width;
8696 
8697   if (width > gtk_adjustment_get_page_size (priv->hadjustment))
8698     {
8699       /* The column is larger than the horizontal page size.  If the
8700        * column has cells which can be focused individually, then we make
8701        * sure the cell which gets focus is fully visible (if even the
8702        * focus cell is bigger than the page size, we make sure the
8703        * left-hand side of the cell is visible).
8704        *
8705        * If the column does not have an activatable cell, we
8706        * make sure the left-hand side of the column is visible.
8707        */
8708 
8709       if (focus_to_cell && gtk_tree_view_has_can_focus_cell (tree_view))
8710         {
8711           GtkCellArea *cell_area;
8712           GtkCellRenderer *focus_cell;
8713 
8714           cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
8715           focus_cell = gtk_cell_area_get_focus_cell (cell_area);
8716 
8717           if (gtk_tree_view_column_cell_get_position (column, focus_cell,
8718                                                       &x, &width))
8719             {
8720               if (width < gtk_adjustment_get_page_size (priv->hadjustment))
8721                 {
8722                   if (gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment) < x + width)
8723                     gtk_adjustment_set_value (priv->hadjustment,
8724                                               x + width - gtk_adjustment_get_page_size (priv->hadjustment));
8725                   else if (gtk_adjustment_get_value (priv->hadjustment) > x)
8726                     gtk_adjustment_set_value (priv->hadjustment, x);
8727                 }
8728             }
8729         }
8730 
8731       gtk_adjustment_set_value (priv->hadjustment, x);
8732     }
8733   else
8734     {
8735       if ((gtk_adjustment_get_value (priv->hadjustment) + gtk_adjustment_get_page_size (priv->hadjustment)) < (x + width))
8736 	  gtk_adjustment_set_value (priv->hadjustment,
8737 				    x + width - gtk_adjustment_get_page_size (priv->hadjustment));
8738       else if (gtk_adjustment_get_value (priv->hadjustment) > x)
8739 	gtk_adjustment_set_value (priv->hadjustment, x);
8740   }
8741 }
8742 
8743 /* This function could be more efficient.  I'll optimize it if profiling seems
8744  * to imply that it is important */
8745 GtkTreePath *
_gtk_tree_path_new_from_rbtree(GtkTreeRBTree * tree,GtkTreeRBNode * node)8746 _gtk_tree_path_new_from_rbtree (GtkTreeRBTree *tree,
8747 			        GtkTreeRBNode *node)
8748 {
8749   GtkTreePath *path;
8750   GtkTreeRBTree *tmp_tree;
8751   GtkTreeRBNode *tmp_node, *last;
8752   int count;
8753 
8754   path = gtk_tree_path_new ();
8755 
8756   g_return_val_if_fail (node != NULL, path);
8757 
8758   count = 1 + node->left->count;
8759 
8760   last = node;
8761   tmp_node = node->parent;
8762   tmp_tree = tree;
8763   while (tmp_tree)
8764     {
8765       while (!gtk_tree_rbtree_is_nil (tmp_node))
8766 	{
8767 	  if (tmp_node->right == last)
8768 	    count += 1 + tmp_node->left->count;
8769 	  last = tmp_node;
8770 	  tmp_node = tmp_node->parent;
8771 	}
8772       gtk_tree_path_prepend_index (path, count - 1);
8773       last = tmp_tree->parent_node;
8774       tmp_tree = tmp_tree->parent_tree;
8775       if (last)
8776 	{
8777 	  count = 1 + last->left->count;
8778 	  tmp_node = last->parent;
8779 	}
8780     }
8781   return path;
8782 }
8783 
8784 /* Returns TRUE if we ran out of tree before finding the path.  If the path is
8785  * invalid (ie. points to a node that’s not in the tree), *tree and *node are
8786  * both set to NULL.
8787  */
8788 gboolean
_gtk_tree_view_find_node(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeRBTree ** tree,GtkTreeRBNode ** node)8789 _gtk_tree_view_find_node (GtkTreeView    *tree_view,
8790 			  GtkTreePath    *path,
8791 			  GtkTreeRBTree **tree,
8792 			  GtkTreeRBNode **node)
8793 {
8794   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8795   GtkTreeRBNode *tmpnode = NULL;
8796   GtkTreeRBTree *tmptree = priv->tree;
8797   int *indices = gtk_tree_path_get_indices (path);
8798   int depth = gtk_tree_path_get_depth (path);
8799   int i = 0;
8800 
8801   *node = NULL;
8802   *tree = NULL;
8803 
8804   if (depth == 0 || tmptree == NULL)
8805     return FALSE;
8806   do
8807     {
8808       tmpnode = gtk_tree_rbtree_find_count (tmptree, indices[i] + 1);
8809       ++i;
8810       if (tmpnode == NULL)
8811 	{
8812 	  *tree = NULL;
8813 	  *node = NULL;
8814 	  return FALSE;
8815 	}
8816       if (i >= depth)
8817 	{
8818 	  *tree = tmptree;
8819 	  *node = tmpnode;
8820 	  return FALSE;
8821 	}
8822       *tree = tmptree;
8823       *node = tmpnode;
8824       tmptree = tmpnode->children;
8825       if (tmptree == NULL)
8826 	return TRUE;
8827     }
8828   while (1);
8829 }
8830 
8831 static gboolean
gtk_tree_view_is_expander_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)8832 gtk_tree_view_is_expander_column (GtkTreeView       *tree_view,
8833 				  GtkTreeViewColumn *column)
8834 {
8835   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8836   GList *list;
8837 
8838   if (priv->is_list)
8839     return FALSE;
8840 
8841   if (priv->expander_column != NULL)
8842     {
8843       if (priv->expander_column == column)
8844 	return TRUE;
8845       return FALSE;
8846     }
8847   else
8848     {
8849       for (list = priv->columns;
8850 	   list;
8851 	   list = list->next)
8852 	if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
8853 	  break;
8854       if (list && list->data == column)
8855 	return TRUE;
8856     }
8857   return FALSE;
8858 }
8859 
8860 static inline gboolean
gtk_tree_view_draw_expanders(GtkTreeView * tree_view)8861 gtk_tree_view_draw_expanders (GtkTreeView *tree_view)
8862 {
8863   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8864 
8865   if (!priv->is_list && priv->show_expanders)
8866     return TRUE;
8867   /* else */
8868   return FALSE;
8869 }
8870 
8871 static void
gtk_tree_view_add_move_binding(GtkWidgetClass * widget_class,guint keyval,guint modmask,gboolean add_shifted_binding,GtkMovementStep step,int count)8872 gtk_tree_view_add_move_binding (GtkWidgetClass *widget_class,
8873 				guint           keyval,
8874 				guint           modmask,
8875 				gboolean        add_shifted_binding,
8876 				GtkMovementStep step,
8877 				int             count)
8878 {
8879   gtk_widget_class_add_binding_signal (widget_class,
8880                                        keyval, modmask,
8881                                        "move-cursor",
8882                                        "(iibb)", step, count, FALSE, FALSE);
8883 
8884   if (add_shifted_binding)
8885     gtk_widget_class_add_binding_signal (widget_class,
8886                                          keyval, GDK_SHIFT_MASK,
8887                                          "move-cursor",
8888                                          "(iibb)", step, count, TRUE, FALSE);
8889 
8890   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
8891    return;
8892 
8893   gtk_widget_class_add_binding_signal (widget_class,
8894                                        keyval, GDK_CONTROL_MASK,
8895                                        "move-cursor",
8896                                        "(iibb)", step, count, FALSE, TRUE);
8897 
8898   if (add_shifted_binding)
8899     gtk_widget_class_add_binding_signal (widget_class, keyval,
8900                                          GDK_CONTROL_MASK | GDK_SHIFT_MASK,
8901                                          "move-cursor",
8902                                          "(iibb)", step, count, TRUE, TRUE);
8903 }
8904 
8905 static int
gtk_tree_view_unref_tree_helper(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeRBTree * tree,GtkTreeRBNode * node)8906 gtk_tree_view_unref_tree_helper (GtkTreeModel  *model,
8907 				 GtkTreeIter   *iter,
8908 				 GtkTreeRBTree *tree,
8909 				 GtkTreeRBNode *node)
8910 {
8911   int retval = FALSE;
8912   do
8913     {
8914       g_return_val_if_fail (node != NULL, FALSE);
8915 
8916       if (node->children)
8917 	{
8918 	  GtkTreeIter child;
8919 	  GtkTreeRBTree *new_tree;
8920 	  GtkTreeRBNode *new_node;
8921 
8922 	  new_tree = node->children;
8923           new_node = gtk_tree_rbtree_first (new_tree);
8924 
8925 	  if (!gtk_tree_model_iter_children (model, &child, iter))
8926 	    return FALSE;
8927 
8928 	  retval = gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node) | retval;
8929 	}
8930 
8931       if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
8932 	retval = TRUE;
8933       gtk_tree_model_unref_node (model, iter);
8934       node = gtk_tree_rbtree_next (tree, node);
8935     }
8936   while (gtk_tree_model_iter_next (model, iter));
8937 
8938   return retval;
8939 }
8940 
8941 static int
gtk_tree_view_unref_and_check_selection_tree(GtkTreeView * tree_view,GtkTreeRBTree * tree)8942 gtk_tree_view_unref_and_check_selection_tree (GtkTreeView   *tree_view,
8943 					      GtkTreeRBTree *tree)
8944 {
8945   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8946   GtkTreeIter iter;
8947   GtkTreePath *path;
8948   GtkTreeRBNode *node;
8949   int retval;
8950 
8951   if (!tree)
8952     return FALSE;
8953 
8954   node = gtk_tree_rbtree_first (tree);
8955 
8956   g_return_val_if_fail (node != NULL, FALSE);
8957   path = _gtk_tree_path_new_from_rbtree (tree, node);
8958   gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->model),
8959 			   &iter, path);
8960   retval = gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (priv->model), &iter, tree, node);
8961   gtk_tree_path_free (path);
8962 
8963   return retval;
8964 }
8965 
8966 static void
gtk_tree_view_set_column_drag_info(GtkTreeView * tree_view,GtkTreeViewColumn * column)8967 gtk_tree_view_set_column_drag_info (GtkTreeView       *tree_view,
8968 				    GtkTreeViewColumn *column)
8969 {
8970   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
8971   GtkTreeViewColumn *left_column;
8972   GtkTreeViewColumn *cur_column = NULL;
8973   GtkTreeViewColumnReorder *reorder;
8974   gboolean rtl;
8975   GList *tmp_list;
8976   int left;
8977 
8978   /* We want to precalculate the motion list such that we know what column slots
8979    * are available.
8980    */
8981   left_column = NULL;
8982   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8983 
8984   /* First, identify all possible drop spots */
8985   if (rtl)
8986     tmp_list = g_list_last (priv->columns);
8987   else
8988     tmp_list = g_list_first (priv->columns);
8989 
8990   while (tmp_list)
8991     {
8992       cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
8993       tmp_list = rtl ? tmp_list->prev : tmp_list->next;
8994 
8995       if (gtk_tree_view_column_get_visible (cur_column) == FALSE)
8996 	continue;
8997 
8998       /* If it's not the column moving and func tells us to skip over the column, we continue. */
8999       if (left_column != column && cur_column != column &&
9000 	  priv->column_drop_func &&
9001 	  ! priv->column_drop_func (tree_view, column, left_column, cur_column, priv->column_drop_func_data))
9002 	{
9003 	  left_column = cur_column;
9004 	  continue;
9005 	}
9006       reorder = g_slice_new0 (GtkTreeViewColumnReorder);
9007       reorder->left_column = left_column;
9008       left_column = reorder->right_column = cur_column;
9009 
9010       priv->column_drag_info = g_list_append (priv->column_drag_info, reorder);
9011     }
9012 
9013   /* Add the last one */
9014   if (priv->column_drop_func == NULL ||
9015       ((left_column != column) &&
9016        priv->column_drop_func (tree_view, column, left_column, NULL, priv->column_drop_func_data)))
9017     {
9018       reorder = g_slice_new0 (GtkTreeViewColumnReorder);
9019       reorder->left_column = left_column;
9020       reorder->right_column = NULL;
9021       priv->column_drag_info = g_list_append (priv->column_drag_info, reorder);
9022     }
9023 
9024   /* We quickly check to see if it even makes sense to reorder columns. */
9025   /* If there is nothing that can be moved, then we return */
9026 
9027   if (priv->column_drag_info == NULL)
9028     return;
9029 
9030   /* We know there are always 2 slots possbile, as you can always return column. */
9031   /* If that's all there is, return */
9032   if (priv->column_drag_info->next == NULL ||
9033       (priv->column_drag_info->next->next == NULL &&
9034        ((GtkTreeViewColumnReorder *)priv->column_drag_info->data)->right_column == column &&
9035        ((GtkTreeViewColumnReorder *)priv->column_drag_info->next->data)->left_column == column))
9036     {
9037       for (tmp_list = priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
9038 	g_slice_free (GtkTreeViewColumnReorder, tmp_list->data);
9039       g_list_free (priv->column_drag_info);
9040       priv->column_drag_info = NULL;
9041       return;
9042     }
9043   /* We fill in the ranges for the columns, now that we've isolated them */
9044   left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
9045 
9046   for (tmp_list = priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
9047     {
9048       reorder = (GtkTreeViewColumnReorder *) tmp_list->data;
9049 
9050       reorder->left_align = left;
9051       if (tmp_list->next != NULL)
9052 	{
9053           GtkAllocation right_allocation, left_allocation;
9054 	  GtkWidget    *left_button, *right_button;
9055 
9056 	  g_assert (tmp_list->next->data);
9057 
9058 	  right_button = gtk_tree_view_column_get_button (reorder->right_column);
9059 	  left_button  = gtk_tree_view_column_get_button
9060 	    (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column);
9061 
9062           gtk_widget_get_allocation (right_button, &right_allocation);
9063           gtk_widget_get_allocation (left_button, &left_allocation);
9064 	  left = reorder->right_align = (right_allocation.x + right_allocation.width + left_allocation.x) / 2;
9065 	}
9066       else
9067 	{
9068 	  reorder->right_align = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view))
9069                                  + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
9070 	}
9071     }
9072 }
9073 
9074 void
_gtk_tree_view_column_start_drag(GtkTreeView * tree_view,GtkTreeViewColumn * column,GdkDevice * device)9075 _gtk_tree_view_column_start_drag (GtkTreeView       *tree_view,
9076 				  GtkTreeViewColumn *column,
9077                                   GdkDevice         *device)
9078 {
9079   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9080   GtkAllocation button_allocation;
9081   GtkWidget *button;
9082   GtkStyleContext *context;
9083 
9084   g_return_if_fail (priv->column_drag_info == NULL);
9085   g_return_if_fail (priv->cur_reorder == NULL);
9086 
9087   gtk_tree_view_set_column_drag_info (tree_view, column);
9088 
9089   if (priv->column_drag_info == NULL)
9090     return;
9091 
9092   button = gtk_tree_view_column_get_button (column);
9093 
9094   context = gtk_widget_get_style_context (button);
9095   gtk_style_context_add_class (context, "dnd");
9096 
9097   gtk_widget_get_allocation (button, &button_allocation);
9098   priv->drag_column_x = button_allocation.x;
9099   priv->drag_column_y = button_allocation.y;
9100 
9101   priv->drag_column = column;
9102 
9103   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9104 
9105   priv->in_column_drag = TRUE;
9106 
9107   gtk_gesture_set_state (priv->column_drag_gesture,
9108                          GTK_EVENT_SEQUENCE_CLAIMED);
9109 }
9110 
9111 static inline int
gtk_tree_view_get_effective_header_height(GtkTreeView * tree_view)9112 gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view)
9113 {
9114   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9115 
9116   if (priv->headers_visible)
9117     return priv->header_height;
9118   else
9119     return 0;
9120 }
9121 
9122 void
_gtk_tree_view_get_row_separator_func(GtkTreeView * tree_view,GtkTreeViewRowSeparatorFunc * func,gpointer * data)9123 _gtk_tree_view_get_row_separator_func (GtkTreeView                 *tree_view,
9124 				       GtkTreeViewRowSeparatorFunc *func,
9125 				       gpointer                    *data)
9126 {
9127   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9128 
9129   *func = priv->row_separator_func;
9130   *data = priv->row_separator_data;
9131 }
9132 
9133 GtkTreePath *
_gtk_tree_view_get_anchor_path(GtkTreeView * tree_view)9134 _gtk_tree_view_get_anchor_path (GtkTreeView *tree_view)
9135 {
9136   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9137 
9138   if (priv->anchor)
9139     return gtk_tree_row_reference_get_path (priv->anchor);
9140 
9141   return NULL;
9142 }
9143 
9144 void
_gtk_tree_view_set_anchor_path(GtkTreeView * tree_view,GtkTreePath * anchor_path)9145 _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view,
9146 				GtkTreePath *anchor_path)
9147 {
9148   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9149 
9150   if (priv->anchor)
9151     {
9152       gtk_tree_row_reference_free (priv->anchor);
9153       priv->anchor = NULL;
9154     }
9155 
9156   if (anchor_path && priv->model)
9157     priv->anchor =
9158       gtk_tree_row_reference_new (priv->model, anchor_path);
9159 }
9160 
9161 GtkTreeRBTree *
_gtk_tree_view_get_rbtree(GtkTreeView * tree_view)9162 _gtk_tree_view_get_rbtree (GtkTreeView *tree_view)
9163 {
9164   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9165 
9166   return priv->tree;
9167 }
9168 
9169 gboolean
_gtk_tree_view_get_cursor_node(GtkTreeView * tree_view,GtkTreeRBTree ** tree,GtkTreeRBNode ** node)9170 _gtk_tree_view_get_cursor_node (GtkTreeView    *tree_view,
9171                                 GtkTreeRBTree **tree,
9172                                 GtkTreeRBNode **node)
9173 {
9174   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9175 
9176   if (priv->cursor_node == NULL)
9177     return FALSE;
9178 
9179   *tree = priv->cursor_tree;
9180   *node = priv->cursor_node;
9181 
9182   return TRUE;
9183 }
9184 
9185 GtkTreeViewColumn *
_gtk_tree_view_get_focus_column(GtkTreeView * tree_view)9186 _gtk_tree_view_get_focus_column (GtkTreeView *tree_view)
9187 {
9188   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9189 
9190   return priv->focus_column;
9191 }
9192 
9193 void
_gtk_tree_view_set_focus_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)9194 _gtk_tree_view_set_focus_column (GtkTreeView       *tree_view,
9195 				 GtkTreeViewColumn *column)
9196 {
9197   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9198   GtkTreeViewColumn *old_column = priv->focus_column;
9199 
9200   if (old_column == column)
9201     return;
9202 
9203   priv->focus_column = column;
9204 }
9205 
9206 /* x and y are the mouse position
9207  */
9208 static void
gtk_tree_view_snapshot_arrow(GtkTreeView * tree_view,GtkSnapshot * snapshot,GtkTreeRBTree * tree,GtkTreeRBNode * node)9209 gtk_tree_view_snapshot_arrow (GtkTreeView   *tree_view,
9210                               GtkSnapshot   *snapshot,
9211                               GtkTreeRBTree *tree,
9212                               GtkTreeRBNode *node)
9213 {
9214   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9215   GdkRectangle area;
9216   GtkStateFlags state = 0;
9217   GtkStyleContext *context;
9218   GtkWidget *widget;
9219   int x_offset = 0;
9220   int x2;
9221   GtkCellRendererState flags = 0;
9222 
9223   widget = GTK_WIDGET (tree_view);
9224   context = gtk_widget_get_style_context (widget);
9225 
9226   if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT))
9227     return;
9228 
9229   gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2);
9230 
9231   area.x = x_offset;
9232   area.y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node);
9233   area.width = x2 - x_offset;
9234   area.height = gtk_tree_view_get_cell_area_height (tree_view, node);
9235 
9236   if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
9237     flags |= GTK_CELL_RENDERER_SELECTED;
9238 
9239   if (node == priv->prelight_node &&
9240       priv->arrow_prelit)
9241     flags |= GTK_CELL_RENDERER_PRELIT;
9242 
9243   state = gtk_cell_renderer_get_state (NULL, widget, flags);
9244 
9245   if (node->children != NULL)
9246     state |= GTK_STATE_FLAG_CHECKED;
9247   else
9248     state &= ~(GTK_STATE_FLAG_CHECKED);
9249 
9250   gtk_style_context_save (context);
9251 
9252   gtk_style_context_set_state (context, state);
9253   gtk_style_context_add_class (context, "expander");
9254 
9255   gtk_snapshot_save (snapshot);
9256   gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (area.x, area.y));
9257   gtk_css_style_snapshot_icon (gtk_style_context_lookup_style (context), snapshot,
9258                                area.width, area.height);
9259   gtk_snapshot_restore (snapshot);
9260 
9261   gtk_style_context_restore (context);
9262 }
9263 
9264 static void
gtk_tree_view_focus_to_cursor(GtkTreeView * tree_view)9265 gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view)
9266 
9267 {
9268   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9269   GtkTreePath *cursor_path;
9270 
9271   if ((priv->tree == NULL) ||
9272       (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
9273     return;
9274 
9275   cursor_path = NULL;
9276   if (priv->cursor_node)
9277     cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9278                                                   priv->cursor_node);
9279 
9280   if (cursor_path == NULL)
9281     {
9282       /* Consult the selection before defaulting to the
9283        * first focusable element
9284        */
9285       GList *selected_rows;
9286       GtkTreeModel *model;
9287       GtkTreeSelection *selection;
9288 
9289       selection = gtk_tree_view_get_selection (tree_view);
9290       selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
9291 
9292       if (selected_rows)
9293 	{
9294           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
9295 	  g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
9296         }
9297       else
9298 	{
9299 	  cursor_path = gtk_tree_path_new_first ();
9300 	  search_first_focusable_path (tree_view, &cursor_path,
9301 				       TRUE, NULL, NULL);
9302 	}
9303 
9304       if (cursor_path)
9305 	{
9306 	  if (gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE)
9307 	    gtk_tree_view_real_set_cursor (tree_view, cursor_path, 0);
9308 	  else
9309 	    gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT);
9310 	}
9311     }
9312 
9313   if (cursor_path)
9314     {
9315       priv->draw_keyfocus = TRUE;
9316 
9317       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9318       gtk_tree_path_free (cursor_path);
9319 
9320       if (priv->focus_column == NULL)
9321 	{
9322 	  GList *list;
9323 	  for (list = priv->columns; list; list = list->next)
9324 	    {
9325 	      if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
9326 		{
9327 		  GtkCellArea *cell_area;
9328 
9329                   _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data));
9330 
9331 		  /* This happens when the treeview initially grabs focus and there
9332 		   * is no column in focus, here we explicitly focus into the first cell */
9333 		  cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column));
9334 		  if (!gtk_cell_area_get_focus_cell (cell_area))
9335                     {
9336                       gboolean rtl;
9337 
9338                       rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9339                       gtk_cell_area_focus (cell_area,
9340                                            rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT);
9341                     }
9342 
9343 		  break;
9344 		}
9345 	    }
9346 	}
9347     }
9348 }
9349 
9350 static void
gtk_tree_view_move_cursor_up_down(GtkTreeView * tree_view,int count)9351 gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
9352 				   int          count)
9353 {
9354   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9355   int selection_count;
9356   GtkTreeRBTree *new_cursor_tree = NULL;
9357   GtkTreeRBNode *new_cursor_node = NULL;
9358   GtkTreePath *cursor_path = NULL;
9359   gboolean selectable;
9360   GtkDirectionType direction;
9361   GtkCellArea *cell_area = NULL;
9362   GtkCellRenderer *last_focus_cell = NULL;
9363   GtkTreeIter iter;
9364 
9365   if (priv->cursor_node == NULL)
9366     return;
9367 
9368   cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9369                                                 priv->cursor_node);
9370 
9371   direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
9372 
9373   if (priv->focus_column)
9374     cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column));
9375 
9376   /* If focus stays in the area for this row, then just return for this round */
9377   if (cell_area && (count == -1 || count == 1) &&
9378       gtk_tree_model_get_iter (priv->model, &iter, cursor_path))
9379     {
9380       gtk_tree_view_column_cell_set_cell_data (priv->focus_column,
9381 					       priv->model,
9382                                                &iter,
9383                                                GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT),
9384 					       priv->cursor_node->children ? TRUE : FALSE);
9385 
9386       /* Save the last cell that had focus, if we hit the end of the view we'll give
9387        * focus back to it. */
9388       last_focus_cell = gtk_cell_area_get_focus_cell (cell_area);
9389 
9390       /* If focus stays in the area, no need to change the cursor row */
9391       if (gtk_cell_area_focus (cell_area, direction))
9392 	return;
9393     }
9394 
9395   selection_count = gtk_tree_selection_count_selected_rows (priv->selection);
9396   selectable = _gtk_tree_selection_row_is_selectable (priv->selection,
9397 						      priv->cursor_node,
9398 						      cursor_path);
9399 
9400   if (selection_count == 0
9401       && gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_NONE
9402       && !priv->modify_selection_pressed
9403       && selectable)
9404     {
9405       /* Don't move the cursor, but just select the current node */
9406       new_cursor_tree = priv->cursor_tree;
9407       new_cursor_node = priv->cursor_node;
9408     }
9409   else
9410     {
9411       if (count == -1)
9412 	gtk_tree_rbtree_prev_full (priv->cursor_tree, priv->cursor_node,
9413 			       &new_cursor_tree, &new_cursor_node);
9414       else
9415 	gtk_tree_rbtree_next_full (priv->cursor_tree, priv->cursor_node,
9416 			       &new_cursor_tree, &new_cursor_node);
9417     }
9418 
9419   gtk_tree_path_free (cursor_path);
9420 
9421   if (new_cursor_node)
9422     {
9423       cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node);
9424 
9425       search_first_focusable_path (tree_view, &cursor_path,
9426 				   (count != -1),
9427 				   &new_cursor_tree,
9428 				   &new_cursor_node);
9429 
9430       if (cursor_path)
9431 	gtk_tree_path_free (cursor_path);
9432     }
9433 
9434   /*
9435    * If the list has only one item and multi-selection is set then select
9436    * the row (if not yet selected).
9437    */
9438   if (gtk_tree_selection_get_mode (priv->selection) == GTK_SELECTION_MULTIPLE &&
9439       new_cursor_node == NULL)
9440     {
9441       if (count == -1)
9442         gtk_tree_rbtree_next_full (priv->cursor_tree, priv->cursor_node,
9443     			       &new_cursor_tree, &new_cursor_node);
9444       else
9445         gtk_tree_rbtree_prev_full (priv->cursor_tree, priv->cursor_node,
9446 			       &new_cursor_tree, &new_cursor_node);
9447 
9448       if (new_cursor_node == NULL
9449 	  && !GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_SELECTED))
9450         {
9451           new_cursor_node = priv->cursor_node;
9452           new_cursor_tree = priv->cursor_tree;
9453         }
9454       else
9455         {
9456           new_cursor_tree = NULL;
9457           new_cursor_node = NULL;
9458         }
9459     }
9460 
9461   if (new_cursor_node)
9462     {
9463       cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node);
9464       gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE);
9465       gtk_tree_path_free (cursor_path);
9466 
9467       /* Give focus to the area in the new row */
9468       if (cell_area)
9469 	gtk_cell_area_focus (cell_area, direction);
9470     }
9471   else
9472     {
9473       gtk_tree_view_clamp_node_visible (tree_view,
9474                                         priv->cursor_tree,
9475                                         priv->cursor_node);
9476 
9477       if (!priv->extend_selection_pressed)
9478         {
9479           if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
9480                                           count < 0 ?
9481                                           GTK_DIR_UP : GTK_DIR_DOWN))
9482             {
9483               GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (tree_view)));
9484 
9485               if (toplevel)
9486                 gtk_widget_child_focus (toplevel,
9487                                         count < 0 ?
9488                                         GTK_DIR_TAB_BACKWARD :
9489                                         GTK_DIR_TAB_FORWARD);
9490             }
9491         }
9492       else
9493         {
9494           gtk_widget_error_bell (GTK_WIDGET (tree_view));
9495         }
9496 
9497       if (cell_area)
9498 	gtk_cell_area_set_focus_cell (cell_area, last_focus_cell);
9499     }
9500 }
9501 
9502 static void
gtk_tree_view_move_cursor_page_up_down(GtkTreeView * tree_view,int count)9503 gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
9504 					int          count)
9505 {
9506   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9507   GtkTreePath *old_cursor_path = NULL;
9508   GtkTreePath *cursor_path = NULL;
9509   GtkTreeRBTree *start_cursor_tree = NULL;
9510   GtkTreeRBNode *start_cursor_node = NULL;
9511   GtkTreeRBTree *cursor_tree;
9512   GtkTreeRBNode *cursor_node;
9513   int y;
9514   int window_y;
9515 
9516   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9517     return;
9518 
9519   if (priv->cursor_node == NULL)
9520     return;
9521 
9522   old_cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9523                                                     priv->cursor_node);
9524 
9525   y = gtk_tree_rbtree_node_find_offset (priv->cursor_tree, priv->cursor_node);
9526   window_y = RBTREE_Y_TO_TREE_WINDOW_Y (priv, y);
9527   y += priv->cursor_offset;
9528   y += count * (int)gtk_adjustment_get_page_increment (priv->vadjustment);
9529   y = CLAMP (y, (int)gtk_adjustment_get_lower (priv->vadjustment),  (int)gtk_adjustment_get_upper (priv->vadjustment));
9530 
9531   if (y >= gtk_tree_view_get_height (tree_view))
9532     y = gtk_tree_view_get_height (tree_view) - 1;
9533 
9534   priv->cursor_offset =
9535     gtk_tree_rbtree_find_offset (priv->tree, y,
9536 			     &cursor_tree, &cursor_node);
9537 
9538   if (cursor_tree == NULL)
9539     {
9540       /* FIXME: we lost the cursor.  Should we try to get one? */
9541       gtk_tree_path_free (old_cursor_path);
9542       return;
9543     }
9544 
9545   if (priv->cursor_offset
9546       > gtk_tree_view_get_row_height (tree_view, cursor_node))
9547     {
9548       gtk_tree_rbtree_next_full (cursor_tree, cursor_node,
9549 			     &cursor_tree, &cursor_node);
9550       priv->cursor_offset -= gtk_tree_view_get_row_height (tree_view, cursor_node);
9551     }
9552 
9553   y -= priv->cursor_offset;
9554   cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9555 
9556   start_cursor_tree = cursor_tree;
9557   start_cursor_node = cursor_node;
9558 
9559   if (! search_first_focusable_path (tree_view, &cursor_path,
9560 				     (count != -1),
9561 				     &cursor_tree, &cursor_node))
9562     {
9563       /* It looks like we reached the end of the view without finding
9564        * a focusable row.  We will step backwards to find the last
9565        * focusable row.
9566        */
9567       cursor_tree = start_cursor_tree;
9568       cursor_node = start_cursor_node;
9569       cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9570 
9571       search_first_focusable_path (tree_view, &cursor_path,
9572 				   (count == -1),
9573 				   &cursor_tree, &cursor_node);
9574     }
9575 
9576   if (!cursor_path)
9577     goto cleanup;
9578 
9579   /* update y */
9580   y = gtk_tree_rbtree_node_find_offset (cursor_tree, cursor_node);
9581 
9582   gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT);
9583 
9584   y -= window_y;
9585   gtk_tree_view_scroll_to_point (tree_view, -1, y);
9586   gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
9587   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9588 
9589   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
9590     gtk_widget_error_bell (GTK_WIDGET (tree_view));
9591 
9592   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9593 
9594 cleanup:
9595   gtk_tree_path_free (old_cursor_path);
9596   gtk_tree_path_free (cursor_path);
9597 }
9598 
9599 static void
gtk_tree_view_move_cursor_left_right(GtkTreeView * tree_view,int count)9600 gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
9601 				      int          count)
9602 {
9603   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9604   GtkTreePath *cursor_path = NULL;
9605   GtkTreeViewColumn *column;
9606   GtkTreeIter iter;
9607   GList *list;
9608   gboolean found_column = FALSE;
9609   gboolean rtl;
9610   GtkDirectionType direction;
9611   GtkCellArea     *cell_area;
9612   GtkCellRenderer *last_focus_cell = NULL;
9613   GtkCellArea     *last_focus_area = NULL;
9614 
9615   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9616 
9617   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9618     return;
9619 
9620   if (priv->cursor_node == NULL)
9621     return;
9622 
9623   cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9624                                                 priv->cursor_node);
9625 
9626   if (gtk_tree_model_get_iter (priv->model, &iter, cursor_path) == FALSE)
9627     {
9628       gtk_tree_path_free (cursor_path);
9629       return;
9630     }
9631   gtk_tree_path_free (cursor_path);
9632 
9633   list = rtl ? g_list_last (priv->columns) : g_list_first (priv->columns);
9634   if (priv->focus_column)
9635     {
9636       /* Save the cell/area we are moving focus from, if moving the cursor
9637        * by one step hits the end we'll set focus back here */
9638       last_focus_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->focus_column));
9639       last_focus_cell = gtk_cell_area_get_focus_cell (last_focus_area);
9640 
9641       for (; list; list = (rtl ? list->prev : list->next))
9642 	{
9643 	  if (list->data == priv->focus_column)
9644 	    break;
9645 	}
9646     }
9647 
9648   direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
9649 
9650   while (list)
9651     {
9652       column = list->data;
9653       if (gtk_tree_view_column_get_visible (column) == FALSE)
9654 	goto loop_end;
9655 
9656       gtk_tree_view_column_cell_set_cell_data (column,
9657 					       priv->model,
9658 					       &iter,
9659 					       GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT),
9660 					       priv->cursor_node->children ? TRUE : FALSE);
9661 
9662       cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
9663       if (gtk_cell_area_focus (cell_area, direction))
9664 	{
9665           _gtk_tree_view_set_focus_column (tree_view, column);
9666 	  found_column = TRUE;
9667 	  break;
9668 	}
9669 
9670     loop_end:
9671       if (count == 1)
9672 	list = rtl ? list->prev : list->next;
9673       else
9674 	list = rtl ? list->next : list->prev;
9675     }
9676 
9677   if (found_column)
9678     {
9679       if (!gtk_tree_view_has_can_focus_cell (tree_view))
9680         gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9681       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9682       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9683     }
9684   else
9685     {
9686       gtk_widget_error_bell (GTK_WIDGET (tree_view));
9687 
9688       if (last_focus_area)
9689 	gtk_cell_area_set_focus_cell (last_focus_area, last_focus_cell);
9690     }
9691 
9692   gtk_tree_view_clamp_column_visible (tree_view,
9693 				      priv->focus_column, TRUE);
9694 }
9695 
9696 static void
gtk_tree_view_move_cursor_start_end(GtkTreeView * tree_view,int count)9697 gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
9698 				     int          count)
9699 {
9700   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9701   GtkTreeRBTree *cursor_tree;
9702   GtkTreeRBNode *cursor_node;
9703   GtkTreePath *path;
9704   GtkTreePath *old_path;
9705 
9706   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9707     return;
9708 
9709   g_return_if_fail (priv->tree != NULL);
9710 
9711   gtk_tree_view_get_cursor (tree_view, &old_path, NULL);
9712 
9713   cursor_tree = priv->tree;
9714 
9715   if (count == -1)
9716     {
9717       cursor_node = gtk_tree_rbtree_first (cursor_tree);
9718 
9719       /* Now go forward to find the first focusable row. */
9720       path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9721       search_first_focusable_path (tree_view, &path,
9722 				   TRUE, &cursor_tree, &cursor_node);
9723     }
9724   else
9725     {
9726       cursor_node = cursor_tree->root;
9727 
9728       do
9729 	{
9730 	  while (cursor_node && !gtk_tree_rbtree_is_nil (cursor_node->right))
9731 	    cursor_node = cursor_node->right;
9732 	  if (cursor_node->children == NULL)
9733 	    break;
9734 
9735 	  cursor_tree = cursor_node->children;
9736 	  cursor_node = cursor_tree->root;
9737 	}
9738       while (1);
9739 
9740       /* Now go backwards to find last focusable row. */
9741       path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9742       search_first_focusable_path (tree_view, &path,
9743 				   FALSE, &cursor_tree, &cursor_node);
9744     }
9745 
9746   if (!path)
9747     goto cleanup;
9748 
9749   if (gtk_tree_path_compare (old_path, path))
9750     {
9751       gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE);
9752       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9753     }
9754   else
9755     {
9756       gtk_widget_error_bell (GTK_WIDGET (tree_view));
9757     }
9758 
9759 cleanup:
9760   gtk_tree_path_free (old_path);
9761   gtk_tree_path_free (path);
9762 }
9763 
9764 static gboolean
gtk_tree_view_real_select_all(GtkTreeView * tree_view)9765 gtk_tree_view_real_select_all (GtkTreeView *tree_view)
9766 {
9767   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9768 
9769   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9770     return FALSE;
9771 
9772   if (gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_MULTIPLE)
9773     return FALSE;
9774 
9775   gtk_tree_selection_select_all (priv->selection);
9776 
9777   return TRUE;
9778 }
9779 
9780 static gboolean
gtk_tree_view_real_unselect_all(GtkTreeView * tree_view)9781 gtk_tree_view_real_unselect_all (GtkTreeView *tree_view)
9782 {
9783   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9784 
9785   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9786     return FALSE;
9787 
9788   if (gtk_tree_selection_get_mode (priv->selection) != GTK_SELECTION_MULTIPLE)
9789     return FALSE;
9790 
9791   gtk_tree_selection_unselect_all (priv->selection);
9792 
9793   return TRUE;
9794 }
9795 
9796 static gboolean
gtk_tree_view_real_select_cursor_row(GtkTreeView * tree_view,gboolean start_editing)9797 gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
9798 				      gboolean     start_editing)
9799 {
9800   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9801   GtkTreeRBTree *new_tree = NULL;
9802   GtkTreeRBNode *new_node = NULL;
9803   GtkTreeRBTree *cursor_tree = NULL;
9804   GtkTreeRBNode *cursor_node = NULL;
9805   GtkTreePath *cursor_path = NULL;
9806   GtkTreeSelectMode mode = 0;
9807 
9808   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9809     return FALSE;
9810 
9811   if (priv->cursor_node == NULL)
9812     return FALSE;
9813 
9814   cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9815                                                 priv->cursor_node);
9816 
9817   _gtk_tree_view_find_node (tree_view, cursor_path,
9818 			    &cursor_tree, &cursor_node);
9819 
9820   if (cursor_tree == NULL)
9821     {
9822       gtk_tree_path_free (cursor_path);
9823       return FALSE;
9824     }
9825 
9826   if (!priv->extend_selection_pressed && start_editing &&
9827       priv->focus_column)
9828     {
9829       if (gtk_tree_view_start_editing (tree_view, cursor_path, FALSE))
9830 	{
9831 	  gtk_tree_path_free (cursor_path);
9832 	  return TRUE;
9833 	}
9834     }
9835 
9836   if (priv->modify_selection_pressed)
9837     mode |= GTK_TREE_SELECT_MODE_TOGGLE;
9838   if (priv->extend_selection_pressed)
9839     mode |= GTK_TREE_SELECT_MODE_EXTEND;
9840 
9841   _gtk_tree_selection_internal_select_node (priv->selection,
9842 					    cursor_node,
9843 					    cursor_tree,
9844 					    cursor_path,
9845                                             mode,
9846 					    FALSE);
9847 
9848   /* We bail out if the original (tree, node) don't exist anymore after
9849    * handling the selection-changed callback.  We do return TRUE because
9850    * the key press has been handled at this point.
9851    */
9852   _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
9853 
9854   if (cursor_tree != new_tree || cursor_node != new_node)
9855     return FALSE;
9856 
9857   gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
9858 
9859   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9860   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9861 
9862   if (!priv->extend_selection_pressed)
9863     gtk_tree_view_row_activated (tree_view, cursor_path,
9864                                  priv->focus_column);
9865 
9866   gtk_tree_path_free (cursor_path);
9867 
9868   return TRUE;
9869 }
9870 
9871 static gboolean
gtk_tree_view_real_toggle_cursor_row(GtkTreeView * tree_view)9872 gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view)
9873 {
9874   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9875   GtkTreeRBTree *new_tree = NULL;
9876   GtkTreeRBNode *new_node = NULL;
9877   GtkTreePath *cursor_path = NULL;
9878 
9879   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9880     return FALSE;
9881 
9882   if (priv->cursor_node == NULL)
9883     return FALSE;
9884 
9885   cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9886                                                 priv->cursor_node);
9887 
9888   _gtk_tree_selection_internal_select_node (priv->selection,
9889 					    priv->cursor_node,
9890 					    priv->cursor_tree,
9891 					    cursor_path,
9892                                             GTK_TREE_SELECT_MODE_TOGGLE,
9893 					    FALSE);
9894 
9895   /* We bail out if the original (tree, node) don't exist anymore after
9896    * handling the selection-changed callback.  We do return TRUE because
9897    * the key press has been handled at this point.
9898    */
9899   _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
9900 
9901   if (priv->cursor_node != new_node)
9902     return FALSE;
9903 
9904   gtk_tree_view_clamp_node_visible (tree_view,
9905                                     priv->cursor_tree,
9906                                     priv->cursor_node);
9907 
9908   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9909   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9910   gtk_tree_path_free (cursor_path);
9911 
9912   return TRUE;
9913 }
9914 
9915 static gboolean
gtk_tree_view_real_expand_collapse_cursor_row(GtkTreeView * tree_view,gboolean logical,gboolean expand,gboolean open_all)9916 gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view,
9917 					       gboolean     logical,
9918 					       gboolean     expand,
9919 					       gboolean     open_all)
9920 {
9921   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9922   GtkTreePath *cursor_path = NULL;
9923 
9924   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9925     return FALSE;
9926 
9927   if (priv->cursor_node == NULL)
9928     return FALSE;
9929 
9930   cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9931                                                 priv->cursor_node);
9932 
9933   /* Don't handle the event if we aren't an expander */
9934   if (!GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_PARENT))
9935     return FALSE;
9936 
9937   if (!logical
9938       && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
9939     expand = !expand;
9940 
9941   if (expand)
9942     gtk_tree_view_real_expand_row (tree_view,
9943                                    cursor_path,
9944                                    priv->cursor_tree,
9945                                    priv->cursor_node,
9946                                    open_all);
9947   else
9948     gtk_tree_view_real_collapse_row (tree_view,
9949                                      cursor_path,
9950                                      priv->cursor_tree,
9951                                      priv->cursor_node);
9952 
9953   gtk_tree_path_free (cursor_path);
9954 
9955   return TRUE;
9956 }
9957 
9958 static gboolean
gtk_tree_view_real_select_cursor_parent(GtkTreeView * tree_view)9959 gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
9960 {
9961   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9962   GtkTreePath *cursor_path = NULL;
9963 
9964   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9965     goto out;
9966 
9967   if (priv->cursor_node == NULL)
9968     goto out;
9969 
9970   cursor_path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
9971                                                 priv->cursor_node);
9972 
9973   if (priv->cursor_tree->parent_node)
9974     {
9975       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9976 
9977       gtk_tree_path_up (cursor_path);
9978 
9979       gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE);
9980       gtk_tree_path_free (cursor_path);
9981 
9982       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9983 
9984       return TRUE;
9985     }
9986 
9987  out:
9988 
9989   priv->search_entry_avoid_unhandled_binding = TRUE;
9990   return FALSE;
9991 }
9992 
9993 static gboolean
gtk_tree_view_search_entry_flush_timeout(GtkTreeView * tree_view)9994 gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
9995 {
9996   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
9997 
9998   gtk_tree_view_search_popover_hide (priv->search_popover, tree_view);
9999   priv->typeselect_flush_timeout = 0;
10000 
10001   return FALSE;
10002 }
10003 
10004 static void
gtk_tree_view_ensure_interactive_directory(GtkTreeView * tree_view)10005 gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
10006 {
10007   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10008   GtkEventController *controller;
10009   GtkGesture *gesture;
10010 
10011   if (priv->search_custom_entry_set)
10012     return;
10013 
10014   if (priv->search_popover)
10015     return;
10016 
10017   priv->search_popover = gtk_popover_new ();
10018   gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (tree_view)),
10019                              gtk_widget_get_css_node (priv->search_popover),
10020                              priv->header_node);
10021   gtk_widget_set_parent (priv->search_popover, GTK_WIDGET (tree_view));
10022   gtk_popover_set_autohide (GTK_POPOVER (priv->search_popover), FALSE);
10023 
10024   controller = gtk_event_controller_key_new ();
10025   g_signal_connect (controller, "key-pressed",
10026 		    G_CALLBACK (gtk_tree_view_search_key_pressed),
10027 		    tree_view);
10028   gtk_widget_add_controller (priv->search_popover, controller);
10029 
10030   gesture = gtk_gesture_click_new ();
10031   g_signal_connect (gesture, "pressed",
10032                     G_CALLBACK (gtk_tree_view_search_pressed_cb), tree_view);
10033   gtk_widget_add_controller (priv->search_popover, GTK_EVENT_CONTROLLER (gesture));
10034 
10035   controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
10036   g_signal_connect (controller, "scroll",
10037 		    G_CALLBACK (gtk_tree_view_search_scroll_event),
10038 		    tree_view);
10039   gtk_widget_add_controller (priv->search_popover, controller);
10040 
10041   priv->search_entry = gtk_text_new ();
10042 
10043   controller = gtk_text_get_key_controller (GTK_TEXT (priv->search_entry));
10044   gtk_event_controller_set_propagation_limit (controller, GTK_LIMIT_NONE);
10045 
10046   g_signal_connect (priv->search_entry, "activate",
10047                     G_CALLBACK (gtk_tree_view_search_activate), tree_view);
10048   g_signal_connect (priv->search_entry, "preedit-changed",
10049 		    G_CALLBACK (gtk_tree_view_search_preedit_changed), tree_view);
10050   g_signal_connect (priv->search_entry, "changed",
10051 		    G_CALLBACK (gtk_tree_view_search_changed), tree_view);
10052 
10053   gtk_popover_set_child (GTK_POPOVER (priv->search_popover), priv->search_entry);
10054 
10055   gtk_widget_realize (priv->search_entry);
10056 }
10057 
10058 /* Pops up the interactive search entry.  If keybinding is TRUE then the user
10059  * started this by typing the start_interactive_search keybinding.  Otherwise, it came from
10060  */
10061 static gboolean
gtk_tree_view_real_start_interactive_search(GtkTreeView * tree_view,gboolean keybinding)10062 gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
10063 					     gboolean     keybinding)
10064 {
10065   /* We only start interactive search if we have focus or the columns
10066    * have focus.  If one of our children have focus, we don't want to
10067    * start the search.
10068    */
10069   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10070   GList *list;
10071   gboolean found_focus = FALSE;
10072 
10073   if (!priv->enable_search && !keybinding)
10074     return FALSE;
10075 
10076   if (priv->search_custom_entry_set)
10077     return FALSE;
10078 
10079   if (priv->search_popover &&
10080       gtk_widget_get_visible (priv->search_popover))
10081     return TRUE;
10082 
10083   for (list = priv->columns; list; list = list->next)
10084     {
10085       GtkTreeViewColumn *column;
10086       GtkWidget         *button;
10087 
10088       column = list->data;
10089       if (!gtk_tree_view_column_get_visible (column))
10090 	continue;
10091 
10092       button = gtk_tree_view_column_get_button (column);
10093       if (gtk_widget_has_focus (button))
10094 	{
10095 	  found_focus = TRUE;
10096 	  break;
10097 	}
10098     }
10099 
10100   if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10101     found_focus = TRUE;
10102 
10103   if (!found_focus)
10104     return FALSE;
10105 
10106   if (priv->search_column < 0)
10107     return FALSE;
10108 
10109   gtk_tree_view_ensure_interactive_directory (tree_view);
10110 
10111   if (keybinding)
10112     gtk_editable_set_text (GTK_EDITABLE (priv->search_entry), "");
10113 
10114   /* Grab focus without selecting all the text. */
10115   if (GTK_IS_ENTRY (priv->search_entry))
10116     gtk_entry_grab_focus_without_selecting (GTK_ENTRY (priv->search_entry));
10117   else
10118     gtk_widget_grab_focus (priv->search_entry);
10119 
10120   gtk_popover_popup (GTK_POPOVER (priv->search_popover));
10121   if (priv->search_entry_changed_id == 0)
10122     {
10123       priv->search_entry_changed_id =
10124 	g_signal_connect (priv->search_entry, "changed",
10125 			  G_CALLBACK (gtk_tree_view_search_init),
10126 			  tree_view);
10127     }
10128 
10129   priv->typeselect_flush_timeout =
10130     g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
10131                    (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
10132                    tree_view);
10133   gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout");
10134 
10135   /* search first matching iter */
10136   gtk_tree_view_search_init (priv->search_entry, tree_view);
10137 
10138   return TRUE;
10139 }
10140 
10141 static gboolean
gtk_tree_view_start_interactive_search(GtkTreeView * tree_view)10142 gtk_tree_view_start_interactive_search (GtkTreeView *tree_view)
10143 {
10144   return gtk_tree_view_real_start_interactive_search (tree_view, TRUE);
10145 }
10146 
10147 /* Callbacks */
10148 static void
gtk_tree_view_adjustment_changed(GtkAdjustment * adjustment,GtkTreeView * tree_view)10149 gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
10150 				  GtkTreeView   *tree_view)
10151 {
10152   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10153 
10154   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10155     {
10156       GtkAllocation allocation;
10157       int dy;
10158 
10159       gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
10160       dy = priv->dy - (int) gtk_adjustment_get_value (priv->vadjustment);
10161 
10162       if (dy != 0)
10163         {
10164           /* update our dy and top_row */
10165           priv->dy = (int) gtk_adjustment_get_value (priv->vadjustment);
10166 
10167           update_prelight (tree_view,
10168                            priv->event_last_x,
10169                            priv->event_last_y);
10170 
10171           if (!priv->in_top_row_to_dy)
10172             gtk_tree_view_dy_to_top_row (tree_view);
10173 
10174         }
10175     }
10176 
10177   gtk_widget_queue_allocate (GTK_WIDGET (tree_view));
10178 }
10179 
10180 
10181 
10182 /* Public methods
10183  */
10184 
10185 /**
10186  * gtk_tree_view_new:
10187  *
10188  * Creates a new `GtkTreeView` widget.
10189  *
10190  * Returns: A newly created `GtkTreeView` widget.
10191  **/
10192 GtkWidget *
gtk_tree_view_new(void)10193 gtk_tree_view_new (void)
10194 {
10195   return g_object_new (GTK_TYPE_TREE_VIEW, NULL);
10196 }
10197 
10198 /**
10199  * gtk_tree_view_new_with_model:
10200  * @model: the model.
10201  *
10202  * Creates a new `GtkTreeView` widget with the model initialized to @model.
10203  *
10204  * Returns: A newly created `GtkTreeView` widget.
10205  **/
10206 GtkWidget *
gtk_tree_view_new_with_model(GtkTreeModel * model)10207 gtk_tree_view_new_with_model (GtkTreeModel *model)
10208 {
10209   return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL);
10210 }
10211 
10212 /* Public Accessors
10213  */
10214 
10215 /**
10216  * gtk_tree_view_get_model:
10217  * @tree_view: a `GtkTreeView`
10218  *
10219  * Returns the model the `GtkTreeView` is based on.  Returns %NULL if the
10220  * model is unset.
10221  *
10222  * Returns: (transfer none) (nullable): A `GtkTreeModel`
10223  **/
10224 GtkTreeModel *
gtk_tree_view_get_model(GtkTreeView * tree_view)10225 gtk_tree_view_get_model (GtkTreeView *tree_view)
10226 {
10227   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10228 
10229   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10230 
10231   return priv->model;
10232 }
10233 
10234 /**
10235  * gtk_tree_view_set_model:
10236  * @tree_view: A `GtkTreeView`.
10237  * @model: (nullable): The model.
10238  *
10239  * Sets the model for a `GtkTreeView`.  If the @tree_view already has a model
10240  * set, it will remove it before setting the new model.  If @model is %NULL,
10241  * then it will unset the old model.
10242  **/
10243 void
gtk_tree_view_set_model(GtkTreeView * tree_view,GtkTreeModel * model)10244 gtk_tree_view_set_model (GtkTreeView  *tree_view,
10245 			 GtkTreeModel *model)
10246 {
10247   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10248 
10249   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10250   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
10251 
10252   if (model == priv->model)
10253     return;
10254 
10255   if (priv->scroll_to_path)
10256     {
10257       gtk_tree_row_reference_free (priv->scroll_to_path);
10258       priv->scroll_to_path = NULL;
10259     }
10260 
10261   if (priv->rubber_band_status)
10262     gtk_tree_view_stop_rubber_band (tree_view);
10263 
10264   if (priv->model)
10265     {
10266       GList *tmplist = priv->columns;
10267 
10268       gtk_tree_view_unref_and_check_selection_tree (tree_view, priv->tree);
10269       gtk_tree_view_stop_editing (tree_view, TRUE);
10270 
10271       g_signal_handlers_disconnect_by_func (priv->model,
10272 					    gtk_tree_view_row_changed,
10273 					    tree_view);
10274       g_signal_handlers_disconnect_by_func (priv->model,
10275 					    gtk_tree_view_row_inserted,
10276 					    tree_view);
10277       g_signal_handlers_disconnect_by_func (priv->model,
10278 					    gtk_tree_view_row_has_child_toggled,
10279 					    tree_view);
10280       g_signal_handlers_disconnect_by_func (priv->model,
10281 					    gtk_tree_view_row_deleted,
10282 					    tree_view);
10283       g_signal_handlers_disconnect_by_func (priv->model,
10284 					    gtk_tree_view_rows_reordered,
10285 					    tree_view);
10286 
10287       for (; tmplist; tmplist = tmplist->next)
10288 	_gtk_tree_view_column_unset_model (tmplist->data,
10289 					   priv->model);
10290 
10291       if (priv->tree)
10292 	gtk_tree_view_free_rbtree (tree_view);
10293 
10294       gtk_tree_row_reference_free (priv->drag_dest_row);
10295       priv->drag_dest_row = NULL;
10296       gtk_tree_row_reference_free (priv->anchor);
10297       priv->anchor = NULL;
10298       gtk_tree_row_reference_free (priv->top_row);
10299       priv->top_row = NULL;
10300       gtk_tree_row_reference_free (priv->scroll_to_path);
10301       priv->scroll_to_path = NULL;
10302 
10303       priv->scroll_to_column = NULL;
10304 
10305       g_object_unref (priv->model);
10306 
10307       priv->search_column = -1;
10308       priv->fixed_height_check = 0;
10309       priv->fixed_height = -1;
10310       priv->dy = priv->top_row_dy = 0;
10311     }
10312 
10313   priv->model = model;
10314 
10315   if (priv->model)
10316     {
10317       int i;
10318       GtkTreePath *path;
10319       GtkTreeIter iter;
10320       GtkTreeModelFlags flags;
10321 
10322       if (priv->search_column == -1)
10323 	{
10324 	  for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
10325 	    {
10326 	      GType type = gtk_tree_model_get_column_type (model, i);
10327 
10328 	      if (g_value_type_transformable (type, G_TYPE_STRING))
10329 		{
10330 		  priv->search_column = i;
10331 		  break;
10332 		}
10333 	    }
10334 	}
10335 
10336       g_object_ref (priv->model);
10337       g_signal_connect (priv->model,
10338 			"row-changed",
10339 			G_CALLBACK (gtk_tree_view_row_changed),
10340 			tree_view);
10341       g_signal_connect (priv->model,
10342 			"row-inserted",
10343 			G_CALLBACK (gtk_tree_view_row_inserted),
10344 			tree_view);
10345       g_signal_connect (priv->model,
10346 			"row-has-child-toggled",
10347 			G_CALLBACK (gtk_tree_view_row_has_child_toggled),
10348 			tree_view);
10349       g_signal_connect (priv->model,
10350 			"row-deleted",
10351 			G_CALLBACK (gtk_tree_view_row_deleted),
10352 			tree_view);
10353       g_signal_connect (priv->model,
10354 			"rows-reordered",
10355 			G_CALLBACK (gtk_tree_view_rows_reordered),
10356 			tree_view);
10357 
10358       flags = gtk_tree_model_get_flags (priv->model);
10359       if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
10360         priv->is_list = TRUE;
10361       else
10362         priv->is_list = FALSE;
10363 
10364       path = gtk_tree_path_new_first ();
10365       if (gtk_tree_model_get_iter (priv->model, &iter, path))
10366 	{
10367 	  priv->tree = gtk_tree_rbtree_new ();
10368 	  gtk_tree_view_build_tree (tree_view, priv->tree, &iter, 1, FALSE);
10369 	}
10370       gtk_tree_path_free (path);
10371 
10372       /*  FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */
10373       install_presize_handler (tree_view);
10374     }
10375 
10376   gtk_tree_view_real_set_cursor (tree_view, NULL, CURSOR_INVALID);
10377 
10378   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_MODEL]);
10379 
10380   if (priv->selection)
10381     _gtk_tree_selection_emit_changed (priv->selection);
10382 
10383   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10384     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
10385 }
10386 
10387 /**
10388  * gtk_tree_view_get_selection:
10389  * @tree_view: A `GtkTreeView`.
10390  *
10391  * Gets the `GtkTreeSelection` associated with @tree_view.
10392  *
10393  * Returns: (transfer none): A `GtkTreeSelection` object.
10394  **/
10395 GtkTreeSelection *
gtk_tree_view_get_selection(GtkTreeView * tree_view)10396 gtk_tree_view_get_selection (GtkTreeView *tree_view)
10397 {
10398   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10399 
10400   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10401 
10402   return priv->selection;
10403 }
10404 
10405 static void
gtk_tree_view_do_set_hadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)10406 gtk_tree_view_do_set_hadjustment (GtkTreeView   *tree_view,
10407                                   GtkAdjustment *adjustment)
10408 {
10409   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10410 
10411   if (adjustment && priv->hadjustment == adjustment)
10412     return;
10413 
10414   if (priv->hadjustment != NULL)
10415     {
10416       g_signal_handlers_disconnect_by_func (priv->hadjustment,
10417                                             gtk_tree_view_adjustment_changed,
10418                                             tree_view);
10419       g_object_unref (priv->hadjustment);
10420     }
10421 
10422   if (adjustment == NULL)
10423     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
10424                                      0.0, 0.0, 0.0);
10425 
10426   g_signal_connect (adjustment, "value-changed",
10427                     G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view);
10428   priv->hadjustment = g_object_ref_sink (adjustment);
10429   /* FIXME: Adjustment should probably be populated here with fresh values, but
10430    * internal details are too complicated for me to decipher right now.
10431    */
10432   gtk_tree_view_adjustment_changed (NULL, tree_view);
10433 
10434   g_object_notify (G_OBJECT (tree_view), "hadjustment");
10435 }
10436 
10437 static void
gtk_tree_view_do_set_vadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)10438 gtk_tree_view_do_set_vadjustment (GtkTreeView   *tree_view,
10439                                   GtkAdjustment *adjustment)
10440 {
10441   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10442 
10443   if (adjustment && priv->vadjustment == adjustment)
10444     return;
10445 
10446   if (priv->vadjustment != NULL)
10447     {
10448       g_signal_handlers_disconnect_by_func (priv->vadjustment,
10449                                             gtk_tree_view_adjustment_changed,
10450                                             tree_view);
10451       g_object_unref (priv->vadjustment);
10452     }
10453 
10454   if (adjustment == NULL)
10455     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
10456                                      0.0, 0.0, 0.0);
10457 
10458   g_signal_connect (adjustment, "value-changed",
10459                     G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view);
10460   priv->vadjustment = g_object_ref_sink (adjustment);
10461   /* FIXME: Adjustment should probably be populated here with fresh values, but
10462    * internal details are too complicated for me to decipher right now.
10463    */
10464   gtk_tree_view_adjustment_changed (NULL, tree_view);
10465   g_object_notify (G_OBJECT (tree_view), "vadjustment");
10466 }
10467 
10468 /* Column and header operations */
10469 
10470 /**
10471  * gtk_tree_view_get_headers_visible:
10472  * @tree_view: A `GtkTreeView`.
10473  *
10474  * Returns %TRUE if the headers on the @tree_view are visible.
10475  *
10476  * Returns: Whether the headers are visible or not.
10477  **/
10478 gboolean
gtk_tree_view_get_headers_visible(GtkTreeView * tree_view)10479 gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
10480 {
10481   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10482 
10483   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
10484 
10485   return priv->headers_visible;
10486 }
10487 
10488 /**
10489  * gtk_tree_view_set_headers_visible:
10490  * @tree_view: A `GtkTreeView`.
10491  * @headers_visible: %TRUE if the headers are visible
10492  *
10493  * Sets the visibility state of the headers.
10494  **/
10495 void
gtk_tree_view_set_headers_visible(GtkTreeView * tree_view,gboolean headers_visible)10496 gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
10497 				   gboolean     headers_visible)
10498 {
10499   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10500   GList *list;
10501   GtkTreeViewColumn *column;
10502   GtkWidget *button;
10503 
10504   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10505 
10506   headers_visible = !! headers_visible;
10507 
10508   if (priv->headers_visible == headers_visible)
10509     return;
10510 
10511   priv->headers_visible = headers_visible == TRUE;
10512 
10513   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10514     {
10515       if (headers_visible)
10516 	{
10517           if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
10518             gtk_tree_view_map_buttons (tree_view);
10519  	}
10520       else
10521 	{
10522 
10523 	  for (list = priv->columns; list; list = list->next)
10524 	    {
10525 	      column = list->data;
10526 	      button = gtk_tree_view_column_get_button (column);
10527 
10528               gtk_widget_hide (button);
10529 	      gtk_widget_unmap (button);
10530 	    }
10531 	}
10532     }
10533 
10534   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
10535 
10536   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_VISIBLE]);
10537 }
10538 
10539 /**
10540  * gtk_tree_view_columns_autosize:
10541  * @tree_view: A `GtkTreeView`.
10542  *
10543  * Resizes all columns to their optimal width. Only works after the
10544  * treeview has been realized.
10545  **/
10546 void
gtk_tree_view_columns_autosize(GtkTreeView * tree_view)10547 gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
10548 {
10549   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10550   gboolean dirty = FALSE;
10551   GList *list;
10552   GtkTreeViewColumn *column;
10553 
10554   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10555 
10556   for (list = priv->columns; list; list = list->next)
10557     {
10558       column = list->data;
10559       if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
10560 	continue;
10561       _gtk_tree_view_column_cell_set_dirty (column, TRUE);
10562       dirty = TRUE;
10563     }
10564 
10565   if (dirty)
10566     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
10567 }
10568 
10569 /**
10570  * gtk_tree_view_set_headers_clickable:
10571  * @tree_view: A `GtkTreeView`.
10572  * @setting: %TRUE if the columns are clickable.
10573  *
10574  * Allow the column title buttons to be clicked.
10575  **/
10576 void
gtk_tree_view_set_headers_clickable(GtkTreeView * tree_view,gboolean setting)10577 gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view,
10578 				     gboolean   setting)
10579 {
10580   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10581   GList *list;
10582   gboolean changed = FALSE;
10583 
10584   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10585 
10586   for (list = priv->columns; list; list = list->next)
10587     {
10588       if (gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)) != setting)
10589         {
10590           gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting);
10591           changed = TRUE;
10592         }
10593     }
10594 
10595   if (changed)
10596     g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_CLICKABLE]);
10597 }
10598 
10599 
10600 /**
10601  * gtk_tree_view_get_headers_clickable:
10602  * @tree_view: A `GtkTreeView`.
10603  *
10604  * Returns whether all header columns are clickable.
10605  *
10606  * Returns: %TRUE if all header columns are clickable, otherwise %FALSE
10607  **/
10608 gboolean
gtk_tree_view_get_headers_clickable(GtkTreeView * tree_view)10609 gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view)
10610 {
10611   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10612   GList *list;
10613 
10614   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
10615 
10616   for (list = priv->columns; list; list = list->next)
10617     if (!gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)))
10618       return FALSE;
10619 
10620   return TRUE;
10621 }
10622 
10623 /**
10624  * gtk_tree_view_set_activate_on_single_click:
10625  * @tree_view: a `GtkTreeView`
10626  * @single: %TRUE to emit row-activated on a single click
10627  *
10628  * Cause the `GtkTreeView`::row-activated signal to be emitted
10629  * on a single click instead of a double click.
10630  **/
10631 void
gtk_tree_view_set_activate_on_single_click(GtkTreeView * tree_view,gboolean single)10632 gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view,
10633                                             gboolean     single)
10634 {
10635   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10636 
10637   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10638 
10639   single = single != FALSE;
10640 
10641   if (priv->activate_on_single_click == single)
10642     return;
10643 
10644   priv->activate_on_single_click = single;
10645   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK]);
10646 }
10647 
10648 /**
10649  * gtk_tree_view_get_activate_on_single_click:
10650  * @tree_view: a `GtkTreeView`
10651  *
10652  * Gets the setting set by gtk_tree_view_set_activate_on_single_click().
10653  *
10654  * Returns: %TRUE if row-activated will be emitted on a single click
10655  **/
10656 gboolean
gtk_tree_view_get_activate_on_single_click(GtkTreeView * tree_view)10657 gtk_tree_view_get_activate_on_single_click (GtkTreeView *tree_view)
10658 {
10659   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10660 
10661   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
10662 
10663   return priv->activate_on_single_click;
10664 }
10665 
10666 /* Public Column functions
10667  */
10668 
10669 /**
10670  * gtk_tree_view_append_column:
10671  * @tree_view: A `GtkTreeView`.
10672  * @column: The `GtkTreeViewColumn` to add.
10673  *
10674  * Appends @column to the list of columns. If @tree_view has “fixed_height”
10675  * mode enabled, then @column must have its “sizing” property set to be
10676  * GTK_TREE_VIEW_COLUMN_FIXED.
10677  *
10678  * Returns: The number of columns in @tree_view after appending.
10679  **/
10680 int
gtk_tree_view_append_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)10681 gtk_tree_view_append_column (GtkTreeView       *tree_view,
10682 			     GtkTreeViewColumn *column)
10683 {
10684   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
10685   g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
10686   g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
10687 
10688   return gtk_tree_view_insert_column (tree_view, column, -1);
10689 }
10690 
10691 /**
10692  * gtk_tree_view_remove_column:
10693  * @tree_view: A `GtkTreeView`.
10694  * @column: The `GtkTreeViewColumn` to remove.
10695  *
10696  * Removes @column from @tree_view.
10697  *
10698  * Returns: The number of columns in @tree_view after removing.
10699  **/
10700 int
gtk_tree_view_remove_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)10701 gtk_tree_view_remove_column (GtkTreeView       *tree_view,
10702                              GtkTreeViewColumn *column)
10703 {
10704   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10705 
10706   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
10707   g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
10708   g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view), -1);
10709 
10710   if (priv->focus_column == column)
10711     _gtk_tree_view_set_focus_column (tree_view, NULL);
10712 
10713   if (priv->edited_column == column)
10714     {
10715       gtk_tree_view_stop_editing (tree_view, TRUE);
10716 
10717       /* no need to, but just to be sure ... */
10718       priv->edited_column = NULL;
10719     }
10720 
10721   if (priv->expander_column == column)
10722     priv->expander_column = NULL;
10723 
10724   g_signal_handlers_disconnect_by_func (column,
10725                                         G_CALLBACK (column_sizing_notify),
10726                                         tree_view);
10727 
10728   _gtk_tree_view_column_unset_tree_view (column);
10729 
10730   priv->columns = g_list_remove (priv->columns, column);
10731   priv->n_columns--;
10732 
10733   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10734     {
10735       GList *list;
10736 
10737       for (list = priv->columns; list; list = list->next)
10738 	{
10739 	  GtkTreeViewColumn *tmp_column;
10740 
10741 	  tmp_column = GTK_TREE_VIEW_COLUMN (list->data);
10742 	  if (gtk_tree_view_column_get_visible (tmp_column))
10743             _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE);
10744 	}
10745 
10746       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
10747     }
10748 
10749   g_object_unref (column);
10750   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
10751 
10752   return priv->n_columns;
10753 }
10754 
10755 /**
10756  * gtk_tree_view_insert_column:
10757  * @tree_view: A `GtkTreeView`.
10758  * @column: The `GtkTreeViewColumn` to be inserted.
10759  * @position: The position to insert @column in.
10760  *
10761  * This inserts the @column into the @tree_view at @position.  If @position is
10762  * -1, then the column is inserted at the end. If @tree_view has
10763  * “fixed_height” mode enabled, then @column must have its “sizing” property
10764  * set to be GTK_TREE_VIEW_COLUMN_FIXED.
10765  *
10766  * Returns: The number of columns in @tree_view after insertion.
10767  **/
10768 int
gtk_tree_view_insert_column(GtkTreeView * tree_view,GtkTreeViewColumn * column,int position)10769 gtk_tree_view_insert_column (GtkTreeView       *tree_view,
10770                              GtkTreeViewColumn *column,
10771                              int                position)
10772 {
10773   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10774 
10775   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
10776   g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
10777   g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
10778 
10779   if (priv->fixed_height_mode)
10780     g_return_val_if_fail (gtk_tree_view_column_get_sizing (column)
10781                           == GTK_TREE_VIEW_COLUMN_FIXED, -1);
10782 
10783   if (position < 0 || position > priv->n_columns)
10784     position = priv->n_columns;
10785 
10786   g_object_ref_sink (column);
10787 
10788   g_signal_connect (column, "notify::sizing",
10789                     G_CALLBACK (column_sizing_notify), tree_view);
10790 
10791   priv->columns = g_list_insert (priv->columns,
10792 					    column, position);
10793   priv->n_columns++;
10794 
10795   _gtk_tree_view_column_set_tree_view (column, tree_view);
10796 
10797   /* XXX: We need to reparent the node into the header, somebody make that a real widget */
10798   gtk_css_node_set_parent (gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)), NULL);
10799   gtk_tree_view_update_button_position (tree_view, column);
10800 
10801   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10802     {
10803       GList *list;
10804 
10805       _gtk_tree_view_column_realize_button (column);
10806 
10807       for (list = priv->columns; list; list = list->next)
10808 	{
10809 	  column = GTK_TREE_VIEW_COLUMN (list->data);
10810 	  if (gtk_tree_view_column_get_visible (column))
10811             _gtk_tree_view_column_cell_set_dirty (column, TRUE);
10812 	}
10813       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
10814     }
10815 
10816   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
10817 
10818   return priv->n_columns;
10819 }
10820 
10821 /**
10822  * gtk_tree_view_insert_column_with_attributes:
10823  * @tree_view: A `GtkTreeView`
10824  * @position: The position to insert the new column in
10825  * @title: The title to set the header to
10826  * @cell: The `GtkCellRenderer`
10827  * @...: A %NULL-terminated list of attributes
10828  *
10829  * Creates a new `GtkTreeViewColumn` and inserts it into the @tree_view at
10830  * @position.  If @position is -1, then the newly created column is inserted at
10831  * the end.  The column is initialized with the attributes given. If @tree_view
10832  * has “fixed_height” mode enabled, then the new column will have its sizing
10833  * property set to be GTK_TREE_VIEW_COLUMN_FIXED.
10834  *
10835  * Returns: The number of columns in @tree_view after insertion.
10836  **/
10837 int
gtk_tree_view_insert_column_with_attributes(GtkTreeView * tree_view,int position,const char * title,GtkCellRenderer * cell,...)10838 gtk_tree_view_insert_column_with_attributes (GtkTreeView     *tree_view,
10839 					     int              position,
10840 					     const char      *title,
10841 					     GtkCellRenderer *cell,
10842 					     ...)
10843 {
10844   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10845   GtkTreeViewColumn *column;
10846   char *attribute;
10847   va_list args;
10848   int column_id;
10849 
10850   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
10851 
10852   column = gtk_tree_view_column_new ();
10853   if (priv->fixed_height_mode)
10854     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
10855 
10856   gtk_tree_view_column_set_title (column, title);
10857   gtk_tree_view_column_pack_start (column, cell, TRUE);
10858 
10859   va_start (args, cell);
10860 
10861   attribute = va_arg (args, char *);
10862 
10863   while (attribute != NULL)
10864     {
10865       column_id = va_arg (args, int);
10866       gtk_tree_view_column_add_attribute (column, cell, attribute, column_id);
10867       attribute = va_arg (args, char *);
10868     }
10869 
10870   va_end (args);
10871 
10872   return gtk_tree_view_insert_column (tree_view, column, position);
10873 }
10874 
10875 /**
10876  * gtk_tree_view_insert_column_with_data_func:
10877  * @tree_view: a `GtkTreeView`
10878  * @position: Position to insert, -1 for append
10879  * @title: column title
10880  * @cell: cell renderer for column
10881  * @func: function to set attributes of cell renderer
10882  * @data: data for @func
10883  * @dnotify: destroy notifier for @data
10884  *
10885  * Convenience function that inserts a new column into the `GtkTreeView`
10886  * with the given cell renderer and a `GtkTreeCellDataFunc` to set cell renderer
10887  * attributes (normally using data from the model). See also
10888  * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start().
10889  * If @tree_view has “fixed_height” mode enabled, then the new column will have its
10890  * “sizing” property set to be GTK_TREE_VIEW_COLUMN_FIXED.
10891  *
10892  * Returns: number of columns in the tree view post-insert
10893  **/
10894 int
gtk_tree_view_insert_column_with_data_func(GtkTreeView * tree_view,int position,const char * title,GtkCellRenderer * cell,GtkTreeCellDataFunc func,gpointer data,GDestroyNotify dnotify)10895 gtk_tree_view_insert_column_with_data_func  (GtkTreeView               *tree_view,
10896                                              int                        position,
10897                                              const char                *title,
10898                                              GtkCellRenderer           *cell,
10899                                              GtkTreeCellDataFunc        func,
10900                                              gpointer                   data,
10901                                              GDestroyNotify             dnotify)
10902 {
10903   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10904   GtkTreeViewColumn *column;
10905 
10906   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
10907 
10908   column = gtk_tree_view_column_new ();
10909   if (priv->fixed_height_mode)
10910     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
10911 
10912   gtk_tree_view_column_set_title (column, title);
10913   gtk_tree_view_column_pack_start (column, cell, TRUE);
10914   gtk_tree_view_column_set_cell_data_func (column, cell, func, data, dnotify);
10915 
10916   return gtk_tree_view_insert_column (tree_view, column, position);
10917 }
10918 
10919 /**
10920  * gtk_tree_view_get_n_columns:
10921  * @tree_view: a `GtkTreeView`
10922  *
10923  * Queries the number of columns in the given @tree_view.
10924  *
10925  * Returns: The number of columns in the @tree_view
10926  **/
10927 guint
gtk_tree_view_get_n_columns(GtkTreeView * tree_view)10928 gtk_tree_view_get_n_columns (GtkTreeView *tree_view)
10929 {
10930   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10931 
10932   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
10933 
10934   return priv->n_columns;
10935 }
10936 
10937 /**
10938  * gtk_tree_view_get_column:
10939  * @tree_view: A `GtkTreeView`.
10940  * @n: The position of the column, counting from 0.
10941  *
10942  * Gets the `GtkTreeViewColumn` at the given position in the #tree_view.
10943  *
10944  * Returns: (nullable) (transfer none): The `GtkTreeViewColumn`, or %NULL if the
10945  * position is outside the range of columns.
10946  **/
10947 GtkTreeViewColumn *
gtk_tree_view_get_column(GtkTreeView * tree_view,int n)10948 gtk_tree_view_get_column (GtkTreeView *tree_view,
10949 			  int          n)
10950 {
10951   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10952 
10953   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10954 
10955   if (n < 0 || n >= priv->n_columns)
10956     return NULL;
10957 
10958   if (priv->columns == NULL)
10959     return NULL;
10960 
10961   return GTK_TREE_VIEW_COLUMN (g_list_nth (priv->columns, n)->data);
10962 }
10963 
10964 /**
10965  * gtk_tree_view_get_columns:
10966  * @tree_view: A `GtkTreeView`
10967  *
10968  * Returns a `GList` of all the `GtkTreeViewColumn`s currently in @tree_view.
10969  * The returned list must be freed with g_list_free ().
10970  *
10971  * Returns: (element-type GtkTreeViewColumn) (transfer container): A list of `GtkTreeViewColumn`s
10972  **/
10973 GList *
gtk_tree_view_get_columns(GtkTreeView * tree_view)10974 gtk_tree_view_get_columns (GtkTreeView *tree_view)
10975 {
10976   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10977 
10978   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10979 
10980   return g_list_copy (priv->columns);
10981 }
10982 
10983 /**
10984  * gtk_tree_view_move_column_after:
10985  * @tree_view: A `GtkTreeView`
10986  * @column: The `GtkTreeViewColumn` to be moved.
10987  * @base_column: (nullable): The `GtkTreeViewColumn` to be moved relative to
10988  *
10989  * Moves @column to be after to @base_column.  If @base_column is %NULL, then
10990  * @column is placed in the first position.
10991  **/
10992 void
gtk_tree_view_move_column_after(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkTreeViewColumn * base_column)10993 gtk_tree_view_move_column_after (GtkTreeView       *tree_view,
10994 				 GtkTreeViewColumn *column,
10995 				 GtkTreeViewColumn *base_column)
10996 {
10997   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
10998   GList *column_list_el, *base_el = NULL;
10999 
11000   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11001 
11002   column_list_el = g_list_find (priv->columns, column);
11003   g_return_if_fail (column_list_el != NULL);
11004 
11005   if (base_column)
11006     {
11007       base_el = g_list_find (priv->columns, base_column);
11008       g_return_if_fail (base_el != NULL);
11009     }
11010 
11011   if (column_list_el->prev == base_el)
11012     return;
11013 
11014   priv->columns = g_list_remove_link (priv->columns, column_list_el);
11015   if (base_el == NULL)
11016     {
11017       column_list_el->prev = NULL;
11018       column_list_el->next = priv->columns;
11019       if (column_list_el->next)
11020 	column_list_el->next->prev = column_list_el;
11021       priv->columns = column_list_el;
11022     }
11023   else
11024     {
11025       column_list_el->prev = base_el;
11026       column_list_el->next = base_el->next;
11027       if (column_list_el->next)
11028 	column_list_el->next->prev = column_list_el;
11029       base_el->next = column_list_el;
11030     }
11031 
11032   gtk_tree_view_update_button_position (tree_view, column);
11033 
11034   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11035 
11036   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
11037 }
11038 
11039 /**
11040  * gtk_tree_view_set_expander_column:
11041  * @tree_view: A `GtkTreeView`
11042  * @column: (nullable): %NULL, or the column to draw the expander arrow at.
11043  *
11044  * Sets the column to draw the expander arrow at. It must be in @tree_view.
11045  * If @column is %NULL, then the expander arrow is always at the first
11046  * visible column.
11047  *
11048  * If you do not want expander arrow to appear in your tree, set the
11049  * expander column to a hidden column.
11050  **/
11051 void
gtk_tree_view_set_expander_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)11052 gtk_tree_view_set_expander_column (GtkTreeView       *tree_view,
11053                                    GtkTreeViewColumn *column)
11054 {
11055   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11056 
11057   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11058   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
11059   g_return_if_fail (column == NULL || gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view));
11060 
11061   if (priv->expander_column != column)
11062     {
11063       priv->expander_column = column;
11064       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_EXPANDER_COLUMN]);
11065     }
11066 }
11067 
11068 /**
11069  * gtk_tree_view_get_expander_column:
11070  * @tree_view: A `GtkTreeView`
11071  *
11072  * Returns the column that is the current expander column,
11073  * or %NULL if none has been set.
11074  * This column has the expander arrow drawn next to it.
11075  *
11076  * Returns: (transfer none) (nullable): The expander column.
11077  **/
11078 GtkTreeViewColumn *
gtk_tree_view_get_expander_column(GtkTreeView * tree_view)11079 gtk_tree_view_get_expander_column (GtkTreeView *tree_view)
11080 {
11081   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11082   GList *list;
11083 
11084   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11085 
11086   for (list = priv->columns; list; list = list->next)
11087     if (gtk_tree_view_is_expander_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data)))
11088       return (GtkTreeViewColumn *) list->data;
11089   return NULL;
11090 }
11091 
11092 
11093 /**
11094  * gtk_tree_view_set_column_drag_function:
11095  * @tree_view: A `GtkTreeView`.
11096  * @func: (nullable): A function to determine which columns are reorderable
11097  * @user_data: (closure): User data to be passed to @func
11098  * @destroy: (nullable): Destroy notifier for @user_data
11099  *
11100  * Sets a user function for determining where a column may be dropped when
11101  * dragged.  This function is called on every column pair in turn at the
11102  * beginning of a column drag to determine where a drop can take place.  The
11103  * arguments passed to @func are: the @tree_view, the `GtkTreeViewColumn` being
11104  * dragged, the two `GtkTreeViewColumn`s determining the drop spot, and
11105  * @user_data.  If either of the `GtkTreeViewColumn` arguments for the drop spot
11106  * are %NULL, then they indicate an edge.  If @func is set to be %NULL, then
11107  * @tree_view reverts to the default behavior of allowing all columns to be
11108  * dropped everywhere.
11109  **/
11110 void
gtk_tree_view_set_column_drag_function(GtkTreeView * tree_view,GtkTreeViewColumnDropFunc func,gpointer user_data,GDestroyNotify destroy)11111 gtk_tree_view_set_column_drag_function (GtkTreeView               *tree_view,
11112 					GtkTreeViewColumnDropFunc  func,
11113 					gpointer                   user_data,
11114 					GDestroyNotify             destroy)
11115 {
11116   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11117 
11118   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11119 
11120   if (priv->column_drop_func_data_destroy)
11121     priv->column_drop_func_data_destroy (priv->column_drop_func_data);
11122 
11123   priv->column_drop_func = func;
11124   priv->column_drop_func_data = user_data;
11125   priv->column_drop_func_data_destroy = destroy;
11126 }
11127 
11128 /**
11129  * gtk_tree_view_scroll_to_point:
11130  * @tree_view: a `GtkTreeView`
11131  * @tree_x: X coordinate of new top-left pixel of visible area, or -1
11132  * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
11133  *
11134  * Scrolls the tree view such that the top-left corner of the visible
11135  * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
11136  * in tree coordinates.  The @tree_view must be realized before
11137  * this function is called.  If it isn't, you probably want to be
11138  * using gtk_tree_view_scroll_to_cell().
11139  *
11140  * If either @tree_x or @tree_y are -1, then that direction isn’t scrolled.
11141  **/
11142 void
gtk_tree_view_scroll_to_point(GtkTreeView * tree_view,int tree_x,int tree_y)11143 gtk_tree_view_scroll_to_point (GtkTreeView *tree_view,
11144                                int          tree_x,
11145                                int          tree_y)
11146 {
11147   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11148   GtkAdjustment *hadj;
11149   GtkAdjustment *vadj;
11150 
11151   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11152   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
11153 
11154   hadj = priv->hadjustment;
11155   vadj = priv->vadjustment;
11156 
11157   if (tree_x != -1)
11158     gtk_adjustment_animate_to_value (hadj, tree_x);
11159   if (tree_y != -1)
11160     gtk_adjustment_animate_to_value (vadj, tree_y);
11161 }
11162 
11163 /**
11164  * gtk_tree_view_scroll_to_cell:
11165  * @tree_view: A `GtkTreeView`.
11166  * @path: (nullable): The path of the row to move to
11167  * @column: (nullable): The `GtkTreeViewColumn` to move horizontally to
11168  * @use_align: whether to use alignment arguments, or %FALSE.
11169  * @row_align: The vertical alignment of the row specified by @path.
11170  * @col_align: The horizontal alignment of the column specified by @column.
11171  *
11172  * Moves the alignments of @tree_view to the position specified by @column and
11173  * @path.  If @column is %NULL, then no horizontal scrolling occurs.  Likewise,
11174  * if @path is %NULL no vertical scrolling occurs.  At a minimum, one of @column
11175  * or @path need to be non-%NULL.  @row_align determines where the row is
11176  * placed, and @col_align determines where @column is placed.  Both are expected
11177  * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
11178  * right/bottom alignment, 0.5 means center.
11179  *
11180  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
11181  * tree does the minimum amount of work to scroll the cell onto the screen.
11182  * This means that the cell will be scrolled to the edge closest to its current
11183  * position.  If the cell is currently visible on the screen, nothing is done.
11184  *
11185  * This function only works if the model is set, and @path is a valid row on the
11186  * model.  If the model changes before the @tree_view is realized, the centered
11187  * path will be modified to reflect this change.
11188  **/
11189 void
gtk_tree_view_scroll_to_cell(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gboolean use_align,float row_align,float col_align)11190 gtk_tree_view_scroll_to_cell (GtkTreeView       *tree_view,
11191                               GtkTreePath       *path,
11192                               GtkTreeViewColumn *column,
11193 			      gboolean           use_align,
11194                               float              row_align,
11195                               float              col_align)
11196 {
11197   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11198 
11199   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11200   g_return_if_fail (priv->model != NULL);
11201   g_return_if_fail (priv->tree != NULL);
11202   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
11203   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
11204   g_return_if_fail (path != NULL || column != NULL);
11205 
11206   row_align = CLAMP (row_align, 0.0, 1.0);
11207   col_align = CLAMP (col_align, 0.0, 1.0);
11208 
11209 
11210   /* Note: Despite the benefits that come from having one code path for the
11211    * scrolling code, we short-circuit validate_visible_area's immplementation as
11212    * it is much slower than just going to the point.
11213    */
11214   if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
11215       !gtk_widget_get_realized (GTK_WIDGET (tree_view)) ||
11216       _gtk_widget_get_alloc_needed (GTK_WIDGET (tree_view)) ||
11217       GTK_TREE_RBNODE_FLAG_SET (priv->tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID))
11218     {
11219       if (priv->scroll_to_path)
11220 	gtk_tree_row_reference_free (priv->scroll_to_path);
11221 
11222       priv->scroll_to_path = NULL;
11223       priv->scroll_to_column = NULL;
11224 
11225       if (path)
11226 	priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path);
11227       if (column)
11228 	priv->scroll_to_column = column;
11229       priv->scroll_to_use_align = use_align;
11230       priv->scroll_to_row_align = row_align;
11231       priv->scroll_to_col_align = col_align;
11232 
11233       install_presize_handler (tree_view);
11234     }
11235   else
11236     {
11237       GdkRectangle cell_rect;
11238       GdkRectangle vis_rect;
11239       int dest_x, dest_y;
11240 
11241       gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect);
11242       gtk_tree_view_get_visible_rect (tree_view, &vis_rect);
11243 
11244       cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, cell_rect.y);
11245 
11246       dest_x = vis_rect.x;
11247       dest_y = vis_rect.y;
11248 
11249       if (column)
11250 	{
11251 	  if (use_align)
11252 	    {
11253 	      dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
11254 	    }
11255 	  else
11256 	    {
11257 	      if (cell_rect.x < vis_rect.x)
11258 		dest_x = cell_rect.x;
11259 	      if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
11260 		dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
11261 	    }
11262 	}
11263 
11264       if (path)
11265 	{
11266 	  if (use_align)
11267 	    {
11268 	      dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
11269 	      dest_y = MAX (dest_y, 0);
11270 	    }
11271 	  else
11272 	    {
11273 	      if (cell_rect.y < vis_rect.y)
11274 		dest_y = cell_rect.y;
11275 	      if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
11276 		dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
11277 	    }
11278 	}
11279 
11280       gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y);
11281     }
11282 }
11283 
11284 /**
11285  * gtk_tree_view_row_activated:
11286  * @tree_view: A `GtkTreeView`
11287  * @path: The `GtkTreePath` to be activated.
11288  * @column: (nullable): The `GtkTreeViewColumn` to be activated.
11289  *
11290  * Activates the cell determined by @path and @column.
11291  **/
11292 void
gtk_tree_view_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column)11293 gtk_tree_view_row_activated (GtkTreeView       *tree_view,
11294 			     GtkTreePath       *path,
11295 			     GtkTreeViewColumn *column)
11296 {
11297   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11298 
11299   g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
11300 }
11301 
11302 
11303 static void
gtk_tree_view_expand_all_emission_helper(GtkTreeRBTree * tree,GtkTreeRBNode * node,gpointer data)11304 gtk_tree_view_expand_all_emission_helper (GtkTreeRBTree *tree,
11305                                           GtkTreeRBNode *node,
11306                                           gpointer       data)
11307 {
11308   GtkTreeView *tree_view = data;
11309   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11310 
11311   if ((node->flags & GTK_TREE_RBNODE_IS_PARENT) == GTK_TREE_RBNODE_IS_PARENT &&
11312       node->children)
11313     {
11314       GtkTreePath *path;
11315       GtkTreeIter iter;
11316 
11317       path = _gtk_tree_path_new_from_rbtree (tree, node);
11318       gtk_tree_model_get_iter (priv->model, &iter, path);
11319 
11320       g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
11321 
11322       gtk_tree_path_free (path);
11323     }
11324 
11325   if (node->children)
11326     gtk_tree_rbtree_traverse (node->children,
11327                           node->children->root,
11328                           G_PRE_ORDER,
11329                           gtk_tree_view_expand_all_emission_helper,
11330                           tree_view);
11331 }
11332 
11333 /**
11334  * gtk_tree_view_expand_all:
11335  * @tree_view: A `GtkTreeView`.
11336  *
11337  * Recursively expands all nodes in the @tree_view.
11338  **/
11339 void
gtk_tree_view_expand_all(GtkTreeView * tree_view)11340 gtk_tree_view_expand_all (GtkTreeView *tree_view)
11341 {
11342   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11343   GtkTreePath *path;
11344   GtkTreeRBTree *tree;
11345   GtkTreeRBNode *node;
11346 
11347   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11348 
11349   if (priv->tree == NULL)
11350     return;
11351 
11352   path = gtk_tree_path_new_first ();
11353   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
11354 
11355   while (node)
11356     {
11357       gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE);
11358       node = gtk_tree_rbtree_next (tree, node);
11359       gtk_tree_path_next (path);
11360   }
11361 
11362   gtk_tree_path_free (path);
11363 }
11364 
11365 /**
11366  * gtk_tree_view_collapse_all:
11367  * @tree_view: A `GtkTreeView`.
11368  *
11369  * Recursively collapses all visible, expanded nodes in @tree_view.
11370  **/
11371 void
gtk_tree_view_collapse_all(GtkTreeView * tree_view)11372 gtk_tree_view_collapse_all (GtkTreeView *tree_view)
11373 {
11374   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11375   GtkTreeRBTree *tree;
11376   GtkTreeRBNode *node;
11377   GtkTreePath *path;
11378   int *indices;
11379 
11380   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11381 
11382   if (priv->tree == NULL)
11383     return;
11384 
11385   path = gtk_tree_path_new ();
11386   gtk_tree_path_down (path);
11387   indices = gtk_tree_path_get_indices (path);
11388 
11389   tree = priv->tree;
11390   node = gtk_tree_rbtree_first (tree);
11391 
11392   while (node)
11393     {
11394       if (node->children)
11395 	gtk_tree_view_real_collapse_row (tree_view, path, tree, node);
11396       indices[0]++;
11397       node = gtk_tree_rbtree_next (tree, node);
11398     }
11399 
11400   gtk_tree_path_free (path);
11401 }
11402 
11403 /**
11404  * gtk_tree_view_expand_to_path:
11405  * @tree_view: A `GtkTreeView`.
11406  * @path: path to a row.
11407  *
11408  * Expands the row at @path. This will also expand all parent rows of
11409  * @path as necessary.
11410  **/
11411 void
gtk_tree_view_expand_to_path(GtkTreeView * tree_view,GtkTreePath * path)11412 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
11413 			      GtkTreePath *path)
11414 {
11415   int i, depth;
11416   int *indices;
11417   GtkTreePath *tmp;
11418 
11419   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11420   g_return_if_fail (path != NULL);
11421 
11422   depth = gtk_tree_path_get_depth (path);
11423   indices = gtk_tree_path_get_indices (path);
11424 
11425   tmp = gtk_tree_path_new ();
11426   g_return_if_fail (tmp != NULL);
11427 
11428   for (i = 0; i < depth; i++)
11429     {
11430       gtk_tree_path_append_index (tmp, indices[i]);
11431       gtk_tree_view_expand_row (tree_view, tmp, FALSE);
11432     }
11433 
11434   gtk_tree_path_free (tmp);
11435 }
11436 
11437 /* FIXME the bool return values for expand_row and collapse_row are
11438  * not analogous; they should be TRUE if the row had children and
11439  * was not already in the requested state.
11440  */
11441 
11442 
11443 static gboolean
gtk_tree_view_real_expand_row(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeRBTree * tree,GtkTreeRBNode * node,gboolean open_all)11444 gtk_tree_view_real_expand_row (GtkTreeView   *tree_view,
11445 			       GtkTreePath   *path,
11446 			       GtkTreeRBTree *tree,
11447 			       GtkTreeRBNode *node,
11448 			       gboolean       open_all)
11449 {
11450   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11451   GtkTreeIter iter;
11452   GtkTreeIter temp;
11453   gboolean expand;
11454 
11455   remove_auto_expand_timeout (tree_view);
11456 
11457   if (node->children && !open_all)
11458     return FALSE;
11459 
11460   if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT))
11461     return FALSE;
11462 
11463   gtk_tree_model_get_iter (priv->model, &iter, path);
11464   if (! gtk_tree_model_iter_has_child (priv->model, &iter))
11465     return FALSE;
11466 
11467 
11468    if (node->children && open_all)
11469     {
11470       gboolean retval = FALSE;
11471       GtkTreePath *tmp_path = gtk_tree_path_copy (path);
11472 
11473       gtk_tree_path_append_index (tmp_path, 0);
11474       tree = node->children;
11475       node = gtk_tree_rbtree_first (tree);
11476       /* try to expand the children */
11477       do
11478         {
11479          gboolean t;
11480 	 t = gtk_tree_view_real_expand_row (tree_view, tmp_path, tree, node,
11481                                             TRUE);
11482          if (t)
11483            retval = TRUE;
11484 
11485          gtk_tree_path_next (tmp_path);
11486 	 node = gtk_tree_rbtree_next (tree, node);
11487        }
11488       while (node != NULL);
11489 
11490       gtk_tree_path_free (tmp_path);
11491 
11492       return retval;
11493     }
11494 
11495   g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand);
11496 
11497   if (!gtk_tree_model_iter_has_child (priv->model, &iter))
11498     return FALSE;
11499 
11500   if (expand)
11501     return FALSE;
11502 
11503   node->children = gtk_tree_rbtree_new ();
11504   node->children->parent_tree = tree;
11505   node->children->parent_node = node;
11506 
11507   gtk_tree_model_iter_children (priv->model, &temp, &iter);
11508 
11509   gtk_tree_view_build_tree (tree_view,
11510 			    node->children,
11511 			    &temp,
11512 			    gtk_tree_path_get_depth (path) + 1,
11513 			    open_all);
11514 
11515   install_presize_handler (tree_view);
11516 
11517   g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
11518   if (open_all && node->children)
11519     {
11520       gtk_tree_rbtree_traverse (node->children,
11521                             node->children->root,
11522                             G_PRE_ORDER,
11523                             gtk_tree_view_expand_all_emission_helper,
11524                             tree_view);
11525     }
11526   return TRUE;
11527 }
11528 
11529 
11530 /**
11531  * gtk_tree_view_expand_row:
11532  * @tree_view: a `GtkTreeView`
11533  * @path: path to a row
11534  * @open_all: whether to recursively expand, or just expand immediate children
11535  *
11536  * Opens the row so its children are visible.
11537  *
11538  * Returns: %TRUE if the row existed and had children
11539  **/
11540 gboolean
gtk_tree_view_expand_row(GtkTreeView * tree_view,GtkTreePath * path,gboolean open_all)11541 gtk_tree_view_expand_row (GtkTreeView *tree_view,
11542 			  GtkTreePath *path,
11543 			  gboolean     open_all)
11544 {
11545   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11546   GtkTreeRBTree *tree;
11547   GtkTreeRBNode *node;
11548 
11549   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11550   g_return_val_if_fail (priv->model != NULL, FALSE);
11551   g_return_val_if_fail (path != NULL, FALSE);
11552 
11553   if (_gtk_tree_view_find_node (tree_view,
11554 				path,
11555 				&tree,
11556 				&node))
11557     return FALSE;
11558 
11559   if (tree != NULL)
11560     return gtk_tree_view_real_expand_row (tree_view, path, tree, node, open_all);
11561   else
11562     return FALSE;
11563 }
11564 
11565 static gboolean
gtk_tree_view_real_collapse_row(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeRBTree * tree,GtkTreeRBNode * node)11566 gtk_tree_view_real_collapse_row (GtkTreeView   *tree_view,
11567 				 GtkTreePath   *path,
11568 				 GtkTreeRBTree *tree,
11569 				 GtkTreeRBNode *node)
11570 {
11571   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11572   GtkTreeIter iter;
11573   GtkTreeIter children;
11574   gboolean collapse;
11575   GList *list;
11576   gboolean selection_changed, cursor_changed;
11577 
11578   remove_auto_expand_timeout (tree_view);
11579 
11580   if (node->children == NULL)
11581     return FALSE;
11582   gtk_tree_model_get_iter (priv->model, &iter, path);
11583 
11584   g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse);
11585 
11586   if (collapse)
11587     return FALSE;
11588 
11589   /* if the prelighted node is a child of us, we want to unprelight it.  We have
11590    * a chance to prelight the correct node below */
11591 
11592   if (priv->prelight_tree)
11593     {
11594       GtkTreeRBTree *parent_tree;
11595       GtkTreeRBNode *parent_node;
11596 
11597       parent_tree = priv->prelight_tree->parent_tree;
11598       parent_node = priv->prelight_tree->parent_node;
11599       while (parent_tree)
11600 	{
11601 	  if (parent_tree == tree && parent_node == node)
11602 	    {
11603 	      ensure_unprelighted (tree_view);
11604 	      break;
11605 	    }
11606 	  parent_node = parent_tree->parent_node;
11607 	  parent_tree = parent_tree->parent_tree;
11608 	}
11609     }
11610 
11611   TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_children (priv->model, &children, &iter), FALSE);
11612 
11613   for (list = priv->columns; list; list = list->next)
11614     {
11615       GtkTreeViewColumn *column = list->data;
11616 
11617       if (gtk_tree_view_column_get_visible (column) == FALSE)
11618 	continue;
11619       if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
11620 	_gtk_tree_view_column_cell_set_dirty (column, TRUE);
11621     }
11622 
11623   if (priv->cursor_node)
11624     {
11625       cursor_changed = (node->children == priv->cursor_tree)
11626                        || gtk_tree_rbtree_contains (node->children, priv->cursor_tree);
11627     }
11628   else
11629     cursor_changed = FALSE;
11630 
11631   if (gtk_tree_row_reference_valid (priv->anchor))
11632     {
11633       GtkTreePath *anchor_path = gtk_tree_row_reference_get_path (priv->anchor);
11634       if (gtk_tree_path_is_ancestor (path, anchor_path))
11635 	{
11636 	  gtk_tree_row_reference_free (priv->anchor);
11637 	  priv->anchor = NULL;
11638 	}
11639       gtk_tree_path_free (anchor_path);
11640     }
11641 
11642   selection_changed = gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children);
11643 
11644   /* Stop a pending double click */
11645   gtk_event_controller_reset (GTK_EVENT_CONTROLLER (priv->click_gesture));
11646 
11647   gtk_tree_rbtree_remove (node->children);
11648 
11649   if (cursor_changed)
11650     gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CURSOR_INVALID);
11651   if (selection_changed)
11652     g_signal_emit_by_name (priv->selection, "changed");
11653 
11654   if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
11655     {
11656       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11657     }
11658 
11659   g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path);
11660 
11661   if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
11662     update_prelight (tree_view,
11663                      priv->event_last_x,
11664                      priv->event_last_y);
11665 
11666   return TRUE;
11667 }
11668 
11669 /**
11670  * gtk_tree_view_collapse_row:
11671  * @tree_view: a `GtkTreeView`
11672  * @path: path to a row in the @tree_view
11673  *
11674  * Collapses a row (hides its child rows, if they exist).
11675  *
11676  * Returns: %TRUE if the row was collapsed.
11677  **/
11678 gboolean
gtk_tree_view_collapse_row(GtkTreeView * tree_view,GtkTreePath * path)11679 gtk_tree_view_collapse_row (GtkTreeView *tree_view,
11680 			    GtkTreePath *path)
11681 {
11682   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11683   GtkTreeRBTree *tree;
11684   GtkTreeRBNode *node;
11685 
11686   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11687   g_return_val_if_fail (priv->tree != NULL, FALSE);
11688   g_return_val_if_fail (path != NULL, FALSE);
11689 
11690   if (_gtk_tree_view_find_node (tree_view,
11691 				path,
11692 				&tree,
11693 				&node))
11694     return FALSE;
11695 
11696   if (tree == NULL || node->children == NULL)
11697     return FALSE;
11698 
11699   return gtk_tree_view_real_collapse_row (tree_view, path, tree, node);
11700 }
11701 
11702 static void
gtk_tree_view_map_expanded_rows_helper(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreePath * path,GtkTreeViewMappingFunc func,gpointer user_data)11703 gtk_tree_view_map_expanded_rows_helper (GtkTreeView            *tree_view,
11704 					GtkTreeRBTree          *tree,
11705 					GtkTreePath            *path,
11706 					GtkTreeViewMappingFunc  func,
11707 					gpointer                user_data)
11708 {
11709   GtkTreeRBNode *node;
11710 
11711   if (tree == NULL || tree->root == NULL)
11712     return;
11713 
11714   node = gtk_tree_rbtree_first (tree);
11715 
11716   while (node)
11717     {
11718       if (node->children)
11719 	{
11720 	  (* func) (tree_view, path, user_data);
11721 	  gtk_tree_path_down (path);
11722 	  gtk_tree_view_map_expanded_rows_helper (tree_view, node->children, path, func, user_data);
11723 	  gtk_tree_path_up (path);
11724 	}
11725       gtk_tree_path_next (path);
11726       node = gtk_tree_rbtree_next (tree, node);
11727     }
11728 }
11729 
11730 /**
11731  * gtk_tree_view_map_expanded_rows:
11732  * @tree_view: A `GtkTreeView`
11733  * @func: (scope call): A function to be called
11734  * @data: User data to be passed to the function.
11735  *
11736  * Calls @func on all expanded rows.
11737  **/
11738 void
gtk_tree_view_map_expanded_rows(GtkTreeView * tree_view,GtkTreeViewMappingFunc func,gpointer user_data)11739 gtk_tree_view_map_expanded_rows (GtkTreeView            *tree_view,
11740 				 GtkTreeViewMappingFunc  func,
11741 				 gpointer                user_data)
11742 {
11743   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11744   GtkTreePath *path;
11745 
11746   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11747   g_return_if_fail (func != NULL);
11748 
11749   path = gtk_tree_path_new_first ();
11750 
11751   gtk_tree_view_map_expanded_rows_helper (tree_view,
11752 					  priv->tree,
11753 					  path, func, user_data);
11754 
11755   gtk_tree_path_free (path);
11756 }
11757 
11758 /**
11759  * gtk_tree_view_row_expanded:
11760  * @tree_view: A `GtkTreeView`.
11761  * @path: A `GtkTreePath` to test expansion state.
11762  *
11763  * Returns %TRUE if the node pointed to by @path is expanded in @tree_view.
11764  *
11765  * Returns: %TRUE if #path is expanded.
11766  **/
11767 gboolean
gtk_tree_view_row_expanded(GtkTreeView * tree_view,GtkTreePath * path)11768 gtk_tree_view_row_expanded (GtkTreeView *tree_view,
11769 			    GtkTreePath *path)
11770 {
11771   GtkTreeRBTree *tree;
11772   GtkTreeRBNode *node;
11773 
11774   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11775   g_return_val_if_fail (path != NULL, FALSE);
11776 
11777   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
11778 
11779   if (node == NULL)
11780     return FALSE;
11781 
11782   return (node->children != NULL);
11783 }
11784 
11785 /**
11786  * gtk_tree_view_get_reorderable:
11787  * @tree_view: a `GtkTreeView`
11788  *
11789  * Retrieves whether the user can reorder the tree via drag-and-drop. See
11790  * gtk_tree_view_set_reorderable().
11791  *
11792  * Returns: %TRUE if the tree can be reordered.
11793  **/
11794 gboolean
gtk_tree_view_get_reorderable(GtkTreeView * tree_view)11795 gtk_tree_view_get_reorderable (GtkTreeView *tree_view)
11796 {
11797   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11798 
11799   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11800 
11801   return priv->reorderable;
11802 }
11803 
11804 /**
11805  * gtk_tree_view_set_reorderable:
11806  * @tree_view: A `GtkTreeView`.
11807  * @reorderable: %TRUE, if the tree can be reordered.
11808  *
11809  * This function is a convenience function to allow you to reorder
11810  * models that support the `GtkTreeDragSourceIface` and the
11811  * `GtkTreeDragDestIface`.  Both `GtkTreeStore` and `GtkListStore` support
11812  * these.  If @reorderable is %TRUE, then the user can reorder the
11813  * model by dragging and dropping rows. The developer can listen to
11814  * these changes by connecting to the model’s `GtkTreeModel::row-inserted`
11815  * and `GtkTreeModel::row-deleted` signals. The reordering is implemented
11816  * by setting up the tree view as a drag source and destination.
11817  * Therefore, drag and drop can not be used in a reorderable view for any
11818  * other purpose.
11819  *
11820  * This function does not give you any degree of control over the order -- any
11821  * reordering is allowed.  If more control is needed, you should probably
11822  * handle drag and drop manually.
11823  **/
11824 void
gtk_tree_view_set_reorderable(GtkTreeView * tree_view,gboolean reorderable)11825 gtk_tree_view_set_reorderable (GtkTreeView *tree_view,
11826 			       gboolean     reorderable)
11827 {
11828   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11829 
11830   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11831 
11832   reorderable = reorderable != FALSE;
11833 
11834   if (priv->reorderable == reorderable)
11835     return;
11836 
11837   if (reorderable)
11838     {
11839       GdkContentFormats *formats;
11840 
11841       formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA);
11842 
11843       gtk_tree_view_enable_model_drag_source (tree_view,
11844 					      GDK_BUTTON1_MASK,
11845 					      formats,
11846 					      GDK_ACTION_MOVE);
11847       gtk_tree_view_enable_model_drag_dest (tree_view,
11848 					    formats,
11849 					    GDK_ACTION_MOVE);
11850       gdk_content_formats_unref (formats);
11851     }
11852   else
11853     {
11854       gtk_tree_view_unset_rows_drag_source (tree_view);
11855       gtk_tree_view_unset_rows_drag_dest (tree_view);
11856     }
11857 
11858   priv->reorderable = reorderable;
11859 
11860   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]);
11861 }
11862 
11863 static void
gtk_tree_view_real_set_cursor(GtkTreeView * tree_view,GtkTreePath * path,SetCursorFlags flags)11864 gtk_tree_view_real_set_cursor (GtkTreeView     *tree_view,
11865 			       GtkTreePath     *path,
11866                                SetCursorFlags   flags)
11867 {
11868   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11869 
11870   if (!(flags & CURSOR_INVALID) && priv->cursor_node)
11871     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
11872 
11873   /* One cannot set the cursor on a separator.   Also, if
11874    * _gtk_tree_view_find_node returns TRUE, it ran out of tree
11875    * before finding the tree and node belonging to path.  The
11876    * path maps to a non-existing path and we will silently bail out.
11877    * We unset tree and node to avoid further processing.
11878    */
11879   if (path == NULL ||
11880       row_is_separator (tree_view, NULL, path)
11881       || _gtk_tree_view_find_node (tree_view,
11882                                    path,
11883                                    &priv->cursor_tree,
11884                                    &priv->cursor_node))
11885     {
11886       priv->cursor_tree = NULL;
11887       priv->cursor_node = NULL;
11888     }
11889 
11890   if (priv->cursor_node != NULL)
11891     {
11892       GtkTreeRBTree *new_tree = NULL;
11893       GtkTreeRBNode *new_node = NULL;
11894 
11895       if ((flags & CLEAR_AND_SELECT) && !priv->modify_selection_pressed)
11896         {
11897           GtkTreeSelectMode mode = 0;
11898 
11899           if (priv->extend_selection_pressed)
11900             mode |= GTK_TREE_SELECT_MODE_EXTEND;
11901 
11902           _gtk_tree_selection_internal_select_node (priv->selection,
11903                                                     priv->cursor_node,
11904                                                     priv->cursor_tree,
11905                                                     path,
11906                                                     mode,
11907                                                     FALSE);
11908         }
11909 
11910       /* We have to re-find tree and node here again, somebody might have
11911        * cleared the node or the whole tree in the GtkTreeSelection::changed
11912        * callback. If the nodes differ we bail out here.
11913        */
11914       _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node);
11915 
11916       if (priv->cursor_node == NULL ||
11917           priv->cursor_node != new_node)
11918         return;
11919 
11920       if (flags & CLAMP_NODE)
11921         {
11922 	  gtk_tree_view_clamp_node_visible (tree_view,
11923                                             priv->cursor_tree,
11924                                             priv->cursor_node);
11925           gtk_widget_queue_draw (GTK_WIDGET (tree_view));
11926 	}
11927     }
11928 
11929   if (!gtk_widget_in_destruction (GTK_WIDGET (tree_view)))
11930     g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
11931 }
11932 
11933 /**
11934  * gtk_tree_view_get_cursor:
11935  * @tree_view: A `GtkTreeView`
11936  * @path: (out) (transfer full) (optional) (nullable): A pointer to be
11937  *   filled with the current cursor path
11938  * @focus_column: (out) (transfer none) (optional) (nullable): A
11939  *   pointer to be filled with the current focus column
11940  *
11941  * Fills in @path and @focus_column with the current path and focus column.  If
11942  * the cursor isn’t currently set, then *@path will be %NULL.  If no column
11943  * currently has focus, then *@focus_column will be %NULL.
11944  *
11945  * The returned `GtkTreePath` must be freed with gtk_tree_path_free() when
11946  * you are done with it.
11947  **/
11948 void
gtk_tree_view_get_cursor(GtkTreeView * tree_view,GtkTreePath ** path,GtkTreeViewColumn ** focus_column)11949 gtk_tree_view_get_cursor (GtkTreeView        *tree_view,
11950 			  GtkTreePath       **path,
11951 			  GtkTreeViewColumn **focus_column)
11952 {
11953   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
11954 
11955   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11956 
11957   if (path)
11958     {
11959       if (priv->cursor_node)
11960         *path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
11961                                                 priv->cursor_node);
11962       else
11963 	*path = NULL;
11964     }
11965 
11966   if (focus_column)
11967     {
11968       *focus_column = priv->focus_column;
11969     }
11970 }
11971 
11972 /**
11973  * gtk_tree_view_set_cursor:
11974  * @tree_view: A `GtkTreeView`
11975  * @path: A `GtkTreePath`
11976  * @focus_column: (nullable): A `GtkTreeViewColumn`
11977  * @start_editing: %TRUE if the specified cell should start being edited.
11978  *
11979  * Sets the current keyboard focus to be at @path, and selects it.  This is
11980  * useful when you want to focus the user’s attention on a particular row.  If
11981  * @focus_column is not %NULL, then focus is given to the column specified by
11982  * it. Additionally, if @focus_column is specified, and @start_editing is
11983  * %TRUE, then editing should be started in the specified cell.
11984  * This function is often followed by @gtk_widget_grab_focus (@tree_view)
11985  * in order to give keyboard focus to the widget.  Please note that editing
11986  * can only happen when the widget is realized.
11987  *
11988  * If @path is invalid for @model, the current cursor (if any) will be unset
11989  * and the function will return without failing.
11990  **/
11991 void
gtk_tree_view_set_cursor(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * focus_column,gboolean start_editing)11992 gtk_tree_view_set_cursor (GtkTreeView       *tree_view,
11993 			  GtkTreePath       *path,
11994 			  GtkTreeViewColumn *focus_column,
11995 			  gboolean           start_editing)
11996 {
11997   gtk_tree_view_set_cursor_on_cell (tree_view, path, focus_column,
11998 				    NULL, start_editing);
11999 }
12000 
12001 /**
12002  * gtk_tree_view_set_cursor_on_cell:
12003  * @tree_view: A `GtkTreeView`
12004  * @path: A `GtkTreePath`
12005  * @focus_column: (nullable): A `GtkTreeViewColumn`
12006  * @focus_cell: (nullable): A `GtkCellRenderer`
12007  * @start_editing: %TRUE if the specified cell should start being edited.
12008  *
12009  * Sets the current keyboard focus to be at @path, and selects it.  This is
12010  * useful when you want to focus the user’s attention on a particular row.  If
12011  * @focus_column is not %NULL, then focus is given to the column specified by
12012  * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
12013  * contains 2 or more editable or activatable cells, then focus is given to
12014  * the cell specified by @focus_cell. Additionally, if @focus_column is
12015  * specified, and @start_editing is %TRUE, then editing should be started in
12016  * the specified cell.  This function is often followed by
12017  * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
12018  * widget.  Please note that editing can only happen when the widget is
12019  * realized.
12020  *
12021  * If @path is invalid for @model, the current cursor (if any) will be unset
12022  * and the function will return without failing.
12023  **/
12024 void
gtk_tree_view_set_cursor_on_cell(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * focus_column,GtkCellRenderer * focus_cell,gboolean start_editing)12025 gtk_tree_view_set_cursor_on_cell (GtkTreeView       *tree_view,
12026 				  GtkTreePath       *path,
12027 				  GtkTreeViewColumn *focus_column,
12028 				  GtkCellRenderer   *focus_cell,
12029 				  gboolean           start_editing)
12030 {
12031   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12032 
12033   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12034   g_return_if_fail (path != NULL);
12035   g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column));
12036 
12037   if (!priv->model)
12038     return;
12039 
12040   if (focus_cell)
12041     {
12042       g_return_if_fail (focus_column);
12043       g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
12044     }
12045 
12046   /* cancel the current editing, if it exists */
12047   if (priv->edited_column &&
12048       gtk_cell_area_get_edit_widget
12049       (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (priv->edited_column))))
12050     gtk_tree_view_stop_editing (tree_view, TRUE);
12051 
12052   gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE);
12053 
12054   if (focus_column &&
12055       gtk_tree_view_column_get_visible (focus_column))
12056     {
12057 #ifndef G_DISABLE_CHECKS
12058       GList *list;
12059       gboolean column_in_tree = FALSE;
12060 
12061       for (list = priv->columns; list; list = list->next)
12062 	if (list->data == focus_column)
12063 	  {
12064 	    column_in_tree = TRUE;
12065 	    break;
12066 	  }
12067       g_return_if_fail (column_in_tree);
12068 #endif
12069       _gtk_tree_view_set_focus_column (tree_view, focus_column);
12070       if (focus_cell)
12071 	gtk_tree_view_column_focus_cell (focus_column, focus_cell);
12072       if (start_editing)
12073 	gtk_tree_view_start_editing (tree_view, path, TRUE);
12074     }
12075 }
12076 
12077 /**
12078  * gtk_tree_view_get_path_at_pos:
12079  * @tree_view: A `GtkTreeView`.
12080  * @x: The x position to be identified (relative to bin_window).
12081  * @y: The y position to be identified (relative to bin_window).
12082  * @path: (out) (optional) (nullable): A pointer to a `GtkTreePath`
12083  *   pointer to be filled in
12084  * @column: (out) (transfer none) (optional) (nullable): A pointer to
12085  *   a `GtkTreeViewColumn` pointer to be filled in
12086  * @cell_x: (out) (optional): A pointer where the X coordinate
12087  *   relative to the cell can be placed
12088  * @cell_y: (out) (optional): A pointer where the Y coordinate
12089  *   relative to the cell can be placed
12090  *
12091  * Finds the path at the point (@x, @y), relative to bin_window coordinates.
12092  * That is, @x and @y are relative to an events coordinates. Widget-relative
12093  * coordinates must be converted using
12094  * gtk_tree_view_convert_widget_to_bin_window_coords(). It is primarily for
12095  * things like popup menus. If @path is non-%NULL, then it will be filled
12096  * with the `GtkTreePath` at that point.  This path should be freed with
12097  * gtk_tree_path_free().  If @column is non-%NULL, then it will be filled
12098  * with the column at that point.  @cell_x and @cell_y return the coordinates
12099  * relative to the cell background (i.e. the @background_area passed to
12100  * gtk_cell_renderer_render()).  This function is only meaningful if
12101  * @tree_view is realized.  Therefore this function will always return %FALSE
12102  * if @tree_view is not realized or does not have a model.
12103  *
12104  * For converting widget coordinates (eg. the ones you get from
12105  * GtkWidget::query-tooltip), please see
12106  * gtk_tree_view_convert_widget_to_bin_window_coords().
12107  *
12108  * Returns: %TRUE if a row exists at that coordinate.
12109  **/
12110 gboolean
gtk_tree_view_get_path_at_pos(GtkTreeView * tree_view,int x,int y,GtkTreePath ** path,GtkTreeViewColumn ** column,int * cell_x,int * cell_y)12111 gtk_tree_view_get_path_at_pos (GtkTreeView        *tree_view,
12112 			       int                 x,
12113 			       int                 y,
12114 			       GtkTreePath       **path,
12115 			       GtkTreeViewColumn **column,
12116                                int                *cell_x,
12117                                int                *cell_y)
12118 {
12119   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12120   GtkTreeRBTree *tree;
12121   GtkTreeRBNode *node;
12122   int y_offset;
12123 
12124   g_return_val_if_fail (tree_view != NULL, FALSE);
12125 
12126   if (path)
12127     *path = NULL;
12128   if (column)
12129     *column = NULL;
12130 
12131   if (priv->tree == NULL)
12132     return FALSE;
12133 
12134   if (x > gtk_adjustment_get_upper (priv->hadjustment))
12135     return FALSE;
12136 
12137   if (x < 0 || y < 0)
12138     return FALSE;
12139 
12140   if (column || cell_x)
12141     {
12142       GtkTreeViewColumn *tmp_column;
12143       GtkTreeViewColumn *last_column = NULL;
12144       GList *list;
12145       int remaining_x = x;
12146       gboolean found = FALSE;
12147       gboolean rtl;
12148       int width;
12149 
12150       rtl = (_gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
12151       for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
12152 	   list;
12153 	   list = (rtl ? list->prev : list->next))
12154 	{
12155 	  tmp_column = list->data;
12156 
12157 	  if (gtk_tree_view_column_get_visible (tmp_column) == FALSE)
12158 	    continue;
12159 
12160 	  last_column = tmp_column;
12161           width = gtk_tree_view_column_get_width (tmp_column);
12162 	  if (remaining_x < width)
12163 	    {
12164               found = TRUE;
12165 
12166               if (column)
12167                 *column = tmp_column;
12168 
12169               if (cell_x)
12170                 *cell_x = remaining_x;
12171 
12172 	      break;
12173 	    }
12174 	  remaining_x -= width;
12175 	}
12176 
12177       /* If found is FALSE and there is a last_column, then it the remainder
12178        * space is in that area
12179        */
12180       if (!found)
12181         {
12182 	  if (last_column)
12183 	    {
12184 	      if (column)
12185 		*column = last_column;
12186 
12187 	      if (cell_x)
12188 		*cell_x = gtk_tree_view_column_get_width (last_column) + remaining_x;
12189 	    }
12190 	  else
12191 	    {
12192 	      return FALSE;
12193 	    }
12194 	}
12195     }
12196 
12197   y_offset = gtk_tree_rbtree_find_offset (priv->tree,
12198 				      TREE_WINDOW_Y_TO_RBTREE_Y (priv, y),
12199 				      &tree, &node);
12200 
12201   if (tree == NULL)
12202     return FALSE;
12203 
12204   if (cell_y)
12205     *cell_y = y_offset;
12206 
12207   if (path)
12208     *path = _gtk_tree_path_new_from_rbtree (tree, node);
12209 
12210   return TRUE;
12211 }
12212 
12213 
12214 static inline int
gtk_tree_view_get_cell_area_height(GtkTreeView * tree_view,GtkTreeRBNode * node)12215 gtk_tree_view_get_cell_area_height (GtkTreeView   *tree_view,
12216                                     GtkTreeRBNode *node)
12217 {
12218   int expander_size = gtk_tree_view_get_expander_size (tree_view);
12219   int height;
12220 
12221   /* The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
12222    * i.e. just the cells, no spacing.
12223    *
12224    * The cell area height is at least expander_size - vertical_separator.
12225    * For regular nodes, the height is then at least expander_size. We should
12226    * be able to enforce the expander_size minimum here, because this
12227    * function will not be called for irregular (e.g. separator) rows.
12228    */
12229   height = gtk_tree_view_get_row_height (tree_view, node);
12230   if (height < expander_size)
12231     height = expander_size;
12232 
12233   return height;
12234 }
12235 
12236 static inline int
gtk_tree_view_get_cell_area_y_offset(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node)12237 gtk_tree_view_get_cell_area_y_offset (GtkTreeView   *tree_view,
12238                                       GtkTreeRBTree *tree,
12239                                       GtkTreeRBNode *node)
12240 {
12241   int offset;
12242 
12243   offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
12244 
12245   return offset;
12246 }
12247 
12248 /**
12249  * gtk_tree_view_get_cell_area:
12250  * @tree_view: a `GtkTreeView`
12251  * @path: (nullable): a `GtkTreePath` for the row, or %NULL to get only horizontal coordinates
12252  * @column: (nullable): a `GtkTreeViewColumn` for the column, or %NULL to get only vertical coordinates
12253  * @rect: (out): rectangle to fill with cell rect
12254  *
12255  * Fills the bounding rectangle in bin_window coordinates for the cell at the
12256  * row specified by @path and the column specified by @column.  If @path is
12257  * %NULL, or points to a path not currently displayed, the @y and @height fields
12258  * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
12259  * fields will be filled with 0.  The sum of all cell rects does not cover the
12260  * entire tree; there are extra pixels in between rows, for example. The
12261  * returned rectangle is equivalent to the @cell_area passed to
12262  * gtk_cell_renderer_render().  This function is only valid if @tree_view is
12263  * realized.
12264  **/
12265 void
gtk_tree_view_get_cell_area(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GdkRectangle * rect)12266 gtk_tree_view_get_cell_area (GtkTreeView        *tree_view,
12267                              GtkTreePath        *path,
12268                              GtkTreeViewColumn  *column,
12269                              GdkRectangle       *rect)
12270 {
12271   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12272   GtkTreeRBTree *tree = NULL;
12273   GtkTreeRBNode *node = NULL;
12274 
12275   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12276   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
12277   g_return_if_fail (rect != NULL);
12278   g_return_if_fail (!column || gtk_tree_view_column_get_tree_view (column) == (GtkWidget *) tree_view);
12279   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
12280 
12281   rect->x = 0;
12282   rect->y = 0;
12283   rect->width = 0;
12284   rect->height = 0;
12285 
12286   if (column)
12287     {
12288       rect->x = gtk_tree_view_column_get_x_offset (column) + _TREE_VIEW_HORIZONTAL_SEPARATOR / 2;
12289       rect->width = gtk_tree_view_column_get_width (column) - _TREE_VIEW_HORIZONTAL_SEPARATOR;
12290     }
12291 
12292   if (path)
12293     {
12294       gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node);
12295 
12296       /* Get vertical coords */
12297       if ((!ret && tree == NULL) || ret)
12298 	return;
12299 
12300       if (row_is_separator (tree_view, NULL, path))
12301         {
12302           /* There isn't really a "cell area" for separator, so we
12303            * return the y, height values for background area instead.
12304            */
12305           rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
12306           rect->height = gtk_tree_view_get_row_height (tree_view, node);
12307         }
12308       else
12309         {
12310           rect->y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node);
12311           rect->height = gtk_tree_view_get_cell_area_height (tree_view, node);
12312         }
12313 
12314       if (column &&
12315 	  gtk_tree_view_is_expander_column (tree_view, column))
12316 	{
12317 	  int depth = gtk_tree_path_get_depth (path);
12318 	  gboolean rtl;
12319 
12320 	  rtl = _gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
12321 
12322 	  if (!rtl)
12323 	    rect->x += (depth - 1) * priv->level_indentation;
12324 	  rect->width -= (depth - 1) * priv->level_indentation;
12325 
12326 	  if (gtk_tree_view_draw_expanders (tree_view))
12327 	    {
12328               int expander_size = gtk_tree_view_get_expander_size (tree_view);
12329 	      if (!rtl)
12330 		rect->x += depth * expander_size;
12331 	      rect->width -= depth * expander_size;
12332 	    }
12333 
12334 	  rect->width = MAX (rect->width, 0);
12335 	}
12336     }
12337 }
12338 
12339 static inline int
gtk_tree_view_get_row_height(GtkTreeView * tree_view,GtkTreeRBNode * node)12340 gtk_tree_view_get_row_height (GtkTreeView   *tree_view,
12341                               GtkTreeRBNode *node)
12342 {
12343   int expander_size = gtk_tree_view_get_expander_size (tree_view);
12344   int height;
12345 
12346   /* The "background" areas of all rows/cells add up to cover the entire tree.
12347    * The background includes all inter-row and inter-cell spacing.
12348    *
12349    * If the row pointed at by node does not have a height set, we default
12350    * to expander_size, which is the minimum height for regular nodes.
12351    * Non-regular nodes (e.g. separators) can have a height set smaller
12352    * than expander_size and should not be overruled here.
12353    */
12354   height = GTK_TREE_RBNODE_GET_HEIGHT (node);
12355   if (height <= 0)
12356     height = expander_size;
12357 
12358   return height;
12359 }
12360 
12361 static inline int
gtk_tree_view_get_row_y_offset(GtkTreeView * tree_view,GtkTreeRBTree * tree,GtkTreeRBNode * node)12362 gtk_tree_view_get_row_y_offset (GtkTreeView   *tree_view,
12363                                 GtkTreeRBTree *tree,
12364                                 GtkTreeRBNode *node)
12365 {
12366   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12367   int offset;
12368 
12369   offset = gtk_tree_rbtree_node_find_offset (tree, node);
12370 
12371   return RBTREE_Y_TO_TREE_WINDOW_Y (priv, offset);
12372 }
12373 
12374 /**
12375  * gtk_tree_view_get_background_area:
12376  * @tree_view: a `GtkTreeView`
12377  * @path: (nullable): a `GtkTreePath` for the row, or %NULL to get only horizontal coordinates
12378  * @column: (nullable): a `GtkTreeViewColumn` for the column, or %NULL to get only vertical coordinates
12379  * @rect: (out): rectangle to fill with cell background rect
12380  *
12381  * Fills the bounding rectangle in bin_window coordinates for the cell at the
12382  * row specified by @path and the column specified by @column.  If @path is
12383  * %NULL, or points to a node not found in the tree, the @y and @height fields of
12384  * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
12385  * fields will be filled with 0.  The returned rectangle is equivalent to the
12386  * @background_area passed to gtk_cell_renderer_render().  These background
12387  * areas tile to cover the entire bin window.  Contrast with the @cell_area,
12388  * returned by gtk_tree_view_get_cell_area(), which returns only the cell
12389  * itself, excluding surrounding borders and the tree expander area.
12390  *
12391  **/
12392 void
gtk_tree_view_get_background_area(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GdkRectangle * rect)12393 gtk_tree_view_get_background_area (GtkTreeView        *tree_view,
12394                                    GtkTreePath        *path,
12395                                    GtkTreeViewColumn  *column,
12396                                    GdkRectangle       *rect)
12397 {
12398   GtkTreeRBTree *tree = NULL;
12399   GtkTreeRBNode *node = NULL;
12400 
12401   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12402   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
12403   g_return_if_fail (rect != NULL);
12404 
12405   rect->x = 0;
12406   rect->y = 0;
12407   rect->width = 0;
12408   rect->height = 0;
12409 
12410   if (path)
12411     {
12412       /* Get vertical coords */
12413 
12414       if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) &&
12415 	  tree == NULL)
12416 	return;
12417 
12418       rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
12419       rect->height = gtk_tree_view_get_row_height (tree_view, node);
12420     }
12421 
12422   if (column)
12423     {
12424       int x2 = 0;
12425 
12426       gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2);
12427       rect->width = x2 - rect->x;
12428     }
12429 }
12430 
12431 /**
12432  * gtk_tree_view_get_visible_rect:
12433  * @tree_view: a `GtkTreeView`
12434  * @visible_rect: (out): rectangle to fill
12435  *
12436  * Fills @visible_rect with the currently-visible region of the
12437  * buffer, in tree coordinates. Convert to bin_window coordinates with
12438  * gtk_tree_view_convert_tree_to_bin_window_coords().
12439  * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
12440  * scrollable area of the tree.
12441  **/
12442 void
gtk_tree_view_get_visible_rect(GtkTreeView * tree_view,GdkRectangle * visible_rect)12443 gtk_tree_view_get_visible_rect (GtkTreeView  *tree_view,
12444                                 GdkRectangle *visible_rect)
12445 {
12446   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12447   GtkAllocation allocation;
12448   GtkWidget *widget;
12449 
12450   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12451 
12452   widget = GTK_WIDGET (tree_view);
12453 
12454   if (visible_rect)
12455     {
12456       gtk_widget_get_allocation (widget, &allocation);
12457       visible_rect->x = gtk_adjustment_get_value (priv->hadjustment);
12458       visible_rect->y = gtk_adjustment_get_value (priv->vadjustment);
12459       visible_rect->width = allocation.width;
12460       visible_rect->height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
12461     }
12462 }
12463 
12464 /**
12465  * gtk_tree_view_convert_widget_to_tree_coords:
12466  * @tree_view: a `GtkTreeView`
12467  * @wx: X coordinate relative to the widget
12468  * @wy: Y coordinate relative to the widget
12469  * @tx: (out): return location for tree X coordinate
12470  * @ty: (out): return location for tree Y coordinate
12471  *
12472  * Converts widget coordinates to coordinates for the
12473  * tree (the full scrollable area of the tree).
12474  **/
12475 void
gtk_tree_view_convert_widget_to_tree_coords(GtkTreeView * tree_view,int wx,int wy,int * tx,int * ty)12476 gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view,
12477                                              int          wx,
12478                                              int          wy,
12479                                              int         *tx,
12480                                              int         *ty)
12481 {
12482   int x, y;
12483 
12484   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12485 
12486   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
12487 						      wx, wy,
12488 						      &x, &y);
12489   gtk_tree_view_convert_bin_window_to_tree_coords (tree_view,
12490 						   x, y,
12491 						   tx, ty);
12492 }
12493 
12494 /**
12495  * gtk_tree_view_convert_tree_to_widget_coords:
12496  * @tree_view: a `GtkTreeView`
12497  * @tx: X coordinate relative to the tree
12498  * @ty: Y coordinate relative to the tree
12499  * @wx: (out): return location for widget X coordinate
12500  * @wy: (out): return location for widget Y coordinate
12501  *
12502  * Converts tree coordinates (coordinates in full scrollable area of the tree)
12503  * to widget coordinates.
12504  **/
12505 void
gtk_tree_view_convert_tree_to_widget_coords(GtkTreeView * tree_view,int tx,int ty,int * wx,int * wy)12506 gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view,
12507                                              int          tx,
12508                                              int          ty,
12509                                              int         *wx,
12510                                              int         *wy)
12511 {
12512   int x, y;
12513 
12514   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12515 
12516   gtk_tree_view_convert_tree_to_bin_window_coords (tree_view,
12517 						    tx, ty,
12518 						    &x, &y);
12519   gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
12520 						     x, y,
12521 						     wx, wy);
12522 }
12523 
12524 /**
12525  * gtk_tree_view_convert_widget_to_bin_window_coords:
12526  * @tree_view: a `GtkTreeView`
12527  * @wx: X coordinate relative to the widget
12528  * @wy: Y coordinate relative to the widget
12529  * @bx: (out): return location for bin_window X coordinate
12530  * @by: (out): return location for bin_window Y coordinate
12531  *
12532  * Converts widget coordinates to coordinates for the bin_window.
12533  **/
12534 void
gtk_tree_view_convert_widget_to_bin_window_coords(GtkTreeView * tree_view,int wx,int wy,int * bx,int * by)12535 gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view,
12536                                                    int          wx,
12537                                                    int          wy,
12538                                                    int         *bx,
12539                                                    int         *by)
12540 {
12541   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12542 
12543   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12544 
12545   if (bx)
12546     *bx = wx + gtk_adjustment_get_value (priv->hadjustment);
12547   if (by)
12548     *by = wy - gtk_tree_view_get_effective_header_height (tree_view);
12549 }
12550 
12551 /**
12552  * gtk_tree_view_convert_bin_window_to_widget_coords:
12553  * @tree_view: a `GtkTreeView`
12554  * @bx: bin_window X coordinate
12555  * @by: bin_window Y coordinate
12556  * @wx: (out): return location for widget X coordinate
12557  * @wy: (out): return location for widget Y coordinate
12558  *
12559  * Converts bin_window coordinates to widget relative coordinates.
12560  **/
12561 void
gtk_tree_view_convert_bin_window_to_widget_coords(GtkTreeView * tree_view,int bx,int by,int * wx,int * wy)12562 gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view,
12563                                                    int          bx,
12564                                                    int          by,
12565                                                    int         *wx,
12566                                                    int         *wy)
12567 {
12568   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12569 
12570   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12571 
12572   if (wx)
12573     *wx = bx - gtk_adjustment_get_value (priv->hadjustment);
12574   if (wy)
12575     *wy = by + gtk_tree_view_get_effective_header_height (tree_view);
12576 }
12577 
12578 /**
12579  * gtk_tree_view_convert_tree_to_bin_window_coords:
12580  * @tree_view: a `GtkTreeView`
12581  * @tx: tree X coordinate
12582  * @ty: tree Y coordinate
12583  * @bx: (out): return location for X coordinate relative to bin_window
12584  * @by: (out): return location for Y coordinate relative to bin_window
12585  *
12586  * Converts tree coordinates (coordinates in full scrollable area of the tree)
12587  * to bin_window coordinates.
12588  **/
12589 void
gtk_tree_view_convert_tree_to_bin_window_coords(GtkTreeView * tree_view,int tx,int ty,int * bx,int * by)12590 gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view,
12591                                                  int          tx,
12592                                                  int          ty,
12593                                                  int         *bx,
12594                                                  int         *by)
12595 {
12596   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12597 
12598   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12599 
12600   if (bx)
12601     *bx = tx;
12602   if (by)
12603     *by = ty - priv->dy;
12604 }
12605 
12606 /**
12607  * gtk_tree_view_convert_bin_window_to_tree_coords:
12608  * @tree_view: a `GtkTreeView`
12609  * @bx: X coordinate relative to bin_window
12610  * @by: Y coordinate relative to bin_window
12611  * @tx: (out): return location for tree X coordinate
12612  * @ty: (out): return location for tree Y coordinate
12613  *
12614  * Converts bin_window coordinates to coordinates for the
12615  * tree (the full scrollable area of the tree).
12616  **/
12617 void
gtk_tree_view_convert_bin_window_to_tree_coords(GtkTreeView * tree_view,int bx,int by,int * tx,int * ty)12618 gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view,
12619                                                  int          bx,
12620                                                  int          by,
12621                                                  int         *tx,
12622                                                  int         *ty)
12623 {
12624   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12625 
12626   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12627 
12628   if (tx)
12629     *tx = bx;
12630   if (ty)
12631     *ty = by + priv->dy;
12632 }
12633 
12634 
12635 
12636 /**
12637  * gtk_tree_view_get_visible_range:
12638  * @tree_view: A `GtkTreeView`
12639  * @start_path: (out) (optional): Return location for start of region
12640  * @end_path: (out) (optional): Return location for end of region
12641  *
12642  * Sets @start_path and @end_path to be the first and last visible path.
12643  * Note that there may be invisible paths in between.
12644  *
12645  * The paths should be freed with gtk_tree_path_free() after use.
12646  *
12647  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
12648  */
12649 gboolean
gtk_tree_view_get_visible_range(GtkTreeView * tree_view,GtkTreePath ** start_path,GtkTreePath ** end_path)12650 gtk_tree_view_get_visible_range (GtkTreeView  *tree_view,
12651                                  GtkTreePath **start_path,
12652                                  GtkTreePath **end_path)
12653 {
12654   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12655   GtkTreeRBTree *tree;
12656   GtkTreeRBNode *node;
12657   gboolean retval;
12658 
12659   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12660 
12661   if (!priv->tree)
12662     return FALSE;
12663 
12664   retval = TRUE;
12665 
12666   if (start_path)
12667     {
12668       gtk_tree_rbtree_find_offset (priv->tree,
12669                                    TREE_WINDOW_Y_TO_RBTREE_Y (priv, 0),
12670                                    &tree, &node);
12671       if (node)
12672         *start_path = _gtk_tree_path_new_from_rbtree (tree, node);
12673       else
12674         retval = FALSE;
12675     }
12676 
12677   if (end_path)
12678     {
12679       int y;
12680 
12681       if (gtk_tree_view_get_height (tree_view) < gtk_adjustment_get_page_size (priv->vadjustment))
12682         y = gtk_tree_view_get_height (tree_view) - 1;
12683       else
12684         y = TREE_WINDOW_Y_TO_RBTREE_Y (priv, gtk_adjustment_get_page_size (priv->vadjustment)) - 1;
12685 
12686       gtk_tree_rbtree_find_offset (priv->tree, y, &tree, &node);
12687       if (node)
12688         *end_path = _gtk_tree_path_new_from_rbtree (tree, node);
12689       else
12690         retval = FALSE;
12691     }
12692 
12693   return retval;
12694 }
12695 
12696 /**
12697  * gtk_tree_view_is_blank_at_pos:
12698  * @tree_view: A `GtkTreeView`
12699  * @x: The x position to be identified (relative to bin_window)
12700  * @y: The y position to be identified (relative to bin_window)
12701  * @path: (out) (optional) (nullable): A pointer to a `GtkTreePath` pointer to
12702  *   be filled in
12703  * @column: (out) (transfer none) (optional) (nullable): A pointer to a
12704  *   `GtkTreeViewColumn` pointer to be filled in
12705  * @cell_x: (out) (optional): A pointer where the X coordinate relative to the
12706  *   cell can be placed
12707  * @cell_y: (out) (optional): A pointer where the Y coordinate relative to the
12708  *   cell can be placed
12709  *
12710  * Determine whether the point (@x, @y) in @tree_view is blank, that is no
12711  * cell content nor an expander arrow is drawn at the location. If so, the
12712  * location can be considered as the background. You might wish to take
12713  * special action on clicks on the background, such as clearing a current
12714  * selection, having a custom context menu or starting rubber banding.
12715  *
12716  * The @x and @y coordinate that are provided must be relative to bin_window
12717  * coordinates.  Widget-relative coordinates must be converted using
12718  * gtk_tree_view_convert_widget_to_bin_window_coords().
12719  *
12720  * For converting widget coordinates (eg. the ones you get from
12721  * GtkWidget::query-tooltip), please see
12722  * gtk_tree_view_convert_widget_to_bin_window_coords().
12723  *
12724  * The @path, @column, @cell_x and @cell_y arguments will be filled in
12725  * likewise as for gtk_tree_view_get_path_at_pos().  Please see
12726  * gtk_tree_view_get_path_at_pos() for more information.
12727  *
12728  * Returns: %TRUE if the area at the given coordinates is blank,
12729  * %FALSE otherwise.
12730  */
12731 gboolean
gtk_tree_view_is_blank_at_pos(GtkTreeView * tree_view,int x,int y,GtkTreePath ** path,GtkTreeViewColumn ** column,int * cell_x,int * cell_y)12732 gtk_tree_view_is_blank_at_pos (GtkTreeView       *tree_view,
12733                                int                 x,
12734                                int                 y,
12735                                GtkTreePath       **path,
12736                                GtkTreeViewColumn **column,
12737                                int                *cell_x,
12738                                int                *cell_y)
12739 {
12740   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12741   GtkTreeRBTree *tree;
12742   GtkTreeRBNode *node;
12743   GtkTreeIter iter;
12744   GtkTreePath *real_path;
12745   GtkTreeViewColumn *real_column;
12746   GdkRectangle cell_area, background_area;
12747 
12748   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12749 
12750   if (!gtk_tree_view_get_path_at_pos (tree_view, x, y,
12751                                       &real_path, &real_column,
12752                                       cell_x, cell_y))
12753     /* If there's no path here, it is blank */
12754     return TRUE;
12755 
12756   if (path)
12757     *path = real_path;
12758 
12759   if (column)
12760     *column = real_column;
12761 
12762   gtk_tree_model_get_iter (priv->model, &iter, real_path);
12763   _gtk_tree_view_find_node (tree_view, real_path, &tree, &node);
12764 
12765   /* Check if there's an expander arrow at (x, y) */
12766   if (real_column == priv->expander_column
12767       && gtk_tree_view_draw_expanders (tree_view))
12768     {
12769       gboolean over_arrow;
12770 
12771       over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
12772 
12773       if (over_arrow)
12774         {
12775           if (!path)
12776             gtk_tree_path_free (real_path);
12777           return FALSE;
12778         }
12779     }
12780 
12781   /* Otherwise, have the column see if there's a cell at (x, y) */
12782   gtk_tree_view_column_cell_set_cell_data (real_column,
12783                                            priv->model,
12784                                            &iter,
12785                                            GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT),
12786                                            node->children ? TRUE : FALSE);
12787 
12788   gtk_tree_view_get_background_area (tree_view, real_path, real_column,
12789                                      &background_area);
12790   gtk_tree_view_get_cell_area (tree_view, real_path, real_column,
12791                                &cell_area);
12792 
12793   if (!path)
12794     gtk_tree_path_free (real_path);
12795 
12796   return _gtk_tree_view_column_is_blank_at_pos (real_column,
12797                                                 &cell_area,
12798                                                 &background_area,
12799                                                 x, y);
12800 }
12801 
12802 static void
unset_reorderable(GtkTreeView * tree_view)12803 unset_reorderable (GtkTreeView *tree_view)
12804 {
12805   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12806 
12807   if (priv->reorderable)
12808     {
12809       priv->reorderable = FALSE;
12810       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]);
12811     }
12812 }
12813 
12814 /**
12815  * gtk_tree_view_enable_model_drag_source:
12816  * @tree_view: a `GtkTreeView`
12817  * @start_button_mask: Mask of allowed buttons to start drag
12818  * @formats: the target formats that the drag will support
12819  * @actions: the bitmask of possible actions for a drag from this
12820  *    widget
12821  *
12822  * Turns @tree_view into a drag source for automatic DND. Calling this
12823  * method sets `GtkTreeView`:reorderable to %FALSE.
12824  **/
12825 void
gtk_tree_view_enable_model_drag_source(GtkTreeView * tree_view,GdkModifierType start_button_mask,GdkContentFormats * formats,GdkDragAction actions)12826 gtk_tree_view_enable_model_drag_source (GtkTreeView       *tree_view,
12827 					GdkModifierType    start_button_mask,
12828 					GdkContentFormats *formats,
12829 					GdkDragAction      actions)
12830 {
12831   TreeViewDragInfo *di;
12832 
12833   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12834 
12835   di = ensure_info (tree_view);
12836 
12837   di->source_formats = gdk_content_formats_ref (formats);
12838   di->source_actions = actions;
12839   di->drag = NULL;
12840 
12841   di->start_button_mask = start_button_mask;
12842   di->source_set = TRUE;
12843 
12844   unset_reorderable (tree_view);
12845 }
12846 
12847 /**
12848  * gtk_tree_view_enable_model_drag_dest:
12849  * @tree_view: a `GtkTreeView`
12850  * @formats: the target formats that the drag will support
12851  * @actions: the bitmask of possible actions for a drag from this
12852  *    widget
12853  *
12854  * Turns @tree_view into a drop destination for automatic DND. Calling
12855  * this method sets `GtkTreeView`:reorderable to %FALSE.
12856  **/
12857 void
gtk_tree_view_enable_model_drag_dest(GtkTreeView * tree_view,GdkContentFormats * formats,GdkDragAction actions)12858 gtk_tree_view_enable_model_drag_dest (GtkTreeView       *tree_view,
12859 				      GdkContentFormats *formats,
12860 				      GdkDragAction      actions)
12861 {
12862   TreeViewDragInfo *di;
12863   GtkCssNode *widget_node;
12864 
12865   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12866 
12867   di = ensure_info (tree_view);
12868   di->dest_set = TRUE;
12869 
12870   di->dest = gtk_drop_target_async_new (gdk_content_formats_ref (formats), actions);
12871   g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view);
12872   g_signal_connect (di->dest, "drag-enter", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
12873   g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
12874   g_signal_connect (di->dest, "drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view);
12875   gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
12876   g_object_ref (di->dest);
12877 
12878   widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view));
12879   di->cssnode = gtk_css_node_new ();
12880   gtk_css_node_set_name (di->cssnode, g_quark_from_static_string ("dndtarget"));
12881   gtk_css_node_set_parent (di->cssnode, widget_node);
12882   gtk_css_node_set_state (di->cssnode, gtk_css_node_get_state (widget_node));
12883   g_object_unref (di->cssnode);
12884 
12885   unset_reorderable (tree_view);
12886 }
12887 
12888 /**
12889  * gtk_tree_view_unset_rows_drag_source:
12890  * @tree_view: a `GtkTreeView`
12891  *
12892  * Undoes the effect of
12893  * gtk_tree_view_enable_model_drag_source(). Calling this method sets
12894  * `GtkTreeView`:reorderable to %FALSE.
12895  **/
12896 void
gtk_tree_view_unset_rows_drag_source(GtkTreeView * tree_view)12897 gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view)
12898 {
12899   TreeViewDragInfo *di;
12900 
12901   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12902 
12903   di = get_info (tree_view);
12904 
12905   if (di)
12906     {
12907       if (di->source_set)
12908         {
12909           g_clear_pointer (&di->source_formats, gdk_content_formats_unref);
12910           di->source_set = FALSE;
12911         }
12912 
12913       if (!di->dest_set && !di->source_set)
12914         remove_info (tree_view);
12915     }
12916 
12917   unset_reorderable (tree_view);
12918 }
12919 
12920 /**
12921  * gtk_tree_view_unset_rows_drag_dest:
12922  * @tree_view: a `GtkTreeView`
12923  *
12924  * Undoes the effect of
12925  * gtk_tree_view_enable_model_drag_dest(). Calling this method sets
12926  * `GtkTreeView`:reorderable to %FALSE.
12927  **/
12928 void
gtk_tree_view_unset_rows_drag_dest(GtkTreeView * tree_view)12929 gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view)
12930 {
12931   TreeViewDragInfo *di;
12932 
12933   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12934 
12935   di = get_info (tree_view);
12936 
12937   if (di)
12938     {
12939       if (di->dest_set)
12940         {
12941           gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
12942           di->dest = NULL;
12943           di->dest_set = FALSE;
12944 
12945           gtk_css_node_set_parent (di->cssnode, NULL);
12946           di->cssnode = NULL;
12947         }
12948 
12949       if (!di->dest_set && !di->source_set)
12950         remove_info (tree_view);
12951     }
12952 
12953   unset_reorderable (tree_view);
12954 }
12955 
12956 /**
12957  * gtk_tree_view_set_drag_dest_row:
12958  * @tree_view: a `GtkTreeView`
12959  * @path: (nullable): The path of the row to highlight
12960  * @pos: Specifies whether to drop before, after or into the row
12961  *
12962  * Sets the row that is highlighted for feedback.
12963  * If @path is %NULL, an existing highlight is removed.
12964  */
12965 void
gtk_tree_view_set_drag_dest_row(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewDropPosition pos)12966 gtk_tree_view_set_drag_dest_row (GtkTreeView            *tree_view,
12967                                  GtkTreePath            *path,
12968                                  GtkTreeViewDropPosition pos)
12969 {
12970   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
12971   GtkTreePath *current_dest;
12972 
12973   /* Note; this function is exported to allow a custom DND
12974    * implementation, so it can't touch TreeViewDragInfo
12975    */
12976 
12977   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12978 
12979   current_dest = NULL;
12980 
12981   if (priv->drag_dest_row)
12982     {
12983       current_dest = gtk_tree_row_reference_get_path (priv->drag_dest_row);
12984       gtk_tree_row_reference_free (priv->drag_dest_row);
12985     }
12986 
12987   /* special case a drop on an empty model */
12988   priv->empty_view_drop = 0;
12989 
12990   if (pos == GTK_TREE_VIEW_DROP_BEFORE && path
12991       && gtk_tree_path_get_depth (path) == 1
12992       && gtk_tree_path_get_indices (path)[0] == 0)
12993     {
12994       int n_children;
12995 
12996       n_children = gtk_tree_model_iter_n_children (priv->model,
12997                                                    NULL);
12998 
12999       if (!n_children)
13000         priv->empty_view_drop = 1;
13001     }
13002 
13003   priv->drag_dest_pos = pos;
13004 
13005   if (path)
13006     {
13007       priv->drag_dest_row =
13008         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), priv->model, path);
13009       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
13010     }
13011   else
13012     priv->drag_dest_row = NULL;
13013 
13014   if (current_dest)
13015     {
13016       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
13017 
13018       gtk_tree_path_free (current_dest);
13019     }
13020 }
13021 
13022 /**
13023  * gtk_tree_view_get_drag_dest_row:
13024  * @tree_view: a `GtkTreeView`
13025  * @path: (out) (optional) (nullable): Return location for the path of the highlighted row
13026  * @pos: (out) (optional): Return location for the drop position
13027  *
13028  * Gets information about the row that is highlighted for feedback.
13029  **/
13030 void
gtk_tree_view_get_drag_dest_row(GtkTreeView * tree_view,GtkTreePath ** path,GtkTreeViewDropPosition * pos)13031 gtk_tree_view_get_drag_dest_row (GtkTreeView              *tree_view,
13032                                  GtkTreePath             **path,
13033                                  GtkTreeViewDropPosition  *pos)
13034 {
13035   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13036 
13037   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13038 
13039   if (path)
13040     {
13041       if (priv->drag_dest_row)
13042         *path = gtk_tree_row_reference_get_path (priv->drag_dest_row);
13043       else
13044         {
13045           if (priv->empty_view_drop)
13046             *path = gtk_tree_path_new_from_indices (0, -1);
13047           else
13048             *path = NULL;
13049         }
13050     }
13051 
13052   if (pos)
13053     *pos = priv->drag_dest_pos;
13054 }
13055 
13056 /**
13057  * gtk_tree_view_get_dest_row_at_pos:
13058  * @tree_view: a `GtkTreeView`
13059  * @drag_x: the position to determine the destination row for
13060  * @drag_y: the position to determine the destination row for
13061  * @path: (out) (optional) (nullable): Return location for the path of
13062  *   the highlighted row
13063  * @pos: (out) (optional): Return location for the drop position, or
13064  *   %NULL
13065  *
13066  * Determines the destination row for a given position.  @drag_x and
13067  * @drag_y are expected to be in widget coordinates.  This function is only
13068  * meaningful if @tree_view is realized.  Therefore this function will always
13069  * return %FALSE if @tree_view is not realized or does not have a model.
13070  *
13071  * Returns: whether there is a row at the given position, %TRUE if this
13072  * is indeed the case.
13073  **/
13074 gboolean
gtk_tree_view_get_dest_row_at_pos(GtkTreeView * tree_view,int drag_x,int drag_y,GtkTreePath ** path,GtkTreeViewDropPosition * pos)13075 gtk_tree_view_get_dest_row_at_pos (GtkTreeView             *tree_view,
13076                                    int                      drag_x,
13077                                    int                      drag_y,
13078                                    GtkTreePath            **path,
13079                                    GtkTreeViewDropPosition *pos)
13080 {
13081   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13082   int cell_y;
13083   int bin_x, bin_y;
13084   double offset_into_row;
13085   double fourth;
13086   GdkRectangle cell;
13087   GtkTreeViewColumn *column = NULL;
13088   GtkTreePath *tmp_path = NULL;
13089 
13090   /* Note; this function is exported to allow a custom DND
13091    * implementation, so it can't touch TreeViewDragInfo
13092    */
13093 
13094   g_return_val_if_fail (tree_view != NULL, FALSE);
13095   g_return_val_if_fail (drag_x >= 0, FALSE);
13096   g_return_val_if_fail (drag_y >= 0, FALSE);
13097 
13098   if (path)
13099     *path = NULL;
13100 
13101   if (priv->tree == NULL)
13102     return FALSE;
13103 
13104   /* If in the top fourth of a row, we drop before that row; if
13105    * in the bottom fourth, drop after that row; if in the middle,
13106    * and the row has children, drop into the row.
13107    */
13108   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
13109 						      &bin_x, &bin_y);
13110 
13111   if (!gtk_tree_view_get_path_at_pos (tree_view,
13112 				      bin_x,
13113 				      bin_y,
13114                                       &tmp_path,
13115                                       &column,
13116                                       NULL,
13117                                       &cell_y))
13118     return FALSE;
13119 
13120   gtk_tree_view_get_background_area (tree_view, tmp_path, column,
13121                                      &cell);
13122 
13123   offset_into_row = cell_y;
13124 
13125   if (path)
13126     *path = tmp_path;
13127   else
13128     gtk_tree_path_free (tmp_path);
13129 
13130   tmp_path = NULL;
13131 
13132   fourth = cell.height / 4.0;
13133 
13134   if (pos)
13135     {
13136       if (offset_into_row < fourth)
13137         {
13138           *pos = GTK_TREE_VIEW_DROP_BEFORE;
13139         }
13140       else if (offset_into_row < (cell.height / 2.0))
13141         {
13142           *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
13143         }
13144       else if (offset_into_row < cell.height - fourth)
13145         {
13146           *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
13147         }
13148       else
13149         {
13150           *pos = GTK_TREE_VIEW_DROP_AFTER;
13151         }
13152     }
13153 
13154   return TRUE;
13155 }
13156 
13157 
13158 static void
gtk_treeview_snapshot_border(GtkSnapshot * snapshot,const graphene_rect_t * rect)13159 gtk_treeview_snapshot_border (GtkSnapshot           *snapshot,
13160                               const graphene_rect_t *rect)
13161 {
13162   GskRoundedRect rounded;
13163 
13164   gsk_rounded_rect_init_from_rect (&rounded, rect, 0);
13165 
13166 #define BLACK { 0, 0, 0, 1 }
13167   gtk_snapshot_append_border (snapshot,
13168                               &rounded,
13169                               (float[4]) { 1, 1, 1, 1 },
13170                               (GdkRGBA[4]) { BLACK, BLACK, BLACK, BLACK });
13171 #undef BLACK
13172 }
13173 
13174 /* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */
13175 /**
13176  * gtk_tree_view_create_row_drag_icon:
13177  * @tree_view: a `GtkTreeView`
13178  * @path: a `GtkTreePath` in @tree_view
13179  *
13180  * Creates a `cairo_surface_t` representation of the row at @path.
13181  * This image is used for a drag icon.
13182  *
13183  * Returns: (transfer full) (nullable): a newly-allocated surface of the drag icon.
13184  **/
13185 GdkPaintable *
gtk_tree_view_create_row_drag_icon(GtkTreeView * tree_view,GtkTreePath * path)13186 gtk_tree_view_create_row_drag_icon (GtkTreeView  *tree_view,
13187                                     GtkTreePath  *path)
13188 {
13189   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13190   GtkTreeIter iter;
13191   GtkTreeRBTree *tree;
13192   GtkTreeRBNode *node;
13193   GtkStyleContext *context;
13194   int cell_offset;
13195   GList *list;
13196   GdkRectangle background_area;
13197   GtkWidget *widget;
13198   GtkSnapshot *snapshot;
13199   GdkPaintable *paintable;
13200   int depth;
13201   /* start drawing inside the black outline */
13202   int x = 1, y = 1;
13203   int bin_window_width;
13204   gboolean is_separator = FALSE;
13205   gboolean rtl;
13206 
13207   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
13208   g_return_val_if_fail (path != NULL, NULL);
13209 
13210   widget = GTK_WIDGET (tree_view);
13211 
13212   if (!gtk_widget_get_realized (widget))
13213     return NULL;
13214 
13215   depth = gtk_tree_path_get_depth (path);
13216 
13217   _gtk_tree_view_find_node (tree_view,
13218                             path,
13219                             &tree,
13220                             &node);
13221 
13222   if (tree == NULL)
13223     return NULL;
13224 
13225   if (!gtk_tree_model_get_iter (priv->model,
13226                                 &iter,
13227                                 path))
13228     return NULL;
13229 
13230   context = gtk_widget_get_style_context (widget);
13231 
13232   is_separator = row_is_separator (tree_view, &iter, NULL);
13233 
13234   cell_offset = x;
13235 
13236   background_area.y = y;
13237   background_area.height = gtk_tree_view_get_row_height (tree_view, node);
13238 
13239   bin_window_width = gtk_widget_get_width (GTK_WIDGET (tree_view));
13240 
13241   snapshot = gtk_snapshot_new ();
13242 
13243   gtk_snapshot_render_background (snapshot, context,
13244                                   0, 0,
13245                                   bin_window_width + 2,
13246                                   background_area.height + 2);
13247 
13248   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
13249 
13250   for (list = (rtl ? g_list_last (priv->columns) : g_list_first (priv->columns));
13251       list;
13252       list = (rtl ? list->prev : list->next))
13253     {
13254       GtkTreeViewColumn *column = list->data;
13255       GdkRectangle cell_area;
13256 
13257       if (!gtk_tree_view_column_get_visible (column))
13258         continue;
13259 
13260       gtk_tree_view_column_cell_set_cell_data (column, priv->model, &iter,
13261 					       GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_PARENT),
13262 					       node->children?TRUE:FALSE);
13263 
13264       background_area.x = cell_offset;
13265       background_area.width = gtk_tree_view_column_get_width (column);
13266 
13267       cell_area = background_area;
13268 
13269       if (gtk_tree_view_is_expander_column (tree_view, column))
13270         {
13271 	  if (!rtl)
13272 	    cell_area.x += (depth - 1) * priv->level_indentation;
13273 	  cell_area.width -= (depth - 1) * priv->level_indentation;
13274 
13275           if (gtk_tree_view_draw_expanders (tree_view))
13276 	    {
13277               int expander_size = gtk_tree_view_get_expander_size (tree_view);
13278 	      if (!rtl)
13279 		cell_area.x += depth * expander_size;
13280 	      cell_area.width -= depth * expander_size;
13281 	    }
13282         }
13283 
13284       if (gtk_tree_view_column_cell_is_visible (column))
13285 	{
13286 	  if (is_separator)
13287             {
13288               GdkRGBA color;
13289 
13290               gtk_style_context_save (context);
13291               gtk_style_context_add_class (context, "separator");
13292 
13293               gtk_style_context_get_color (context, &color);
13294               gtk_snapshot_append_color (snapshot,
13295                                          &color,
13296                                          &GRAPHENE_RECT_INIT(
13297                                              cell_area.x,
13298                                              cell_area.y + cell_area.height / 2,
13299                                              cell_area.x + cell_area.width,
13300                                              1
13301                                          ));
13302 
13303               gtk_style_context_restore (context);
13304             }
13305 	  else
13306             {
13307               gtk_tree_view_column_cell_snapshot (column,
13308                                                   snapshot,
13309                                                   &background_area,
13310                                                   &cell_area,
13311                                                   0, FALSE);
13312             }
13313 	}
13314       cell_offset += gtk_tree_view_column_get_width (column);
13315     }
13316 
13317   gtk_treeview_snapshot_border (snapshot,
13318                                 &GRAPHENE_RECT_INIT(0, 0, bin_window_width + 2, background_area.height + 2));
13319 
13320   paintable = gtk_snapshot_free_to_paintable (snapshot, NULL);
13321 
13322   return paintable;
13323 }
13324 
13325 
13326 /*
13327  * Interactive search
13328  */
13329 
13330 /**
13331  * gtk_tree_view_set_enable_search:
13332  * @tree_view: A `GtkTreeView`
13333  * @enable_search: %TRUE, if the user can search interactively
13334  *
13335  * If @enable_search is set, then the user can type in text to search through
13336  * the tree interactively (this is sometimes called "typeahead find").
13337  *
13338  * Note that even if this is %FALSE, the user can still initiate a search
13339  * using the “start-interactive-search” key binding.
13340  */
13341 void
gtk_tree_view_set_enable_search(GtkTreeView * tree_view,gboolean enable_search)13342 gtk_tree_view_set_enable_search (GtkTreeView *tree_view,
13343 				 gboolean     enable_search)
13344 {
13345   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13346 
13347   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13348 
13349   enable_search = !!enable_search;
13350 
13351   if (priv->enable_search != enable_search)
13352     {
13353        priv->enable_search = enable_search;
13354        g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_SEARCH]);
13355     }
13356 }
13357 
13358 /**
13359  * gtk_tree_view_get_enable_search:
13360  * @tree_view: A `GtkTreeView`
13361  *
13362  * Returns whether or not the tree allows to start interactive searching
13363  * by typing in text.
13364  *
13365  * Returns: whether or not to let the user search interactively
13366  */
13367 gboolean
gtk_tree_view_get_enable_search(GtkTreeView * tree_view)13368 gtk_tree_view_get_enable_search (GtkTreeView *tree_view)
13369 {
13370   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13371 
13372   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13373 
13374   return priv->enable_search;
13375 }
13376 
13377 
13378 /**
13379  * gtk_tree_view_get_search_column:
13380  * @tree_view: A `GtkTreeView`
13381  *
13382  * Gets the column searched on by the interactive search code.
13383  *
13384  * Returns: the column the interactive search code searches in.
13385  */
13386 int
gtk_tree_view_get_search_column(GtkTreeView * tree_view)13387 gtk_tree_view_get_search_column (GtkTreeView *tree_view)
13388 {
13389   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13390 
13391   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
13392 
13393   return priv->search_column;
13394 }
13395 
13396 /**
13397  * gtk_tree_view_set_search_column:
13398  * @tree_view: A `GtkTreeView`
13399  * @column: the column of the model to search in, or -1 to disable searching
13400  *
13401  * Sets @column as the column where the interactive search code should
13402  * search in for the current model.
13403  *
13404  * If the search column is set, users can use the “start-interactive-search”
13405  * key binding to bring up search popup. The enable-search property controls
13406  * whether simply typing text will also start an interactive search.
13407  *
13408  * Note that @column refers to a column of the current model. The search
13409  * column is reset to -1 when the model is changed.
13410  */
13411 void
gtk_tree_view_set_search_column(GtkTreeView * tree_view,int column)13412 gtk_tree_view_set_search_column (GtkTreeView *tree_view,
13413 				 int          column)
13414 {
13415   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13416 
13417   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13418   g_return_if_fail (column >= -1);
13419 
13420   if (priv->search_column == column)
13421     return;
13422 
13423   priv->search_column = column;
13424   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SEARCH_COLUMN]);
13425 }
13426 
13427 /**
13428  * gtk_tree_view_get_search_equal_func: (skip)
13429  * @tree_view: A `GtkTreeView`
13430  *
13431  * Returns the compare function currently in use.
13432  *
13433  * Returns: the currently used compare function for the search code.
13434  */
13435 
13436 GtkTreeViewSearchEqualFunc
gtk_tree_view_get_search_equal_func(GtkTreeView * tree_view)13437 gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view)
13438 {
13439   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13440 
13441   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
13442 
13443   return priv->search_equal_func;
13444 }
13445 
13446 /**
13447  * gtk_tree_view_set_search_equal_func:
13448  * @tree_view: A `GtkTreeView`
13449  * @search_equal_func: the compare function to use during the search
13450  * @search_user_data: (nullable): user data to pass to @search_equal_func
13451  * @search_destroy: (nullable): Destroy notifier for @search_user_data
13452  *
13453  * Sets the compare function for the interactive search capabilities; note
13454  * that somewhat like strcmp() returning 0 for equality
13455  * `GtkTreeView`SearchEqualFunc returns %FALSE on matches.
13456  **/
13457 void
gtk_tree_view_set_search_equal_func(GtkTreeView * tree_view,GtkTreeViewSearchEqualFunc search_equal_func,gpointer search_user_data,GDestroyNotify search_destroy)13458 gtk_tree_view_set_search_equal_func (GtkTreeView                *tree_view,
13459 				     GtkTreeViewSearchEqualFunc  search_equal_func,
13460 				     gpointer                    search_user_data,
13461 				     GDestroyNotify              search_destroy)
13462 {
13463   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13464 
13465   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13466   g_return_if_fail (search_equal_func != NULL);
13467 
13468   if (priv->search_destroy)
13469     priv->search_destroy (priv->search_user_data);
13470 
13471   priv->search_equal_func = search_equal_func;
13472   priv->search_user_data = search_user_data;
13473   priv->search_destroy = search_destroy;
13474   if (priv->search_equal_func == NULL)
13475     priv->search_equal_func = gtk_tree_view_search_equal_func;
13476 }
13477 
13478 /**
13479  * gtk_tree_view_get_search_entry:
13480  * @tree_view: A `GtkTreeView`
13481  *
13482  * Returns the `GtkEntry` which is currently in use as interactive search
13483  * entry for @tree_view.  In case the built-in entry is being used, %NULL
13484  * will be returned.
13485  *
13486  * Returns: (transfer none) (nullable): the entry currently in use as search entry.
13487  */
13488 GtkEditable *
gtk_tree_view_get_search_entry(GtkTreeView * tree_view)13489 gtk_tree_view_get_search_entry (GtkTreeView *tree_view)
13490 {
13491   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13492 
13493   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
13494 
13495   if (priv->search_custom_entry_set)
13496     return GTK_EDITABLE (priv->search_entry);
13497 
13498   return NULL;
13499 }
13500 
13501 /**
13502  * gtk_tree_view_set_search_entry:
13503  * @tree_view: A `GtkTreeView`
13504  * @entry: (nullable): the entry the interactive search code of @tree_view should use
13505  *
13506  * Sets the entry which the interactive search code will use for this
13507  * @tree_view.  This is useful when you want to provide a search entry
13508  * in our interface at all time at a fixed position.  Passing %NULL for
13509  * @entry will make the interactive search code use the built-in popup
13510  * entry again.
13511  */
13512 void
gtk_tree_view_set_search_entry(GtkTreeView * tree_view,GtkEditable * entry)13513 gtk_tree_view_set_search_entry (GtkTreeView *tree_view,
13514 				GtkEditable *entry)
13515 {
13516   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13517 
13518   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13519   g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry) || GTK_IS_SEARCH_ENTRY (entry));
13520 
13521   if (priv->search_custom_entry_set)
13522     {
13523       if (priv->search_entry_changed_id)
13524         {
13525 	  g_signal_handler_disconnect (priv->search_entry,
13526 				       priv->search_entry_changed_id);
13527 	  priv->search_entry_changed_id = 0;
13528 	}
13529 
13530       g_signal_handlers_disconnect_by_func (gtk_entry_get_key_controller (GTK_ENTRY (priv->search_entry)),
13531 					    G_CALLBACK (gtk_tree_view_search_key_pressed),
13532 					    tree_view);
13533 
13534       g_object_unref (priv->search_entry);
13535     }
13536   else if (priv->search_popover)
13537     {
13538       gtk_tree_view_destroy_search_popover (tree_view);
13539     }
13540 
13541   if (entry)
13542     {
13543       GtkEventController *controller;
13544 
13545       priv->search_entry = GTK_WIDGET (g_object_ref (entry));
13546       priv->search_custom_entry_set = TRUE;
13547 
13548       if (priv->search_entry_changed_id == 0)
13549         {
13550           priv->search_entry_changed_id =
13551 	    g_signal_connect (priv->search_entry, "changed",
13552 			      G_CALLBACK (gtk_tree_view_search_init),
13553 			      tree_view);
13554 	}
13555 
13556       if (GTK_IS_ENTRY (entry))
13557         controller = gtk_entry_get_key_controller (GTK_ENTRY (entry));
13558       else
13559         controller = gtk_search_entry_get_key_controller (GTK_SEARCH_ENTRY (entry));
13560       g_signal_connect (controller, "key-pressed",
13561                         G_CALLBACK (gtk_tree_view_search_key_pressed), tree_view);
13562 
13563       gtk_tree_view_search_init (priv->search_entry, tree_view);
13564     }
13565   else
13566     {
13567       priv->search_entry = NULL;
13568       priv->search_custom_entry_set = FALSE;
13569     }
13570 }
13571 
13572 static void
gtk_tree_view_search_popover_hide(GtkWidget * search_popover,GtkTreeView * tree_view)13573 gtk_tree_view_search_popover_hide (GtkWidget   *search_popover,
13574                                    GtkTreeView *tree_view)
13575 {
13576   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13577 
13578   if (priv->disable_popdown)
13579     return;
13580 
13581   if (priv->search_entry_changed_id)
13582     {
13583       g_signal_handler_disconnect (priv->search_entry,
13584 				   priv->search_entry_changed_id);
13585       priv->search_entry_changed_id = 0;
13586     }
13587   if (priv->typeselect_flush_timeout)
13588     {
13589       g_source_remove (priv->typeselect_flush_timeout);
13590       priv->typeselect_flush_timeout = 0;
13591     }
13592 
13593   if (gtk_widget_get_visible (search_popover))
13594     {
13595       gtk_popover_popdown (GTK_POPOVER (search_popover));
13596       gtk_editable_set_text (GTK_EDITABLE (priv->search_entry), "");
13597       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
13598     }
13599 }
13600 
13601 /* Because we're visible but offscreen, we just set a flag in the preedit
13602  * callback.
13603  */
13604 static void
gtk_tree_view_search_preedit_changed(GtkText * text,const char * predit,GtkTreeView * tree_view)13605 gtk_tree_view_search_preedit_changed (GtkText      *text,
13606                                       const char   *predit,
13607 				      GtkTreeView  *tree_view)
13608 {
13609   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13610 
13611   priv->imcontext_changed = 1;
13612   if (priv->typeselect_flush_timeout)
13613     {
13614       g_source_remove (priv->typeselect_flush_timeout);
13615       priv->typeselect_flush_timeout =
13616 	g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
13617                        (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
13618 		       tree_view);
13619       gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout");
13620     }
13621 
13622 }
13623 
13624 static void
gtk_tree_view_search_changed(GtkEditable * editable,GtkTreeView * tree_view)13625 gtk_tree_view_search_changed (GtkEditable *editable,
13626                               GtkTreeView  *tree_view)
13627 {
13628   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13629 
13630   priv->imcontext_changed = 1;
13631 }
13632 
13633 static void
gtk_tree_view_search_activate(GtkEntry * entry,GtkTreeView * tree_view)13634 gtk_tree_view_search_activate (GtkEntry    *entry,
13635 			       GtkTreeView *tree_view)
13636 {
13637   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13638   GtkTreePath *path;
13639 
13640   gtk_tree_view_search_popover_hide (priv->search_popover, tree_view);
13641 
13642   /* If we have a row selected and it's the cursor row, we activate
13643    * the row XXX */
13644   if (priv->cursor_node &&
13645       GTK_TREE_RBNODE_FLAG_SET (priv->cursor_node, GTK_TREE_RBNODE_IS_SELECTED))
13646     {
13647       path = _gtk_tree_path_new_from_rbtree (priv->cursor_tree,
13648                                              priv->cursor_node);
13649 
13650       gtk_tree_view_row_activated (tree_view, path, priv->focus_column);
13651 
13652       gtk_tree_path_free (path);
13653     }
13654 }
13655 
13656 static void
gtk_tree_view_search_pressed_cb(GtkGesture * gesture,int n_press,double x,double y,GtkTreeView * tree_view)13657 gtk_tree_view_search_pressed_cb (GtkGesture  *gesture,
13658                                  int          n_press,
13659                                  double       x,
13660                                  double       y,
13661                                  GtkTreeView *tree_view)
13662 {
13663   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13664 
13665   gtk_tree_view_search_popover_hide (priv->search_popover, tree_view);
13666 }
13667 
13668 static gboolean
gtk_tree_view_search_scroll_event(GtkWidget * widget,double dx,double dy,GtkTreeView * tree_view)13669 gtk_tree_view_search_scroll_event (GtkWidget   *widget,
13670                                    double       dx,
13671                                    double       dy,
13672 				   GtkTreeView *tree_view)
13673 {
13674   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13675   GdkScrollDirection direction;
13676 
13677   direction = dy > 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
13678 
13679   if (direction == GDK_SCROLL_UP)
13680     gtk_tree_view_search_move (widget, tree_view, TRUE);
13681   else if (direction == GDK_SCROLL_DOWN)
13682     gtk_tree_view_search_move (widget, tree_view, FALSE);
13683 
13684   /* renew the flush timeout */
13685   if (priv->typeselect_flush_timeout &&
13686       !priv->search_custom_entry_set)
13687     {
13688       g_source_remove (priv->typeselect_flush_timeout);
13689       priv->typeselect_flush_timeout =
13690 	g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
13691 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
13692 		       tree_view);
13693       gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout");
13694     }
13695 
13696   return GDK_EVENT_STOP;
13697 }
13698 
13699 static gboolean
gtk_tree_view_search_key_pressed(GtkEventControllerKey * key,guint keyval,guint keycode,GdkModifierType state,GtkTreeView * tree_view)13700 gtk_tree_view_search_key_pressed (GtkEventControllerKey *key,
13701                                   guint                  keyval,
13702                                   guint                  keycode,
13703                                   GdkModifierType        state,
13704                                   GtkTreeView           *tree_view)
13705 {
13706   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13707   GtkWidget *widget = priv->search_entry;
13708   GdkModifierType default_accel;
13709   gboolean retval = FALSE;
13710 
13711   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
13712   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13713 
13714   /* close window and cancel the search */
13715   if (!priv->search_custom_entry_set
13716       && gtk_tree_view_search_key_cancels_search (keyval))
13717     {
13718       gtk_tree_view_search_popover_hide (priv->search_popover, tree_view);
13719       return TRUE;
13720     }
13721 
13722   default_accel = GDK_CONTROL_MASK;
13723 
13724   /* select previous matching iter */
13725   if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
13726     {
13727       if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
13728         gtk_widget_error_bell (widget);
13729 
13730       retval = TRUE;
13731     }
13732 
13733   if (((state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK))
13734       && (keyval == GDK_KEY_g || keyval == GDK_KEY_G))
13735     {
13736       if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
13737         gtk_widget_error_bell (widget);
13738 
13739       retval = TRUE;
13740     }
13741 
13742   /* select next matching iter */
13743   if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
13744     {
13745       if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
13746         gtk_widget_error_bell (widget);
13747 
13748       retval = TRUE;
13749     }
13750 
13751   if (((state & (default_accel | GDK_SHIFT_MASK)) == default_accel)
13752       && (keyval == GDK_KEY_g || keyval == GDK_KEY_G))
13753     {
13754       if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
13755         gtk_widget_error_bell (widget);
13756 
13757       retval = TRUE;
13758     }
13759 
13760   /* renew the flush timeout */
13761   if (retval && priv->typeselect_flush_timeout
13762       && !priv->search_custom_entry_set)
13763     {
13764       g_source_remove (priv->typeselect_flush_timeout);
13765       priv->typeselect_flush_timeout =
13766 	g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
13767 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
13768 		       tree_view);
13769       gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout");
13770     }
13771 
13772   if (!retval)
13773     gtk_event_controller_key_forward (key, priv->search_entry);
13774 
13775   return retval;
13776 }
13777 
13778 /*  this function returns FALSE if there is a search string but
13779  *  nothing was found, and TRUE otherwise.
13780  */
13781 static gboolean
gtk_tree_view_search_move(GtkWidget * popover,GtkTreeView * tree_view,gboolean up)13782 gtk_tree_view_search_move (GtkWidget   *popover,
13783 			   GtkTreeView *tree_view,
13784 			   gboolean     up)
13785 {
13786   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13787   gboolean ret;
13788   int len;
13789   int count = 0;
13790   const char *text;
13791   GtkTreeIter iter;
13792   GtkTreeModel *model;
13793   GtkTreeSelection *selection;
13794 
13795   text = gtk_editable_get_text (GTK_EDITABLE (priv->search_entry));
13796 
13797   g_return_val_if_fail (text != NULL, FALSE);
13798 
13799   len = strlen (text);
13800 
13801   if (up && priv->selected_iter == 1)
13802     return len < 1;
13803 
13804   if (len < 1)
13805     return TRUE;
13806 
13807   model = gtk_tree_view_get_model (tree_view);
13808   selection = gtk_tree_view_get_selection (tree_view);
13809 
13810   /* search */
13811   gtk_tree_selection_unselect_all (selection);
13812   if (!gtk_tree_model_get_iter_first (model, &iter))
13813     return TRUE;
13814 
13815   ret = gtk_tree_view_search_iter (model, selection, &iter, text,
13816 				   &count, up?((priv->selected_iter) - 1):((priv->selected_iter + 1)));
13817 
13818   if (ret)
13819     {
13820       /* found */
13821       priv->selected_iter += up?(-1):(1);
13822       return TRUE;
13823     }
13824   else
13825     {
13826       /* return to old iter */
13827       count = 0;
13828       gtk_tree_model_get_iter_first (model, &iter);
13829       gtk_tree_view_search_iter (model, selection,
13830 				 &iter, text,
13831 				 &count, priv->selected_iter);
13832       return FALSE;
13833     }
13834 }
13835 
13836 static gboolean
gtk_tree_view_search_equal_func(GtkTreeModel * model,int column,const char * key,GtkTreeIter * iter,gpointer search_data)13837 gtk_tree_view_search_equal_func (GtkTreeModel *model,
13838 				 int           column,
13839 				 const char   *key,
13840 				 GtkTreeIter  *iter,
13841 				 gpointer      search_data)
13842 {
13843   gboolean retval = TRUE;
13844   const char *str;
13845   char *normalized_string;
13846   char *normalized_key;
13847   char *case_normalized_string = NULL;
13848   char *case_normalized_key = NULL;
13849   GValue value = G_VALUE_INIT;
13850   GValue transformed = G_VALUE_INIT;
13851 
13852   gtk_tree_model_get_value (model, iter, column, &value);
13853 
13854   g_value_init (&transformed, G_TYPE_STRING);
13855 
13856   if (!g_value_transform (&value, &transformed))
13857     {
13858       g_value_unset (&value);
13859       return TRUE;
13860     }
13861 
13862   g_value_unset (&value);
13863 
13864   str = g_value_get_string (&transformed);
13865   if (!str)
13866     {
13867       g_value_unset (&transformed);
13868       return TRUE;
13869     }
13870 
13871   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
13872   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
13873 
13874   if (normalized_string && normalized_key)
13875     {
13876       case_normalized_string = g_utf8_casefold (normalized_string, -1);
13877       case_normalized_key = g_utf8_casefold (normalized_key, -1);
13878 
13879       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
13880         retval = FALSE;
13881     }
13882 
13883   g_value_unset (&transformed);
13884   g_free (normalized_key);
13885   g_free (normalized_string);
13886   g_free (case_normalized_key);
13887   g_free (case_normalized_string);
13888 
13889   return retval;
13890 }
13891 
13892 static gboolean
gtk_tree_view_search_iter(GtkTreeModel * model,GtkTreeSelection * selection,GtkTreeIter * iter,const char * text,int * count,int n)13893 gtk_tree_view_search_iter (GtkTreeModel     *model,
13894 			   GtkTreeSelection *selection,
13895 			   GtkTreeIter      *iter,
13896 			   const char       *text,
13897 			   int              *count,
13898 			   int               n)
13899 {
13900   GtkTreeRBTree *tree = NULL;
13901   GtkTreeRBNode *node = NULL;
13902   GtkTreePath *path;
13903 
13904   GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
13905   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
13906 
13907   path = gtk_tree_model_get_path (model, iter);
13908   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
13909 
13910   do
13911     {
13912       if (! priv->search_equal_func (model, priv->search_column, text, iter, priv->search_user_data))
13913         {
13914           (*count)++;
13915           if (*count == n)
13916             {
13917               gtk_tree_view_scroll_to_cell (tree_view, path, NULL,
13918 					    TRUE, 0.5, 0.0);
13919               gtk_tree_selection_select_iter (selection, iter);
13920               gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
13921 
13922 	      if (path)
13923 		gtk_tree_path_free (path);
13924 
13925               return TRUE;
13926             }
13927         }
13928 
13929       if (node->children)
13930 	{
13931 	  gboolean has_child;
13932 	  GtkTreeIter tmp;
13933 
13934 	  tree = node->children;
13935           node = gtk_tree_rbtree_first (tree);
13936 
13937 	  tmp = *iter;
13938 	  has_child = gtk_tree_model_iter_children (model, iter, &tmp);
13939 	  gtk_tree_path_down (path);
13940 
13941 	  /* sanity check */
13942 	  TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
13943 	}
13944       else
13945 	{
13946 	  gboolean done = FALSE;
13947 
13948 	  do
13949 	    {
13950 	      node = gtk_tree_rbtree_next (tree, node);
13951 
13952 	      if (node)
13953 		{
13954 		  gboolean has_next;
13955 
13956 		  has_next = gtk_tree_model_iter_next (model, iter);
13957 
13958 		  done = TRUE;
13959 		  gtk_tree_path_next (path);
13960 
13961 		  /* sanity check */
13962 		  TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
13963 		}
13964 	      else
13965 		{
13966 		  gboolean has_parent;
13967 		  GtkTreeIter tmp_iter = *iter;
13968 
13969 		  node = tree->parent_node;
13970 		  tree = tree->parent_tree;
13971 
13972 		  if (!tree)
13973 		    {
13974 		      if (path)
13975 			gtk_tree_path_free (path);
13976 
13977 		      /* we've run out of tree, done with this func */
13978 		      return FALSE;
13979 		    }
13980 
13981 		  has_parent = gtk_tree_model_iter_parent (model,
13982 							   iter,
13983 							   &tmp_iter);
13984 		  gtk_tree_path_up (path);
13985 
13986 		  /* sanity check */
13987 		  TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
13988 		}
13989 	    }
13990 	  while (!done);
13991 	}
13992     }
13993   while (1);
13994 
13995   return FALSE;
13996 }
13997 
13998 static void
gtk_tree_view_search_init(GtkWidget * entry,GtkTreeView * tree_view)13999 gtk_tree_view_search_init (GtkWidget   *entry,
14000 			   GtkTreeView *tree_view)
14001 {
14002   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14003   int ret;
14004   int count = 0;
14005   const char *text;
14006   GtkTreeIter iter;
14007   GtkTreeModel *model;
14008   GtkTreeSelection *selection;
14009 
14010   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14011 
14012   text = gtk_editable_get_text (GTK_EDITABLE (entry));
14013 
14014   model = gtk_tree_view_get_model (tree_view);
14015   selection = gtk_tree_view_get_selection (tree_view);
14016 
14017   /* search */
14018   gtk_tree_selection_unselect_all (selection);
14019   if (priv->typeselect_flush_timeout
14020       && !priv->search_custom_entry_set)
14021     {
14022       g_source_remove (priv->typeselect_flush_timeout);
14023       priv->typeselect_flush_timeout =
14024 	g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
14025 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
14026 		       tree_view);
14027       gdk_source_set_static_name_by_id (priv->typeselect_flush_timeout, "[gtk] gtk_tree_view_search_entry_flush_timeout");
14028     }
14029 
14030   if (*text == '\0')
14031     return;
14032 
14033   if (!gtk_tree_model_get_iter_first (model, &iter))
14034     return;
14035 
14036   ret = gtk_tree_view_search_iter (model, selection,
14037 				   &iter, text,
14038 				   &count, 1);
14039 
14040   if (ret)
14041     priv->selected_iter = 1;
14042 }
14043 
14044 void
_gtk_tree_view_remove_editable(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkCellEditable * cell_editable)14045 _gtk_tree_view_remove_editable (GtkTreeView       *tree_view,
14046                                 GtkTreeViewColumn *column,
14047                                 GtkCellEditable   *cell_editable)
14048 {
14049   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14050 
14051   if (priv->edited_column == NULL)
14052     return;
14053 
14054   g_return_if_fail (column == priv->edited_column);
14055 
14056   priv->edited_column = NULL;
14057 
14058   if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
14059     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
14060 
14061   gtk_tree_view_remove (tree_view, GTK_WIDGET (cell_editable));
14062 
14063   /* FIXME should only redraw a single node */
14064   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
14065 }
14066 
14067 static gboolean
gtk_tree_view_start_editing(GtkTreeView * tree_view,GtkTreePath * cursor_path,gboolean edit_only)14068 gtk_tree_view_start_editing (GtkTreeView *tree_view,
14069 			     GtkTreePath *cursor_path,
14070 			     gboolean     edit_only)
14071 {
14072   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14073   GtkTreeIter iter;
14074   GdkRectangle cell_area;
14075   GtkTreeViewColumn *focus_column;
14076   guint flags = 0; /* can be 0, as the flags are primarily for rendering */
14077   int retval = FALSE;
14078   GtkTreeRBTree *cursor_tree;
14079   GtkTreeRBNode *cursor_node;
14080 
14081   g_assert (priv->focus_column);
14082   focus_column = priv->focus_column;
14083 
14084   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
14085     return FALSE;
14086 
14087   if (_gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node) ||
14088       cursor_node == NULL)
14089     return FALSE;
14090 
14091   gtk_tree_model_get_iter (priv->model, &iter, cursor_path);
14092 
14093   validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path);
14094 
14095   gtk_tree_view_column_cell_set_cell_data (focus_column,
14096                                            priv->model,
14097                                            &iter,
14098                                            GTK_TREE_RBNODE_FLAG_SET (cursor_node, GTK_TREE_RBNODE_IS_PARENT),
14099                                            cursor_node->children ? TRUE : FALSE);
14100   gtk_tree_view_get_cell_area (tree_view,
14101                                cursor_path,
14102                                focus_column,
14103                                &cell_area);
14104 
14105   if (gtk_cell_area_activate (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (focus_column)),
14106                               _gtk_tree_view_column_get_context (focus_column),
14107                               GTK_WIDGET (tree_view),
14108                               &cell_area,
14109                               flags, edit_only))
14110     retval = TRUE;
14111 
14112   return retval;
14113 }
14114 
14115 void
_gtk_tree_view_add_editable(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkTreePath * path,GtkCellEditable * cell_editable,GdkRectangle * cell_area)14116 _gtk_tree_view_add_editable (GtkTreeView       *tree_view,
14117                              GtkTreeViewColumn *column,
14118                              GtkTreePath       *path,
14119                              GtkCellEditable   *cell_editable,
14120                              GdkRectangle      *cell_area)
14121 {
14122   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14123   GdkRectangle full_area;
14124   GtkBorder border;
14125 
14126   priv->edited_column = column;
14127 
14128   gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
14129 
14130   priv->draw_keyfocus = TRUE;
14131 
14132   gtk_tree_view_get_cell_area (tree_view, path, column, &full_area);
14133   border.left = cell_area->x - full_area.x;
14134   border.top = cell_area->y - full_area.y;
14135   border.right = (full_area.x + full_area.width) - (cell_area->x + cell_area->width);
14136   border.bottom = (full_area.y + full_area.height) - (cell_area->y + cell_area->height);
14137 
14138   gtk_tree_view_put (tree_view,
14139                      GTK_WIDGET (cell_editable),
14140                      path,
14141                      column,
14142                      &border);
14143 }
14144 
14145 static void
gtk_tree_view_stop_editing(GtkTreeView * tree_view,gboolean cancel_editing)14146 gtk_tree_view_stop_editing (GtkTreeView *tree_view,
14147 			    gboolean     cancel_editing)
14148 {
14149   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14150   GtkTreeViewColumn *column;
14151 
14152   if (priv->edited_column == NULL)
14153     return;
14154 
14155   /*
14156    * This is very evil. We need to do this, because
14157    * gtk_cell_editable_editing_done may trigger gtk_tree_view_row_changed
14158    * later on. If gtk_tree_view_row_changed notices
14159    * priv->edited_column != NULL, it'll call
14160    * gtk_tree_view_stop_editing again. Bad things will happen then.
14161    *
14162    * Please read that again if you intend to modify anything here.
14163    */
14164 
14165   column = priv->edited_column;
14166   gtk_cell_area_stop_editing (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)), cancel_editing);
14167   priv->edited_column = NULL;
14168 }
14169 
14170 
14171 /**
14172  * gtk_tree_view_set_hover_selection:
14173  * @tree_view: a `GtkTreeView`
14174  * @hover: %TRUE to enable hover selection mode
14175  *
14176  * Enables or disables the hover selection mode of @tree_view.
14177  * Hover selection makes the selected row follow the pointer.
14178  * Currently, this works only for the selection modes
14179  * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
14180  **/
14181 void
gtk_tree_view_set_hover_selection(GtkTreeView * tree_view,gboolean hover)14182 gtk_tree_view_set_hover_selection (GtkTreeView *tree_view,
14183 				   gboolean     hover)
14184 {
14185   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14186 
14187   hover = hover != FALSE;
14188 
14189   if (hover != priv->hover_selection)
14190     {
14191       priv->hover_selection = hover;
14192 
14193       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_SELECTION]);
14194     }
14195 }
14196 
14197 /**
14198  * gtk_tree_view_get_hover_selection:
14199  * @tree_view: a `GtkTreeView`
14200  *
14201  * Returns whether hover selection mode is turned on for @tree_view.
14202  *
14203  * Returns: %TRUE if @tree_view is in hover selection mode
14204  **/
14205 gboolean
gtk_tree_view_get_hover_selection(GtkTreeView * tree_view)14206 gtk_tree_view_get_hover_selection (GtkTreeView *tree_view)
14207 {
14208   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14209 
14210   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14211 
14212   return priv->hover_selection;
14213 }
14214 
14215 /**
14216  * gtk_tree_view_set_hover_expand:
14217  * @tree_view: a `GtkTreeView`
14218  * @expand: %TRUE to enable hover selection mode
14219  *
14220  * Enables or disables the hover expansion mode of @tree_view.
14221  * Hover expansion makes rows expand or collapse if the pointer
14222  * moves over them.
14223  **/
14224 void
gtk_tree_view_set_hover_expand(GtkTreeView * tree_view,gboolean expand)14225 gtk_tree_view_set_hover_expand (GtkTreeView *tree_view,
14226 				gboolean     expand)
14227 {
14228   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14229 
14230   expand = expand != FALSE;
14231 
14232   if (expand != priv->hover_expand)
14233     {
14234       priv->hover_expand = expand;
14235 
14236       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_EXPAND]);
14237     }
14238 }
14239 
14240 /**
14241  * gtk_tree_view_get_hover_expand:
14242  * @tree_view: a `GtkTreeView`
14243  *
14244  * Returns whether hover expansion mode is turned on for @tree_view.
14245  *
14246  * Returns: %TRUE if @tree_view is in hover expansion mode
14247  **/
14248 gboolean
gtk_tree_view_get_hover_expand(GtkTreeView * tree_view)14249 gtk_tree_view_get_hover_expand (GtkTreeView *tree_view)
14250 {
14251   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14252 
14253   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14254 
14255   return priv->hover_expand;
14256 }
14257 
14258 /**
14259  * gtk_tree_view_set_rubber_banding:
14260  * @tree_view: a `GtkTreeView`
14261  * @enable: %TRUE to enable rubber banding
14262  *
14263  * Enables or disables rubber banding in @tree_view.  If the selection mode
14264  * is %GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select
14265  * multiple rows by dragging the mouse.
14266  **/
14267 void
gtk_tree_view_set_rubber_banding(GtkTreeView * tree_view,gboolean enable)14268 gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view,
14269 				  gboolean     enable)
14270 {
14271   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14272 
14273   enable = enable != FALSE;
14274 
14275   if (enable != priv->rubber_banding_enable)
14276     {
14277       priv->rubber_banding_enable = enable;
14278 
14279       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_RUBBER_BANDING]);
14280     }
14281 }
14282 
14283 /**
14284  * gtk_tree_view_get_rubber_banding:
14285  * @tree_view: a `GtkTreeView`
14286  *
14287  * Returns whether rubber banding is turned on for @tree_view.  If the
14288  * selection mode is %GTK_SELECTION_MULTIPLE, rubber banding will allow the
14289  * user to select multiple rows by dragging the mouse.
14290  *
14291  * Returns: %TRUE if rubber banding in @tree_view is enabled.
14292  **/
14293 gboolean
gtk_tree_view_get_rubber_banding(GtkTreeView * tree_view)14294 gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view)
14295 {
14296   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14297 
14298   return priv->rubber_banding_enable;
14299 }
14300 
14301 /**
14302  * gtk_tree_view_is_rubber_banding_active:
14303  * @tree_view: a `GtkTreeView`
14304  *
14305  * Returns whether a rubber banding operation is currently being done
14306  * in @tree_view.
14307  *
14308  * Returns: %TRUE if a rubber banding operation is currently being
14309  * done in @tree_view.
14310  **/
14311 gboolean
gtk_tree_view_is_rubber_banding_active(GtkTreeView * tree_view)14312 gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view)
14313 {
14314   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14315 
14316   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14317 
14318   if (priv->rubber_banding_enable
14319       && priv->rubber_band_status == RUBBER_BAND_ACTIVE)
14320     return TRUE;
14321 
14322   return FALSE;
14323 }
14324 
14325 /**
14326  * gtk_tree_view_get_row_separator_func: (skip)
14327  * @tree_view: a `GtkTreeView`
14328  *
14329  * Returns the current row separator function.
14330  *
14331  * Returns: the current row separator function.
14332  **/
14333 GtkTreeViewRowSeparatorFunc
gtk_tree_view_get_row_separator_func(GtkTreeView * tree_view)14334 gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view)
14335 {
14336   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14337 
14338   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14339 
14340   return priv->row_separator_func;
14341 }
14342 
14343 /**
14344  * gtk_tree_view_set_row_separator_func:
14345  * @tree_view: a `GtkTreeView`
14346  * @func: (nullable): a `GtkTreeView`RowSeparatorFunc
14347  * @data: (nullable): user data to pass to @func
14348  * @destroy: (nullable): destroy notifier for @data
14349  *
14350  * Sets the row separator function, which is used to determine
14351  * whether a row should be drawn as a separator. If the row separator
14352  * function is %NULL, no separators are drawn. This is the default value.
14353  **/
14354 void
gtk_tree_view_set_row_separator_func(GtkTreeView * tree_view,GtkTreeViewRowSeparatorFunc func,gpointer data,GDestroyNotify destroy)14355 gtk_tree_view_set_row_separator_func (GtkTreeView                 *tree_view,
14356 				      GtkTreeViewRowSeparatorFunc  func,
14357 				      gpointer                     data,
14358 				      GDestroyNotify               destroy)
14359 {
14360   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14361 
14362   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14363 
14364   if (priv->row_separator_destroy)
14365     priv->row_separator_destroy (priv->row_separator_data);
14366 
14367   priv->row_separator_func = func;
14368   priv->row_separator_data = data;
14369   priv->row_separator_destroy = destroy;
14370 
14371   /* Have the tree recalculate heights */
14372   gtk_tree_rbtree_mark_invalid (priv->tree);
14373   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
14374 }
14375 
14376 /**
14377  * gtk_tree_view_get_grid_lines:
14378  * @tree_view: a `GtkTreeView`
14379  *
14380  * Returns which grid lines are enabled in @tree_view.
14381  *
14382  * Returns: a `GtkTreeView`GridLines value indicating which grid lines
14383  * are enabled.
14384  */
14385 GtkTreeViewGridLines
gtk_tree_view_get_grid_lines(GtkTreeView * tree_view)14386 gtk_tree_view_get_grid_lines (GtkTreeView *tree_view)
14387 {
14388   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14389 
14390   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
14391 
14392   return priv->grid_lines;
14393 }
14394 
14395 /**
14396  * gtk_tree_view_set_grid_lines:
14397  * @tree_view: a `GtkTreeView`
14398  * @grid_lines: a `GtkTreeView`GridLines value indicating which grid lines to
14399  * enable.
14400  *
14401  * Sets which grid lines to draw in @tree_view.
14402  */
14403 void
gtk_tree_view_set_grid_lines(GtkTreeView * tree_view,GtkTreeViewGridLines grid_lines)14404 gtk_tree_view_set_grid_lines (GtkTreeView           *tree_view,
14405 			      GtkTreeViewGridLines   grid_lines)
14406 {
14407   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14408   GtkTreeViewGridLines old_grid_lines;
14409 
14410   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14411 
14412   old_grid_lines = priv->grid_lines;
14413   priv->grid_lines = grid_lines;
14414 
14415   if (old_grid_lines != grid_lines)
14416     {
14417       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
14418 
14419       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_GRID_LINES]);
14420     }
14421 }
14422 
14423 /**
14424  * gtk_tree_view_get_enable_tree_lines:
14425  * @tree_view: a `GtkTreeView`.
14426  *
14427  * Returns whether or not tree lines are drawn in @tree_view.
14428  *
14429  * Returns: %TRUE if tree lines are drawn in @tree_view, %FALSE
14430  * otherwise.
14431  */
14432 gboolean
gtk_tree_view_get_enable_tree_lines(GtkTreeView * tree_view)14433 gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view)
14434 {
14435   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14436 
14437   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14438 
14439   return priv->tree_lines_enabled;
14440 }
14441 
14442 /**
14443  * gtk_tree_view_set_enable_tree_lines:
14444  * @tree_view: a `GtkTreeView`
14445  * @enabled: %TRUE to enable tree line drawing, %FALSE otherwise.
14446  *
14447  * Sets whether to draw lines interconnecting the expanders in @tree_view.
14448  * This does not have any visible effects for lists.
14449  */
14450 void
gtk_tree_view_set_enable_tree_lines(GtkTreeView * tree_view,gboolean enabled)14451 gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view,
14452 				     gboolean     enabled)
14453 {
14454   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14455   gboolean was_enabled;
14456 
14457   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14458 
14459   enabled = enabled != FALSE;
14460 
14461   was_enabled = priv->tree_lines_enabled;
14462 
14463   priv->tree_lines_enabled = enabled;
14464 
14465   if (was_enabled != enabled)
14466     {
14467       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
14468 
14469       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_TREE_LINES]);
14470     }
14471 }
14472 
14473 
14474 /**
14475  * gtk_tree_view_set_show_expanders:
14476  * @tree_view: a `GtkTreeView`
14477  * @enabled: %TRUE to enable expander drawing, %FALSE otherwise.
14478  *
14479  * Sets whether to draw and enable expanders and indent child rows in
14480  * @tree_view.  When disabled there will be no expanders visible in trees
14481  * and there will be no way to expand and collapse rows by default.  Also
14482  * note that hiding the expanders will disable the default indentation.  You
14483  * can set a custom indentation in this case using
14484  * gtk_tree_view_set_level_indentation().
14485  * This does not have any visible effects for lists.
14486  */
14487 void
gtk_tree_view_set_show_expanders(GtkTreeView * tree_view,gboolean enabled)14488 gtk_tree_view_set_show_expanders (GtkTreeView *tree_view,
14489 				  gboolean     enabled)
14490 {
14491   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14492 
14493   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14494 
14495   enabled = enabled != FALSE;
14496   if (priv->show_expanders != enabled)
14497     {
14498       priv->show_expanders = enabled;
14499       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
14500       g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SHOW_EXPANDERS]);
14501     }
14502 }
14503 
14504 /**
14505  * gtk_tree_view_get_show_expanders:
14506  * @tree_view: a `GtkTreeView`.
14507  *
14508  * Returns whether or not expanders are drawn in @tree_view.
14509  *
14510  * Returns: %TRUE if expanders are drawn in @tree_view, %FALSE
14511  * otherwise.
14512  */
14513 gboolean
gtk_tree_view_get_show_expanders(GtkTreeView * tree_view)14514 gtk_tree_view_get_show_expanders (GtkTreeView *tree_view)
14515 {
14516   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14517 
14518   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14519 
14520   return priv->show_expanders;
14521 }
14522 
14523 /**
14524  * gtk_tree_view_set_level_indentation:
14525  * @tree_view: a `GtkTreeView`
14526  * @indentation: the amount, in pixels, of extra indentation in @tree_view.
14527  *
14528  * Sets the amount of extra indentation for child levels to use in @tree_view
14529  * in addition to the default indentation.  The value should be specified in
14530  * pixels, a value of 0 disables this feature and in this case only the default
14531  * indentation will be used.
14532  * This does not have any visible effects for lists.
14533  */
14534 void
gtk_tree_view_set_level_indentation(GtkTreeView * tree_view,int indentation)14535 gtk_tree_view_set_level_indentation (GtkTreeView *tree_view,
14536 				     int          indentation)
14537 {
14538   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14539 
14540   priv->level_indentation = indentation;
14541 
14542   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
14543 }
14544 
14545 /**
14546  * gtk_tree_view_get_level_indentation:
14547  * @tree_view: a `GtkTreeView`.
14548  *
14549  * Returns the amount, in pixels, of extra indentation for child levels
14550  * in @tree_view.
14551  *
14552  * Returns: the amount of extra indentation for child levels in
14553  * @tree_view.  A return value of 0 means that this feature is disabled.
14554  */
14555 int
gtk_tree_view_get_level_indentation(GtkTreeView * tree_view)14556 gtk_tree_view_get_level_indentation (GtkTreeView *tree_view)
14557 {
14558   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14559 
14560   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
14561 
14562   return priv->level_indentation;
14563 }
14564 
14565 /**
14566  * gtk_tree_view_set_tooltip_row:
14567  * @tree_view: a `GtkTreeView`
14568  * @tooltip: a `GtkTooltip`
14569  * @path: a `GtkTreePath`
14570  *
14571  * Sets the tip area of @tooltip to be the area covered by the row at @path.
14572  * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
14573  * See also gtk_tooltip_set_tip_area().
14574  */
14575 void
gtk_tree_view_set_tooltip_row(GtkTreeView * tree_view,GtkTooltip * tooltip,GtkTreePath * path)14576 gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view,
14577 			       GtkTooltip  *tooltip,
14578 			       GtkTreePath *path)
14579 {
14580   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14581   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
14582 
14583   gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
14584 }
14585 
14586 /**
14587  * gtk_tree_view_set_tooltip_cell:
14588  * @tree_view: a `GtkTreeView`
14589  * @tooltip: a `GtkTooltip`
14590  * @path: (nullable): a `GtkTreePath`
14591  * @column: (nullable): a `GtkTreeViewColumn`
14592  * @cell: (nullable): a `GtkCellRenderer`
14593  *
14594  * Sets the tip area of @tooltip to the area @path, @column and @cell have
14595  * in common.  For example if @path is %NULL and @column is set, the tip
14596  * area will be set to the full area covered by @column.  See also
14597  * gtk_tooltip_set_tip_area().
14598  *
14599  * Note that if @path is not specified and @cell is set and part of a column
14600  * containing the expander, the tooltip might not show and hide at the correct
14601  * position.  In such cases @path must be set to the current node under the
14602  * mouse cursor for this function to operate correctly.
14603  *
14604  * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
14605  */
14606 void
gtk_tree_view_set_tooltip_cell(GtkTreeView * tree_view,GtkTooltip * tooltip,GtkTreePath * path,GtkTreeViewColumn * column,GtkCellRenderer * cell)14607 gtk_tree_view_set_tooltip_cell (GtkTreeView       *tree_view,
14608 				GtkTooltip        *tooltip,
14609 				GtkTreePath       *path,
14610 				GtkTreeViewColumn *column,
14611 				GtkCellRenderer   *cell)
14612 {
14613   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14614   GdkRectangle rect;
14615 
14616   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14617   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
14618   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
14619   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
14620 
14621   /* Determine x values. */
14622   if (column && cell)
14623     {
14624       GdkRectangle tmp;
14625       int start, width;
14626 
14627       /* We always pass in path here, whether it is NULL or not.
14628        * For cells in expander columns path must be specified so that
14629        * we can correctly account for the indentation.  This also means
14630        * that the tooltip is constrained vertically by the "Determine y
14631        * values" code below; this is not a real problem since cells actually
14632        * don't stretch vertically in contrast to columns.
14633        */
14634       gtk_tree_view_get_cell_area (tree_view, path, column, &tmp);
14635       gtk_tree_view_column_cell_get_position (column, cell, &start, &width);
14636 
14637       gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
14638 							 tmp.x + start, 0,
14639 							 &rect.x, NULL);
14640       rect.width = width;
14641     }
14642   else if (column)
14643     {
14644       GdkRectangle tmp;
14645 
14646       gtk_tree_view_get_background_area (tree_view, NULL, column, &tmp);
14647       gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
14648 							 tmp.x, 0,
14649 							 &rect.x, NULL);
14650       rect.width = tmp.width;
14651     }
14652   else
14653     {
14654       rect.x = 0;
14655       rect.width = gtk_widget_get_width (GTK_WIDGET (tree_view));;
14656     }
14657 
14658   /* Determine y values. */
14659   if (path)
14660     {
14661       GdkRectangle tmp;
14662 
14663       gtk_tree_view_get_background_area (tree_view, path, NULL, &tmp);
14664       gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
14665 							 0, tmp.y,
14666 							 NULL, &rect.y);
14667       rect.height = tmp.height;
14668     }
14669   else
14670     {
14671       rect.y = 0;
14672       rect.height = gtk_adjustment_get_page_size (priv->vadjustment);
14673     }
14674 
14675   gtk_tooltip_set_tip_area (tooltip, &rect);
14676 }
14677 
14678 /**
14679  * gtk_tree_view_get_tooltip_context:
14680  * @tree_view: a `GtkTreeView`
14681  * @x: the x coordinate (relative to widget coordinates)
14682  * @y: the y coordinate (relative to widget coordinates)
14683  * @keyboard_tip: whether this is a keyboard tooltip or not
14684  * @model: (out) (optional) (nullable) (transfer none): a pointer to
14685  *   receive a `GtkTreeModel`
14686  * @path: (out) (optional): a pointer to receive a `GtkTreePath`
14687  * @iter: (out) (optional): a pointer to receive a `GtkTreeIter`
14688  *
14689  * This function is supposed to be used in a ::query-tooltip
14690  * signal handler for `GtkTreeView`. The @x, @y and @keyboard_tip values
14691  * which are received in the signal handler, should be passed to this
14692  * function without modification.
14693  *
14694  * The return value indicates whether there is a tree view row at the given
14695  * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
14696  * tooltips the row returned will be the cursor row. When %TRUE, then any of
14697  * @model, @path and @iter which have been provided will be set to point to
14698  * that row and the corresponding model. @x and @y will always be converted
14699  * to be relative to @tree_view’s bin_window if @keyboard_tooltip is %FALSE.
14700  *
14701  * Returns: whether or not the given tooltip context points to a row
14702  */
14703 gboolean
gtk_tree_view_get_tooltip_context(GtkTreeView * tree_view,int x,int y,gboolean keyboard_tip,GtkTreeModel ** model,GtkTreePath ** path,GtkTreeIter * iter)14704 gtk_tree_view_get_tooltip_context (GtkTreeView   *tree_view,
14705 				   int            x,
14706 				   int            y,
14707 				   gboolean       keyboard_tip,
14708 				   GtkTreeModel **model,
14709 				   GtkTreePath  **path,
14710 				   GtkTreeIter   *iter)
14711 {
14712   GtkTreePath *tmppath = NULL;
14713 
14714   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14715 
14716   if (keyboard_tip)
14717     {
14718       gtk_tree_view_get_cursor (tree_view, &tmppath, NULL);
14719 
14720       if (!tmppath)
14721 	return FALSE;
14722     }
14723   else
14724     {
14725       int rel_x, rel_y;
14726 
14727       gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y,
14728                                                          &rel_x, &rel_y);
14729 
14730       if (!gtk_tree_view_get_path_at_pos (tree_view, rel_x, rel_y,
14731 					  &tmppath, NULL, NULL, NULL))
14732 	return FALSE;
14733     }
14734 
14735   if (model)
14736     *model = gtk_tree_view_get_model (tree_view);
14737 
14738   if (iter)
14739     gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view),
14740 			     iter, tmppath);
14741 
14742   if (path)
14743     *path = tmppath;
14744   else
14745     gtk_tree_path_free (tmppath);
14746 
14747   return TRUE;
14748 }
14749 
14750 static gboolean
gtk_tree_view_set_tooltip_query_cb(GtkWidget * widget,int x,int y,gboolean keyboard_tip,GtkTooltip * tooltip,gpointer data)14751 gtk_tree_view_set_tooltip_query_cb (GtkWidget  *widget,
14752 				    int         x,
14753 				    int         y,
14754 				    gboolean    keyboard_tip,
14755 				    GtkTooltip *tooltip,
14756 				    gpointer    data)
14757 {
14758   GValue value = G_VALUE_INIT;
14759   GValue transformed = G_VALUE_INIT;
14760   GtkTreeIter iter;
14761   GtkTreePath *path;
14762   GtkTreeModel *model;
14763   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
14764   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14765 
14766   if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
14767 					  x, y,
14768 					  keyboard_tip,
14769 					  &model, &path, &iter))
14770     return FALSE;
14771 
14772   gtk_tree_model_get_value (model, &iter,
14773                             priv->tooltip_column, &value);
14774 
14775   g_value_init (&transformed, G_TYPE_STRING);
14776 
14777   if (!g_value_transform (&value, &transformed))
14778     {
14779       g_value_unset (&value);
14780       gtk_tree_path_free (path);
14781 
14782       return FALSE;
14783     }
14784 
14785   g_value_unset (&value);
14786 
14787   if (!g_value_get_string (&transformed))
14788     {
14789       g_value_unset (&transformed);
14790       gtk_tree_path_free (path);
14791 
14792       return FALSE;
14793     }
14794 
14795   gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
14796   gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
14797 
14798   gtk_tree_path_free (path);
14799   g_value_unset (&transformed);
14800 
14801   return TRUE;
14802 }
14803 
14804 /**
14805  * gtk_tree_view_set_tooltip_column:
14806  * @tree_view: a `GtkTreeView`
14807  * @column: an integer, which is a valid column number for @tree_view’s model
14808  *
14809  * If you only plan to have simple (text-only) tooltips on full rows, you
14810  * can use this function to have `GtkTreeView` handle these automatically
14811  * for you. @column should be set to the column in @tree_view’s model
14812  * containing the tooltip texts, or -1 to disable this feature.
14813  *
14814  * When enabled, `GtkWidget:has-tooltip` will be set to %TRUE and
14815  * @tree_view will connect a `GtkWidget::query-tooltip` signal handler.
14816  *
14817  * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
14818  * so &, <, etc have to be escaped in the text.
14819  */
14820 void
gtk_tree_view_set_tooltip_column(GtkTreeView * tree_view,int column)14821 gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view,
14822 			          int          column)
14823 {
14824   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14825 
14826   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14827 
14828   if (column == priv->tooltip_column)
14829     return;
14830 
14831   if (column == -1)
14832     {
14833       g_signal_handlers_disconnect_by_func (tree_view,
14834 	  				    gtk_tree_view_set_tooltip_query_cb,
14835 					    NULL);
14836       gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
14837     }
14838   else
14839     {
14840       if (priv->tooltip_column == -1)
14841         {
14842           g_signal_connect (tree_view, "query-tooltip",
14843 		            G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL);
14844           gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
14845         }
14846     }
14847 
14848   priv->tooltip_column = column;
14849   g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_TOOLTIP_COLUMN]);
14850 }
14851 
14852 /**
14853  * gtk_tree_view_get_tooltip_column:
14854  * @tree_view: a `GtkTreeView`
14855  *
14856  * Returns the column of @tree_view’s model which is being used for
14857  * displaying tooltips on @tree_view’s rows.
14858  *
14859  * Returns: the index of the tooltip column that is currently being
14860  * used, or -1 if this is disabled.
14861  */
14862 int
gtk_tree_view_get_tooltip_column(GtkTreeView * tree_view)14863 gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view)
14864 {
14865   GtkTreeViewPrivate *priv = gtk_tree_view_get_instance_private (tree_view);
14866 
14867   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
14868 
14869   return priv->tooltip_column;
14870 }
14871 
14872 static gboolean
gtk_tree_view_get_border(GtkScrollable * scrollable,GtkBorder * border)14873 gtk_tree_view_get_border (GtkScrollable *scrollable,
14874                           GtkBorder     *border)
14875 {
14876   border->top = gtk_tree_view_get_effective_header_height (GTK_TREE_VIEW (scrollable));
14877 
14878   return TRUE;
14879 }
14880 
14881 static void
gtk_tree_view_scrollable_init(GtkScrollableInterface * iface)14882 gtk_tree_view_scrollable_init (GtkScrollableInterface *iface)
14883 {
14884   iface->get_border = gtk_tree_view_get_border;
14885 }
14886