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

source code of gtk/gtk/gtktreeview.c