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