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 <math.h>
22 #include <string.h>
23
24 #include "gtktreeview.h"
25
26 #include "gtkadjustmentprivate.h"
27 #include "gtkcssnumbervalueprivate.h"
28 #include "gtkrbtree.h"
29 #include "gtktreednd.h"
30 #include "gtktreeprivate.h"
31 #include "gtkcellrenderer.h"
32 #include "gtkmarshalers.h"
33 #include "gtkbuildable.h"
34 #include "gtkbutton.h"
35 #include "gtklabel.h"
36 #include "gtkbox.h"
37 #include "gtkintl.h"
38 #include "gtkbindings.h"
39 #include "gtkcontainer.h"
40 #include "gtkentry.h"
41 #include "gtkframe.h"
42 #include "gtkmain.h"
43 #include "gtktreemodelsort.h"
44 #include "gtktooltip.h"
45 #include "gtkscrollable.h"
46 #include "gtkcelllayout.h"
47 #include "gtkprivate.h"
48 #include "gtkwidgetprivate.h"
49 #include "gtkentryprivate.h"
50 #include "gtkstylecontextprivate.h"
51 #include "gtkcssstylepropertyprivate.h"
52 #include "gtkcssrgbavalueprivate.h"
53 #include "gtktypebuiltins.h"
54 #include "gtkmain.h"
55 #include "gtksettingsprivate.h"
56 #include "gtkwidgetpath.h"
57 #include "gtkpixelcacheprivate.h"
58 #include "a11y/gtktreeviewaccessibleprivate.h"
59
60
61 /**
62 * SECTION:gtktreeview
63 * @Short_description: A widget for displaying both trees and lists
64 * @Title: GtkTreeView
65 * @See_also: #GtkTreeViewColumn, #GtkTreeSelection, #GtkTreeModel,
66 * [GtkTreeView drag-and-drop][gtk3-GtkTreeView-drag-and-drop],
67 * #GtkTreeSortable, #GtkTreeModelSort, #GtkListStore, #GtkTreeStore,
68 * #GtkCellRenderer, #GtkCellEditable, #GtkCellRendererPixbuf,
69 * #GtkCellRendererText, #GtkCellRendererToggle
70 *
71 * Widget that displays any object that implements the #GtkTreeModel interface.
72 *
73 * Please refer to the
74 * [tree widget conceptual overview][TreeWidget]
75 * for an overview of all the objects and data types related
76 * to the tree widget and how they work together.
77 *
78 * Several different coordinate systems are exposed in the GtkTreeView API.
79 * These are:
80 *
81 * ![](tree-view-coordinates.png)
82 *
83 * Coordinate systems in GtkTreeView API:
84 *
85 * - Widget coordinates: Coordinates relative to the widget (usually `widget->window`).
86 *
87 * - Bin window coordinates: Coordinates relative to the window that GtkTreeView renders to.
88 *
89 * - Tree coordinates: Coordinates relative to the entire scrollable area of GtkTreeView. These
90 * coordinates start at (0, 0) for row 0 of the tree.
91 *
92 * Several functions are available for converting between the different
93 * coordinate systems. The most common translations are between widget and bin
94 * window coordinates and between bin window and tree coordinates. For the
95 * former you can use gtk_tree_view_convert_widget_to_bin_window_coords()
96 * (and vice versa), for the latter gtk_tree_view_convert_bin_window_to_tree_coords()
97 * (and vice versa).
98 *
99 * # GtkTreeView as GtkBuildable
100 *
101 * The GtkTreeView implementation of the GtkBuildable interface accepts
102 * #GtkTreeViewColumn objects as `<child>` elements and exposes the internal
103 * #GtkTreeSelection in UI definitions.
104 *
105 * An example of a UI definition fragment with GtkTreeView:
106 *
107 * |[<!-- language="xml" -->
108 * <object class="GtkTreeView" id="treeview">
109 * <property name="model">liststore1</property>
110 * <child>
111 * <object class="GtkTreeViewColumn" id="test-column">
112 * <property name="title">Test</property>
113 * <child>
114 * <object class="GtkCellRendererText" id="test-renderer"/>
115 * <attributes>
116 * <attribute name="text">1</attribute>
117 * </attributes>
118 * </child>
119 * </object>
120 * </child>
121 * <child internal-child="selection">
122 * <object class="GtkTreeSelection" id="selection">
123 * <signal name="changed" handler="on_treeview_selection_changed"/>
124 * </object>
125 * </child>
126 * </object>
127 * ]|
128 *
129 * # CSS nodes
130 *
131 * |[<!-- language="plain" -->
132 * treeview.view
133 * ├── header
134 * │ ├── <column header>
135 * ┊ ┊
136 * │ ╰── <column header>
137 * │
138 * ╰── [rubberband]
139 * ]|
140 *
141 * GtkTreeView has a main CSS node with name treeview and style class .view.
142 * It has a subnode with name header, which is the parent for all the column
143 * header widgets' CSS nodes.
144 * For rubberband selection, a subnode with name rubberband is used.
145 */
146
147 enum
148 {
149 DRAG_COLUMN_WINDOW_STATE_UNSET = 0,
150 DRAG_COLUMN_WINDOW_STATE_ORIGINAL = 1,
151 DRAG_COLUMN_WINDOW_STATE_ARROW = 2,
152 DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT = 3,
153 DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT = 4
154 };
155
156 enum
157 {
158 RUBBER_BAND_OFF = 0,
159 RUBBER_BAND_MAYBE_START = 1,
160 RUBBER_BAND_ACTIVE = 2
161 };
162
163 typedef enum {
164 CLEAR_AND_SELECT = (1 << 0),
165 CLAMP_NODE = (1 << 1),
166 CURSOR_INVALID = (1 << 2)
167 } SetCursorFlags;
168
169 /* This lovely little value is used to determine how far away from the title bar
170 * you can move the mouse and still have a column drag work.
171 */
172 #define TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER(tree_view) (10*gtk_tree_view_get_effective_header_height(tree_view))
173
174 #ifdef __GNUC__
175
176 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
177 if (!(expr)) \
178 { \
179 g_log (G_LOG_DOMAIN, \
180 G_LOG_LEVEL_CRITICAL, \
181 "%s (%s): assertion `%s' failed.\n" \
182 "There is a disparity between the internal view of the GtkTreeView,\n" \
183 "and the GtkTreeModel. This generally means that the model has changed\n"\
184 "without letting the view know. Any display from now on is likely to\n" \
185 "be incorrect.\n", \
186 G_STRLOC, \
187 G_STRFUNC, \
188 #expr); \
189 return ret; \
190 }; }G_STMT_END
191
192 #define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
193 if (!(expr)) \
194 { \
195 g_log (G_LOG_DOMAIN, \
196 G_LOG_LEVEL_CRITICAL, \
197 "%s (%s): assertion `%s' failed.\n" \
198 "There is a disparity between the internal view of the GtkTreeView,\n" \
199 "and the GtkTreeModel. This generally means that the model has changed\n"\
200 "without letting the view know. Any display from now on is likely to\n" \
201 "be incorrect.\n", \
202 G_STRLOC, \
203 G_STRFUNC, \
204 #expr); \
205 return; \
206 }; }G_STMT_END
207
208 #else
209
210 #define TREE_VIEW_INTERNAL_ASSERT(expr, ret) G_STMT_START{ \
211 if (!(expr)) \
212 { \
213 g_log (G_LOG_DOMAIN, \
214 G_LOG_LEVEL_CRITICAL, \
215 "file %s: line %d: assertion `%s' failed.\n" \
216 "There is a disparity between the internal view of the GtkTreeView,\n" \
217 "and the GtkTreeModel. This generally means that the model has changed\n"\
218 "without letting the view know. Any display from now on is likely to\n" \
219 "be incorrect.\n", \
220 __FILE__, \
221 __LINE__, \
222 #expr); \
223 return ret; \
224 }; }G_STMT_END
225
226 #define TREE_VIEW_INTERNAL_ASSERT_VOID(expr) G_STMT_START{ \
227 if (!(expr)) \
228 { \
229 g_log (G_LOG_DOMAIN, \
230 G_LOG_LEVEL_CRITICAL, \
231 "file %s: line %d: assertion '%s' failed.\n" \
232 "There is a disparity between the internal view of the GtkTreeView,\n" \
233 "and the GtkTreeModel. This generally means that the model has changed\n"\
234 "without letting the view know. Any display from now on is likely to\n" \
235 "be incorrect.\n", \
236 __FILE__, \
237 __LINE__, \
238 #expr); \
239 return; \
240 }; }G_STMT_END
241 #endif
242
243 #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
244 #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2)
245 /* 3/5 of gdkframeclockidle.c's FRAME_INTERVAL (16667 microsecs) */
246 #define GTK_TREE_VIEW_TIME_MS_PER_IDLE 10
247 #define SCROLL_EDGE_SIZE 15
248 #define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000
249 #define AUTO_EXPAND_TIMEOUT 500
250
251 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
252 * vice versa.
253 */
254 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
255 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
256
257 typedef struct _GtkTreeViewColumnReorder GtkTreeViewColumnReorder;
258 struct _GtkTreeViewColumnReorder
259 {
260 gint left_align;
261 gint right_align;
262 GtkTreeViewColumn *left_column;
263 GtkTreeViewColumn *right_column;
264 };
265
266 typedef struct _GtkTreeViewChild GtkTreeViewChild;
267 struct _GtkTreeViewChild
268 {
269 GtkWidget *widget;
270 GtkRBNode *node;
271 GtkRBTree *tree;
272 GtkTreeViewColumn *column;
273 GtkBorder border;
274 };
275
276
277 typedef struct _TreeViewDragInfo TreeViewDragInfo;
278 struct _TreeViewDragInfo
279 {
280 GdkModifierType start_button_mask;
281 GtkTargetList *_unused_source_target_list;
282 GdkDragAction source_actions;
283
284 GtkTargetList *_unused_dest_target_list;
285
286 guint source_set : 1;
287 guint dest_set : 1;
288 };
289
290
291 struct _GtkTreeViewPrivate
292 {
293 GtkTreeModel *model;
294
295 /* tree information */
296 GtkRBTree *tree;
297
298 /* Container info */
299 GList *children;
300 gint width;
301
302 guint presize_handler_tick_cb;
303
304 /* Adjustments */
305 GtkAdjustment *hadjustment;
306 GtkAdjustment *vadjustment;
307 gint min_display_width;
308 gint min_display_height;
309
310 /* Sub windows */
311 GdkWindow *bin_window;
312 GdkWindow *header_window;
313
314 GtkPixelCache *pixel_cache;
315
316 /* CSS nodes */
317 GtkCssNode *header_node;
318
319 /* Scroll position state keeping */
320 GtkTreeRowReference *top_row;
321 gint top_row_dy;
322 /* dy == y pos of top_row + top_row_dy */
323 /* we cache it for simplicity of the code */
324 gint dy;
325
326 guint validate_rows_timer;
327 guint scroll_sync_timer;
328
329 /* Indentation and expander layout */
330 GtkTreeViewColumn *expander_column;
331
332 gint level_indentation;
333
334 /* Key navigation (focus), selection */
335 gint cursor_offset;
336
337 GtkTreeRowReference *anchor;
338 GtkRBNode *cursor_node;
339 GtkRBTree *cursor_tree;
340
341 GtkTreeViewColumn *focus_column;
342
343 /* Current pressed node, previously pressed, prelight */
344 GtkRBNode *button_pressed_node;
345 GtkRBTree *button_pressed_tree;
346
347 gint press_start_x;
348 gint press_start_y;
349
350 gint event_last_x;
351 gint event_last_y;
352
353 GtkRBNode *prelight_node;
354 GtkRBTree *prelight_tree;
355
356 /* Cell Editing */
357 GtkTreeViewColumn *edited_column;
358
359 /* Auto expand/collapse timeout in hover mode */
360 guint auto_expand_timeout;
361
362 /* Selection information */
363 GtkTreeSelection *selection;
364
365 /* Header information */
366 gint header_height;
367 gint n_columns;
368 GList *columns;
369
370 GtkTreeViewColumnDropFunc column_drop_func;
371 gpointer column_drop_func_data;
372 GDestroyNotify column_drop_func_data_destroy;
373 GList *column_drag_info;
374 GtkTreeViewColumnReorder *cur_reorder;
375
376 gint prev_width_before_expander;
377
378 /* Scroll timeout (e.g. during dnd, rubber banding) */
379 guint scroll_timeout;
380
381 /* Interactive Header reordering */
382 GdkWindow *drag_window;
383 GdkWindow *drag_highlight_window;
384 GtkTreeViewColumn *drag_column;
385 gint drag_column_x;
386
387 /* Interactive Header Resizing */
388 gint drag_pos;
389 gint x_drag;
390
391 /* Non-interactive Header Resizing, expand flag support */
392 gint last_extra_space;
393 gint last_extra_space_per_column;
394 gint last_number_of_expand_columns;
395
396 /* ATK Hack */
397 GtkTreeDestroyCountFunc destroy_count_func;
398 gpointer destroy_count_data;
399 GDestroyNotify destroy_count_destroy;
400
401 /* Row drag-and-drop */
402 GtkTreeRowReference *drag_dest_row;
403 GtkTreeViewDropPosition drag_dest_pos;
404 guint open_dest_timeout;
405
406 /* Rubber banding */
407 gint rubber_band_status;
408 gint rubber_band_x;
409 gint rubber_band_y;
410 gint rubber_band_extend;
411 gint rubber_band_modify;
412
413 /* fixed height */
414 gint fixed_height;
415
416 GtkRBNode *rubber_band_start_node;
417 GtkRBTree *rubber_band_start_tree;
418
419 GtkRBNode *rubber_band_end_node;
420 GtkRBTree *rubber_band_end_tree;
421 GtkCssNode *rubber_band_cssnode;
422
423 /* Scroll-to functionality when unrealized */
424 GtkTreeRowReference *scroll_to_path;
425 GtkTreeViewColumn *scroll_to_column;
426 gfloat scroll_to_row_align;
427 gfloat scroll_to_col_align;
428
429 /* Interactive search */
430 gint selected_iter;
431 gint search_column;
432 GtkTreeViewSearchPositionFunc search_position_func;
433 GtkTreeViewSearchEqualFunc search_equal_func;
434 gpointer search_user_data;
435 GDestroyNotify search_destroy;
436 gpointer search_position_user_data;
437 GDestroyNotify search_position_destroy;
438 GtkWidget *search_window;
439 GtkWidget *search_entry;
440 gulong search_entry_changed_id;
441 guint typeselect_flush_timeout;
442
443 /* Grid and tree lines */
444 GtkTreeViewGridLines grid_lines;
445 double grid_line_dashes[2];
446 int grid_line_width;
447
448 gboolean tree_lines_enabled;
449 double tree_line_dashes[2];
450 int tree_line_width;
451
452 /* Row separators */
453 GtkTreeViewRowSeparatorFunc row_separator_func;
454 gpointer row_separator_data;
455 GDestroyNotify row_separator_destroy;
456
457 /* Gestures */
458 GtkGesture *multipress_gesture;
459 GtkGesture *column_multipress_gesture;
460 GtkGesture *drag_gesture; /* Rubberbanding, row DnD */
461 GtkGesture *column_drag_gesture; /* Column reordering, resizing */
462
463 /* Tooltip support */
464 gint tooltip_column;
465
466 /* Here comes the bitfield */
467 guint scroll_to_use_align : 1;
468
469 guint fixed_height_mode : 1;
470 guint fixed_height_check : 1;
471
472 guint activate_on_single_click : 1;
473 guint reorderable : 1;
474 guint header_has_focus : 1;
475 guint drag_column_window_state : 3;
476 /* hint to display rows in alternating colors */
477 guint has_rules : 1;
478 guint mark_rows_col_dirty : 1;
479
480 /* for DnD */
481 guint empty_view_drop : 1;
482
483 guint modify_selection_pressed : 1;
484 guint extend_selection_pressed : 1;
485
486 guint init_hadjust_value : 1;
487
488 guint in_top_row_to_dy : 1;
489
490 /* interactive search */
491 guint enable_search : 1;
492 guint disable_popdown : 1;
493 guint search_custom_entry_set : 1;
494
495 guint hover_selection : 1;
496 guint hover_expand : 1;
497 guint imcontext_changed : 1;
498
499 guint rubber_banding_enable : 1;
500
501 guint in_grab : 1;
502
503 guint post_validation_flag : 1;
504
505 /* Whether our key press handler is to avoid sending an unhandled binding to the search entry */
506 guint search_entry_avoid_unhandled_binding : 1;
507
508 /* GtkScrollablePolicy needs to be checked when
509 * driving the scrollable adjustment values */
510 guint hscroll_policy : 1;
511 guint vscroll_policy : 1;
512
513 /* GtkTreeView flags */
514 guint is_list : 1;
515 guint show_expanders : 1;
516 guint in_column_resize : 1;
517 guint arrow_prelit : 1;
518 guint headers_visible : 1;
519 guint draw_keyfocus : 1;
520 guint model_setup : 1;
521 guint in_column_drag : 1;
522 };
523
524
525 /* Signals */
526 enum
527 {
528 ROW_ACTIVATED,
529 TEST_EXPAND_ROW,
530 TEST_COLLAPSE_ROW,
531 ROW_EXPANDED,
532 ROW_COLLAPSED,
533 COLUMNS_CHANGED,
534 CURSOR_CHANGED,
535 MOVE_CURSOR,
536 SELECT_ALL,
537 UNSELECT_ALL,
538 SELECT_CURSOR_ROW,
539 TOGGLE_CURSOR_ROW,
540 EXPAND_COLLAPSE_CURSOR_ROW,
541 SELECT_CURSOR_PARENT,
542 START_INTERACTIVE_SEARCH,
543 LAST_SIGNAL
544 };
545
546 /* Properties */
547 enum {
548 PROP_0,
549 PROP_MODEL,
550 PROP_HEADERS_VISIBLE,
551 PROP_HEADERS_CLICKABLE,
552 PROP_EXPANDER_COLUMN,
553 PROP_REORDERABLE,
554 PROP_RULES_HINT,
555 PROP_ENABLE_SEARCH,
556 PROP_SEARCH_COLUMN,
557 PROP_FIXED_HEIGHT_MODE,
558 PROP_HOVER_SELECTION,
559 PROP_HOVER_EXPAND,
560 PROP_SHOW_EXPANDERS,
561 PROP_LEVEL_INDENTATION,
562 PROP_RUBBER_BANDING,
563 PROP_ENABLE_GRID_LINES,
564 PROP_ENABLE_TREE_LINES,
565 PROP_TOOLTIP_COLUMN,
566 PROP_ACTIVATE_ON_SINGLE_CLICK,
567 LAST_PROP,
568 /* overridden */
569 PROP_HADJUSTMENT = LAST_PROP,
570 PROP_VADJUSTMENT,
571 PROP_HSCROLL_POLICY,
572 PROP_VSCROLL_POLICY,
573 };
574
575 /* object signals */
576 static void gtk_tree_view_finalize (GObject *object);
577 static void gtk_tree_view_set_property (GObject *object,
578 guint prop_id,
579 const GValue *value,
580 GParamSpec *pspec);
581 static void gtk_tree_view_get_property (GObject *object,
582 guint prop_id,
583 GValue *value,
584 GParamSpec *pspec);
585
586 /* gtkwidget signals */
587 static void gtk_tree_view_destroy (GtkWidget *widget);
588 static void gtk_tree_view_realize (GtkWidget *widget);
589 static void gtk_tree_view_unrealize (GtkWidget *widget);
590 static void gtk_tree_view_map (GtkWidget *widget);
591 static void gtk_tree_view_unmap (GtkWidget *widget);
592 static void gtk_tree_view_get_preferred_width (GtkWidget *widget,
593 gint *minimum,
594 gint *natural);
595 static void gtk_tree_view_get_preferred_height (GtkWidget *widget,
596 gint *minimum,
597 gint *natural);
598 static void gtk_tree_view_size_allocate (GtkWidget *widget,
599 GtkAllocation *allocation);
600 static gboolean gtk_tree_view_draw (GtkWidget *widget,
601 cairo_t *cr);
602 static gboolean gtk_tree_view_key_press (GtkWidget *widget,
603 GdkEventKey *event);
604 static gboolean gtk_tree_view_key_release (GtkWidget *widget,
605 GdkEventKey *event);
606 static gboolean gtk_tree_view_motion (GtkWidget *widget,
607 GdkEventMotion *event);
608 static gboolean gtk_tree_view_enter_notify (GtkWidget *widget,
609 GdkEventCrossing *event);
610 static gboolean gtk_tree_view_leave_notify (GtkWidget *widget,
611 GdkEventCrossing *event);
612
613 static void gtk_tree_view_set_focus_child (GtkContainer *container,
614 GtkWidget *child);
615 static gint gtk_tree_view_focus_out (GtkWidget *widget,
616 GdkEventFocus *event);
617 static gint gtk_tree_view_focus (GtkWidget *widget,
618 GtkDirectionType direction);
619 static void gtk_tree_view_grab_focus (GtkWidget *widget);
620 static void gtk_tree_view_style_updated (GtkWidget *widget);
621
622 /* container signals */
623 static void gtk_tree_view_remove (GtkContainer *container,
624 GtkWidget *widget);
625 static void gtk_tree_view_forall (GtkContainer *container,
626 gboolean include_internals,
627 GtkCallback callback,
628 gpointer callback_data);
629
630 /* Source side drag signals */
631 static void gtk_tree_view_drag_begin (GtkWidget *widget,
632 GdkDragContext *context);
633 static void gtk_tree_view_drag_end (GtkWidget *widget,
634 GdkDragContext *context);
635 static void gtk_tree_view_drag_data_get (GtkWidget *widget,
636 GdkDragContext *context,
637 GtkSelectionData *selection_data,
638 guint info,
639 guint time);
640 static void gtk_tree_view_drag_data_delete (GtkWidget *widget,
641 GdkDragContext *context);
642
643 /* Target side drag signals */
644 static void gtk_tree_view_drag_leave (GtkWidget *widget,
645 GdkDragContext *context,
646 guint time);
647 static gboolean gtk_tree_view_drag_motion (GtkWidget *widget,
648 GdkDragContext *context,
649 gint x,
650 gint y,
651 guint time);
652 static gboolean gtk_tree_view_drag_drop (GtkWidget *widget,
653 GdkDragContext *context,
654 gint x,
655 gint y,
656 guint time);
657 static void gtk_tree_view_drag_data_received (GtkWidget *widget,
658 GdkDragContext *context,
659 gint x,
660 gint y,
661 GtkSelectionData *selection_data,
662 guint info,
663 guint time);
664
665 /* tree_model signals */
666 static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
667 GtkMovementStep step,
668 gint count);
669 static gboolean gtk_tree_view_real_select_all (GtkTreeView *tree_view);
670 static gboolean gtk_tree_view_real_unselect_all (GtkTreeView *tree_view);
671 static gboolean gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
672 gboolean start_editing);
673 static gboolean gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view);
674 static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view,
675 gboolean logical,
676 gboolean expand,
677 gboolean open_all);
678 static gboolean gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view);
679 static void gtk_tree_view_row_changed (GtkTreeModel *model,
680 GtkTreePath *path,
681 GtkTreeIter *iter,
682 gpointer data);
683 static void gtk_tree_view_row_inserted (GtkTreeModel *model,
684 GtkTreePath *path,
685 GtkTreeIter *iter,
686 gpointer data);
687 static void gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
688 GtkTreePath *path,
689 GtkTreeIter *iter,
690 gpointer data);
691 static void gtk_tree_view_row_deleted (GtkTreeModel *model,
692 GtkTreePath *path,
693 gpointer data);
694 static void gtk_tree_view_rows_reordered (GtkTreeModel *model,
695 GtkTreePath *parent,
696 GtkTreeIter *iter,
697 gint *new_order,
698 gpointer data);
699
700 /* Incremental reflow */
701 static gboolean validate_row (GtkTreeView *tree_view,
702 GtkRBTree *tree,
703 GtkRBNode *node,
704 GtkTreeIter *iter,
705 GtkTreePath *path);
706 static void validate_visible_area (GtkTreeView *tree_view);
707 static gboolean do_validate_rows (GtkTreeView *tree_view,
708 gboolean queue_resize);
709 static gboolean validate_rows (GtkTreeView *tree_view);
710 static void install_presize_handler (GtkTreeView *tree_view);
711 static void install_scroll_sync_handler (GtkTreeView *tree_view);
712 static void gtk_tree_view_set_top_row (GtkTreeView *tree_view,
713 GtkTreePath *path,
714 gint offset);
715 static void gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view);
716 static void gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view);
717 static void invalidate_empty_focus (GtkTreeView *tree_view);
718
719 /* Internal functions */
720 static gboolean gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
721 GtkTreeViewColumn *column);
722 static inline gboolean gtk_tree_view_draw_expanders (GtkTreeView *tree_view);
723 static void gtk_tree_view_add_move_binding (GtkBindingSet *binding_set,
724 guint keyval,
725 guint modmask,
726 gboolean add_shifted_binding,
727 GtkMovementStep step,
728 gint count);
729 static gint gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view,
730 GtkRBTree *tree);
731 static void gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
732 GtkTreePath *path,
733 const GdkRectangle *clip_rect);
734 static void gtk_tree_view_queue_draw_arrow (GtkTreeView *tree_view,
735 GtkRBTree *tree,
736 GtkRBNode *node);
737 static void gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
738 cairo_t *cr,
739 GtkRBTree *tree,
740 GtkRBNode *node);
741 static void gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
742 GtkRBTree *tree,
743 gint *x1,
744 gint *x2);
745 static void gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
746 GtkTreeView *tree_view);
747 static void gtk_tree_view_build_tree (GtkTreeView *tree_view,
748 GtkRBTree *tree,
749 GtkTreeIter *iter,
750 gint depth,
751 gboolean recurse);
752 static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
753 GtkRBTree *tree,
754 GtkRBNode *node);
755 static void gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view,
756 GtkTreeViewColumn *column,
757 gboolean focus_to_cell);
758 static gboolean gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view);
759 static void gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view);
760 static void gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
761 gint count);
762 static void gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
763 gint count);
764 static void gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
765 gint count);
766 static void gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
767 gint count);
768 static gboolean gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
769 GtkTreePath *path,
770 GtkRBTree *tree,
771 GtkRBNode *node,
772 gboolean animate);
773 static gboolean gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
774 GtkTreePath *path,
775 GtkRBTree *tree,
776 GtkRBNode *node,
777 gboolean open_all,
778 gboolean animate);
779 static void gtk_tree_view_real_set_cursor (GtkTreeView *tree_view,
780 GtkTreePath *path,
781 SetCursorFlags flags);
782 static gboolean gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view);
783 static void column_sizing_notify (GObject *object,
784 GParamSpec *pspec,
785 gpointer data);
786 static void gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view);
787 static void update_prelight (GtkTreeView *tree_view,
788 int x,
789 int y);
790 static void gtk_tree_view_queue_draw_region (GtkWidget *widget,
791 const cairo_region_t *region);
792
793 static inline gint gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view);
794
795 static inline gint gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view,
796 GtkRBTree *tree,
797 GtkRBNode *node,
798 gint vertical_separator);
799 static inline gint gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view,
800 GtkRBNode *node,
801 gint vertical_separator);
802
803 static inline gint gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view,
804 GtkRBTree *tree,
805 GtkRBNode *node);
806 static inline gint gtk_tree_view_get_row_height (GtkTreeView *tree_view,
807 GtkRBNode *node);
808
809 /* interactive search */
810 static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
811 static void gtk_tree_view_search_window_hide (GtkWidget *search_window,
812 GtkTreeView *tree_view,
813 GdkDevice *device);
814 static void gtk_tree_view_search_position_func (GtkTreeView *tree_view,
815 GtkWidget *search_window,
816 gpointer user_data);
817 static void gtk_tree_view_search_disable_popdown (GtkEntry *entry,
818 GtkMenu *menu,
819 gpointer data);
820 static void gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
821 GtkTreeView *tree_view);
822 static void gtk_tree_view_search_commit (GtkIMContext *im_context,
823 gchar *buf,
824 GtkTreeView *tree_view);
825 static void gtk_tree_view_search_activate (GtkEntry *entry,
826 GtkTreeView *tree_view);
827 static gboolean gtk_tree_view_real_search_enable_popdown(gpointer data);
828 static void gtk_tree_view_search_enable_popdown (GtkWidget *widget,
829 gpointer data);
830 static gboolean gtk_tree_view_search_delete_event (GtkWidget *widget,
831 GdkEventAny *event,
832 GtkTreeView *tree_view);
833 static gboolean gtk_tree_view_search_button_press_event (GtkWidget *widget,
834 GdkEventButton *event,
835 GtkTreeView *tree_view);
836 static gboolean gtk_tree_view_search_scroll_event (GtkWidget *entry,
837 GdkEventScroll *event,
838 GtkTreeView *tree_view);
839 static gboolean gtk_tree_view_search_key_press_event (GtkWidget *entry,
840 GdkEventKey *event,
841 GtkTreeView *tree_view);
842 static gboolean gtk_tree_view_search_move (GtkWidget *window,
843 GtkTreeView *tree_view,
844 gboolean up);
845 static gboolean gtk_tree_view_search_equal_func (GtkTreeModel *model,
846 gint column,
847 const gchar *key,
848 GtkTreeIter *iter,
849 gpointer search_data);
850 static gboolean gtk_tree_view_search_iter (GtkTreeModel *model,
851 GtkTreeSelection *selection,
852 GtkTreeIter *iter,
853 const gchar *text,
854 gint *count,
855 gint n);
856 static void gtk_tree_view_search_init (GtkWidget *entry,
857 GtkTreeView *tree_view);
858 static void gtk_tree_view_put (GtkTreeView *tree_view,
859 GtkWidget *child_widget,
860 GtkTreePath *path,
861 GtkTreeViewColumn*column,
862 const GtkBorder *border);
863 static gboolean gtk_tree_view_start_editing (GtkTreeView *tree_view,
864 GtkTreePath *cursor_path,
865 gboolean edit_only);
866 static void gtk_tree_view_stop_editing (GtkTreeView *tree_view,
867 gboolean cancel_editing);
868 static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
869 GdkDevice *device,
870 gboolean keybinding);
871 static gboolean gtk_tree_view_start_interactive_search (GtkTreeView *tree_view);
872 static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree_view,
873 GtkTreeViewColumn *column,
874 gint drop_position);
875
876 /* GtkBuildable */
877 static void gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
878 GtkBuilder *builder,
879 GObject *child,
880 const gchar *type);
881 static GObject *gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable,
882 GtkBuilder *builder,
883 const gchar *childname);
884 static void gtk_tree_view_buildable_init (GtkBuildableIface *iface);
885
886 /* GtkScrollable */
887 static void gtk_tree_view_scrollable_init (GtkScrollableInterface *iface);
888
889 static GtkAdjustment *gtk_tree_view_do_get_hadjustment (GtkTreeView *tree_view);
890 static void gtk_tree_view_do_set_hadjustment (GtkTreeView *tree_view,
891 GtkAdjustment *adjustment);
892 static GtkAdjustment *gtk_tree_view_do_get_vadjustment (GtkTreeView *tree_view);
893 static void gtk_tree_view_do_set_vadjustment (GtkTreeView *tree_view,
894 GtkAdjustment *adjustment);
895
896 static gboolean scroll_row_timeout (gpointer data);
897 static void add_scroll_timeout (GtkTreeView *tree_view);
898 static void remove_scroll_timeout (GtkTreeView *tree_view);
899
900 static void grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view);
901
902 /* Gestures */
903 static void gtk_tree_view_column_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
904 gint n_press,
905 gdouble x,
906 gdouble y,
907 GtkTreeView *tree_view);
908
909 static void gtk_tree_view_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
910 gint n_press,
911 gdouble x,
912 gdouble y,
913 GtkTreeView *tree_view);
914 static void gtk_tree_view_multipress_gesture_released (GtkGestureMultiPress *gesture,
915 gint n_press,
916 gdouble x,
917 gdouble y,
918 GtkTreeView *tree_view);
919
920 static void gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture,
921 gdouble start_x,
922 gdouble start_y,
923 GtkTreeView *tree_view);
924 static void gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture,
925 gdouble offset_x,
926 gdouble offset_y,
927 GtkTreeView *tree_view);
928 static void gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture,
929 gdouble offset_x,
930 gdouble offset_y,
931 GtkTreeView *tree_view);
932
933 static void gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture,
934 gdouble start_x,
935 gdouble start_y,
936 GtkTreeView *tree_view);
937 static void gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture,
938 gdouble offset_x,
939 gdouble offset_y,
940 GtkTreeView *tree_view);
941 static void gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture,
942 gdouble offset_x,
943 gdouble offset_y,
944 GtkTreeView *tree_view);
945
946 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
947 static GParamSpec *tree_view_props [LAST_PROP] = { NULL };
948
949
950
951 /* GType Methods
952 */
953
G_DEFINE_TYPE_WITH_CODE(GtkTreeView,gtk_tree_view,GTK_TYPE_CONTAINER,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))954 G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER,
955 G_ADD_PRIVATE (GtkTreeView)
956 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
957 gtk_tree_view_buildable_init)
958 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,
959 gtk_tree_view_scrollable_init))
960
961 static void
962 gtk_tree_view_class_init (GtkTreeViewClass *class)
963 {
964 GObjectClass *o_class;
965 GtkWidgetClass *widget_class;
966 GtkContainerClass *container_class;
967 GtkBindingSet *binding_set;
968
969 binding_set = gtk_binding_set_by_class (class);
970
971 o_class = (GObjectClass *) class;
972 widget_class = (GtkWidgetClass *) class;
973 container_class = (GtkContainerClass *) class;
974
975 /* GObject signals */
976 o_class->set_property = gtk_tree_view_set_property;
977 o_class->get_property = gtk_tree_view_get_property;
978 o_class->finalize = gtk_tree_view_finalize;
979
980 /* GtkWidget signals */
981 widget_class->destroy = gtk_tree_view_destroy;
982 widget_class->map = gtk_tree_view_map;
983 widget_class->unmap = gtk_tree_view_unmap;
984 widget_class->realize = gtk_tree_view_realize;
985 widget_class->unrealize = gtk_tree_view_unrealize;
986 widget_class->get_preferred_width = gtk_tree_view_get_preferred_width;
987 widget_class->get_preferred_height = gtk_tree_view_get_preferred_height;
988 widget_class->size_allocate = gtk_tree_view_size_allocate;
989 widget_class->motion_notify_event = gtk_tree_view_motion;
990 widget_class->draw = gtk_tree_view_draw;
991 widget_class->key_press_event = gtk_tree_view_key_press;
992 widget_class->key_release_event = gtk_tree_view_key_release;
993 widget_class->enter_notify_event = gtk_tree_view_enter_notify;
994 widget_class->leave_notify_event = gtk_tree_view_leave_notify;
995 widget_class->focus_out_event = gtk_tree_view_focus_out;
996 widget_class->drag_begin = gtk_tree_view_drag_begin;
997 widget_class->drag_end = gtk_tree_view_drag_end;
998 widget_class->drag_data_get = gtk_tree_view_drag_data_get;
999 widget_class->drag_data_delete = gtk_tree_view_drag_data_delete;
1000 widget_class->drag_leave = gtk_tree_view_drag_leave;
1001 widget_class->drag_motion = gtk_tree_view_drag_motion;
1002 widget_class->drag_drop = gtk_tree_view_drag_drop;
1003 widget_class->drag_data_received = gtk_tree_view_drag_data_received;
1004 widget_class->focus = gtk_tree_view_focus;
1005 widget_class->grab_focus = gtk_tree_view_grab_focus;
1006 widget_class->style_updated = gtk_tree_view_style_updated;
1007 widget_class->queue_draw_region = gtk_tree_view_queue_draw_region;
1008
1009 /* GtkContainer signals */
1010 container_class->remove = gtk_tree_view_remove;
1011 container_class->forall = gtk_tree_view_forall;
1012 container_class->set_focus_child = gtk_tree_view_set_focus_child;
1013
1014 class->move_cursor = gtk_tree_view_real_move_cursor;
1015 class->select_all = gtk_tree_view_real_select_all;
1016 class->unselect_all = gtk_tree_view_real_unselect_all;
1017 class->select_cursor_row = gtk_tree_view_real_select_cursor_row;
1018 class->toggle_cursor_row = gtk_tree_view_real_toggle_cursor_row;
1019 class->expand_collapse_cursor_row = gtk_tree_view_real_expand_collapse_cursor_row;
1020 class->select_cursor_parent = gtk_tree_view_real_select_cursor_parent;
1021 class->start_interactive_search = gtk_tree_view_start_interactive_search;
1022
1023 /* Properties */
1024
1025 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
1026 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
1027 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1028 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1029
1030 tree_view_props[PROP_MODEL] =
1031 g_param_spec_object ("model",
1032 P_("TreeView Model"),
1033 P_("The model for the tree view"),
1034 GTK_TYPE_TREE_MODEL,
1035 GTK_PARAM_READWRITE);
1036
1037 tree_view_props[PROP_HEADERS_VISIBLE] =
1038 g_param_spec_boolean ("headers-visible",
1039 P_("Headers Visible"),
1040 P_("Show the column header buttons"),
1041 TRUE,
1042 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1043
1044 tree_view_props[PROP_HEADERS_CLICKABLE] =
1045 g_param_spec_boolean ("headers-clickable",
1046 P_("Headers Clickable"),
1047 P_("Column headers respond to click events"),
1048 TRUE,
1049 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1050
1051 tree_view_props[PROP_EXPANDER_COLUMN] =
1052 g_param_spec_object ("expander-column",
1053 P_("Expander Column"),
1054 P_("Set the column for the expander column"),
1055 GTK_TYPE_TREE_VIEW_COLUMN,
1056 GTK_PARAM_READWRITE);
1057
1058 tree_view_props[PROP_REORDERABLE] =
1059 g_param_spec_boolean ("reorderable",
1060 P_("Reorderable"),
1061 P_("View is reorderable"),
1062 FALSE,
1063 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1064
1065 /**
1066 * GtkTreeView:rules-hint:
1067 *
1068 * Sets a hint to the theme to draw rows in alternating colors.
1069 *
1070 * Deprecated: 3.14: The theme is responsible for drawing rows
1071 * using zebra striping
1072 */
1073 tree_view_props[PROP_RULES_HINT] =
1074 g_param_spec_boolean ("rules-hint",
1075 P_("Rules Hint"),
1076 P_("Set a hint to the theme engine to draw rows in alternating colors"),
1077 FALSE,
1078 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
1079
1080 tree_view_props[PROP_ENABLE_SEARCH] =
1081 g_param_spec_boolean ("enable-search",
1082 P_("Enable Search"),
1083 P_("View allows user to search through columns interactively"),
1084 TRUE,
1085 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1086
1087 tree_view_props[PROP_SEARCH_COLUMN] =
1088 g_param_spec_int ("search-column",
1089 P_("Search Column"),
1090 P_("Model column to search through during interactive search"),
1091 -1, G_MAXINT,
1092 -1,
1093 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1094
1095 /**
1096 * GtkTreeView:fixed-height-mode:
1097 *
1098 * Setting the ::fixed-height-mode property to %TRUE speeds up
1099 * #GtkTreeView by assuming that all rows have the same height.
1100 * Only enable this option if all rows are the same height.
1101 * Please see gtk_tree_view_set_fixed_height_mode() for more
1102 * information on this option.
1103 *
1104 * Since: 2.4
1105 */
1106 tree_view_props[PROP_FIXED_HEIGHT_MODE] =
1107 g_param_spec_boolean ("fixed-height-mode",
1108 P_("Fixed Height Mode"),
1109 P_("Speeds up GtkTreeView by assuming that all rows have the same height"),
1110 FALSE,
1111 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1112
1113 /**
1114 * GtkTreeView:hover-selection:
1115 *
1116 * Enables or disables the hover selection mode of @tree_view.
1117 * Hover selection makes the selected row follow the pointer.
1118 * Currently, this works only for the selection modes
1119 * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
1120 *
1121 * This mode is primarily intended for treeviews in popups, e.g.
1122 * in #GtkComboBox or #GtkEntryCompletion.
1123 *
1124 * Since: 2.6
1125 */
1126 tree_view_props[PROP_HOVER_SELECTION] =
1127 g_param_spec_boolean ("hover-selection",
1128 P_("Hover Selection"),
1129 P_("Whether the selection should follow the pointer"),
1130 FALSE,
1131 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1132
1133 /**
1134 * GtkTreeView:hover-expand:
1135 *
1136 * Enables or disables the hover expansion mode of @tree_view.
1137 * Hover expansion makes rows expand or collapse if the pointer moves
1138 * over them.
1139 *
1140 * This mode is primarily intended for treeviews in popups, e.g.
1141 * in #GtkComboBox or #GtkEntryCompletion.
1142 *
1143 * Since: 2.6
1144 */
1145 tree_view_props[PROP_HOVER_EXPAND] =
1146 g_param_spec_boolean ("hover-expand",
1147 P_("Hover Expand"),
1148 P_("Whether rows should be expanded/collapsed when the pointer moves over them"),
1149 FALSE,
1150 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1151
1152 /**
1153 * GtkTreeView:show-expanders:
1154 *
1155 * %TRUE if the view has expanders.
1156 *
1157 * Since: 2.12
1158 */
1159 tree_view_props[PROP_SHOW_EXPANDERS] =
1160 g_param_spec_boolean ("show-expanders",
1161 P_("Show Expanders"),
1162 P_("View has expanders"),
1163 TRUE,
1164 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1165
1166 /**
1167 * GtkTreeView:level-indentation:
1168 *
1169 * Extra indentation for each level.
1170 *
1171 * Since: 2.12
1172 */
1173 tree_view_props[PROP_LEVEL_INDENTATION] =
1174 g_param_spec_int ("level-indentation",
1175 P_("Level Indentation"),
1176 P_("Extra indentation for each level"),
1177 0, G_MAXINT,
1178 0,
1179 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1180
1181 tree_view_props[PROP_RUBBER_BANDING] =
1182 g_param_spec_boolean ("rubber-banding",
1183 P_("Rubber Banding"),
1184 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
1185 FALSE,
1186 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1187
1188 tree_view_props[PROP_ENABLE_GRID_LINES] =
1189 g_param_spec_enum ("enable-grid-lines",
1190 P_("Enable Grid Lines"),
1191 P_("Whether grid lines should be drawn in the tree view"),
1192 GTK_TYPE_TREE_VIEW_GRID_LINES,
1193 GTK_TREE_VIEW_GRID_LINES_NONE,
1194 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1195
1196 tree_view_props[PROP_ENABLE_TREE_LINES] =
1197 g_param_spec_boolean ("enable-tree-lines",
1198 P_("Enable Tree Lines"),
1199 P_("Whether tree lines should be drawn in the tree view"),
1200 FALSE,
1201 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1202
1203 tree_view_props[PROP_TOOLTIP_COLUMN] =
1204 g_param_spec_int ("tooltip-column",
1205 P_("Tooltip Column"),
1206 P_("The column in the model containing the tooltip texts for the rows"),
1207 -1, G_MAXINT,
1208 -1,
1209 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1210
1211 /**
1212 * GtkTreeView:activate-on-single-click:
1213 *
1214 * The activate-on-single-click property specifies whether the "row-activated" signal
1215 * will be emitted after a single click.
1216 *
1217 * Since: 3.8
1218 */
1219 tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK] =
1220 g_param_spec_boolean ("activate-on-single-click",
1221 P_("Activate on Single Click"),
1222 P_("Activate row on a single click"),
1223 FALSE,
1224 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1225
1226 g_object_class_install_properties (o_class, LAST_PROP, tree_view_props);
1227
1228 /* Style properties */
1229 #define _TREE_VIEW_EXPANDER_SIZE 14
1230 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
1231 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
1232
1233 gtk_widget_class_install_style_property (widget_class,
1234 g_param_spec_int ("expander-size",
1235 P_("Expander Size"),
1236 P_("Size of the expander arrow"),
1237 0,
1238 G_MAXINT,
1239 _TREE_VIEW_EXPANDER_SIZE,
1240 GTK_PARAM_READABLE));
1241
1242 gtk_widget_class_install_style_property (widget_class,
1243 g_param_spec_int ("vertical-separator",
1244 P_("Vertical Separator Width"),
1245 P_("Vertical space between cells. Must be an even number"),
1246 0,
1247 G_MAXINT,
1248 _TREE_VIEW_VERTICAL_SEPARATOR,
1249 GTK_PARAM_READABLE));
1250
1251 gtk_widget_class_install_style_property (widget_class,
1252 g_param_spec_int ("horizontal-separator",
1253 P_("Horizontal Separator Width"),
1254 P_("Horizontal space between cells. Must be an even number"),
1255 0,
1256 G_MAXINT,
1257 _TREE_VIEW_HORIZONTAL_SEPARATOR,
1258 GTK_PARAM_READABLE));
1259
1260 gtk_widget_class_install_style_property (widget_class,
1261 g_param_spec_boolean ("allow-rules",
1262 P_("Allow Rules"),
1263 P_("Allow drawing of alternating color rows"),
1264 TRUE,
1265 GTK_PARAM_READABLE));
1266
1267 gtk_widget_class_install_style_property (widget_class,
1268 g_param_spec_boolean ("indent-expanders",
1269 P_("Indent Expanders"),
1270 P_("Make the expanders indented"),
1271 TRUE,
1272 GTK_PARAM_READABLE));
1273 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1274 gtk_widget_class_install_style_property (widget_class,
1275 g_param_spec_boxed ("even-row-color",
1276 P_("Even Row Color"),
1277 P_("Color to use for even rows"),
1278 GDK_TYPE_COLOR,
1279 GTK_PARAM_READABLE));
1280
1281 gtk_widget_class_install_style_property (widget_class,
1282 g_param_spec_boxed ("odd-row-color",
1283 P_("Odd Row Color"),
1284 P_("Color to use for odd rows"),
1285 GDK_TYPE_COLOR,
1286 GTK_PARAM_READABLE));
1287 G_GNUC_END_IGNORE_DEPRECATIONS
1288
1289 gtk_widget_class_install_style_property (widget_class,
1290 g_param_spec_int ("grid-line-width",
1291 P_("Grid line width"),
1292 P_("Width, in pixels, of the tree view grid lines"),
1293 0, G_MAXINT, 1,
1294 GTK_PARAM_READABLE));
1295
1296 gtk_widget_class_install_style_property (widget_class,
1297 g_param_spec_int ("tree-line-width",
1298 P_("Tree line width"),
1299 P_("Width, in pixels, of the tree view lines"),
1300 0, G_MAXINT, 1,
1301 GTK_PARAM_READABLE));
1302
1303 gtk_widget_class_install_style_property (widget_class,
1304 g_param_spec_string ("grid-line-pattern",
1305 P_("Grid line pattern"),
1306 P_("Dash pattern used to draw the tree view grid lines"),
1307 "\1\1",
1308 GTK_PARAM_READABLE));
1309
1310 gtk_widget_class_install_style_property (widget_class,
1311 g_param_spec_string ("tree-line-pattern",
1312 P_("Tree line pattern"),
1313 P_("Dash pattern used to draw the tree view lines"),
1314 "\1\1",
1315 GTK_PARAM_READABLE));
1316
1317 /* Signals */
1318 /**
1319 * GtkTreeView::row-activated:
1320 * @tree_view: the object on which the signal is emitted
1321 * @path: the #GtkTreePath for the activated row
1322 * @column: the #GtkTreeViewColumn in which the activation occurred
1323 *
1324 * The "row-activated" signal is emitted when the method
1325 * gtk_tree_view_row_activated() is called, when the user double
1326 * clicks a treeview row with the "activate-on-single-click"
1327 * property set to %FALSE, or when the user single clicks a row when
1328 * the "activate-on-single-click" property set to %TRUE. It is also
1329 * emitted when a non-editable row is selected and one of the keys:
1330 * Space, Shift+Space, Return or Enter is pressed.
1331 *
1332 * For selection handling refer to the
1333 * [tree widget conceptual overview][TreeWidget]
1334 * as well as #GtkTreeSelection.
1335 */
1336 tree_view_signals[ROW_ACTIVATED] =
1337 g_signal_new (I_("row-activated"),
1338 G_TYPE_FROM_CLASS (o_class),
1339 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1340 G_STRUCT_OFFSET (GtkTreeViewClass, row_activated),
1341 NULL, NULL,
1342 _gtk_marshal_VOID__BOXED_OBJECT,
1343 G_TYPE_NONE, 2,
1344 GTK_TYPE_TREE_PATH,
1345 GTK_TYPE_TREE_VIEW_COLUMN);
1346 g_signal_set_va_marshaller (tree_view_signals[ROW_ACTIVATED],
1347 G_TYPE_FROM_CLASS (o_class),
1348 _gtk_marshal_VOID__BOXED_OBJECTv);
1349
1350 /**
1351 * GtkTreeView::test-expand-row:
1352 * @tree_view: the object on which the signal is emitted
1353 * @iter: the tree iter of the row to expand
1354 * @path: a tree path that points to the row
1355 *
1356 * The given row is about to be expanded (show its children nodes). Use this
1357 * signal if you need to control the expandability of individual rows.
1358 *
1359 * Returns: %FALSE to allow expansion, %TRUE to reject
1360 */
1361 tree_view_signals[TEST_EXPAND_ROW] =
1362 g_signal_new (I_("test-expand-row"),
1363 G_TYPE_FROM_CLASS (o_class),
1364 G_SIGNAL_RUN_LAST,
1365 G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row),
1366 _gtk_boolean_handled_accumulator, NULL,
1367 _gtk_marshal_BOOLEAN__BOXED_BOXED,
1368 G_TYPE_BOOLEAN, 2,
1369 GTK_TYPE_TREE_ITER,
1370 GTK_TYPE_TREE_PATH);
1371 g_signal_set_va_marshaller (tree_view_signals[TEST_EXPAND_ROW],
1372 G_TYPE_FROM_CLASS (o_class),
1373 _gtk_marshal_BOOLEAN__BOXED_BOXEDv);
1374
1375 /**
1376 * GtkTreeView::test-collapse-row:
1377 * @tree_view: the object on which the signal is emitted
1378 * @iter: the tree iter of the row to collapse
1379 * @path: a tree path that points to the row
1380 *
1381 * The given row is about to be collapsed (hide its children nodes). Use this
1382 * signal if you need to control the collapsibility of individual rows.
1383 *
1384 * Returns: %FALSE to allow collapsing, %TRUE to reject
1385 */
1386 tree_view_signals[TEST_COLLAPSE_ROW] =
1387 g_signal_new (I_("test-collapse-row"),
1388 G_TYPE_FROM_CLASS (o_class),
1389 G_SIGNAL_RUN_LAST,
1390 G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row),
1391 _gtk_boolean_handled_accumulator, NULL,
1392 _gtk_marshal_BOOLEAN__BOXED_BOXED,
1393 G_TYPE_BOOLEAN, 2,
1394 GTK_TYPE_TREE_ITER,
1395 GTK_TYPE_TREE_PATH);
1396 g_signal_set_va_marshaller (tree_view_signals[TEST_COLLAPSE_ROW],
1397 G_TYPE_FROM_CLASS (o_class),
1398 _gtk_marshal_BOOLEAN__BOXED_BOXEDv);
1399
1400 /**
1401 * GtkTreeView::row-expanded:
1402 * @tree_view: the object on which the signal is emitted
1403 * @iter: the tree iter of the expanded row
1404 * @path: a tree path that points to the row
1405 *
1406 * The given row has been expanded (child nodes are shown).
1407 */
1408 tree_view_signals[ROW_EXPANDED] =
1409 g_signal_new (I_("row-expanded"),
1410 G_TYPE_FROM_CLASS (o_class),
1411 G_SIGNAL_RUN_LAST,
1412 G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded),
1413 NULL, NULL,
1414 _gtk_marshal_VOID__BOXED_BOXED,
1415 G_TYPE_NONE, 2,
1416 GTK_TYPE_TREE_ITER,
1417 GTK_TYPE_TREE_PATH);
1418 g_signal_set_va_marshaller (tree_view_signals[ROW_EXPANDED],
1419 G_TYPE_FROM_CLASS (o_class),
1420 _gtk_marshal_VOID__BOXED_BOXEDv);
1421
1422 /**
1423 * GtkTreeView::row-collapsed:
1424 * @tree_view: the object on which the signal is emitted
1425 * @iter: the tree iter of the collapsed row
1426 * @path: a tree path that points to the row
1427 *
1428 * The given row has been collapsed (child nodes are hidden).
1429 */
1430 tree_view_signals[ROW_COLLAPSED] =
1431 g_signal_new (I_("row-collapsed"),
1432 G_TYPE_FROM_CLASS (o_class),
1433 G_SIGNAL_RUN_LAST,
1434 G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed),
1435 NULL, NULL,
1436 _gtk_marshal_VOID__BOXED_BOXED,
1437 G_TYPE_NONE, 2,
1438 GTK_TYPE_TREE_ITER,
1439 GTK_TYPE_TREE_PATH);
1440 g_signal_set_va_marshaller (tree_view_signals[ROW_COLLAPSED],
1441 G_TYPE_FROM_CLASS (o_class),
1442 _gtk_marshal_VOID__BOXED_BOXEDv);
1443
1444 /**
1445 * GtkTreeView::columns-changed:
1446 * @tree_view: the object on which the signal is emitted
1447 *
1448 * The number of columns of the treeview has changed.
1449 */
1450 tree_view_signals[COLUMNS_CHANGED] =
1451 g_signal_new (I_("columns-changed"),
1452 G_TYPE_FROM_CLASS (o_class),
1453 G_SIGNAL_RUN_LAST,
1454 G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed),
1455 NULL, NULL,
1456 NULL,
1457 G_TYPE_NONE, 0);
1458
1459 /**
1460 * GtkTreeView::cursor-changed:
1461 * @tree_view: the object on which the signal is emitted
1462 *
1463 * The position of the cursor (focused cell) has changed.
1464 */
1465 tree_view_signals[CURSOR_CHANGED] =
1466 g_signal_new (I_("cursor-changed"),
1467 G_TYPE_FROM_CLASS (o_class),
1468 G_SIGNAL_RUN_LAST,
1469 G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed),
1470 NULL, NULL,
1471 NULL,
1472 G_TYPE_NONE, 0);
1473
1474 /**
1475 * GtkTreeView::move-cursor:
1476 * @tree_view: the object on which the signal is emitted.
1477 * @step: the granularity of the move, as a
1478 * #GtkMovementStep. %GTK_MOVEMENT_LOGICAL_POSITIONS,
1479 * %GTK_MOVEMENT_VISUAL_POSITIONS, %GTK_MOVEMENT_DISPLAY_LINES,
1480 * %GTK_MOVEMENT_PAGES and %GTK_MOVEMENT_BUFFER_ENDS are
1481 * supported. %GTK_MOVEMENT_LOGICAL_POSITIONS and
1482 * %GTK_MOVEMENT_VISUAL_POSITIONS are treated identically.
1483 * @direction: the direction to move: +1 to move forwards;
1484 * -1 to move backwards. The resulting movement is
1485 * undefined for all other values.
1486 *
1487 * The #GtkTreeView::move-cursor signal is a [keybinding
1488 * signal][GtkBindingSignal] which gets emitted when the user
1489 * presses one of the cursor keys.
1490 *
1491 * Applications should not connect to it, but may emit it with
1492 * g_signal_emit_by_name() if they need to control the cursor
1493 * programmatically. In contrast to gtk_tree_view_set_cursor() and
1494 * gtk_tree_view_set_cursor_on_cell() when moving horizontally
1495 * #GtkTreeView::move-cursor does not reset the current selection.
1496 *
1497 * Returns: %TRUE if @step is supported, %FALSE otherwise.
1498 */
1499 tree_view_signals[MOVE_CURSOR] =
1500 g_signal_new (I_("move-cursor"),
1501 G_TYPE_FROM_CLASS (o_class),
1502 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1503 G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor),
1504 NULL, NULL,
1505 _gtk_marshal_BOOLEAN__ENUM_INT,
1506 G_TYPE_BOOLEAN, 2,
1507 GTK_TYPE_MOVEMENT_STEP,
1508 G_TYPE_INT);
1509 g_signal_set_va_marshaller (tree_view_signals[MOVE_CURSOR],
1510 G_TYPE_FROM_CLASS (o_class),
1511 _gtk_marshal_BOOLEAN__ENUM_INTv);
1512
1513 tree_view_signals[SELECT_ALL] =
1514 g_signal_new (I_("select-all"),
1515 G_TYPE_FROM_CLASS (o_class),
1516 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1517 G_STRUCT_OFFSET (GtkTreeViewClass, select_all),
1518 NULL, NULL,
1519 _gtk_marshal_BOOLEAN__VOID,
1520 G_TYPE_BOOLEAN, 0);
1521 g_signal_set_va_marshaller (tree_view_signals[SELECT_ALL],
1522 G_TYPE_FROM_CLASS (o_class),
1523 _gtk_marshal_BOOLEAN__VOIDv);
1524
1525 tree_view_signals[UNSELECT_ALL] =
1526 g_signal_new (I_("unselect-all"),
1527 G_TYPE_FROM_CLASS (o_class),
1528 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1529 G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all),
1530 NULL, NULL,
1531 _gtk_marshal_BOOLEAN__VOID,
1532 G_TYPE_BOOLEAN, 0);
1533 g_signal_set_va_marshaller (tree_view_signals[UNSELECT_ALL],
1534 G_TYPE_FROM_CLASS (o_class),
1535 _gtk_marshal_BOOLEAN__VOIDv);
1536
1537 tree_view_signals[SELECT_CURSOR_ROW] =
1538 g_signal_new (I_("select-cursor-row"),
1539 G_TYPE_FROM_CLASS (o_class),
1540 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1541 G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row),
1542 NULL, NULL,
1543 _gtk_marshal_BOOLEAN__BOOLEAN,
1544 G_TYPE_BOOLEAN, 1,
1545 G_TYPE_BOOLEAN);
1546 g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_ROW],
1547 G_TYPE_FROM_CLASS (o_class),
1548 _gtk_marshal_BOOLEAN__BOOLEANv);
1549
1550 tree_view_signals[TOGGLE_CURSOR_ROW] =
1551 g_signal_new (I_("toggle-cursor-row"),
1552 G_TYPE_FROM_CLASS (o_class),
1553 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1554 G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row),
1555 NULL, NULL,
1556 _gtk_marshal_BOOLEAN__VOID,
1557 G_TYPE_BOOLEAN, 0);
1558 g_signal_set_va_marshaller (tree_view_signals[TOGGLE_CURSOR_ROW],
1559 G_TYPE_FROM_CLASS (o_class),
1560 _gtk_marshal_BOOLEAN__VOIDv);
1561
1562 tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] =
1563 g_signal_new (I_("expand-collapse-cursor-row"),
1564 G_TYPE_FROM_CLASS (o_class),
1565 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1566 G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row),
1567 NULL, NULL,
1568 _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN,
1569 G_TYPE_BOOLEAN, 3,
1570 G_TYPE_BOOLEAN,
1571 G_TYPE_BOOLEAN,
1572 G_TYPE_BOOLEAN);
1573 g_signal_set_va_marshaller (tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW],
1574 G_TYPE_FROM_CLASS (o_class),
1575 _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEANv);
1576
1577 tree_view_signals[SELECT_CURSOR_PARENT] =
1578 g_signal_new (I_("select-cursor-parent"),
1579 G_TYPE_FROM_CLASS (o_class),
1580 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1581 G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent),
1582 NULL, NULL,
1583 _gtk_marshal_BOOLEAN__VOID,
1584 G_TYPE_BOOLEAN, 0);
1585 g_signal_set_va_marshaller (tree_view_signals[SELECT_CURSOR_PARENT],
1586 G_TYPE_FROM_CLASS (o_class),
1587 _gtk_marshal_BOOLEAN__VOIDv);
1588
1589 tree_view_signals[START_INTERACTIVE_SEARCH] =
1590 g_signal_new (I_("start-interactive-search"),
1591 G_TYPE_FROM_CLASS (o_class),
1592 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1593 G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search),
1594 NULL, NULL,
1595 _gtk_marshal_BOOLEAN__VOID,
1596 G_TYPE_BOOLEAN, 0);
1597 g_signal_set_va_marshaller (tree_view_signals[START_INTERACTIVE_SEARCH],
1598 G_TYPE_FROM_CLASS (o_class),
1599 _gtk_marshal_BOOLEAN__VOIDv);
1600
1601 /* Key bindings */
1602 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Up, 0, TRUE,
1603 GTK_MOVEMENT_DISPLAY_LINES, -1);
1604 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Up, 0, TRUE,
1605 GTK_MOVEMENT_DISPLAY_LINES, -1);
1606
1607 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Down, 0, TRUE,
1608 GTK_MOVEMENT_DISPLAY_LINES, 1);
1609 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Down, 0, TRUE,
1610 GTK_MOVEMENT_DISPLAY_LINES, 1);
1611
1612 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_p, GDK_CONTROL_MASK, FALSE,
1613 GTK_MOVEMENT_DISPLAY_LINES, -1);
1614
1615 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_n, GDK_CONTROL_MASK, FALSE,
1616 GTK_MOVEMENT_DISPLAY_LINES, 1);
1617
1618 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Home, 0, TRUE,
1619 GTK_MOVEMENT_BUFFER_ENDS, -1);
1620 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Home, 0, TRUE,
1621 GTK_MOVEMENT_BUFFER_ENDS, -1);
1622
1623 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_End, 0, TRUE,
1624 GTK_MOVEMENT_BUFFER_ENDS, 1);
1625 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_End, 0, TRUE,
1626 GTK_MOVEMENT_BUFFER_ENDS, 1);
1627
1628 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Page_Up, 0, TRUE,
1629 GTK_MOVEMENT_PAGES, -1);
1630 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0, TRUE,
1631 GTK_MOVEMENT_PAGES, -1);
1632
1633 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_Page_Down, 0, TRUE,
1634 GTK_MOVEMENT_PAGES, 1);
1635 gtk_tree_view_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0, TRUE,
1636 GTK_MOVEMENT_PAGES, 1);
1637
1638
1639 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, 0, "move-cursor", 2,
1640 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1641 G_TYPE_INT, 1);
1642
1643 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, 0, "move-cursor", 2,
1644 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1645 G_TYPE_INT, -1);
1646
1647 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, 0, "move-cursor", 2,
1648 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1649 G_TYPE_INT, 1);
1650
1651 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, 0, "move-cursor", 2,
1652 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1653 G_TYPE_INT, -1);
1654
1655 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
1656 "move-cursor", 2,
1657 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1658 G_TYPE_INT, 1);
1659
1660 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
1661 "move-cursor", 2,
1662 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1663 G_TYPE_INT, -1);
1664
1665 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
1666 "move-cursor", 2,
1667 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1668 G_TYPE_INT, 1);
1669
1670 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
1671 "move-cursor", 2,
1672 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1673 G_TYPE_INT, -1);
1674
1675 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1676 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1677
1678 gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", 0);
1679 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, GDK_CONTROL_MASK, "select-all", 0);
1680
1681 gtk_binding_entry_add_signal (binding_set, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1682 gtk_binding_entry_add_signal (binding_set, GDK_KEY_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1683
1684 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1685 G_TYPE_BOOLEAN, TRUE);
1686 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1687 G_TYPE_BOOLEAN, TRUE);
1688
1689 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0, "select-cursor-row", 1,
1690 G_TYPE_BOOLEAN, TRUE);
1691 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Space, 0, "select-cursor-row", 1,
1692 G_TYPE_BOOLEAN, TRUE);
1693 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "select-cursor-row", 1,
1694 G_TYPE_BOOLEAN, TRUE);
1695 gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0, "select-cursor-row", 1,
1696 G_TYPE_BOOLEAN, TRUE);
1697 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "select-cursor-row", 1,
1698 G_TYPE_BOOLEAN, TRUE);
1699
1700 /* expand and collapse rows */
1701 gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, 0, "expand-collapse-cursor-row", 3,
1702 G_TYPE_BOOLEAN, TRUE,
1703 G_TYPE_BOOLEAN, TRUE,
1704 G_TYPE_BOOLEAN, FALSE);
1705
1706 gtk_binding_entry_add_signal (binding_set, GDK_KEY_asterisk, 0,
1707 "expand-collapse-cursor-row", 3,
1708 G_TYPE_BOOLEAN, TRUE,
1709 G_TYPE_BOOLEAN, TRUE,
1710 G_TYPE_BOOLEAN, TRUE);
1711 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Multiply, 0,
1712 "expand-collapse-cursor-row", 3,
1713 G_TYPE_BOOLEAN, TRUE,
1714 G_TYPE_BOOLEAN, TRUE,
1715 G_TYPE_BOOLEAN, TRUE);
1716
1717 gtk_binding_entry_add_signal (binding_set, GDK_KEY_slash, 0,
1718 "expand-collapse-cursor-row", 3,
1719 G_TYPE_BOOLEAN, TRUE,
1720 G_TYPE_BOOLEAN, FALSE,
1721 G_TYPE_BOOLEAN, FALSE);
1722 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Divide, 0,
1723 "expand-collapse-cursor-row", 3,
1724 G_TYPE_BOOLEAN, TRUE,
1725 G_TYPE_BOOLEAN, FALSE,
1726 G_TYPE_BOOLEAN, FALSE);
1727
1728 /* Not doable on US keyboards */
1729 gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1730 G_TYPE_BOOLEAN, TRUE,
1731 G_TYPE_BOOLEAN, TRUE,
1732 G_TYPE_BOOLEAN, TRUE);
1733 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, 0, "expand-collapse-cursor-row", 3,
1734 G_TYPE_BOOLEAN, TRUE,
1735 G_TYPE_BOOLEAN, TRUE,
1736 G_TYPE_BOOLEAN, FALSE);
1737 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1738 G_TYPE_BOOLEAN, TRUE,
1739 G_TYPE_BOOLEAN, TRUE,
1740 G_TYPE_BOOLEAN, TRUE);
1741 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1742 G_TYPE_BOOLEAN, TRUE,
1743 G_TYPE_BOOLEAN, TRUE,
1744 G_TYPE_BOOLEAN, TRUE);
1745 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right, GDK_SHIFT_MASK,
1746 "expand-collapse-cursor-row", 3,
1747 G_TYPE_BOOLEAN, FALSE,
1748 G_TYPE_BOOLEAN, TRUE,
1749 G_TYPE_BOOLEAN, TRUE);
1750 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right, GDK_SHIFT_MASK,
1751 "expand-collapse-cursor-row", 3,
1752 G_TYPE_BOOLEAN, FALSE,
1753 G_TYPE_BOOLEAN, TRUE,
1754 G_TYPE_BOOLEAN, TRUE);
1755 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Right,
1756 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1757 "expand-collapse-cursor-row", 3,
1758 G_TYPE_BOOLEAN, FALSE,
1759 G_TYPE_BOOLEAN, TRUE,
1760 G_TYPE_BOOLEAN, TRUE);
1761 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Right,
1762 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1763 "expand-collapse-cursor-row", 3,
1764 G_TYPE_BOOLEAN, FALSE,
1765 G_TYPE_BOOLEAN, TRUE,
1766 G_TYPE_BOOLEAN, TRUE);
1767
1768 gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, 0, "expand-collapse-cursor-row", 3,
1769 G_TYPE_BOOLEAN, TRUE,
1770 G_TYPE_BOOLEAN, FALSE,
1771 G_TYPE_BOOLEAN, FALSE);
1772 gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1773 G_TYPE_BOOLEAN, TRUE,
1774 G_TYPE_BOOLEAN, FALSE,
1775 G_TYPE_BOOLEAN, TRUE);
1776 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, 0, "expand-collapse-cursor-row", 3,
1777 G_TYPE_BOOLEAN, TRUE,
1778 G_TYPE_BOOLEAN, FALSE,
1779 G_TYPE_BOOLEAN, FALSE);
1780 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Subtract, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1781 G_TYPE_BOOLEAN, TRUE,
1782 G_TYPE_BOOLEAN, FALSE,
1783 G_TYPE_BOOLEAN, TRUE);
1784 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left, GDK_SHIFT_MASK,
1785 "expand-collapse-cursor-row", 3,
1786 G_TYPE_BOOLEAN, FALSE,
1787 G_TYPE_BOOLEAN, FALSE,
1788 G_TYPE_BOOLEAN, TRUE);
1789 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left, GDK_SHIFT_MASK,
1790 "expand-collapse-cursor-row", 3,
1791 G_TYPE_BOOLEAN, FALSE,
1792 G_TYPE_BOOLEAN, FALSE,
1793 G_TYPE_BOOLEAN, TRUE);
1794 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Left,
1795 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1796 "expand-collapse-cursor-row", 3,
1797 G_TYPE_BOOLEAN, FALSE,
1798 G_TYPE_BOOLEAN, FALSE,
1799 G_TYPE_BOOLEAN, TRUE);
1800 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Left,
1801 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1802 "expand-collapse-cursor-row", 3,
1803 G_TYPE_BOOLEAN, FALSE,
1804 G_TYPE_BOOLEAN, FALSE,
1805 G_TYPE_BOOLEAN, TRUE);
1806
1807 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0, "select-cursor-parent", 0);
1808 gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1809
1810 gtk_binding_entry_add_signal (binding_set, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1811
1812 gtk_binding_entry_add_signal (binding_set, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1813
1814 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TREE_VIEW_ACCESSIBLE);
1815 gtk_widget_class_set_css_name (widget_class, "treeview");
1816 }
1817
1818 static void
gtk_tree_view_init(GtkTreeView * tree_view)1819 gtk_tree_view_init (GtkTreeView *tree_view)
1820 {
1821 GtkTreeViewPrivate *priv;
1822 GtkCssNode *widget_node;
1823
1824 priv = tree_view->priv = gtk_tree_view_get_instance_private (tree_view);
1825
1826 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1827
1828 priv->show_expanders = TRUE;
1829 priv->draw_keyfocus = TRUE;
1830 priv->headers_visible = TRUE;
1831 priv->activate_on_single_click = FALSE;
1832
1833 priv->pixel_cache = _gtk_pixel_cache_new ();
1834
1835 /* We need some padding */
1836 priv->dy = 0;
1837 priv->cursor_offset = 0;
1838 priv->n_columns = 0;
1839 priv->header_height = 1;
1840 priv->x_drag = 0;
1841 priv->drag_pos = -1;
1842 priv->header_has_focus = FALSE;
1843 priv->press_start_x = -1;
1844 priv->press_start_y = -1;
1845 priv->reorderable = FALSE;
1846 priv->presize_handler_tick_cb = 0;
1847 priv->scroll_sync_timer = 0;
1848 priv->fixed_height = -1;
1849 priv->fixed_height_mode = FALSE;
1850 priv->fixed_height_check = 0;
1851 priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view);
1852 priv->enable_search = TRUE;
1853 priv->search_column = -1;
1854 priv->search_position_func = gtk_tree_view_search_position_func;
1855 priv->search_equal_func = gtk_tree_view_search_equal_func;
1856 priv->search_custom_entry_set = FALSE;
1857 priv->typeselect_flush_timeout = 0;
1858 priv->init_hadjust_value = TRUE;
1859 priv->width = 0;
1860
1861 priv->hover_selection = FALSE;
1862 priv->hover_expand = FALSE;
1863
1864 priv->level_indentation = 0;
1865
1866 priv->rubber_banding_enable = FALSE;
1867
1868 priv->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE;
1869 priv->tree_lines_enabled = FALSE;
1870
1871 priv->tooltip_column = -1;
1872
1873 priv->post_validation_flag = FALSE;
1874
1875 priv->event_last_x = -10000;
1876 priv->event_last_y = -10000;
1877
1878 gtk_tree_view_do_set_vadjustment (tree_view, NULL);
1879 gtk_tree_view_do_set_hadjustment (tree_view, NULL);
1880
1881 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1882 GTK_STYLE_CLASS_VIEW);
1883
1884 widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view));
1885 priv->header_node = gtk_css_node_new ();
1886 gtk_css_node_set_name (priv->header_node, I_("header"));
1887 gtk_css_node_set_parent (priv->header_node, widget_node);
1888 gtk_css_node_set_state (priv->header_node, gtk_css_node_get_state (widget_node));
1889 g_object_unref (priv->header_node);
1890
1891 priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (tree_view));
1892 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->multipress_gesture), 0);
1893 g_signal_connect (priv->multipress_gesture, "pressed",
1894 G_CALLBACK (gtk_tree_view_multipress_gesture_pressed), tree_view);
1895 g_signal_connect (priv->multipress_gesture, "released",
1896 G_CALLBACK (gtk_tree_view_multipress_gesture_released), tree_view);
1897
1898 priv->column_multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (tree_view));
1899 g_signal_connect (priv->column_multipress_gesture, "pressed",
1900 G_CALLBACK (gtk_tree_view_column_multipress_gesture_pressed), tree_view);
1901 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->column_multipress_gesture),
1902 GTK_PHASE_CAPTURE);
1903
1904 priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (tree_view));
1905 g_signal_connect (priv->drag_gesture, "drag-begin",
1906 G_CALLBACK (gtk_tree_view_drag_gesture_begin), tree_view);
1907 g_signal_connect (priv->drag_gesture, "drag-update",
1908 G_CALLBACK (gtk_tree_view_drag_gesture_update), tree_view);
1909 g_signal_connect (priv->drag_gesture, "drag-end",
1910 G_CALLBACK (gtk_tree_view_drag_gesture_end), tree_view);
1911
1912 priv->column_drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (tree_view));
1913 g_signal_connect (priv->column_drag_gesture, "drag-begin",
1914 G_CALLBACK (gtk_tree_view_column_drag_gesture_begin), tree_view);
1915 g_signal_connect (priv->column_drag_gesture, "drag-update",
1916 G_CALLBACK (gtk_tree_view_column_drag_gesture_update), tree_view);
1917 g_signal_connect (priv->column_drag_gesture, "drag-end",
1918 G_CALLBACK (gtk_tree_view_column_drag_gesture_end), tree_view);
1919 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->column_drag_gesture),
1920 GTK_PHASE_CAPTURE);
1921 }
1922
1923
1924
1925 /* GObject Methods
1926 */
1927
1928 static void
gtk_tree_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1929 gtk_tree_view_set_property (GObject *object,
1930 guint prop_id,
1931 const GValue *value,
1932 GParamSpec *pspec)
1933 {
1934 GtkTreeView *tree_view;
1935
1936 tree_view = GTK_TREE_VIEW (object);
1937
1938 switch (prop_id)
1939 {
1940 case PROP_MODEL:
1941 gtk_tree_view_set_model (tree_view, g_value_get_object (value));
1942 break;
1943 case PROP_HADJUSTMENT:
1944 gtk_tree_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1945 break;
1946 case PROP_VADJUSTMENT:
1947 gtk_tree_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1948 break;
1949 case PROP_HSCROLL_POLICY:
1950 if (tree_view->priv->hscroll_policy != g_value_get_enum (value))
1951 {
1952 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1953 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1954 g_object_notify_by_pspec (object, pspec);
1955 }
1956 break;
1957 case PROP_VSCROLL_POLICY:
1958 if (tree_view->priv->vscroll_policy != g_value_get_enum (value))
1959 {
1960 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1961 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1962 g_object_notify_by_pspec (object, pspec);
1963 }
1964 break;
1965 case PROP_HEADERS_VISIBLE:
1966 gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1967 break;
1968 case PROP_HEADERS_CLICKABLE:
1969 gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1970 break;
1971 case PROP_EXPANDER_COLUMN:
1972 gtk_tree_view_set_expander_column (tree_view, g_value_get_object (value));
1973 break;
1974 case PROP_REORDERABLE:
1975 gtk_tree_view_set_reorderable (tree_view, g_value_get_boolean (value));
1976 break;
1977 case PROP_RULES_HINT:
1978 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1979 gtk_tree_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1980 G_GNUC_END_IGNORE_DEPRECATIONS
1981 break;
1982 case PROP_ENABLE_SEARCH:
1983 gtk_tree_view_set_enable_search (tree_view, g_value_get_boolean (value));
1984 break;
1985 case PROP_SEARCH_COLUMN:
1986 gtk_tree_view_set_search_column (tree_view, g_value_get_int (value));
1987 break;
1988 case PROP_FIXED_HEIGHT_MODE:
1989 gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value));
1990 break;
1991 case PROP_HOVER_SELECTION:
1992 if (tree_view->priv->hover_selection != g_value_get_boolean (value))
1993 {
1994 tree_view->priv->hover_selection = g_value_get_boolean (value);
1995 g_object_notify_by_pspec (object, pspec);
1996 }
1997 break;
1998 case PROP_HOVER_EXPAND:
1999 if (tree_view->priv->hover_expand != g_value_get_boolean (value))
2000 {
2001 tree_view->priv->hover_expand = g_value_get_boolean (value);
2002 g_object_notify_by_pspec (object, pspec);
2003 }
2004 break;
2005 case PROP_SHOW_EXPANDERS:
2006 gtk_tree_view_set_show_expanders (tree_view, g_value_get_boolean (value));
2007 break;
2008 case PROP_LEVEL_INDENTATION:
2009 if (tree_view->priv->level_indentation != g_value_get_int (value))
2010 {
2011 tree_view->priv->level_indentation = g_value_get_int (value);
2012 g_object_notify_by_pspec (object, pspec);
2013 }
2014 break;
2015 case PROP_RUBBER_BANDING:
2016 if (tree_view->priv->rubber_banding_enable != g_value_get_boolean (value))
2017 {
2018 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
2019 g_object_notify_by_pspec (object, pspec);
2020 }
2021 break;
2022 case PROP_ENABLE_GRID_LINES:
2023 gtk_tree_view_set_grid_lines (tree_view, g_value_get_enum (value));
2024 break;
2025 case PROP_ENABLE_TREE_LINES:
2026 gtk_tree_view_set_enable_tree_lines (tree_view, g_value_get_boolean (value));
2027 break;
2028 case PROP_TOOLTIP_COLUMN:
2029 gtk_tree_view_set_tooltip_column (tree_view, g_value_get_int (value));
2030 break;
2031 case PROP_ACTIVATE_ON_SINGLE_CLICK:
2032 gtk_tree_view_set_activate_on_single_click (tree_view, g_value_get_boolean (value));
2033 break;
2034 default:
2035 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2036 break;
2037 }
2038 }
2039
2040 static void
gtk_tree_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2041 gtk_tree_view_get_property (GObject *object,
2042 guint prop_id,
2043 GValue *value,
2044 GParamSpec *pspec)
2045 {
2046 GtkTreeView *tree_view;
2047
2048 tree_view = GTK_TREE_VIEW (object);
2049
2050 switch (prop_id)
2051 {
2052 case PROP_MODEL:
2053 g_value_set_object (value, tree_view->priv->model);
2054 break;
2055 case PROP_HADJUSTMENT:
2056 g_value_set_object (value, tree_view->priv->hadjustment);
2057 break;
2058 case PROP_VADJUSTMENT:
2059 g_value_set_object (value, tree_view->priv->vadjustment);
2060 break;
2061 case PROP_HSCROLL_POLICY:
2062 g_value_set_enum (value, tree_view->priv->hscroll_policy);
2063 break;
2064 case PROP_VSCROLL_POLICY:
2065 g_value_set_enum (value, tree_view->priv->vscroll_policy);
2066 break;
2067 case PROP_HEADERS_VISIBLE:
2068 g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view));
2069 break;
2070 case PROP_HEADERS_CLICKABLE:
2071 g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view));
2072 break;
2073 case PROP_EXPANDER_COLUMN:
2074 g_value_set_object (value, tree_view->priv->expander_column);
2075 break;
2076 case PROP_REORDERABLE:
2077 g_value_set_boolean (value, tree_view->priv->reorderable);
2078 break;
2079 case PROP_RULES_HINT:
2080 g_value_set_boolean (value, tree_view->priv->has_rules);
2081 break;
2082 case PROP_ENABLE_SEARCH:
2083 g_value_set_boolean (value, tree_view->priv->enable_search);
2084 break;
2085 case PROP_SEARCH_COLUMN:
2086 g_value_set_int (value, tree_view->priv->search_column);
2087 break;
2088 case PROP_FIXED_HEIGHT_MODE:
2089 g_value_set_boolean (value, tree_view->priv->fixed_height_mode);
2090 break;
2091 case PROP_HOVER_SELECTION:
2092 g_value_set_boolean (value, tree_view->priv->hover_selection);
2093 break;
2094 case PROP_HOVER_EXPAND:
2095 g_value_set_boolean (value, tree_view->priv->hover_expand);
2096 break;
2097 case PROP_SHOW_EXPANDERS:
2098 g_value_set_boolean (value, tree_view->priv->show_expanders);
2099 break;
2100 case PROP_LEVEL_INDENTATION:
2101 g_value_set_int (value, tree_view->priv->level_indentation);
2102 break;
2103 case PROP_RUBBER_BANDING:
2104 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
2105 break;
2106 case PROP_ENABLE_GRID_LINES:
2107 g_value_set_enum (value, tree_view->priv->grid_lines);
2108 break;
2109 case PROP_ENABLE_TREE_LINES:
2110 g_value_set_boolean (value, tree_view->priv->tree_lines_enabled);
2111 break;
2112 case PROP_TOOLTIP_COLUMN:
2113 g_value_set_int (value, tree_view->priv->tooltip_column);
2114 break;
2115 case PROP_ACTIVATE_ON_SINGLE_CLICK:
2116 g_value_set_boolean (value, tree_view->priv->activate_on_single_click);
2117 break;
2118 default:
2119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2120 break;
2121 }
2122 }
2123
2124 static void
gtk_tree_view_finalize(GObject * object)2125 gtk_tree_view_finalize (GObject *object)
2126 {
2127 G_OBJECT_CLASS (gtk_tree_view_parent_class)->finalize (object);
2128 }
2129
2130
2131 static GtkBuildableIface *parent_buildable_iface;
2132
2133 static void
gtk_tree_view_buildable_init(GtkBuildableIface * iface)2134 gtk_tree_view_buildable_init (GtkBuildableIface *iface)
2135 {
2136 parent_buildable_iface = g_type_interface_peek_parent (iface);
2137 iface->add_child = gtk_tree_view_buildable_add_child;
2138 iface->get_internal_child = gtk_tree_view_buildable_get_internal_child;
2139 }
2140
2141 static void
gtk_tree_view_buildable_add_child(GtkBuildable * tree_view,GtkBuilder * builder,GObject * child,const gchar * type)2142 gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
2143 GtkBuilder *builder,
2144 GObject *child,
2145 const gchar *type)
2146 {
2147 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child));
2148 }
2149
2150 static GObject *
gtk_tree_view_buildable_get_internal_child(GtkBuildable * buildable,GtkBuilder * builder,const gchar * childname)2151 gtk_tree_view_buildable_get_internal_child (GtkBuildable *buildable,
2152 GtkBuilder *builder,
2153 const gchar *childname)
2154 {
2155 if (strcmp (childname, "selection") == 0)
2156 return G_OBJECT (GTK_TREE_VIEW (buildable)->priv->selection);
2157
2158 return parent_buildable_iface->get_internal_child (buildable,
2159 builder,
2160 childname);
2161 }
2162
2163 /* GtkWidget Methods
2164 */
2165
2166 static void
gtk_tree_view_free_rbtree(GtkTreeView * tree_view)2167 gtk_tree_view_free_rbtree (GtkTreeView *tree_view)
2168 {
2169 _gtk_rbtree_free (tree_view->priv->tree);
2170
2171 tree_view->priv->tree = NULL;
2172 tree_view->priv->button_pressed_node = NULL;
2173 tree_view->priv->button_pressed_tree = NULL;
2174 tree_view->priv->prelight_tree = NULL;
2175 tree_view->priv->prelight_node = NULL;
2176 }
2177
2178 static void
gtk_tree_view_destroy_search_window(GtkTreeView * tree_view)2179 gtk_tree_view_destroy_search_window (GtkTreeView *tree_view)
2180 {
2181 gtk_widget_destroy (tree_view->priv->search_window);
2182
2183 tree_view->priv->search_window = NULL;
2184 tree_view->priv->search_entry = NULL;
2185 tree_view->priv->search_entry_changed_id = 0;
2186 }
2187
2188 static void
gtk_tree_view_destroy(GtkWidget * widget)2189 gtk_tree_view_destroy (GtkWidget *widget)
2190 {
2191 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2192 GList *list;
2193
2194 gtk_tree_view_stop_editing (tree_view, TRUE);
2195 gtk_tree_view_stop_rubber_band (tree_view);
2196
2197 if (tree_view->priv->columns != NULL)
2198 {
2199 list = tree_view->priv->columns;
2200 while (list)
2201 {
2202 GtkTreeViewColumn *column;
2203 column = GTK_TREE_VIEW_COLUMN (list->data);
2204 list = list->next;
2205 gtk_tree_view_remove_column (tree_view, column);
2206 }
2207 tree_view->priv->columns = NULL;
2208 }
2209
2210 if (tree_view->priv->tree != NULL)
2211 {
2212 gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree);
2213
2214 gtk_tree_view_free_rbtree (tree_view);
2215 }
2216
2217 if (tree_view->priv->selection != NULL)
2218 {
2219 _gtk_tree_selection_set_tree_view (tree_view->priv->selection, NULL);
2220 g_object_unref (tree_view->priv->selection);
2221 tree_view->priv->selection = NULL;
2222 }
2223
2224 if (tree_view->priv->scroll_to_path != NULL)
2225 {
2226 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
2227 tree_view->priv->scroll_to_path = NULL;
2228 }
2229
2230 if (tree_view->priv->drag_dest_row != NULL)
2231 {
2232 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
2233 tree_view->priv->drag_dest_row = NULL;
2234 }
2235
2236 if (tree_view->priv->top_row != NULL)
2237 {
2238 gtk_tree_row_reference_free (tree_view->priv->top_row);
2239 tree_view->priv->top_row = NULL;
2240 }
2241
2242 if (tree_view->priv->column_drop_func_data &&
2243 tree_view->priv->column_drop_func_data_destroy)
2244 {
2245 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
2246 tree_view->priv->column_drop_func_data = NULL;
2247 }
2248
2249 if (tree_view->priv->destroy_count_destroy &&
2250 tree_view->priv->destroy_count_data)
2251 {
2252 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
2253 tree_view->priv->destroy_count_data = NULL;
2254 }
2255
2256 gtk_tree_row_reference_free (tree_view->priv->anchor);
2257 tree_view->priv->anchor = NULL;
2258
2259 /* destroy interactive search dialog */
2260 if (tree_view->priv->search_window)
2261 {
2262 gtk_tree_view_destroy_search_window (tree_view);
2263 if (tree_view->priv->typeselect_flush_timeout)
2264 {
2265 g_source_remove (tree_view->priv->typeselect_flush_timeout);
2266 tree_view->priv->typeselect_flush_timeout = 0;
2267 }
2268 }
2269
2270 if (tree_view->priv->search_custom_entry_set)
2271 {
2272 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
2273 G_CALLBACK (gtk_tree_view_search_init),
2274 tree_view);
2275 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
2276 G_CALLBACK (gtk_tree_view_search_key_press_event),
2277 tree_view);
2278
2279 g_object_unref (tree_view->priv->search_entry);
2280
2281 tree_view->priv->search_entry = NULL;
2282 tree_view->priv->search_custom_entry_set = FALSE;
2283 }
2284
2285 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
2286 {
2287 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
2288 tree_view->priv->search_user_data = NULL;
2289 }
2290
2291 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
2292 {
2293 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
2294 tree_view->priv->search_position_user_data = NULL;
2295 }
2296
2297 if (tree_view->priv->row_separator_destroy && tree_view->priv->row_separator_data)
2298 {
2299 tree_view->priv->row_separator_destroy (tree_view->priv->row_separator_data);
2300 tree_view->priv->row_separator_data = NULL;
2301 }
2302
2303 gtk_tree_view_set_model (tree_view, NULL);
2304
2305 if (tree_view->priv->hadjustment)
2306 {
2307 g_object_unref (tree_view->priv->hadjustment);
2308 tree_view->priv->hadjustment = NULL;
2309 }
2310 if (tree_view->priv->vadjustment)
2311 {
2312 g_object_unref (tree_view->priv->vadjustment);
2313 tree_view->priv->vadjustment = NULL;
2314 }
2315
2316 if (tree_view->priv->pixel_cache)
2317 _gtk_pixel_cache_free (tree_view->priv->pixel_cache);
2318 tree_view->priv->pixel_cache = NULL;
2319
2320 g_clear_object (&tree_view->priv->multipress_gesture);
2321 g_clear_object (&tree_view->priv->drag_gesture);
2322 g_clear_object (&tree_view->priv->column_multipress_gesture);
2323 g_clear_object (&tree_view->priv->column_drag_gesture);
2324
2325 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->destroy (widget);
2326 }
2327
2328 /* GtkWidget::map helper */
2329 static void
gtk_tree_view_map_buttons(GtkTreeView * tree_view)2330 gtk_tree_view_map_buttons (GtkTreeView *tree_view)
2331 {
2332 GList *list;
2333
2334 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
2335
2336 if (tree_view->priv->headers_visible)
2337 {
2338 GtkTreeViewColumn *column;
2339 GtkWidget *button;
2340 GdkWindow *window;
2341
2342 for (list = tree_view->priv->columns; list; list = list->next)
2343 {
2344 column = list->data;
2345 button = gtk_tree_view_column_get_button (column);
2346
2347 if (gtk_tree_view_column_get_visible (column) && button)
2348 gtk_widget_show_now (button);
2349
2350 if (gtk_widget_get_visible (button) &&
2351 !gtk_widget_get_mapped (button))
2352 gtk_widget_map (button);
2353 }
2354 for (list = tree_view->priv->columns; list; list = list->next)
2355 {
2356 column = list->data;
2357 if (gtk_tree_view_column_get_visible (column) == FALSE)
2358 continue;
2359
2360 window = _gtk_tree_view_column_get_window (column);
2361 if (gtk_tree_view_column_get_resizable (column))
2362 {
2363 gdk_window_raise (window);
2364 gdk_window_show (window);
2365 }
2366 else
2367 gdk_window_hide (window);
2368 }
2369 gdk_window_show (tree_view->priv->header_window);
2370 }
2371 }
2372
2373 static void
gtk_tree_view_map(GtkWidget * widget)2374 gtk_tree_view_map (GtkWidget *widget)
2375 {
2376 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2377 GList *tmp_list;
2378
2379 _gtk_pixel_cache_map (tree_view->priv->pixel_cache);
2380
2381 gtk_widget_set_mapped (widget, TRUE);
2382
2383 tmp_list = tree_view->priv->children;
2384 while (tmp_list)
2385 {
2386 GtkTreeViewChild *child = tmp_list->data;
2387 tmp_list = tmp_list->next;
2388
2389 if (gtk_widget_get_visible (child->widget))
2390 {
2391 if (!gtk_widget_get_mapped (child->widget))
2392 gtk_widget_map (child->widget);
2393 }
2394 }
2395 gdk_window_show (tree_view->priv->bin_window);
2396
2397 gtk_tree_view_map_buttons (tree_view);
2398
2399 gdk_window_show (gtk_widget_get_window (widget));
2400 }
2401
2402 static void
gtk_tree_view_unmap(GtkWidget * widget)2403 gtk_tree_view_unmap (GtkWidget *widget)
2404 {
2405 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2406
2407 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unmap (widget);
2408
2409 _gtk_pixel_cache_unmap (tree_view->priv->pixel_cache);
2410 }
2411
2412 static void
gtk_tree_view_bin_window_invalidate_handler(GdkWindow * window,cairo_region_t * region)2413 gtk_tree_view_bin_window_invalidate_handler (GdkWindow *window,
2414 cairo_region_t *region)
2415 {
2416 gpointer widget;
2417 GtkTreeView *tree_view;
2418 int y;
2419
2420 gdk_window_get_user_data (window, &widget);
2421 tree_view = GTK_TREE_VIEW (widget);
2422
2423 y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2424 cairo_region_translate (region,
2425 0, y);
2426 _gtk_pixel_cache_invalidate (tree_view->priv->pixel_cache, region);
2427 cairo_region_translate (region,
2428 0, -y);
2429 }
2430
2431 static void
gtk_tree_view_queue_draw_region(GtkWidget * widget,const cairo_region_t * region)2432 gtk_tree_view_queue_draw_region (GtkWidget *widget,
2433 const cairo_region_t *region)
2434 {
2435 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2436
2437 /* There is no way we can know if a region targets the
2438 not-currently-visible but in pixel cache region, so we
2439 always just invalidate the whole thing whenever the
2440 tree view gets a queue draw. This doesn't normally happen
2441 in normal scrolling cases anyway. */
2442 _gtk_pixel_cache_invalidate (tree_view->priv->pixel_cache, NULL);
2443
2444 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->queue_draw_region (widget,
2445 region);
2446 }
2447
2448 static void
gtk_tree_view_realize(GtkWidget * widget)2449 gtk_tree_view_realize (GtkWidget *widget)
2450 {
2451 GtkAllocation allocation;
2452 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2453 GdkWindow *window;
2454 GdkWindowAttr attributes;
2455 GList *tmp_list;
2456 gint attributes_mask;
2457
2458 gtk_widget_set_realized (widget, TRUE);
2459
2460 gtk_widget_get_allocation (widget, &allocation);
2461
2462 /* Make the main, clipping window */
2463 attributes.window_type = GDK_WINDOW_CHILD;
2464 attributes.x = allocation.x;
2465 attributes.y = allocation.y;
2466 attributes.width = allocation.width;
2467 attributes.height = allocation.height;
2468 attributes.wclass = GDK_INPUT_OUTPUT;
2469 attributes.visual = gtk_widget_get_visual (widget);
2470 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
2471
2472 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2473
2474 window = gdk_window_new (gtk_widget_get_parent_window (widget),
2475 &attributes, attributes_mask);
2476 gtk_widget_set_window (widget, window);
2477 gtk_widget_register_window (widget, window);
2478
2479 gtk_widget_get_allocation (widget, &allocation);
2480
2481 /* Make the window for the tree */
2482 attributes.x = 0;
2483 attributes.y = gtk_tree_view_get_effective_header_height (tree_view);
2484 attributes.width = MAX (tree_view->priv->width, allocation.width);
2485 attributes.height = allocation.height;
2486 attributes.event_mask = (GDK_SCROLL_MASK |
2487 GDK_SMOOTH_SCROLL_MASK |
2488 GDK_POINTER_MOTION_MASK |
2489 GDK_ENTER_NOTIFY_MASK |
2490 GDK_LEAVE_NOTIFY_MASK |
2491 GDK_BUTTON_PRESS_MASK |
2492 GDK_BUTTON_RELEASE_MASK |
2493 gtk_widget_get_events (widget));
2494
2495 tree_view->priv->bin_window = gdk_window_new (window,
2496 &attributes, attributes_mask);
2497 gtk_widget_register_window (widget, tree_view->priv->bin_window);
2498 gdk_window_set_invalidate_handler (tree_view->priv->bin_window,
2499 gtk_tree_view_bin_window_invalidate_handler);
2500
2501 gtk_widget_get_allocation (widget, &allocation);
2502
2503 /* Make the column header window */
2504 attributes.x = 0;
2505 attributes.y = 0;
2506 attributes.width = MAX (tree_view->priv->width, allocation.width);
2507 attributes.height = tree_view->priv->header_height;
2508 attributes.event_mask = (GDK_SCROLL_MASK |
2509 GDK_ENTER_NOTIFY_MASK |
2510 GDK_LEAVE_NOTIFY_MASK |
2511 GDK_BUTTON_PRESS_MASK |
2512 GDK_BUTTON_RELEASE_MASK |
2513 GDK_KEY_PRESS_MASK |
2514 GDK_KEY_RELEASE_MASK |
2515 gtk_widget_get_events (widget));
2516
2517 tree_view->priv->header_window = gdk_window_new (window,
2518 &attributes, attributes_mask);
2519 gtk_widget_register_window (widget, tree_view->priv->header_window);
2520
2521 tmp_list = tree_view->priv->children;
2522 while (tmp_list)
2523 {
2524 GtkTreeViewChild *child = tmp_list->data;
2525 tmp_list = tmp_list->next;
2526
2527 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
2528 }
2529
2530 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2531 _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data));
2532
2533 /* Need to call those here, since they create GCs */
2534 gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
2535 gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
2536
2537 install_presize_handler (tree_view);
2538
2539 gtk_gesture_set_window (tree_view->priv->multipress_gesture,
2540 tree_view->priv->bin_window);
2541 gtk_gesture_set_window (tree_view->priv->drag_gesture,
2542 tree_view->priv->bin_window);
2543 }
2544
2545 static void
gtk_tree_view_unrealize(GtkWidget * widget)2546 gtk_tree_view_unrealize (GtkWidget *widget)
2547 {
2548 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2549 GtkTreeViewPrivate *priv = tree_view->priv;
2550 GList *list;
2551
2552 if (priv->scroll_timeout != 0)
2553 {
2554 g_source_remove (priv->scroll_timeout);
2555 priv->scroll_timeout = 0;
2556 }
2557
2558 if (priv->auto_expand_timeout != 0)
2559 {
2560 g_source_remove (priv->auto_expand_timeout);
2561 priv->auto_expand_timeout = 0;
2562 }
2563
2564 if (priv->open_dest_timeout != 0)
2565 {
2566 g_source_remove (priv->open_dest_timeout);
2567 priv->open_dest_timeout = 0;
2568 }
2569
2570 if (priv->presize_handler_tick_cb != 0)
2571 {
2572 gtk_widget_remove_tick_callback (widget, priv->presize_handler_tick_cb);
2573 priv->presize_handler_tick_cb = 0;
2574 }
2575
2576 if (priv->validate_rows_timer != 0)
2577 {
2578 g_source_remove (priv->validate_rows_timer);
2579 priv->validate_rows_timer = 0;
2580 }
2581
2582 if (priv->scroll_sync_timer != 0)
2583 {
2584 g_source_remove (priv->scroll_sync_timer);
2585 priv->scroll_sync_timer = 0;
2586 }
2587
2588 if (priv->typeselect_flush_timeout)
2589 {
2590 g_source_remove (priv->typeselect_flush_timeout);
2591 priv->typeselect_flush_timeout = 0;
2592 }
2593
2594 for (list = priv->columns; list; list = list->next)
2595 _gtk_tree_view_column_unrealize_button (GTK_TREE_VIEW_COLUMN (list->data));
2596
2597 gtk_widget_unregister_window (widget, priv->bin_window);
2598 gdk_window_destroy (priv->bin_window);
2599 priv->bin_window = NULL;
2600
2601 gtk_widget_unregister_window (widget, priv->header_window);
2602 gdk_window_destroy (priv->header_window);
2603 priv->header_window = NULL;
2604
2605 if (priv->drag_window)
2606 {
2607 gtk_widget_unregister_window (widget, priv->drag_window);
2608 gdk_window_destroy (priv->drag_window);
2609 priv->drag_window = NULL;
2610 }
2611
2612 if (priv->drag_highlight_window)
2613 {
2614 gtk_widget_unregister_window (widget, priv->drag_highlight_window);
2615 gdk_window_destroy (priv->drag_highlight_window);
2616 priv->drag_highlight_window = NULL;
2617 }
2618
2619 gtk_gesture_set_window (tree_view->priv->multipress_gesture, NULL);
2620 gtk_gesture_set_window (tree_view->priv->drag_gesture, NULL);
2621
2622 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unrealize (widget);
2623 }
2624
2625 /* GtkWidget::get_preferred_height helper */
2626 static void
gtk_tree_view_update_height(GtkTreeView * tree_view)2627 gtk_tree_view_update_height (GtkTreeView *tree_view)
2628 {
2629 GList *list;
2630
2631 tree_view->priv->header_height = 0;
2632
2633 for (list = tree_view->priv->columns; list; list = list->next)
2634 {
2635 GtkRequisition requisition;
2636 GtkTreeViewColumn *column = list->data;
2637 GtkWidget *button = gtk_tree_view_column_get_button (column);
2638
2639 if (button == NULL)
2640 continue;
2641
2642 gtk_widget_get_preferred_size (button, &requisition, NULL);
2643 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
2644 }
2645 }
2646
2647 static gint
gtk_tree_view_get_height(GtkTreeView * tree_view)2648 gtk_tree_view_get_height (GtkTreeView *tree_view)
2649 {
2650 if (tree_view->priv->tree == NULL)
2651 return 0;
2652 else
2653 return tree_view->priv->tree->root->offset;
2654 }
2655
2656 static void
gtk_tree_view_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)2657 gtk_tree_view_get_preferred_width (GtkWidget *widget,
2658 gint *minimum,
2659 gint *natural)
2660 {
2661 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2662 GList *list;
2663 GtkTreeViewColumn *column;
2664 gint width = 0;
2665
2666 /* we validate some rows initially just to make sure we have some size.
2667 * In practice, with a lot of static lists, this should get a good width.
2668 */
2669 do_validate_rows (tree_view, FALSE);
2670
2671 /* keep this in sync with size_allocate below */
2672 for (list = tree_view->priv->columns; list; list = list->next)
2673 {
2674 column = list->data;
2675 if (!gtk_tree_view_column_get_visible (column) || column == tree_view->priv->drag_column)
2676 continue;
2677
2678 width += _gtk_tree_view_column_request_width (column);
2679 }
2680
2681 *minimum = *natural = width;
2682 }
2683
2684 static void
gtk_tree_view_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)2685 gtk_tree_view_get_preferred_height (GtkWidget *widget,
2686 gint *minimum,
2687 gint *natural)
2688 {
2689 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2690 gint height;
2691
2692 gtk_tree_view_update_height (tree_view);
2693
2694 height = gtk_tree_view_get_height (tree_view) + gtk_tree_view_get_effective_header_height (tree_view);
2695
2696 *minimum = *natural = height;
2697 }
2698
2699 static int
gtk_tree_view_calculate_width_before_expander(GtkTreeView * tree_view)2700 gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view)
2701 {
2702 int width = 0;
2703 GList *list;
2704 gboolean rtl;
2705
2706 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2707 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2708 list->data != tree_view->priv->expander_column;
2709 list = (rtl ? list->prev : list->next))
2710 {
2711 GtkTreeViewColumn *column = list->data;
2712
2713 width += gtk_tree_view_column_get_width (column);
2714 }
2715
2716 return width;
2717 }
2718
2719 /* GtkWidget::size_allocate helper */
2720 static void
gtk_tree_view_size_allocate_columns(GtkWidget * widget,gboolean * width_changed)2721 gtk_tree_view_size_allocate_columns (GtkWidget *widget,
2722 gboolean *width_changed)
2723 {
2724 GtkTreeView *tree_view;
2725 GList *list, *first_column, *last_column;
2726 GtkTreeViewColumn *column;
2727 GtkAllocation widget_allocation;
2728 gint width = 0;
2729 gint extra, extra_per_column, extra_for_last;
2730 gint full_requested_width = 0;
2731 gint number_of_expand_columns = 0;
2732 gboolean rtl;
2733 gboolean update_expand;
2734
2735 tree_view = GTK_TREE_VIEW (widget);
2736
2737 for (last_column = g_list_last (tree_view->priv->columns);
2738 last_column &&
2739 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
2740 last_column = last_column->prev)
2741 ;
2742 if (last_column == NULL)
2743 return;
2744
2745 for (first_column = g_list_first (tree_view->priv->columns);
2746 first_column &&
2747 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
2748 first_column = first_column->next)
2749 ;
2750
2751 if (first_column == NULL)
2752 return;
2753
2754 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2755
2756 /* find out how many extra space and expandable columns we have */
2757 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
2758 {
2759 column = (GtkTreeViewColumn *)list->data;
2760
2761 if (!gtk_tree_view_column_get_visible (column) || column == tree_view->priv->drag_column)
2762 continue;
2763
2764 full_requested_width += _gtk_tree_view_column_request_width (column);
2765
2766 if (gtk_tree_view_column_get_expand (column))
2767 number_of_expand_columns++;
2768 }
2769
2770 /* Only update the expand value if the width of the widget has changed,
2771 * or the number of expand columns has changed, or if there are no expand
2772 * columns, or if we didn't have an size-allocation yet after the
2773 * last validated node.
2774 */
2775 update_expand = (width_changed && *width_changed == TRUE)
2776 || number_of_expand_columns != tree_view->priv->last_number_of_expand_columns
2777 || number_of_expand_columns == 0
2778 || tree_view->priv->post_validation_flag == TRUE;
2779
2780 tree_view->priv->post_validation_flag = FALSE;
2781
2782 gtk_widget_get_allocation (widget, &widget_allocation);
2783 if (!update_expand)
2784 {
2785 extra = tree_view->priv->last_extra_space;
2786 extra_for_last = MAX (widget_allocation.width - full_requested_width - extra, 0);
2787 }
2788 else
2789 {
2790 extra = MAX (widget_allocation.width - full_requested_width, 0);
2791 extra_for_last = 0;
2792
2793 tree_view->priv->last_extra_space = extra;
2794 }
2795
2796 if (number_of_expand_columns > 0)
2797 extra_per_column = extra/number_of_expand_columns;
2798 else
2799 extra_per_column = 0;
2800
2801 if (update_expand)
2802 {
2803 tree_view->priv->last_extra_space_per_column = extra_per_column;
2804 tree_view->priv->last_number_of_expand_columns = number_of_expand_columns;
2805 }
2806
2807 for (list = (rtl ? last_column : first_column);
2808 list != (rtl ? first_column->prev : last_column->next);
2809 list = (rtl ? list->prev : list->next))
2810 {
2811 gint column_width;
2812
2813 column = list->data;
2814
2815 if (!gtk_tree_view_column_get_visible (column) || column == tree_view->priv->drag_column)
2816 continue;
2817
2818 column_width = _gtk_tree_view_column_request_width (column);
2819
2820 if (gtk_tree_view_column_get_expand (column))
2821 {
2822 if (number_of_expand_columns == 1)
2823 {
2824 /* We add the remander to the last column as
2825 * */
2826 column_width += extra;
2827 }
2828 else
2829 {
2830 column_width += extra_per_column;
2831 extra -= extra_per_column;
2832 number_of_expand_columns --;
2833 }
2834 }
2835 else if (number_of_expand_columns == 0 &&
2836 list == last_column)
2837 {
2838 column_width += extra;
2839 }
2840
2841 /* In addition to expand, the last column can get even more
2842 * extra space so all available space is filled up.
2843 */
2844 if (extra_for_last > 0 && list == last_column)
2845 column_width += extra_for_last;
2846
2847 _gtk_tree_view_column_allocate (column, width, column_width);
2848
2849 width += column_width;
2850 }
2851
2852 /* We change the width here. The user might have been resizing columns,
2853 * which changes the total width of the tree view. This is of
2854 * importance for getting the horizontal scroll bar right.
2855 */
2856 if (tree_view->priv->width != width)
2857 {
2858 tree_view->priv->width = width;
2859 if (width_changed)
2860 *width_changed = TRUE;
2861 }
2862 }
2863
2864 /* GtkWidget::size_allocate helper */
2865 static void
gtk_tree_view_size_allocate_drag_column(GtkWidget * widget)2866 gtk_tree_view_size_allocate_drag_column (GtkWidget *widget)
2867 {
2868 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2869 GtkAllocation drag_allocation;
2870 GtkWidget *button;
2871
2872 if (tree_view->priv->drag_column == NULL)
2873 return;
2874
2875 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
2876
2877 drag_allocation.x = 0;
2878 drag_allocation.y = 0;
2879 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
2880 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
2881 gtk_widget_size_allocate (button, &drag_allocation);
2882 }
2883
2884 static void
gtk_tree_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)2885 gtk_tree_view_size_allocate (GtkWidget *widget,
2886 GtkAllocation *allocation)
2887 {
2888 GtkAllocation widget_allocation;
2889 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2890 GList *tmp_list;
2891 gboolean width_changed = FALSE;
2892 gint old_width;
2893 double page_size;
2894
2895 gtk_widget_get_allocation (widget, &widget_allocation);
2896 old_width = widget_allocation.width;
2897 if (allocation->width != widget_allocation.width)
2898 width_changed = TRUE;
2899
2900 gtk_widget_set_allocation (widget, allocation);
2901
2902 /* We size-allocate the columns first because the width of the
2903 * tree view (used in updating the adjustments below) might change.
2904 */
2905 gtk_tree_view_size_allocate_columns (widget, &width_changed);
2906 gtk_tree_view_size_allocate_drag_column (widget);
2907
2908 g_object_freeze_notify (G_OBJECT (tree_view->priv->hadjustment));
2909 gtk_adjustment_set_page_size (tree_view->priv->hadjustment,
2910 allocation->width);
2911 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment,
2912 allocation->width * 0.9);
2913 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment,
2914 allocation->width * 0.1);
2915 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2916 gtk_adjustment_set_upper (tree_view->priv->hadjustment,
2917 MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment),
2918 tree_view->priv->width));
2919 g_object_thaw_notify (G_OBJECT (tree_view->priv->hadjustment));
2920
2921 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2922 {
2923 if (allocation->width < tree_view->priv->width)
2924 {
2925 if (tree_view->priv->init_hadjust_value)
2926 {
2927 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2928 MAX (tree_view->priv->width -
2929 allocation->width, 0));
2930 tree_view->priv->init_hadjust_value = FALSE;
2931 }
2932 else if (allocation->width != old_width)
2933 {
2934 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2935 CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_width,
2936 0,
2937 tree_view->priv->width - allocation->width));
2938 }
2939 }
2940 else
2941 {
2942 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2943 tree_view->priv->init_hadjust_value = TRUE;
2944 }
2945 }
2946 else
2947 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2948 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2949 MAX (tree_view->priv->width -
2950 allocation->width, 0));
2951
2952 page_size = allocation->height - gtk_tree_view_get_effective_header_height (tree_view);
2953 gtk_adjustment_configure (tree_view->priv->vadjustment,
2954 gtk_adjustment_get_value (tree_view->priv->vadjustment),
2955 0,
2956 MAX (page_size, gtk_tree_view_get_height (tree_view)),
2957 page_size * 0.1,
2958 page_size * 0.9,
2959 page_size);
2960
2961 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2962 if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2963 gtk_tree_view_top_row_to_dy (tree_view);
2964 else
2965 gtk_tree_view_dy_to_top_row (tree_view);
2966
2967 if (gtk_widget_get_realized (widget))
2968 {
2969 gdk_window_move_resize (gtk_widget_get_window (widget),
2970 allocation->x, allocation->y,
2971 allocation->width, allocation->height);
2972 gdk_window_move_resize (tree_view->priv->header_window,
2973 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2974 0,
2975 MAX (tree_view->priv->width, allocation->width),
2976 tree_view->priv->header_height);
2977 gdk_window_move_resize (tree_view->priv->bin_window,
2978 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2979 gtk_tree_view_get_effective_header_height (tree_view),
2980 MAX (tree_view->priv->width, allocation->width),
2981 allocation->height - gtk_tree_view_get_effective_header_height (tree_view));
2982
2983 if (tree_view->priv->tree == NULL)
2984 invalidate_empty_focus (tree_view);
2985
2986 if (width_changed && tree_view->priv->expander_column)
2987 {
2988 /* Might seem awkward, but is the best heuristic I could come up
2989 * with. Only if the width of the columns before the expander
2990 * changes, we will update the prelight status. It is this
2991 * width that makes the expander move vertically. Always updating
2992 * prelight status causes trouble with hover selections.
2993 */
2994 gint width_before_expander;
2995
2996 width_before_expander = gtk_tree_view_calculate_width_before_expander (tree_view);
2997
2998 if (tree_view->priv->prev_width_before_expander
2999 != width_before_expander)
3000 update_prelight (tree_view,
3001 tree_view->priv->event_last_x,
3002 tree_view->priv->event_last_y);
3003
3004 tree_view->priv->prev_width_before_expander = width_before_expander;
3005 }
3006 }
3007
3008 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
3009 {
3010 GtkTreeViewChild *child = tmp_list->data;
3011 GtkTreePath *path;
3012 GdkRectangle child_rect;
3013 int min_x, max_x, min_y, max_y;
3014 int size;
3015 GtkTextDirection direction;
3016
3017 direction = gtk_widget_get_direction (child->widget);
3018 path = _gtk_tree_path_new_from_rbtree (child->tree, child->node);
3019 gtk_tree_view_get_cell_area (tree_view, path, child->column, &child_rect);
3020 child_rect.x += child->border.left;
3021 child_rect.y += child->border.top;
3022 child_rect.width -= child->border.left + child->border.right;
3023 child_rect.height -= child->border.top + child->border.bottom;
3024
3025 gtk_widget_get_preferred_width (GTK_WIDGET (child->widget), &size, NULL);
3026
3027 if (size > child_rect.width)
3028 {
3029 /* Enlarge the child, extending it to the left (RTL) */
3030 if (direction == GTK_TEXT_DIR_RTL)
3031 child_rect.x -= (size - child_rect.width);
3032 /* or to the right (LTR) */
3033 else
3034 child_rect.x += 0;
3035
3036 child_rect.width = size;
3037 }
3038
3039 gtk_widget_get_preferred_height_for_width (GTK_WIDGET (child->widget),
3040 child_rect.width,
3041 &size, NULL);
3042 if (size > child_rect.height)
3043 {
3044 /* Enlarge the child, extending in both directions equally */
3045 child_rect.y -= (size - child_rect.height) / 2;
3046 child_rect.height = size;
3047 }
3048
3049 /* push the rect back in the visible area if needed,
3050 * preferring the top left corner (for RTL)
3051 * or top right corner (for LTR)
3052 */
3053 min_x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
3054 max_x = min_x + allocation->width - child_rect.width;
3055 min_y = 0;
3056 max_y = min_y + allocation->height - gtk_tree_view_get_effective_header_height (tree_view) - child_rect.height;
3057
3058 if (direction == GTK_TEXT_DIR_LTR)
3059 /* Ensure that child's right edge is not sticking to the right
3060 * (if (child_rect.x > max_x) child_rect.x = max_x),
3061 * then ensure that child's left edge is visible and is not sticking to the left
3062 * (if (child_rect.x < min_x) child_rect.x = min_x).
3063 */
3064 child_rect.x = MAX (min_x, MIN (max_x, child_rect.x));
3065 else
3066 /* Ensure that child's left edge is not sticking to the left
3067 * (if (child_rect.x < min_x) child_rect.x = min_x),
3068 * then ensure that child's right edge is visible and is not sticking to the right
3069 * (if (child_rect.x > max_x) child_rect.x = max_x).
3070 */
3071 child_rect.x = MIN (max_x, MAX (min_x, child_rect.x));
3072
3073 child_rect.y = MAX (min_y, MIN (max_y, child_rect.y));
3074
3075 gtk_tree_path_free (path);
3076 gtk_widget_size_allocate (child->widget, &child_rect);
3077 }
3078 }
3079
3080 /* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */
3081 static void
grab_focus_and_unset_draw_keyfocus(GtkTreeView * tree_view)3082 grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view)
3083 {
3084 GtkWidget *widget = GTK_WIDGET (tree_view);
3085
3086 if (gtk_widget_get_can_focus (widget) &&
3087 !gtk_widget_has_focus (widget) &&
3088 !_gtk_widget_get_shadowed (widget))
3089 gtk_widget_grab_focus (widget);
3090
3091 tree_view->priv->draw_keyfocus = 0;
3092 }
3093
3094 static inline gboolean
row_is_separator(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)3095 row_is_separator (GtkTreeView *tree_view,
3096 GtkTreeIter *iter,
3097 GtkTreePath *path)
3098 {
3099 gboolean is_separator = FALSE;
3100
3101 if (tree_view->priv->row_separator_func)
3102 {
3103 GtkTreeIter tmpiter;
3104
3105 if (iter)
3106 tmpiter = *iter;
3107 else
3108 {
3109 if (!gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, path))
3110 return FALSE;
3111 }
3112
3113 is_separator = tree_view->priv->row_separator_func (tree_view->priv->model,
3114 &tmpiter,
3115 tree_view->priv->row_separator_data);
3116 }
3117
3118 return is_separator;
3119 }
3120
3121 static int
gtk_tree_view_get_expander_size(GtkTreeView * tree_view)3122 gtk_tree_view_get_expander_size (GtkTreeView *tree_view)
3123 {
3124 gint expander_size;
3125 gint horizontal_separator;
3126
3127 gtk_widget_style_get (GTK_WIDGET (tree_view),
3128 "expander-size", &expander_size,
3129 "horizontal-separator", &horizontal_separator,
3130 NULL);
3131
3132 return expander_size + (horizontal_separator / 2);
3133 }
3134
3135 static void
get_current_selection_modifiers(GtkWidget * widget,gboolean * modify,gboolean * extend)3136 get_current_selection_modifiers (GtkWidget *widget,
3137 gboolean *modify,
3138 gboolean *extend)
3139 {
3140 GdkModifierType state = 0;
3141 GdkModifierType mask;
3142
3143 *modify = FALSE;
3144 *extend = FALSE;
3145
3146 if (gtk_get_current_event_state (&state))
3147 {
3148 mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
3149 if ((state & mask) == mask)
3150 *modify = TRUE;
3151 mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
3152 if ((state & mask) == mask)
3153 *extend = TRUE;
3154 }
3155 }
3156
3157 static void
gtk_tree_view_multipress_gesture_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,GtkTreeView * tree_view)3158 gtk_tree_view_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
3159 gint n_press,
3160 gdouble x,
3161 gdouble y,
3162 GtkTreeView *tree_view)
3163 {
3164 gint vertical_separator, horizontal_separator;
3165 GtkWidget *widget = GTK_WIDGET (tree_view);
3166 GdkRectangle background_area, cell_area;
3167 GtkTreeViewColumn *column = NULL;
3168 GdkEventSequence *sequence;
3169 GdkModifierType modifiers;
3170 const GdkEvent *event;
3171 gint new_y, y_offset;
3172 gint bin_x, bin_y;
3173 GtkTreePath *path;
3174 GtkRBNode *node;
3175 GtkRBTree *tree;
3176 gint depth;
3177 guint button;
3178 GList *list;
3179 gboolean rtl;
3180
3181 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3182 gtk_tree_view_stop_editing (tree_view, FALSE);
3183 gtk_widget_style_get (widget,
3184 "vertical-separator", &vertical_separator,
3185 "horizontal-separator", &horizontal_separator,
3186 NULL);
3187 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
3188
3189 if (button > 3)
3190 {
3191 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
3192 return;
3193 }
3194
3195 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
3196 * we're done handling the button press.
3197 */
3198 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y,
3199 &bin_x, &bin_y);
3200 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
3201
3202 if (n_press > 1)
3203 gtk_gesture_set_state (tree_view->priv->drag_gesture,
3204 GTK_EVENT_SEQUENCE_DENIED);
3205
3206 /* Empty tree? */
3207 if (tree_view->priv->tree == NULL)
3208 {
3209 grab_focus_and_unset_draw_keyfocus (tree_view);
3210 return;
3211 }
3212
3213 /* are we in an arrow? */
3214 if (tree_view->priv->prelight_node &&
3215 tree_view->priv->arrow_prelit &&
3216 gtk_tree_view_draw_expanders (tree_view))
3217 {
3218 if (button == GDK_BUTTON_PRIMARY)
3219 {
3220 tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
3221 tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
3222 gtk_tree_view_queue_draw_arrow (tree_view,
3223 tree_view->priv->prelight_tree,
3224 tree_view->priv->prelight_node);
3225 }
3226
3227 grab_focus_and_unset_draw_keyfocus (tree_view);
3228 return;
3229 }
3230
3231 /* find the node that was clicked */
3232 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, bin_y);
3233 if (new_y < 0)
3234 new_y = 0;
3235 y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
3236
3237 if (node == NULL)
3238 {
3239 /* We clicked in dead space */
3240 grab_focus_and_unset_draw_keyfocus (tree_view);
3241 return;
3242 }
3243
3244 /* Get the path and the node */
3245 path = _gtk_tree_path_new_from_rbtree (tree, node);
3246
3247 if (row_is_separator (tree_view, NULL, path))
3248 {
3249 gtk_tree_path_free (path);
3250 grab_focus_and_unset_draw_keyfocus (tree_view);
3251 return;
3252 }
3253
3254 depth = gtk_tree_path_get_depth (path);
3255 background_area.y = y_offset + bin_y;
3256 background_area.height = gtk_tree_view_get_row_height (tree_view, node);
3257 background_area.x = 0;
3258
3259 /* Let the column have a chance at selecting it. */
3260 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3261 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3262 list; list = (rtl ? list->prev : list->next))
3263 {
3264 GtkTreeViewColumn *candidate = list->data;
3265
3266 if (!gtk_tree_view_column_get_visible (candidate))
3267 continue;
3268
3269 background_area.width = gtk_tree_view_column_get_width (candidate);
3270 if ((background_area.x > bin_x) ||
3271 (background_area.x + background_area.width <= bin_x))
3272 {
3273 background_area.x += background_area.width;
3274 continue;
3275 }
3276
3277 /* we found the focus column */
3278 column = candidate;
3279 cell_area = background_area;
3280 cell_area.width -= horizontal_separator;
3281 cell_area.height -= vertical_separator;
3282 cell_area.x += horizontal_separator/2;
3283 cell_area.y += vertical_separator/2;
3284 if (gtk_tree_view_is_expander_column (tree_view, column))
3285 {
3286 if (!rtl)
3287 cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
3288 cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
3289
3290 if (gtk_tree_view_draw_expanders (tree_view))
3291 {
3292 gint expander_size = gtk_tree_view_get_expander_size (tree_view);
3293 if (!rtl)
3294 cell_area.x += depth * expander_size;
3295 cell_area.width -= depth * expander_size;
3296 }
3297 }
3298 break;
3299 }
3300
3301 if (column == NULL)
3302 {
3303 gtk_tree_path_free (path);
3304 grab_focus_and_unset_draw_keyfocus (tree_view);
3305 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
3306 return;
3307 }
3308
3309 _gtk_tree_view_set_focus_column (tree_view, column);
3310
3311 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
3312 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
3313 gdk_event_get_state (event, &modifiers);
3314
3315 /* decide if we edit */
3316 if (button == GDK_BUTTON_PRIMARY &&
3317 !(modifiers & gtk_accelerator_get_default_mod_mask ()))
3318 {
3319 GtkTreePath *anchor;
3320 GtkTreeIter iter;
3321
3322 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
3323 gtk_tree_view_column_cell_set_cell_data (column,
3324 tree_view->priv->model,
3325 &iter,
3326 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
3327 node->children?TRUE:FALSE);
3328
3329 if (tree_view->priv->anchor)
3330 anchor = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
3331 else
3332 anchor = NULL;
3333
3334 if ((anchor && !gtk_tree_path_compare (anchor, path))
3335 || !_gtk_tree_view_column_has_editable_cell (column))
3336 {
3337 GtkCellEditable *cell_editable = NULL;
3338
3339 /* FIXME: get the right flags */
3340 guint flags = 0;
3341
3342 if (_gtk_tree_view_column_cell_event (column,
3343 (GdkEvent *)event,
3344 &cell_area, flags))
3345 {
3346 GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
3347 cell_editable = gtk_cell_area_get_edit_widget (area);
3348
3349 if (cell_editable != NULL)
3350 {
3351 gtk_tree_path_free (path);
3352 gtk_tree_path_free (anchor);
3353 return;
3354 }
3355 }
3356 }
3357 if (anchor)
3358 gtk_tree_path_free (anchor);
3359 }
3360
3361 /* we only handle selection modifications on the first button press
3362 */
3363 if (n_press == 1)
3364 {
3365 GtkCellRenderer *focus_cell;
3366 gboolean modify, extend;
3367
3368 get_current_selection_modifiers (widget, &modify, &extend);
3369 tree_view->priv->modify_selection_pressed = modify;
3370 tree_view->priv->extend_selection_pressed = extend;
3371
3372 /* We update the focus cell here, this is also needed if the
3373 * column does not contain an editable cell. In this case,
3374 * GtkCellArea did not receive the event for processing (and
3375 * could not update the focus cell).
3376 */
3377 focus_cell = _gtk_tree_view_column_get_cell_at_pos (column,
3378 &cell_area,
3379 &background_area,
3380 bin_x, bin_y);
3381
3382 if (focus_cell)
3383 gtk_tree_view_column_focus_cell (column, focus_cell);
3384
3385 if (modify)
3386 {
3387 gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
3388 gtk_tree_view_real_toggle_cursor_row (tree_view);
3389 }
3390 else if (extend)
3391 {
3392 gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
3393 gtk_tree_view_real_select_cursor_row (tree_view, FALSE);
3394 }
3395 else
3396 {
3397 gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE);
3398 }
3399
3400 tree_view->priv->modify_selection_pressed = FALSE;
3401 tree_view->priv->extend_selection_pressed = FALSE;
3402 }
3403
3404 if (button == GDK_BUTTON_PRIMARY && n_press == 2)
3405 gtk_tree_view_row_activated (tree_view, path, column);
3406 else
3407 {
3408 if (n_press == 1)
3409 {
3410 tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
3411 tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
3412 }
3413
3414 grab_focus_and_unset_draw_keyfocus (tree_view);
3415 }
3416
3417 gtk_tree_path_free (path);
3418
3419 if (n_press >= 2)
3420 gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
3421 }
3422
3423 static void
gtk_tree_view_drag_gesture_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,GtkTreeView * tree_view)3424 gtk_tree_view_drag_gesture_begin (GtkGestureDrag *gesture,
3425 gdouble start_x,
3426 gdouble start_y,
3427 GtkTreeView *tree_view)
3428 {
3429 gint bin_x, bin_y;
3430 GtkRBTree *tree;
3431 GtkRBNode *node;
3432
3433 if (tree_view->priv->tree == NULL)
3434 {
3435 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
3436 return;
3437 }
3438
3439 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
3440 &bin_x, &bin_y);
3441 tree_view->priv->press_start_x = tree_view->priv->rubber_band_x = bin_x;
3442 tree_view->priv->press_start_y = tree_view->priv->rubber_band_y = bin_y;
3443 _gtk_rbtree_find_offset (tree_view->priv->tree, bin_y + tree_view->priv->dy,
3444 &tree, &node);
3445
3446 if (tree_view->priv->rubber_banding_enable
3447 && !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)
3448 && gtk_tree_selection_get_mode (tree_view->priv->selection) == GTK_SELECTION_MULTIPLE)
3449 {
3450 gboolean modify, extend;
3451
3452 tree_view->priv->press_start_y += tree_view->priv->dy;
3453 tree_view->priv->rubber_band_y += tree_view->priv->dy;
3454 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
3455
3456 get_current_selection_modifiers (GTK_WIDGET (tree_view), &modify, &extend);
3457 tree_view->priv->rubber_band_modify = modify;
3458 tree_view->priv->rubber_band_extend = extend;
3459 }
3460 }
3461
3462 static void
gtk_tree_view_column_multipress_gesture_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,GtkTreeView * tree_view)3463 gtk_tree_view_column_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
3464 gint n_press,
3465 gdouble x,
3466 gdouble y,
3467 GtkTreeView *tree_view)
3468 {
3469 GdkEventSequence *sequence;
3470 GtkTreeViewColumn *column;
3471 const GdkEvent *event;
3472 GList *list;
3473 gint i;
3474
3475 if (n_press != 2)
3476 return;
3477
3478 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
3479 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
3480
3481 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
3482 {
3483 column = list->data;
3484
3485 if (event->any.window != _gtk_tree_view_column_get_window (column) ||
3486 !gtk_tree_view_column_get_resizable (column))
3487 continue;
3488
3489 if (gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE)
3490 {
3491 gtk_tree_view_column_set_fixed_width (column, -1);
3492 gtk_tree_view_column_set_expand (column, FALSE);
3493 _gtk_tree_view_column_autosize (tree_view, column);
3494 }
3495
3496 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
3497 break;
3498 }
3499 }
3500
3501 static void
gtk_tree_view_column_drag_gesture_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,GtkTreeView * tree_view)3502 gtk_tree_view_column_drag_gesture_begin (GtkGestureDrag *gesture,
3503 gdouble start_x,
3504 gdouble start_y,
3505 GtkTreeView *tree_view)
3506 {
3507 GdkEventSequence *sequence;
3508 GtkTreeViewColumn *column;
3509 const GdkEvent *event;
3510 GdkWindow *window;
3511 gboolean rtl;
3512 GList *list;
3513 gint i;
3514
3515 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
3516 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
3517 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
3518 window = event->any.window;
3519
3520 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
3521 {
3522 gpointer drag_data;
3523 gint column_width;
3524
3525 column = list->data;
3526
3527 if (window != _gtk_tree_view_column_get_window (column))
3528 continue;
3529
3530 if (!gtk_tree_view_column_get_resizable (column))
3531 break;
3532
3533 tree_view->priv->in_column_resize = TRUE;
3534
3535 /* block attached dnd signal handler */
3536 drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data");
3537 if (drag_data)
3538 g_signal_handlers_block_matched (tree_view,
3539 G_SIGNAL_MATCH_DATA,
3540 0, 0, NULL, NULL,
3541 drag_data);
3542
3543 column_width = gtk_tree_view_column_get_width (column);
3544 gtk_tree_view_column_set_fixed_width (column, column_width);
3545 gtk_tree_view_column_set_expand (column, FALSE);
3546
3547 tree_view->priv->drag_pos = i;
3548 tree_view->priv->x_drag = start_x + (rtl ? column_width : -column_width);
3549
3550 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3551 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
3552
3553 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
3554 return;
3555 }
3556 }
3557
3558 static void
gtk_tree_view_update_button_position(GtkTreeView * tree_view,GtkTreeViewColumn * column)3559 gtk_tree_view_update_button_position (GtkTreeView *tree_view,
3560 GtkTreeViewColumn *column)
3561 {
3562 GtkTreeViewPrivate *priv = tree_view->priv;
3563 GList *column_el;
3564
3565 column_el = g_list_find (priv->columns, column);
3566 g_return_if_fail (column_el != NULL);
3567
3568 gtk_css_node_insert_after (priv->header_node,
3569 gtk_widget_get_css_node (gtk_tree_view_column_get_button (column)),
3570 column_el->prev ? gtk_widget_get_css_node (
3571 gtk_tree_view_column_get_button (column_el->prev->data)) : NULL);
3572 }
3573
3574 /* column drag gesture helper */
3575 static gboolean
gtk_tree_view_button_release_drag_column(GtkTreeView * tree_view)3576 gtk_tree_view_button_release_drag_column (GtkTreeView *tree_view)
3577 {
3578 GtkWidget *button, *widget = GTK_WIDGET (tree_view);
3579 GList *l;
3580 gboolean rtl;
3581 GtkStyleContext *context;
3582
3583 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3584
3585 /* Move the button back */
3586 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
3587
3588 context = gtk_widget_get_style_context (button);
3589 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_DND);
3590
3591 g_object_ref (button);
3592 gtk_container_remove (GTK_CONTAINER (tree_view), button);
3593 gtk_widget_set_parent_window (button, tree_view->priv->header_window);
3594 gtk_tree_view_update_button_position (tree_view, tree_view->priv->drag_column);
3595 gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
3596 g_object_unref (button);
3597 gtk_widget_queue_resize (widget);
3598 if (gtk_tree_view_column_get_resizable (tree_view->priv->drag_column))
3599 {
3600 gdk_window_raise (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
3601 gdk_window_show (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
3602 }
3603 else
3604 gdk_window_hide (_gtk_tree_view_column_get_window (tree_view->priv->drag_column));
3605
3606 gtk_widget_grab_focus (button);
3607
3608 if (rtl)
3609 {
3610 if (tree_view->priv->cur_reorder &&
3611 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
3612 gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column,
3613 tree_view->priv->cur_reorder->right_column);
3614 }
3615 else
3616 {
3617 if (tree_view->priv->cur_reorder &&
3618 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
3619 gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column,
3620 tree_view->priv->cur_reorder->left_column);
3621 }
3622 tree_view->priv->drag_column = NULL;
3623 gtk_widget_unregister_window (widget, tree_view->priv->drag_window);
3624 gdk_window_destroy (tree_view->priv->drag_window);
3625 tree_view->priv->drag_window = NULL;
3626
3627 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
3628 g_slice_free (GtkTreeViewColumnReorder, l->data);
3629 g_list_free (tree_view->priv->column_drag_info);
3630 tree_view->priv->column_drag_info = NULL;
3631 tree_view->priv->cur_reorder = NULL;
3632
3633 if (tree_view->priv->drag_highlight_window)
3634 gdk_window_hide (tree_view->priv->drag_highlight_window);
3635
3636 /* Reset our flags */
3637 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
3638 tree_view->priv->in_column_drag = FALSE;
3639
3640 return TRUE;
3641 }
3642
3643 /* column drag gesture helper */
3644 static gboolean
gtk_tree_view_button_release_column_resize(GtkTreeView * tree_view)3645 gtk_tree_view_button_release_column_resize (GtkTreeView *tree_view)
3646 {
3647 gpointer drag_data;
3648
3649 tree_view->priv->drag_pos = -1;
3650
3651 /* unblock attached dnd signal handler */
3652 drag_data = g_object_get_data (G_OBJECT (tree_view), "gtk-site-data");
3653 if (drag_data)
3654 g_signal_handlers_unblock_matched (tree_view,
3655 G_SIGNAL_MATCH_DATA,
3656 0, 0, NULL, NULL,
3657 drag_data);
3658
3659 tree_view->priv->in_column_resize = FALSE;
3660 return TRUE;
3661 }
3662
3663 static void
gtk_tree_view_column_drag_gesture_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkTreeView * tree_view)3664 gtk_tree_view_column_drag_gesture_end (GtkGestureDrag *gesture,
3665 gdouble offset_x,
3666 gdouble offset_y,
3667 GtkTreeView *tree_view)
3668 {
3669 GdkEventSequence *sequence;
3670
3671 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
3672
3673 /* Cancel reorder if the drag got cancelled */
3674 if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
3675 tree_view->priv->cur_reorder = NULL;
3676
3677 if (tree_view->priv->in_column_drag)
3678 {
3679 GdkDevice *device;
3680
3681 gtk_tree_view_button_release_drag_column (tree_view);
3682 device = gtk_gesture_get_device (GTK_GESTURE (gesture));
3683 gdk_seat_ungrab (gdk_device_get_seat (device));
3684 }
3685 else if (tree_view->priv->in_column_resize)
3686 gtk_tree_view_button_release_column_resize (tree_view);
3687 }
3688
3689 static void
gtk_tree_view_drag_gesture_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkTreeView * tree_view)3690 gtk_tree_view_drag_gesture_end (GtkGestureDrag *gesture,
3691 gdouble offset_x,
3692 gdouble offset_y,
3693 GtkTreeView *tree_view)
3694 {
3695 gtk_tree_view_stop_rubber_band (tree_view);
3696 }
3697
3698 static void
gtk_tree_view_multipress_gesture_released(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,GtkTreeView * tree_view)3699 gtk_tree_view_multipress_gesture_released (GtkGestureMultiPress *gesture,
3700 gint n_press,
3701 gdouble x,
3702 gdouble y,
3703 GtkTreeView *tree_view)
3704 {
3705 gboolean modify, extend;
3706 guint button;
3707
3708 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
3709
3710 if (button != GDK_BUTTON_PRIMARY ||
3711 tree_view->priv->button_pressed_node == NULL ||
3712 tree_view->priv->button_pressed_node != tree_view->priv->prelight_node)
3713 return;
3714
3715 get_current_selection_modifiers (GTK_WIDGET (tree_view), &modify, &extend);
3716
3717 if (tree_view->priv->arrow_prelit)
3718 {
3719 GtkTreePath *path = NULL;
3720
3721 path = _gtk_tree_path_new_from_rbtree (tree_view->priv->button_pressed_tree,
3722 tree_view->priv->button_pressed_node);
3723 /* Actually activate the node */
3724 if (tree_view->priv->button_pressed_node->children == NULL)
3725 gtk_tree_view_real_expand_row (tree_view, path,
3726 tree_view->priv->button_pressed_tree,
3727 tree_view->priv->button_pressed_node,
3728 FALSE, TRUE);
3729 else
3730 gtk_tree_view_real_collapse_row (tree_view, path,
3731 tree_view->priv->button_pressed_tree,
3732 tree_view->priv->button_pressed_node, TRUE);
3733 gtk_tree_path_free (path);
3734 }
3735 else if (tree_view->priv->activate_on_single_click && !modify && !extend)
3736 {
3737 GtkTreePath *path = NULL;
3738
3739 path = _gtk_tree_path_new_from_rbtree (tree_view->priv->button_pressed_tree,
3740 tree_view->priv->button_pressed_node);
3741 gtk_tree_view_row_activated (tree_view, path, tree_view->priv->focus_column);
3742 gtk_tree_path_free (path);
3743 }
3744
3745 tree_view->priv->button_pressed_tree = NULL;
3746 tree_view->priv->button_pressed_node = NULL;
3747 }
3748
3749 /* GtkWidget::motion_event function set.
3750 */
3751
3752 static gboolean
coords_are_over_arrow(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)3753 coords_are_over_arrow (GtkTreeView *tree_view,
3754 GtkRBTree *tree,
3755 GtkRBNode *node,
3756 /* these are in bin window coords */
3757 gint x,
3758 gint y)
3759 {
3760 GdkRectangle arrow;
3761 gint x2;
3762
3763 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
3764 return FALSE;
3765
3766 if ((node->flags & GTK_RBNODE_IS_PARENT) == 0)
3767 return FALSE;
3768
3769 arrow.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
3770 arrow.height = gtk_tree_view_get_row_height (tree_view, node);
3771
3772 gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2);
3773
3774 arrow.width = x2 - arrow.x;
3775
3776 return (x >= arrow.x &&
3777 x < (arrow.x + arrow.width) &&
3778 y >= arrow.y &&
3779 y < (arrow.y + arrow.height));
3780 }
3781
3782 static gboolean
auto_expand_timeout(gpointer data)3783 auto_expand_timeout (gpointer data)
3784 {
3785 GtkTreeView *tree_view = GTK_TREE_VIEW (data);
3786 GtkTreePath *path;
3787
3788 if (tree_view->priv->prelight_node)
3789 {
3790 path = _gtk_tree_path_new_from_rbtree (tree_view->priv->prelight_tree,
3791 tree_view->priv->prelight_node);
3792
3793 if (tree_view->priv->prelight_node->children)
3794 gtk_tree_view_collapse_row (tree_view, path);
3795 else
3796 gtk_tree_view_expand_row (tree_view, path, FALSE);
3797
3798 gtk_tree_path_free (path);
3799 }
3800
3801 tree_view->priv->auto_expand_timeout = 0;
3802
3803 return FALSE;
3804 }
3805
3806 static void
remove_auto_expand_timeout(GtkTreeView * tree_view)3807 remove_auto_expand_timeout (GtkTreeView *tree_view)
3808 {
3809 if (tree_view->priv->auto_expand_timeout != 0)
3810 {
3811 g_source_remove (tree_view->priv->auto_expand_timeout);
3812 tree_view->priv->auto_expand_timeout = 0;
3813 }
3814 }
3815
3816 static void
do_prelight(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)3817 do_prelight (GtkTreeView *tree_view,
3818 GtkRBTree *tree,
3819 GtkRBNode *node,
3820 /* these are in bin_window coords */
3821 gint x,
3822 gint y)
3823 {
3824 if (tree_view->priv->prelight_tree == tree &&
3825 tree_view->priv->prelight_node == node)
3826 {
3827 /* We are still on the same node,
3828 but we might need to take care of the arrow */
3829
3830 if (tree && node && gtk_tree_view_draw_expanders (tree_view))
3831 {
3832 gboolean over_arrow;
3833
3834 over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
3835
3836 if (over_arrow != tree_view->priv->arrow_prelit)
3837 {
3838 if (over_arrow)
3839 tree_view->priv->arrow_prelit = TRUE;
3840 else
3841 tree_view->priv->arrow_prelit = FALSE;
3842
3843 gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
3844 }
3845 }
3846
3847 return;
3848 }
3849
3850 if (tree_view->priv->prelight_tree && tree_view->priv->prelight_node)
3851 {
3852 /* Unprelight the old node and arrow */
3853
3854 GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node,
3855 GTK_RBNODE_IS_PRELIT);
3856
3857 if (tree_view->priv->arrow_prelit
3858 && gtk_tree_view_draw_expanders (tree_view))
3859 {
3860 tree_view->priv->arrow_prelit = FALSE;
3861
3862 gtk_tree_view_queue_draw_arrow (tree_view,
3863 tree_view->priv->prelight_tree,
3864 tree_view->priv->prelight_node);
3865 }
3866
3867 _gtk_tree_view_queue_draw_node (tree_view,
3868 tree_view->priv->prelight_tree,
3869 tree_view->priv->prelight_node,
3870 NULL);
3871 }
3872
3873
3874 if (tree_view->priv->hover_expand)
3875 remove_auto_expand_timeout (tree_view);
3876
3877 /* Set the new prelight values */
3878 tree_view->priv->prelight_node = node;
3879 tree_view->priv->prelight_tree = tree;
3880
3881 if (!node || !tree)
3882 return;
3883
3884 /* Prelight the new node and arrow */
3885
3886 if (gtk_tree_view_draw_expanders (tree_view)
3887 && coords_are_over_arrow (tree_view, tree, node, x, y))
3888 {
3889 tree_view->priv->arrow_prelit = TRUE;
3890
3891 gtk_tree_view_queue_draw_arrow (tree_view, tree, node);
3892 }
3893
3894 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
3895
3896 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
3897
3898 if (tree_view->priv->hover_expand)
3899 {
3900 tree_view->priv->auto_expand_timeout =
3901 gdk_threads_add_timeout (AUTO_EXPAND_TIMEOUT, auto_expand_timeout, tree_view);
3902 g_source_set_name_by_id (tree_view->priv->auto_expand_timeout, "[gtk+] auto_expand_timeout");
3903 }
3904 }
3905
3906 static void
prelight_or_select(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)3907 prelight_or_select (GtkTreeView *tree_view,
3908 GtkRBTree *tree,
3909 GtkRBNode *node,
3910 /* these are in bin_window coords */
3911 gint x,
3912 gint y)
3913 {
3914 GtkSelectionMode mode = gtk_tree_selection_get_mode (tree_view->priv->selection);
3915
3916 if (tree_view->priv->hover_selection &&
3917 (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) &&
3918 !(tree_view->priv->edited_column &&
3919 gtk_cell_area_get_edit_widget
3920 (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column)))))
3921 {
3922 if (node)
3923 {
3924 if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
3925 {
3926 GtkTreePath *path;
3927
3928 path = _gtk_tree_path_new_from_rbtree (tree, node);
3929 gtk_tree_selection_select_path (tree_view->priv->selection, path);
3930 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
3931 {
3932 tree_view->priv->draw_keyfocus = FALSE;
3933 gtk_tree_view_real_set_cursor (tree_view, path, 0);
3934 }
3935 gtk_tree_path_free (path);
3936 }
3937 }
3938
3939 else if (mode == GTK_SELECTION_SINGLE)
3940 gtk_tree_selection_unselect_all (tree_view->priv->selection);
3941 }
3942
3943 do_prelight (tree_view, tree, node, x, y);
3944 }
3945
3946 static void
ensure_unprelighted(GtkTreeView * tree_view)3947 ensure_unprelighted (GtkTreeView *tree_view)
3948 {
3949 do_prelight (tree_view,
3950 NULL, NULL,
3951 -1000, -1000); /* coords not possibly over an arrow */
3952
3953 g_assert (tree_view->priv->prelight_node == NULL);
3954 }
3955
3956 static void
update_prelight(GtkTreeView * tree_view,gint x,gint y)3957 update_prelight (GtkTreeView *tree_view,
3958 gint x,
3959 gint y)
3960 {
3961 int new_y;
3962 GtkRBTree *tree;
3963 GtkRBNode *node;
3964
3965 if (tree_view->priv->tree == NULL)
3966 return;
3967
3968 if (x == -10000)
3969 {
3970 ensure_unprelighted (tree_view);
3971 return;
3972 }
3973
3974 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
3975 if (new_y < 0)
3976 new_y = 0;
3977
3978 _gtk_rbtree_find_offset (tree_view->priv->tree,
3979 new_y, &tree, &node);
3980
3981 if (node)
3982 prelight_or_select (tree_view, tree, node, x, y);
3983 }
3984
3985
3986
3987
3988 /* Our motion arrow is either a box (in the case of the original spot)
3989 * or an arrow. It is expander_size wide.
3990 */
3991 /*
3992 * 11111111111111
3993 * 01111111111110
3994 * 00111111111100
3995 * 00011111111000
3996 * 00001111110000
3997 * 00000111100000
3998 * 00000111100000
3999 * 00000111100000
4000 * ~ ~ ~ ~ ~ ~ ~
4001 * 00000111100000
4002 * 00000111100000
4003 * 00000111100000
4004 * 00001111110000
4005 * 00011111111000
4006 * 00111111111100
4007 * 01111111111110
4008 * 11111111111111
4009 */
4010
4011 static void
gtk_tree_view_motion_draw_column_motion_arrow(GtkTreeView * tree_view)4012 gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
4013 {
4014 GtkTreeViewColumnReorder *reorder = tree_view->priv->cur_reorder;
4015 GtkWidget *widget = GTK_WIDGET (tree_view);
4016 cairo_surface_t *mask_image;
4017 cairo_region_t *mask_region;
4018 gint x;
4019 gint y;
4020 gint width;
4021 gint height;
4022 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
4023 GdkWindowAttr attributes;
4024 guint attributes_mask;
4025 cairo_t *cr;
4026
4027 if (!reorder ||
4028 reorder->left_column == tree_view->priv->drag_column ||
4029 reorder->right_column == tree_view->priv->drag_column)
4030 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
4031 else if (reorder->left_column || reorder->right_column)
4032 {
4033 GtkAllocation left_allocation, right_allocation;
4034 GdkRectangle visible_rect;
4035 GtkWidget *button;
4036
4037 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
4038 if (reorder->left_column)
4039 {
4040 button = gtk_tree_view_column_get_button (reorder->left_column);
4041 gtk_widget_get_allocation (button, &left_allocation);
4042 x = left_allocation.x + left_allocation.width;
4043 }
4044 else
4045 {
4046 button = gtk_tree_view_column_get_button (reorder->right_column);
4047 gtk_widget_get_allocation (button, &right_allocation);
4048 x = right_allocation.x;
4049 }
4050
4051 if (x < visible_rect.x)
4052 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
4053 else if (x > visible_rect.x + visible_rect.width)
4054 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
4055 else
4056 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
4057 }
4058
4059 /* We want to draw the rectangle over the initial location. */
4060 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
4061 {
4062 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
4063 {
4064 GtkAllocation drag_allocation;
4065 GtkWidget *button;
4066
4067 if (tree_view->priv->drag_highlight_window)
4068 {
4069 gtk_widget_unregister_window (GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
4070 gdk_window_destroy (tree_view->priv->drag_highlight_window);
4071 }
4072
4073 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
4074 attributes.window_type = GDK_WINDOW_CHILD;
4075 attributes.wclass = GDK_INPUT_OUTPUT;
4076 attributes.x = tree_view->priv->drag_column_x;
4077 attributes.y = 0;
4078 gtk_widget_get_allocation (button, &drag_allocation);
4079 width = attributes.width = drag_allocation.width;
4080 height = attributes.height = drag_allocation.height;
4081 attributes.visual = gdk_screen_get_rgba_visual (gtk_widget_get_screen (widget));
4082 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_POINTER_MOTION_MASK;
4083 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4084 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
4085 gtk_widget_register_window (GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
4086
4087 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
4088 }
4089 }
4090 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
4091 {
4092 GtkAllocation button_allocation;
4093 GtkWidget *button;
4094
4095 width = gtk_tree_view_get_expander_size (tree_view);
4096
4097 /* Get x, y, width, height of arrow */
4098 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
4099 if (reorder->left_column)
4100 {
4101 button = gtk_tree_view_column_get_button (reorder->left_column);
4102 gtk_widget_get_allocation (button, &button_allocation);
4103 x += button_allocation.x + button_allocation.width - width/2;
4104 height = button_allocation.height;
4105 }
4106 else
4107 {
4108 button = gtk_tree_view_column_get_button (reorder->right_column);
4109 gtk_widget_get_allocation (button, &button_allocation);
4110 x += button_allocation.x - width/2;
4111 height = button_allocation.height;
4112 }
4113 y -= width/2; /* The arrow takes up only half the space */
4114 height += width;
4115
4116 /* Create the new window */
4117 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
4118 {
4119 if (tree_view->priv->drag_highlight_window)
4120 {
4121 gtk_widget_unregister_window (GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
4122 gdk_window_destroy (tree_view->priv->drag_highlight_window);
4123 }
4124
4125 attributes.window_type = GDK_WINDOW_TEMP;
4126 attributes.wclass = GDK_INPUT_OUTPUT;
4127 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
4128 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_POINTER_MOTION_MASK;
4129 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4130 attributes.x = x;
4131 attributes.y = y;
4132 attributes.width = width;
4133 attributes.height = height;
4134 tree_view->priv->drag_highlight_window = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (widget)),
4135 &attributes, attributes_mask);
4136 gtk_widget_register_window (GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
4137
4138 mask_image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
4139
4140 cr = cairo_create (mask_image);
4141 cairo_move_to (cr, 0, 0);
4142 cairo_line_to (cr, width, 0);
4143 cairo_line_to (cr, width / 2., width / 2);
4144 cairo_move_to (cr, 0, height);
4145 cairo_line_to (cr, width, height);
4146 cairo_line_to (cr, width / 2., height - width / 2.);
4147 cairo_fill (cr);
4148 cairo_destroy (cr);
4149
4150 mask_region = gdk_cairo_region_create_from_surface (mask_image);
4151 gdk_window_shape_combine_region (tree_view->priv->drag_highlight_window,
4152 mask_region, 0, 0);
4153
4154 cairo_region_destroy (mask_region);
4155 cairo_surface_destroy (mask_image);
4156 }
4157
4158 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
4159 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
4160 }
4161 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
4162 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
4163 {
4164 GtkAllocation allocation;
4165 GtkWidget *button;
4166 gint expander_size;
4167
4168 expander_size = gtk_tree_view_get_expander_size (tree_view);
4169
4170 /* Get x, y, width, height of arrow */
4171 width = expander_size/2; /* remember, the arrow only takes half the available width */
4172 gdk_window_get_origin (gtk_widget_get_window (widget),
4173 &x, &y);
4174 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
4175 {
4176 gtk_widget_get_allocation (widget, &allocation);
4177 x += allocation.width - width;
4178 }
4179
4180 if (reorder->left_column)
4181 {
4182 button = gtk_tree_view_column_get_button (reorder->left_column);
4183 gtk_widget_get_allocation (button, &allocation);
4184 height = allocation.height;
4185 }
4186 else
4187 {
4188 button = gtk_tree_view_column_get_button (reorder->right_column);
4189 gtk_widget_get_allocation (button, &allocation);
4190 height = allocation.height;
4191 }
4192
4193 y -= expander_size;
4194 height += 2 * expander_size;
4195
4196 /* Create the new window */
4197 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
4198 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
4199 {
4200 if (tree_view->priv->drag_highlight_window)
4201 {
4202 gtk_widget_unregister_window (GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
4203 gdk_window_destroy (tree_view->priv->drag_highlight_window);
4204 }
4205
4206 attributes.window_type = GDK_WINDOW_TEMP;
4207 attributes.wclass = GDK_INPUT_OUTPUT;
4208 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
4209 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_POINTER_MOTION_MASK;
4210 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4211 attributes.x = x;
4212 attributes.y = y;
4213 attributes.width = width;
4214 attributes.height = height;
4215 tree_view->priv->drag_highlight_window = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (widget)), &attributes, attributes_mask);
4216 gtk_widget_register_window (GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
4217
4218 mask_image = cairo_image_surface_create (CAIRO_FORMAT_A1, width, height);
4219
4220 cr = cairo_create (mask_image);
4221 /* mirror if we're on the left */
4222 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
4223 {
4224 cairo_translate (cr, width, 0);
4225 cairo_scale (cr, -1, 1);
4226 }
4227 cairo_move_to (cr, 0, 0);
4228 cairo_line_to (cr, width, width);
4229 cairo_line_to (cr, 0, expander_size);
4230 cairo_move_to (cr, 0, height);
4231 cairo_line_to (cr, width, height - width);
4232 cairo_line_to (cr, 0, height - expander_size);
4233 cairo_fill (cr);
4234 cairo_destroy (cr);
4235
4236 mask_region = gdk_cairo_region_create_from_surface (mask_image);
4237 gdk_window_shape_combine_region (tree_view->priv->drag_highlight_window,
4238 mask_region, 0, 0);
4239
4240 cairo_region_destroy (mask_region);
4241 cairo_surface_destroy (mask_image);
4242 }
4243
4244 tree_view->priv->drag_column_window_state = arrow_type;
4245 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
4246 }
4247 else
4248 {
4249 g_warning (G_STRLOC"Invalid GtkTreeViewColumnReorder struct");
4250 gdk_window_hide (tree_view->priv->drag_highlight_window);
4251 return;
4252 }
4253
4254 gdk_window_show (tree_view->priv->drag_highlight_window);
4255 gdk_window_raise (tree_view->priv->drag_highlight_window);
4256 }
4257
4258 static gboolean
gtk_tree_view_motion_resize_column(GtkTreeView * tree_view,gdouble x,gdouble y)4259 gtk_tree_view_motion_resize_column (GtkTreeView *tree_view,
4260 gdouble x,
4261 gdouble y)
4262 {
4263 gint new_width;
4264 GtkTreeViewColumn *column;
4265
4266 column = gtk_tree_view_get_column (tree_view, tree_view->priv->drag_pos);
4267
4268 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
4269 new_width = MAX (tree_view->priv->x_drag - x, 0);
4270 else
4271 new_width = MAX (x - tree_view->priv->x_drag, 0);
4272
4273 if (new_width != gtk_tree_view_column_get_fixed_width (column))
4274 gtk_tree_view_column_set_fixed_width (column, new_width);
4275
4276 return FALSE;
4277 }
4278
4279 static void
gtk_tree_view_update_current_reorder(GtkTreeView * tree_view)4280 gtk_tree_view_update_current_reorder (GtkTreeView *tree_view)
4281 {
4282 GtkTreeViewColumnReorder *reorder = NULL;
4283 GdkEventSequence *sequence;
4284 GList *list;
4285 gdouble x;
4286
4287 sequence = gtk_gesture_single_get_current_sequence
4288 (GTK_GESTURE_SINGLE (tree_view->priv->column_drag_gesture));
4289 gtk_gesture_get_point (tree_view->priv->column_drag_gesture,
4290 sequence, &x, NULL);
4291 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
4292
4293 for (list = tree_view->priv->column_drag_info; list; list = list->next)
4294 {
4295 reorder = (GtkTreeViewColumnReorder *) list->data;
4296 if (x >= reorder->left_align && x < reorder->right_align)
4297 break;
4298 reorder = NULL;
4299 }
4300
4301 tree_view->priv->cur_reorder = reorder;
4302 gtk_tree_view_motion_draw_column_motion_arrow (tree_view);
4303 }
4304
4305 static void
gtk_tree_view_vertical_autoscroll(GtkTreeView * tree_view)4306 gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view)
4307 {
4308 GdkRectangle visible_rect;
4309 gint y;
4310 gint offset;
4311
4312 if (gtk_gesture_is_recognized (tree_view->priv->drag_gesture))
4313 {
4314 GdkEventSequence *sequence;
4315 gdouble py;
4316
4317 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (tree_view->priv->drag_gesture));
4318 gtk_gesture_get_point (tree_view->priv->drag_gesture, sequence, NULL, &py);
4319 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, py,
4320 NULL, &y);
4321 }
4322 else
4323 {
4324 y = tree_view->priv->event_last_y;
4325 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, y, NULL, &y);
4326 }
4327
4328 y += tree_view->priv->dy;
4329 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
4330
4331 /* see if we are near the edge. */
4332 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
4333 if (offset > 0)
4334 {
4335 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
4336 if (offset < 0)
4337 return;
4338 }
4339
4340 gtk_adjustment_set_value (tree_view->priv->vadjustment,
4341 MAX (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0));
4342 }
4343
4344 static gboolean
gtk_tree_view_horizontal_autoscroll(GtkTreeView * tree_view)4345 gtk_tree_view_horizontal_autoscroll (GtkTreeView *tree_view)
4346 {
4347 GdkEventSequence *sequence;
4348 GdkRectangle visible_rect;
4349 gdouble x;
4350 gint offset;
4351
4352 sequence = gtk_gesture_single_get_current_sequence
4353 (GTK_GESTURE_SINGLE (tree_view->priv->column_drag_gesture));
4354 gtk_gesture_get_point (tree_view->priv->column_drag_gesture,
4355 sequence, &x, NULL);
4356 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
4357
4358 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
4359
4360 /* See if we are near the edge. */
4361 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
4362 if (offset > 0)
4363 {
4364 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
4365 if (offset < 0)
4366 return TRUE;
4367 }
4368 offset = offset/3;
4369
4370 gtk_adjustment_set_value (tree_view->priv->hadjustment,
4371 MAX (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset, 0.0));
4372
4373 return TRUE;
4374
4375 }
4376
4377 static gboolean
gtk_tree_view_motion_drag_column(GtkTreeView * tree_view,gdouble x,gdouble y)4378 gtk_tree_view_motion_drag_column (GtkTreeView *tree_view,
4379 gdouble x,
4380 gdouble y)
4381 {
4382 GtkAllocation allocation, button_allocation;
4383 GtkTreeViewColumn *column = tree_view->priv->drag_column;
4384 GtkWidget *button;
4385 gint win_x, win_y;
4386
4387 button = gtk_tree_view_column_get_button (column);
4388 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
4389
4390 /* Handle moving the header */
4391 gdk_window_get_position (tree_view->priv->drag_window, &win_x, &win_y);
4392 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
4393 gtk_widget_get_allocation (button, &button_allocation);
4394 win_x = CLAMP (x - _gtk_tree_view_column_get_drag_x (column), 0,
4395 MAX (tree_view->priv->width, allocation.width) - button_allocation.width);
4396 gdk_window_move (tree_view->priv->drag_window, win_x, win_y);
4397 gdk_window_raise (tree_view->priv->drag_window);
4398
4399 /* autoscroll, if needed */
4400 gtk_tree_view_horizontal_autoscroll (tree_view);
4401 /* Update the current reorder position and arrow; */
4402 gtk_tree_view_update_current_reorder (tree_view);
4403
4404 return TRUE;
4405 }
4406
4407 static void
gtk_tree_view_stop_rubber_band(GtkTreeView * tree_view)4408 gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view)
4409 {
4410 remove_scroll_timeout (tree_view);
4411
4412 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4413 {
4414 GtkTreePath *tmp_path;
4415
4416 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
4417
4418 /* The anchor path should be set to the start path */
4419 if (tree_view->priv->rubber_band_start_node)
4420 {
4421 tmp_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->rubber_band_start_tree,
4422 tree_view->priv->rubber_band_start_node);
4423
4424 if (tree_view->priv->anchor)
4425 gtk_tree_row_reference_free (tree_view->priv->anchor);
4426
4427 tree_view->priv->anchor =
4428 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
4429 tree_view->priv->model,
4430 tmp_path);
4431
4432 gtk_tree_path_free (tmp_path);
4433 }
4434
4435 /* ... and the cursor to the end path */
4436 if (tree_view->priv->rubber_band_end_node)
4437 {
4438 tmp_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->rubber_band_end_tree,
4439 tree_view->priv->rubber_band_end_node);
4440 gtk_tree_view_real_set_cursor (GTK_TREE_VIEW (tree_view), tmp_path, 0);
4441 gtk_tree_path_free (tmp_path);
4442 }
4443
4444 _gtk_tree_selection_emit_changed (tree_view->priv->selection);
4445
4446 gtk_css_node_set_parent (tree_view->priv->rubber_band_cssnode, NULL);
4447 tree_view->priv->rubber_band_cssnode = NULL;
4448 }
4449
4450 /* Clear status variables */
4451 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
4452 tree_view->priv->rubber_band_extend = FALSE;
4453 tree_view->priv->rubber_band_modify = FALSE;
4454
4455 tree_view->priv->rubber_band_start_node = NULL;
4456 tree_view->priv->rubber_band_start_tree = NULL;
4457 tree_view->priv->rubber_band_end_node = NULL;
4458 tree_view->priv->rubber_band_end_tree = NULL;
4459 }
4460
4461 static void
gtk_tree_view_update_rubber_band_selection_range(GtkTreeView * tree_view,GtkRBTree * start_tree,GtkRBNode * start_node,GtkRBTree * end_tree,GtkRBNode * end_node,gboolean select,gboolean skip_start,gboolean skip_end)4462 gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view,
4463 GtkRBTree *start_tree,
4464 GtkRBNode *start_node,
4465 GtkRBTree *end_tree,
4466 GtkRBNode *end_node,
4467 gboolean select,
4468 gboolean skip_start,
4469 gboolean skip_end)
4470 {
4471 if (start_node == end_node)
4472 return;
4473
4474 /* We skip the first node and jump inside the loop */
4475 if (skip_start)
4476 goto skip_first;
4477
4478 do
4479 {
4480 /* Small optimization by assuming insensitive nodes are never
4481 * selected.
4482 */
4483 if (!GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
4484 {
4485 GtkTreePath *path;
4486 gboolean selectable;
4487
4488 path = _gtk_tree_path_new_from_rbtree (start_tree, start_node);
4489 selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection, start_node, path);
4490 gtk_tree_path_free (path);
4491
4492 if (!selectable)
4493 goto node_not_selectable;
4494 }
4495
4496 if (select)
4497 {
4498 if (tree_view->priv->rubber_band_extend)
4499 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4500 else if (tree_view->priv->rubber_band_modify)
4501 {
4502 /* Toggle the selection state */
4503 if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
4504 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4505 else
4506 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4507 }
4508 else
4509 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4510 }
4511 else
4512 {
4513 /* Mirror the above */
4514 if (tree_view->priv->rubber_band_extend)
4515 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4516 else if (tree_view->priv->rubber_band_modify)
4517 {
4518 /* Toggle the selection state */
4519 if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
4520 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4521 else
4522 GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4523 }
4524 else
4525 GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
4526 }
4527
4528 _gtk_tree_view_queue_draw_node (tree_view, start_tree, start_node, NULL);
4529
4530 node_not_selectable:
4531 if (start_node == end_node)
4532 break;
4533
4534 skip_first:
4535
4536 if (start_node->children)
4537 {
4538 start_tree = start_node->children;
4539 start_node = _gtk_rbtree_first (start_tree);
4540 }
4541 else
4542 {
4543 _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
4544
4545 if (!start_tree)
4546 /* Ran out of tree */
4547 break;
4548 }
4549
4550 if (skip_end && start_node == end_node)
4551 break;
4552 }
4553 while (TRUE);
4554 }
4555
4556 static void
gtk_tree_view_update_rubber_band_selection(GtkTreeView * tree_view)4557 gtk_tree_view_update_rubber_band_selection (GtkTreeView *tree_view)
4558 {
4559 GtkRBTree *start_tree, *end_tree;
4560 GtkRBNode *start_node, *end_node;
4561 gdouble start_y, offset_y;
4562 gint bin_y;
4563
4564 if (!gtk_gesture_is_active (tree_view->priv->drag_gesture))
4565 return;
4566
4567 gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
4568 NULL, &offset_y);
4569 gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
4570 NULL, &start_y);
4571 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, 0, start_y,
4572 NULL, &bin_y);
4573 bin_y = MAX (0, bin_y + offset_y + tree_view->priv->dy);
4574
4575 _gtk_rbtree_find_offset (tree_view->priv->tree, MIN (tree_view->priv->press_start_y, bin_y), &start_tree, &start_node);
4576 _gtk_rbtree_find_offset (tree_view->priv->tree, MAX (tree_view->priv->press_start_y, bin_y), &end_tree, &end_node);
4577
4578 /* Handle the start area first */
4579 if (!start_node && !end_node)
4580 {
4581 if (tree_view->priv->rubber_band_start_node)
4582 {
4583 GtkRBNode *node = tree_view->priv->rubber_band_start_node;
4584 GtkRBTree *tree = tree_view->priv->rubber_band_start_tree;
4585
4586 if (tree_view->priv->rubber_band_modify)
4587 {
4588 /* Toggle the selection state */
4589 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
4590 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SELECTED);
4591 else
4592 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SELECTED);
4593 }
4594 else
4595 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SELECTED);
4596
4597 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
4598 }
4599 }
4600 if (!tree_view->priv->rubber_band_start_node || !start_node)
4601 {
4602 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4603 start_tree,
4604 start_node,
4605 end_tree,
4606 end_node,
4607 TRUE,
4608 FALSE,
4609 FALSE);
4610 }
4611 else if (_gtk_rbtree_node_find_offset (start_tree, start_node) <
4612 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_start_tree, tree_view->priv->rubber_band_start_node))
4613 {
4614 /* New node is above the old one; selection became bigger */
4615 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4616 start_tree,
4617 start_node,
4618 tree_view->priv->rubber_band_start_tree,
4619 tree_view->priv->rubber_band_start_node,
4620 TRUE,
4621 FALSE,
4622 TRUE);
4623 }
4624 else if (_gtk_rbtree_node_find_offset (start_tree, start_node) >
4625 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_start_tree, tree_view->priv->rubber_band_start_node))
4626 {
4627 /* New node is below the old one; selection became smaller */
4628 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4629 tree_view->priv->rubber_band_start_tree,
4630 tree_view->priv->rubber_band_start_node,
4631 start_tree,
4632 start_node,
4633 FALSE,
4634 FALSE,
4635 TRUE);
4636 }
4637
4638 tree_view->priv->rubber_band_start_tree = start_tree;
4639 tree_view->priv->rubber_band_start_node = start_node;
4640
4641 /* Next, handle the end area */
4642 if (!tree_view->priv->rubber_band_end_node)
4643 {
4644 /* In the event this happens, start_node was also NULL; this case is
4645 * handled above.
4646 */
4647 }
4648 else if (!end_node)
4649 {
4650 /* Find the last node in the tree */
4651 _gtk_rbtree_find_offset (tree_view->priv->tree, gtk_tree_view_get_height (tree_view) - 1,
4652 &end_tree, &end_node);
4653
4654 /* Selection reached end of the tree */
4655 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4656 tree_view->priv->rubber_band_end_tree,
4657 tree_view->priv->rubber_band_end_node,
4658 end_tree,
4659 end_node,
4660 TRUE,
4661 TRUE,
4662 FALSE);
4663 }
4664 else if (_gtk_rbtree_node_find_offset (end_tree, end_node) >
4665 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_end_tree, tree_view->priv->rubber_band_end_node))
4666 {
4667 /* New node is below the old one; selection became bigger */
4668 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4669 tree_view->priv->rubber_band_end_tree,
4670 tree_view->priv->rubber_band_end_node,
4671 end_tree,
4672 end_node,
4673 TRUE,
4674 TRUE,
4675 FALSE);
4676 }
4677 else if (_gtk_rbtree_node_find_offset (end_tree, end_node) <
4678 _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_end_tree, tree_view->priv->rubber_band_end_node))
4679 {
4680 /* New node is above the old one; selection became smaller */
4681 gtk_tree_view_update_rubber_band_selection_range (tree_view,
4682 end_tree,
4683 end_node,
4684 tree_view->priv->rubber_band_end_tree,
4685 tree_view->priv->rubber_band_end_node,
4686 FALSE,
4687 TRUE,
4688 FALSE);
4689 }
4690
4691 tree_view->priv->rubber_band_end_tree = end_tree;
4692 tree_view->priv->rubber_band_end_node = end_node;
4693 }
4694
4695 static void
gtk_tree_view_update_rubber_band(GtkTreeView * tree_view)4696 gtk_tree_view_update_rubber_band (GtkTreeView *tree_view)
4697 {
4698 gdouble start_x, start_y, offset_x, offset_y, x, y;
4699 GdkRectangle old_area;
4700 GdkRectangle new_area;
4701 cairo_region_t *invalid_region;
4702 gint bin_x, bin_y;
4703
4704 if (!gtk_gesture_is_recognized (tree_view->priv->drag_gesture))
4705 return;
4706
4707 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
4708 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
4709 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
4710 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
4711
4712 gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
4713 &offset_x, &offset_y);
4714 gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
4715 &start_x, &start_y);
4716 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
4717 &bin_x, &bin_y);
4718 bin_y += tree_view->priv->dy;
4719
4720 x = MAX (bin_x + offset_x, 0);
4721 y = MAX (bin_y + offset_y, 0);
4722
4723 new_area.x = MIN (tree_view->priv->press_start_x, x);
4724 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
4725 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
4726 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
4727
4728 invalid_region = cairo_region_create_rectangle (&old_area);
4729 cairo_region_union_rectangle (invalid_region, &new_area);
4730
4731 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
4732
4733 cairo_region_destroy (invalid_region);
4734
4735 tree_view->priv->rubber_band_x = x;
4736 tree_view->priv->rubber_band_y = y;
4737
4738 gtk_tree_view_update_rubber_band_selection (tree_view);
4739 }
4740
4741 static void
gtk_tree_view_paint_rubber_band(GtkTreeView * tree_view,cairo_t * cr)4742 gtk_tree_view_paint_rubber_band (GtkTreeView *tree_view,
4743 cairo_t *cr)
4744 {
4745 gdouble start_x, start_y, offset_x, offset_y;
4746 GdkRectangle rect;
4747 GtkStyleContext *context;
4748 gint bin_x, bin_y;
4749
4750 if (!gtk_gesture_is_recognized (tree_view->priv->drag_gesture))
4751 return;
4752
4753 gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
4754 &offset_x, &offset_y);
4755 gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
4756 &start_x, &start_y);
4757 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
4758 &bin_x, &bin_y);
4759 bin_x = MAX (0, bin_x + offset_x);
4760 bin_y = MAX (0, bin_y + offset_y + tree_view->priv->dy);
4761
4762 cairo_save (cr);
4763
4764 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
4765
4766 gtk_style_context_save_to_node (context, tree_view->priv->rubber_band_cssnode);
4767
4768 rect.x = MIN (tree_view->priv->press_start_x, bin_x);
4769 rect.y = MIN (tree_view->priv->press_start_y, bin_y) - tree_view->priv->dy;
4770 rect.width = ABS (tree_view->priv->press_start_x - bin_x) + 1;
4771 rect.height = ABS (tree_view->priv->press_start_y - bin_y) + 1;
4772
4773 gdk_cairo_rectangle (cr, &rect);
4774 cairo_clip (cr);
4775
4776 gtk_render_background (context, cr,
4777 rect.x, rect.y,
4778 rect.width, rect.height);
4779 gtk_render_frame (context, cr,
4780 rect.x, rect.y,
4781 rect.width, rect.height);
4782
4783 gtk_style_context_restore (context);
4784 cairo_restore (cr);
4785 }
4786
4787 static void
gtk_tree_view_column_drag_gesture_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkTreeView * tree_view)4788 gtk_tree_view_column_drag_gesture_update (GtkGestureDrag *gesture,
4789 gdouble offset_x,
4790 gdouble offset_y,
4791 GtkTreeView *tree_view)
4792 {
4793 gdouble start_x, start_y, x, y;
4794 GdkEventSequence *sequence;
4795
4796 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
4797
4798 if (gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence) != GTK_EVENT_SEQUENCE_CLAIMED)
4799 return;
4800
4801 gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
4802 x = start_x + offset_x;
4803 y = start_y + offset_y;
4804
4805 if (tree_view->priv->in_column_resize)
4806 gtk_tree_view_motion_resize_column (tree_view, x, y);
4807 else if (tree_view->priv->in_column_drag)
4808 gtk_tree_view_motion_drag_column (tree_view, x, y);
4809 }
4810
4811 static void
gtk_tree_view_drag_gesture_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GtkTreeView * tree_view)4812 gtk_tree_view_drag_gesture_update (GtkGestureDrag *gesture,
4813 gdouble offset_x,
4814 gdouble offset_y,
4815 GtkTreeView *tree_view)
4816 {
4817 if (tree_view->priv->tree == NULL)
4818 {
4819 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
4820 return;
4821 }
4822
4823 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
4824 {
4825 GtkCssNode *widget_node;
4826
4827 widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view));
4828 tree_view->priv->rubber_band_cssnode = gtk_css_node_new ();
4829 gtk_css_node_set_name (tree_view->priv->rubber_band_cssnode, I_("rubberband"));
4830 gtk_css_node_set_parent (tree_view->priv->rubber_band_cssnode, widget_node);
4831 gtk_css_node_set_state (tree_view->priv->rubber_band_cssnode, gtk_css_node_get_state (widget_node));
4832 g_object_unref (tree_view->priv->rubber_band_cssnode);
4833
4834 gtk_tree_view_update_rubber_band (tree_view);
4835
4836 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
4837 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
4838 }
4839 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4840 {
4841 gtk_tree_view_update_rubber_band (tree_view);
4842
4843 add_scroll_timeout (tree_view);
4844 }
4845 else if (!tree_view->priv->rubber_band_status)
4846 {
4847 if (gtk_tree_view_maybe_begin_dragging_row (tree_view))
4848 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
4849 }
4850 }
4851
4852 static gboolean
gtk_tree_view_motion(GtkWidget * widget,GdkEventMotion * event)4853 gtk_tree_view_motion (GtkWidget *widget,
4854 GdkEventMotion *event)
4855 {
4856 GtkTreeView *tree_view;
4857 GtkRBTree *tree;
4858 GtkRBNode *node;
4859 gint new_y;
4860
4861 tree_view = (GtkTreeView *) widget;
4862
4863 if (tree_view->priv->tree)
4864 {
4865 /* If we are currently pressing down a button, we don't want to prelight anything else. */
4866 if (gtk_gesture_is_active (tree_view->priv->drag_gesture) ||
4867 gtk_gesture_is_active (tree_view->priv->multipress_gesture))
4868 node = NULL;
4869
4870 new_y = MAX (0, TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, event->y));
4871
4872 _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
4873
4874 tree_view->priv->event_last_x = event->x;
4875 tree_view->priv->event_last_y = event->y;
4876 prelight_or_select (tree_view, tree, node, event->x, event->y);
4877 }
4878
4879 return GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->motion_notify_event (widget, event);
4880 }
4881
4882 /* Invalidate the focus rectangle near the edge of the bin_window; used when
4883 * the tree is empty.
4884 */
4885 static void
invalidate_empty_focus(GtkTreeView * tree_view)4886 invalidate_empty_focus (GtkTreeView *tree_view)
4887 {
4888 GdkRectangle area;
4889
4890 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
4891 return;
4892
4893 area.x = 0;
4894 area.y = 0;
4895 area.width = gdk_window_get_width (tree_view->priv->bin_window);
4896 area.height = gdk_window_get_height (tree_view->priv->bin_window);
4897 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
4898 }
4899
4900 /* Draws background and a focus rectangle near the edge of the bin_window;
4901 * used when the tree is empty.
4902 */
4903 static void
draw_empty(GtkTreeView * tree_view,cairo_t * cr)4904 draw_empty (GtkTreeView *tree_view,
4905 cairo_t *cr)
4906 {
4907 GtkWidget *widget = GTK_WIDGET (tree_view);
4908 GtkStyleContext *context;
4909 gint width, height;
4910
4911 context = gtk_widget_get_style_context (widget);
4912
4913 width = gdk_window_get_width (tree_view->priv->bin_window);
4914 height = gdk_window_get_height (tree_view->priv->bin_window);
4915
4916 gtk_render_background (context, cr, 0, 0, width, height);
4917
4918 if (gtk_widget_has_visible_focus (widget))
4919 gtk_render_focus (context, cr, 0, 0, width, height);
4920 }
4921
4922 typedef enum {
4923 GTK_TREE_VIEW_GRID_LINE,
4924 GTK_TREE_VIEW_TREE_LINE,
4925 GTK_TREE_VIEW_FOREGROUND_LINE
4926 } GtkTreeViewLineType;
4927
4928 static void
gtk_tree_view_draw_line(GtkTreeView * tree_view,cairo_t * cr,GtkTreeViewLineType type,int x1,int y1,int x2,int y2)4929 gtk_tree_view_draw_line (GtkTreeView *tree_view,
4930 cairo_t *cr,
4931 GtkTreeViewLineType type,
4932 int x1,
4933 int y1,
4934 int x2,
4935 int y2)
4936 {
4937 GtkStyleContext *context;
4938
4939 cairo_save (cr);
4940
4941 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
4942
4943 switch (type)
4944 {
4945 case GTK_TREE_VIEW_TREE_LINE:
4946 {
4947 const GdkRGBA *color;
4948
4949 color = _gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_LEFT_COLOR));
4950
4951 gdk_cairo_set_source_rgba (cr, color);
4952 cairo_set_line_width (cr, tree_view->priv->tree_line_width);
4953 if (tree_view->priv->tree_line_dashes[0])
4954 cairo_set_dash (cr, tree_view->priv->tree_line_dashes, 2, 0.5);
4955 }
4956 break;
4957
4958 case GTK_TREE_VIEW_GRID_LINE:
4959 {
4960 const GdkRGBA *color;
4961
4962 color = _gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_COLOR));
4963
4964 gdk_cairo_set_source_rgba (cr, color);
4965 cairo_set_line_width (cr, tree_view->priv->grid_line_width);
4966 if (tree_view->priv->grid_line_dashes[0])
4967 cairo_set_dash (cr, tree_view->priv->grid_line_dashes, 2, 0.5);
4968 }
4969 break;
4970
4971 case GTK_TREE_VIEW_FOREGROUND_LINE:
4972 {
4973 GdkRGBA color;
4974
4975 cairo_set_line_width (cr, 1.0);
4976 gtk_style_context_get_color (context, gtk_style_context_get_state (context), &color);
4977 gdk_cairo_set_source_rgba (cr, &color);
4978 }
4979 break;
4980
4981 default:
4982 g_assert_not_reached ();
4983 break;
4984 }
4985
4986 cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
4987 cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
4988 cairo_stroke (cr);
4989
4990 cairo_restore (cr);
4991 }
4992
4993 static void
gtk_tree_view_draw_grid_lines(GtkTreeView * tree_view,cairo_t * cr)4994 gtk_tree_view_draw_grid_lines (GtkTreeView *tree_view,
4995 cairo_t *cr)
4996 {
4997 GList *list, *first, *last;
4998 gboolean rtl;
4999 gint current_x = 0;
5000
5001 if (tree_view->priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_VERTICAL
5002 && tree_view->priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_BOTH)
5003 return;
5004
5005 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
5006
5007 first = g_list_first (tree_view->priv->columns);
5008 last = g_list_last (tree_view->priv->columns);
5009
5010 for (list = (rtl ? last : first);
5011 list;
5012 list = (rtl ? list->prev : list->next))
5013 {
5014 GtkTreeViewColumn *column = list->data;
5015
5016 /* We don't want a line for the last column */
5017 if (column == (rtl ? first->data : last->data))
5018 break;
5019
5020 if (!gtk_tree_view_column_get_visible (column))
5021 continue;
5022
5023 current_x += gtk_tree_view_column_get_width (column);
5024
5025 gtk_tree_view_draw_line (tree_view, cr,
5026 GTK_TREE_VIEW_GRID_LINE,
5027 current_x - 1, 0,
5028 current_x - 1, gtk_tree_view_get_height (tree_view));
5029 }
5030 }
5031
5032 /* Warning: Very scary function.
5033 * Modify at your own risk
5034 *
5035 * KEEP IN SYNC WITH gtk_tree_view_create_row_drag_icon()!
5036 * FIXME: It’s not...
5037 */
5038 static gboolean
gtk_tree_view_bin_draw(GtkWidget * widget,cairo_t * cr)5039 gtk_tree_view_bin_draw (GtkWidget *widget,
5040 cairo_t *cr)
5041 {
5042 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5043 GtkTreePath *path;
5044 GtkRBTree *tree;
5045 GList *list;
5046 GtkRBNode *node;
5047 GtkRBNode *drag_highlight = NULL;
5048 GtkRBTree *drag_highlight_tree = NULL;
5049 GtkTreeIter iter;
5050 gint new_y;
5051 gint y_offset, cell_offset;
5052 gint max_height;
5053 gint depth;
5054 GdkRectangle background_area;
5055 GdkRectangle cell_area;
5056 GdkRectangle clip;
5057 guint flags;
5058 gint bin_window_width;
5059 gint bin_window_height;
5060 GtkTreePath *drag_dest_path = NULL;
5061 GList *first_column, *last_column;
5062 gint vertical_separator;
5063 gint horizontal_separator;
5064 gboolean allow_rules;
5065 gboolean has_can_focus_cell;
5066 gboolean rtl;
5067 gint n_visible_columns;
5068 gint grid_line_width;
5069 gint expander_size;
5070 gboolean draw_vgrid_lines, draw_hgrid_lines;
5071 GtkStyleContext *context;
5072 gboolean parity;
5073
5074 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5075 context = gtk_widget_get_style_context (widget);
5076
5077 gtk_widget_style_get (widget,
5078 "horizontal-separator", &horizontal_separator,
5079 "vertical-separator", &vertical_separator,
5080 "allow-rules", &allow_rules,
5081 NULL);
5082
5083 if (tree_view->priv->tree == NULL)
5084 {
5085 draw_empty (tree_view, cr);
5086 return TRUE;
5087 }
5088
5089 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
5090 bin_window_height = gdk_window_get_height (tree_view->priv->bin_window);
5091 if (!gdk_cairo_get_clip_rectangle (cr, &clip))
5092 return TRUE;
5093
5094 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, clip.y);
5095
5096 if (new_y < 0)
5097 new_y = 0;
5098 y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
5099
5100 if (gtk_tree_view_get_height (tree_view) < bin_window_height)
5101 {
5102 gtk_style_context_save (context);
5103 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
5104
5105 gtk_render_background (context, cr,
5106 0, gtk_tree_view_get_height (tree_view),
5107 bin_window_width,
5108 bin_window_height - gtk_tree_view_get_height (tree_view));
5109
5110 gtk_style_context_restore (context);
5111 }
5112
5113 if (node == NULL)
5114 goto done;
5115
5116 /* find the path for the node */
5117 path = _gtk_tree_path_new_from_rbtree (tree, node);
5118 gtk_tree_model_get_iter (tree_view->priv->model,
5119 &iter,
5120 path);
5121 depth = gtk_tree_path_get_depth (path);
5122 gtk_tree_path_free (path);
5123
5124 if (tree_view->priv->drag_dest_row)
5125 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
5126
5127 if (drag_dest_path)
5128 _gtk_tree_view_find_node (tree_view, drag_dest_path,
5129 &drag_highlight_tree, &drag_highlight);
5130
5131 draw_vgrid_lines =
5132 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
5133 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
5134 draw_hgrid_lines =
5135 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
5136 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
5137 expander_size = gtk_tree_view_get_expander_size (tree_view);
5138
5139 if (draw_vgrid_lines || draw_hgrid_lines)
5140 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
5141
5142 n_visible_columns = 0;
5143 for (list = tree_view->priv->columns; list; list = list->next)
5144 {
5145 if (!gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
5146 continue;
5147 n_visible_columns ++;
5148 }
5149
5150 /* Find the last column */
5151 for (last_column = g_list_last (tree_view->priv->columns);
5152 last_column &&
5153 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
5154 last_column = last_column->prev)
5155 ;
5156
5157 /* and the first */
5158 for (first_column = g_list_first (tree_view->priv->columns);
5159 first_column &&
5160 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
5161 first_column = first_column->next)
5162 ;
5163
5164 /* Actually process the expose event. To do this, we want to
5165 * start at the first node of the event, and walk the tree in
5166 * order, drawing each successive node.
5167 */
5168
5169 parity = !(_gtk_rbtree_node_get_index (tree, node) % 2);
5170
5171 do
5172 {
5173 gboolean is_separator = FALSE;
5174 gint n_col = 0;
5175
5176 parity = !parity;
5177 is_separator = row_is_separator (tree_view, &iter, NULL);
5178
5179 max_height = gtk_tree_view_get_row_height (tree_view, node);
5180
5181 cell_offset = 0;
5182
5183 background_area.y = y_offset + clip.y;
5184 background_area.height = max_height;
5185
5186 flags = 0;
5187
5188 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT))
5189 flags |= GTK_CELL_RENDERER_PRELIT;
5190
5191 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
5192 flags |= GTK_CELL_RENDERER_SELECTED;
5193
5194 /* we *need* to set cell data on all cells before the call
5195 * to _has_can_focus_cell, else _has_can_focus_cell() does not
5196 * return a correct value.
5197 */
5198 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
5199 list;
5200 list = (rtl ? list->prev : list->next))
5201 {
5202 GtkTreeViewColumn *column = list->data;
5203 gtk_tree_view_column_cell_set_cell_data (column,
5204 tree_view->priv->model,
5205 &iter,
5206 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
5207 node->children?TRUE:FALSE);
5208 }
5209
5210 has_can_focus_cell = gtk_tree_view_has_can_focus_cell (tree_view);
5211
5212 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
5213 list;
5214 list = (rtl ? list->prev : list->next))
5215 {
5216 GtkTreeViewColumn *column = list->data;
5217 GtkStateFlags state = 0;
5218 gint width;
5219 gboolean draw_focus;
5220
5221 if (!gtk_tree_view_column_get_visible (column))
5222 continue;
5223
5224 n_col++;
5225 width = gtk_tree_view_column_get_width (column);
5226
5227 if (cell_offset > clip.x + clip.width ||
5228 cell_offset + width < clip.x)
5229 {
5230 cell_offset += width;
5231 continue;
5232 }
5233
5234 if (gtk_tree_view_column_get_sort_indicator (column))
5235 flags |= GTK_CELL_RENDERER_SORTED;
5236 else
5237 flags &= ~GTK_CELL_RENDERER_SORTED;
5238
5239 if (tree_view->priv->cursor_node == node)
5240 flags |= GTK_CELL_RENDERER_FOCUSED;
5241 else
5242 flags &= ~GTK_CELL_RENDERER_FOCUSED;
5243
5244 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
5245 flags |= GTK_CELL_RENDERER_EXPANDABLE;
5246 else
5247 flags &= ~GTK_CELL_RENDERER_EXPANDABLE;
5248
5249 if (node->children)
5250 flags |= GTK_CELL_RENDERER_EXPANDED;
5251 else
5252 flags &= ~GTK_CELL_RENDERER_EXPANDED;
5253
5254 background_area.x = cell_offset;
5255 background_area.width = width;
5256
5257 cell_area = background_area;
5258 cell_area.y += vertical_separator / 2;
5259 cell_area.x += horizontal_separator / 2;
5260 cell_area.height -= vertical_separator;
5261 cell_area.width -= horizontal_separator;
5262
5263 if (draw_vgrid_lines)
5264 {
5265 if (list == first_column)
5266 {
5267 cell_area.width -= grid_line_width / 2;
5268 }
5269 else if (list == last_column)
5270 {
5271 cell_area.x += grid_line_width / 2;
5272 cell_area.width -= grid_line_width / 2;
5273 }
5274 else
5275 {
5276 cell_area.x += grid_line_width / 2;
5277 cell_area.width -= grid_line_width;
5278 }
5279 }
5280
5281 if (draw_hgrid_lines)
5282 {
5283 cell_area.y += grid_line_width / 2;
5284 cell_area.height -= grid_line_width;
5285 }
5286
5287 if (!gdk_rectangle_intersect (&clip, &background_area, NULL))
5288 {
5289 cell_offset += gtk_tree_view_column_get_width (column);
5290 continue;
5291 }
5292
5293 gtk_tree_view_column_cell_set_cell_data (column,
5294 tree_view->priv->model,
5295 &iter,
5296 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
5297 node->children?TRUE:FALSE);
5298
5299 gtk_style_context_save (context);
5300
5301 state = gtk_cell_renderer_get_state (NULL, widget, flags);
5302 gtk_style_context_set_state (context, state);
5303
5304 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
5305
5306 if (node == tree_view->priv->cursor_node && has_can_focus_cell
5307 && ((column == tree_view->priv->focus_column
5308 && tree_view->priv->draw_keyfocus &&
5309 gtk_widget_has_visible_focus (widget))
5310 || (column == tree_view->priv->edited_column)))
5311 draw_focus = TRUE;
5312 else
5313 draw_focus = FALSE;
5314
5315 /* Draw background */
5316 gtk_render_background (context, cr,
5317 background_area.x,
5318 background_area.y,
5319 background_area.width,
5320 background_area.height);
5321
5322 /* Draw frame */
5323 gtk_render_frame (context, cr,
5324 background_area.x,
5325 background_area.y,
5326 background_area.width,
5327 background_area.height);
5328
5329 if (gtk_tree_view_is_expander_column (tree_view, column))
5330 {
5331 if (!rtl)
5332 cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
5333 cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
5334
5335 if (gtk_tree_view_draw_expanders (tree_view))
5336 {
5337 if (!rtl)
5338 cell_area.x += depth * expander_size;
5339 cell_area.width -= depth * expander_size;
5340 }
5341
5342 if (is_separator)
5343 {
5344 gtk_style_context_save (context);
5345 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR);
5346
5347 gtk_render_line (context, cr,
5348 cell_area.x,
5349 cell_area.y + cell_area.height / 2,
5350 cell_area.x + cell_area.width,
5351 cell_area.y + cell_area.height / 2);
5352
5353 gtk_style_context_restore (context);
5354 }
5355 else
5356 {
5357 _gtk_tree_view_column_cell_render (column,
5358 cr,
5359 &background_area,
5360 &cell_area,
5361 flags,
5362 draw_focus);
5363 }
5364
5365 if (gtk_tree_view_draw_expanders (tree_view)
5366 && (node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
5367 {
5368 gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
5369 cr,
5370 tree,
5371 node);
5372 }
5373 }
5374 else
5375 {
5376 if (is_separator)
5377 {
5378 gtk_style_context_save (context);
5379 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR);
5380
5381 gtk_render_line (context, cr,
5382 cell_area.x,
5383 cell_area.y + cell_area.height / 2,
5384 cell_area.x + cell_area.width,
5385 cell_area.y + cell_area.height / 2);
5386
5387 gtk_style_context_restore (context);
5388 }
5389 else
5390 _gtk_tree_view_column_cell_render (column,
5391 cr,
5392 &background_area,
5393 &cell_area,
5394 flags,
5395 draw_focus);
5396 }
5397
5398 if (draw_hgrid_lines)
5399 {
5400 if (background_area.y >= clip.y)
5401 gtk_tree_view_draw_line (tree_view, cr,
5402 GTK_TREE_VIEW_GRID_LINE,
5403 background_area.x, background_area.y,
5404 background_area.x + background_area.width,
5405 background_area.y);
5406
5407 if (background_area.y + max_height < clip.y + clip.height)
5408 gtk_tree_view_draw_line (tree_view, cr,
5409 GTK_TREE_VIEW_GRID_LINE,
5410 background_area.x, background_area.y + max_height,
5411 background_area.x + background_area.width,
5412 background_area.y + max_height);
5413 }
5414
5415 if (gtk_tree_view_is_expander_column (tree_view, column) &&
5416 tree_view->priv->tree_lines_enabled)
5417 {
5418 gint x = background_area.x;
5419 gint mult = rtl ? -1 : 1;
5420 gint y0 = background_area.y;
5421 gint y1 = background_area.y + background_area.height/2;
5422 gint y2 = background_area.y + background_area.height;
5423
5424 if (rtl)
5425 x += background_area.width - 1;
5426
5427 if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT
5428 && depth > 1)
5429 {
5430 gtk_tree_view_draw_line (tree_view, cr,
5431 GTK_TREE_VIEW_TREE_LINE,
5432 x + expander_size * (depth - 1.5) * mult,
5433 y1,
5434 x + expander_size * (depth - 1.1) * mult,
5435 y1);
5436 }
5437 else if (depth > 1)
5438 {
5439 gtk_tree_view_draw_line (tree_view, cr,
5440 GTK_TREE_VIEW_TREE_LINE,
5441 x + expander_size * (depth - 1.5) * mult,
5442 y1,
5443 x + expander_size * (depth - 0.5) * mult,
5444 y1);
5445 }
5446
5447 if (depth > 1)
5448 {
5449 gint i;
5450 GtkRBNode *tmp_node;
5451 GtkRBTree *tmp_tree;
5452
5453 if (!_gtk_rbtree_next (tree, node))
5454 gtk_tree_view_draw_line (tree_view, cr,
5455 GTK_TREE_VIEW_TREE_LINE,
5456 x + expander_size * (depth - 1.5) * mult,
5457 y0,
5458 x + expander_size * (depth - 1.5) * mult,
5459 y1);
5460 else
5461 gtk_tree_view_draw_line (tree_view, cr,
5462 GTK_TREE_VIEW_TREE_LINE,
5463 x + expander_size * (depth - 1.5) * mult,
5464 y0,
5465 x + expander_size * (depth - 1.5) * mult,
5466 y2);
5467
5468 tmp_node = tree->parent_node;
5469 tmp_tree = tree->parent_tree;
5470
5471 for (i = depth - 2; i > 0; i--)
5472 {
5473 if (_gtk_rbtree_next (tmp_tree, tmp_node))
5474 gtk_tree_view_draw_line (tree_view, cr,
5475 GTK_TREE_VIEW_TREE_LINE,
5476 x + expander_size * (i - 0.5) * mult,
5477 y0,
5478 x + expander_size * (i - 0.5) * mult,
5479 y2);
5480
5481 tmp_node = tmp_tree->parent_node;
5482 tmp_tree = tmp_tree->parent_tree;
5483 }
5484 }
5485 }
5486
5487 gtk_style_context_restore (context);
5488 cell_offset += gtk_tree_view_column_get_width (column);
5489 }
5490
5491 if (node == drag_highlight)
5492 {
5493 /* Draw indicator for the drop
5494 */
5495 GtkRBTree *drag_tree = NULL;
5496 GtkRBNode *drag_node = NULL;
5497
5498 gtk_style_context_save (context);
5499 gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE);
5500
5501 switch (tree_view->priv->drag_dest_pos)
5502 {
5503 case GTK_TREE_VIEW_DROP_BEFORE:
5504 gtk_style_context_add_class (context, "before");
5505 break;
5506
5507 case GTK_TREE_VIEW_DROP_AFTER:
5508 gtk_style_context_add_class (context, "after");
5509 break;
5510
5511 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
5512 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
5513 gtk_style_context_add_class (context, "into");
5514 break;
5515 }
5516
5517 _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node);
5518 if (drag_tree != NULL)
5519 gtk_render_frame (context, cr,
5520 0, gtk_tree_view_get_row_y_offset (tree_view, drag_tree, drag_node),
5521 gdk_window_get_width (tree_view->priv->bin_window),
5522 gtk_tree_view_get_row_height (tree_view, drag_node));
5523
5524 gtk_style_context_restore (context);
5525 }
5526
5527 /* draw the big row-spanning focus rectangle, if needed */
5528 if (!has_can_focus_cell && node == tree_view->priv->cursor_node &&
5529 tree_view->priv->draw_keyfocus &&
5530 gtk_widget_has_visible_focus (widget))
5531 {
5532 gint tmp_y, tmp_height;
5533 GtkStateFlags focus_rect_state = 0;
5534
5535 gtk_style_context_save (context);
5536
5537 focus_rect_state = gtk_cell_renderer_get_state (NULL, widget, flags);
5538 gtk_style_context_set_state (context, focus_rect_state);
5539
5540 if (draw_hgrid_lines)
5541 {
5542 tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node) + grid_line_width / 2;
5543 tmp_height = gtk_tree_view_get_row_height (tree_view, node) - grid_line_width;
5544 }
5545 else
5546 {
5547 tmp_y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
5548 tmp_height = gtk_tree_view_get_row_height (tree_view, node);
5549 }
5550
5551 gtk_render_focus (context, cr,
5552 0, tmp_y,
5553 gdk_window_get_width (tree_view->priv->bin_window),
5554 tmp_height);
5555
5556 gtk_style_context_restore (context);
5557 }
5558
5559 y_offset += max_height;
5560 if (node->children)
5561 {
5562 GtkTreeIter parent = iter;
5563 gboolean has_child;
5564
5565 tree = node->children;
5566 node = _gtk_rbtree_first (tree);
5567
5568 has_child = gtk_tree_model_iter_children (tree_view->priv->model,
5569 &iter,
5570 &parent);
5571 depth++;
5572
5573 /* Sanity Check! */
5574 TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
5575 }
5576 else
5577 {
5578 gboolean done = FALSE;
5579
5580 do
5581 {
5582 node = _gtk_rbtree_next (tree, node);
5583 if (node != NULL)
5584 {
5585 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5586 done = TRUE;
5587
5588 /* Sanity Check! */
5589 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
5590 }
5591 else
5592 {
5593 GtkTreeIter parent_iter = iter;
5594 gboolean has_parent;
5595
5596 node = tree->parent_node;
5597 tree = tree->parent_tree;
5598 if (tree == NULL)
5599 /* we should go to done to free some memory */
5600 goto done;
5601 has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
5602 &iter,
5603 &parent_iter);
5604 depth--;
5605
5606 /* Sanity check */
5607 TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
5608 }
5609 }
5610 while (!done);
5611 }
5612 }
5613 while (y_offset < clip.height);
5614
5615 done:
5616 gtk_tree_view_draw_grid_lines (tree_view, cr);
5617
5618 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5619 gtk_tree_view_paint_rubber_band (tree_view, cr);
5620
5621 if (drag_dest_path)
5622 gtk_tree_path_free (drag_dest_path);
5623
5624 return FALSE;
5625 }
5626
5627 static void
draw_bin(cairo_t * cr,gpointer user_data)5628 draw_bin (cairo_t *cr,
5629 gpointer user_data)
5630 {
5631 GtkWidget *widget = GTK_WIDGET (user_data);
5632 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5633 GList *tmp_list;
5634
5635 cairo_save (cr);
5636
5637 gtk_cairo_transform_to_window (cr, widget, tree_view->priv->bin_window);
5638 gtk_tree_view_bin_draw (widget, cr);
5639
5640 cairo_restore (cr);
5641
5642 /* We can't just chain up to Container::draw as it will try to send the
5643 * event to the headers, so we handle propagating it to our children
5644 * (eg. widgets being edited) ourselves.
5645 */
5646 tmp_list = tree_view->priv->children;
5647 while (tmp_list)
5648 {
5649 GtkTreeViewChild *child = tmp_list->data;
5650 tmp_list = tmp_list->next;
5651
5652 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
5653 }
5654 }
5655
5656 static gboolean
gtk_tree_view_draw(GtkWidget * widget,cairo_t * cr)5657 gtk_tree_view_draw (GtkWidget *widget,
5658 cairo_t *cr)
5659 {
5660 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5661 GtkWidget *button;
5662 GtkStyleContext *context;
5663
5664 context = gtk_widget_get_style_context (widget);
5665
5666 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
5667 {
5668 cairo_rectangle_int_t view_rect;
5669 cairo_rectangle_int_t canvas_rect;
5670
5671 view_rect.x = 0;
5672 view_rect.y = gtk_tree_view_get_effective_header_height (tree_view);
5673 view_rect.width = gtk_widget_get_allocated_width (widget);
5674 view_rect.height = gtk_widget_get_allocated_height (widget) - view_rect.y;
5675
5676 gdk_window_get_position (tree_view->priv->bin_window, &canvas_rect.x, &canvas_rect.y);
5677 canvas_rect.y = -gtk_adjustment_get_value (tree_view->priv->vadjustment);
5678 canvas_rect.width = gdk_window_get_width (tree_view->priv->bin_window);
5679 canvas_rect.height = gtk_tree_view_get_height (tree_view);
5680
5681 _gtk_pixel_cache_draw (tree_view->priv->pixel_cache, cr, tree_view->priv->bin_window,
5682 &view_rect, &canvas_rect,
5683 draw_bin, widget);
5684 }
5685 else if (tree_view->priv->drag_highlight_window &&
5686 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_highlight_window))
5687 {
5688 GdkRGBA color;
5689
5690 gtk_style_context_get_color (context, gtk_style_context_get_state (context), &color);
5691 cairo_save (cr);
5692 gtk_cairo_transform_to_window (cr, GTK_WIDGET (tree_view), tree_view->priv->drag_highlight_window);
5693 if (tree_view->priv->drag_column_window_state == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
5694 {
5695 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
5696 cairo_paint (cr);
5697 gdk_cairo_set_source_rgba (cr, &color);
5698 cairo_rectangle (cr,
5699 1, 1,
5700 gdk_window_get_width (tree_view->priv->drag_highlight_window) - 2,
5701 gdk_window_get_height (tree_view->priv->drag_highlight_window) - 2);
5702 cairo_stroke (cr);
5703 }
5704 else
5705 {
5706 gdk_cairo_set_source_rgba (cr, &color);
5707 cairo_paint (cr);
5708 }
5709 cairo_restore (cr);
5710 }
5711 else
5712 {
5713 gtk_render_background (context, cr,
5714 0, 0,
5715 gtk_widget_get_allocated_width (widget),
5716 gtk_widget_get_allocated_height (widget));
5717 }
5718
5719 gtk_style_context_save (context);
5720 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
5721
5722 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
5723 {
5724 GList *list;
5725
5726 for (list = tree_view->priv->columns; list != NULL; list = list->next)
5727 {
5728 GtkTreeViewColumn *column = list->data;
5729
5730 if (column == tree_view->priv->drag_column)
5731 continue;
5732
5733 if (gtk_tree_view_column_get_visible (column))
5734 {
5735 button = gtk_tree_view_column_get_button (column);
5736 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
5737 button, cr);
5738 }
5739 }
5740 }
5741
5742 if (tree_view->priv->drag_window &&
5743 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
5744 {
5745 button = gtk_tree_view_column_get_button (tree_view->priv->drag_column);
5746 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
5747 button, cr);
5748 }
5749
5750 gtk_style_context_restore (context);
5751
5752 return FALSE;
5753 }
5754
5755 enum
5756 {
5757 DROP_HOME,
5758 DROP_RIGHT,
5759 DROP_LEFT,
5760 DROP_END
5761 };
5762
5763 /* returns 0x1 when no column has been found -- yes it's hackish */
5764 static GtkTreeViewColumn *
gtk_tree_view_get_drop_column(GtkTreeView * tree_view,GtkTreeViewColumn * column,gint drop_position)5765 gtk_tree_view_get_drop_column (GtkTreeView *tree_view,
5766 GtkTreeViewColumn *column,
5767 gint drop_position)
5768 {
5769 GtkTreeViewColumn *left_column = NULL;
5770 GtkTreeViewColumn *cur_column = NULL;
5771 GList *tmp_list;
5772
5773 if (!gtk_tree_view_column_get_reorderable (column))
5774 return (GtkTreeViewColumn *)0x1;
5775
5776 switch (drop_position)
5777 {
5778 case DROP_HOME:
5779 /* find first column where we can drop */
5780 tmp_list = tree_view->priv->columns;
5781 if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5782 return (GtkTreeViewColumn *)0x1;
5783
5784 while (tmp_list)
5785 {
5786 g_assert (tmp_list);
5787
5788 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5789 tmp_list = tmp_list->next;
5790
5791 if (left_column &&
5792 gtk_tree_view_column_get_visible (left_column) == FALSE)
5793 continue;
5794
5795 if (!tree_view->priv->column_drop_func)
5796 return left_column;
5797
5798 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5799 {
5800 left_column = cur_column;
5801 continue;
5802 }
5803
5804 return left_column;
5805 }
5806
5807 if (!tree_view->priv->column_drop_func)
5808 return left_column;
5809
5810 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
5811 return left_column;
5812 else
5813 return (GtkTreeViewColumn *)0x1;
5814 break;
5815
5816 case DROP_RIGHT:
5817 /* find first column after column where we can drop */
5818 tmp_list = tree_view->priv->columns;
5819
5820 for (; tmp_list; tmp_list = tmp_list->next)
5821 if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5822 break;
5823
5824 if (!tmp_list || !tmp_list->next)
5825 return (GtkTreeViewColumn *)0x1;
5826
5827 tmp_list = tmp_list->next;
5828 left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5829 tmp_list = tmp_list->next;
5830
5831 while (tmp_list)
5832 {
5833 g_assert (tmp_list);
5834
5835 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5836 tmp_list = tmp_list->next;
5837
5838 if (left_column &&
5839 gtk_tree_view_column_get_visible (left_column) == FALSE)
5840 {
5841 left_column = cur_column;
5842 if (tmp_list)
5843 tmp_list = tmp_list->next;
5844 continue;
5845 }
5846
5847 if (!tree_view->priv->column_drop_func)
5848 return left_column;
5849
5850 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5851 {
5852 left_column = cur_column;
5853 continue;
5854 }
5855
5856 return left_column;
5857 }
5858
5859 if (!tree_view->priv->column_drop_func)
5860 return left_column;
5861
5862 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
5863 return left_column;
5864 else
5865 return (GtkTreeViewColumn *)0x1;
5866 break;
5867
5868 case DROP_LEFT:
5869 /* find first column before column where we can drop */
5870 tmp_list = tree_view->priv->columns;
5871
5872 for (; tmp_list; tmp_list = tmp_list->next)
5873 if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5874 break;
5875
5876 if (!tmp_list || !tmp_list->prev)
5877 return (GtkTreeViewColumn *)0x1;
5878
5879 tmp_list = tmp_list->prev;
5880 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5881 tmp_list = tmp_list->prev;
5882
5883 while (tmp_list)
5884 {
5885 g_assert (tmp_list);
5886
5887 left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5888
5889 if (left_column &&
5890 gtk_tree_view_column_get_visible (left_column) == FALSE)
5891 {
5892 /*if (!tmp_list->prev)
5893 return (GtkTreeViewColumn *)0x1;
5894 */
5895 /*
5896 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->prev->data);
5897 tmp_list = tmp_list->prev->prev;
5898 continue;*/
5899
5900 cur_column = left_column;
5901 if (tmp_list)
5902 tmp_list = tmp_list->prev;
5903 continue;
5904 }
5905
5906 if (!tree_view->priv->column_drop_func)
5907 return left_column;
5908
5909 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5910 return left_column;
5911
5912 cur_column = left_column;
5913 tmp_list = tmp_list->prev;
5914 }
5915
5916 if (!tree_view->priv->column_drop_func)
5917 return NULL;
5918
5919 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
5920 return NULL;
5921 else
5922 return (GtkTreeViewColumn *)0x1;
5923 break;
5924
5925 case DROP_END:
5926 /* same as DROP_HOME case, but doing it backwards */
5927 tmp_list = g_list_last (tree_view->priv->columns);
5928 cur_column = NULL;
5929
5930 if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5931 return (GtkTreeViewColumn *)0x1;
5932
5933 while (tmp_list)
5934 {
5935 g_assert (tmp_list);
5936
5937 left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5938
5939 if (left_column &&
5940 gtk_tree_view_column_get_visible (left_column) == FALSE)
5941 {
5942 cur_column = left_column;
5943 tmp_list = tmp_list->prev;
5944 }
5945
5946 if (!tree_view->priv->column_drop_func)
5947 return left_column;
5948
5949 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5950 return left_column;
5951
5952 cur_column = left_column;
5953 tmp_list = tmp_list->prev;
5954 }
5955
5956 if (!tree_view->priv->column_drop_func)
5957 return NULL;
5958
5959 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
5960 return NULL;
5961 else
5962 return (GtkTreeViewColumn *)0x1;
5963 break;
5964 }
5965
5966 return (GtkTreeViewColumn *)0x1;
5967 }
5968
5969 static gboolean
gtk_tree_view_search_key_cancels_search(guint keyval)5970 gtk_tree_view_search_key_cancels_search (guint keyval)
5971 {
5972 return keyval == GDK_KEY_Escape
5973 || keyval == GDK_KEY_Tab
5974 || keyval == GDK_KEY_KP_Tab
5975 || keyval == GDK_KEY_ISO_Left_Tab;
5976 }
5977
5978 static gboolean
gtk_tree_view_key_press(GtkWidget * widget,GdkEventKey * event)5979 gtk_tree_view_key_press (GtkWidget *widget,
5980 GdkEventKey *event)
5981 {
5982 GtkTreeView *tree_view = (GtkTreeView *) widget;
5983 GtkWidget *button;
5984
5985 if (tree_view->priv->rubber_band_status)
5986 {
5987 if (event->keyval == GDK_KEY_Escape)
5988 gtk_tree_view_stop_rubber_band (tree_view);
5989
5990 return TRUE;
5991 }
5992
5993 if (tree_view->priv->in_column_drag)
5994 {
5995 if (event->keyval == GDK_KEY_Escape)
5996 gtk_gesture_set_state (GTK_GESTURE (tree_view->priv->column_drag_gesture),
5997 GTK_EVENT_SEQUENCE_DENIED);
5998 return TRUE;
5999 }
6000
6001 if (tree_view->priv->headers_visible)
6002 {
6003 GList *focus_column;
6004 gboolean rtl;
6005
6006 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6007
6008 for (focus_column = tree_view->priv->columns;
6009 focus_column;
6010 focus_column = focus_column->next)
6011 {
6012 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
6013
6014 button = gtk_tree_view_column_get_button (column);
6015 if (gtk_widget_has_focus (button))
6016 break;
6017 }
6018
6019 if (focus_column &&
6020 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
6021 (event->keyval == GDK_KEY_Left || event->keyval == GDK_KEY_KP_Left
6022 || event->keyval == GDK_KEY_Right || event->keyval == GDK_KEY_KP_Right))
6023 {
6024 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
6025 gint column_width;
6026
6027 if (!gtk_tree_view_column_get_resizable (column))
6028 {
6029 gtk_widget_error_bell (widget);
6030 return TRUE;
6031 }
6032
6033 column_width = gtk_tree_view_column_get_width (column);
6034
6035 if (event->keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left)
6036 || event->keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
6037 {
6038 column_width = MAX (column_width - 2, 0);
6039 }
6040 else if (event->keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
6041 || event->keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
6042 {
6043 column_width = column_width + 2;
6044 }
6045
6046 gtk_tree_view_column_set_fixed_width (column, column_width);
6047 gtk_tree_view_column_set_expand (column, FALSE);
6048 return TRUE;
6049 }
6050
6051 if (focus_column &&
6052 (event->state & GDK_MOD1_MASK) &&
6053 (event->keyval == GDK_KEY_Left || event->keyval == GDK_KEY_KP_Left
6054 || event->keyval == GDK_KEY_Right || event->keyval == GDK_KEY_KP_Right
6055 || event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_KP_Home
6056 || event->keyval == GDK_KEY_End || event->keyval == GDK_KEY_KP_End))
6057 {
6058 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
6059
6060 if (event->keyval == (rtl ? GDK_KEY_Right : GDK_KEY_Left)
6061 || event->keyval == (rtl ? GDK_KEY_KP_Right : GDK_KEY_KP_Left))
6062 {
6063 GtkTreeViewColumn *col;
6064 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT);
6065 if (col != (GtkTreeViewColumn *)0x1)
6066 gtk_tree_view_move_column_after (tree_view, column, col);
6067 else
6068 gtk_widget_error_bell (widget);
6069 }
6070 else if (event->keyval == (rtl ? GDK_KEY_Left : GDK_KEY_Right)
6071 || event->keyval == (rtl ? GDK_KEY_KP_Left : GDK_KEY_KP_Right))
6072 {
6073 GtkTreeViewColumn *col;
6074 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT);
6075 if (col != (GtkTreeViewColumn *)0x1)
6076 gtk_tree_view_move_column_after (tree_view, column, col);
6077 else
6078 gtk_widget_error_bell (widget);
6079 }
6080 else if (event->keyval == GDK_KEY_Home || event->keyval == GDK_KEY_KP_Home)
6081 {
6082 GtkTreeViewColumn *col;
6083 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME);
6084 if (col != (GtkTreeViewColumn *)0x1)
6085 gtk_tree_view_move_column_after (tree_view, column, col);
6086 else
6087 gtk_widget_error_bell (widget);
6088 }
6089 else if (event->keyval == GDK_KEY_End || event->keyval == GDK_KEY_KP_End)
6090 {
6091 GtkTreeViewColumn *col;
6092 col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END);
6093 if (col != (GtkTreeViewColumn *)0x1)
6094 gtk_tree_view_move_column_after (tree_view, column, col);
6095 else
6096 gtk_widget_error_bell (widget);
6097 }
6098
6099 return TRUE;
6100 }
6101 }
6102
6103 /* Chain up to the parent class. It handles the keybindings. */
6104 if (GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->key_press_event (widget, event))
6105 return TRUE;
6106
6107 if (tree_view->priv->search_entry_avoid_unhandled_binding)
6108 {
6109 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
6110 return FALSE;
6111 }
6112
6113 /* Initially, before the search window is visible, we pass the event to the
6114 * IM context of the search entry box. If it triggers a commit or a preedit,
6115 * we then show the search window without loosing tree view focus.
6116 * If the seach window is already visible, we forward the events to it,
6117 * keeping the focus on the tree view.
6118 */
6119 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
6120 && tree_view->priv->enable_search
6121 && !tree_view->priv->search_custom_entry_set
6122 && !gtk_tree_view_search_key_cancels_search (event->keyval))
6123 {
6124 GtkWidget *search_window;
6125
6126 gtk_tree_view_ensure_interactive_directory (tree_view);
6127
6128 search_window = tree_view->priv->search_window;
6129 if (!gtk_widget_is_visible (search_window))
6130 {
6131 GtkIMContext *im_context =
6132 _gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry));
6133
6134 tree_view->priv->imcontext_changed = FALSE;
6135 gtk_im_context_filter_keypress (im_context, event);
6136
6137 if (tree_view->priv->imcontext_changed)
6138 {
6139 GdkDevice *device;
6140
6141 device = gdk_event_get_device ((GdkEvent *) event);
6142 if (gtk_tree_view_real_start_interactive_search (tree_view,
6143 device,
6144 FALSE))
6145 {
6146 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6147 return TRUE;
6148 }
6149 else
6150 {
6151 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
6152 return FALSE;
6153 }
6154 }
6155 }
6156 else
6157 {
6158 GdkEvent *new_event;
6159 gulong popup_menu_id;
6160
6161 new_event = gdk_event_copy ((GdkEvent *) event);
6162 g_object_unref (((GdkEventKey *) new_event)->window);
6163 ((GdkEventKey *) new_event)->window =
6164 g_object_ref (gtk_widget_get_window (search_window));
6165 gtk_widget_realize (search_window);
6166
6167 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
6168 "popup-menu", G_CALLBACK (gtk_true),
6169 NULL);
6170
6171 /* Because we keep the focus on the treeview, we need to forward the
6172 * key events to the entry, when it is visible. */
6173 gtk_widget_event (search_window, new_event);
6174 gdk_event_free (new_event);
6175
6176 g_signal_handler_disconnect (tree_view->priv->search_entry,
6177 popup_menu_id);
6178
6179 }
6180 }
6181
6182 return FALSE;
6183 }
6184
6185 static gboolean
gtk_tree_view_key_release(GtkWidget * widget,GdkEventKey * event)6186 gtk_tree_view_key_release (GtkWidget *widget,
6187 GdkEventKey *event)
6188 {
6189 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
6190
6191 if (tree_view->priv->rubber_band_status)
6192 return TRUE;
6193
6194 return GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->key_release_event (widget, event);
6195 }
6196
6197 /* FIXME Is this function necessary? Can I get an enter_notify event
6198 * w/o either an expose event or a mouse motion event?
6199 */
6200 static gboolean
gtk_tree_view_enter_notify(GtkWidget * widget,GdkEventCrossing * event)6201 gtk_tree_view_enter_notify (GtkWidget *widget,
6202 GdkEventCrossing *event)
6203 {
6204 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
6205 GtkRBTree *tree;
6206 GtkRBNode *node;
6207 gint new_y;
6208
6209 /* Sanity check it */
6210 if (event->window != tree_view->priv->bin_window)
6211 return FALSE;
6212
6213 if (tree_view->priv->tree == NULL)
6214 return FALSE;
6215
6216 /* find the node internally */
6217 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
6218 if (new_y < 0)
6219 new_y = 0;
6220 _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
6221
6222 tree_view->priv->event_last_x = event->x;
6223 tree_view->priv->event_last_y = event->y;
6224
6225 if ((tree_view->priv->button_pressed_node == NULL) ||
6226 (tree_view->priv->button_pressed_node == node))
6227 prelight_or_select (tree_view, tree, node, event->x, event->y);
6228
6229 return TRUE;
6230 }
6231
6232 static gboolean
gtk_tree_view_leave_notify(GtkWidget * widget,GdkEventCrossing * event)6233 gtk_tree_view_leave_notify (GtkWidget *widget,
6234 GdkEventCrossing *event)
6235 {
6236 GtkTreeView *tree_view;
6237
6238 tree_view = GTK_TREE_VIEW (widget);
6239
6240 if (tree_view->priv->prelight_node)
6241 _gtk_tree_view_queue_draw_node (tree_view,
6242 tree_view->priv->prelight_tree,
6243 tree_view->priv->prelight_node,
6244 NULL);
6245
6246 tree_view->priv->event_last_x = -10000;
6247 tree_view->priv->event_last_y = -10000;
6248
6249 prelight_or_select (tree_view,
6250 NULL, NULL,
6251 -1000, -1000); /* coords not possibly over an arrow */
6252
6253 return TRUE;
6254 }
6255
6256
6257 static gint
gtk_tree_view_focus_out(GtkWidget * widget,GdkEventFocus * event)6258 gtk_tree_view_focus_out (GtkWidget *widget,
6259 GdkEventFocus *event)
6260 {
6261 GtkTreeView *tree_view;
6262
6263 tree_view = GTK_TREE_VIEW (widget);
6264
6265 gtk_widget_queue_draw (widget);
6266
6267 /* destroy interactive search dialog */
6268 if (tree_view->priv->search_window)
6269 gtk_tree_view_search_window_hide (tree_view->priv->search_window, tree_view,
6270 gdk_event_get_device ((GdkEvent *) event));
6271 return FALSE;
6272 }
6273
6274
6275 /* Incremental Reflow
6276 */
6277
6278 static void
gtk_tree_view_node_queue_redraw(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)6279 gtk_tree_view_node_queue_redraw (GtkTreeView *tree_view,
6280 GtkRBTree *tree,
6281 GtkRBNode *node)
6282 {
6283 GdkRectangle rect;
6284
6285 rect.x = 0;
6286 rect.y =
6287 _gtk_rbtree_node_find_offset (tree, node)
6288 - gtk_adjustment_get_value (tree_view->priv->vadjustment);
6289 rect.width = gtk_widget_get_allocated_width (GTK_WIDGET (tree_view));
6290 rect.height = GTK_RBNODE_GET_HEIGHT (node);
6291
6292 gdk_window_invalidate_rect (tree_view->priv->bin_window,
6293 &rect, TRUE);
6294 }
6295
6296 static gboolean
node_is_visible(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)6297 node_is_visible (GtkTreeView *tree_view,
6298 GtkRBTree *tree,
6299 GtkRBNode *node)
6300 {
6301 int y;
6302 int height;
6303
6304 y = _gtk_rbtree_node_find_offset (tree, node);
6305 height = gtk_tree_view_get_row_height (tree_view, node);
6306
6307 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
6308 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
6309 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
6310 return TRUE;
6311
6312 return FALSE;
6313 }
6314
6315 static gint
get_separator_height(GtkTreeView * tree_view)6316 get_separator_height (GtkTreeView *tree_view)
6317 {
6318 GtkStyleContext *context;
6319 GtkCssStyle *style;
6320 gdouble d;
6321 gint min_size;
6322
6323 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6324 gtk_style_context_save (context);
6325 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR);
6326
6327 style = gtk_style_context_lookup_style (context);
6328 d = _gtk_css_number_value_get
6329 (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_MIN_HEIGHT), 100);
6330
6331 if (d < 1)
6332 min_size = ceil (d);
6333 else
6334 min_size = floor (d);
6335
6336 gtk_style_context_restore (context);
6337
6338 return min_size;
6339 }
6340
6341 /* Returns TRUE if it updated the size
6342 */
6343 static gboolean
validate_row(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,GtkTreeIter * iter,GtkTreePath * path)6344 validate_row (GtkTreeView *tree_view,
6345 GtkRBTree *tree,
6346 GtkRBNode *node,
6347 GtkTreeIter *iter,
6348 GtkTreePath *path)
6349 {
6350 GtkTreeViewColumn *column;
6351 GtkStyleContext *context;
6352 GList *list, *first_column, *last_column;
6353 gint height = 0;
6354 gint horizontal_separator;
6355 gint vertical_separator;
6356 gint depth = gtk_tree_path_get_depth (path);
6357 gboolean retval = FALSE;
6358 gboolean is_separator = FALSE;
6359 gboolean draw_vgrid_lines, draw_hgrid_lines;
6360 gint grid_line_width;
6361 gint expander_size;
6362
6363 /* double check the row needs validating */
6364 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) &&
6365 ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6366 return FALSE;
6367
6368 is_separator = row_is_separator (tree_view, iter, NULL);
6369
6370 gtk_widget_style_get (GTK_WIDGET (tree_view),
6371 "horizontal-separator", &horizontal_separator,
6372 "vertical-separator", &vertical_separator,
6373 "grid-line-width", &grid_line_width,
6374 NULL);
6375
6376 draw_vgrid_lines =
6377 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
6378 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
6379 draw_hgrid_lines =
6380 tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
6381 || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
6382 expander_size = gtk_tree_view_get_expander_size (tree_view);
6383
6384 for (last_column = g_list_last (tree_view->priv->columns);
6385 last_column &&
6386 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (last_column->data)));
6387 last_column = last_column->prev)
6388 ;
6389
6390 for (first_column = g_list_first (tree_view->priv->columns);
6391 first_column &&
6392 !(gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (first_column->data)));
6393 first_column = first_column->next)
6394 ;
6395
6396 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6397 gtk_style_context_save (context);
6398 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
6399
6400 for (list = tree_view->priv->columns; list; list = list->next)
6401 {
6402 gint padding = 0;
6403 gint original_width;
6404 gint new_width;
6405 gint row_height;
6406
6407 column = list->data;
6408
6409 if (!gtk_tree_view_column_get_visible (column))
6410 continue;
6411
6412 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) &&
6413 !_gtk_tree_view_column_cell_get_dirty (column))
6414 continue;
6415
6416 original_width = _gtk_tree_view_column_get_requested_width (column);
6417
6418 gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter,
6419 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
6420 node->children?TRUE:FALSE);
6421 gtk_tree_view_column_cell_get_size (column,
6422 NULL, NULL, NULL,
6423 NULL, &row_height);
6424
6425 if (is_separator)
6426 {
6427 height = get_separator_height (tree_view);
6428 /* gtk_tree_view_get_row_height() assumes separator nodes are > 0 */
6429 height = MAX (height, 1);
6430 }
6431 else
6432 {
6433 row_height += vertical_separator;
6434 height = MAX (height, row_height);
6435 height = MAX (height, expander_size);
6436 }
6437
6438 if (gtk_tree_view_is_expander_column (tree_view, column))
6439 {
6440 padding += horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
6441
6442 if (gtk_tree_view_draw_expanders (tree_view))
6443 padding += depth * expander_size;
6444 }
6445 else
6446 padding += horizontal_separator;
6447
6448 if (draw_vgrid_lines)
6449 {
6450 if (list->data == first_column || list->data == last_column)
6451 padding += grid_line_width / 2.0;
6452 else
6453 padding += grid_line_width;
6454 }
6455
6456 /* Update the padding for the column */
6457 _gtk_tree_view_column_push_padding (column, padding);
6458 new_width = _gtk_tree_view_column_get_requested_width (column);
6459
6460 if (new_width > original_width)
6461 retval = TRUE;
6462 }
6463
6464 gtk_style_context_restore (context);
6465
6466 if (draw_hgrid_lines)
6467 height += grid_line_width;
6468
6469 if (height != GTK_RBNODE_GET_HEIGHT (node))
6470 {
6471 retval = TRUE;
6472 _gtk_rbtree_node_set_height (tree, node, height);
6473 }
6474 _gtk_rbtree_node_mark_valid (tree, node);
6475 tree_view->priv->post_validation_flag = TRUE;
6476
6477 return retval;
6478 }
6479
6480
6481 static void
validate_visible_area(GtkTreeView * tree_view)6482 validate_visible_area (GtkTreeView *tree_view)
6483 {
6484 GtkAllocation allocation;
6485 GtkTreePath *path = NULL;
6486 GtkTreePath *above_path = NULL;
6487 GtkTreeIter iter;
6488 GtkRBTree *tree = NULL;
6489 GtkRBNode *node = NULL;
6490 gboolean need_redraw = FALSE;
6491 gboolean size_changed = FALSE;
6492 gint total_height;
6493 gint area_above = 0;
6494 gint area_below = 0;
6495
6496 if (tree_view->priv->tree == NULL)
6497 return;
6498
6499 if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID) &&
6500 tree_view->priv->scroll_to_path == NULL)
6501 return;
6502
6503 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
6504 total_height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
6505
6506 if (total_height == 0)
6507 return;
6508
6509 /* First, we check to see if we need to scroll anywhere
6510 */
6511 if (tree_view->priv->scroll_to_path)
6512 {
6513 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
6514 if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node))
6515 {
6516 /* we are going to scroll, and will update dy */
6517 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6518 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6519 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6520 {
6521 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6522 if (validate_row (tree_view, tree, node, &iter, path))
6523 size_changed = TRUE;
6524 }
6525
6526 if (tree_view->priv->scroll_to_use_align)
6527 {
6528 gint height = gtk_tree_view_get_row_height (tree_view, node);
6529 area_above = (total_height - height) *
6530 tree_view->priv->scroll_to_row_align;
6531 area_below = total_height - area_above - height;
6532 area_above = MAX (area_above, 0);
6533 area_below = MAX (area_below, 0);
6534 }
6535 else
6536 {
6537 /* two cases:
6538 * 1) row not visible
6539 * 2) row visible
6540 */
6541 gint dy;
6542 gint height = gtk_tree_view_get_row_height (tree_view, node);
6543
6544 dy = _gtk_rbtree_node_find_offset (tree, node);
6545
6546 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
6547 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
6548 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
6549 {
6550 /* row visible: keep the row at the same position */
6551 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
6552 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
6553 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
6554 - dy - height;
6555 }
6556 else
6557 {
6558 /* row not visible */
6559 if (dy >= 0
6560 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
6561 {
6562 /* row at the beginning -- fixed */
6563 area_above = dy;
6564 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
6565 - area_above - height;
6566 }
6567 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
6568 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
6569 {
6570 /* row at the end -- fixed */
6571 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
6572 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
6573 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
6574 area_above - height;
6575
6576 if (area_below < 0)
6577 {
6578 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
6579 area_below = 0;
6580 }
6581 }
6582 else
6583 {
6584 /* row somewhere in the middle, bring it to the top
6585 * of the view
6586 */
6587 area_above = 0;
6588 area_below = total_height - height;
6589 }
6590 }
6591 }
6592 }
6593 else
6594 /* the scroll to isn't valid; ignore it.
6595 */
6596 {
6597 if (tree_view->priv->scroll_to_path && !path)
6598 {
6599 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
6600 tree_view->priv->scroll_to_path = NULL;
6601 }
6602 if (path)
6603 gtk_tree_path_free (path);
6604 path = NULL;
6605 }
6606 }
6607
6608 /* We didn't have a scroll_to set, so we just handle things normally
6609 */
6610 if (path == NULL)
6611 {
6612 gint offset;
6613
6614 offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
6615 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
6616 &tree, &node);
6617 if (node == NULL)
6618 {
6619 /* In this case, nothing has been validated */
6620 path = gtk_tree_path_new_first ();
6621 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
6622 }
6623 else
6624 {
6625 path = _gtk_tree_path_new_from_rbtree (tree, node);
6626 total_height += offset;
6627 }
6628
6629 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6630
6631 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6632 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6633 {
6634 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6635 if (validate_row (tree_view, tree, node, &iter, path))
6636 size_changed = TRUE;
6637 }
6638 area_above = 0;
6639 area_below = total_height - gtk_tree_view_get_row_height (tree_view, node);
6640 }
6641
6642 above_path = gtk_tree_path_copy (path);
6643
6644 /* if we do not validate any row above the new top_row, we will make sure
6645 * that the row immediately above top_row has been validated. (if we do not
6646 * do this, _gtk_rbtree_find_offset will find the row above top_row, because
6647 * when invalidated that row's height will be zero. and this will mess up
6648 * scrolling).
6649 */
6650 if (area_above == 0)
6651 {
6652 GtkRBTree *tmptree;
6653 GtkRBNode *tmpnode;
6654
6655 _gtk_tree_view_find_node (tree_view, above_path, &tmptree, &tmpnode);
6656 _gtk_rbtree_prev_full (tmptree, tmpnode, &tmptree, &tmpnode);
6657
6658 if (tmpnode)
6659 {
6660 GtkTreePath *tmppath;
6661 GtkTreeIter tmpiter;
6662
6663 tmppath = _gtk_tree_path_new_from_rbtree (tmptree, tmpnode);
6664 gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, tmppath);
6665
6666 if (GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_INVALID) ||
6667 GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_COLUMN_INVALID))
6668 {
6669 _gtk_tree_view_queue_draw_node (tree_view, tmptree, tmpnode, NULL);
6670 if (validate_row (tree_view, tmptree, tmpnode, &tmpiter, tmppath))
6671 size_changed = TRUE;
6672 }
6673
6674 gtk_tree_path_free (tmppath);
6675 }
6676 }
6677
6678 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
6679 * backwards is much slower then forward, as there is no iter_prev function.
6680 * We go forwards first in case we run out of tree. Then we go backwards to
6681 * fill out the top.
6682 */
6683 while (node && area_below > 0)
6684 {
6685 if (node->children)
6686 {
6687 GtkTreeIter parent = iter;
6688 gboolean has_child;
6689
6690 tree = node->children;
6691 node = _gtk_rbtree_first (tree);
6692
6693 has_child = gtk_tree_model_iter_children (tree_view->priv->model,
6694 &iter,
6695 &parent);
6696 TREE_VIEW_INTERNAL_ASSERT_VOID (has_child);
6697 gtk_tree_path_down (path);
6698 }
6699 else
6700 {
6701 gboolean done = FALSE;
6702 do
6703 {
6704 node = _gtk_rbtree_next (tree, node);
6705 if (node != NULL)
6706 {
6707 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
6708 done = TRUE;
6709 gtk_tree_path_next (path);
6710
6711 /* Sanity Check! */
6712 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
6713 }
6714 else
6715 {
6716 GtkTreeIter parent_iter = iter;
6717 gboolean has_parent;
6718
6719 node = tree->parent_node;
6720 tree = tree->parent_tree;
6721 if (tree == NULL)
6722 break;
6723 has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
6724 &iter,
6725 &parent_iter);
6726 gtk_tree_path_up (path);
6727
6728 /* Sanity check */
6729 TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
6730 }
6731 }
6732 while (!done);
6733 }
6734
6735 if (!node)
6736 break;
6737
6738 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6739 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6740 {
6741 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6742 if (validate_row (tree_view, tree, node, &iter, path))
6743 size_changed = TRUE;
6744 }
6745
6746 area_below -= gtk_tree_view_get_row_height (tree_view, node);
6747 }
6748 gtk_tree_path_free (path);
6749
6750 /* If we ran out of tree, and have extra area_below left, we need to add it
6751 * to area_above */
6752 if (area_below > 0)
6753 area_above += area_below;
6754
6755 _gtk_tree_view_find_node (tree_view, above_path, &tree, &node);
6756
6757 /* We walk backwards */
6758 while (area_above > 0)
6759 {
6760 _gtk_rbtree_prev_full (tree, node, &tree, &node);
6761
6762 /* Always find the new path in the tree. We cannot just assume
6763 * a gtk_tree_path_prev() is enough here, as there might be children
6764 * in between this node and the previous sibling node. If this
6765 * appears to be a performance hotspot in profiles, we can look into
6766 * intrigate logic for keeping path, node and iter in sync like
6767 * we do for forward walks. (Which will be hard because of the lacking
6768 * iter_prev).
6769 */
6770
6771 if (node == NULL)
6772 break;
6773
6774 gtk_tree_path_free (above_path);
6775 above_path = _gtk_tree_path_new_from_rbtree (tree, node);
6776
6777 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
6778
6779 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6780 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6781 {
6782 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6783 if (validate_row (tree_view, tree, node, &iter, above_path))
6784 size_changed = TRUE;
6785 }
6786 area_above -= gtk_tree_view_get_row_height (tree_view, node);
6787 }
6788
6789 /* if we scrolled to a path, we need to set the dy here,
6790 * and sync the top row accordingly
6791 */
6792 if (tree_view->priv->scroll_to_path)
6793 {
6794 gtk_tree_view_set_top_row (tree_view, above_path, -area_above);
6795 gtk_tree_view_top_row_to_dy (tree_view);
6796
6797 need_redraw = TRUE;
6798 }
6799 else if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
6800 {
6801 /* when we are not scrolling, we should never set dy to something
6802 * else than zero. we update top_row to be in sync with dy = 0.
6803 */
6804 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
6805 gtk_tree_view_dy_to_top_row (tree_view);
6806 }
6807 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > gtk_tree_view_get_height (tree_view))
6808 {
6809 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
6810 gtk_tree_view_dy_to_top_row (tree_view);
6811 }
6812 else
6813 gtk_tree_view_top_row_to_dy (tree_view);
6814
6815 /* update width/height and queue a resize */
6816 if (size_changed)
6817 {
6818 GtkRequisition requisition;
6819
6820 /* We temporarily guess a size, under the assumption that it will be the
6821 * same when we get our next size_allocate. If we don't do this, we'll be
6822 * in an inconsistent state if we call top_row_to_dy. */
6823
6824 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view),
6825 &requisition, NULL);
6826 gtk_adjustment_set_upper (tree_view->priv->hadjustment,
6827 MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), requisition.width));
6828 gtk_adjustment_set_upper (tree_view->priv->vadjustment,
6829 MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), requisition.height));
6830 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6831 }
6832
6833 if (tree_view->priv->scroll_to_path)
6834 {
6835 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
6836 tree_view->priv->scroll_to_path = NULL;
6837 }
6838
6839 if (above_path)
6840 gtk_tree_path_free (above_path);
6841
6842 if (tree_view->priv->scroll_to_column)
6843 {
6844 tree_view->priv->scroll_to_column = NULL;
6845 }
6846 if (need_redraw)
6847 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
6848 }
6849
6850 static void
initialize_fixed_height_mode(GtkTreeView * tree_view)6851 initialize_fixed_height_mode (GtkTreeView *tree_view)
6852 {
6853 if (!tree_view->priv->tree)
6854 return;
6855
6856 if (tree_view->priv->fixed_height < 0)
6857 {
6858 GtkTreeIter iter;
6859 GtkTreePath *path;
6860
6861 GtkRBTree *tree = NULL;
6862 GtkRBNode *node = NULL;
6863
6864 tree = tree_view->priv->tree;
6865 node = tree->root;
6866
6867 path = _gtk_tree_path_new_from_rbtree (tree, node);
6868 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6869
6870 validate_row (tree_view, tree, node, &iter, path);
6871
6872 gtk_tree_path_free (path);
6873
6874 tree_view->priv->fixed_height = gtk_tree_view_get_row_height (tree_view, node);
6875 }
6876
6877 _gtk_rbtree_set_fixed_height (tree_view->priv->tree,
6878 tree_view->priv->fixed_height, TRUE);
6879 }
6880
6881 /* Our strategy for finding nodes to validate is a little convoluted. We find
6882 * the left-most uninvalidated node. We then try walking right, validating
6883 * nodes. Once we find a valid node, we repeat the previous process of finding
6884 * the first invalid node.
6885 */
6886
6887 static gboolean
do_validate_rows(GtkTreeView * tree_view,gboolean queue_resize)6888 do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize)
6889 {
6890 static gboolean prevent_recursion_hack = FALSE;
6891
6892 GtkRBTree *tree = NULL;
6893 GtkRBNode *node = NULL;
6894 gboolean validated_area = FALSE;
6895 gint retval = TRUE;
6896 GtkTreePath *path = NULL;
6897 GtkTreeIter iter;
6898 GTimer *timer;
6899 gint i = 0;
6900
6901 gint y = -1;
6902 gint prev_height = -1;
6903 gboolean fixed_height = TRUE;
6904
6905 g_assert (tree_view);
6906
6907 /* prevent infinite recursion via get_preferred_width() */
6908 if (prevent_recursion_hack)
6909 return FALSE;
6910
6911 if (tree_view->priv->tree == NULL)
6912 return FALSE;
6913
6914 if (tree_view->priv->fixed_height_mode)
6915 {
6916 if (tree_view->priv->fixed_height < 0)
6917 initialize_fixed_height_mode (tree_view);
6918
6919 return FALSE;
6920 }
6921
6922 timer = g_timer_new ();
6923 g_timer_start (timer);
6924
6925 do
6926 {
6927 gboolean changed = FALSE;
6928
6929 if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID))
6930 {
6931 retval = FALSE;
6932 goto done;
6933 }
6934
6935 if (path != NULL)
6936 {
6937 node = _gtk_rbtree_next (tree, node);
6938 if (node != NULL)
6939 {
6940 TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_next (tree_view->priv->model, &iter), FALSE);
6941 gtk_tree_path_next (path);
6942 }
6943 else
6944 {
6945 gtk_tree_path_free (path);
6946 path = NULL;
6947 }
6948 }
6949
6950 if (path == NULL)
6951 {
6952 tree = tree_view->priv->tree;
6953 node = tree_view->priv->tree->root;
6954
6955 g_assert (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_DESCENDANTS_INVALID));
6956
6957 do
6958 {
6959 if (!_gtk_rbtree_is_nil (node->left) &&
6960 GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID))
6961 {
6962 node = node->left;
6963 }
6964 else if (!_gtk_rbtree_is_nil (node->right) &&
6965 GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID))
6966 {
6967 node = node->right;
6968 }
6969 else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6970 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6971 {
6972 break;
6973 }
6974 else if (node->children != NULL)
6975 {
6976 tree = node->children;
6977 node = tree->root;
6978 }
6979 else
6980 /* RBTree corruption! All bad */
6981 g_assert_not_reached ();
6982 }
6983 while (TRUE);
6984 path = _gtk_tree_path_new_from_rbtree (tree, node);
6985 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6986 }
6987
6988 changed = validate_row (tree_view, tree, node, &iter, path);
6989 validated_area = changed || validated_area;
6990
6991 if (changed)
6992 {
6993 gint offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
6994
6995 if (y == -1 || y > offset)
6996 y = offset;
6997 }
6998
6999 if (!tree_view->priv->fixed_height_check)
7000 {
7001 gint height;
7002
7003 height = gtk_tree_view_get_row_height (tree_view, node);
7004 if (prev_height < 0)
7005 prev_height = height;
7006 else if (prev_height != height)
7007 fixed_height = FALSE;
7008 }
7009
7010 i++;
7011 }
7012 while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.);
7013
7014 if (!tree_view->priv->fixed_height_check)
7015 {
7016 if (fixed_height)
7017 _gtk_rbtree_set_fixed_height (tree_view->priv->tree, prev_height, FALSE);
7018
7019 tree_view->priv->fixed_height_check = 1;
7020 }
7021
7022 done:
7023 if (validated_area)
7024 {
7025 GtkRequisition requisition;
7026 gint dummy;
7027
7028 /* We temporarily guess a size, under the assumption that it will be the
7029 * same when we get our next size_allocate. If we don't do this, we'll be
7030 * in an inconsistent state when we call top_row_to_dy. */
7031
7032 /* FIXME: This is called from size_request, for some reason it is not infinitely
7033 * recursing, we cannot call gtk_widget_get_preferred_size() here because that's
7034 * not allowed (from inside ->get_preferred_width/height() implementations, one
7035 * should call the vfuncs directly). However what is desired here is the full
7036 * size including any margins and limited by any alignment (i.e. after
7037 * GtkWidget:adjust_size_request() is called).
7038 *
7039 * Currently bypassing this but the real solution is to not update the scroll adjustments
7040 * untill we've recieved an allocation (never update scroll adjustments from size-requests).
7041 */
7042 prevent_recursion_hack = TRUE;
7043 gtk_tree_view_get_preferred_width (GTK_WIDGET (tree_view), &requisition.width, &dummy);
7044 gtk_tree_view_get_preferred_height (GTK_WIDGET (tree_view), &requisition.height, &dummy);
7045 prevent_recursion_hack = FALSE;
7046
7047 /* If rows above the current position have changed height, this has
7048 * affected the current view and thus needs a redraw.
7049 */
7050 if (y != -1 && y < gtk_adjustment_get_value (tree_view->priv->vadjustment))
7051 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7052
7053 gtk_adjustment_set_upper (tree_view->priv->hadjustment,
7054 MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), requisition.width));
7055 gtk_adjustment_set_upper (tree_view->priv->vadjustment,
7056 MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), requisition.height));
7057
7058 if (queue_resize)
7059 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7060 }
7061
7062 if (path) gtk_tree_path_free (path);
7063 g_timer_destroy (timer);
7064
7065 if (!retval && gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
7066 update_prelight (tree_view,
7067 tree_view->priv->event_last_x,
7068 tree_view->priv->event_last_y);
7069
7070 return retval;
7071 }
7072
7073 static void
disable_adjustment_animation(GtkTreeView * tree_view)7074 disable_adjustment_animation (GtkTreeView *tree_view)
7075 {
7076 gtk_adjustment_enable_animation (tree_view->priv->vadjustment,
7077 NULL,
7078 gtk_adjustment_get_animation_duration (tree_view->priv->vadjustment));
7079 }
7080
7081 static void
maybe_reenable_adjustment_animation(GtkTreeView * tree_view)7082 maybe_reenable_adjustment_animation (GtkTreeView *tree_view)
7083 {
7084 if (tree_view->priv->presize_handler_tick_cb != 0 ||
7085 tree_view->priv->validate_rows_timer != 0)
7086 return;
7087
7088 gtk_adjustment_enable_animation (tree_view->priv->vadjustment,
7089 gtk_widget_get_frame_clock (GTK_WIDGET (tree_view)),
7090 gtk_adjustment_get_animation_duration (tree_view->priv->vadjustment));
7091 }
7092
7093 static gboolean
do_presize_handler(GtkTreeView * tree_view)7094 do_presize_handler (GtkTreeView *tree_view)
7095 {
7096 if (tree_view->priv->mark_rows_col_dirty)
7097 {
7098 if (tree_view->priv->tree)
7099 _gtk_rbtree_column_invalid (tree_view->priv->tree);
7100 tree_view->priv->mark_rows_col_dirty = FALSE;
7101 }
7102 validate_visible_area (tree_view);
7103 if (tree_view->priv->presize_handler_tick_cb != 0)
7104 {
7105 gtk_widget_remove_tick_callback (GTK_WIDGET (tree_view), tree_view->priv->presize_handler_tick_cb);
7106 tree_view->priv->presize_handler_tick_cb = 0;
7107 }
7108
7109 if (tree_view->priv->fixed_height_mode)
7110 {
7111 GtkRequisition requisition;
7112
7113 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view),
7114 &requisition, NULL);
7115
7116 gtk_adjustment_set_upper (tree_view->priv->hadjustment,
7117 MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), requisition.width));
7118 gtk_adjustment_set_upper (tree_view->priv->vadjustment,
7119 MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), requisition.height));
7120 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7121 }
7122
7123 maybe_reenable_adjustment_animation (tree_view);
7124
7125 return FALSE;
7126 }
7127
7128 static gboolean
presize_handler_callback(GtkWidget * widget,GdkFrameClock * clock,gpointer unused)7129 presize_handler_callback (GtkWidget *widget,
7130 GdkFrameClock *clock,
7131 gpointer unused)
7132 {
7133 do_presize_handler (GTK_TREE_VIEW (widget));
7134
7135 return G_SOURCE_REMOVE;
7136 }
7137
7138 static gboolean
validate_rows(GtkTreeView * tree_view)7139 validate_rows (GtkTreeView *tree_view)
7140 {
7141 gboolean retval;
7142
7143 if (tree_view->priv->presize_handler_tick_cb)
7144 {
7145 do_presize_handler (tree_view);
7146 return G_SOURCE_CONTINUE;
7147 }
7148
7149 retval = do_validate_rows (tree_view, TRUE);
7150
7151 if (! retval && tree_view->priv->validate_rows_timer)
7152 {
7153 g_source_remove (tree_view->priv->validate_rows_timer);
7154 tree_view->priv->validate_rows_timer = 0;
7155 maybe_reenable_adjustment_animation (tree_view);
7156 }
7157
7158 return retval;
7159 }
7160
7161 static void
install_presize_handler(GtkTreeView * tree_view)7162 install_presize_handler (GtkTreeView *tree_view)
7163 {
7164 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7165 return;
7166
7167 disable_adjustment_animation (tree_view);
7168
7169 if (! tree_view->priv->presize_handler_tick_cb)
7170 {
7171 tree_view->priv->presize_handler_tick_cb =
7172 gtk_widget_add_tick_callback (GTK_WIDGET (tree_view), presize_handler_callback, NULL, NULL);
7173 }
7174 if (! tree_view->priv->validate_rows_timer)
7175 {
7176 tree_view->priv->validate_rows_timer =
7177 gdk_threads_add_idle_full (GTK_TREE_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows, tree_view, NULL);
7178 g_source_set_name_by_id (tree_view->priv->validate_rows_timer, "[gtk+] validate_rows");
7179 }
7180 }
7181
7182 static gboolean
scroll_sync_handler(GtkTreeView * tree_view)7183 scroll_sync_handler (GtkTreeView *tree_view)
7184 {
7185 if (gtk_tree_view_get_height (tree_view) <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
7186 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
7187 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
7188 gtk_tree_view_top_row_to_dy (tree_view);
7189 else
7190 gtk_tree_view_dy_to_top_row (tree_view);
7191
7192 tree_view->priv->scroll_sync_timer = 0;
7193
7194 return FALSE;
7195 }
7196
7197 static void
install_scroll_sync_handler(GtkTreeView * tree_view)7198 install_scroll_sync_handler (GtkTreeView *tree_view)
7199 {
7200 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7201 return;
7202
7203 if (!tree_view->priv->scroll_sync_timer)
7204 {
7205 tree_view->priv->scroll_sync_timer =
7206 gdk_threads_add_idle_full (GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
7207 g_source_set_name_by_id (tree_view->priv->scroll_sync_timer, "[gtk+] scroll_sync_handler");
7208 }
7209 }
7210
7211 static void
gtk_tree_view_set_top_row(GtkTreeView * tree_view,GtkTreePath * path,gint offset)7212 gtk_tree_view_set_top_row (GtkTreeView *tree_view,
7213 GtkTreePath *path,
7214 gint offset)
7215 {
7216 gtk_tree_row_reference_free (tree_view->priv->top_row);
7217
7218 if (!path)
7219 {
7220 tree_view->priv->top_row = NULL;
7221 tree_view->priv->top_row_dy = 0;
7222 }
7223 else
7224 {
7225 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
7226 tree_view->priv->top_row_dy = offset;
7227 }
7228 }
7229
7230 /* Always call this iff dy is in the visible range. If the tree is empty, then
7231 * it’s set to be NULL, and top_row_dy is 0;
7232 */
7233 static void
gtk_tree_view_dy_to_top_row(GtkTreeView * tree_view)7234 gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view)
7235 {
7236 gint offset;
7237 GtkTreePath *path;
7238 GtkRBTree *tree;
7239 GtkRBNode *node;
7240
7241 if (tree_view->priv->tree == NULL)
7242 {
7243 gtk_tree_view_set_top_row (tree_view, NULL, 0);
7244 }
7245 else
7246 {
7247 offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
7248 tree_view->priv->dy,
7249 &tree, &node);
7250
7251 if (tree == NULL)
7252 {
7253 gtk_tree_view_set_top_row (tree_view, NULL, 0);
7254 }
7255 else
7256 {
7257 path = _gtk_tree_path_new_from_rbtree (tree, node);
7258 gtk_tree_view_set_top_row (tree_view, path, offset);
7259 gtk_tree_path_free (path);
7260 }
7261 }
7262 }
7263
7264 static void
gtk_tree_view_top_row_to_dy(GtkTreeView * tree_view)7265 gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view)
7266 {
7267 GtkTreePath *path;
7268 GtkRBTree *tree;
7269 GtkRBNode *node;
7270 int new_dy;
7271
7272 /* Avoid recursive calls */
7273 if (tree_view->priv->in_top_row_to_dy)
7274 return;
7275
7276 if (tree_view->priv->top_row)
7277 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
7278 else
7279 path = NULL;
7280
7281 if (!path)
7282 tree = NULL;
7283 else
7284 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
7285
7286 if (path)
7287 gtk_tree_path_free (path);
7288
7289 if (tree == NULL)
7290 {
7291 /* keep dy and set new toprow */
7292 gtk_tree_row_reference_free (tree_view->priv->top_row);
7293 tree_view->priv->top_row = NULL;
7294 tree_view->priv->top_row_dy = 0;
7295 /* DO NOT install the idle handler */
7296 gtk_tree_view_dy_to_top_row (tree_view);
7297 return;
7298 }
7299
7300 if (gtk_tree_view_get_row_height (tree_view, node)
7301 < tree_view->priv->top_row_dy)
7302 {
7303 /* new top row -- do NOT install the idle handler */
7304 gtk_tree_view_dy_to_top_row (tree_view);
7305 return;
7306 }
7307
7308 new_dy = _gtk_rbtree_node_find_offset (tree, node);
7309 new_dy += tree_view->priv->top_row_dy;
7310
7311 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > gtk_tree_view_get_height (tree_view))
7312 new_dy = gtk_tree_view_get_height (tree_view) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
7313
7314 new_dy = MAX (0, new_dy);
7315
7316 tree_view->priv->in_top_row_to_dy = TRUE;
7317 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
7318 tree_view->priv->in_top_row_to_dy = FALSE;
7319 }
7320
7321
7322 void
_gtk_tree_view_install_mark_rows_col_dirty(GtkTreeView * tree_view,gboolean install_handler)7323 _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view,
7324 gboolean install_handler)
7325 {
7326 tree_view->priv->mark_rows_col_dirty = TRUE;
7327
7328 if (install_handler)
7329 install_presize_handler (tree_view);
7330 }
7331
7332 /*
7333 * This function works synchronously (due to the while (validate_rows...)
7334 * loop).
7335 *
7336 * There was a check for column_type != GTK_TREE_VIEW_COLUMN_AUTOSIZE
7337 * here. You now need to check that yourself.
7338 */
7339 void
_gtk_tree_view_column_autosize(GtkTreeView * tree_view,GtkTreeViewColumn * column)7340 _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
7341 GtkTreeViewColumn *column)
7342 {
7343 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
7344 g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
7345
7346 _gtk_tree_view_column_cell_set_dirty (column, FALSE);
7347
7348 do_presize_handler (tree_view);
7349 while (validate_rows (tree_view));
7350
7351 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7352 }
7353
7354 /* Drag-and-drop */
7355
7356 static void
set_source_row(GdkDragContext * context,GtkTreeModel * model,GtkTreePath * source_row)7357 set_source_row (GdkDragContext *context,
7358 GtkTreeModel *model,
7359 GtkTreePath *source_row)
7360 {
7361 g_object_set_data_full (G_OBJECT (context),
7362 I_("gtk-tree-view-source-row"),
7363 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
7364 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
7365 }
7366
7367 static GtkTreePath*
get_source_row(GdkDragContext * context)7368 get_source_row (GdkDragContext *context)
7369 {
7370 GtkTreeRowReference *ref =
7371 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
7372
7373 if (ref)
7374 return gtk_tree_row_reference_get_path (ref);
7375 else
7376 return NULL;
7377 }
7378
7379 typedef struct
7380 {
7381 GtkTreeRowReference *dest_row;
7382 guint path_down_mode : 1;
7383 guint empty_view_drop : 1;
7384 guint drop_append_mode : 1;
7385 }
7386 DestRow;
7387
7388 static void
dest_row_free(gpointer data)7389 dest_row_free (gpointer data)
7390 {
7391 DestRow *dr = (DestRow *)data;
7392
7393 gtk_tree_row_reference_free (dr->dest_row);
7394 g_slice_free (DestRow, dr);
7395 }
7396
7397 static void
set_dest_row(GdkDragContext * context,GtkTreeModel * model,GtkTreePath * dest_row,gboolean path_down_mode,gboolean empty_view_drop,gboolean drop_append_mode)7398 set_dest_row (GdkDragContext *context,
7399 GtkTreeModel *model,
7400 GtkTreePath *dest_row,
7401 gboolean path_down_mode,
7402 gboolean empty_view_drop,
7403 gboolean drop_append_mode)
7404 {
7405 DestRow *dr;
7406
7407 if (!dest_row)
7408 {
7409 g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"),
7410 NULL, NULL);
7411 return;
7412 }
7413
7414 dr = g_slice_new (DestRow);
7415
7416 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
7417 dr->path_down_mode = path_down_mode != FALSE;
7418 dr->empty_view_drop = empty_view_drop != FALSE;
7419 dr->drop_append_mode = drop_append_mode != FALSE;
7420
7421 g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"),
7422 dr, (GDestroyNotify) dest_row_free);
7423 }
7424
7425 static GtkTreePath*
get_dest_row(GdkDragContext * context,gboolean * path_down_mode)7426 get_dest_row (GdkDragContext *context,
7427 gboolean *path_down_mode)
7428 {
7429 DestRow *dr =
7430 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
7431
7432 if (dr)
7433 {
7434 GtkTreePath *path = NULL;
7435
7436 if (path_down_mode)
7437 *path_down_mode = dr->path_down_mode;
7438
7439 if (dr->dest_row)
7440 path = gtk_tree_row_reference_get_path (dr->dest_row);
7441 else if (dr->empty_view_drop)
7442 path = gtk_tree_path_new_from_indices (0, -1);
7443 else
7444 path = NULL;
7445
7446 if (path && dr->drop_append_mode)
7447 gtk_tree_path_next (path);
7448
7449 return path;
7450 }
7451 else
7452 return NULL;
7453 }
7454
7455 /* Get/set whether drag_motion requested the drag data and
7456 * drag_data_received should thus not actually insert the data,
7457 * since the data doesn’t result from a drop.
7458 */
7459 static void
set_status_pending(GdkDragContext * context,GdkDragAction suggested_action)7460 set_status_pending (GdkDragContext *context,
7461 GdkDragAction suggested_action)
7462 {
7463 g_object_set_data (G_OBJECT (context),
7464 I_("gtk-tree-view-status-pending"),
7465 GINT_TO_POINTER (suggested_action));
7466 }
7467
7468 static GdkDragAction
get_status_pending(GdkDragContext * context)7469 get_status_pending (GdkDragContext *context)
7470 {
7471 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
7472 "gtk-tree-view-status-pending"));
7473 }
7474
7475 static TreeViewDragInfo*
get_info(GtkTreeView * tree_view)7476 get_info (GtkTreeView *tree_view)
7477 {
7478 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
7479 }
7480
7481 static void
destroy_info(TreeViewDragInfo * di)7482 destroy_info (TreeViewDragInfo *di)
7483 {
7484 g_slice_free (TreeViewDragInfo, di);
7485 }
7486
7487 static TreeViewDragInfo*
ensure_info(GtkTreeView * tree_view)7488 ensure_info (GtkTreeView *tree_view)
7489 {
7490 TreeViewDragInfo *di;
7491
7492 di = get_info (tree_view);
7493
7494 if (di == NULL)
7495 {
7496 di = g_slice_new0 (TreeViewDragInfo);
7497
7498 g_object_set_data_full (G_OBJECT (tree_view),
7499 I_("gtk-tree-view-drag-info"),
7500 di,
7501 (GDestroyNotify) destroy_info);
7502 }
7503
7504 return di;
7505 }
7506
7507 static void
remove_info(GtkTreeView * tree_view)7508 remove_info (GtkTreeView *tree_view)
7509 {
7510 g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL);
7511 }
7512
7513 #if 0
7514 static gint
7515 drag_scan_timeout (gpointer data)
7516 {
7517 GtkTreeView *tree_view;
7518 gint x, y;
7519 GdkModifierType state;
7520 GtkTreePath *path = NULL;
7521 GtkTreeViewColumn *column = NULL;
7522 GdkRectangle visible_rect;
7523 GdkSeat *seat;
7524
7525 gdk_threads_enter ();
7526
7527 tree_view = GTK_TREE_VIEW (data);
7528
7529 seat = gdk_display_get_default_seat (gtk_widget_get_display (GTK_WIDGET (tree_view)));
7530 gdk_window_get_device_position (tree_view->priv->bin_window,
7531 gdk_seat_get_pointer (seat),
7532 &x, &y, &state);
7533
7534 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
7535
7536 /* See if we are near the edge. */
7537 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
7538 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
7539 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
7540 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
7541 {
7542 gtk_tree_view_get_path_at_pos (tree_view,
7543 tree_view->priv->bin_window,
7544 x, y,
7545 &path,
7546 &column,
7547 NULL,
7548 NULL);
7549
7550 if (path != NULL)
7551 {
7552 gtk_tree_view_scroll_to_cell (tree_view,
7553 path,
7554 column,
7555 TRUE,
7556 0.5, 0.5);
7557
7558 gtk_tree_path_free (path);
7559 }
7560 }
7561
7562 gdk_threads_leave ();
7563
7564 return TRUE;
7565 }
7566 #endif /* 0 */
7567
7568 static void
add_scroll_timeout(GtkTreeView * tree_view)7569 add_scroll_timeout (GtkTreeView *tree_view)
7570 {
7571 if (tree_view->priv->scroll_timeout == 0)
7572 {
7573 tree_view->priv->scroll_timeout =
7574 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
7575 g_source_set_name_by_id (tree_view->priv->scroll_timeout, "[gtk+] scroll_row_timeout");
7576 }
7577 }
7578
7579 static void
remove_scroll_timeout(GtkTreeView * tree_view)7580 remove_scroll_timeout (GtkTreeView *tree_view)
7581 {
7582 if (tree_view->priv->scroll_timeout != 0)
7583 {
7584 g_source_remove (tree_view->priv->scroll_timeout);
7585 tree_view->priv->scroll_timeout = 0;
7586 }
7587 }
7588
7589 static gboolean
check_model_dnd(GtkTreeModel * model,GType required_iface,const gchar * signal)7590 check_model_dnd (GtkTreeModel *model,
7591 GType required_iface,
7592 const gchar *signal)
7593 {
7594 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
7595 {
7596 g_warning ("You must override the default '%s' handler "
7597 "on GtkTreeView when using models that don't support "
7598 "the %s interface and enabling drag-and-drop. The simplest way to do this "
7599 "is to connect to '%s' and call "
7600 "g_signal_stop_emission_by_name() in your signal handler to prevent "
7601 "the default handler from running. Look at the source code "
7602 "for the default handler in gtktreeview.c to get an idea what "
7603 "your handler should do. (gtktreeview.c is in the GTK source "
7604 "code.) If you're using GTK from a language other than C, "
7605 "there may be a more natural way to override default handlers, e.g. via derivation.",
7606 signal, g_type_name (required_iface), signal);
7607 return FALSE;
7608 }
7609 else
7610 return TRUE;
7611 }
7612
7613 static void
remove_open_timeout(GtkTreeView * tree_view)7614 remove_open_timeout (GtkTreeView *tree_view)
7615 {
7616 if (tree_view->priv->open_dest_timeout != 0)
7617 {
7618 g_source_remove (tree_view->priv->open_dest_timeout);
7619 tree_view->priv->open_dest_timeout = 0;
7620 }
7621 }
7622
7623
7624 static gint
open_row_timeout(gpointer data)7625 open_row_timeout (gpointer data)
7626 {
7627 GtkTreeView *tree_view = data;
7628 GtkTreePath *dest_path = NULL;
7629 GtkTreeViewDropPosition pos;
7630 gboolean result = FALSE;
7631
7632 gtk_tree_view_get_drag_dest_row (tree_view,
7633 &dest_path,
7634 &pos);
7635
7636 if (dest_path &&
7637 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7638 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
7639 {
7640 gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
7641 tree_view->priv->open_dest_timeout = 0;
7642
7643 gtk_tree_path_free (dest_path);
7644 }
7645 else
7646 {
7647 if (dest_path)
7648 gtk_tree_path_free (dest_path);
7649
7650 result = TRUE;
7651 }
7652
7653 return result;
7654 }
7655
7656 static gboolean
scroll_row_timeout(gpointer data)7657 scroll_row_timeout (gpointer data)
7658 {
7659 GtkTreeView *tree_view = data;
7660
7661 gtk_tree_view_vertical_autoscroll (tree_view);
7662
7663 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
7664 gtk_tree_view_update_rubber_band (tree_view);
7665
7666 return TRUE;
7667 }
7668
7669 /* Returns TRUE if event should not be propagated to parent widgets */
7670 static gboolean
set_destination_row(GtkTreeView * tree_view,GdkDragContext * context,gint x,gint y,GdkDragAction * suggested_action,GdkAtom * target)7671 set_destination_row (GtkTreeView *tree_view,
7672 GdkDragContext *context,
7673 /* coordinates relative to the widget */
7674 gint x,
7675 gint y,
7676 GdkDragAction *suggested_action,
7677 GdkAtom *target)
7678 {
7679 GtkTreePath *path = NULL;
7680 GtkTreeViewDropPosition pos;
7681 GtkTreeViewDropPosition old_pos;
7682 TreeViewDragInfo *di;
7683 GtkWidget *widget;
7684 GtkTreePath *old_dest_path = NULL;
7685 gboolean can_drop = FALSE;
7686
7687 *suggested_action = 0;
7688 *target = GDK_NONE;
7689
7690 widget = GTK_WIDGET (tree_view);
7691
7692 di = get_info (tree_view);
7693
7694 if (di == NULL || y - gtk_tree_view_get_effective_header_height (tree_view) < 0)
7695 {
7696 /* someone unset us as a drag dest, note that if
7697 * we return FALSE drag_leave isn't called
7698 */
7699
7700 gtk_tree_view_set_drag_dest_row (tree_view,
7701 NULL,
7702 GTK_TREE_VIEW_DROP_BEFORE);
7703
7704 remove_scroll_timeout (GTK_TREE_VIEW (widget));
7705 remove_open_timeout (GTK_TREE_VIEW (widget));
7706
7707 return FALSE; /* no longer a drop site */
7708 }
7709
7710 *target = gtk_drag_dest_find_target (widget, context,
7711 gtk_drag_dest_get_target_list (widget));
7712 if (*target == GDK_NONE)
7713 {
7714 return FALSE;
7715 }
7716
7717 if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
7718 x, y,
7719 &path,
7720 &pos))
7721 {
7722 gint n_children;
7723 GtkTreeModel *model;
7724
7725 remove_open_timeout (tree_view);
7726
7727 /* the row got dropped on empty space, let's setup a special case
7728 */
7729
7730 if (path)
7731 gtk_tree_path_free (path);
7732
7733 model = gtk_tree_view_get_model (tree_view);
7734
7735 n_children = gtk_tree_model_iter_n_children (model, NULL);
7736 if (n_children)
7737 {
7738 pos = GTK_TREE_VIEW_DROP_AFTER;
7739 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
7740 }
7741 else
7742 {
7743 pos = GTK_TREE_VIEW_DROP_BEFORE;
7744 path = gtk_tree_path_new_from_indices (0, -1);
7745 }
7746
7747 can_drop = TRUE;
7748
7749 goto out;
7750 }
7751
7752 g_assert (path);
7753
7754 /* If we left the current row's "open" zone, unset the timeout for
7755 * opening the row
7756 */
7757 gtk_tree_view_get_drag_dest_row (tree_view,
7758 &old_dest_path,
7759 &old_pos);
7760
7761 if (old_dest_path &&
7762 (gtk_tree_path_compare (path, old_dest_path) != 0 ||
7763 !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7764 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)))
7765 remove_open_timeout (tree_view);
7766
7767 if (old_dest_path)
7768 gtk_tree_path_free (old_dest_path);
7769
7770 if (TRUE /* FIXME if the location droppable predicate */)
7771 {
7772 can_drop = TRUE;
7773 }
7774
7775 out:
7776 if (can_drop)
7777 {
7778 GtkWidget *source_widget;
7779
7780 *suggested_action = gdk_drag_context_get_suggested_action (context);
7781 source_widget = gtk_drag_get_source_widget (context);
7782
7783 if (source_widget == widget)
7784 {
7785 /* Default to MOVE, unless the user has
7786 * pressed ctrl or shift to affect available actions
7787 */
7788 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
7789 *suggested_action = GDK_ACTION_MOVE;
7790 }
7791
7792 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7793 path, pos);
7794 }
7795 else
7796 {
7797 /* can't drop here */
7798 remove_open_timeout (tree_view);
7799
7800 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7801 NULL,
7802 GTK_TREE_VIEW_DROP_BEFORE);
7803 }
7804
7805 if (path)
7806 gtk_tree_path_free (path);
7807
7808 return TRUE;
7809 }
7810
7811 static GtkTreePath*
get_logical_dest_row(GtkTreeView * tree_view,gboolean * path_down_mode,gboolean * drop_append_mode)7812 get_logical_dest_row (GtkTreeView *tree_view,
7813 gboolean *path_down_mode,
7814 gboolean *drop_append_mode)
7815 {
7816 /* adjust path to point to the row the drop goes in front of */
7817 GtkTreePath *path = NULL;
7818 GtkTreeViewDropPosition pos;
7819
7820 g_return_val_if_fail (path_down_mode != NULL, NULL);
7821 g_return_val_if_fail (drop_append_mode != NULL, NULL);
7822
7823 *path_down_mode = FALSE;
7824 *drop_append_mode = 0;
7825
7826 gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
7827
7828 if (path == NULL)
7829 return NULL;
7830
7831 if (pos == GTK_TREE_VIEW_DROP_BEFORE)
7832 ; /* do nothing */
7833 else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
7834 pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
7835 *path_down_mode = TRUE;
7836 else
7837 {
7838 GtkTreeIter iter;
7839 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
7840
7841 g_assert (pos == GTK_TREE_VIEW_DROP_AFTER);
7842
7843 if (!gtk_tree_model_get_iter (model, &iter, path) ||
7844 !gtk_tree_model_iter_next (model, &iter))
7845 *drop_append_mode = 1;
7846 else
7847 {
7848 *drop_append_mode = 0;
7849 gtk_tree_path_next (path);
7850 }
7851 }
7852
7853 return path;
7854 }
7855
7856 static gboolean
gtk_tree_view_maybe_begin_dragging_row(GtkTreeView * tree_view)7857 gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
7858 {
7859 GtkWidget *widget = GTK_WIDGET (tree_view);
7860 gdouble start_x, start_y, offset_x, offset_y;
7861 GdkEventSequence *sequence;
7862 const GdkEvent *event;
7863 GdkDragContext *context;
7864 TreeViewDragInfo *di;
7865 GtkTreePath *path = NULL;
7866 gint button;
7867 GtkTreeModel *model;
7868 gboolean retval = FALSE;
7869 gint bin_x, bin_y;
7870
7871 di = get_info (tree_view);
7872
7873 if (di == NULL || !di->source_set)
7874 goto out;
7875
7876 if (!gtk_gesture_is_recognized (tree_view->priv->drag_gesture))
7877 goto out;
7878
7879 gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
7880 &start_x, &start_y);
7881 gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (tree_view->priv->drag_gesture),
7882 &offset_x, &offset_y);
7883
7884 if (!gtk_drag_check_threshold (widget, 0, 0, offset_x, offset_y))
7885 goto out;
7886
7887 model = gtk_tree_view_get_model (tree_view);
7888
7889 if (model == NULL)
7890 goto out;
7891
7892 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (tree_view->priv->drag_gesture));
7893
7894 /* Deny the multipress gesture */
7895 gtk_gesture_set_state (GTK_GESTURE (tree_view->priv->multipress_gesture),
7896 GTK_EVENT_SEQUENCE_DENIED);
7897
7898 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, start_x, start_y,
7899 &bin_x, &bin_y);
7900 gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, &path,
7901 NULL, NULL, NULL);
7902
7903 if (path == NULL)
7904 goto out;
7905
7906 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7907 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7908 path))
7909 goto out;
7910
7911 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
7912 goto out;
7913
7914 retval = TRUE;
7915
7916 /* Now we can begin the drag */
7917 gtk_gesture_set_state (GTK_GESTURE (tree_view->priv->drag_gesture),
7918 GTK_EVENT_SEQUENCE_CLAIMED);
7919 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (tree_view->priv->drag_gesture));
7920 event = gtk_gesture_get_last_event (GTK_GESTURE (tree_view->priv->drag_gesture), sequence);
7921
7922 context = gtk_drag_begin_with_coordinates (widget,
7923 gtk_drag_source_get_target_list (widget),
7924 di->source_actions,
7925 button,
7926 (GdkEvent*)event,
7927 start_x, start_y);
7928
7929 set_source_row (context, model, path);
7930
7931 out:
7932 if (path)
7933 gtk_tree_path_free (path);
7934
7935 return retval;
7936 }
7937
7938
7939 static void
gtk_tree_view_drag_begin(GtkWidget * widget,GdkDragContext * context)7940 gtk_tree_view_drag_begin (GtkWidget *widget,
7941 GdkDragContext *context)
7942 {
7943 GtkTreeView *tree_view;
7944 GtkTreePath *path = NULL;
7945 gint cell_x, cell_y;
7946 cairo_surface_t *row_pix;
7947 TreeViewDragInfo *di;
7948 double sx, sy;
7949
7950 tree_view = GTK_TREE_VIEW (widget);
7951
7952 /* if the user uses a custom DND source impl, we don't set the icon here */
7953 di = get_info (tree_view);
7954
7955 if (di == NULL || !di->source_set)
7956 return;
7957
7958 gtk_tree_view_get_path_at_pos (tree_view,
7959 tree_view->priv->press_start_x,
7960 tree_view->priv->press_start_y,
7961 &path,
7962 NULL,
7963 &cell_x,
7964 &cell_y);
7965
7966 /* If path is NULL, there's nothing we can drag. For now, we silently
7967 * bail out. Actually, dragging should not be possible from an empty
7968 * tree view, but there's no way we can cancel that from here.
7969 * Automatically unsetting the tree view as drag source for empty models
7970 * is something that would likely break other people's code ...
7971 */
7972 if (!path)
7973 return;
7974
7975 row_pix = gtk_tree_view_create_row_drag_icon (tree_view,
7976 path);
7977 cairo_surface_get_device_scale (row_pix, &sx, &sy);
7978 cairo_surface_set_device_offset (row_pix,
7979 /* the + 1 is for the black border in the icon */
7980 - (tree_view->priv->press_start_x + 1) * sx,
7981 - (cell_y + 1) * sy);
7982
7983 gtk_drag_set_icon_surface (context, row_pix);
7984
7985 cairo_surface_destroy (row_pix);
7986 gtk_tree_path_free (path);
7987 }
7988
7989 static void
gtk_tree_view_drag_end(GtkWidget * widget,GdkDragContext * context)7990 gtk_tree_view_drag_end (GtkWidget *widget,
7991 GdkDragContext *context)
7992 {
7993 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
7994
7995 tree_view->priv->event_last_x = -10000;
7996 tree_view->priv->event_last_y = -10000;
7997 }
7998
7999 /* Default signal implementations for the drag signals */
8000 static void
gtk_tree_view_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)8001 gtk_tree_view_drag_data_get (GtkWidget *widget,
8002 GdkDragContext *context,
8003 GtkSelectionData *selection_data,
8004 guint info,
8005 guint time)
8006 {
8007 GtkTreeView *tree_view;
8008 GtkTreeModel *model;
8009 TreeViewDragInfo *di;
8010 GtkTreePath *source_row;
8011
8012 tree_view = GTK_TREE_VIEW (widget);
8013
8014 model = gtk_tree_view_get_model (tree_view);
8015
8016 if (model == NULL)
8017 return;
8018
8019 di = get_info (GTK_TREE_VIEW (widget));
8020
8021 if (di == NULL)
8022 return;
8023
8024 source_row = get_source_row (context);
8025
8026 if (source_row == NULL)
8027 return;
8028
8029 /* We can implement the GTK_TREE_MODEL_ROW target generically for
8030 * any model; for DragSource models there are some other targets
8031 * we also support.
8032 */
8033
8034 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
8035 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
8036 source_row,
8037 selection_data))
8038 goto done;
8039
8040 /* If drag_data_get does nothing, try providing row data. */
8041 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
8042 {
8043 gtk_tree_set_row_drag_data (selection_data,
8044 model,
8045 source_row);
8046 }
8047
8048 done:
8049 gtk_tree_path_free (source_row);
8050 }
8051
8052
8053 static void
gtk_tree_view_drag_data_delete(GtkWidget * widget,GdkDragContext * context)8054 gtk_tree_view_drag_data_delete (GtkWidget *widget,
8055 GdkDragContext *context)
8056 {
8057 TreeViewDragInfo *di;
8058 GtkTreeModel *model;
8059 GtkTreeView *tree_view;
8060 GtkTreePath *source_row;
8061
8062 tree_view = GTK_TREE_VIEW (widget);
8063 model = gtk_tree_view_get_model (tree_view);
8064
8065 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
8066 return;
8067
8068 di = get_info (tree_view);
8069
8070 if (di == NULL)
8071 return;
8072
8073 source_row = get_source_row (context);
8074
8075 if (source_row == NULL)
8076 return;
8077
8078 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
8079 source_row);
8080
8081 gtk_tree_path_free (source_row);
8082
8083 set_source_row (context, NULL, NULL);
8084 }
8085
8086 static void
gtk_tree_view_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)8087 gtk_tree_view_drag_leave (GtkWidget *widget,
8088 GdkDragContext *context,
8089 guint time)
8090 {
8091 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8092
8093 /* unset any highlight row */
8094 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
8095 NULL,
8096 GTK_TREE_VIEW_DROP_BEFORE);
8097
8098 remove_scroll_timeout (GTK_TREE_VIEW (widget));
8099 remove_open_timeout (GTK_TREE_VIEW (widget));
8100
8101 tree_view->priv->event_last_x = -10000;
8102 tree_view->priv->event_last_y = -10000;
8103 }
8104
8105
8106 static gboolean
gtk_tree_view_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)8107 gtk_tree_view_drag_motion (GtkWidget *widget,
8108 GdkDragContext *context,
8109 /* coordinates relative to the widget */
8110 gint x,
8111 gint y,
8112 guint time)
8113 {
8114 gboolean empty;
8115 GtkTreePath *path = NULL;
8116 GtkTreeViewDropPosition pos;
8117 GtkTreeView *tree_view;
8118 GdkDragAction suggested_action = 0;
8119 GdkAtom target;
8120
8121 tree_view = GTK_TREE_VIEW (widget);
8122
8123 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
8124 return FALSE;
8125
8126 tree_view->priv->event_last_x = x;
8127 tree_view->priv->event_last_y = y;
8128
8129 gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
8130
8131 /* we only know this *after* set_desination_row */
8132 empty = tree_view->priv->empty_view_drop;
8133
8134 if (path == NULL && !empty)
8135 {
8136 /* Can't drop here. */
8137 gdk_drag_status (context, 0, time);
8138 }
8139 else
8140 {
8141 if (tree_view->priv->open_dest_timeout == 0 &&
8142 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
8143 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
8144 {
8145 tree_view->priv->open_dest_timeout =
8146 gdk_threads_add_timeout (AUTO_EXPAND_TIMEOUT, open_row_timeout, tree_view);
8147 g_source_set_name_by_id (tree_view->priv->open_dest_timeout, "[gtk+] open_row_timeout");
8148 }
8149 else
8150 {
8151 add_scroll_timeout (tree_view);
8152 }
8153
8154 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
8155 {
8156 /* Request data so we can use the source row when
8157 * determining whether to accept the drop
8158 */
8159 set_status_pending (context, suggested_action);
8160 gtk_drag_get_data (widget, context, target, time);
8161 }
8162 else
8163 {
8164 set_status_pending (context, 0);
8165 gdk_drag_status (context, suggested_action, time);
8166 }
8167 }
8168
8169 if (path)
8170 gtk_tree_path_free (path);
8171
8172 return TRUE;
8173 }
8174
8175
8176 static gboolean
gtk_tree_view_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)8177 gtk_tree_view_drag_drop (GtkWidget *widget,
8178 GdkDragContext *context,
8179 /* coordinates relative to the widget */
8180 gint x,
8181 gint y,
8182 guint time)
8183 {
8184 GtkTreeView *tree_view;
8185 GtkTreePath *path;
8186 GdkDragAction suggested_action = 0;
8187 GdkAtom target = GDK_NONE;
8188 TreeViewDragInfo *di;
8189 GtkTreeModel *model;
8190 gboolean path_down_mode;
8191 gboolean drop_append_mode;
8192
8193 tree_view = GTK_TREE_VIEW (widget);
8194
8195 model = gtk_tree_view_get_model (tree_view);
8196
8197 remove_scroll_timeout (GTK_TREE_VIEW (widget));
8198 remove_open_timeout (GTK_TREE_VIEW (widget));
8199
8200 di = get_info (tree_view);
8201
8202 if (di == NULL)
8203 return FALSE;
8204
8205 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
8206 return FALSE;
8207
8208 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
8209 return FALSE;
8210
8211 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
8212
8213 if (target != GDK_NONE && path != NULL)
8214 {
8215 /* in case a motion had requested drag data, change things so we
8216 * treat drag data receives as a drop.
8217 */
8218 set_status_pending (context, 0);
8219 set_dest_row (context, model, path,
8220 path_down_mode, tree_view->priv->empty_view_drop,
8221 drop_append_mode);
8222 }
8223
8224 if (path)
8225 gtk_tree_path_free (path);
8226
8227 /* Unset this thing */
8228 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
8229 NULL,
8230 GTK_TREE_VIEW_DROP_BEFORE);
8231
8232 if (target != GDK_NONE)
8233 {
8234 gtk_drag_get_data (widget, context, target, time);
8235 return TRUE;
8236 }
8237 else
8238 return FALSE;
8239 }
8240
8241 static void
gtk_tree_view_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)8242 gtk_tree_view_drag_data_received (GtkWidget *widget,
8243 GdkDragContext *context,
8244 /* coordinates relative to the widget */
8245 gint x,
8246 gint y,
8247 GtkSelectionData *selection_data,
8248 guint info,
8249 guint time)
8250 {
8251 GtkTreePath *path;
8252 TreeViewDragInfo *di;
8253 gboolean accepted = FALSE;
8254 GtkTreeModel *model;
8255 GtkTreeView *tree_view;
8256 GtkTreePath *dest_row;
8257 GdkDragAction suggested_action;
8258 gboolean path_down_mode;
8259 gboolean drop_append_mode;
8260
8261 tree_view = GTK_TREE_VIEW (widget);
8262
8263 model = gtk_tree_view_get_model (tree_view);
8264
8265 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
8266 return;
8267
8268 di = get_info (tree_view);
8269
8270 if (di == NULL)
8271 return;
8272
8273 suggested_action = get_status_pending (context);
8274
8275 if (suggested_action)
8276 {
8277 /* We are getting this data due to a request in drag_motion,
8278 * rather than due to a request in drag_drop, so we are just
8279 * supposed to call drag_status, not actually paste in the
8280 * data.
8281 */
8282 path = get_logical_dest_row (tree_view, &path_down_mode,
8283 &drop_append_mode);
8284
8285 if (path == NULL)
8286 suggested_action = 0;
8287 else if (path_down_mode)
8288 gtk_tree_path_down (path);
8289
8290 if (suggested_action)
8291 {
8292 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
8293 path,
8294 selection_data))
8295 {
8296 if (path_down_mode)
8297 {
8298 path_down_mode = FALSE;
8299 gtk_tree_path_up (path);
8300
8301 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
8302 path,
8303 selection_data))
8304 suggested_action = 0;
8305 }
8306 else
8307 suggested_action = 0;
8308 }
8309 }
8310
8311 gdk_drag_status (context, suggested_action, time);
8312
8313 if (path)
8314 gtk_tree_path_free (path);
8315
8316 /* If you can't drop, remove user drop indicator until the next motion */
8317 if (suggested_action == 0)
8318 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
8319 NULL,
8320 GTK_TREE_VIEW_DROP_BEFORE);
8321
8322 return;
8323 }
8324
8325 dest_row = get_dest_row (context, &path_down_mode);
8326
8327 if (dest_row == NULL)
8328 return;
8329
8330 if (gtk_selection_data_get_length (selection_data) >= 0)
8331 {
8332 if (path_down_mode)
8333 {
8334 gtk_tree_path_down (dest_row);
8335 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
8336 dest_row, selection_data))
8337 gtk_tree_path_up (dest_row);
8338 }
8339 }
8340
8341 if (gtk_selection_data_get_length (selection_data) >= 0)
8342 {
8343 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
8344 dest_row,
8345 selection_data))
8346 accepted = TRUE;
8347 }
8348
8349 gtk_drag_finish (context,
8350 accepted,
8351 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
8352 time);
8353
8354 if (gtk_tree_path_get_depth (dest_row) == 1 &&
8355 gtk_tree_path_get_indices (dest_row)[0] == 0 &&
8356 gtk_tree_model_iter_n_children (tree_view->priv->model, NULL) != 0)
8357 {
8358 /* special special case drag to "0", scroll to first item */
8359 if (!tree_view->priv->scroll_to_path)
8360 gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
8361 }
8362
8363 gtk_tree_path_free (dest_row);
8364
8365 /* drop dest_row */
8366 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
8367 }
8368
8369
8370
8371 /* GtkContainer Methods
8372 */
8373
8374
8375 static void
gtk_tree_view_remove(GtkContainer * container,GtkWidget * widget)8376 gtk_tree_view_remove (GtkContainer *container,
8377 GtkWidget *widget)
8378 {
8379 GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8380 GtkTreeViewChild *child = NULL;
8381 GList *tmp_list;
8382
8383 tmp_list = tree_view->priv->children;
8384 while (tmp_list)
8385 {
8386 child = tmp_list->data;
8387 if (child->widget == widget)
8388 {
8389 gtk_widget_unparent (widget);
8390
8391 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
8392 g_list_free_1 (tmp_list);
8393 g_slice_free (GtkTreeViewChild, child);
8394 return;
8395 }
8396
8397 tmp_list = tmp_list->next;
8398 }
8399
8400 tmp_list = tree_view->priv->columns;
8401
8402 while (tmp_list)
8403 {
8404 GtkTreeViewColumn *column;
8405 GtkWidget *button;
8406
8407 column = tmp_list->data;
8408 button = gtk_tree_view_column_get_button (column);
8409
8410 if (button == widget)
8411 {
8412 gtk_widget_unparent (widget);
8413 return;
8414 }
8415 tmp_list = tmp_list->next;
8416 }
8417 }
8418
8419 static void
gtk_tree_view_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)8420 gtk_tree_view_forall (GtkContainer *container,
8421 gboolean include_internals,
8422 GtkCallback callback,
8423 gpointer callback_data)
8424 {
8425 GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8426 GtkTreeViewChild *child = NULL;
8427 GtkTreeViewColumn *column;
8428 GtkWidget *button;
8429 GList *tmp_list;
8430
8431 tmp_list = tree_view->priv->children;
8432 while (tmp_list)
8433 {
8434 child = tmp_list->data;
8435 tmp_list = tmp_list->next;
8436
8437 (* callback) (child->widget, callback_data);
8438 }
8439 if (include_internals == FALSE)
8440 return;
8441
8442 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
8443 {
8444 column = tmp_list->data;
8445 button = gtk_tree_view_column_get_button (column);
8446
8447 if (button)
8448 (* callback) (button, callback_data);
8449 }
8450 }
8451
8452 /* Returns TRUE is any of the columns contains a cell that can-focus.
8453 * If this is not the case, a column-spanning focus rectangle will be
8454 * drawn.
8455 */
8456 static gboolean
gtk_tree_view_has_can_focus_cell(GtkTreeView * tree_view)8457 gtk_tree_view_has_can_focus_cell (GtkTreeView *tree_view)
8458 {
8459 GList *list;
8460
8461 for (list = tree_view->priv->columns; list; list = list->next)
8462 {
8463 GtkTreeViewColumn *column = list->data;
8464
8465 if (!gtk_tree_view_column_get_visible (column))
8466 continue;
8467 if (gtk_cell_area_is_activatable (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column))))
8468 return TRUE;
8469 }
8470
8471 return FALSE;
8472 }
8473
8474 static void
column_sizing_notify(GObject * object,GParamSpec * pspec,gpointer data)8475 column_sizing_notify (GObject *object,
8476 GParamSpec *pspec,
8477 gpointer data)
8478 {
8479 GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (object);
8480
8481 if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED)
8482 /* disable fixed height mode */
8483 g_object_set (data, "fixed-height-mode", FALSE, NULL);
8484 }
8485
8486 /**
8487 * gtk_tree_view_set_fixed_height_mode:
8488 * @tree_view: a #GtkTreeView
8489 * @enable: %TRUE to enable fixed height mode
8490 *
8491 * Enables or disables the fixed height mode of @tree_view.
8492 * Fixed height mode speeds up #GtkTreeView by assuming that all
8493 * rows have the same height.
8494 * Only enable this option if all rows are the same height and all
8495 * columns are of type %GTK_TREE_VIEW_COLUMN_FIXED.
8496 *
8497 * Since: 2.6
8498 **/
8499 void
gtk_tree_view_set_fixed_height_mode(GtkTreeView * tree_view,gboolean enable)8500 gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
8501 gboolean enable)
8502 {
8503 GList *l;
8504
8505 enable = enable != FALSE;
8506
8507 if (enable == tree_view->priv->fixed_height_mode)
8508 return;
8509
8510 if (!enable)
8511 {
8512 tree_view->priv->fixed_height_mode = 0;
8513 tree_view->priv->fixed_height = -1;
8514 }
8515 else
8516 {
8517 /* make sure all columns are of type FIXED */
8518 for (l = tree_view->priv->columns; l; l = l->next)
8519 {
8520 GtkTreeViewColumn *c = l->data;
8521
8522 g_return_if_fail (gtk_tree_view_column_get_sizing (c) == GTK_TREE_VIEW_COLUMN_FIXED);
8523 }
8524
8525 /* yes, we really have to do this is in a separate loop */
8526 for (l = tree_view->priv->columns; l; l = l->next)
8527 g_signal_connect (l->data, "notify::sizing",
8528 G_CALLBACK (column_sizing_notify), tree_view);
8529
8530 tree_view->priv->fixed_height_mode = 1;
8531 tree_view->priv->fixed_height = -1;
8532 }
8533
8534 /* force a revalidation */
8535 install_presize_handler (tree_view);
8536
8537 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_FIXED_HEIGHT_MODE]);
8538 }
8539
8540 /**
8541 * gtk_tree_view_get_fixed_height_mode:
8542 * @tree_view: a #GtkTreeView
8543 *
8544 * Returns whether fixed height mode is turned on for @tree_view.
8545 *
8546 * Returns: %TRUE if @tree_view is in fixed height mode
8547 *
8548 * Since: 2.6
8549 **/
8550 gboolean
gtk_tree_view_get_fixed_height_mode(GtkTreeView * tree_view)8551 gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view)
8552 {
8553 return tree_view->priv->fixed_height_mode;
8554 }
8555
8556 /* Returns TRUE if the focus is within the headers, after the focus operation is
8557 * done
8558 */
8559 static gboolean
gtk_tree_view_header_focus(GtkTreeView * tree_view,GtkDirectionType dir,gboolean clamp_column_visible)8560 gtk_tree_view_header_focus (GtkTreeView *tree_view,
8561 GtkDirectionType dir,
8562 gboolean clamp_column_visible)
8563 {
8564 GtkTreeViewColumn *column;
8565 GtkWidget *button;
8566 GtkWidget *focus_child;
8567 GList *last_column, *first_column;
8568 GList *tmp_list;
8569 gboolean rtl;
8570
8571 if (! tree_view->priv->headers_visible)
8572 return FALSE;
8573
8574 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
8575
8576 first_column = tree_view->priv->columns;
8577 while (first_column)
8578 {
8579 column = GTK_TREE_VIEW_COLUMN (first_column->data);
8580 button = gtk_tree_view_column_get_button (column);
8581
8582 if (gtk_widget_get_can_focus (button) &&
8583 gtk_tree_view_column_get_visible (column) &&
8584 (gtk_tree_view_column_get_clickable (column) ||
8585 gtk_tree_view_column_get_reorderable (column)))
8586 break;
8587 first_column = first_column->next;
8588 }
8589
8590 /* No headers are visible, or are focusable. We can't focus in or out.
8591 */
8592 if (first_column == NULL)
8593 return FALSE;
8594
8595 last_column = g_list_last (tree_view->priv->columns);
8596 while (last_column)
8597 {
8598 column = GTK_TREE_VIEW_COLUMN (last_column->data);
8599 button = gtk_tree_view_column_get_button (column);
8600
8601 if (gtk_widget_get_can_focus (button) &&
8602 gtk_tree_view_column_get_visible (column) &&
8603 (gtk_tree_view_column_get_clickable (column) ||
8604 gtk_tree_view_column_get_reorderable (column)))
8605 break;
8606 last_column = last_column->prev;
8607 }
8608
8609
8610 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8611
8612 switch (dir)
8613 {
8614 case GTK_DIR_TAB_BACKWARD:
8615 case GTK_DIR_TAB_FORWARD:
8616 case GTK_DIR_UP:
8617 case GTK_DIR_DOWN:
8618 if (focus_child == NULL)
8619 {
8620 if (tree_view->priv->focus_column != NULL)
8621 button = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
8622 else
8623 button = NULL;
8624
8625 if (button && gtk_widget_get_can_focus (button))
8626 focus_child = button;
8627 else
8628 focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
8629
8630 gtk_widget_grab_focus (focus_child);
8631 break;
8632 }
8633 return FALSE;
8634
8635 case GTK_DIR_LEFT:
8636 case GTK_DIR_RIGHT:
8637 if (focus_child == NULL)
8638 {
8639 if (tree_view->priv->focus_column != NULL)
8640 focus_child = gtk_tree_view_column_get_button (tree_view->priv->focus_column);
8641 else if (dir == GTK_DIR_LEFT)
8642 focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (last_column->data));
8643 else
8644 focus_child = gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (first_column->data));
8645
8646 gtk_widget_grab_focus (focus_child);
8647 break;
8648 }
8649
8650 if (gtk_widget_child_focus (focus_child, dir))
8651 {
8652 /* The focus moves inside the button. */
8653 /* This is probably a great example of bad UI */
8654 break;
8655 }
8656
8657 /* We need to move the focus among the row of buttons. */
8658 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
8659 if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
8660 break;
8661
8662 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
8663 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
8664 {
8665 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8666 break;
8667 }
8668
8669 while (tmp_list)
8670 {
8671 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
8672 tmp_list = tmp_list->next;
8673 else
8674 tmp_list = tmp_list->prev;
8675
8676 if (tmp_list == NULL)
8677 {
8678 g_warning ("Internal button not found");
8679 break;
8680 }
8681 column = tmp_list->data;
8682 button = gtk_tree_view_column_get_button (column);
8683 if (button &&
8684 gtk_tree_view_column_get_visible (column) &&
8685 gtk_widget_get_can_focus (button))
8686 {
8687 focus_child = button;
8688 gtk_widget_grab_focus (button);
8689 break;
8690 }
8691 }
8692 break;
8693 default:
8694 g_assert_not_reached ();
8695 break;
8696 }
8697
8698 /* if focus child is non-null, we assume it's been set to the current focus child
8699 */
8700 if (focus_child)
8701 {
8702 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
8703 if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (tmp_list->data)) == focus_child)
8704 {
8705 _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (tmp_list->data));
8706 break;
8707 }
8708
8709 if (clamp_column_visible)
8710 {
8711 gtk_tree_view_clamp_column_visible (tree_view,
8712 tree_view->priv->focus_column,
8713 FALSE);
8714 }
8715 }
8716
8717 return (focus_child != NULL);
8718 }
8719
8720 /* This function returns in 'path' the first focusable path, if the given path
8721 * is already focusable, it’s the returned one.
8722 */
8723 static gboolean
search_first_focusable_path(GtkTreeView * tree_view,GtkTreePath ** path,gboolean search_forward,GtkRBTree ** new_tree,GtkRBNode ** new_node)8724 search_first_focusable_path (GtkTreeView *tree_view,
8725 GtkTreePath **path,
8726 gboolean search_forward,
8727 GtkRBTree **new_tree,
8728 GtkRBNode **new_node)
8729 {
8730 GtkRBTree *tree = NULL;
8731 GtkRBNode *node = NULL;
8732
8733 if (!path || !*path)
8734 return FALSE;
8735
8736 _gtk_tree_view_find_node (tree_view, *path, &tree, &node);
8737
8738 if (!tree || !node)
8739 return FALSE;
8740
8741 while (node && row_is_separator (tree_view, NULL, *path))
8742 {
8743 if (search_forward)
8744 _gtk_rbtree_next_full (tree, node, &tree, &node);
8745 else
8746 _gtk_rbtree_prev_full (tree, node, &tree, &node);
8747
8748 if (*path)
8749 gtk_tree_path_free (*path);
8750
8751 if (node)
8752 *path = _gtk_tree_path_new_from_rbtree (tree, node);
8753 else
8754 *path = NULL;
8755 }
8756
8757 if (new_tree)
8758 *new_tree = tree;
8759
8760 if (new_node)
8761 *new_node = node;
8762
8763 return (*path != NULL);
8764 }
8765
8766 static gint
gtk_tree_view_focus(GtkWidget * widget,GtkDirectionType direction)8767 gtk_tree_view_focus (GtkWidget *widget,
8768 GtkDirectionType direction)
8769 {
8770 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8771 GtkContainer *container = GTK_CONTAINER (widget);
8772 GtkWidget *focus_child;
8773
8774 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
8775 return FALSE;
8776
8777 focus_child = gtk_container_get_focus_child (container);
8778
8779 gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE);
8780 /* Case 1. Headers currently have focus. */
8781 if (focus_child)
8782 {
8783 switch (direction)
8784 {
8785 case GTK_DIR_LEFT:
8786 case GTK_DIR_RIGHT:
8787 gtk_tree_view_header_focus (tree_view, direction, TRUE);
8788 return TRUE;
8789 case GTK_DIR_TAB_BACKWARD:
8790 case GTK_DIR_UP:
8791 return FALSE;
8792 case GTK_DIR_TAB_FORWARD:
8793 case GTK_DIR_DOWN:
8794 gtk_widget_grab_focus (widget);
8795 return TRUE;
8796 default:
8797 g_assert_not_reached ();
8798 return FALSE;
8799 }
8800 }
8801
8802 /* Case 2. We don't have focus at all. */
8803 if (!gtk_widget_has_focus (widget))
8804 {
8805 gtk_widget_grab_focus (widget);
8806 return TRUE;
8807 }
8808
8809 /* Case 3. We have focus already. */
8810 if (direction == GTK_DIR_TAB_BACKWARD)
8811 return (gtk_tree_view_header_focus (tree_view, direction, FALSE));
8812 else if (direction == GTK_DIR_TAB_FORWARD)
8813 return FALSE;
8814
8815 /* Other directions caught by the keybindings */
8816 gtk_widget_grab_focus (widget);
8817 return TRUE;
8818 }
8819
8820 static void
gtk_tree_view_grab_focus(GtkWidget * widget)8821 gtk_tree_view_grab_focus (GtkWidget *widget)
8822 {
8823 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->grab_focus (widget);
8824
8825 gtk_tree_view_focus_to_cursor (GTK_TREE_VIEW (widget));
8826 }
8827
8828 static void
gtk_tree_view_style_updated(GtkWidget * widget)8829 gtk_tree_view_style_updated (GtkWidget *widget)
8830 {
8831 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8832 GList *list;
8833 GtkTreeViewColumn *column;
8834 GtkStyleContext *style_context;
8835 GtkCssStyleChange *change;
8836
8837 GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->style_updated (widget);
8838
8839 if (gtk_widget_get_realized (widget))
8840 {
8841 gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
8842 gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
8843 }
8844
8845 style_context = gtk_widget_get_style_context (widget);
8846 change = gtk_style_context_get_change (style_context);
8847
8848 if (change == NULL || gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_SIZE | GTK_CSS_AFFECTS_CLIP))
8849 {
8850 for (list = tree_view->priv->columns; list; list = list->next)
8851 {
8852 column = list->data;
8853 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
8854 }
8855
8856 tree_view->priv->fixed_height = -1;
8857 _gtk_rbtree_mark_invalid (tree_view->priv->tree);
8858 }
8859 }
8860
8861
8862 static void
gtk_tree_view_set_focus_child(GtkContainer * container,GtkWidget * child)8863 gtk_tree_view_set_focus_child (GtkContainer *container,
8864 GtkWidget *child)
8865 {
8866 GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8867 GList *list;
8868
8869 for (list = tree_view->priv->columns; list; list = list->next)
8870 {
8871 if (gtk_tree_view_column_get_button (GTK_TREE_VIEW_COLUMN (list->data)) == child)
8872 {
8873 _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data));
8874 break;
8875 }
8876 }
8877
8878 GTK_CONTAINER_CLASS (gtk_tree_view_parent_class)->set_focus_child (container, child);
8879 }
8880
8881 static gboolean
gtk_tree_view_real_move_cursor(GtkTreeView * tree_view,GtkMovementStep step,gint count)8882 gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
8883 GtkMovementStep step,
8884 gint count)
8885 {
8886 GdkModifierType state;
8887
8888 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
8889 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
8890 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
8891 step == GTK_MOVEMENT_DISPLAY_LINES ||
8892 step == GTK_MOVEMENT_PAGES ||
8893 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
8894
8895 if (tree_view->priv->tree == NULL)
8896 return FALSE;
8897 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8898 return FALSE;
8899
8900 gtk_tree_view_stop_editing (tree_view, FALSE);
8901 tree_view->priv->draw_keyfocus = TRUE;
8902 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8903
8904 if (gtk_get_current_event_state (&state))
8905 {
8906 GdkModifierType extend_mod_mask;
8907 GdkModifierType modify_mod_mask;
8908
8909 extend_mod_mask =
8910 gtk_widget_get_modifier_mask (GTK_WIDGET (tree_view),
8911 GDK_MODIFIER_INTENT_EXTEND_SELECTION);
8912
8913 modify_mod_mask =
8914 gtk_widget_get_modifier_mask (GTK_WIDGET (tree_view),
8915 GDK_MODIFIER_INTENT_MODIFY_SELECTION);
8916
8917 if ((state & modify_mod_mask) == modify_mod_mask)
8918 tree_view->priv->modify_selection_pressed = TRUE;
8919 if ((state & extend_mod_mask) == extend_mod_mask)
8920 tree_view->priv->extend_selection_pressed = TRUE;
8921 }
8922 /* else we assume not pressed */
8923
8924 switch (step)
8925 {
8926 /* currently we make no distinction. When we go bi-di, we need to */
8927 case GTK_MOVEMENT_LOGICAL_POSITIONS:
8928 case GTK_MOVEMENT_VISUAL_POSITIONS:
8929 gtk_tree_view_move_cursor_left_right (tree_view, count);
8930 break;
8931 case GTK_MOVEMENT_DISPLAY_LINES:
8932 gtk_tree_view_move_cursor_up_down (tree_view, count);
8933 break;
8934 case GTK_MOVEMENT_PAGES:
8935 gtk_tree_view_move_cursor_page_up_down (tree_view, count);
8936 break;
8937 case GTK_MOVEMENT_BUFFER_ENDS:
8938 gtk_tree_view_move_cursor_start_end (tree_view, count);
8939 break;
8940 default:
8941 g_assert_not_reached ();
8942 }
8943
8944 tree_view->priv->modify_selection_pressed = FALSE;
8945 tree_view->priv->extend_selection_pressed = FALSE;
8946
8947 return TRUE;
8948 }
8949
8950 static void
gtk_tree_view_put(GtkTreeView * tree_view,GtkWidget * child_widget,GtkTreePath * path,GtkTreeViewColumn * column,const GtkBorder * border)8951 gtk_tree_view_put (GtkTreeView *tree_view,
8952 GtkWidget *child_widget,
8953 GtkTreePath *path,
8954 GtkTreeViewColumn *column,
8955 const GtkBorder *border)
8956 {
8957 GtkTreeViewChild *child;
8958
8959 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
8960 g_return_if_fail (GTK_IS_WIDGET (child_widget));
8961
8962 child = g_slice_new (GtkTreeViewChild);
8963
8964 child->widget = child_widget;
8965 if (_gtk_tree_view_find_node (tree_view,
8966 path,
8967 &child->tree,
8968 &child->node))
8969 {
8970 g_assert_not_reached ();
8971 }
8972 child->column = column;
8973 child->border = *border;
8974
8975 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
8976
8977 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8978 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
8979
8980 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
8981 }
8982
8983 /* TreeModel Callbacks
8984 */
8985
8986 static void
gtk_tree_view_row_changed(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8987 gtk_tree_view_row_changed (GtkTreeModel *model,
8988 GtkTreePath *path,
8989 GtkTreeIter *iter,
8990 gpointer data)
8991 {
8992 GtkTreeView *tree_view = (GtkTreeView *)data;
8993 GtkRBTree *tree;
8994 GtkRBNode *node;
8995 gboolean free_path = FALSE;
8996 GList *list;
8997 GtkTreePath *cursor_path;
8998
8999 g_return_if_fail (path != NULL || iter != NULL);
9000
9001 if (tree_view->priv->cursor_node != NULL)
9002 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
9003 tree_view->priv->cursor_node);
9004 else
9005 cursor_path = NULL;
9006
9007 if (tree_view->priv->edited_column &&
9008 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
9009 gtk_tree_view_stop_editing (tree_view, TRUE);
9010
9011 if (cursor_path != NULL)
9012 gtk_tree_path_free (cursor_path);
9013
9014 if (path == NULL)
9015 {
9016 path = gtk_tree_model_get_path (model, iter);
9017 free_path = TRUE;
9018 }
9019 else if (iter == NULL)
9020 gtk_tree_model_get_iter (model, iter, path);
9021
9022 if (_gtk_tree_view_find_node (tree_view,
9023 path,
9024 &tree,
9025 &node))
9026 /* We aren't actually showing the node */
9027 goto done;
9028
9029 if (tree == NULL)
9030 goto done;
9031
9032 _gtk_tree_view_accessible_changed (tree_view, tree, node);
9033
9034 if (tree_view->priv->fixed_height_mode
9035 && tree_view->priv->fixed_height >= 0)
9036 {
9037 _gtk_rbtree_node_set_height (tree, node, tree_view->priv->fixed_height);
9038 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9039 gtk_tree_view_node_queue_redraw (tree_view, tree, node);
9040 }
9041 else
9042 {
9043 _gtk_rbtree_node_mark_invalid (tree, node);
9044 for (list = tree_view->priv->columns; list; list = list->next)
9045 {
9046 GtkTreeViewColumn *column;
9047
9048 column = list->data;
9049 if (!gtk_tree_view_column_get_visible (column))
9050 continue;
9051
9052 if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
9053 {
9054 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
9055 }
9056 }
9057 }
9058
9059 done:
9060 if (!tree_view->priv->fixed_height_mode &&
9061 gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9062 install_presize_handler (tree_view);
9063 if (free_path)
9064 gtk_tree_path_free (path);
9065 }
9066
9067 static void
gtk_tree_view_row_inserted(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)9068 gtk_tree_view_row_inserted (GtkTreeModel *model,
9069 GtkTreePath *path,
9070 GtkTreeIter *iter,
9071 gpointer data)
9072 {
9073 GtkTreeView *tree_view = (GtkTreeView *) data;
9074 gint *indices;
9075 GtkRBTree *tree;
9076 GtkRBNode *tmpnode = NULL;
9077 gint depth;
9078 gint i = 0;
9079 gint height;
9080 gboolean free_path = FALSE;
9081 gboolean node_visible = TRUE;
9082
9083 g_return_if_fail (path != NULL || iter != NULL);
9084
9085 if (tree_view->priv->fixed_height_mode
9086 && tree_view->priv->fixed_height >= 0)
9087 height = tree_view->priv->fixed_height;
9088 else
9089 height = 0;
9090
9091 if (path == NULL)
9092 {
9093 path = gtk_tree_model_get_path (model, iter);
9094 free_path = TRUE;
9095 }
9096 else if (iter == NULL)
9097 gtk_tree_model_get_iter (model, iter, path);
9098
9099 if (tree_view->priv->tree == NULL)
9100 tree_view->priv->tree = _gtk_rbtree_new ();
9101
9102 tree = tree_view->priv->tree;
9103
9104 /* Update all row-references */
9105 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
9106 depth = gtk_tree_path_get_depth (path);
9107 indices = gtk_tree_path_get_indices (path);
9108
9109 /* First, find the parent tree */
9110 while (i < depth - 1)
9111 {
9112 if (tree == NULL)
9113 {
9114 /* We aren't showing the node */
9115 node_visible = FALSE;
9116 goto done;
9117 }
9118
9119 tmpnode = _gtk_rbtree_find_count (tree, indices[i] + 1);
9120 if (tmpnode == NULL)
9121 {
9122 g_warning ("A node was inserted with a parent that's not in the tree.\n" \
9123 "This possibly means that a GtkTreeModel inserted a child node\n" \
9124 "before the parent was inserted.");
9125 goto done;
9126 }
9127 else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT))
9128 {
9129 /* FIXME enforce correct behavior on model, probably */
9130 /* In theory, the model should have emitted has_child_toggled here. We
9131 * try to catch it anyway, just to be safe, in case the model hasn't.
9132 */
9133 GtkTreePath *tmppath = _gtk_tree_path_new_from_rbtree (tree, tmpnode);
9134 gtk_tree_view_row_has_child_toggled (model, tmppath, NULL, data);
9135 gtk_tree_path_free (tmppath);
9136 goto done;
9137 }
9138
9139 tree = tmpnode->children;
9140 i++;
9141 }
9142
9143 if (tree == NULL)
9144 {
9145 node_visible = FALSE;
9146 goto done;
9147 }
9148
9149 /* ref the node */
9150 gtk_tree_model_ref_node (tree_view->priv->model, iter);
9151 if (indices[depth - 1] == 0)
9152 {
9153 tmpnode = _gtk_rbtree_find_count (tree, 1);
9154 tmpnode = _gtk_rbtree_insert_before (tree, tmpnode, height, FALSE);
9155 }
9156 else
9157 {
9158 tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]);
9159 tmpnode = _gtk_rbtree_insert_after (tree, tmpnode, height, FALSE);
9160 }
9161
9162 _gtk_tree_view_accessible_add (tree_view, tree, tmpnode);
9163
9164 done:
9165 if (height > 0)
9166 {
9167 if (tree)
9168 _gtk_rbtree_node_mark_valid (tree, tmpnode);
9169
9170 if (node_visible && node_is_visible (tree_view, tree, tmpnode))
9171 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9172 else
9173 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
9174 }
9175 else
9176 install_presize_handler (tree_view);
9177 if (free_path)
9178 gtk_tree_path_free (path);
9179 }
9180
9181 static void
gtk_tree_view_row_has_child_toggled(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)9182 gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
9183 GtkTreePath *path,
9184 GtkTreeIter *iter,
9185 gpointer data)
9186 {
9187 GtkTreeView *tree_view = (GtkTreeView *)data;
9188 GtkTreeIter real_iter;
9189 gboolean has_child;
9190 GtkRBTree *tree;
9191 GtkRBNode *node;
9192 gboolean free_path = FALSE;
9193
9194 g_return_if_fail (path != NULL || iter != NULL);
9195
9196 if (iter)
9197 real_iter = *iter;
9198
9199 if (path == NULL)
9200 {
9201 path = gtk_tree_model_get_path (model, iter);
9202 free_path = TRUE;
9203 }
9204 else if (iter == NULL)
9205 gtk_tree_model_get_iter (model, &real_iter, path);
9206
9207 if (_gtk_tree_view_find_node (tree_view,
9208 path,
9209 &tree,
9210 &node))
9211 /* We aren't actually showing the node */
9212 goto done;
9213
9214 if (tree == NULL)
9215 goto done;
9216
9217 has_child = gtk_tree_model_iter_has_child (model, &real_iter);
9218 /* Sanity check.
9219 */
9220 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child)
9221 goto done;
9222
9223 if (has_child)
9224 {
9225 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT);
9226 _gtk_tree_view_accessible_add_state (tree_view, tree, node, GTK_CELL_RENDERER_EXPANDABLE);
9227 }
9228 else
9229 {
9230 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
9231 _gtk_tree_view_accessible_remove_state (tree_view, tree, node, GTK_CELL_RENDERER_EXPANDABLE);
9232 }
9233
9234 if (has_child && tree_view->priv->is_list)
9235 {
9236 tree_view->priv->is_list = FALSE;
9237 if (tree_view->priv->show_expanders)
9238 {
9239 GList *list;
9240
9241 for (list = tree_view->priv->columns; list; list = list->next)
9242 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
9243 {
9244 _gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE);
9245 break;
9246 }
9247 }
9248 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9249 }
9250 else
9251 {
9252 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
9253 }
9254
9255 done:
9256 if (free_path)
9257 gtk_tree_path_free (path);
9258 }
9259
9260 static void
count_children_helper(GtkRBTree * tree,GtkRBNode * node,gpointer data)9261 count_children_helper (GtkRBTree *tree,
9262 GtkRBNode *node,
9263 gpointer data)
9264 {
9265 if (node->children)
9266 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, data);
9267 (*((gint *)data))++;
9268 }
9269
9270 static void
check_selection_helper(GtkRBTree * tree,GtkRBNode * node,gpointer data)9271 check_selection_helper (GtkRBTree *tree,
9272 GtkRBNode *node,
9273 gpointer data)
9274 {
9275 gint *value = (gint *)data;
9276
9277 *value |= GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
9278
9279 if (node->children && !*value)
9280 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data);
9281 }
9282
9283 static void
gtk_tree_view_row_deleted(GtkTreeModel * model,GtkTreePath * path,gpointer data)9284 gtk_tree_view_row_deleted (GtkTreeModel *model,
9285 GtkTreePath *path,
9286 gpointer data)
9287 {
9288 GtkTreeView *tree_view = (GtkTreeView *)data;
9289 GtkRBTree *tree;
9290 GtkRBNode *node;
9291 GList *list;
9292 gboolean selection_changed = FALSE, cursor_changed = FALSE;
9293 GtkRBTree *cursor_tree = NULL;
9294 GtkRBNode *cursor_node = NULL;
9295
9296 g_return_if_fail (path != NULL);
9297
9298 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
9299
9300 if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
9301 return;
9302
9303 if (tree == NULL)
9304 return;
9305
9306 /* check if the selection has been changed */
9307 _gtk_rbtree_traverse (tree, node, G_POST_ORDER,
9308 check_selection_helper, &selection_changed);
9309
9310 for (list = tree_view->priv->columns; list; list = list->next)
9311 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)) &&
9312 gtk_tree_view_column_get_sizing (GTK_TREE_VIEW_COLUMN (list->data)) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
9313 _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE);
9314
9315 /* Ensure we don't have a dangling pointer to a dead node */
9316 ensure_unprelighted (tree_view);
9317
9318 /* Cancel editting if we've started */
9319 gtk_tree_view_stop_editing (tree_view, TRUE);
9320
9321 /* If the cursor row got deleted, move the cursor to the next row */
9322 if (tree_view->priv->cursor_node &&
9323 (tree_view->priv->cursor_node == node ||
9324 (node->children && (tree_view->priv->cursor_tree == node->children ||
9325 _gtk_rbtree_contains (node->children, tree_view->priv->cursor_tree)))))
9326 {
9327 GtkTreePath *cursor_path;
9328
9329 cursor_tree = tree;
9330 cursor_node = _gtk_rbtree_next (tree, node);
9331 /* find the first node that is not going to be deleted */
9332 while (cursor_node == NULL && cursor_tree->parent_tree)
9333 {
9334 cursor_node = _gtk_rbtree_next (cursor_tree->parent_tree,
9335 cursor_tree->parent_node);
9336 cursor_tree = cursor_tree->parent_tree;
9337 }
9338
9339 if (cursor_node != NULL)
9340 cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9341 else
9342 cursor_path = NULL;
9343
9344 if (cursor_path == NULL ||
9345 ! search_first_focusable_path (tree_view, &cursor_path, TRUE,
9346 &cursor_tree, &cursor_node))
9347 {
9348 /* It looks like we reached the end of the view without finding
9349 * a focusable row. We will step backwards to find the last
9350 * focusable row.
9351 */
9352 _gtk_rbtree_prev_full (tree, node, &cursor_tree, &cursor_node);
9353 if (cursor_node)
9354 {
9355 cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9356 if (! search_first_focusable_path (tree_view, &cursor_path, FALSE,
9357 &cursor_tree, &cursor_node))
9358 cursor_node = NULL;
9359 gtk_tree_path_free (cursor_path);
9360 }
9361 }
9362 else if (cursor_path)
9363 gtk_tree_path_free (cursor_path);
9364
9365 cursor_changed = TRUE;
9366 }
9367
9368 if (tree_view->priv->destroy_count_func)
9369 {
9370 gint child_count = 0;
9371 if (node->children)
9372 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, &child_count);
9373 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
9374 }
9375
9376 if (tree->root->count == 1)
9377 {
9378 if (tree_view->priv->tree == tree)
9379 tree_view->priv->tree = NULL;
9380
9381 _gtk_tree_view_accessible_remove_state (tree_view,
9382 tree->parent_tree, tree->parent_node,
9383 GTK_CELL_RENDERER_EXPANDED);
9384 _gtk_tree_view_accessible_remove (tree_view, tree, NULL);
9385 _gtk_rbtree_remove (tree);
9386 }
9387 else
9388 {
9389 _gtk_tree_view_accessible_remove (tree_view, tree, node);
9390 _gtk_rbtree_remove_node (tree, node);
9391 }
9392
9393 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
9394 {
9395 gtk_tree_row_reference_free (tree_view->priv->top_row);
9396 tree_view->priv->top_row = NULL;
9397 }
9398
9399 install_scroll_sync_handler (tree_view);
9400
9401 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9402
9403 if (cursor_changed)
9404 {
9405 if (cursor_node)
9406 {
9407 GtkTreePath *cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
9408 gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CURSOR_INVALID);
9409 gtk_tree_path_free (cursor_path);
9410 }
9411 else
9412 gtk_tree_view_real_set_cursor (tree_view, NULL, CLEAR_AND_SELECT | CURSOR_INVALID);
9413 }
9414 if (selection_changed)
9415 g_signal_emit_by_name (tree_view->priv->selection, "changed");
9416 }
9417
9418 static void
gtk_tree_view_rows_reordered(GtkTreeModel * model,GtkTreePath * parent,GtkTreeIter * iter,gint * new_order,gpointer data)9419 gtk_tree_view_rows_reordered (GtkTreeModel *model,
9420 GtkTreePath *parent,
9421 GtkTreeIter *iter,
9422 gint *new_order,
9423 gpointer data)
9424 {
9425 GtkTreeView *tree_view = GTK_TREE_VIEW (data);
9426 GtkRBTree *tree;
9427 GtkRBNode *node;
9428 gint len;
9429
9430 len = gtk_tree_model_iter_n_children (model, iter);
9431
9432 if (len < 2)
9433 return;
9434
9435 gtk_tree_row_reference_reordered (G_OBJECT (data),
9436 parent,
9437 iter,
9438 new_order);
9439
9440 if (_gtk_tree_view_find_node (tree_view,
9441 parent,
9442 &tree,
9443 &node))
9444 return;
9445
9446 /* We need to special case the parent path */
9447 if (tree == NULL)
9448 tree = tree_view->priv->tree;
9449 else
9450 tree = node->children;
9451
9452 if (tree == NULL)
9453 return;
9454
9455 if (tree_view->priv->edited_column)
9456 gtk_tree_view_stop_editing (tree_view, TRUE);
9457
9458 /* we need to be unprelighted */
9459 ensure_unprelighted (tree_view);
9460
9461 _gtk_rbtree_reorder (tree, new_order, len);
9462
9463 _gtk_tree_view_accessible_reorder (tree_view);
9464
9465 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9466
9467 gtk_tree_view_dy_to_top_row (tree_view);
9468 }
9469
9470
9471 /* Internal tree functions
9472 */
9473
9474
9475 static void
gtk_tree_view_get_background_xrange(GtkTreeView * tree_view,GtkRBTree * tree,GtkTreeViewColumn * column,gint * x1,gint * x2)9476 gtk_tree_view_get_background_xrange (GtkTreeView *tree_view,
9477 GtkRBTree *tree,
9478 GtkTreeViewColumn *column,
9479 gint *x1,
9480 gint *x2)
9481 {
9482 GtkTreeViewColumn *tmp_column = NULL;
9483 gint total_width;
9484 GList *list;
9485 gboolean rtl;
9486
9487 if (x1)
9488 *x1 = 0;
9489
9490 if (x2)
9491 *x2 = 0;
9492
9493 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9494
9495 total_width = 0;
9496 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
9497 list;
9498 list = (rtl ? list->prev : list->next))
9499 {
9500 tmp_column = list->data;
9501
9502 if (tmp_column == column)
9503 break;
9504
9505 if (gtk_tree_view_column_get_visible (tmp_column))
9506 total_width += gtk_tree_view_column_get_width (tmp_column);
9507 }
9508
9509 if (tmp_column != column)
9510 {
9511 g_warning (G_STRLOC": passed-in column isn't in the tree");
9512 return;
9513 }
9514
9515 if (x1)
9516 *x1 = total_width;
9517
9518 if (x2)
9519 {
9520 if (gtk_tree_view_column_get_visible (column))
9521 *x2 = total_width + gtk_tree_view_column_get_width (column);
9522 else
9523 *x2 = total_width; /* width of 0 */
9524 }
9525 }
9526
9527 static void
gtk_tree_view_get_arrow_xrange(GtkTreeView * tree_view,GtkRBTree * tree,gint * x1,gint * x2)9528 gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
9529 GtkRBTree *tree,
9530 gint *x1,
9531 gint *x2)
9532 {
9533 gint x_offset = 0;
9534 GList *list;
9535 GtkTreeViewColumn *tmp_column = NULL;
9536 gint total_width;
9537 gint expander_size, expander_render_size;
9538 gint horizontal_separator;
9539 gboolean indent_expanders;
9540 gboolean rtl;
9541
9542 gtk_widget_style_get (GTK_WIDGET (tree_view),
9543 "indent-expanders", &indent_expanders,
9544 "horizontal-separator", &horizontal_separator,
9545 NULL);
9546
9547 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9548 expander_size = gtk_tree_view_get_expander_size (tree_view);
9549 expander_render_size = expander_size - (horizontal_separator / 2);
9550
9551 total_width = 0;
9552 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
9553 list;
9554 list = (rtl ? list->prev : list->next))
9555 {
9556 tmp_column = list->data;
9557
9558 if (gtk_tree_view_is_expander_column (tree_view, tmp_column))
9559 {
9560 if (rtl)
9561 x_offset = total_width + gtk_tree_view_column_get_width (tmp_column) - expander_size;
9562 else
9563 x_offset = total_width;
9564 break;
9565 }
9566
9567 if (gtk_tree_view_column_get_visible (tmp_column))
9568 total_width += gtk_tree_view_column_get_width (tmp_column);
9569 }
9570
9571 x_offset += (expander_size - expander_render_size);
9572
9573 if (indent_expanders)
9574 {
9575 if (rtl)
9576 x_offset -= expander_size * _gtk_rbtree_get_depth (tree);
9577 else
9578 x_offset += expander_size * _gtk_rbtree_get_depth (tree);
9579 }
9580
9581 *x1 = x_offset;
9582
9583 if (tmp_column &&
9584 gtk_tree_view_column_get_visible (tmp_column))
9585 *x2 = *x1 + expander_render_size;
9586 else
9587 *x2 = *x1;
9588 }
9589
9590 static void
gtk_tree_view_build_tree(GtkTreeView * tree_view,GtkRBTree * tree,GtkTreeIter * iter,gint depth,gboolean recurse)9591 gtk_tree_view_build_tree (GtkTreeView *tree_view,
9592 GtkRBTree *tree,
9593 GtkTreeIter *iter,
9594 gint depth,
9595 gboolean recurse)
9596 {
9597 GtkRBNode *temp = NULL;
9598 GtkTreePath *path = NULL;
9599
9600 do
9601 {
9602 gtk_tree_model_ref_node (tree_view->priv->model, iter);
9603 temp = _gtk_rbtree_insert_after (tree, temp, 0, FALSE);
9604
9605 if (tree_view->priv->fixed_height > 0)
9606 {
9607 if (GTK_RBNODE_FLAG_SET (temp, GTK_RBNODE_INVALID))
9608 {
9609 _gtk_rbtree_node_set_height (tree, temp, tree_view->priv->fixed_height);
9610 _gtk_rbtree_node_mark_valid (tree, temp);
9611 }
9612 }
9613
9614 if (tree_view->priv->is_list)
9615 continue;
9616
9617 if (recurse)
9618 {
9619 GtkTreeIter child;
9620
9621 if (!path)
9622 path = gtk_tree_model_get_path (tree_view->priv->model, iter);
9623 else
9624 gtk_tree_path_next (path);
9625
9626 if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter))
9627 {
9628 gboolean expand;
9629
9630 g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, iter, path, &expand);
9631
9632 if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter)
9633 && !expand)
9634 {
9635 temp->children = _gtk_rbtree_new ();
9636 temp->children->parent_tree = tree;
9637 temp->children->parent_node = temp;
9638 gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse);
9639 }
9640 }
9641 }
9642
9643 if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter))
9644 {
9645 if ((temp->flags>K_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT)
9646 temp->flags ^= GTK_RBNODE_IS_PARENT;
9647 }
9648 }
9649 while (gtk_tree_model_iter_next (tree_view->priv->model, iter));
9650
9651 if (path)
9652 gtk_tree_path_free (path);
9653 }
9654
9655 /* Make sure the node is visible vertically */
9656 static void
gtk_tree_view_clamp_node_visible(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)9657 gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
9658 GtkRBTree *tree,
9659 GtkRBNode *node)
9660 {
9661 gint node_dy, height;
9662 GtkTreePath *path = NULL;
9663
9664 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9665 return;
9666
9667 /* just return if the node is visible, avoiding a costly expose */
9668 node_dy = _gtk_rbtree_node_find_offset (tree, node);
9669 height = gtk_tree_view_get_row_height (tree_view, node);
9670 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID)
9671 && node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
9672 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
9673 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
9674 return;
9675
9676 path = _gtk_tree_path_new_from_rbtree (tree, node);
9677 if (path)
9678 {
9679 gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
9680 gtk_tree_path_free (path);
9681 }
9682 }
9683
9684 static void
gtk_tree_view_clamp_column_visible(GtkTreeView * tree_view,GtkTreeViewColumn * column,gboolean focus_to_cell)9685 gtk_tree_view_clamp_column_visible (GtkTreeView *tree_view,
9686 GtkTreeViewColumn *column,
9687 gboolean focus_to_cell)
9688 {
9689 GtkAllocation allocation;
9690 gint x, width;
9691
9692 if (column == NULL)
9693 return;
9694
9695 gtk_widget_get_allocation (gtk_tree_view_column_get_button (column), &allocation);
9696 x = allocation.x;
9697 width = allocation.width;
9698
9699 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
9700 {
9701 /* The column is larger than the horizontal page size. If the
9702 * column has cells which can be focused individually, then we make
9703 * sure the cell which gets focus is fully visible (if even the
9704 * focus cell is bigger than the page size, we make sure the
9705 * left-hand side of the cell is visible).
9706 *
9707 * If the column does not have an activatable cell, we
9708 * make sure the left-hand side of the column is visible.
9709 */
9710
9711 if (focus_to_cell && gtk_tree_view_has_can_focus_cell (tree_view))
9712 {
9713 GtkCellArea *cell_area;
9714 GtkCellRenderer *focus_cell;
9715
9716 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
9717 focus_cell = gtk_cell_area_get_focus_cell (cell_area);
9718
9719 if (gtk_tree_view_column_cell_get_position (column, focus_cell,
9720 &x, &width))
9721 {
9722 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
9723 {
9724 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment) < x + width)
9725 gtk_adjustment_set_value (tree_view->priv->hadjustment,
9726 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
9727 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
9728 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9729 }
9730 }
9731 }
9732
9733 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9734 }
9735 else
9736 {
9737 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
9738 gtk_adjustment_set_value (tree_view->priv->hadjustment,
9739 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
9740 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
9741 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9742 }
9743 }
9744
9745 /* This function could be more efficient. I'll optimize it if profiling seems
9746 * to imply that it is important */
9747 GtkTreePath *
_gtk_tree_path_new_from_rbtree(GtkRBTree * tree,GtkRBNode * node)9748 _gtk_tree_path_new_from_rbtree (GtkRBTree *tree,
9749 GtkRBNode *node)
9750 {
9751 GtkTreePath *path;
9752 GtkRBTree *tmp_tree;
9753 GtkRBNode *tmp_node, *last;
9754 gint count;
9755
9756 path = gtk_tree_path_new ();
9757
9758 g_return_val_if_fail (node != NULL, path);
9759
9760 count = 1 + node->left->count;
9761
9762 last = node;
9763 tmp_node = node->parent;
9764 tmp_tree = tree;
9765 while (tmp_tree)
9766 {
9767 while (!_gtk_rbtree_is_nil (tmp_node))
9768 {
9769 if (tmp_node->right == last)
9770 count += 1 + tmp_node->left->count;
9771 last = tmp_node;
9772 tmp_node = tmp_node->parent;
9773 }
9774 gtk_tree_path_prepend_index (path, count - 1);
9775 last = tmp_tree->parent_node;
9776 tmp_tree = tmp_tree->parent_tree;
9777 if (last)
9778 {
9779 count = 1 + last->left->count;
9780 tmp_node = last->parent;
9781 }
9782 }
9783 return path;
9784 }
9785
9786 /* Returns TRUE if we ran out of tree before finding the path. If the path is
9787 * invalid (ie. points to a node that’s not in the tree), *tree and *node are
9788 * both set to NULL.
9789 */
9790 gboolean
_gtk_tree_view_find_node(GtkTreeView * tree_view,GtkTreePath * path,GtkRBTree ** tree,GtkRBNode ** node)9791 _gtk_tree_view_find_node (GtkTreeView *tree_view,
9792 GtkTreePath *path,
9793 GtkRBTree **tree,
9794 GtkRBNode **node)
9795 {
9796 GtkRBNode *tmpnode = NULL;
9797 GtkRBTree *tmptree = tree_view->priv->tree;
9798 gint *indices = gtk_tree_path_get_indices (path);
9799 gint depth = gtk_tree_path_get_depth (path);
9800 gint i = 0;
9801
9802 *node = NULL;
9803 *tree = NULL;
9804
9805 if (depth == 0 || tmptree == NULL)
9806 return FALSE;
9807 do
9808 {
9809 tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
9810 ++i;
9811 if (tmpnode == NULL)
9812 {
9813 *tree = NULL;
9814 *node = NULL;
9815 return FALSE;
9816 }
9817 if (i >= depth)
9818 {
9819 *tree = tmptree;
9820 *node = tmpnode;
9821 return FALSE;
9822 }
9823 *tree = tmptree;
9824 *node = tmpnode;
9825 tmptree = tmpnode->children;
9826 if (tmptree == NULL)
9827 return TRUE;
9828 }
9829 while (1);
9830 }
9831
9832 static gboolean
gtk_tree_view_is_expander_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)9833 gtk_tree_view_is_expander_column (GtkTreeView *tree_view,
9834 GtkTreeViewColumn *column)
9835 {
9836 GList *list;
9837
9838 if (tree_view->priv->is_list)
9839 return FALSE;
9840
9841 if (tree_view->priv->expander_column != NULL)
9842 {
9843 if (tree_view->priv->expander_column == column)
9844 return TRUE;
9845 return FALSE;
9846 }
9847 else
9848 {
9849 for (list = tree_view->priv->columns;
9850 list;
9851 list = list->next)
9852 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
9853 break;
9854 if (list && list->data == column)
9855 return TRUE;
9856 }
9857 return FALSE;
9858 }
9859
9860 static inline gboolean
gtk_tree_view_draw_expanders(GtkTreeView * tree_view)9861 gtk_tree_view_draw_expanders (GtkTreeView *tree_view)
9862 {
9863 if (!tree_view->priv->is_list && tree_view->priv->show_expanders)
9864 return TRUE;
9865 /* else */
9866 return FALSE;
9867 }
9868
9869 static void
gtk_tree_view_add_move_binding(GtkBindingSet * binding_set,guint keyval,guint modmask,gboolean add_shifted_binding,GtkMovementStep step,gint count)9870 gtk_tree_view_add_move_binding (GtkBindingSet *binding_set,
9871 guint keyval,
9872 guint modmask,
9873 gboolean add_shifted_binding,
9874 GtkMovementStep step,
9875 gint count)
9876 {
9877
9878 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
9879 "move-cursor", 2,
9880 G_TYPE_ENUM, step,
9881 G_TYPE_INT, count);
9882
9883 if (add_shifted_binding)
9884 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
9885 "move-cursor", 2,
9886 G_TYPE_ENUM, step,
9887 G_TYPE_INT, count);
9888
9889 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
9890 return;
9891
9892 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
9893 "move-cursor", 2,
9894 G_TYPE_ENUM, step,
9895 G_TYPE_INT, count);
9896
9897 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
9898 "move-cursor", 2,
9899 G_TYPE_ENUM, step,
9900 G_TYPE_INT, count);
9901 }
9902
9903 static gint
gtk_tree_view_unref_tree_helper(GtkTreeModel * model,GtkTreeIter * iter,GtkRBTree * tree,GtkRBNode * node)9904 gtk_tree_view_unref_tree_helper (GtkTreeModel *model,
9905 GtkTreeIter *iter,
9906 GtkRBTree *tree,
9907 GtkRBNode *node)
9908 {
9909 gint retval = FALSE;
9910 do
9911 {
9912 g_return_val_if_fail (node != NULL, FALSE);
9913
9914 if (node->children)
9915 {
9916 GtkTreeIter child;
9917 GtkRBTree *new_tree;
9918 GtkRBNode *new_node;
9919
9920 new_tree = node->children;
9921 new_node = _gtk_rbtree_first (new_tree);
9922
9923 if (!gtk_tree_model_iter_children (model, &child, iter))
9924 return FALSE;
9925
9926 retval = gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node) | retval;
9927 }
9928
9929 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
9930 retval = TRUE;
9931 gtk_tree_model_unref_node (model, iter);
9932 node = _gtk_rbtree_next (tree, node);
9933 }
9934 while (gtk_tree_model_iter_next (model, iter));
9935
9936 return retval;
9937 }
9938
9939 static gint
gtk_tree_view_unref_and_check_selection_tree(GtkTreeView * tree_view,GtkRBTree * tree)9940 gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view,
9941 GtkRBTree *tree)
9942 {
9943 GtkTreeIter iter;
9944 GtkTreePath *path;
9945 GtkRBNode *node;
9946 gint retval;
9947
9948 if (!tree)
9949 return FALSE;
9950
9951 node = _gtk_rbtree_first (tree);
9952
9953 g_return_val_if_fail (node != NULL, FALSE);
9954 path = _gtk_tree_path_new_from_rbtree (tree, node);
9955 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model),
9956 &iter, path);
9957 retval = gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (tree_view->priv->model), &iter, tree, node);
9958 gtk_tree_path_free (path);
9959
9960 return retval;
9961 }
9962
9963 static void
gtk_tree_view_set_column_drag_info(GtkTreeView * tree_view,GtkTreeViewColumn * column)9964 gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view,
9965 GtkTreeViewColumn *column)
9966 {
9967 GtkTreeViewColumn *left_column;
9968 GtkTreeViewColumn *cur_column = NULL;
9969 GtkTreeViewColumnReorder *reorder;
9970 gboolean rtl;
9971 GList *tmp_list;
9972 gint left;
9973
9974 /* We want to precalculate the motion list such that we know what column slots
9975 * are available.
9976 */
9977 left_column = NULL;
9978 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9979
9980 /* First, identify all possible drop spots */
9981 if (rtl)
9982 tmp_list = g_list_last (tree_view->priv->columns);
9983 else
9984 tmp_list = g_list_first (tree_view->priv->columns);
9985
9986 while (tmp_list)
9987 {
9988 cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
9989 tmp_list = rtl ? tmp_list->prev : tmp_list->next;
9990
9991 if (gtk_tree_view_column_get_visible (cur_column) == FALSE)
9992 continue;
9993
9994 /* If it's not the column moving and func tells us to skip over the column, we continue. */
9995 if (left_column != column && cur_column != column &&
9996 tree_view->priv->column_drop_func &&
9997 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
9998 {
9999 left_column = cur_column;
10000 continue;
10001 }
10002 reorder = g_slice_new0 (GtkTreeViewColumnReorder);
10003 reorder->left_column = left_column;
10004 left_column = reorder->right_column = cur_column;
10005
10006 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
10007 }
10008
10009 /* Add the last one */
10010 if (tree_view->priv->column_drop_func == NULL ||
10011 ((left_column != column) &&
10012 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
10013 {
10014 reorder = g_slice_new0 (GtkTreeViewColumnReorder);
10015 reorder->left_column = left_column;
10016 reorder->right_column = NULL;
10017 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
10018 }
10019
10020 /* We quickly check to see if it even makes sense to reorder columns. */
10021 /* If there is nothing that can be moved, then we return */
10022
10023 if (tree_view->priv->column_drag_info == NULL)
10024 return;
10025
10026 /* We know there are always 2 slots possbile, as you can always return column. */
10027 /* If that's all there is, return */
10028 if (tree_view->priv->column_drag_info->next == NULL ||
10029 (tree_view->priv->column_drag_info->next->next == NULL &&
10030 ((GtkTreeViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
10031 ((GtkTreeViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
10032 {
10033 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
10034 g_slice_free (GtkTreeViewColumnReorder, tmp_list->data);
10035 g_list_free (tree_view->priv->column_drag_info);
10036 tree_view->priv->column_drag_info = NULL;
10037 return;
10038 }
10039 /* We fill in the ranges for the columns, now that we've isolated them */
10040 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
10041
10042 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
10043 {
10044 reorder = (GtkTreeViewColumnReorder *) tmp_list->data;
10045
10046 reorder->left_align = left;
10047 if (tmp_list->next != NULL)
10048 {
10049 GtkAllocation right_allocation, left_allocation;
10050 GtkWidget *left_button, *right_button;
10051
10052 g_assert (tmp_list->next->data);
10053
10054 right_button = gtk_tree_view_column_get_button (reorder->right_column);
10055 left_button = gtk_tree_view_column_get_button
10056 (((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column);
10057
10058 gtk_widget_get_allocation (right_button, &right_allocation);
10059 gtk_widget_get_allocation (left_button, &left_allocation);
10060 left = reorder->right_align = (right_allocation.x + right_allocation.width + left_allocation.x) / 2;
10061 }
10062 else
10063 {
10064 reorder->right_align = gdk_window_get_width (tree_view->priv->header_window)
10065 + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
10066 }
10067 }
10068 }
10069
10070 void
_gtk_tree_view_column_start_drag(GtkTreeView * tree_view,GtkTreeViewColumn * column,GdkDevice * device)10071 _gtk_tree_view_column_start_drag (GtkTreeView *tree_view,
10072 GtkTreeViewColumn *column,
10073 GdkDevice *device)
10074 {
10075 GtkAllocation allocation;
10076 GtkAllocation button_allocation;
10077 GtkWidget *button;
10078 GdkWindowAttr attributes;
10079 guint attributes_mask;
10080 GtkStyleContext *context;
10081
10082 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
10083 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
10084 g_return_if_fail (tree_view->priv->drag_window == NULL);
10085
10086 gtk_tree_view_set_column_drag_info (tree_view, column);
10087
10088 if (tree_view->priv->column_drag_info == NULL)
10089 return;
10090
10091 button = gtk_tree_view_column_get_button (column);
10092
10093 context = gtk_widget_get_style_context (button);
10094 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DND);
10095
10096 gtk_widget_get_allocation (button, &button_allocation);
10097
10098 attributes.window_type = GDK_WINDOW_CHILD;
10099 attributes.wclass = GDK_INPUT_OUTPUT;
10100 attributes.x = button_allocation.x;
10101 attributes.y = 0;
10102 attributes.width = button_allocation.width;
10103 attributes.height = button_allocation.height;
10104 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
10105 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_POINTER_MOTION_MASK;
10106 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
10107
10108 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->header_window,
10109 &attributes,
10110 attributes_mask);
10111 gtk_widget_register_window (GTK_WIDGET (tree_view), tree_view->priv->drag_window);
10112
10113 /* Kids, don't try this at home */
10114 g_object_ref (button);
10115 gtk_container_remove (GTK_CONTAINER (tree_view), button);
10116 gtk_widget_set_parent_window (button, tree_view->priv->drag_window);
10117 gtk_widget_set_parent (button, GTK_WIDGET (tree_view));
10118 g_object_unref (button);
10119
10120 gtk_widget_get_allocation (button, &button_allocation);
10121 tree_view->priv->drag_column_x = button_allocation.x;
10122 allocation = button_allocation;
10123 allocation.x = 0;
10124 gtk_widget_size_allocate (button, &allocation);
10125
10126 tree_view->priv->drag_column = column;
10127 gdk_window_show (tree_view->priv->drag_window);
10128
10129 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10130
10131 tree_view->priv->in_column_drag = TRUE;
10132
10133 /* Widget reparenting above unmaps and indirectly breaks
10134 * the implicit grab, replace it with an active one.
10135 */
10136 gdk_seat_grab (gdk_device_get_seat (device),
10137 tree_view->priv->drag_window,
10138 GDK_SEAT_CAPABILITY_ALL, FALSE,
10139 NULL, NULL, NULL, NULL);
10140
10141 gtk_gesture_set_state (tree_view->priv->column_drag_gesture,
10142 GTK_EVENT_SEQUENCE_CLAIMED);
10143 }
10144
10145 static void
gtk_tree_view_queue_draw_arrow(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)10146 gtk_tree_view_queue_draw_arrow (GtkTreeView *tree_view,
10147 GtkRBTree *tree,
10148 GtkRBNode *node)
10149 {
10150 GtkAllocation allocation;
10151 GdkRectangle rect;
10152
10153 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10154 return;
10155
10156 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
10157 rect.x = 0;
10158 rect.width = gtk_tree_view_get_expander_size (tree_view);
10159 rect.width = MAX (rect.width, MAX (tree_view->priv->width, allocation.width));
10160
10161 rect.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
10162 rect.height = gtk_tree_view_get_row_height (tree_view, node);
10163
10164 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
10165 }
10166
10167 void
_gtk_tree_view_queue_draw_node(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,const GdkRectangle * clip_rect)10168 _gtk_tree_view_queue_draw_node (GtkTreeView *tree_view,
10169 GtkRBTree *tree,
10170 GtkRBNode *node,
10171 const GdkRectangle *clip_rect)
10172 {
10173 GtkAllocation allocation;
10174 GdkRectangle rect;
10175
10176 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10177 return;
10178
10179 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
10180 rect.x = 0;
10181 rect.width = MAX (tree_view->priv->width, allocation.width);
10182
10183 rect.y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
10184 rect.height = gtk_tree_view_get_row_height (tree_view, node);
10185
10186 if (clip_rect)
10187 {
10188 GdkRectangle new_rect;
10189
10190 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
10191
10192 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
10193 }
10194 else
10195 {
10196 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
10197 }
10198 }
10199
10200 static inline gint
gtk_tree_view_get_effective_header_height(GtkTreeView * tree_view)10201 gtk_tree_view_get_effective_header_height (GtkTreeView *tree_view)
10202 {
10203 if (tree_view->priv->headers_visible)
10204 return tree_view->priv->header_height;
10205 /* else */
10206 return 0;
10207 }
10208
10209 gint
_gtk_tree_view_get_header_height(GtkTreeView * tree_view)10210 _gtk_tree_view_get_header_height (GtkTreeView *tree_view)
10211 {
10212 return tree_view->priv->header_height;
10213 }
10214
10215 void
_gtk_tree_view_get_row_separator_func(GtkTreeView * tree_view,GtkTreeViewRowSeparatorFunc * func,gpointer * data)10216 _gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view,
10217 GtkTreeViewRowSeparatorFunc *func,
10218 gpointer *data)
10219 {
10220 *func = tree_view->priv->row_separator_func;
10221 *data = tree_view->priv->row_separator_data;
10222 }
10223
10224 GtkTreePath *
_gtk_tree_view_get_anchor_path(GtkTreeView * tree_view)10225 _gtk_tree_view_get_anchor_path (GtkTreeView *tree_view)
10226 {
10227 if (tree_view->priv->anchor)
10228 return gtk_tree_row_reference_get_path (tree_view->priv->anchor);
10229
10230 return NULL;
10231 }
10232
10233 void
_gtk_tree_view_set_anchor_path(GtkTreeView * tree_view,GtkTreePath * anchor_path)10234 _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view,
10235 GtkTreePath *anchor_path)
10236 {
10237 if (tree_view->priv->anchor)
10238 {
10239 gtk_tree_row_reference_free (tree_view->priv->anchor);
10240 tree_view->priv->anchor = NULL;
10241 }
10242
10243 if (anchor_path && tree_view->priv->model)
10244 tree_view->priv->anchor =
10245 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10246 tree_view->priv->model, anchor_path);
10247 }
10248
10249 GtkRBTree *
_gtk_tree_view_get_rbtree(GtkTreeView * tree_view)10250 _gtk_tree_view_get_rbtree (GtkTreeView *tree_view)
10251 {
10252 return tree_view->priv->tree;
10253 }
10254
10255 gboolean
_gtk_tree_view_get_cursor_node(GtkTreeView * tree_view,GtkRBTree ** tree,GtkRBNode ** node)10256 _gtk_tree_view_get_cursor_node (GtkTreeView *tree_view,
10257 GtkRBTree **tree,
10258 GtkRBNode **node)
10259 {
10260 GtkTreeViewPrivate *priv;
10261
10262 priv = tree_view->priv;
10263
10264 if (priv->cursor_node == NULL)
10265 return FALSE;
10266
10267 *tree = priv->cursor_tree;
10268 *node = priv->cursor_node;
10269
10270 return TRUE;
10271 }
10272
10273 GdkWindow *
_gtk_tree_view_get_header_window(GtkTreeView * tree_view)10274 _gtk_tree_view_get_header_window (GtkTreeView *tree_view)
10275 {
10276 return tree_view->priv->header_window;
10277 }
10278
10279 GtkTreeViewColumn *
_gtk_tree_view_get_focus_column(GtkTreeView * tree_view)10280 _gtk_tree_view_get_focus_column (GtkTreeView *tree_view)
10281 {
10282 return tree_view->priv->focus_column;
10283 }
10284
10285 void
_gtk_tree_view_set_focus_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)10286 _gtk_tree_view_set_focus_column (GtkTreeView *tree_view,
10287 GtkTreeViewColumn *column)
10288 {
10289 GtkTreeViewColumn *old_column = tree_view->priv->focus_column;
10290
10291 if (old_column == column)
10292 return;
10293
10294 tree_view->priv->focus_column = column;
10295
10296 _gtk_tree_view_accessible_update_focus_column (tree_view,
10297 old_column,
10298 column);
10299 }
10300
10301
10302 static void
gtk_tree_view_queue_draw_path(GtkTreeView * tree_view,GtkTreePath * path,const GdkRectangle * clip_rect)10303 gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
10304 GtkTreePath *path,
10305 const GdkRectangle *clip_rect)
10306 {
10307 GtkRBTree *tree = NULL;
10308 GtkRBNode *node = NULL;
10309
10310 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
10311
10312 if (tree)
10313 _gtk_tree_view_queue_draw_node (tree_view, tree, node, clip_rect);
10314 }
10315
10316 /* x and y are the mouse position
10317 */
10318 static void
gtk_tree_view_draw_arrow(GtkTreeView * tree_view,cairo_t * cr,GtkRBTree * tree,GtkRBNode * node)10319 gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
10320 cairo_t *cr,
10321 GtkRBTree *tree,
10322 GtkRBNode *node)
10323 {
10324 GdkRectangle area;
10325 GtkStateFlags state = 0;
10326 GtkStyleContext *context;
10327 GtkWidget *widget;
10328 gint x_offset = 0;
10329 gint x2;
10330 gint vertical_separator;
10331 GtkCellRendererState flags = 0;
10332
10333 widget = GTK_WIDGET (tree_view);
10334 context = gtk_widget_get_style_context (widget);
10335
10336 gtk_widget_style_get (widget,
10337 "vertical-separator", &vertical_separator,
10338 NULL);
10339
10340 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
10341 return;
10342
10343 gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2);
10344
10345 area.x = x_offset;
10346 area.y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node,
10347 vertical_separator);
10348 area.width = x2 - x_offset;
10349 area.height = gtk_tree_view_get_cell_area_height (tree_view, node,
10350 vertical_separator);
10351
10352 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
10353 flags |= GTK_CELL_RENDERER_SELECTED;
10354
10355 if (node == tree_view->priv->prelight_node &&
10356 tree_view->priv->arrow_prelit)
10357 flags |= GTK_CELL_RENDERER_PRELIT;
10358
10359 state = gtk_cell_renderer_get_state (NULL, widget, flags);
10360
10361 if (node->children != NULL)
10362 state |= GTK_STATE_FLAG_CHECKED;
10363 else
10364 state &= ~(GTK_STATE_FLAG_CHECKED);
10365
10366 gtk_style_context_save (context);
10367
10368 gtk_style_context_set_state (context, state);
10369 gtk_style_context_add_class (context, GTK_STYLE_CLASS_EXPANDER);
10370
10371 /* Make sure area.height has the same parity as the "expander-size" style
10372 * property (which area.width is assumed to be exactly equal to). This is done
10373 * to avoid the arrow being vertically centered in a half-pixel, which would
10374 * result in a fuzzy rendering.
10375 */
10376 if (area.height % 2 != area.width % 2)
10377 {
10378 area.y += 1;
10379 area.height -= 1;
10380 }
10381
10382 gtk_render_expander (context, cr,
10383 area.x, area.y,
10384 area.width, area.height);
10385
10386 gtk_style_context_restore (context);
10387 }
10388
10389 static void
gtk_tree_view_focus_to_cursor(GtkTreeView * tree_view)10390 gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view)
10391
10392 {
10393 GtkTreePath *cursor_path;
10394
10395 if ((tree_view->priv->tree == NULL) ||
10396 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
10397 return;
10398
10399 cursor_path = NULL;
10400 if (tree_view->priv->cursor_node)
10401 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
10402 tree_view->priv->cursor_node);
10403
10404 if (cursor_path == NULL)
10405 {
10406 /* Consult the selection before defaulting to the
10407 * first focusable element
10408 */
10409 GList *selected_rows;
10410 GtkTreeModel *model;
10411 GtkTreeSelection *selection;
10412
10413 selection = gtk_tree_view_get_selection (tree_view);
10414 selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
10415
10416 if (selected_rows)
10417 {
10418 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
10419 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
10420 }
10421 else
10422 {
10423 cursor_path = gtk_tree_path_new_first ();
10424 search_first_focusable_path (tree_view, &cursor_path,
10425 TRUE, NULL, NULL);
10426 }
10427
10428 if (cursor_path)
10429 {
10430 if (gtk_tree_selection_get_mode (tree_view->priv->selection) == GTK_SELECTION_MULTIPLE)
10431 gtk_tree_view_real_set_cursor (tree_view, cursor_path, 0);
10432 else
10433 gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT);
10434 }
10435 }
10436
10437 if (cursor_path)
10438 {
10439 tree_view->priv->draw_keyfocus = TRUE;
10440
10441 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10442 gtk_tree_path_free (cursor_path);
10443
10444 if (tree_view->priv->focus_column == NULL)
10445 {
10446 GList *list;
10447 for (list = tree_view->priv->columns; list; list = list->next)
10448 {
10449 if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data)))
10450 {
10451 GtkCellArea *cell_area;
10452
10453 _gtk_tree_view_set_focus_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data));
10454
10455 /* This happens when the treeview initially grabs focus and there
10456 * is no column in focus, here we explicitly focus into the first cell */
10457 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
10458 if (!gtk_cell_area_get_focus_cell (cell_area))
10459 {
10460 gboolean rtl;
10461
10462 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10463 gtk_cell_area_focus (cell_area,
10464 rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT);
10465 }
10466
10467 break;
10468 }
10469 }
10470 }
10471 }
10472 }
10473
10474 static void
gtk_tree_view_move_cursor_up_down(GtkTreeView * tree_view,gint count)10475 gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
10476 gint count)
10477 {
10478 gint selection_count;
10479 GtkRBTree *new_cursor_tree = NULL;
10480 GtkRBNode *new_cursor_node = NULL;
10481 GtkTreePath *cursor_path = NULL;
10482 gboolean grab_focus = TRUE;
10483 gboolean selectable;
10484 GtkDirectionType direction;
10485 GtkCellArea *cell_area = NULL;
10486 GtkCellRenderer *last_focus_cell = NULL;
10487 GtkTreeIter iter;
10488
10489 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10490 return;
10491
10492 if (tree_view->priv->cursor_node == NULL)
10493 return;
10494
10495 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
10496 tree_view->priv->cursor_node);
10497
10498 direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
10499
10500 if (tree_view->priv->focus_column)
10501 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
10502
10503 /* If focus stays in the area for this row, then just return for this round */
10504 if (cell_area && (count == -1 || count == 1) &&
10505 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path))
10506 {
10507 gtk_tree_view_column_cell_set_cell_data (tree_view->priv->focus_column,
10508 tree_view->priv->model,
10509 &iter,
10510 GTK_RBNODE_FLAG_SET (tree_view->priv->cursor_node, GTK_RBNODE_IS_PARENT),
10511 tree_view->priv->cursor_node->children ? TRUE : FALSE);
10512
10513 /* Save the last cell that had focus, if we hit the end of the view we'll give
10514 * focus back to it. */
10515 last_focus_cell = gtk_cell_area_get_focus_cell (cell_area);
10516
10517 /* If focus stays in the area, no need to change the cursor row */
10518 if (gtk_cell_area_focus (cell_area, direction))
10519 return;
10520 }
10521
10522 selection_count = gtk_tree_selection_count_selected_rows (tree_view->priv->selection);
10523 selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
10524 tree_view->priv->cursor_node,
10525 cursor_path);
10526
10527 if (selection_count == 0
10528 && gtk_tree_selection_get_mode (tree_view->priv->selection) != GTK_SELECTION_NONE
10529 && !tree_view->priv->modify_selection_pressed
10530 && selectable)
10531 {
10532 /* Don't move the cursor, but just select the current node */
10533 new_cursor_tree = tree_view->priv->cursor_tree;
10534 new_cursor_node = tree_view->priv->cursor_node;
10535 }
10536 else
10537 {
10538 if (count == -1)
10539 _gtk_rbtree_prev_full (tree_view->priv->cursor_tree, tree_view->priv->cursor_node,
10540 &new_cursor_tree, &new_cursor_node);
10541 else
10542 _gtk_rbtree_next_full (tree_view->priv->cursor_tree, tree_view->priv->cursor_node,
10543 &new_cursor_tree, &new_cursor_node);
10544 }
10545
10546 gtk_tree_path_free (cursor_path);
10547
10548 if (new_cursor_node)
10549 {
10550 cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node);
10551
10552 search_first_focusable_path (tree_view, &cursor_path,
10553 (count != -1),
10554 &new_cursor_tree,
10555 &new_cursor_node);
10556
10557 if (cursor_path)
10558 gtk_tree_path_free (cursor_path);
10559 }
10560
10561 /*
10562 * If the list has only one item and multi-selection is set then select
10563 * the row (if not yet selected).
10564 */
10565 if (gtk_tree_selection_get_mode (tree_view->priv->selection) == GTK_SELECTION_MULTIPLE &&
10566 new_cursor_node == NULL)
10567 {
10568 if (count == -1)
10569 _gtk_rbtree_next_full (tree_view->priv->cursor_tree, tree_view->priv->cursor_node,
10570 &new_cursor_tree, &new_cursor_node);
10571 else
10572 _gtk_rbtree_prev_full (tree_view->priv->cursor_tree, tree_view->priv->cursor_node,
10573 &new_cursor_tree, &new_cursor_node);
10574
10575 if (new_cursor_node == NULL
10576 && !GTK_RBNODE_FLAG_SET (tree_view->priv->cursor_node, GTK_RBNODE_IS_SELECTED))
10577 {
10578 new_cursor_node = tree_view->priv->cursor_node;
10579 new_cursor_tree = tree_view->priv->cursor_tree;
10580 }
10581 else
10582 {
10583 new_cursor_tree = NULL;
10584 new_cursor_node = NULL;
10585 }
10586 }
10587
10588 if (new_cursor_node)
10589 {
10590 cursor_path = _gtk_tree_path_new_from_rbtree (new_cursor_tree, new_cursor_node);
10591 gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE);
10592 gtk_tree_path_free (cursor_path);
10593
10594 /* Give focus to the area in the new row */
10595 if (cell_area)
10596 gtk_cell_area_focus (cell_area, direction);
10597 }
10598 else
10599 {
10600 gtk_tree_view_clamp_node_visible (tree_view,
10601 tree_view->priv->cursor_tree,
10602 tree_view->priv->cursor_node);
10603
10604 if (!tree_view->priv->extend_selection_pressed)
10605 {
10606 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
10607 count < 0 ?
10608 GTK_DIR_UP : GTK_DIR_DOWN))
10609 {
10610 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
10611
10612 if (toplevel)
10613 gtk_widget_child_focus (toplevel,
10614 count < 0 ?
10615 GTK_DIR_TAB_BACKWARD :
10616 GTK_DIR_TAB_FORWARD);
10617
10618 grab_focus = FALSE;
10619 }
10620 }
10621 else
10622 {
10623 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10624 }
10625
10626 if (cell_area)
10627 gtk_cell_area_set_focus_cell (cell_area, last_focus_cell);
10628 }
10629
10630 if (grab_focus)
10631 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10632 }
10633
10634 static void
gtk_tree_view_move_cursor_page_up_down(GtkTreeView * tree_view,gint count)10635 gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
10636 gint count)
10637 {
10638 GtkTreePath *old_cursor_path = NULL;
10639 GtkTreePath *cursor_path = NULL;
10640 GtkRBTree *start_cursor_tree = NULL;
10641 GtkRBNode *start_cursor_node = NULL;
10642 GtkRBTree *cursor_tree;
10643 GtkRBNode *cursor_node;
10644 gint y;
10645 gint window_y;
10646 gint vertical_separator;
10647
10648 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10649 return;
10650
10651 if (tree_view->priv->cursor_node == NULL)
10652 return;
10653
10654 old_cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
10655 tree_view->priv->cursor_node);
10656
10657 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
10658
10659 y = _gtk_rbtree_node_find_offset (tree_view->priv->cursor_tree, tree_view->priv->cursor_node);
10660 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
10661 y += tree_view->priv->cursor_offset;
10662 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
10663 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
10664
10665 if (y >= gtk_tree_view_get_height (tree_view))
10666 y = gtk_tree_view_get_height (tree_view) - 1;
10667
10668 tree_view->priv->cursor_offset =
10669 _gtk_rbtree_find_offset (tree_view->priv->tree, y,
10670 &cursor_tree, &cursor_node);
10671
10672 if (cursor_tree == NULL)
10673 {
10674 /* FIXME: we lost the cursor. Should we try to get one? */
10675 gtk_tree_path_free (old_cursor_path);
10676 return;
10677 }
10678
10679 if (tree_view->priv->cursor_offset
10680 > gtk_tree_view_get_row_height (tree_view, cursor_node))
10681 {
10682 _gtk_rbtree_next_full (cursor_tree, cursor_node,
10683 &cursor_tree, &cursor_node);
10684 tree_view->priv->cursor_offset -= gtk_tree_view_get_row_height (tree_view, cursor_node);
10685 }
10686
10687 y -= tree_view->priv->cursor_offset;
10688 cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
10689
10690 start_cursor_tree = cursor_tree;
10691 start_cursor_node = cursor_node;
10692
10693 if (! search_first_focusable_path (tree_view, &cursor_path,
10694 (count != -1),
10695 &cursor_tree, &cursor_node))
10696 {
10697 /* It looks like we reached the end of the view without finding
10698 * a focusable row. We will step backwards to find the last
10699 * focusable row.
10700 */
10701 cursor_tree = start_cursor_tree;
10702 cursor_node = start_cursor_node;
10703 cursor_path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
10704
10705 search_first_focusable_path (tree_view, &cursor_path,
10706 (count == -1),
10707 &cursor_tree, &cursor_node);
10708 }
10709
10710 if (!cursor_path)
10711 goto cleanup;
10712
10713 /* update y */
10714 y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
10715
10716 gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT);
10717
10718 y -= window_y;
10719 gtk_tree_view_scroll_to_point (tree_view, -1, y);
10720 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10721 _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
10722
10723 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
10724 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10725
10726 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10727
10728 cleanup:
10729 gtk_tree_path_free (old_cursor_path);
10730 gtk_tree_path_free (cursor_path);
10731 }
10732
10733 static void
gtk_tree_view_move_cursor_left_right(GtkTreeView * tree_view,gint count)10734 gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
10735 gint count)
10736 {
10737 GtkTreePath *cursor_path = NULL;
10738 GtkTreeViewColumn *column;
10739 GtkTreeIter iter;
10740 GList *list;
10741 gboolean found_column = FALSE;
10742 gboolean rtl;
10743 GtkDirectionType direction;
10744 GtkCellArea *cell_area;
10745 GtkCellRenderer *last_focus_cell = NULL;
10746 GtkCellArea *last_focus_area = NULL;
10747
10748 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10749
10750 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10751 return;
10752
10753 if (tree_view->priv->cursor_node == NULL)
10754 return;
10755
10756 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
10757 tree_view->priv->cursor_node);
10758
10759 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
10760 {
10761 gtk_tree_path_free (cursor_path);
10762 return;
10763 }
10764 gtk_tree_path_free (cursor_path);
10765
10766 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
10767 if (tree_view->priv->focus_column)
10768 {
10769 /* Save the cell/area we are moving focus from, if moving the cursor
10770 * by one step hits the end we'll set focus back here */
10771 last_focus_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->focus_column));
10772 last_focus_cell = gtk_cell_area_get_focus_cell (last_focus_area);
10773
10774 for (; list; list = (rtl ? list->prev : list->next))
10775 {
10776 if (list->data == tree_view->priv->focus_column)
10777 break;
10778 }
10779 }
10780
10781 direction = count > 0 ? GTK_DIR_RIGHT : GTK_DIR_LEFT;
10782
10783 while (list)
10784 {
10785 column = list->data;
10786 if (gtk_tree_view_column_get_visible (column) == FALSE)
10787 goto loop_end;
10788
10789 gtk_tree_view_column_cell_set_cell_data (column,
10790 tree_view->priv->model,
10791 &iter,
10792 GTK_RBNODE_FLAG_SET (tree_view->priv->cursor_node, GTK_RBNODE_IS_PARENT),
10793 tree_view->priv->cursor_node->children ? TRUE : FALSE);
10794
10795 cell_area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column));
10796 if (gtk_cell_area_focus (cell_area, direction))
10797 {
10798 _gtk_tree_view_set_focus_column (tree_view, column);
10799 found_column = TRUE;
10800 break;
10801 }
10802
10803 loop_end:
10804 if (count == 1)
10805 list = rtl ? list->prev : list->next;
10806 else
10807 list = rtl ? list->next : list->prev;
10808 }
10809
10810 if (found_column)
10811 {
10812 if (!gtk_tree_view_has_can_focus_cell (tree_view))
10813 _gtk_tree_view_queue_draw_node (tree_view,
10814 tree_view->priv->cursor_tree,
10815 tree_view->priv->cursor_node,
10816 NULL);
10817 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10818 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10819 }
10820 else
10821 {
10822 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10823
10824 if (last_focus_area)
10825 gtk_cell_area_set_focus_cell (last_focus_area, last_focus_cell);
10826 }
10827
10828 gtk_tree_view_clamp_column_visible (tree_view,
10829 tree_view->priv->focus_column, TRUE);
10830 }
10831
10832 static void
gtk_tree_view_move_cursor_start_end(GtkTreeView * tree_view,gint count)10833 gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
10834 gint count)
10835 {
10836 GtkRBTree *cursor_tree;
10837 GtkRBNode *cursor_node;
10838 GtkTreePath *path;
10839 GtkTreePath *old_path;
10840
10841 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10842 return;
10843
10844 g_return_if_fail (tree_view->priv->tree != NULL);
10845
10846 gtk_tree_view_get_cursor (tree_view, &old_path, NULL);
10847
10848 cursor_tree = tree_view->priv->tree;
10849
10850 if (count == -1)
10851 {
10852 cursor_node = _gtk_rbtree_first (cursor_tree);
10853
10854 /* Now go forward to find the first focusable row. */
10855 path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
10856 search_first_focusable_path (tree_view, &path,
10857 TRUE, &cursor_tree, &cursor_node);
10858 }
10859 else
10860 {
10861 cursor_node = cursor_tree->root;
10862
10863 do
10864 {
10865 while (cursor_node && !_gtk_rbtree_is_nil (cursor_node->right))
10866 cursor_node = cursor_node->right;
10867 if (cursor_node->children == NULL)
10868 break;
10869
10870 cursor_tree = cursor_node->children;
10871 cursor_node = cursor_tree->root;
10872 }
10873 while (1);
10874
10875 /* Now go backwards to find last focusable row. */
10876 path = _gtk_tree_path_new_from_rbtree (cursor_tree, cursor_node);
10877 search_first_focusable_path (tree_view, &path,
10878 FALSE, &cursor_tree, &cursor_node);
10879 }
10880
10881 if (!path)
10882 goto cleanup;
10883
10884 if (gtk_tree_path_compare (old_path, path))
10885 {
10886 gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE);
10887 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10888 }
10889 else
10890 {
10891 gtk_widget_error_bell (GTK_WIDGET (tree_view));
10892 }
10893
10894 cleanup:
10895 gtk_tree_path_free (old_path);
10896 gtk_tree_path_free (path);
10897 }
10898
10899 static gboolean
gtk_tree_view_real_select_all(GtkTreeView * tree_view)10900 gtk_tree_view_real_select_all (GtkTreeView *tree_view)
10901 {
10902 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10903 return FALSE;
10904
10905 if (gtk_tree_selection_get_mode (tree_view->priv->selection) != GTK_SELECTION_MULTIPLE)
10906 return FALSE;
10907
10908 gtk_tree_selection_select_all (tree_view->priv->selection);
10909
10910 return TRUE;
10911 }
10912
10913 static gboolean
gtk_tree_view_real_unselect_all(GtkTreeView * tree_view)10914 gtk_tree_view_real_unselect_all (GtkTreeView *tree_view)
10915 {
10916 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10917 return FALSE;
10918
10919 if (gtk_tree_selection_get_mode (tree_view->priv->selection) != GTK_SELECTION_MULTIPLE)
10920 return FALSE;
10921
10922 gtk_tree_selection_unselect_all (tree_view->priv->selection);
10923
10924 return TRUE;
10925 }
10926
10927 static gboolean
gtk_tree_view_real_select_cursor_row(GtkTreeView * tree_view,gboolean start_editing)10928 gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
10929 gboolean start_editing)
10930 {
10931 GtkRBTree *new_tree = NULL;
10932 GtkRBNode *new_node = NULL;
10933 GtkRBTree *cursor_tree = NULL;
10934 GtkRBNode *cursor_node = NULL;
10935 GtkTreePath *cursor_path = NULL;
10936 GtkTreeSelectMode mode = 0;
10937
10938 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10939 return FALSE;
10940
10941 if (tree_view->priv->cursor_node == NULL)
10942 return FALSE;
10943
10944 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
10945 tree_view->priv->cursor_node);
10946
10947 _gtk_tree_view_find_node (tree_view, cursor_path,
10948 &cursor_tree, &cursor_node);
10949
10950 if (cursor_tree == NULL)
10951 {
10952 gtk_tree_path_free (cursor_path);
10953 return FALSE;
10954 }
10955
10956 if (!tree_view->priv->extend_selection_pressed && start_editing &&
10957 tree_view->priv->focus_column)
10958 {
10959 if (gtk_tree_view_start_editing (tree_view, cursor_path, FALSE))
10960 {
10961 gtk_tree_path_free (cursor_path);
10962 return TRUE;
10963 }
10964 }
10965
10966 if (tree_view->priv->modify_selection_pressed)
10967 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
10968 if (tree_view->priv->extend_selection_pressed)
10969 mode |= GTK_TREE_SELECT_MODE_EXTEND;
10970
10971 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
10972 cursor_node,
10973 cursor_tree,
10974 cursor_path,
10975 mode,
10976 FALSE);
10977
10978 /* We bail out if the original (tree, node) don't exist anymore after
10979 * handling the selection-changed callback. We do return TRUE because
10980 * the key press has been handled at this point.
10981 */
10982 _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
10983
10984 if (cursor_tree != new_tree || cursor_node != new_node)
10985 return FALSE;
10986
10987 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10988
10989 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10990 _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
10991
10992 if (!tree_view->priv->extend_selection_pressed)
10993 gtk_tree_view_row_activated (tree_view, cursor_path,
10994 tree_view->priv->focus_column);
10995
10996 gtk_tree_path_free (cursor_path);
10997
10998 return TRUE;
10999 }
11000
11001 static gboolean
gtk_tree_view_real_toggle_cursor_row(GtkTreeView * tree_view)11002 gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view)
11003 {
11004 GtkRBTree *new_tree = NULL;
11005 GtkRBNode *new_node = NULL;
11006 GtkTreePath *cursor_path = NULL;
11007
11008 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
11009 return FALSE;
11010
11011 if (tree_view->priv->cursor_node == NULL)
11012 return FALSE;
11013
11014 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
11015 tree_view->priv->cursor_node);
11016
11017 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
11018 tree_view->priv->cursor_node,
11019 tree_view->priv->cursor_tree,
11020 cursor_path,
11021 GTK_TREE_SELECT_MODE_TOGGLE,
11022 FALSE);
11023
11024 /* We bail out if the original (tree, node) don't exist anymore after
11025 * handling the selection-changed callback. We do return TRUE because
11026 * the key press has been handled at this point.
11027 */
11028 _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
11029
11030 if (tree_view->priv->cursor_node != new_node)
11031 return FALSE;
11032
11033 gtk_tree_view_clamp_node_visible (tree_view,
11034 tree_view->priv->cursor_tree,
11035 tree_view->priv->cursor_node);
11036
11037 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
11038 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
11039 gtk_tree_path_free (cursor_path);
11040
11041 return TRUE;
11042 }
11043
11044 static gboolean
gtk_tree_view_real_expand_collapse_cursor_row(GtkTreeView * tree_view,gboolean logical,gboolean expand,gboolean open_all)11045 gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view,
11046 gboolean logical,
11047 gboolean expand,
11048 gboolean open_all)
11049 {
11050 GtkTreePath *cursor_path = NULL;
11051
11052 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
11053 return FALSE;
11054
11055 if (tree_view->priv->cursor_node == NULL)
11056 return FALSE;
11057
11058 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
11059 tree_view->priv->cursor_node);
11060
11061 /* Don't handle the event if we aren't an expander */
11062 if (!GTK_RBNODE_FLAG_SET (tree_view->priv->cursor_node, GTK_RBNODE_IS_PARENT))
11063 return FALSE;
11064
11065 if (!logical
11066 && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
11067 expand = !expand;
11068
11069 if (expand)
11070 gtk_tree_view_real_expand_row (tree_view,
11071 cursor_path,
11072 tree_view->priv->cursor_tree,
11073 tree_view->priv->cursor_node,
11074 open_all,
11075 TRUE);
11076 else
11077 gtk_tree_view_real_collapse_row (tree_view,
11078 cursor_path,
11079 tree_view->priv->cursor_tree,
11080 tree_view->priv->cursor_node,
11081 TRUE);
11082
11083 gtk_tree_path_free (cursor_path);
11084
11085 return TRUE;
11086 }
11087
11088 static gboolean
gtk_tree_view_real_select_cursor_parent(GtkTreeView * tree_view)11089 gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
11090 {
11091 GtkTreePath *cursor_path = NULL;
11092 GdkModifierType state;
11093
11094 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
11095 goto out;
11096
11097 if (tree_view->priv->cursor_node == NULL)
11098 goto out;
11099
11100 cursor_path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
11101 tree_view->priv->cursor_node);
11102
11103 if (tree_view->priv->cursor_tree->parent_node)
11104 {
11105 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
11106
11107 gtk_tree_path_up (cursor_path);
11108
11109 if (gtk_get_current_event_state (&state))
11110 {
11111 GdkModifierType modify_mod_mask;
11112
11113 modify_mod_mask =
11114 gtk_widget_get_modifier_mask (GTK_WIDGET (tree_view),
11115 GDK_MODIFIER_INTENT_MODIFY_SELECTION);
11116
11117 if ((state & modify_mod_mask) == modify_mod_mask)
11118 tree_view->priv->modify_selection_pressed = TRUE;
11119 }
11120
11121 gtk_tree_view_real_set_cursor (tree_view, cursor_path, CLEAR_AND_SELECT | CLAMP_NODE);
11122 gtk_tree_path_free (cursor_path);
11123
11124 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
11125
11126 tree_view->priv->modify_selection_pressed = FALSE;
11127
11128 return TRUE;
11129 }
11130
11131 out:
11132
11133 tree_view->priv->search_entry_avoid_unhandled_binding = TRUE;
11134 return FALSE;
11135 }
11136
11137 static gboolean
gtk_tree_view_search_entry_flush_timeout(GtkTreeView * tree_view)11138 gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
11139 {
11140 gtk_tree_view_search_window_hide (tree_view->priv->search_window, tree_view, NULL);
11141 tree_view->priv->typeselect_flush_timeout = 0;
11142
11143 return FALSE;
11144 }
11145
11146 /* Cut and paste from gtkwindow.c */
11147 static void
send_focus_change(GtkWidget * widget,GdkDevice * device,gboolean in)11148 send_focus_change (GtkWidget *widget,
11149 GdkDevice *device,
11150 gboolean in)
11151 {
11152 GdkDeviceManager *device_manager;
11153 GList *devices, *d;
11154
11155 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
11156 device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
11157 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
11158 devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
11159 devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
11160 G_GNUC_END_IGNORE_DEPRECATIONS;
11161
11162 for (d = devices; d; d = d->next)
11163 {
11164 GdkDevice *dev = d->data;
11165 GdkEvent *fevent;
11166 GdkWindow *window;
11167
11168 if (gdk_device_get_source (dev) != GDK_SOURCE_KEYBOARD)
11169 continue;
11170
11171 window = gtk_widget_get_window (widget);
11172
11173 /* Skip non-master keyboards that haven't
11174 * selected for events from this window
11175 */
11176 if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
11177 !gdk_window_get_device_events (window, dev))
11178 continue;
11179
11180 fevent = gdk_event_new (GDK_FOCUS_CHANGE);
11181
11182 fevent->focus_change.type = GDK_FOCUS_CHANGE;
11183 fevent->focus_change.window = g_object_ref (window);
11184 fevent->focus_change.in = in;
11185 gdk_event_set_device (fevent, device);
11186
11187 gtk_widget_send_focus_change (widget, fevent);
11188
11189 gdk_event_free (fevent);
11190 }
11191
11192 g_list_free (devices);
11193 }
11194
11195 static void
gtk_tree_view_ensure_interactive_directory(GtkTreeView * tree_view)11196 gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
11197 {
11198 GtkWidget *frame, *vbox, *toplevel;
11199 GdkScreen *screen;
11200
11201 if (tree_view->priv->search_custom_entry_set)
11202 return;
11203
11204 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
11205 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
11206
11207 if (tree_view->priv->search_window != NULL)
11208 {
11209 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
11210 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
11211 GTK_WINDOW (tree_view->priv->search_window));
11212 else if (gtk_window_has_group (GTK_WINDOW (tree_view->priv->search_window)))
11213 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
11214 GTK_WINDOW (tree_view->priv->search_window));
11215
11216 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
11217
11218 return;
11219 }
11220
11221 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
11222 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
11223
11224 if (gtk_window_has_group (GTK_WINDOW (toplevel)))
11225 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
11226 GTK_WINDOW (tree_view->priv->search_window));
11227
11228 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
11229 GDK_WINDOW_TYPE_HINT_UTILITY);
11230 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
11231 gtk_window_set_transient_for (GTK_WINDOW (tree_view->priv->search_window),
11232 GTK_WINDOW (toplevel));
11233
11234 g_signal_connect (tree_view->priv->search_window, "delete-event",
11235 G_CALLBACK (gtk_tree_view_search_delete_event),
11236 tree_view);
11237 g_signal_connect (tree_view->priv->search_window, "key-press-event",
11238 G_CALLBACK (gtk_tree_view_search_key_press_event),
11239 tree_view);
11240 g_signal_connect (tree_view->priv->search_window, "button-press-event",
11241 G_CALLBACK (gtk_tree_view_search_button_press_event),
11242 tree_view);
11243 g_signal_connect (tree_view->priv->search_window, "scroll-event",
11244 G_CALLBACK (gtk_tree_view_search_scroll_event),
11245 tree_view);
11246
11247 frame = gtk_frame_new (NULL);
11248 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
11249 gtk_widget_show (frame);
11250 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
11251
11252 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
11253 gtk_widget_show (vbox);
11254 gtk_container_add (GTK_CONTAINER (frame), vbox);
11255 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
11256
11257 /* add entry */
11258 tree_view->priv->search_entry = gtk_entry_new ();
11259 gtk_widget_show (tree_view->priv->search_entry);
11260 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
11261 G_CALLBACK (gtk_tree_view_search_disable_popdown),
11262 tree_view);
11263 g_signal_connect (tree_view->priv->search_entry,
11264 "activate", G_CALLBACK (gtk_tree_view_search_activate),
11265 tree_view);
11266
11267 g_signal_connect (_gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry)),
11268 "preedit-changed",
11269 G_CALLBACK (gtk_tree_view_search_preedit_changed),
11270 tree_view);
11271 g_signal_connect (_gtk_entry_get_im_context (GTK_ENTRY (tree_view->priv->search_entry)),
11272 "commit",
11273 G_CALLBACK (gtk_tree_view_search_commit),
11274 tree_view);
11275
11276 gtk_container_add (GTK_CONTAINER (vbox),
11277 tree_view->priv->search_entry);
11278
11279 gtk_widget_realize (tree_view->priv->search_entry);
11280 }
11281
11282 /* Pops up the interactive search entry. If keybinding is TRUE then the user
11283 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
11284 */
11285 static gboolean
gtk_tree_view_real_start_interactive_search(GtkTreeView * tree_view,GdkDevice * device,gboolean keybinding)11286 gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
11287 GdkDevice *device,
11288 gboolean keybinding)
11289 {
11290 /* We only start interactive search if we have focus or the columns
11291 * have focus. If one of our children have focus, we don't want to
11292 * start the search.
11293 */
11294 GList *list;
11295 gboolean found_focus = FALSE;
11296
11297 if (!tree_view->priv->enable_search && !keybinding)
11298 return FALSE;
11299
11300 if (tree_view->priv->search_custom_entry_set)
11301 return FALSE;
11302
11303 if (tree_view->priv->search_window != NULL &&
11304 gtk_widget_get_visible (tree_view->priv->search_window))
11305 return TRUE;
11306
11307 for (list = tree_view->priv->columns; list; list = list->next)
11308 {
11309 GtkTreeViewColumn *column;
11310 GtkWidget *button;
11311
11312 column = list->data;
11313 if (!gtk_tree_view_column_get_visible (column))
11314 continue;
11315
11316 button = gtk_tree_view_column_get_button (column);
11317 if (gtk_widget_has_focus (button))
11318 {
11319 found_focus = TRUE;
11320 break;
11321 }
11322 }
11323
11324 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
11325 found_focus = TRUE;
11326
11327 if (!found_focus)
11328 return FALSE;
11329
11330 if (tree_view->priv->search_column < 0)
11331 return FALSE;
11332
11333 gtk_tree_view_ensure_interactive_directory (tree_view);
11334
11335 if (keybinding)
11336 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11337
11338 /* done, show it */
11339 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
11340
11341 /* Grab focus without selecting all the text. */
11342 gtk_entry_grab_focus_without_selecting (GTK_ENTRY (tree_view->priv->search_entry));
11343
11344 gtk_widget_show (tree_view->priv->search_window);
11345 if (tree_view->priv->search_entry_changed_id == 0)
11346 {
11347 tree_view->priv->search_entry_changed_id =
11348 g_signal_connect (tree_view->priv->search_entry, "changed",
11349 G_CALLBACK (gtk_tree_view_search_init),
11350 tree_view);
11351 }
11352
11353 tree_view->priv->typeselect_flush_timeout =
11354 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
11355 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
11356 tree_view);
11357 g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout");
11358
11359 /* send focus-in event */
11360 send_focus_change (tree_view->priv->search_entry, device, TRUE);
11361
11362 /* search first matching iter */
11363 gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
11364
11365 return TRUE;
11366 }
11367
11368 static gboolean
gtk_tree_view_start_interactive_search(GtkTreeView * tree_view)11369 gtk_tree_view_start_interactive_search (GtkTreeView *tree_view)
11370 {
11371 return gtk_tree_view_real_start_interactive_search (tree_view,
11372 gtk_get_current_event_device (),
11373 TRUE);
11374 }
11375
11376 /* Callbacks */
11377 static void
gtk_tree_view_adjustment_changed(GtkAdjustment * adjustment,GtkTreeView * tree_view)11378 gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
11379 GtkTreeView *tree_view)
11380 {
11381 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11382 {
11383 gint dy;
11384
11385 gdk_window_move (tree_view->priv->bin_window,
11386 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
11387 gtk_tree_view_get_effective_header_height (tree_view));
11388 gdk_window_move (tree_view->priv->header_window,
11389 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
11390 0);
11391 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
11392 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
11393
11394 if (dy != 0)
11395 {
11396 /* update our dy and top_row */
11397 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
11398
11399 update_prelight (tree_view,
11400 tree_view->priv->event_last_x,
11401 tree_view->priv->event_last_y);
11402
11403 if (!tree_view->priv->in_top_row_to_dy)
11404 gtk_tree_view_dy_to_top_row (tree_view);
11405
11406 }
11407 }
11408 }
11409
11410
11411
11412 /* Public methods
11413 */
11414
11415 /**
11416 * gtk_tree_view_new:
11417 *
11418 * Creates a new #GtkTreeView widget.
11419 *
11420 * Returns: A newly created #GtkTreeView widget.
11421 **/
11422 GtkWidget *
gtk_tree_view_new(void)11423 gtk_tree_view_new (void)
11424 {
11425 return g_object_new (GTK_TYPE_TREE_VIEW, NULL);
11426 }
11427
11428 /**
11429 * gtk_tree_view_new_with_model:
11430 * @model: the model.
11431 *
11432 * Creates a new #GtkTreeView widget with the model initialized to @model.
11433 *
11434 * Returns: A newly created #GtkTreeView widget.
11435 **/
11436 GtkWidget *
gtk_tree_view_new_with_model(GtkTreeModel * model)11437 gtk_tree_view_new_with_model (GtkTreeModel *model)
11438 {
11439 return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL);
11440 }
11441
11442 /* Public Accessors
11443 */
11444
11445 /**
11446 * gtk_tree_view_get_model:
11447 * @tree_view: a #GtkTreeView
11448 *
11449 * Returns the model the #GtkTreeView is based on. Returns %NULL if the
11450 * model is unset.
11451 *
11452 * Returns: (transfer none) (nullable): A #GtkTreeModel, or %NULL if
11453 * none is currently being used.
11454 **/
11455 GtkTreeModel *
gtk_tree_view_get_model(GtkTreeView * tree_view)11456 gtk_tree_view_get_model (GtkTreeView *tree_view)
11457 {
11458 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11459
11460 return tree_view->priv->model;
11461 }
11462
11463 /**
11464 * gtk_tree_view_set_model:
11465 * @tree_view: A #GtkTreeView.
11466 * @model: (allow-none): The model.
11467 *
11468 * Sets the model for a #GtkTreeView. If the @tree_view already has a model
11469 * set, it will remove it before setting the new model. If @model is %NULL,
11470 * then it will unset the old model.
11471 **/
11472 void
gtk_tree_view_set_model(GtkTreeView * tree_view,GtkTreeModel * model)11473 gtk_tree_view_set_model (GtkTreeView *tree_view,
11474 GtkTreeModel *model)
11475 {
11476 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11477 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
11478
11479 if (model == tree_view->priv->model)
11480 return;
11481
11482 if (tree_view->priv->scroll_to_path)
11483 {
11484 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
11485 tree_view->priv->scroll_to_path = NULL;
11486 }
11487
11488 if (tree_view->priv->rubber_band_status)
11489 gtk_tree_view_stop_rubber_band (tree_view);
11490
11491 if (tree_view->priv->model)
11492 {
11493 GList *tmplist = tree_view->priv->columns;
11494
11495 gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree);
11496 gtk_tree_view_stop_editing (tree_view, TRUE);
11497
11498 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11499 gtk_tree_view_row_changed,
11500 tree_view);
11501 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11502 gtk_tree_view_row_inserted,
11503 tree_view);
11504 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11505 gtk_tree_view_row_has_child_toggled,
11506 tree_view);
11507 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11508 gtk_tree_view_row_deleted,
11509 tree_view);
11510 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
11511 gtk_tree_view_rows_reordered,
11512 tree_view);
11513
11514 for (; tmplist; tmplist = tmplist->next)
11515 _gtk_tree_view_column_unset_model (tmplist->data,
11516 tree_view->priv->model);
11517
11518 if (tree_view->priv->tree)
11519 gtk_tree_view_free_rbtree (tree_view);
11520
11521 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11522 tree_view->priv->drag_dest_row = NULL;
11523 gtk_tree_row_reference_free (tree_view->priv->anchor);
11524 tree_view->priv->anchor = NULL;
11525 gtk_tree_row_reference_free (tree_view->priv->top_row);
11526 tree_view->priv->top_row = NULL;
11527 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
11528 tree_view->priv->scroll_to_path = NULL;
11529
11530 tree_view->priv->scroll_to_column = NULL;
11531
11532 g_object_unref (tree_view->priv->model);
11533
11534 tree_view->priv->search_column = -1;
11535 tree_view->priv->fixed_height_check = 0;
11536 tree_view->priv->fixed_height = -1;
11537 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
11538 }
11539
11540 tree_view->priv->model = model;
11541
11542 if (tree_view->priv->model)
11543 {
11544 gint i;
11545 GtkTreePath *path;
11546 GtkTreeIter iter;
11547 GtkTreeModelFlags flags;
11548
11549 if (tree_view->priv->search_column == -1)
11550 {
11551 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
11552 {
11553 GType type = gtk_tree_model_get_column_type (model, i);
11554
11555 if (g_value_type_transformable (type, G_TYPE_STRING))
11556 {
11557 tree_view->priv->search_column = i;
11558 break;
11559 }
11560 }
11561 }
11562
11563 g_object_ref (tree_view->priv->model);
11564 g_signal_connect (tree_view->priv->model,
11565 "row-changed",
11566 G_CALLBACK (gtk_tree_view_row_changed),
11567 tree_view);
11568 g_signal_connect (tree_view->priv->model,
11569 "row-inserted",
11570 G_CALLBACK (gtk_tree_view_row_inserted),
11571 tree_view);
11572 g_signal_connect (tree_view->priv->model,
11573 "row-has-child-toggled",
11574 G_CALLBACK (gtk_tree_view_row_has_child_toggled),
11575 tree_view);
11576 g_signal_connect (tree_view->priv->model,
11577 "row-deleted",
11578 G_CALLBACK (gtk_tree_view_row_deleted),
11579 tree_view);
11580 g_signal_connect (tree_view->priv->model,
11581 "rows-reordered",
11582 G_CALLBACK (gtk_tree_view_rows_reordered),
11583 tree_view);
11584
11585 flags = gtk_tree_model_get_flags (tree_view->priv->model);
11586 if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
11587 tree_view->priv->is_list = TRUE;
11588 else
11589 tree_view->priv->is_list = FALSE;
11590
11591 path = gtk_tree_path_new_first ();
11592 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path))
11593 {
11594 tree_view->priv->tree = _gtk_rbtree_new ();
11595 gtk_tree_view_build_tree (tree_view, tree_view->priv->tree, &iter, 1, FALSE);
11596 _gtk_tree_view_accessible_add (tree_view, tree_view->priv->tree, NULL);
11597 }
11598 gtk_tree_path_free (path);
11599
11600 /* FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */
11601 install_presize_handler (tree_view);
11602 }
11603
11604 gtk_tree_view_real_set_cursor (tree_view, NULL, CURSOR_INVALID);
11605
11606 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_MODEL]);
11607
11608 if (tree_view->priv->selection)
11609 _gtk_tree_selection_emit_changed (tree_view->priv->selection);
11610
11611 if (tree_view->priv->pixel_cache != NULL)
11612 _gtk_pixel_cache_set_always_cache (tree_view->priv->pixel_cache, (model != NULL));
11613
11614 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11615 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11616 }
11617
11618 /**
11619 * gtk_tree_view_get_selection:
11620 * @tree_view: A #GtkTreeView.
11621 *
11622 * Gets the #GtkTreeSelection associated with @tree_view.
11623 *
11624 * Returns: (transfer none): A #GtkTreeSelection object.
11625 **/
11626 GtkTreeSelection *
gtk_tree_view_get_selection(GtkTreeView * tree_view)11627 gtk_tree_view_get_selection (GtkTreeView *tree_view)
11628 {
11629 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11630
11631 return tree_view->priv->selection;
11632 }
11633
11634 /**
11635 * gtk_tree_view_get_hadjustment:
11636 * @tree_view: A #GtkTreeView
11637 *
11638 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
11639 *
11640 * Returns: (transfer none): A #GtkAdjustment object, or %NULL
11641 * if none is currently being used.
11642 *
11643 * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
11644 **/
11645 GtkAdjustment *
gtk_tree_view_get_hadjustment(GtkTreeView * tree_view)11646 gtk_tree_view_get_hadjustment (GtkTreeView *tree_view)
11647 {
11648 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11649
11650 return gtk_tree_view_do_get_hadjustment (tree_view);
11651 }
11652
11653 static GtkAdjustment *
gtk_tree_view_do_get_hadjustment(GtkTreeView * tree_view)11654 gtk_tree_view_do_get_hadjustment (GtkTreeView *tree_view)
11655 {
11656 return tree_view->priv->hadjustment;
11657 }
11658
11659 /**
11660 * gtk_tree_view_set_hadjustment:
11661 * @tree_view: A #GtkTreeView
11662 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
11663 *
11664 * Sets the #GtkAdjustment for the current horizontal aspect.
11665 *
11666 * Deprecated: 3.0: Use gtk_scrollable_set_hadjustment()
11667 **/
11668 void
gtk_tree_view_set_hadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)11669 gtk_tree_view_set_hadjustment (GtkTreeView *tree_view,
11670 GtkAdjustment *adjustment)
11671 {
11672 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11673 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
11674
11675 gtk_tree_view_do_set_hadjustment (tree_view, adjustment);
11676 }
11677
11678 static void
gtk_tree_view_do_set_hadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)11679 gtk_tree_view_do_set_hadjustment (GtkTreeView *tree_view,
11680 GtkAdjustment *adjustment)
11681 {
11682 GtkTreeViewPrivate *priv = tree_view->priv;
11683
11684 if (adjustment && priv->hadjustment == adjustment)
11685 return;
11686
11687 if (priv->hadjustment != NULL)
11688 {
11689 g_signal_handlers_disconnect_by_func (priv->hadjustment,
11690 gtk_tree_view_adjustment_changed,
11691 tree_view);
11692 g_object_unref (priv->hadjustment);
11693 }
11694
11695 if (adjustment == NULL)
11696 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
11697 0.0, 0.0, 0.0);
11698
11699 g_signal_connect (adjustment, "value-changed",
11700 G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view);
11701 priv->hadjustment = g_object_ref_sink (adjustment);
11702 /* FIXME: Adjustment should probably be populated here with fresh values, but
11703 * internal details are too complicated for me to decipher right now.
11704 */
11705 gtk_tree_view_adjustment_changed (NULL, tree_view);
11706
11707 g_object_notify (G_OBJECT (tree_view), "hadjustment");
11708 }
11709
11710 /**
11711 * gtk_tree_view_get_vadjustment:
11712 * @tree_view: A #GtkTreeView
11713 *
11714 * Gets the #GtkAdjustment currently being used for the vertical aspect.
11715 *
11716 * Returns: (transfer none): A #GtkAdjustment object, or %NULL
11717 * if none is currently being used.
11718 *
11719 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
11720 **/
11721 GtkAdjustment *
gtk_tree_view_get_vadjustment(GtkTreeView * tree_view)11722 gtk_tree_view_get_vadjustment (GtkTreeView *tree_view)
11723 {
11724 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11725
11726 return gtk_tree_view_do_get_vadjustment (tree_view);
11727 }
11728
11729 static GtkAdjustment *
gtk_tree_view_do_get_vadjustment(GtkTreeView * tree_view)11730 gtk_tree_view_do_get_vadjustment (GtkTreeView *tree_view)
11731 {
11732 return tree_view->priv->vadjustment;
11733 }
11734
11735 /**
11736 * gtk_tree_view_set_vadjustment:
11737 * @tree_view: A #GtkTreeView
11738 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
11739 *
11740 * Sets the #GtkAdjustment for the current vertical aspect.
11741 *
11742 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
11743 **/
11744 void
gtk_tree_view_set_vadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)11745 gtk_tree_view_set_vadjustment (GtkTreeView *tree_view,
11746 GtkAdjustment *adjustment)
11747 {
11748 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11749 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
11750
11751 gtk_tree_view_do_set_vadjustment (tree_view, adjustment);
11752 }
11753
11754 static void
gtk_tree_view_do_set_vadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)11755 gtk_tree_view_do_set_vadjustment (GtkTreeView *tree_view,
11756 GtkAdjustment *adjustment)
11757 {
11758 GtkTreeViewPrivate *priv = tree_view->priv;
11759
11760 if (adjustment && priv->vadjustment == adjustment)
11761 return;
11762
11763 if (priv->vadjustment != NULL)
11764 {
11765 g_signal_handlers_disconnect_by_func (priv->vadjustment,
11766 gtk_tree_view_adjustment_changed,
11767 tree_view);
11768 g_object_unref (priv->vadjustment);
11769 }
11770
11771 if (adjustment == NULL)
11772 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
11773 0.0, 0.0, 0.0);
11774
11775 g_signal_connect (adjustment, "value-changed",
11776 G_CALLBACK (gtk_tree_view_adjustment_changed), tree_view);
11777 priv->vadjustment = g_object_ref_sink (adjustment);
11778 /* FIXME: Adjustment should probably be populated here with fresh values, but
11779 * internal details are too complicated for me to decipher right now.
11780 */
11781 gtk_tree_view_adjustment_changed (NULL, tree_view);
11782 g_object_notify (G_OBJECT (tree_view), "vadjustment");
11783 }
11784
11785 /* Column and header operations */
11786
11787 /**
11788 * gtk_tree_view_get_headers_visible:
11789 * @tree_view: A #GtkTreeView.
11790 *
11791 * Returns %TRUE if the headers on the @tree_view are visible.
11792 *
11793 * Returns: Whether the headers are visible or not.
11794 **/
11795 gboolean
gtk_tree_view_get_headers_visible(GtkTreeView * tree_view)11796 gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
11797 {
11798 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11799
11800 return tree_view->priv->headers_visible;
11801 }
11802
11803 /**
11804 * gtk_tree_view_set_headers_visible:
11805 * @tree_view: A #GtkTreeView.
11806 * @headers_visible: %TRUE if the headers are visible
11807 *
11808 * Sets the visibility state of the headers.
11809 **/
11810 void
gtk_tree_view_set_headers_visible(GtkTreeView * tree_view,gboolean headers_visible)11811 gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
11812 gboolean headers_visible)
11813 {
11814 gint x, y;
11815 GList *list;
11816 GtkTreeViewColumn *column;
11817 GtkAllocation allocation;
11818 GtkWidget *button;
11819
11820 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11821
11822 headers_visible = !! headers_visible;
11823
11824 if (tree_view->priv->headers_visible == headers_visible)
11825 return;
11826
11827 tree_view->priv->headers_visible = headers_visible == TRUE;
11828
11829 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11830 {
11831 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
11832 if (headers_visible)
11833 {
11834 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
11835 gdk_window_move_resize (tree_view->priv->bin_window,
11836 x, y + gtk_tree_view_get_effective_header_height (tree_view),
11837 tree_view->priv->width, allocation.height - + gtk_tree_view_get_effective_header_height (tree_view));
11838
11839 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
11840 gtk_tree_view_map_buttons (tree_view);
11841 }
11842 else
11843 {
11844 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, gtk_tree_view_get_height (tree_view));
11845
11846 for (list = tree_view->priv->columns; list; list = list->next)
11847 {
11848 column = list->data;
11849 button = gtk_tree_view_column_get_button (column);
11850
11851 gtk_widget_hide (button);
11852 gtk_widget_unmap (button);
11853 }
11854 gdk_window_hide (tree_view->priv->header_window);
11855 }
11856 }
11857
11858 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
11859 gtk_adjustment_configure (tree_view->priv->vadjustment,
11860 gtk_adjustment_get_value (tree_view->priv->vadjustment),
11861 0,
11862 gtk_tree_view_get_height (tree_view),
11863 gtk_adjustment_get_step_increment (tree_view->priv->vadjustment),
11864 (allocation.height - gtk_tree_view_get_effective_header_height (tree_view)) / 2,
11865 allocation.height - gtk_tree_view_get_effective_header_height (tree_view));
11866
11867 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11868
11869 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_VISIBLE]);
11870 }
11871
11872 /**
11873 * gtk_tree_view_columns_autosize:
11874 * @tree_view: A #GtkTreeView.
11875 *
11876 * Resizes all columns to their optimal width. Only works after the
11877 * treeview has been realized.
11878 **/
11879 void
gtk_tree_view_columns_autosize(GtkTreeView * tree_view)11880 gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
11881 {
11882 gboolean dirty = FALSE;
11883 GList *list;
11884 GtkTreeViewColumn *column;
11885
11886 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11887
11888 for (list = tree_view->priv->columns; list; list = list->next)
11889 {
11890 column = list->data;
11891 if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
11892 continue;
11893 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
11894 dirty = TRUE;
11895 }
11896
11897 if (dirty)
11898 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11899 }
11900
11901 /**
11902 * gtk_tree_view_set_headers_clickable:
11903 * @tree_view: A #GtkTreeView.
11904 * @setting: %TRUE if the columns are clickable.
11905 *
11906 * Allow the column title buttons to be clicked.
11907 **/
11908 void
gtk_tree_view_set_headers_clickable(GtkTreeView * tree_view,gboolean setting)11909 gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view,
11910 gboolean setting)
11911 {
11912 GList *list;
11913 gboolean changed = FALSE;
11914
11915 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11916
11917 for (list = tree_view->priv->columns; list; list = list->next)
11918 {
11919 if (gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)) != setting)
11920 {
11921 gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting);
11922 changed = TRUE;
11923 }
11924 }
11925
11926 if (changed)
11927 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HEADERS_CLICKABLE]);
11928 }
11929
11930
11931 /**
11932 * gtk_tree_view_get_headers_clickable:
11933 * @tree_view: A #GtkTreeView.
11934 *
11935 * Returns whether all header columns are clickable.
11936 *
11937 * Returns: %TRUE if all header columns are clickable, otherwise %FALSE
11938 *
11939 * Since: 2.10
11940 **/
11941 gboolean
gtk_tree_view_get_headers_clickable(GtkTreeView * tree_view)11942 gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view)
11943 {
11944 GList *list;
11945
11946 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11947
11948 for (list = tree_view->priv->columns; list; list = list->next)
11949 if (!gtk_tree_view_column_get_clickable (GTK_TREE_VIEW_COLUMN (list->data)))
11950 return FALSE;
11951
11952 return TRUE;
11953 }
11954
11955 /**
11956 * gtk_tree_view_set_rules_hint:
11957 * @tree_view: a #GtkTreeView
11958 * @setting: %TRUE if the tree requires reading across rows
11959 *
11960 * Sets a hint for the theme to draw even/odd rows in the @tree_view
11961 * with different colors, also known as "zebra striping".
11962 *
11963 * This function tells the GTK+ theme that the user interface for your
11964 * application requires users to read across tree rows and associate
11965 * cells with one another.
11966 *
11967 * Do not use it just because you prefer the appearance of the ruled
11968 * tree; that’s a question for the theme. Some themes will draw tree
11969 * rows in alternating colors even when rules are turned off, and
11970 * users who prefer that appearance all the time can choose those
11971 * themes. You should call this function only as a semantic hint to
11972 * the theme engine that your tree makes alternating colors useful
11973 * from a functional standpoint (since it has lots of columns,
11974 * generally).
11975 *
11976 * Deprecated: 3.14
11977 */
11978 void
gtk_tree_view_set_rules_hint(GtkTreeView * tree_view,gboolean setting)11979 gtk_tree_view_set_rules_hint (GtkTreeView *tree_view,
11980 gboolean setting)
11981 {
11982 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11983
11984 setting = setting != FALSE;
11985
11986 if (tree_view->priv->has_rules != setting)
11987 {
11988 tree_view->priv->has_rules = setting;
11989 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
11990 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_RULES_HINT]);
11991 }
11992 }
11993
11994 /**
11995 * gtk_tree_view_get_rules_hint:
11996 * @tree_view: a #GtkTreeView
11997 *
11998 * Gets the setting set by gtk_tree_view_set_rules_hint().
11999 *
12000 * Returns: %TRUE if the hint is set
12001 *
12002 * Deprecated: 3.14
12003 */
12004 gboolean
gtk_tree_view_get_rules_hint(GtkTreeView * tree_view)12005 gtk_tree_view_get_rules_hint (GtkTreeView *tree_view)
12006 {
12007 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12008
12009 return tree_view->priv->has_rules;
12010 }
12011
12012
12013 /**
12014 * gtk_tree_view_set_activate_on_single_click:
12015 * @tree_view: a #GtkTreeView
12016 * @single: %TRUE to emit row-activated on a single click
12017 *
12018 * Cause the #GtkTreeView::row-activated signal to be emitted
12019 * on a single click instead of a double click.
12020 *
12021 * Since: 3.8
12022 **/
12023 void
gtk_tree_view_set_activate_on_single_click(GtkTreeView * tree_view,gboolean single)12024 gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view,
12025 gboolean single)
12026 {
12027 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12028
12029 single = single != FALSE;
12030
12031 if (tree_view->priv->activate_on_single_click == single)
12032 return;
12033
12034 tree_view->priv->activate_on_single_click = single;
12035 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ACTIVATE_ON_SINGLE_CLICK]);
12036 }
12037
12038 /**
12039 * gtk_tree_view_get_activate_on_single_click:
12040 * @tree_view: a #GtkTreeView
12041 *
12042 * Gets the setting set by gtk_tree_view_set_activate_on_single_click().
12043 *
12044 * Returns: %TRUE if row-activated will be emitted on a single click
12045 *
12046 * Since: 3.8
12047 **/
12048 gboolean
gtk_tree_view_get_activate_on_single_click(GtkTreeView * tree_view)12049 gtk_tree_view_get_activate_on_single_click (GtkTreeView *tree_view)
12050 {
12051 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12052
12053 return tree_view->priv->activate_on_single_click;
12054 }
12055
12056 /* Public Column functions
12057 */
12058
12059 /**
12060 * gtk_tree_view_append_column:
12061 * @tree_view: A #GtkTreeView.
12062 * @column: The #GtkTreeViewColumn to add.
12063 *
12064 * Appends @column to the list of columns. If @tree_view has “fixed_height”
12065 * mode enabled, then @column must have its “sizing” property set to be
12066 * GTK_TREE_VIEW_COLUMN_FIXED.
12067 *
12068 * Returns: The number of columns in @tree_view after appending.
12069 **/
12070 gint
gtk_tree_view_append_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)12071 gtk_tree_view_append_column (GtkTreeView *tree_view,
12072 GtkTreeViewColumn *column)
12073 {
12074 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
12075 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
12076 g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
12077
12078 return gtk_tree_view_insert_column (tree_view, column, -1);
12079 }
12080
12081 /**
12082 * gtk_tree_view_remove_column:
12083 * @tree_view: A #GtkTreeView.
12084 * @column: The #GtkTreeViewColumn to remove.
12085 *
12086 * Removes @column from @tree_view.
12087 *
12088 * Returns: The number of columns in @tree_view after removing.
12089 **/
12090 gint
gtk_tree_view_remove_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)12091 gtk_tree_view_remove_column (GtkTreeView *tree_view,
12092 GtkTreeViewColumn *column)
12093 {
12094 guint position;
12095
12096 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
12097 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
12098 g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view), -1);
12099
12100 if (tree_view->priv->focus_column == column)
12101 _gtk_tree_view_set_focus_column (tree_view, NULL);
12102
12103 if (tree_view->priv->edited_column == column)
12104 {
12105 gtk_tree_view_stop_editing (tree_view, TRUE);
12106
12107 /* no need to, but just to be sure ... */
12108 tree_view->priv->edited_column = NULL;
12109 }
12110
12111 if (tree_view->priv->expander_column == column)
12112 tree_view->priv->expander_column = NULL;
12113
12114 g_signal_handlers_disconnect_by_func (column,
12115 G_CALLBACK (column_sizing_notify),
12116 tree_view);
12117
12118 position = g_list_index (tree_view->priv->columns, column);
12119
12120 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12121 _gtk_tree_view_column_unrealize_button (column);
12122
12123 _gtk_tree_view_column_unset_tree_view (column);
12124
12125 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
12126 tree_view->priv->n_columns--;
12127
12128 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12129 {
12130 GList *list;
12131
12132 for (list = tree_view->priv->columns; list; list = list->next)
12133 {
12134 GtkTreeViewColumn *tmp_column;
12135
12136 tmp_column = GTK_TREE_VIEW_COLUMN (list->data);
12137 if (gtk_tree_view_column_get_visible (tmp_column))
12138 _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE);
12139 }
12140
12141 if (tree_view->priv->n_columns == 0 &&
12142 gtk_tree_view_get_headers_visible (tree_view))
12143 gdk_window_hide (tree_view->priv->header_window);
12144
12145 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
12146 }
12147
12148 _gtk_tree_view_accessible_remove_column (tree_view, column, position);
12149
12150 g_object_unref (column);
12151 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
12152
12153 return tree_view->priv->n_columns;
12154 }
12155
12156 /**
12157 * gtk_tree_view_insert_column:
12158 * @tree_view: A #GtkTreeView.
12159 * @column: The #GtkTreeViewColumn to be inserted.
12160 * @position: The position to insert @column in.
12161 *
12162 * This inserts the @column into the @tree_view at @position. If @position is
12163 * -1, then the column is inserted at the end. If @tree_view has
12164 * “fixed_height” mode enabled, then @column must have its “sizing” property
12165 * set to be GTK_TREE_VIEW_COLUMN_FIXED.
12166 *
12167 * Returns: The number of columns in @tree_view after insertion.
12168 **/
12169 gint
gtk_tree_view_insert_column(GtkTreeView * tree_view,GtkTreeViewColumn * column,gint position)12170 gtk_tree_view_insert_column (GtkTreeView *tree_view,
12171 GtkTreeViewColumn *column,
12172 gint position)
12173 {
12174 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
12175 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
12176 g_return_val_if_fail (gtk_tree_view_column_get_tree_view (column) == NULL, -1);
12177
12178 if (tree_view->priv->fixed_height_mode)
12179 g_return_val_if_fail (gtk_tree_view_column_get_sizing (column)
12180 == GTK_TREE_VIEW_COLUMN_FIXED, -1);
12181
12182 if (position < 0 || position > tree_view->priv->n_columns)
12183 position = tree_view->priv->n_columns;
12184
12185 g_object_ref_sink (column);
12186
12187 if (tree_view->priv->n_columns == 0 &&
12188 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
12189 gtk_tree_view_get_headers_visible (tree_view))
12190 {
12191 gdk_window_show (tree_view->priv->header_window);
12192 }
12193
12194 g_signal_connect (column, "notify::sizing",
12195 G_CALLBACK (column_sizing_notify), tree_view);
12196
12197 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
12198 column, position);
12199 tree_view->priv->n_columns++;
12200
12201 _gtk_tree_view_column_set_tree_view (column, tree_view);
12202
12203 gtk_tree_view_update_button_position (tree_view, column);
12204
12205 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12206 {
12207 GList *list;
12208
12209 _gtk_tree_view_column_realize_button (column);
12210
12211 for (list = tree_view->priv->columns; list; list = list->next)
12212 {
12213 column = GTK_TREE_VIEW_COLUMN (list->data);
12214 if (gtk_tree_view_column_get_visible (column))
12215 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
12216 }
12217 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
12218 }
12219
12220 _gtk_tree_view_accessible_add_column (tree_view, column, position);
12221
12222 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
12223
12224 return tree_view->priv->n_columns;
12225 }
12226
12227 /**
12228 * gtk_tree_view_insert_column_with_attributes:
12229 * @tree_view: A #GtkTreeView
12230 * @position: The position to insert the new column in
12231 * @title: The title to set the header to
12232 * @cell: The #GtkCellRenderer
12233 * @...: A %NULL-terminated list of attributes
12234 *
12235 * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at
12236 * @position. If @position is -1, then the newly created column is inserted at
12237 * the end. The column is initialized with the attributes given. If @tree_view
12238 * has “fixed_height” mode enabled, then the new column will have its sizing
12239 * property set to be GTK_TREE_VIEW_COLUMN_FIXED.
12240 *
12241 * Returns: The number of columns in @tree_view after insertion.
12242 **/
12243 gint
gtk_tree_view_insert_column_with_attributes(GtkTreeView * tree_view,gint position,const gchar * title,GtkCellRenderer * cell,...)12244 gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view,
12245 gint position,
12246 const gchar *title,
12247 GtkCellRenderer *cell,
12248 ...)
12249 {
12250 GtkTreeViewColumn *column;
12251 gchar *attribute;
12252 va_list args;
12253 gint column_id;
12254
12255 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
12256
12257 column = gtk_tree_view_column_new ();
12258 if (tree_view->priv->fixed_height_mode)
12259 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
12260
12261 gtk_tree_view_column_set_title (column, title);
12262 gtk_tree_view_column_pack_start (column, cell, TRUE);
12263
12264 va_start (args, cell);
12265
12266 attribute = va_arg (args, gchar *);
12267
12268 while (attribute != NULL)
12269 {
12270 column_id = va_arg (args, gint);
12271 gtk_tree_view_column_add_attribute (column, cell, attribute, column_id);
12272 attribute = va_arg (args, gchar *);
12273 }
12274
12275 va_end (args);
12276
12277 return gtk_tree_view_insert_column (tree_view, column, position);
12278 }
12279
12280 /**
12281 * gtk_tree_view_insert_column_with_data_func:
12282 * @tree_view: a #GtkTreeView
12283 * @position: Position to insert, -1 for append
12284 * @title: column title
12285 * @cell: cell renderer for column
12286 * @func: function to set attributes of cell renderer
12287 * @data: data for @func
12288 * @dnotify: destroy notifier for @data
12289 *
12290 * Convenience function that inserts a new column into the #GtkTreeView
12291 * with the given cell renderer and a #GtkTreeCellDataFunc to set cell renderer
12292 * attributes (normally using data from the model). See also
12293 * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start().
12294 * If @tree_view has “fixed_height” mode enabled, then the new column will have its
12295 * “sizing” property set to be GTK_TREE_VIEW_COLUMN_FIXED.
12296 *
12297 * Returns: number of columns in the tree view post-insert
12298 **/
12299 gint
gtk_tree_view_insert_column_with_data_func(GtkTreeView * tree_view,gint position,const gchar * title,GtkCellRenderer * cell,GtkTreeCellDataFunc func,gpointer data,GDestroyNotify dnotify)12300 gtk_tree_view_insert_column_with_data_func (GtkTreeView *tree_view,
12301 gint position,
12302 const gchar *title,
12303 GtkCellRenderer *cell,
12304 GtkTreeCellDataFunc func,
12305 gpointer data,
12306 GDestroyNotify dnotify)
12307 {
12308 GtkTreeViewColumn *column;
12309
12310 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
12311
12312 column = gtk_tree_view_column_new ();
12313 if (tree_view->priv->fixed_height_mode)
12314 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
12315
12316 gtk_tree_view_column_set_title (column, title);
12317 gtk_tree_view_column_pack_start (column, cell, TRUE);
12318 gtk_tree_view_column_set_cell_data_func (column, cell, func, data, dnotify);
12319
12320 return gtk_tree_view_insert_column (tree_view, column, position);
12321 }
12322
12323 /**
12324 * gtk_tree_view_get_n_columns:
12325 * @tree_view: a #GtkTreeView
12326 *
12327 * Queries the number of columns in the given @tree_view.
12328 *
12329 * Returns: The number of columns in the @tree_view
12330 *
12331 * Since: 3.4
12332 **/
12333 guint
gtk_tree_view_get_n_columns(GtkTreeView * tree_view)12334 gtk_tree_view_get_n_columns (GtkTreeView *tree_view)
12335 {
12336 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
12337
12338 return tree_view->priv->n_columns;
12339 }
12340
12341 /**
12342 * gtk_tree_view_get_column:
12343 * @tree_view: A #GtkTreeView.
12344 * @n: The position of the column, counting from 0.
12345 *
12346 * Gets the #GtkTreeViewColumn at the given position in the #tree_view.
12347 *
12348 * Returns: (nullable) (transfer none): The #GtkTreeViewColumn, or %NULL if the
12349 * position is outside the range of columns.
12350 **/
12351 GtkTreeViewColumn *
gtk_tree_view_get_column(GtkTreeView * tree_view,gint n)12352 gtk_tree_view_get_column (GtkTreeView *tree_view,
12353 gint n)
12354 {
12355 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12356
12357 if (n < 0 || n >= tree_view->priv->n_columns)
12358 return NULL;
12359
12360 if (tree_view->priv->columns == NULL)
12361 return NULL;
12362
12363 return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
12364 }
12365
12366 /**
12367 * gtk_tree_view_get_columns:
12368 * @tree_view: A #GtkTreeView
12369 *
12370 * Returns a #GList of all the #GtkTreeViewColumn s currently in @tree_view.
12371 * The returned list must be freed with g_list_free ().
12372 *
12373 * Returns: (element-type GtkTreeViewColumn) (transfer container): A list of #GtkTreeViewColumn s
12374 **/
12375 GList *
gtk_tree_view_get_columns(GtkTreeView * tree_view)12376 gtk_tree_view_get_columns (GtkTreeView *tree_view)
12377 {
12378 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12379
12380 return g_list_copy (tree_view->priv->columns);
12381 }
12382
12383 /**
12384 * gtk_tree_view_move_column_after:
12385 * @tree_view: A #GtkTreeView
12386 * @column: The #GtkTreeViewColumn to be moved.
12387 * @base_column: (allow-none): The #GtkTreeViewColumn to be moved relative to, or %NULL.
12388 *
12389 * Moves @column to be after to @base_column. If @base_column is %NULL, then
12390 * @column is placed in the first position.
12391 **/
12392 void
gtk_tree_view_move_column_after(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkTreeViewColumn * base_column)12393 gtk_tree_view_move_column_after (GtkTreeView *tree_view,
12394 GtkTreeViewColumn *column,
12395 GtkTreeViewColumn *base_column)
12396 {
12397 GList *column_list_el, *base_el = NULL;
12398
12399 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12400
12401 column_list_el = g_list_find (tree_view->priv->columns, column);
12402 g_return_if_fail (column_list_el != NULL);
12403
12404 if (base_column)
12405 {
12406 base_el = g_list_find (tree_view->priv->columns, base_column);
12407 g_return_if_fail (base_el != NULL);
12408 }
12409
12410 if (column_list_el->prev == base_el)
12411 return;
12412
12413 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
12414 if (base_el == NULL)
12415 {
12416 column_list_el->prev = NULL;
12417 column_list_el->next = tree_view->priv->columns;
12418 if (column_list_el->next)
12419 column_list_el->next->prev = column_list_el;
12420 tree_view->priv->columns = column_list_el;
12421 }
12422 else
12423 {
12424 column_list_el->prev = base_el;
12425 column_list_el->next = base_el->next;
12426 if (column_list_el->next)
12427 column_list_el->next->prev = column_list_el;
12428 base_el->next = column_list_el;
12429 }
12430
12431 gtk_tree_view_update_button_position (tree_view, column);
12432
12433 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12434 {
12435 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
12436 gtk_tree_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
12437 }
12438
12439 _gtk_tree_view_accessible_reorder_column (tree_view, column);
12440
12441 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
12442 }
12443
12444 /**
12445 * gtk_tree_view_set_expander_column:
12446 * @tree_view: A #GtkTreeView
12447 * @column: (nullable): %NULL, or the column to draw the expander arrow at.
12448 *
12449 * Sets the column to draw the expander arrow at. It must be in @tree_view.
12450 * If @column is %NULL, then the expander arrow is always at the first
12451 * visible column.
12452 *
12453 * If you do not want expander arrow to appear in your tree, set the
12454 * expander column to a hidden column.
12455 **/
12456 void
gtk_tree_view_set_expander_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)12457 gtk_tree_view_set_expander_column (GtkTreeView *tree_view,
12458 GtkTreeViewColumn *column)
12459 {
12460 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12461 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
12462 g_return_if_fail (column == NULL || gtk_tree_view_column_get_tree_view (column) == GTK_WIDGET (tree_view));
12463
12464 if (tree_view->priv->expander_column != column)
12465 {
12466 tree_view->priv->expander_column = column;
12467 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_EXPANDER_COLUMN]);
12468 }
12469 }
12470
12471 /**
12472 * gtk_tree_view_get_expander_column:
12473 * @tree_view: A #GtkTreeView
12474 *
12475 * Returns the column that is the current expander column.
12476 * This column has the expander arrow drawn next to it.
12477 *
12478 * Returns: (transfer none): The expander column.
12479 **/
12480 GtkTreeViewColumn *
gtk_tree_view_get_expander_column(GtkTreeView * tree_view)12481 gtk_tree_view_get_expander_column (GtkTreeView *tree_view)
12482 {
12483 GList *list;
12484
12485 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12486
12487 for (list = tree_view->priv->columns; list; list = list->next)
12488 if (gtk_tree_view_is_expander_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data)))
12489 return (GtkTreeViewColumn *) list->data;
12490 return NULL;
12491 }
12492
12493
12494 /**
12495 * gtk_tree_view_set_column_drag_function:
12496 * @tree_view: A #GtkTreeView.
12497 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
12498 * @user_data: (allow-none): User data to be passed to @func, or %NULL
12499 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
12500 *
12501 * Sets a user function for determining where a column may be dropped when
12502 * dragged. This function is called on every column pair in turn at the
12503 * beginning of a column drag to determine where a drop can take place. The
12504 * arguments passed to @func are: the @tree_view, the #GtkTreeViewColumn being
12505 * dragged, the two #GtkTreeViewColumn s determining the drop spot, and
12506 * @user_data. If either of the #GtkTreeViewColumn arguments for the drop spot
12507 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
12508 * @tree_view reverts to the default behavior of allowing all columns to be
12509 * dropped everywhere.
12510 **/
12511 void
gtk_tree_view_set_column_drag_function(GtkTreeView * tree_view,GtkTreeViewColumnDropFunc func,gpointer user_data,GDestroyNotify destroy)12512 gtk_tree_view_set_column_drag_function (GtkTreeView *tree_view,
12513 GtkTreeViewColumnDropFunc func,
12514 gpointer user_data,
12515 GDestroyNotify destroy)
12516 {
12517 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12518
12519 if (tree_view->priv->column_drop_func_data_destroy)
12520 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
12521
12522 tree_view->priv->column_drop_func = func;
12523 tree_view->priv->column_drop_func_data = user_data;
12524 tree_view->priv->column_drop_func_data_destroy = destroy;
12525 }
12526
12527 /**
12528 * gtk_tree_view_scroll_to_point:
12529 * @tree_view: a #GtkTreeView
12530 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
12531 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
12532 *
12533 * Scrolls the tree view such that the top-left corner of the visible
12534 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
12535 * in tree coordinates. The @tree_view must be realized before
12536 * this function is called. If it isn't, you probably want to be
12537 * using gtk_tree_view_scroll_to_cell().
12538 *
12539 * If either @tree_x or @tree_y are -1, then that direction isn’t scrolled.
12540 **/
12541 void
gtk_tree_view_scroll_to_point(GtkTreeView * tree_view,gint tree_x,gint tree_y)12542 gtk_tree_view_scroll_to_point (GtkTreeView *tree_view,
12543 gint tree_x,
12544 gint tree_y)
12545 {
12546 GtkAdjustment *hadj;
12547 GtkAdjustment *vadj;
12548
12549 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12550 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
12551
12552 hadj = tree_view->priv->hadjustment;
12553 vadj = tree_view->priv->vadjustment;
12554
12555 if (tree_x != -1)
12556 gtk_adjustment_animate_to_value (hadj, tree_x);
12557 if (tree_y != -1)
12558 gtk_adjustment_animate_to_value (vadj, tree_y);
12559 }
12560
12561 /**
12562 * gtk_tree_view_scroll_to_cell:
12563 * @tree_view: A #GtkTreeView.
12564 * @path: (allow-none): The path of the row to move to, or %NULL.
12565 * @column: (allow-none): The #GtkTreeViewColumn to move horizontally to, or %NULL.
12566 * @use_align: whether to use alignment arguments, or %FALSE.
12567 * @row_align: The vertical alignment of the row specified by @path.
12568 * @col_align: The horizontal alignment of the column specified by @column.
12569 *
12570 * Moves the alignments of @tree_view to the position specified by @column and
12571 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
12572 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
12573 * or @path need to be non-%NULL. @row_align determines where the row is
12574 * placed, and @col_align determines where @column is placed. Both are expected
12575 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
12576 * right/bottom alignment, 0.5 means center.
12577 *
12578 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
12579 * tree does the minimum amount of work to scroll the cell onto the screen.
12580 * This means that the cell will be scrolled to the edge closest to its current
12581 * position. If the cell is currently visible on the screen, nothing is done.
12582 *
12583 * This function only works if the model is set, and @path is a valid row on the
12584 * model. If the model changes before the @tree_view is realized, the centered
12585 * path will be modified to reflect this change.
12586 **/
12587 void
gtk_tree_view_scroll_to_cell(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gboolean use_align,gfloat row_align,gfloat col_align)12588 gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view,
12589 GtkTreePath *path,
12590 GtkTreeViewColumn *column,
12591 gboolean use_align,
12592 gfloat row_align,
12593 gfloat col_align)
12594 {
12595 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12596 g_return_if_fail (tree_view->priv->model != NULL);
12597 g_return_if_fail (tree_view->priv->tree != NULL);
12598 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
12599 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
12600 g_return_if_fail (path != NULL || column != NULL);
12601
12602 row_align = CLAMP (row_align, 0.0, 1.0);
12603 col_align = CLAMP (col_align, 0.0, 1.0);
12604
12605
12606 /* Note: Despite the benefits that come from having one code path for the
12607 * scrolling code, we short-circuit validate_visible_area's immplementation as
12608 * it is much slower than just going to the point.
12609 */
12610 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
12611 !gtk_widget_get_realized (GTK_WIDGET (tree_view)) ||
12612 _gtk_widget_get_alloc_needed (GTK_WIDGET (tree_view)) ||
12613 GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID))
12614 {
12615 if (tree_view->priv->scroll_to_path)
12616 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
12617
12618 tree_view->priv->scroll_to_path = NULL;
12619 tree_view->priv->scroll_to_column = NULL;
12620
12621 if (path)
12622 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
12623 if (column)
12624 tree_view->priv->scroll_to_column = column;
12625 tree_view->priv->scroll_to_use_align = use_align;
12626 tree_view->priv->scroll_to_row_align = row_align;
12627 tree_view->priv->scroll_to_col_align = col_align;
12628
12629 install_presize_handler (tree_view);
12630 }
12631 else
12632 {
12633 GdkRectangle cell_rect;
12634 GdkRectangle vis_rect;
12635 gint dest_x, dest_y;
12636
12637 gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect);
12638 gtk_tree_view_get_visible_rect (tree_view, &vis_rect);
12639
12640 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
12641
12642 dest_x = vis_rect.x;
12643 dest_y = vis_rect.y;
12644
12645 if (column)
12646 {
12647 if (use_align)
12648 {
12649 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
12650 }
12651 else
12652 {
12653 if (cell_rect.x < vis_rect.x)
12654 dest_x = cell_rect.x;
12655 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
12656 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
12657 }
12658 }
12659
12660 if (path)
12661 {
12662 if (use_align)
12663 {
12664 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
12665 dest_y = MAX (dest_y, 0);
12666 }
12667 else
12668 {
12669 if (cell_rect.y < vis_rect.y)
12670 dest_y = cell_rect.y;
12671 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
12672 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
12673 }
12674 }
12675
12676 gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y);
12677 }
12678 }
12679
12680 /**
12681 * gtk_tree_view_row_activated:
12682 * @tree_view: A #GtkTreeView
12683 * @path: The #GtkTreePath to be activated.
12684 * @column: The #GtkTreeViewColumn to be activated.
12685 *
12686 * Activates the cell determined by @path and @column.
12687 **/
12688 void
gtk_tree_view_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column)12689 gtk_tree_view_row_activated (GtkTreeView *tree_view,
12690 GtkTreePath *path,
12691 GtkTreeViewColumn *column)
12692 {
12693 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12694
12695 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
12696 }
12697
12698
12699 static void
gtk_tree_view_expand_all_emission_helper(GtkRBTree * tree,GtkRBNode * node,gpointer data)12700 gtk_tree_view_expand_all_emission_helper (GtkRBTree *tree,
12701 GtkRBNode *node,
12702 gpointer data)
12703 {
12704 GtkTreeView *tree_view = data;
12705
12706 if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT &&
12707 node->children)
12708 {
12709 GtkTreePath *path;
12710 GtkTreeIter iter;
12711
12712 path = _gtk_tree_path_new_from_rbtree (tree, node);
12713 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12714
12715 g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
12716
12717 gtk_tree_path_free (path);
12718 }
12719
12720 if (node->children)
12721 _gtk_rbtree_traverse (node->children,
12722 node->children->root,
12723 G_PRE_ORDER,
12724 gtk_tree_view_expand_all_emission_helper,
12725 tree_view);
12726 }
12727
12728 /**
12729 * gtk_tree_view_expand_all:
12730 * @tree_view: A #GtkTreeView.
12731 *
12732 * Recursively expands all nodes in the @tree_view.
12733 **/
12734 void
gtk_tree_view_expand_all(GtkTreeView * tree_view)12735 gtk_tree_view_expand_all (GtkTreeView *tree_view)
12736 {
12737 GtkTreePath *path;
12738 GtkRBTree *tree;
12739 GtkRBNode *node;
12740
12741 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12742
12743 if (tree_view->priv->tree == NULL)
12744 return;
12745
12746 path = gtk_tree_path_new_first ();
12747 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
12748
12749 while (node)
12750 {
12751 gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE, FALSE);
12752 node = _gtk_rbtree_next (tree, node);
12753 gtk_tree_path_next (path);
12754 }
12755
12756 gtk_tree_path_free (path);
12757 }
12758
12759 /**
12760 * gtk_tree_view_collapse_all:
12761 * @tree_view: A #GtkTreeView.
12762 *
12763 * Recursively collapses all visible, expanded nodes in @tree_view.
12764 **/
12765 void
gtk_tree_view_collapse_all(GtkTreeView * tree_view)12766 gtk_tree_view_collapse_all (GtkTreeView *tree_view)
12767 {
12768 GtkRBTree *tree;
12769 GtkRBNode *node;
12770 GtkTreePath *path;
12771 gint *indices;
12772
12773 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12774
12775 if (tree_view->priv->tree == NULL)
12776 return;
12777
12778 path = gtk_tree_path_new ();
12779 gtk_tree_path_down (path);
12780 indices = gtk_tree_path_get_indices (path);
12781
12782 tree = tree_view->priv->tree;
12783 node = _gtk_rbtree_first (tree);
12784
12785 while (node)
12786 {
12787 if (node->children)
12788 gtk_tree_view_real_collapse_row (tree_view, path, tree, node, FALSE);
12789 indices[0]++;
12790 node = _gtk_rbtree_next (tree, node);
12791 }
12792
12793 gtk_tree_path_free (path);
12794 }
12795
12796 /**
12797 * gtk_tree_view_expand_to_path:
12798 * @tree_view: A #GtkTreeView.
12799 * @path: path to a row.
12800 *
12801 * Expands the row at @path. This will also expand all parent rows of
12802 * @path as necessary.
12803 *
12804 * Since: 2.2
12805 **/
12806 void
gtk_tree_view_expand_to_path(GtkTreeView * tree_view,GtkTreePath * path)12807 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
12808 GtkTreePath *path)
12809 {
12810 gint i, depth;
12811 gint *indices;
12812 GtkTreePath *tmp;
12813
12814 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12815 g_return_if_fail (path != NULL);
12816
12817 depth = gtk_tree_path_get_depth (path);
12818 indices = gtk_tree_path_get_indices (path);
12819
12820 tmp = gtk_tree_path_new ();
12821 g_return_if_fail (tmp != NULL);
12822
12823 for (i = 0; i < depth; i++)
12824 {
12825 gtk_tree_path_append_index (tmp, indices[i]);
12826 gtk_tree_view_expand_row (tree_view, tmp, FALSE);
12827 }
12828
12829 gtk_tree_path_free (tmp);
12830 }
12831
12832 /* FIXME the bool return values for expand_row and collapse_row are
12833 * not analagous; they should be TRUE if the row had children and
12834 * was not already in the requested state.
12835 */
12836
12837
12838 static gboolean
gtk_tree_view_real_expand_row(GtkTreeView * tree_view,GtkTreePath * path,GtkRBTree * tree,GtkRBNode * node,gboolean open_all,gboolean animate)12839 gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
12840 GtkTreePath *path,
12841 GtkRBTree *tree,
12842 GtkRBNode *node,
12843 gboolean open_all,
12844 gboolean animate)
12845 {
12846 GtkTreeIter iter;
12847 GtkTreeIter temp;
12848 gboolean expand;
12849
12850 if (animate)
12851 animate = gtk_settings_get_enable_animations (gtk_widget_get_settings (GTK_WIDGET (tree_view)));
12852
12853 remove_auto_expand_timeout (tree_view);
12854
12855 if (node->children && !open_all)
12856 return FALSE;
12857
12858 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
12859 return FALSE;
12860
12861 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12862 if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
12863 return FALSE;
12864
12865
12866 if (node->children && open_all)
12867 {
12868 gboolean retval = FALSE;
12869 GtkTreePath *tmp_path = gtk_tree_path_copy (path);
12870
12871 gtk_tree_path_append_index (tmp_path, 0);
12872 tree = node->children;
12873 node = _gtk_rbtree_first (tree);
12874 /* try to expand the children */
12875 do
12876 {
12877 gboolean t;
12878 t = gtk_tree_view_real_expand_row (tree_view, tmp_path, tree, node,
12879 TRUE, animate);
12880 if (t)
12881 retval = TRUE;
12882
12883 gtk_tree_path_next (tmp_path);
12884 node = _gtk_rbtree_next (tree, node);
12885 }
12886 while (node != NULL);
12887
12888 gtk_tree_path_free (tmp_path);
12889
12890 return retval;
12891 }
12892
12893 g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand);
12894
12895 if (!gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
12896 return FALSE;
12897
12898 if (expand)
12899 return FALSE;
12900
12901 node->children = _gtk_rbtree_new ();
12902 node->children->parent_tree = tree;
12903 node->children->parent_node = node;
12904
12905 gtk_tree_model_iter_children (tree_view->priv->model, &temp, &iter);
12906
12907 gtk_tree_view_build_tree (tree_view,
12908 node->children,
12909 &temp,
12910 gtk_tree_path_get_depth (path) + 1,
12911 open_all);
12912
12913 _gtk_tree_view_accessible_add (tree_view, node->children, NULL);
12914 _gtk_tree_view_accessible_add_state (tree_view,
12915 tree, node,
12916 GTK_CELL_RENDERER_EXPANDED);
12917
12918 install_presize_handler (tree_view);
12919
12920 g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
12921 if (open_all && node->children)
12922 {
12923 _gtk_rbtree_traverse (node->children,
12924 node->children->root,
12925 G_PRE_ORDER,
12926 gtk_tree_view_expand_all_emission_helper,
12927 tree_view);
12928 }
12929 return TRUE;
12930 }
12931
12932
12933 /**
12934 * gtk_tree_view_expand_row:
12935 * @tree_view: a #GtkTreeView
12936 * @path: path to a row
12937 * @open_all: whether to recursively expand, or just expand immediate children
12938 *
12939 * Opens the row so its children are visible.
12940 *
12941 * Returns: %TRUE if the row existed and had children
12942 **/
12943 gboolean
gtk_tree_view_expand_row(GtkTreeView * tree_view,GtkTreePath * path,gboolean open_all)12944 gtk_tree_view_expand_row (GtkTreeView *tree_view,
12945 GtkTreePath *path,
12946 gboolean open_all)
12947 {
12948 GtkRBTree *tree;
12949 GtkRBNode *node;
12950
12951 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12952 g_return_val_if_fail (tree_view->priv->model != NULL, FALSE);
12953 g_return_val_if_fail (path != NULL, FALSE);
12954
12955 if (_gtk_tree_view_find_node (tree_view,
12956 path,
12957 &tree,
12958 &node))
12959 return FALSE;
12960
12961 if (tree != NULL)
12962 return gtk_tree_view_real_expand_row (tree_view, path, tree, node, open_all, FALSE);
12963 else
12964 return FALSE;
12965 }
12966
12967 static gboolean
gtk_tree_view_real_collapse_row(GtkTreeView * tree_view,GtkTreePath * path,GtkRBTree * tree,GtkRBNode * node,gboolean animate)12968 gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
12969 GtkTreePath *path,
12970 GtkRBTree *tree,
12971 GtkRBNode *node,
12972 gboolean animate)
12973 {
12974 GtkTreeIter iter;
12975 GtkTreeIter children;
12976 gboolean collapse;
12977 GList *list;
12978 gboolean selection_changed, cursor_changed;
12979
12980 if (animate)
12981 animate = gtk_settings_get_enable_animations (gtk_widget_get_settings (GTK_WIDGET (tree_view)));
12982
12983 remove_auto_expand_timeout (tree_view);
12984
12985 if (node->children == NULL)
12986 return FALSE;
12987 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12988
12989 g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse);
12990
12991 if (collapse)
12992 return FALSE;
12993
12994 /* if the prelighted node is a child of us, we want to unprelight it. We have
12995 * a chance to prelight the correct node below */
12996
12997 if (tree_view->priv->prelight_tree)
12998 {
12999 GtkRBTree *parent_tree;
13000 GtkRBNode *parent_node;
13001
13002 parent_tree = tree_view->priv->prelight_tree->parent_tree;
13003 parent_node = tree_view->priv->prelight_tree->parent_node;
13004 while (parent_tree)
13005 {
13006 if (parent_tree == tree && parent_node == node)
13007 {
13008 ensure_unprelighted (tree_view);
13009 break;
13010 }
13011 parent_node = parent_tree->parent_node;
13012 parent_tree = parent_tree->parent_tree;
13013 }
13014 }
13015
13016 TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_children (tree_view->priv->model, &children, &iter), FALSE);
13017
13018 for (list = tree_view->priv->columns; list; list = list->next)
13019 {
13020 GtkTreeViewColumn *column = list->data;
13021
13022 if (gtk_tree_view_column_get_visible (column) == FALSE)
13023 continue;
13024 if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
13025 _gtk_tree_view_column_cell_set_dirty (column, TRUE);
13026 }
13027
13028 if (tree_view->priv->destroy_count_func)
13029 {
13030 GtkTreePath *child_path;
13031 gint child_count = 0;
13032 child_path = gtk_tree_path_copy (path);
13033 gtk_tree_path_down (child_path);
13034 if (node->children)
13035 _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, &child_count);
13036 tree_view->priv->destroy_count_func (tree_view, child_path, child_count, tree_view->priv->destroy_count_data);
13037 gtk_tree_path_free (child_path);
13038 }
13039
13040 if (tree_view->priv->cursor_node)
13041 {
13042 cursor_changed = (node->children == tree_view->priv->cursor_tree)
13043 || _gtk_rbtree_contains (node->children, tree_view->priv->cursor_tree);
13044 }
13045 else
13046 cursor_changed = FALSE;
13047
13048 if (gtk_tree_row_reference_valid (tree_view->priv->anchor))
13049 {
13050 GtkTreePath *anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
13051 if (gtk_tree_path_is_ancestor (path, anchor_path))
13052 {
13053 gtk_tree_row_reference_free (tree_view->priv->anchor);
13054 tree_view->priv->anchor = NULL;
13055 }
13056 gtk_tree_path_free (anchor_path);
13057 }
13058
13059 selection_changed = gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children);
13060
13061 /* Stop a pending double click */
13062 gtk_event_controller_reset (GTK_EVENT_CONTROLLER (tree_view->priv->multipress_gesture));
13063
13064 _gtk_tree_view_accessible_remove (tree_view, node->children, NULL);
13065 _gtk_tree_view_accessible_remove_state (tree_view,
13066 tree, node,
13067 GTK_CELL_RENDERER_EXPANDED);
13068
13069 _gtk_rbtree_remove (node->children);
13070
13071 if (cursor_changed)
13072 gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CURSOR_INVALID);
13073 if (selection_changed)
13074 g_signal_emit_by_name (tree_view->priv->selection, "changed");
13075
13076 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
13077 {
13078 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
13079 }
13080
13081 g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path);
13082
13083 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
13084 update_prelight (tree_view,
13085 tree_view->priv->event_last_x,
13086 tree_view->priv->event_last_y);
13087
13088 return TRUE;
13089 }
13090
13091 /**
13092 * gtk_tree_view_collapse_row:
13093 * @tree_view: a #GtkTreeView
13094 * @path: path to a row in the @tree_view
13095 *
13096 * Collapses a row (hides its child rows, if they exist).
13097 *
13098 * Returns: %TRUE if the row was collapsed.
13099 **/
13100 gboolean
gtk_tree_view_collapse_row(GtkTreeView * tree_view,GtkTreePath * path)13101 gtk_tree_view_collapse_row (GtkTreeView *tree_view,
13102 GtkTreePath *path)
13103 {
13104 GtkRBTree *tree;
13105 GtkRBNode *node;
13106
13107 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13108 g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE);
13109 g_return_val_if_fail (path != NULL, FALSE);
13110
13111 if (_gtk_tree_view_find_node (tree_view,
13112 path,
13113 &tree,
13114 &node))
13115 return FALSE;
13116
13117 if (tree == NULL || node->children == NULL)
13118 return FALSE;
13119
13120 return gtk_tree_view_real_collapse_row (tree_view, path, tree, node, FALSE);
13121 }
13122
13123 static void
gtk_tree_view_map_expanded_rows_helper(GtkTreeView * tree_view,GtkRBTree * tree,GtkTreePath * path,GtkTreeViewMappingFunc func,gpointer user_data)13124 gtk_tree_view_map_expanded_rows_helper (GtkTreeView *tree_view,
13125 GtkRBTree *tree,
13126 GtkTreePath *path,
13127 GtkTreeViewMappingFunc func,
13128 gpointer user_data)
13129 {
13130 GtkRBNode *node;
13131
13132 if (tree == NULL || tree->root == NULL)
13133 return;
13134
13135 node = _gtk_rbtree_first (tree);
13136
13137 while (node)
13138 {
13139 if (node->children)
13140 {
13141 (* func) (tree_view, path, user_data);
13142 gtk_tree_path_down (path);
13143 gtk_tree_view_map_expanded_rows_helper (tree_view, node->children, path, func, user_data);
13144 gtk_tree_path_up (path);
13145 }
13146 gtk_tree_path_next (path);
13147 node = _gtk_rbtree_next (tree, node);
13148 }
13149 }
13150
13151 /**
13152 * gtk_tree_view_map_expanded_rows:
13153 * @tree_view: A #GtkTreeView
13154 * @func: (scope call): A function to be called
13155 * @data: User data to be passed to the function.
13156 *
13157 * Calls @func on all expanded rows.
13158 **/
13159 void
gtk_tree_view_map_expanded_rows(GtkTreeView * tree_view,GtkTreeViewMappingFunc func,gpointer user_data)13160 gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view,
13161 GtkTreeViewMappingFunc func,
13162 gpointer user_data)
13163 {
13164 GtkTreePath *path;
13165
13166 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13167 g_return_if_fail (func != NULL);
13168
13169 path = gtk_tree_path_new_first ();
13170
13171 gtk_tree_view_map_expanded_rows_helper (tree_view,
13172 tree_view->priv->tree,
13173 path, func, user_data);
13174
13175 gtk_tree_path_free (path);
13176 }
13177
13178 /**
13179 * gtk_tree_view_row_expanded:
13180 * @tree_view: A #GtkTreeView.
13181 * @path: A #GtkTreePath to test expansion state.
13182 *
13183 * Returns %TRUE if the node pointed to by @path is expanded in @tree_view.
13184 *
13185 * Returns: %TRUE if #path is expanded.
13186 **/
13187 gboolean
gtk_tree_view_row_expanded(GtkTreeView * tree_view,GtkTreePath * path)13188 gtk_tree_view_row_expanded (GtkTreeView *tree_view,
13189 GtkTreePath *path)
13190 {
13191 GtkRBTree *tree;
13192 GtkRBNode *node;
13193
13194 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13195 g_return_val_if_fail (path != NULL, FALSE);
13196
13197 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
13198
13199 if (node == NULL)
13200 return FALSE;
13201
13202 return (node->children != NULL);
13203 }
13204
13205 /**
13206 * gtk_tree_view_get_reorderable:
13207 * @tree_view: a #GtkTreeView
13208 *
13209 * Retrieves whether the user can reorder the tree via drag-and-drop. See
13210 * gtk_tree_view_set_reorderable().
13211 *
13212 * Returns: %TRUE if the tree can be reordered.
13213 **/
13214 gboolean
gtk_tree_view_get_reorderable(GtkTreeView * tree_view)13215 gtk_tree_view_get_reorderable (GtkTreeView *tree_view)
13216 {
13217 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13218
13219 return tree_view->priv->reorderable;
13220 }
13221
13222 /**
13223 * gtk_tree_view_set_reorderable:
13224 * @tree_view: A #GtkTreeView.
13225 * @reorderable: %TRUE, if the tree can be reordered.
13226 *
13227 * This function is a convenience function to allow you to reorder
13228 * models that support the #GtkTreeDragSourceIface and the
13229 * #GtkTreeDragDestIface. Both #GtkTreeStore and #GtkListStore support
13230 * these. If @reorderable is %TRUE, then the user can reorder the
13231 * model by dragging and dropping rows. The developer can listen to
13232 * these changes by connecting to the model’s #GtkTreeModel::row-inserted
13233 * and #GtkTreeModel::row-deleted signals. The reordering is implemented
13234 * by setting up the tree view as a drag source and destination.
13235 * Therefore, drag and drop can not be used in a reorderable view for any
13236 * other purpose.
13237 *
13238 * This function does not give you any degree of control over the order -- any
13239 * reordering is allowed. If more control is needed, you should probably
13240 * handle drag and drop manually.
13241 **/
13242 void
gtk_tree_view_set_reorderable(GtkTreeView * tree_view,gboolean reorderable)13243 gtk_tree_view_set_reorderable (GtkTreeView *tree_view,
13244 gboolean reorderable)
13245 {
13246 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13247
13248 reorderable = reorderable != FALSE;
13249
13250 if (tree_view->priv->reorderable == reorderable)
13251 return;
13252
13253 if (reorderable)
13254 {
13255 const GtkTargetEntry row_targets[] = {
13256 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
13257 };
13258
13259 gtk_tree_view_enable_model_drag_source (tree_view,
13260 GDK_BUTTON1_MASK,
13261 row_targets,
13262 G_N_ELEMENTS (row_targets),
13263 GDK_ACTION_MOVE);
13264 gtk_tree_view_enable_model_drag_dest (tree_view,
13265 row_targets,
13266 G_N_ELEMENTS (row_targets),
13267 GDK_ACTION_MOVE);
13268 }
13269 else
13270 {
13271 gtk_tree_view_unset_rows_drag_source (tree_view);
13272 gtk_tree_view_unset_rows_drag_dest (tree_view);
13273 }
13274
13275 tree_view->priv->reorderable = reorderable;
13276
13277 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]);
13278 }
13279
13280 static void
gtk_tree_view_real_set_cursor(GtkTreeView * tree_view,GtkTreePath * path,SetCursorFlags flags)13281 gtk_tree_view_real_set_cursor (GtkTreeView *tree_view,
13282 GtkTreePath *path,
13283 SetCursorFlags flags)
13284 {
13285 if (!(flags & CURSOR_INVALID) && tree_view->priv->cursor_node)
13286 {
13287 _gtk_tree_view_accessible_remove_state (tree_view,
13288 tree_view->priv->cursor_tree,
13289 tree_view->priv->cursor_node,
13290 GTK_CELL_RENDERER_FOCUSED);
13291 _gtk_tree_view_queue_draw_node (tree_view,
13292 tree_view->priv->cursor_tree,
13293 tree_view->priv->cursor_node,
13294 NULL);
13295 }
13296
13297 /* One cannot set the cursor on a separator. Also, if
13298 * _gtk_tree_view_find_node returns TRUE, it ran out of tree
13299 * before finding the tree and node belonging to path. The
13300 * path maps to a non-existing path and we will silently bail out.
13301 * We unset tree and node to avoid further processing.
13302 */
13303 if (path == NULL ||
13304 row_is_separator (tree_view, NULL, path)
13305 || _gtk_tree_view_find_node (tree_view,
13306 path,
13307 &tree_view->priv->cursor_tree,
13308 &tree_view->priv->cursor_node))
13309 {
13310 tree_view->priv->cursor_tree = NULL;
13311 tree_view->priv->cursor_node = NULL;
13312 }
13313
13314 if (tree_view->priv->cursor_node != NULL)
13315 {
13316 GtkRBTree *new_tree = NULL;
13317 GtkRBNode *new_node = NULL;
13318
13319 if ((flags & CLEAR_AND_SELECT) && !tree_view->priv->modify_selection_pressed)
13320 {
13321 GtkTreeSelectMode mode = 0;
13322
13323 if (tree_view->priv->extend_selection_pressed)
13324 mode |= GTK_TREE_SELECT_MODE_EXTEND;
13325
13326 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
13327 tree_view->priv->cursor_node,
13328 tree_view->priv->cursor_tree,
13329 path,
13330 mode,
13331 FALSE);
13332 }
13333
13334 /* We have to re-find tree and node here again, somebody might have
13335 * cleared the node or the whole tree in the GtkTreeSelection::changed
13336 * callback. If the nodes differ we bail out here.
13337 */
13338 _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node);
13339
13340 if (tree_view->priv->cursor_node == NULL ||
13341 tree_view->priv->cursor_node != new_node)
13342 return;
13343
13344 if (flags & CLAMP_NODE)
13345 {
13346 gtk_tree_view_clamp_node_visible (tree_view,
13347 tree_view->priv->cursor_tree,
13348 tree_view->priv->cursor_node);
13349 _gtk_tree_view_queue_draw_node (tree_view,
13350 tree_view->priv->cursor_tree,
13351 tree_view->priv->cursor_node,
13352 NULL);
13353 }
13354
13355 _gtk_tree_view_accessible_add_state (tree_view,
13356 tree_view->priv->cursor_tree,
13357 tree_view->priv->cursor_node,
13358 GTK_CELL_RENDERER_FOCUSED);
13359 }
13360
13361 if (!gtk_widget_in_destruction (GTK_WIDGET (tree_view)))
13362 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
13363 }
13364
13365 /**
13366 * gtk_tree_view_get_cursor:
13367 * @tree_view: A #GtkTreeView
13368 * @path: (out) (transfer full) (optional) (nullable): A pointer to be
13369 * filled with the current cursor path, or %NULL
13370 * @focus_column: (out) (transfer none) (optional) (nullable): A
13371 * pointer to be filled with the current focus column, or %NULL
13372 *
13373 * Fills in @path and @focus_column with the current path and focus column. If
13374 * the cursor isn’t currently set, then *@path will be %NULL. If no column
13375 * currently has focus, then *@focus_column will be %NULL.
13376 *
13377 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
13378 * you are done with it.
13379 **/
13380 void
gtk_tree_view_get_cursor(GtkTreeView * tree_view,GtkTreePath ** path,GtkTreeViewColumn ** focus_column)13381 gtk_tree_view_get_cursor (GtkTreeView *tree_view,
13382 GtkTreePath **path,
13383 GtkTreeViewColumn **focus_column)
13384 {
13385 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13386
13387 if (path)
13388 {
13389 if (tree_view->priv->cursor_node)
13390 *path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
13391 tree_view->priv->cursor_node);
13392 else
13393 *path = NULL;
13394 }
13395
13396 if (focus_column)
13397 {
13398 *focus_column = tree_view->priv->focus_column;
13399 }
13400 }
13401
13402 /**
13403 * gtk_tree_view_set_cursor:
13404 * @tree_view: A #GtkTreeView
13405 * @path: A #GtkTreePath
13406 * @focus_column: (allow-none): A #GtkTreeViewColumn, or %NULL
13407 * @start_editing: %TRUE if the specified cell should start being edited.
13408 *
13409 * Sets the current keyboard focus to be at @path, and selects it. This is
13410 * useful when you want to focus the user’s attention on a particular row. If
13411 * @focus_column is not %NULL, then focus is given to the column specified by
13412 * it. Additionally, if @focus_column is specified, and @start_editing is
13413 * %TRUE, then editing should be started in the specified cell.
13414 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
13415 * in order to give keyboard focus to the widget. Please note that editing
13416 * can only happen when the widget is realized.
13417 *
13418 * If @path is invalid for @model, the current cursor (if any) will be unset
13419 * and the function will return without failing.
13420 **/
13421 void
gtk_tree_view_set_cursor(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * focus_column,gboolean start_editing)13422 gtk_tree_view_set_cursor (GtkTreeView *tree_view,
13423 GtkTreePath *path,
13424 GtkTreeViewColumn *focus_column,
13425 gboolean start_editing)
13426 {
13427 gtk_tree_view_set_cursor_on_cell (tree_view, path, focus_column,
13428 NULL, start_editing);
13429 }
13430
13431 /**
13432 * gtk_tree_view_set_cursor_on_cell:
13433 * @tree_view: A #GtkTreeView
13434 * @path: A #GtkTreePath
13435 * @focus_column: (allow-none): A #GtkTreeViewColumn, or %NULL
13436 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
13437 * @start_editing: %TRUE if the specified cell should start being edited.
13438 *
13439 * Sets the current keyboard focus to be at @path, and selects it. This is
13440 * useful when you want to focus the user’s attention on a particular row. If
13441 * @focus_column is not %NULL, then focus is given to the column specified by
13442 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
13443 * contains 2 or more editable or activatable cells, then focus is given to
13444 * the cell specified by @focus_cell. Additionally, if @focus_column is
13445 * specified, and @start_editing is %TRUE, then editing should be started in
13446 * the specified cell. This function is often followed by
13447 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
13448 * widget. Please note that editing can only happen when the widget is
13449 * realized.
13450 *
13451 * If @path is invalid for @model, the current cursor (if any) will be unset
13452 * and the function will return without failing.
13453 *
13454 * Since: 2.2
13455 **/
13456 void
gtk_tree_view_set_cursor_on_cell(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * focus_column,GtkCellRenderer * focus_cell,gboolean start_editing)13457 gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view,
13458 GtkTreePath *path,
13459 GtkTreeViewColumn *focus_column,
13460 GtkCellRenderer *focus_cell,
13461 gboolean start_editing)
13462 {
13463 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13464 g_return_if_fail (path != NULL);
13465 g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column));
13466
13467 if (!tree_view->priv->model)
13468 return;
13469
13470 if (focus_cell)
13471 {
13472 g_return_if_fail (focus_column);
13473 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
13474 }
13475
13476 /* cancel the current editing, if it exists */
13477 if (tree_view->priv->edited_column &&
13478 gtk_cell_area_get_edit_widget
13479 (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (tree_view->priv->edited_column))))
13480 gtk_tree_view_stop_editing (tree_view, TRUE);
13481
13482 gtk_tree_view_real_set_cursor (tree_view, path, CLEAR_AND_SELECT | CLAMP_NODE);
13483
13484 if (focus_column &&
13485 gtk_tree_view_column_get_visible (focus_column))
13486 {
13487 GList *list;
13488 gboolean column_in_tree = FALSE;
13489
13490 for (list = tree_view->priv->columns; list; list = list->next)
13491 if (list->data == focus_column)
13492 {
13493 column_in_tree = TRUE;
13494 break;
13495 }
13496 g_return_if_fail (column_in_tree);
13497 _gtk_tree_view_set_focus_column (tree_view, focus_column);
13498 if (focus_cell)
13499 gtk_tree_view_column_focus_cell (focus_column, focus_cell);
13500 if (start_editing)
13501 gtk_tree_view_start_editing (tree_view, path, TRUE);
13502 }
13503 }
13504
13505 /**
13506 * gtk_tree_view_get_bin_window:
13507 * @tree_view: A #GtkTreeView
13508 *
13509 * Returns the window that @tree_view renders to.
13510 * This is used primarily to compare to `event->window`
13511 * to confirm that the event on @tree_view is on the right window.
13512 *
13513 * Returns: (nullable) (transfer none): A #GdkWindow, or %NULL when @tree_view
13514 * hasn’t been realized yet.
13515 **/
13516 GdkWindow *
gtk_tree_view_get_bin_window(GtkTreeView * tree_view)13517 gtk_tree_view_get_bin_window (GtkTreeView *tree_view)
13518 {
13519 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
13520
13521 return tree_view->priv->bin_window;
13522 }
13523
13524 /**
13525 * gtk_tree_view_get_path_at_pos:
13526 * @tree_view: A #GtkTreeView.
13527 * @x: The x position to be identified (relative to bin_window).
13528 * @y: The y position to be identified (relative to bin_window).
13529 * @path: (out) (optional) (nullable): A pointer to a #GtkTreePath
13530 * pointer to be filled in, or %NULL
13531 * @column: (out) (transfer none) (optional) (nullable): A pointer to
13532 * a #GtkTreeViewColumn pointer to be filled in, or %NULL
13533 * @cell_x: (out) (optional): A pointer where the X coordinate
13534 * relative to the cell can be placed, or %NULL
13535 * @cell_y: (out) (optional): A pointer where the Y coordinate
13536 * relative to the cell can be placed, or %NULL
13537 *
13538 * Finds the path at the point (@x, @y), relative to bin_window coordinates
13539 * (please see gtk_tree_view_get_bin_window()).
13540 * That is, @x and @y are relative to an events coordinates. @x and @y must
13541 * come from an event on the @tree_view only where `event->window ==
13542 * gtk_tree_view_get_bin_window ()`. It is primarily for
13543 * things like popup menus. If @path is non-%NULL, then it will be filled
13544 * with the #GtkTreePath at that point. This path should be freed with
13545 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
13546 * with the column at that point. @cell_x and @cell_y return the coordinates
13547 * relative to the cell background (i.e. the @background_area passed to
13548 * gtk_cell_renderer_render()). This function is only meaningful if
13549 * @tree_view is realized. Therefore this function will always return %FALSE
13550 * if @tree_view is not realized or does not have a model.
13551 *
13552 * For converting widget coordinates (eg. the ones you get from
13553 * GtkWidget::query-tooltip), please see
13554 * gtk_tree_view_convert_widget_to_bin_window_coords().
13555 *
13556 * Returns: %TRUE if a row exists at that coordinate.
13557 **/
13558 gboolean
gtk_tree_view_get_path_at_pos(GtkTreeView * tree_view,gint x,gint y,GtkTreePath ** path,GtkTreeViewColumn ** column,gint * cell_x,gint * cell_y)13559 gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view,
13560 gint x,
13561 gint y,
13562 GtkTreePath **path,
13563 GtkTreeViewColumn **column,
13564 gint *cell_x,
13565 gint *cell_y)
13566 {
13567 GtkRBTree *tree;
13568 GtkRBNode *node;
13569 gint y_offset;
13570
13571 g_return_val_if_fail (tree_view != NULL, FALSE);
13572
13573 if (path)
13574 *path = NULL;
13575 if (column)
13576 *column = NULL;
13577
13578 if (tree_view->priv->bin_window == NULL)
13579 return FALSE;
13580
13581 if (tree_view->priv->tree == NULL)
13582 return FALSE;
13583
13584 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
13585 return FALSE;
13586
13587 if (x < 0 || y < 0)
13588 return FALSE;
13589
13590 if (column || cell_x)
13591 {
13592 GtkTreeViewColumn *tmp_column;
13593 GtkTreeViewColumn *last_column = NULL;
13594 GList *list;
13595 gint remaining_x = x;
13596 gboolean found = FALSE;
13597 gboolean rtl;
13598 gint width;
13599
13600 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
13601 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
13602 list;
13603 list = (rtl ? list->prev : list->next))
13604 {
13605 tmp_column = list->data;
13606
13607 if (gtk_tree_view_column_get_visible (tmp_column) == FALSE)
13608 continue;
13609
13610 last_column = tmp_column;
13611 width = gtk_tree_view_column_get_width (tmp_column);
13612 if (remaining_x < width)
13613 {
13614 found = TRUE;
13615
13616 if (column)
13617 *column = tmp_column;
13618
13619 if (cell_x)
13620 *cell_x = remaining_x;
13621
13622 break;
13623 }
13624 remaining_x -= width;
13625 }
13626
13627 /* If found is FALSE and there is a last_column, then it the remainder
13628 * space is in that area
13629 */
13630 if (!found)
13631 {
13632 if (last_column)
13633 {
13634 if (column)
13635 *column = last_column;
13636
13637 if (cell_x)
13638 *cell_x = gtk_tree_view_column_get_width (last_column) + remaining_x;
13639 }
13640 else
13641 {
13642 return FALSE;
13643 }
13644 }
13645 }
13646
13647 y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
13648 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
13649 &tree, &node);
13650
13651 if (tree == NULL)
13652 return FALSE;
13653
13654 if (cell_y)
13655 *cell_y = y_offset;
13656
13657 if (path)
13658 *path = _gtk_tree_path_new_from_rbtree (tree, node);
13659
13660 return TRUE;
13661 }
13662
13663
13664 static inline gint
gtk_tree_view_get_cell_area_height(GtkTreeView * tree_view,GtkRBNode * node,gint vertical_separator)13665 gtk_tree_view_get_cell_area_height (GtkTreeView *tree_view,
13666 GtkRBNode *node,
13667 gint vertical_separator)
13668 {
13669 int expander_size = gtk_tree_view_get_expander_size (tree_view);
13670 int height;
13671
13672 /* The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
13673 * i.e. just the cells, no spacing.
13674 *
13675 * The cell area height is at least expander_size - vertical_separator.
13676 * For regular nodes, the height is then at least expander_size. We should
13677 * be able to enforce the expander_size minimum here, because this
13678 * function will not be called for irregular (e.g. separator) rows.
13679 */
13680 height = gtk_tree_view_get_row_height (tree_view, node);
13681 if (height < expander_size)
13682 height = expander_size;
13683
13684 return height - vertical_separator;
13685 }
13686
13687 static inline gint
gtk_tree_view_get_cell_area_y_offset(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint vertical_separator)13688 gtk_tree_view_get_cell_area_y_offset (GtkTreeView *tree_view,
13689 GtkRBTree *tree,
13690 GtkRBNode *node,
13691 gint vertical_separator)
13692 {
13693 int offset;
13694
13695 offset = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
13696 offset += vertical_separator / 2;
13697
13698 return offset;
13699 }
13700
13701 /**
13702 * gtk_tree_view_get_cell_area:
13703 * @tree_view: a #GtkTreeView
13704 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
13705 * @column: (allow-none): a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordinates
13706 * @rect: (out): rectangle to fill with cell rect
13707 *
13708 * Fills the bounding rectangle in bin_window coordinates for the cell at the
13709 * row specified by @path and the column specified by @column. If @path is
13710 * %NULL, or points to a path not currently displayed, the @y and @height fields
13711 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
13712 * fields will be filled with 0. The sum of all cell rects does not cover the
13713 * entire tree; there are extra pixels in between rows, for example. The
13714 * returned rectangle is equivalent to the @cell_area passed to
13715 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
13716 * realized.
13717 **/
13718 void
gtk_tree_view_get_cell_area(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GdkRectangle * rect)13719 gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
13720 GtkTreePath *path,
13721 GtkTreeViewColumn *column,
13722 GdkRectangle *rect)
13723 {
13724 GtkRBTree *tree = NULL;
13725 GtkRBNode *node = NULL;
13726 gint vertical_separator;
13727 gint horizontal_separator;
13728
13729 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13730 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
13731 g_return_if_fail (rect != NULL);
13732 g_return_if_fail (!column || gtk_tree_view_column_get_tree_view (column) == (GtkWidget *) tree_view);
13733 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
13734
13735 gtk_widget_style_get (GTK_WIDGET (tree_view),
13736 "vertical-separator", &vertical_separator,
13737 "horizontal-separator", &horizontal_separator,
13738 NULL);
13739
13740 rect->x = 0;
13741 rect->y = 0;
13742 rect->width = 0;
13743 rect->height = 0;
13744
13745 if (column)
13746 {
13747 rect->x = gtk_tree_view_column_get_x_offset (column) + horizontal_separator/2;
13748 rect->width = gtk_tree_view_column_get_width (column) - horizontal_separator;
13749 }
13750
13751 if (path)
13752 {
13753 gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node);
13754
13755 /* Get vertical coords */
13756 if ((!ret && tree == NULL) || ret)
13757 return;
13758
13759 if (row_is_separator (tree_view, NULL, path))
13760 {
13761 /* There isn't really a "cell area" for separator, so we
13762 * return the y, height values for background area instead.
13763 */
13764 rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
13765 rect->height = gtk_tree_view_get_row_height (tree_view, node);
13766 }
13767 else
13768 {
13769 rect->y = gtk_tree_view_get_cell_area_y_offset (tree_view, tree, node,
13770 vertical_separator);
13771 rect->height = gtk_tree_view_get_cell_area_height (tree_view, node,
13772 vertical_separator);
13773 }
13774
13775 if (column &&
13776 gtk_tree_view_is_expander_column (tree_view, column))
13777 {
13778 gint depth = gtk_tree_path_get_depth (path);
13779 gboolean rtl;
13780
13781 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
13782
13783 if (!rtl)
13784 rect->x += (depth - 1) * tree_view->priv->level_indentation;
13785 rect->width -= (depth - 1) * tree_view->priv->level_indentation;
13786
13787 if (gtk_tree_view_draw_expanders (tree_view))
13788 {
13789 int expander_size = gtk_tree_view_get_expander_size (tree_view);
13790 if (!rtl)
13791 rect->x += depth * expander_size;
13792 rect->width -= depth * expander_size;
13793 }
13794
13795 rect->width = MAX (rect->width, 0);
13796 }
13797 }
13798 }
13799
13800 static inline gint
gtk_tree_view_get_row_height(GtkTreeView * tree_view,GtkRBNode * node)13801 gtk_tree_view_get_row_height (GtkTreeView *tree_view,
13802 GtkRBNode *node)
13803 {
13804 int expander_size = gtk_tree_view_get_expander_size (tree_view);
13805 int height;
13806
13807 /* The "background" areas of all rows/cells add up to cover the entire tree.
13808 * The background includes all inter-row and inter-cell spacing.
13809 *
13810 * If the row pointed at by node does not have a height set, we default
13811 * to expander_size, which is the minimum height for regular nodes.
13812 * Non-regular nodes (e.g. separators) can have a height set smaller
13813 * than expander_size and should not be overruled here.
13814 */
13815 height = GTK_RBNODE_GET_HEIGHT (node);
13816 if (height <= 0)
13817 height = expander_size;
13818
13819 return height;
13820 }
13821
13822 static inline gint
gtk_tree_view_get_row_y_offset(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)13823 gtk_tree_view_get_row_y_offset (GtkTreeView *tree_view,
13824 GtkRBTree *tree,
13825 GtkRBNode *node)
13826 {
13827 int offset;
13828
13829 offset = _gtk_rbtree_node_find_offset (tree, node);
13830
13831 return RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, offset);
13832 }
13833
13834 /**
13835 * gtk_tree_view_get_background_area:
13836 * @tree_view: a #GtkTreeView
13837 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
13838 * @column: (allow-none): a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes
13839 * @rect: (out): rectangle to fill with cell background rect
13840 *
13841 * Fills the bounding rectangle in bin_window coordinates for the cell at the
13842 * row specified by @path and the column specified by @column. If @path is
13843 * %NULL, or points to a node not found in the tree, the @y and @height fields of
13844 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
13845 * fields will be filled with 0. The returned rectangle is equivalent to the
13846 * @background_area passed to gtk_cell_renderer_render(). These background
13847 * areas tile to cover the entire bin window. Contrast with the @cell_area,
13848 * returned by gtk_tree_view_get_cell_area(), which returns only the cell
13849 * itself, excluding surrounding borders and the tree expander area.
13850 *
13851 **/
13852 void
gtk_tree_view_get_background_area(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GdkRectangle * rect)13853 gtk_tree_view_get_background_area (GtkTreeView *tree_view,
13854 GtkTreePath *path,
13855 GtkTreeViewColumn *column,
13856 GdkRectangle *rect)
13857 {
13858 GtkRBTree *tree = NULL;
13859 GtkRBNode *node = NULL;
13860
13861 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13862 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
13863 g_return_if_fail (rect != NULL);
13864
13865 rect->x = 0;
13866 rect->y = 0;
13867 rect->width = 0;
13868 rect->height = 0;
13869
13870 if (path)
13871 {
13872 /* Get vertical coords */
13873
13874 if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) &&
13875 tree == NULL)
13876 return;
13877
13878 rect->y = gtk_tree_view_get_row_y_offset (tree_view, tree, node);
13879 rect->height = gtk_tree_view_get_row_height (tree_view, node);
13880 }
13881
13882 if (column)
13883 {
13884 gint x2 = 0;
13885
13886 gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2);
13887 rect->width = x2 - rect->x;
13888 }
13889 }
13890
13891 /**
13892 * gtk_tree_view_get_visible_rect:
13893 * @tree_view: a #GtkTreeView
13894 * @visible_rect: (out): rectangle to fill
13895 *
13896 * Fills @visible_rect with the currently-visible region of the
13897 * buffer, in tree coordinates. Convert to bin_window coordinates with
13898 * gtk_tree_view_convert_tree_to_bin_window_coords().
13899 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
13900 * scrollable area of the tree.
13901 **/
13902 void
gtk_tree_view_get_visible_rect(GtkTreeView * tree_view,GdkRectangle * visible_rect)13903 gtk_tree_view_get_visible_rect (GtkTreeView *tree_view,
13904 GdkRectangle *visible_rect)
13905 {
13906 GtkAllocation allocation;
13907 GtkWidget *widget;
13908
13909 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13910
13911 widget = GTK_WIDGET (tree_view);
13912
13913 if (visible_rect)
13914 {
13915 gtk_widget_get_allocation (widget, &allocation);
13916 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
13917 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
13918 visible_rect->width = allocation.width;
13919 visible_rect->height = allocation.height - gtk_tree_view_get_effective_header_height (tree_view);
13920 }
13921 }
13922
13923 /**
13924 * gtk_tree_view_convert_widget_to_tree_coords:
13925 * @tree_view: a #GtkTreeView
13926 * @wx: X coordinate relative to the widget
13927 * @wy: Y coordinate relative to the widget
13928 * @tx: (out): return location for tree X coordinate
13929 * @ty: (out): return location for tree Y coordinate
13930 *
13931 * Converts widget coordinates to coordinates for the
13932 * tree (the full scrollable area of the tree).
13933 *
13934 * Since: 2.12
13935 **/
13936 void
gtk_tree_view_convert_widget_to_tree_coords(GtkTreeView * tree_view,gint wx,gint wy,gint * tx,gint * ty)13937 gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view,
13938 gint wx,
13939 gint wy,
13940 gint *tx,
13941 gint *ty)
13942 {
13943 gint x, y;
13944
13945 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13946
13947 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
13948 wx, wy,
13949 &x, &y);
13950 gtk_tree_view_convert_bin_window_to_tree_coords (tree_view,
13951 x, y,
13952 tx, ty);
13953 }
13954
13955 /**
13956 * gtk_tree_view_convert_tree_to_widget_coords:
13957 * @tree_view: a #GtkTreeView
13958 * @tx: X coordinate relative to the tree
13959 * @ty: Y coordinate relative to the tree
13960 * @wx: (out): return location for widget X coordinate
13961 * @wy: (out): return location for widget Y coordinate
13962 *
13963 * Converts tree coordinates (coordinates in full scrollable area of the tree)
13964 * to widget coordinates.
13965 *
13966 * Since: 2.12
13967 **/
13968 void
gtk_tree_view_convert_tree_to_widget_coords(GtkTreeView * tree_view,gint tx,gint ty,gint * wx,gint * wy)13969 gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view,
13970 gint tx,
13971 gint ty,
13972 gint *wx,
13973 gint *wy)
13974 {
13975 gint x, y;
13976
13977 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13978
13979 gtk_tree_view_convert_tree_to_bin_window_coords (tree_view,
13980 tx, ty,
13981 &x, &y);
13982 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
13983 x, y,
13984 wx, wy);
13985 }
13986
13987 /**
13988 * gtk_tree_view_convert_widget_to_bin_window_coords:
13989 * @tree_view: a #GtkTreeView
13990 * @wx: X coordinate relative to the widget
13991 * @wy: Y coordinate relative to the widget
13992 * @bx: (out): return location for bin_window X coordinate
13993 * @by: (out): return location for bin_window Y coordinate
13994 *
13995 * Converts widget coordinates to coordinates for the bin_window
13996 * (see gtk_tree_view_get_bin_window()).
13997 *
13998 * Since: 2.12
13999 **/
14000 void
gtk_tree_view_convert_widget_to_bin_window_coords(GtkTreeView * tree_view,gint wx,gint wy,gint * bx,gint * by)14001 gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view,
14002 gint wx,
14003 gint wy,
14004 gint *bx,
14005 gint *by)
14006 {
14007 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14008
14009 if (bx)
14010 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
14011 if (by)
14012 *by = wy - gtk_tree_view_get_effective_header_height (tree_view);
14013 }
14014
14015 /**
14016 * gtk_tree_view_convert_bin_window_to_widget_coords:
14017 * @tree_view: a #GtkTreeView
14018 * @bx: bin_window X coordinate
14019 * @by: bin_window Y coordinate
14020 * @wx: (out): return location for widget X coordinate
14021 * @wy: (out): return location for widget Y coordinate
14022 *
14023 * Converts bin_window coordinates (see gtk_tree_view_get_bin_window())
14024 * to widget relative coordinates.
14025 *
14026 * Since: 2.12
14027 **/
14028 void
gtk_tree_view_convert_bin_window_to_widget_coords(GtkTreeView * tree_view,gint bx,gint by,gint * wx,gint * wy)14029 gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view,
14030 gint bx,
14031 gint by,
14032 gint *wx,
14033 gint *wy)
14034 {
14035 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14036
14037 if (wx)
14038 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
14039 if (wy)
14040 *wy = by + gtk_tree_view_get_effective_header_height (tree_view);
14041 }
14042
14043 /**
14044 * gtk_tree_view_convert_tree_to_bin_window_coords:
14045 * @tree_view: a #GtkTreeView
14046 * @tx: tree X coordinate
14047 * @ty: tree Y coordinate
14048 * @bx: (out): return location for X coordinate relative to bin_window
14049 * @by: (out): return location for Y coordinate relative to bin_window
14050 *
14051 * Converts tree coordinates (coordinates in full scrollable area of the tree)
14052 * to bin_window coordinates.
14053 *
14054 * Since: 2.12
14055 **/
14056 void
gtk_tree_view_convert_tree_to_bin_window_coords(GtkTreeView * tree_view,gint tx,gint ty,gint * bx,gint * by)14057 gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view,
14058 gint tx,
14059 gint ty,
14060 gint *bx,
14061 gint *by)
14062 {
14063 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14064
14065 if (bx)
14066 *bx = tx;
14067 if (by)
14068 *by = ty - tree_view->priv->dy;
14069 }
14070
14071 /**
14072 * gtk_tree_view_convert_bin_window_to_tree_coords:
14073 * @tree_view: a #GtkTreeView
14074 * @bx: X coordinate relative to bin_window
14075 * @by: Y coordinate relative to bin_window
14076 * @tx: (out): return location for tree X coordinate
14077 * @ty: (out): return location for tree Y coordinate
14078 *
14079 * Converts bin_window coordinates to coordinates for the
14080 * tree (the full scrollable area of the tree).
14081 *
14082 * Since: 2.12
14083 **/
14084 void
gtk_tree_view_convert_bin_window_to_tree_coords(GtkTreeView * tree_view,gint bx,gint by,gint * tx,gint * ty)14085 gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view,
14086 gint bx,
14087 gint by,
14088 gint *tx,
14089 gint *ty)
14090 {
14091 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14092
14093 if (tx)
14094 *tx = bx;
14095 if (ty)
14096 *ty = by + tree_view->priv->dy;
14097 }
14098
14099
14100
14101 /**
14102 * gtk_tree_view_get_visible_range:
14103 * @tree_view: A #GtkTreeView
14104 * @start_path: (out) (allow-none): Return location for start of region,
14105 * or %NULL.
14106 * @end_path: (out) (allow-none): Return location for end of region, or %NULL.
14107 *
14108 * Sets @start_path and @end_path to be the first and last visible path.
14109 * Note that there may be invisible paths in between.
14110 *
14111 * The paths should be freed with gtk_tree_path_free() after use.
14112 *
14113 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
14114 *
14115 * Since: 2.8
14116 **/
14117 gboolean
gtk_tree_view_get_visible_range(GtkTreeView * tree_view,GtkTreePath ** start_path,GtkTreePath ** end_path)14118 gtk_tree_view_get_visible_range (GtkTreeView *tree_view,
14119 GtkTreePath **start_path,
14120 GtkTreePath **end_path)
14121 {
14122 GtkRBTree *tree;
14123 GtkRBNode *node;
14124 gboolean retval;
14125
14126 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14127
14128 if (!tree_view->priv->tree)
14129 return FALSE;
14130
14131 retval = TRUE;
14132
14133 if (start_path)
14134 {
14135 _gtk_rbtree_find_offset (tree_view->priv->tree,
14136 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
14137 &tree, &node);
14138 if (node)
14139 *start_path = _gtk_tree_path_new_from_rbtree (tree, node);
14140 else
14141 retval = FALSE;
14142 }
14143
14144 if (end_path)
14145 {
14146 gint y;
14147
14148 if (gtk_tree_view_get_height (tree_view) < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
14149 y = gtk_tree_view_get_height (tree_view) - 1;
14150 else
14151 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
14152
14153 _gtk_rbtree_find_offset (tree_view->priv->tree, y, &tree, &node);
14154 if (node)
14155 *end_path = _gtk_tree_path_new_from_rbtree (tree, node);
14156 else
14157 retval = FALSE;
14158 }
14159
14160 return retval;
14161 }
14162
14163 /**
14164 * gtk_tree_view_is_blank_at_pos:
14165 * @tree_view: A #GtkTreeView
14166 * @x: The x position to be identified (relative to bin_window)
14167 * @y: The y position to be identified (relative to bin_window)
14168 * @path: (out) (optional) (nullable): A pointer to a #GtkTreePath pointer to
14169 * be filled in, or %NULL
14170 * @column: (out) (transfer none) (optional) (nullable): A pointer to a
14171 * #GtkTreeViewColumn pointer to be filled in, or %NULL
14172 * @cell_x: (out) (optional): A pointer where the X coordinate relative to the
14173 * cell can be placed, or %NULL
14174 * @cell_y: (out) (optional): A pointer where the Y coordinate relative to the
14175 * cell can be placed, or %NULL
14176 *
14177 * Determine whether the point (@x, @y) in @tree_view is blank, that is no
14178 * cell content nor an expander arrow is drawn at the location. If so, the
14179 * location can be considered as the background. You might wish to take
14180 * special action on clicks on the background, such as clearing a current
14181 * selection, having a custom context menu or starting rubber banding.
14182 *
14183 * The @x and @y coordinate that are provided must be relative to bin_window
14184 * coordinates. That is, @x and @y must come from an event on @tree_view
14185 * where `event->window == gtk_tree_view_get_bin_window ()`.
14186 *
14187 * For converting widget coordinates (eg. the ones you get from
14188 * GtkWidget::query-tooltip), please see
14189 * gtk_tree_view_convert_widget_to_bin_window_coords().
14190 *
14191 * The @path, @column, @cell_x and @cell_y arguments will be filled in
14192 * likewise as for gtk_tree_view_get_path_at_pos(). Please see
14193 * gtk_tree_view_get_path_at_pos() for more information.
14194 *
14195 * Returns: %TRUE if the area at the given coordinates is blank,
14196 * %FALSE otherwise.
14197 *
14198 * Since: 3.0
14199 */
14200 gboolean
gtk_tree_view_is_blank_at_pos(GtkTreeView * tree_view,gint x,gint y,GtkTreePath ** path,GtkTreeViewColumn ** column,gint * cell_x,gint * cell_y)14201 gtk_tree_view_is_blank_at_pos (GtkTreeView *tree_view,
14202 gint x,
14203 gint y,
14204 GtkTreePath **path,
14205 GtkTreeViewColumn **column,
14206 gint *cell_x,
14207 gint *cell_y)
14208 {
14209 GtkRBTree *tree;
14210 GtkRBNode *node;
14211 GtkTreeIter iter;
14212 GtkTreePath *real_path;
14213 GtkTreeViewColumn *real_column;
14214 GdkRectangle cell_area, background_area;
14215
14216 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14217
14218 if (!gtk_tree_view_get_path_at_pos (tree_view, x, y,
14219 &real_path, &real_column,
14220 cell_x, cell_y))
14221 /* If there's no path here, it is blank */
14222 return TRUE;
14223
14224 if (path)
14225 *path = real_path;
14226
14227 if (column)
14228 *column = real_column;
14229
14230 gtk_tree_model_get_iter (tree_view->priv->model, &iter, real_path);
14231 _gtk_tree_view_find_node (tree_view, real_path, &tree, &node);
14232
14233 /* Check if there's an expander arrow at (x, y) */
14234 if (real_column == tree_view->priv->expander_column
14235 && gtk_tree_view_draw_expanders (tree_view))
14236 {
14237 gboolean over_arrow;
14238
14239 over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
14240
14241 if (over_arrow)
14242 {
14243 if (!path)
14244 gtk_tree_path_free (real_path);
14245 return FALSE;
14246 }
14247 }
14248
14249 /* Otherwise, have the column see if there's a cell at (x, y) */
14250 gtk_tree_view_column_cell_set_cell_data (real_column,
14251 tree_view->priv->model,
14252 &iter,
14253 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
14254 node->children ? TRUE : FALSE);
14255
14256 gtk_tree_view_get_background_area (tree_view, real_path, real_column,
14257 &background_area);
14258 gtk_tree_view_get_cell_area (tree_view, real_path, real_column,
14259 &cell_area);
14260
14261 if (!path)
14262 gtk_tree_path_free (real_path);
14263
14264 return _gtk_tree_view_column_is_blank_at_pos (real_column,
14265 &cell_area,
14266 &background_area,
14267 x, y);
14268 }
14269
14270 static void
unset_reorderable(GtkTreeView * tree_view)14271 unset_reorderable (GtkTreeView *tree_view)
14272 {
14273 if (tree_view->priv->reorderable)
14274 {
14275 tree_view->priv->reorderable = FALSE;
14276 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_REORDERABLE]);
14277 }
14278 }
14279
14280 /**
14281 * gtk_tree_view_enable_model_drag_source:
14282 * @tree_view: a #GtkTreeView
14283 * @start_button_mask: Mask of allowed buttons to start drag
14284 * @targets: (array length=n_targets): the table of targets that the drag will support
14285 * @n_targets: the number of items in @targets
14286 * @actions: the bitmask of possible actions for a drag from this
14287 * widget
14288 *
14289 * Turns @tree_view into a drag source for automatic DND. Calling this
14290 * method sets #GtkTreeView:reorderable to %FALSE.
14291 **/
14292 void
gtk_tree_view_enable_model_drag_source(GtkTreeView * tree_view,GdkModifierType start_button_mask,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)14293 gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view,
14294 GdkModifierType start_button_mask,
14295 const GtkTargetEntry *targets,
14296 gint n_targets,
14297 GdkDragAction actions)
14298 {
14299 TreeViewDragInfo *di;
14300
14301 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14302
14303 gtk_drag_source_set (GTK_WIDGET (tree_view),
14304 0,
14305 targets,
14306 n_targets,
14307 actions);
14308
14309 di = ensure_info (tree_view);
14310
14311 di->start_button_mask = start_button_mask;
14312 di->source_actions = actions;
14313 di->source_set = TRUE;
14314
14315 unset_reorderable (tree_view);
14316 }
14317
14318 /**
14319 * gtk_tree_view_enable_model_drag_dest:
14320 * @tree_view: a #GtkTreeView
14321 * @targets: (array length=n_targets): the table of targets that
14322 * the drag will support
14323 * @n_targets: the number of items in @targets
14324 * @actions: the bitmask of possible actions for a drag from this
14325 * widget
14326 *
14327 * Turns @tree_view into a drop destination for automatic DND. Calling
14328 * this method sets #GtkTreeView:reorderable to %FALSE.
14329 **/
14330 void
gtk_tree_view_enable_model_drag_dest(GtkTreeView * tree_view,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)14331 gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view,
14332 const GtkTargetEntry *targets,
14333 gint n_targets,
14334 GdkDragAction actions)
14335 {
14336 TreeViewDragInfo *di;
14337
14338 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14339
14340 gtk_drag_dest_set (GTK_WIDGET (tree_view),
14341 0,
14342 targets,
14343 n_targets,
14344 actions);
14345
14346 di = ensure_info (tree_view);
14347 di->dest_set = TRUE;
14348
14349 unset_reorderable (tree_view);
14350 }
14351
14352 /**
14353 * gtk_tree_view_unset_rows_drag_source:
14354 * @tree_view: a #GtkTreeView
14355 *
14356 * Undoes the effect of
14357 * gtk_tree_view_enable_model_drag_source(). Calling this method sets
14358 * #GtkTreeView:reorderable to %FALSE.
14359 **/
14360 void
gtk_tree_view_unset_rows_drag_source(GtkTreeView * tree_view)14361 gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view)
14362 {
14363 TreeViewDragInfo *di;
14364
14365 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14366
14367 di = get_info (tree_view);
14368
14369 if (di)
14370 {
14371 if (di->source_set)
14372 {
14373 gtk_drag_source_unset (GTK_WIDGET (tree_view));
14374 di->source_set = FALSE;
14375 }
14376
14377 if (!di->dest_set && !di->source_set)
14378 remove_info (tree_view);
14379 }
14380
14381 unset_reorderable (tree_view);
14382 }
14383
14384 /**
14385 * gtk_tree_view_unset_rows_drag_dest:
14386 * @tree_view: a #GtkTreeView
14387 *
14388 * Undoes the effect of
14389 * gtk_tree_view_enable_model_drag_dest(). Calling this method sets
14390 * #GtkTreeView:reorderable to %FALSE.
14391 **/
14392 void
gtk_tree_view_unset_rows_drag_dest(GtkTreeView * tree_view)14393 gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view)
14394 {
14395 TreeViewDragInfo *di;
14396
14397 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14398
14399 di = get_info (tree_view);
14400
14401 if (di)
14402 {
14403 if (di->dest_set)
14404 {
14405 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
14406 di->dest_set = FALSE;
14407 }
14408
14409 if (!di->dest_set && !di->source_set)
14410 remove_info (tree_view);
14411 }
14412
14413 unset_reorderable (tree_view);
14414 }
14415
14416 /**
14417 * gtk_tree_view_set_drag_dest_row:
14418 * @tree_view: a #GtkTreeView
14419 * @path: (allow-none): The path of the row to highlight, or %NULL
14420 * @pos: Specifies whether to drop before, after or into the row
14421 *
14422 * Sets the row that is highlighted for feedback.
14423 * If @path is %NULL, an existing highlight is removed.
14424 */
14425 void
gtk_tree_view_set_drag_dest_row(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewDropPosition pos)14426 gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view,
14427 GtkTreePath *path,
14428 GtkTreeViewDropPosition pos)
14429 {
14430 GtkTreePath *current_dest;
14431
14432 /* Note; this function is exported to allow a custom DND
14433 * implementation, so it can't touch TreeViewDragInfo
14434 */
14435
14436 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14437
14438 current_dest = NULL;
14439
14440 if (tree_view->priv->drag_dest_row)
14441 {
14442 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
14443 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
14444 }
14445
14446 /* special case a drop on an empty model */
14447 tree_view->priv->empty_view_drop = 0;
14448
14449 if (pos == GTK_TREE_VIEW_DROP_BEFORE && path
14450 && gtk_tree_path_get_depth (path) == 1
14451 && gtk_tree_path_get_indices (path)[0] == 0)
14452 {
14453 gint n_children;
14454
14455 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
14456 NULL);
14457
14458 if (!n_children)
14459 tree_view->priv->empty_view_drop = 1;
14460 }
14461
14462 tree_view->priv->drag_dest_pos = pos;
14463
14464 if (path)
14465 {
14466 tree_view->priv->drag_dest_row =
14467 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
14468 gtk_tree_view_queue_draw_path (tree_view, path, NULL);
14469 }
14470 else
14471 tree_view->priv->drag_dest_row = NULL;
14472
14473 if (current_dest)
14474 {
14475 GtkRBTree *tree, *new_tree;
14476 GtkRBNode *node, *new_node;
14477
14478 _gtk_tree_view_find_node (tree_view, current_dest, &tree, &node);
14479 _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
14480
14481 if (tree && node)
14482 {
14483 _gtk_rbtree_next_full (tree, node, &new_tree, &new_node);
14484 if (new_tree && new_node)
14485 _gtk_tree_view_queue_draw_node (tree_view, new_tree, new_node, NULL);
14486
14487 _gtk_rbtree_prev_full (tree, node, &new_tree, &new_node);
14488 if (new_tree && new_node)
14489 _gtk_tree_view_queue_draw_node (tree_view, new_tree, new_node, NULL);
14490 }
14491 gtk_tree_path_free (current_dest);
14492 }
14493 }
14494
14495 /**
14496 * gtk_tree_view_get_drag_dest_row:
14497 * @tree_view: a #GtkTreeView
14498 * @path: (out) (optional) (nullable): Return location for the path of the highlighted row, or %NULL.
14499 * @pos: (out) (optional): Return location for the drop position, or %NULL
14500 *
14501 * Gets information about the row that is highlighted for feedback.
14502 **/
14503 void
gtk_tree_view_get_drag_dest_row(GtkTreeView * tree_view,GtkTreePath ** path,GtkTreeViewDropPosition * pos)14504 gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view,
14505 GtkTreePath **path,
14506 GtkTreeViewDropPosition *pos)
14507 {
14508 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14509
14510 if (path)
14511 {
14512 if (tree_view->priv->drag_dest_row)
14513 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
14514 else
14515 {
14516 if (tree_view->priv->empty_view_drop)
14517 *path = gtk_tree_path_new_from_indices (0, -1);
14518 else
14519 *path = NULL;
14520 }
14521 }
14522
14523 if (pos)
14524 *pos = tree_view->priv->drag_dest_pos;
14525 }
14526
14527 /**
14528 * gtk_tree_view_get_dest_row_at_pos:
14529 * @tree_view: a #GtkTreeView
14530 * @drag_x: the position to determine the destination row for
14531 * @drag_y: the position to determine the destination row for
14532 * @path: (out) (optional) (nullable): Return location for the path of
14533 * the highlighted row, or %NULL.
14534 * @pos: (out) (optional): Return location for the drop position, or
14535 * %NULL
14536 *
14537 * Determines the destination row for a given position. @drag_x and
14538 * @drag_y are expected to be in widget coordinates. This function is only
14539 * meaningful if @tree_view is realized. Therefore this function will always
14540 * return %FALSE if @tree_view is not realized or does not have a model.
14541 *
14542 * Returns: whether there is a row at the given position, %TRUE if this
14543 * is indeed the case.
14544 **/
14545 gboolean
gtk_tree_view_get_dest_row_at_pos(GtkTreeView * tree_view,gint drag_x,gint drag_y,GtkTreePath ** path,GtkTreeViewDropPosition * pos)14546 gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view,
14547 gint drag_x,
14548 gint drag_y,
14549 GtkTreePath **path,
14550 GtkTreeViewDropPosition *pos)
14551 {
14552 gint cell_y;
14553 gint bin_x, bin_y;
14554 gdouble offset_into_row;
14555 gdouble fourth;
14556 GdkRectangle cell;
14557 GtkTreeViewColumn *column = NULL;
14558 GtkTreePath *tmp_path = NULL;
14559
14560 /* Note; this function is exported to allow a custom DND
14561 * implementation, so it can't touch TreeViewDragInfo
14562 */
14563
14564 g_return_val_if_fail (tree_view != NULL, FALSE);
14565 g_return_val_if_fail (drag_x >= 0, FALSE);
14566 g_return_val_if_fail (drag_y >= 0, FALSE);
14567
14568 if (path)
14569 *path = NULL;
14570
14571 if (tree_view->priv->bin_window == NULL)
14572 return FALSE;
14573
14574 if (tree_view->priv->tree == NULL)
14575 return FALSE;
14576
14577 /* If in the top fourth of a row, we drop before that row; if
14578 * in the bottom fourth, drop after that row; if in the middle,
14579 * and the row has children, drop into the row.
14580 */
14581 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
14582 &bin_x, &bin_y);
14583
14584 if (!gtk_tree_view_get_path_at_pos (tree_view,
14585 bin_x,
14586 bin_y,
14587 &tmp_path,
14588 &column,
14589 NULL,
14590 &cell_y))
14591 return FALSE;
14592
14593 gtk_tree_view_get_background_area (tree_view, tmp_path, column,
14594 &cell);
14595
14596 offset_into_row = cell_y;
14597
14598 if (path)
14599 *path = tmp_path;
14600 else
14601 gtk_tree_path_free (tmp_path);
14602
14603 tmp_path = NULL;
14604
14605 fourth = cell.height / 4.0;
14606
14607 if (pos)
14608 {
14609 if (offset_into_row < fourth)
14610 {
14611 *pos = GTK_TREE_VIEW_DROP_BEFORE;
14612 }
14613 else if (offset_into_row < (cell.height / 2.0))
14614 {
14615 *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
14616 }
14617 else if (offset_into_row < cell.height - fourth)
14618 {
14619 *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
14620 }
14621 else
14622 {
14623 *pos = GTK_TREE_VIEW_DROP_AFTER;
14624 }
14625 }
14626
14627 return TRUE;
14628 }
14629
14630
14631
14632 /* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */
14633 /**
14634 * gtk_tree_view_create_row_drag_icon:
14635 * @tree_view: a #GtkTreeView
14636 * @path: a #GtkTreePath in @tree_view
14637 *
14638 * Creates a #cairo_surface_t representation of the row at @path.
14639 * This image is used for a drag icon.
14640 *
14641 * Returns: (transfer full): a newly-allocated surface of the drag icon.
14642 **/
14643 cairo_surface_t *
gtk_tree_view_create_row_drag_icon(GtkTreeView * tree_view,GtkTreePath * path)14644 gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view,
14645 GtkTreePath *path)
14646 {
14647 GtkTreeIter iter;
14648 GtkRBTree *tree;
14649 GtkRBNode *node;
14650 GtkStyleContext *context;
14651 gint cell_offset;
14652 GList *list;
14653 GdkRectangle background_area;
14654 GtkWidget *widget;
14655 gint depth;
14656 /* start drawing inside the black outline */
14657 gint x = 1, y = 1;
14658 cairo_surface_t *surface;
14659 gint bin_window_width;
14660 gboolean is_separator = FALSE;
14661 gboolean rtl;
14662 cairo_t *cr;
14663
14664 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14665 g_return_val_if_fail (path != NULL, NULL);
14666
14667 widget = GTK_WIDGET (tree_view);
14668
14669 if (!gtk_widget_get_realized (widget))
14670 return NULL;
14671
14672 depth = gtk_tree_path_get_depth (path);
14673
14674 _gtk_tree_view_find_node (tree_view,
14675 path,
14676 &tree,
14677 &node);
14678
14679 if (tree == NULL)
14680 return NULL;
14681
14682 if (!gtk_tree_model_get_iter (tree_view->priv->model,
14683 &iter,
14684 path))
14685 return NULL;
14686
14687 context = gtk_widget_get_style_context (widget);
14688
14689 is_separator = row_is_separator (tree_view, &iter, NULL);
14690
14691 cell_offset = x;
14692
14693 background_area.y = y;
14694 background_area.height = gtk_tree_view_get_row_height (tree_view, node);
14695
14696 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
14697
14698 surface = gdk_window_create_similar_surface (tree_view->priv->bin_window,
14699 CAIRO_CONTENT_COLOR,
14700 bin_window_width + 2,
14701 background_area.height + 2);
14702
14703 cr = cairo_create (surface);
14704
14705 gtk_render_background (context, cr, 0, 0,
14706 bin_window_width + 2,
14707 background_area.height + 2);
14708
14709 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
14710
14711 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
14712 list;
14713 list = (rtl ? list->prev : list->next))
14714 {
14715 GtkTreeViewColumn *column = list->data;
14716 GdkRectangle cell_area;
14717 gint vertical_separator;
14718
14719 if (!gtk_tree_view_column_get_visible (column))
14720 continue;
14721
14722 gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter,
14723 GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
14724 node->children?TRUE:FALSE);
14725
14726 background_area.x = cell_offset;
14727 background_area.width = gtk_tree_view_column_get_width (column);
14728
14729 gtk_widget_style_get (widget,
14730 "vertical-separator", &vertical_separator,
14731 NULL);
14732
14733 cell_area = background_area;
14734
14735 cell_area.y += vertical_separator / 2;
14736 cell_area.height -= vertical_separator;
14737
14738 if (gtk_tree_view_is_expander_column (tree_view, column))
14739 {
14740 if (!rtl)
14741 cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
14742 cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
14743
14744 if (gtk_tree_view_draw_expanders (tree_view))
14745 {
14746 int expander_size = gtk_tree_view_get_expander_size (tree_view);
14747 if (!rtl)
14748 cell_area.x += depth * expander_size;
14749 cell_area.width -= depth * expander_size;
14750 }
14751 }
14752
14753 if (gtk_tree_view_column_cell_is_visible (column))
14754 {
14755 if (is_separator)
14756 {
14757 gtk_style_context_save (context);
14758 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SEPARATOR);
14759
14760 gtk_render_line (context, cr,
14761 cell_area.x,
14762 cell_area.y + cell_area.height / 2,
14763 cell_area.x + cell_area.width,
14764 cell_area.y + cell_area.height / 2);
14765
14766 gtk_style_context_restore (context);
14767 }
14768 else
14769 {
14770 _gtk_tree_view_column_cell_render (column,
14771 cr,
14772 &background_area,
14773 &cell_area,
14774 0, FALSE);
14775 }
14776 }
14777 cell_offset += gtk_tree_view_column_get_width (column);
14778 }
14779
14780 cairo_set_source_rgb (cr, 0, 0, 0);
14781 cairo_rectangle (cr,
14782 0.5, 0.5,
14783 bin_window_width + 1,
14784 background_area.height + 1);
14785 cairo_set_line_width (cr, 1.0);
14786 cairo_stroke (cr);
14787
14788 cairo_destroy (cr);
14789
14790 cairo_surface_set_device_offset (surface, 2, 2);
14791
14792 return surface;
14793 }
14794
14795
14796 /**
14797 * gtk_tree_view_set_destroy_count_func:
14798 * @tree_view: A #GtkTreeView
14799 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
14800 * @data: (allow-none): User data to be passed to @func, or %NULL
14801 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
14802 *
14803 * This function should almost never be used. It is meant for private use by
14804 * ATK for determining the number of visible children that are removed when the
14805 * user collapses a row, or a row is deleted.
14806 *
14807 * Deprecated: 3.4: Accessibility does not need the function anymore.
14808 **/
14809 void
gtk_tree_view_set_destroy_count_func(GtkTreeView * tree_view,GtkTreeDestroyCountFunc func,gpointer data,GDestroyNotify destroy)14810 gtk_tree_view_set_destroy_count_func (GtkTreeView *tree_view,
14811 GtkTreeDestroyCountFunc func,
14812 gpointer data,
14813 GDestroyNotify destroy)
14814 {
14815 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14816
14817 if (tree_view->priv->destroy_count_destroy)
14818 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
14819
14820 tree_view->priv->destroy_count_func = func;
14821 tree_view->priv->destroy_count_data = data;
14822 tree_view->priv->destroy_count_destroy = destroy;
14823 }
14824
14825
14826 /*
14827 * Interactive search
14828 */
14829
14830 /**
14831 * gtk_tree_view_set_enable_search:
14832 * @tree_view: A #GtkTreeView
14833 * @enable_search: %TRUE, if the user can search interactively
14834 *
14835 * If @enable_search is set, then the user can type in text to search through
14836 * the tree interactively (this is sometimes called "typeahead find").
14837 *
14838 * Note that even if this is %FALSE, the user can still initiate a search
14839 * using the “start-interactive-search” key binding.
14840 */
14841 void
gtk_tree_view_set_enable_search(GtkTreeView * tree_view,gboolean enable_search)14842 gtk_tree_view_set_enable_search (GtkTreeView *tree_view,
14843 gboolean enable_search)
14844 {
14845 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14846
14847 enable_search = !!enable_search;
14848
14849 if (tree_view->priv->enable_search != enable_search)
14850 {
14851 tree_view->priv->enable_search = enable_search;
14852 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_SEARCH]);
14853 }
14854 }
14855
14856 /**
14857 * gtk_tree_view_get_enable_search:
14858 * @tree_view: A #GtkTreeView
14859 *
14860 * Returns whether or not the tree allows to start interactive searching
14861 * by typing in text.
14862 *
14863 * Returns: whether or not to let the user search interactively
14864 */
14865 gboolean
gtk_tree_view_get_enable_search(GtkTreeView * tree_view)14866 gtk_tree_view_get_enable_search (GtkTreeView *tree_view)
14867 {
14868 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14869
14870 return tree_view->priv->enable_search;
14871 }
14872
14873
14874 /**
14875 * gtk_tree_view_get_search_column:
14876 * @tree_view: A #GtkTreeView
14877 *
14878 * Gets the column searched on by the interactive search code.
14879 *
14880 * Returns: the column the interactive search code searches in.
14881 */
14882 gint
gtk_tree_view_get_search_column(GtkTreeView * tree_view)14883 gtk_tree_view_get_search_column (GtkTreeView *tree_view)
14884 {
14885 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
14886
14887 return (tree_view->priv->search_column);
14888 }
14889
14890 /**
14891 * gtk_tree_view_set_search_column:
14892 * @tree_view: A #GtkTreeView
14893 * @column: the column of the model to search in, or -1 to disable searching
14894 *
14895 * Sets @column as the column where the interactive search code should
14896 * search in for the current model.
14897 *
14898 * If the search column is set, users can use the “start-interactive-search”
14899 * key binding to bring up search popup. The enable-search property controls
14900 * whether simply typing text will also start an interactive search.
14901 *
14902 * Note that @column refers to a column of the current model. The search
14903 * column is reset to -1 when the model is changed.
14904 */
14905 void
gtk_tree_view_set_search_column(GtkTreeView * tree_view,gint column)14906 gtk_tree_view_set_search_column (GtkTreeView *tree_view,
14907 gint column)
14908 {
14909 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14910 g_return_if_fail (column >= -1);
14911
14912 if (tree_view->priv->search_column == column)
14913 return;
14914
14915 tree_view->priv->search_column = column;
14916 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SEARCH_COLUMN]);
14917 }
14918
14919 /**
14920 * gtk_tree_view_get_search_equal_func: (skip)
14921 * @tree_view: A #GtkTreeView
14922 *
14923 * Returns the compare function currently in use.
14924 *
14925 * Returns: the currently used compare function for the search code.
14926 */
14927
14928 GtkTreeViewSearchEqualFunc
gtk_tree_view_get_search_equal_func(GtkTreeView * tree_view)14929 gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view)
14930 {
14931 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14932
14933 return tree_view->priv->search_equal_func;
14934 }
14935
14936 /**
14937 * gtk_tree_view_set_search_equal_func:
14938 * @tree_view: A #GtkTreeView
14939 * @search_equal_func: the compare function to use during the search
14940 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
14941 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
14942 *
14943 * Sets the compare function for the interactive search capabilities; note
14944 * that somewhat like strcmp() returning 0 for equality
14945 * #GtkTreeViewSearchEqualFunc returns %FALSE on matches.
14946 **/
14947 void
gtk_tree_view_set_search_equal_func(GtkTreeView * tree_view,GtkTreeViewSearchEqualFunc search_equal_func,gpointer search_user_data,GDestroyNotify search_destroy)14948 gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view,
14949 GtkTreeViewSearchEqualFunc search_equal_func,
14950 gpointer search_user_data,
14951 GDestroyNotify search_destroy)
14952 {
14953 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14954 g_return_if_fail (search_equal_func != NULL);
14955
14956 if (tree_view->priv->search_destroy)
14957 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
14958
14959 tree_view->priv->search_equal_func = search_equal_func;
14960 tree_view->priv->search_user_data = search_user_data;
14961 tree_view->priv->search_destroy = search_destroy;
14962 if (tree_view->priv->search_equal_func == NULL)
14963 tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func;
14964 }
14965
14966 /**
14967 * gtk_tree_view_get_search_entry:
14968 * @tree_view: A #GtkTreeView
14969 *
14970 * Returns the #GtkEntry which is currently in use as interactive search
14971 * entry for @tree_view. In case the built-in entry is being used, %NULL
14972 * will be returned.
14973 *
14974 * Returns: (transfer none): the entry currently in use as search entry.
14975 *
14976 * Since: 2.10
14977 */
14978 GtkEntry *
gtk_tree_view_get_search_entry(GtkTreeView * tree_view)14979 gtk_tree_view_get_search_entry (GtkTreeView *tree_view)
14980 {
14981 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14982
14983 if (tree_view->priv->search_custom_entry_set)
14984 return GTK_ENTRY (tree_view->priv->search_entry);
14985
14986 return NULL;
14987 }
14988
14989 /**
14990 * gtk_tree_view_set_search_entry:
14991 * @tree_view: A #GtkTreeView
14992 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
14993 *
14994 * Sets the entry which the interactive search code will use for this
14995 * @tree_view. This is useful when you want to provide a search entry
14996 * in our interface at all time at a fixed position. Passing %NULL for
14997 * @entry will make the interactive search code use the built-in popup
14998 * entry again.
14999 *
15000 * Since: 2.10
15001 */
15002 void
gtk_tree_view_set_search_entry(GtkTreeView * tree_view,GtkEntry * entry)15003 gtk_tree_view_set_search_entry (GtkTreeView *tree_view,
15004 GtkEntry *entry)
15005 {
15006 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15007 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
15008
15009 if (tree_view->priv->search_custom_entry_set)
15010 {
15011 if (tree_view->priv->search_entry_changed_id)
15012 {
15013 g_signal_handler_disconnect (tree_view->priv->search_entry,
15014 tree_view->priv->search_entry_changed_id);
15015 tree_view->priv->search_entry_changed_id = 0;
15016 }
15017 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
15018 G_CALLBACK (gtk_tree_view_search_key_press_event),
15019 tree_view);
15020
15021 g_object_unref (tree_view->priv->search_entry);
15022 }
15023 else if (tree_view->priv->search_window)
15024 {
15025 gtk_tree_view_destroy_search_window (tree_view);
15026 }
15027
15028 if (entry)
15029 {
15030 tree_view->priv->search_entry = GTK_WIDGET (g_object_ref (entry));
15031 tree_view->priv->search_custom_entry_set = TRUE;
15032
15033 if (tree_view->priv->search_entry_changed_id == 0)
15034 {
15035 tree_view->priv->search_entry_changed_id =
15036 g_signal_connect (tree_view->priv->search_entry, "changed",
15037 G_CALLBACK (gtk_tree_view_search_init),
15038 tree_view);
15039 }
15040
15041 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
15042 G_CALLBACK (gtk_tree_view_search_key_press_event),
15043 tree_view);
15044
15045 gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
15046 }
15047 else
15048 {
15049 tree_view->priv->search_entry = NULL;
15050 tree_view->priv->search_custom_entry_set = FALSE;
15051 }
15052 }
15053
15054 /**
15055 * gtk_tree_view_set_search_position_func:
15056 * @tree_view: A #GtkTreeView
15057 * @func: (allow-none): the function to use to position the search dialog, or %NULL
15058 * to use the default search position function
15059 * @data: (allow-none): user data to pass to @func, or %NULL
15060 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
15061 *
15062 * Sets the function to use when positioning the search dialog.
15063 *
15064 * Since: 2.10
15065 **/
15066 void
gtk_tree_view_set_search_position_func(GtkTreeView * tree_view,GtkTreeViewSearchPositionFunc func,gpointer user_data,GDestroyNotify destroy)15067 gtk_tree_view_set_search_position_func (GtkTreeView *tree_view,
15068 GtkTreeViewSearchPositionFunc func,
15069 gpointer user_data,
15070 GDestroyNotify destroy)
15071 {
15072 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15073
15074 if (tree_view->priv->search_position_destroy)
15075 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
15076
15077 tree_view->priv->search_position_func = func;
15078 tree_view->priv->search_position_user_data = user_data;
15079 tree_view->priv->search_position_destroy = destroy;
15080 if (tree_view->priv->search_position_func == NULL)
15081 tree_view->priv->search_position_func = gtk_tree_view_search_position_func;
15082 }
15083
15084 /**
15085 * gtk_tree_view_get_search_position_func: (skip)
15086 * @tree_view: A #GtkTreeView
15087 *
15088 * Returns the positioning function currently in use.
15089 *
15090 * Returns: the currently used function for positioning the search dialog.
15091 *
15092 * Since: 2.10
15093 */
15094 GtkTreeViewSearchPositionFunc
gtk_tree_view_get_search_position_func(GtkTreeView * tree_view)15095 gtk_tree_view_get_search_position_func (GtkTreeView *tree_view)
15096 {
15097 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
15098
15099 return tree_view->priv->search_position_func;
15100 }
15101
15102
15103 static void
gtk_tree_view_search_window_hide(GtkWidget * search_window,GtkTreeView * tree_view,GdkDevice * device)15104 gtk_tree_view_search_window_hide (GtkWidget *search_window,
15105 GtkTreeView *tree_view,
15106 GdkDevice *device)
15107 {
15108 if (tree_view->priv->disable_popdown)
15109 return;
15110
15111 if (tree_view->priv->search_entry_changed_id)
15112 {
15113 g_signal_handler_disconnect (tree_view->priv->search_entry,
15114 tree_view->priv->search_entry_changed_id);
15115 tree_view->priv->search_entry_changed_id = 0;
15116 }
15117 if (tree_view->priv->typeselect_flush_timeout)
15118 {
15119 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15120 tree_view->priv->typeselect_flush_timeout = 0;
15121 }
15122
15123 if (gtk_widget_get_visible (search_window))
15124 {
15125 /* send focus-in event */
15126 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), device, FALSE);
15127 gtk_widget_hide (search_window);
15128 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
15129 send_focus_change (GTK_WIDGET (tree_view), device, TRUE);
15130 }
15131 }
15132
15133 static void
gtk_tree_view_search_position_func(GtkTreeView * tree_view,GtkWidget * search_window,gpointer user_data)15134 gtk_tree_view_search_position_func (GtkTreeView *tree_view,
15135 GtkWidget *search_window,
15136 gpointer user_data)
15137 {
15138 gint x, y;
15139 gint tree_x, tree_y;
15140 gint tree_width, tree_height;
15141 GdkDisplay *display;
15142 GdkMonitor *monitor;
15143 GdkRectangle workarea;
15144 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
15145 GtkRequisition requisition;
15146
15147 gtk_widget_realize (search_window);
15148
15149 display = gtk_widget_get_display (GTK_WIDGET (tree_view));
15150 monitor = gdk_display_get_monitor_at_window (display, tree_window);
15151 gdk_monitor_get_workarea (monitor, &workarea);
15152
15153 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
15154 tree_width = gdk_window_get_width (tree_window);
15155 tree_height = gdk_window_get_height (tree_window);
15156 gtk_widget_get_preferred_size (search_window, &requisition, NULL);
15157
15158 if (tree_x + tree_width > workarea.x + workarea.width)
15159 x = workarea.x + workarea.width - requisition.width;
15160 else if (tree_x + tree_width - requisition.width < workarea.x)
15161 x = workarea.x;
15162 else
15163 x = tree_x + tree_width - requisition.width;
15164
15165 if (tree_y + tree_height + requisition.height > workarea.y + workarea.height)
15166 y = workarea.y + workarea.height - requisition.height;
15167 else if (tree_y + tree_height < workarea.y) /* isn't really possible ... */
15168 y = workarea.y;
15169 else
15170 y = tree_y + tree_height;
15171
15172 gtk_window_move (GTK_WINDOW (search_window), x, y);
15173 }
15174
15175 static void
gtk_tree_view_search_disable_popdown(GtkEntry * entry,GtkMenu * menu,gpointer data)15176 gtk_tree_view_search_disable_popdown (GtkEntry *entry,
15177 GtkMenu *menu,
15178 gpointer data)
15179 {
15180 GtkTreeView *tree_view = (GtkTreeView *)data;
15181
15182 tree_view->priv->disable_popdown = 1;
15183 g_signal_connect (menu, "hide",
15184 G_CALLBACK (gtk_tree_view_search_enable_popdown), data);
15185 }
15186
15187 /* Because we're visible but offscreen, we just set a flag in the preedit
15188 * callback.
15189 */
15190 static void
gtk_tree_view_search_preedit_changed(GtkIMContext * im_context,GtkTreeView * tree_view)15191 gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
15192 GtkTreeView *tree_view)
15193 {
15194 tree_view->priv->imcontext_changed = 1;
15195 if (tree_view->priv->typeselect_flush_timeout)
15196 {
15197 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15198 tree_view->priv->typeselect_flush_timeout =
15199 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15200 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15201 tree_view);
15202 g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout");
15203 }
15204
15205 }
15206
15207 static void
gtk_tree_view_search_commit(GtkIMContext * im_context,gchar * buf,GtkTreeView * tree_view)15208 gtk_tree_view_search_commit (GtkIMContext *im_context,
15209 gchar *buf,
15210 GtkTreeView *tree_view)
15211 {
15212 tree_view->priv->imcontext_changed = 1;
15213 }
15214
15215 static void
gtk_tree_view_search_activate(GtkEntry * entry,GtkTreeView * tree_view)15216 gtk_tree_view_search_activate (GtkEntry *entry,
15217 GtkTreeView *tree_view)
15218 {
15219 GtkTreePath *path;
15220
15221 gtk_tree_view_search_window_hide (tree_view->priv->search_window,
15222 tree_view,
15223 gtk_get_current_event_device ());
15224
15225 /* If we have a row selected and it's the cursor row, we activate
15226 * the row XXX */
15227 if (tree_view->priv->cursor_node &&
15228 GTK_RBNODE_FLAG_SET (tree_view->priv->cursor_node, GTK_RBNODE_IS_SELECTED))
15229 {
15230 path = _gtk_tree_path_new_from_rbtree (tree_view->priv->cursor_tree,
15231 tree_view->priv->cursor_node);
15232
15233 gtk_tree_view_row_activated (tree_view, path, tree_view->priv->focus_column);
15234
15235 gtk_tree_path_free (path);
15236 }
15237 }
15238
15239 static gboolean
gtk_tree_view_real_search_enable_popdown(gpointer data)15240 gtk_tree_view_real_search_enable_popdown (gpointer data)
15241 {
15242 GtkTreeView *tree_view = (GtkTreeView *)data;
15243
15244 tree_view->priv->disable_popdown = 0;
15245
15246 return FALSE;
15247 }
15248
15249 static void
gtk_tree_view_search_enable_popdown(GtkWidget * widget,gpointer data)15250 gtk_tree_view_search_enable_popdown (GtkWidget *widget,
15251 gpointer data)
15252 {
15253 guint id;
15254 id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, gtk_tree_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
15255 g_source_set_name_by_id (id, "[gtk+] gtk_tree_view_real_search_enable_popdown");
15256 }
15257
15258 static gboolean
gtk_tree_view_search_delete_event(GtkWidget * widget,GdkEventAny * event,GtkTreeView * tree_view)15259 gtk_tree_view_search_delete_event (GtkWidget *widget,
15260 GdkEventAny *event,
15261 GtkTreeView *tree_view)
15262 {
15263 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
15264
15265 gtk_tree_view_search_window_hide (widget, tree_view, NULL);
15266
15267 return TRUE;
15268 }
15269
15270 static gboolean
gtk_tree_view_search_button_press_event(GtkWidget * widget,GdkEventButton * event,GtkTreeView * tree_view)15271 gtk_tree_view_search_button_press_event (GtkWidget *widget,
15272 GdkEventButton *event,
15273 GtkTreeView *tree_view)
15274 {
15275 GdkDevice *keyb_device;
15276
15277 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
15278
15279 keyb_device = gdk_device_get_associated_device (event->device);
15280 gtk_tree_view_search_window_hide (widget, tree_view, keyb_device);
15281
15282 return TRUE;
15283 }
15284
15285 static gboolean
gtk_tree_view_search_scroll_event(GtkWidget * widget,GdkEventScroll * event,GtkTreeView * tree_view)15286 gtk_tree_view_search_scroll_event (GtkWidget *widget,
15287 GdkEventScroll *event,
15288 GtkTreeView *tree_view)
15289 {
15290 gboolean retval = FALSE;
15291
15292 if (event->direction == GDK_SCROLL_UP)
15293 {
15294 gtk_tree_view_search_move (widget, tree_view, TRUE);
15295 retval = TRUE;
15296 }
15297 else if (event->direction == GDK_SCROLL_DOWN)
15298 {
15299 gtk_tree_view_search_move (widget, tree_view, FALSE);
15300 retval = TRUE;
15301 }
15302
15303 /* renew the flush timeout */
15304 if (retval && tree_view->priv->typeselect_flush_timeout
15305 && !tree_view->priv->search_custom_entry_set)
15306 {
15307 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15308 tree_view->priv->typeselect_flush_timeout =
15309 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15310 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15311 tree_view);
15312 g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout");
15313 }
15314
15315 return retval;
15316 }
15317
15318 static gboolean
gtk_tree_view_search_key_press_event(GtkWidget * widget,GdkEventKey * event,GtkTreeView * tree_view)15319 gtk_tree_view_search_key_press_event (GtkWidget *widget,
15320 GdkEventKey *event,
15321 GtkTreeView *tree_view)
15322 {
15323 GdkModifierType default_accel;
15324 gboolean retval = FALSE;
15325
15326 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
15327 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15328
15329 /* close window and cancel the search */
15330 if (!tree_view->priv->search_custom_entry_set
15331 && gtk_tree_view_search_key_cancels_search (event->keyval))
15332 {
15333 gtk_tree_view_search_window_hide (widget, tree_view,
15334 gdk_event_get_device ((GdkEvent *) event));
15335 return TRUE;
15336 }
15337
15338 default_accel = gtk_widget_get_modifier_mask (widget,
15339 GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
15340
15341 /* select previous matching iter */
15342 if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
15343 {
15344 if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
15345 gtk_widget_error_bell (widget);
15346
15347 retval = TRUE;
15348 }
15349
15350 if (((event->state & (default_accel | GDK_SHIFT_MASK)) == (default_accel | GDK_SHIFT_MASK))
15351 && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
15352 {
15353 if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
15354 gtk_widget_error_bell (widget);
15355
15356 retval = TRUE;
15357 }
15358
15359 /* select next matching iter */
15360 if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
15361 {
15362 if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
15363 gtk_widget_error_bell (widget);
15364
15365 retval = TRUE;
15366 }
15367
15368 if (((event->state & (default_accel | GDK_SHIFT_MASK)) == default_accel)
15369 && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
15370 {
15371 if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
15372 gtk_widget_error_bell (widget);
15373
15374 retval = TRUE;
15375 }
15376
15377 /* renew the flush timeout */
15378 if (retval && tree_view->priv->typeselect_flush_timeout
15379 && !tree_view->priv->search_custom_entry_set)
15380 {
15381 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15382 tree_view->priv->typeselect_flush_timeout =
15383 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15384 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15385 tree_view);
15386 g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout");
15387 }
15388
15389 return retval;
15390 }
15391
15392 /* this function returns FALSE if there is a search string but
15393 * nothing was found, and TRUE otherwise.
15394 */
15395 static gboolean
gtk_tree_view_search_move(GtkWidget * window,GtkTreeView * tree_view,gboolean up)15396 gtk_tree_view_search_move (GtkWidget *window,
15397 GtkTreeView *tree_view,
15398 gboolean up)
15399 {
15400 gboolean ret;
15401 gint len;
15402 gint count = 0;
15403 const gchar *text;
15404 GtkTreeIter iter;
15405 GtkTreeModel *model;
15406 GtkTreeSelection *selection;
15407
15408 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
15409
15410 g_return_val_if_fail (text != NULL, FALSE);
15411
15412 len = strlen (text);
15413
15414 if (up && tree_view->priv->selected_iter == 1)
15415 return len < 1;
15416
15417 if (len < 1)
15418 return TRUE;
15419
15420 model = gtk_tree_view_get_model (tree_view);
15421 selection = gtk_tree_view_get_selection (tree_view);
15422
15423 /* search */
15424 gtk_tree_selection_unselect_all (selection);
15425 if (!gtk_tree_model_get_iter_first (model, &iter))
15426 return TRUE;
15427
15428 ret = gtk_tree_view_search_iter (model, selection, &iter, text,
15429 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
15430
15431 if (ret)
15432 {
15433 /* found */
15434 tree_view->priv->selected_iter += up?(-1):(1);
15435 return TRUE;
15436 }
15437 else
15438 {
15439 /* return to old iter */
15440 count = 0;
15441 gtk_tree_model_get_iter_first (model, &iter);
15442 gtk_tree_view_search_iter (model, selection,
15443 &iter, text,
15444 &count, tree_view->priv->selected_iter);
15445 return FALSE;
15446 }
15447 }
15448
15449 static gboolean
gtk_tree_view_search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)15450 gtk_tree_view_search_equal_func (GtkTreeModel *model,
15451 gint column,
15452 const gchar *key,
15453 GtkTreeIter *iter,
15454 gpointer search_data)
15455 {
15456 gboolean retval = TRUE;
15457 const gchar *str;
15458 gchar *normalized_string;
15459 gchar *normalized_key;
15460 gchar *case_normalized_string = NULL;
15461 gchar *case_normalized_key = NULL;
15462 GValue value = G_VALUE_INIT;
15463 GValue transformed = G_VALUE_INIT;
15464
15465 gtk_tree_model_get_value (model, iter, column, &value);
15466
15467 g_value_init (&transformed, G_TYPE_STRING);
15468
15469 if (!g_value_transform (&value, &transformed))
15470 {
15471 g_value_unset (&value);
15472 return TRUE;
15473 }
15474
15475 g_value_unset (&value);
15476
15477 str = g_value_get_string (&transformed);
15478 if (!str)
15479 {
15480 g_value_unset (&transformed);
15481 return TRUE;
15482 }
15483
15484 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
15485 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
15486
15487 if (normalized_string && normalized_key)
15488 {
15489 case_normalized_string = g_utf8_casefold (normalized_string, -1);
15490 case_normalized_key = g_utf8_casefold (normalized_key, -1);
15491
15492 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
15493 retval = FALSE;
15494 }
15495
15496 g_value_unset (&transformed);
15497 g_free (normalized_key);
15498 g_free (normalized_string);
15499 g_free (case_normalized_key);
15500 g_free (case_normalized_string);
15501
15502 return retval;
15503 }
15504
15505 static gboolean
gtk_tree_view_search_iter(GtkTreeModel * model,GtkTreeSelection * selection,GtkTreeIter * iter,const gchar * text,gint * count,gint n)15506 gtk_tree_view_search_iter (GtkTreeModel *model,
15507 GtkTreeSelection *selection,
15508 GtkTreeIter *iter,
15509 const gchar *text,
15510 gint *count,
15511 gint n)
15512 {
15513 GtkRBTree *tree = NULL;
15514 GtkRBNode *node = NULL;
15515 GtkTreePath *path;
15516
15517 GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
15518
15519 path = gtk_tree_model_get_path (model, iter);
15520 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
15521
15522 do
15523 {
15524 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
15525 {
15526 (*count)++;
15527 if (*count == n)
15528 {
15529 gtk_tree_view_scroll_to_cell (tree_view, path, NULL,
15530 TRUE, 0.5, 0.0);
15531 gtk_tree_selection_select_iter (selection, iter);
15532 gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
15533
15534 if (path)
15535 gtk_tree_path_free (path);
15536
15537 return TRUE;
15538 }
15539 }
15540
15541 if (node->children)
15542 {
15543 gboolean has_child;
15544 GtkTreeIter tmp;
15545
15546 tree = node->children;
15547 node = _gtk_rbtree_first (tree);
15548
15549 tmp = *iter;
15550 has_child = gtk_tree_model_iter_children (model, iter, &tmp);
15551 gtk_tree_path_down (path);
15552
15553 /* sanity check */
15554 TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
15555 }
15556 else
15557 {
15558 gboolean done = FALSE;
15559
15560 do
15561 {
15562 node = _gtk_rbtree_next (tree, node);
15563
15564 if (node)
15565 {
15566 gboolean has_next;
15567
15568 has_next = gtk_tree_model_iter_next (model, iter);
15569
15570 done = TRUE;
15571 gtk_tree_path_next (path);
15572
15573 /* sanity check */
15574 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
15575 }
15576 else
15577 {
15578 gboolean has_parent;
15579 GtkTreeIter tmp_iter = *iter;
15580
15581 node = tree->parent_node;
15582 tree = tree->parent_tree;
15583
15584 if (!tree)
15585 {
15586 if (path)
15587 gtk_tree_path_free (path);
15588
15589 /* we've run out of tree, done with this func */
15590 return FALSE;
15591 }
15592
15593 has_parent = gtk_tree_model_iter_parent (model,
15594 iter,
15595 &tmp_iter);
15596 gtk_tree_path_up (path);
15597
15598 /* sanity check */
15599 TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
15600 }
15601 }
15602 while (!done);
15603 }
15604 }
15605 while (1);
15606
15607 return FALSE;
15608 }
15609
15610 static void
gtk_tree_view_search_init(GtkWidget * entry,GtkTreeView * tree_view)15611 gtk_tree_view_search_init (GtkWidget *entry,
15612 GtkTreeView *tree_view)
15613 {
15614 gint ret;
15615 gint count = 0;
15616 const gchar *text;
15617 GtkTreeIter iter;
15618 GtkTreeModel *model;
15619 GtkTreeSelection *selection;
15620
15621 g_return_if_fail (GTK_IS_ENTRY (entry));
15622 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15623
15624 text = gtk_entry_get_text (GTK_ENTRY (entry));
15625
15626 model = gtk_tree_view_get_model (tree_view);
15627 selection = gtk_tree_view_get_selection (tree_view);
15628
15629 /* search */
15630 gtk_tree_selection_unselect_all (selection);
15631 if (tree_view->priv->typeselect_flush_timeout
15632 && !tree_view->priv->search_custom_entry_set)
15633 {
15634 g_source_remove (tree_view->priv->typeselect_flush_timeout);
15635 tree_view->priv->typeselect_flush_timeout =
15636 gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
15637 (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
15638 tree_view);
15639 g_source_set_name_by_id (tree_view->priv->typeselect_flush_timeout, "[gtk+] gtk_tree_view_search_entry_flush_timeout");
15640 }
15641
15642 if (*text == '\0')
15643 return;
15644
15645 if (!gtk_tree_model_get_iter_first (model, &iter))
15646 return;
15647
15648 ret = gtk_tree_view_search_iter (model, selection,
15649 &iter, text,
15650 &count, 1);
15651
15652 if (ret)
15653 tree_view->priv->selected_iter = 1;
15654 }
15655
15656 void
_gtk_tree_view_remove_editable(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkCellEditable * cell_editable)15657 _gtk_tree_view_remove_editable (GtkTreeView *tree_view,
15658 GtkTreeViewColumn *column,
15659 GtkCellEditable *cell_editable)
15660 {
15661 if (tree_view->priv->edited_column == NULL)
15662 return;
15663
15664 g_return_if_fail (column == tree_view->priv->edited_column);
15665
15666 tree_view->priv->edited_column = NULL;
15667
15668 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
15669 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
15670
15671 gtk_container_remove (GTK_CONTAINER (tree_view),
15672 GTK_WIDGET (cell_editable));
15673
15674 /* FIXME should only redraw a single node */
15675 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15676 }
15677
15678 static gboolean
gtk_tree_view_start_editing(GtkTreeView * tree_view,GtkTreePath * cursor_path,gboolean edit_only)15679 gtk_tree_view_start_editing (GtkTreeView *tree_view,
15680 GtkTreePath *cursor_path,
15681 gboolean edit_only)
15682 {
15683 GtkTreeIter iter;
15684 GdkRectangle cell_area;
15685 GtkTreeViewColumn *focus_column;
15686 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
15687 gint retval = FALSE;
15688 GtkRBTree *cursor_tree;
15689 GtkRBNode *cursor_node;
15690
15691 g_assert (tree_view->priv->focus_column);
15692 focus_column = tree_view->priv->focus_column;
15693
15694 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
15695 return FALSE;
15696
15697 if (_gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node) ||
15698 cursor_node == NULL)
15699 return FALSE;
15700
15701 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
15702
15703 validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path);
15704
15705 gtk_tree_view_column_cell_set_cell_data (focus_column,
15706 tree_view->priv->model,
15707 &iter,
15708 GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
15709 cursor_node->children ? TRUE : FALSE);
15710 gtk_tree_view_get_cell_area (tree_view,
15711 cursor_path,
15712 focus_column,
15713 &cell_area);
15714
15715 if (gtk_cell_area_activate (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (focus_column)),
15716 _gtk_tree_view_column_get_context (focus_column),
15717 GTK_WIDGET (tree_view),
15718 &cell_area,
15719 flags, edit_only))
15720 retval = TRUE;
15721
15722 return retval;
15723 }
15724
15725 void
_gtk_tree_view_add_editable(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkTreePath * path,GtkCellEditable * cell_editable,GdkRectangle * cell_area)15726 _gtk_tree_view_add_editable (GtkTreeView *tree_view,
15727 GtkTreeViewColumn *column,
15728 GtkTreePath *path,
15729 GtkCellEditable *cell_editable,
15730 GdkRectangle *cell_area)
15731 {
15732 GdkRectangle full_area;
15733 GtkBorder border;
15734
15735 tree_view->priv->edited_column = column;
15736
15737 gtk_tree_view_real_set_cursor (tree_view, path, CLAMP_NODE);
15738
15739 tree_view->priv->draw_keyfocus = TRUE;
15740
15741 gtk_tree_view_get_cell_area (tree_view, path, column, &full_area);
15742 border.left = cell_area->x - full_area.x;
15743 border.top = cell_area->y - full_area.y;
15744 border.right = (full_area.x + full_area.width) - (cell_area->x + cell_area->width);
15745 border.bottom = (full_area.y + full_area.height) - (cell_area->y + cell_area->height);
15746
15747 gtk_tree_view_put (tree_view,
15748 GTK_WIDGET (cell_editable),
15749 path,
15750 column,
15751 &border);
15752 }
15753
15754 static void
gtk_tree_view_stop_editing(GtkTreeView * tree_view,gboolean cancel_editing)15755 gtk_tree_view_stop_editing (GtkTreeView *tree_view,
15756 gboolean cancel_editing)
15757 {
15758 GtkTreeViewColumn *column;
15759
15760 if (tree_view->priv->edited_column == NULL)
15761 return;
15762
15763 /*
15764 * This is very evil. We need to do this, because
15765 * gtk_cell_editable_editing_done may trigger gtk_tree_view_row_changed
15766 * later on. If gtk_tree_view_row_changed notices
15767 * tree_view->priv->edited_column != NULL, it'll call
15768 * gtk_tree_view_stop_editing again. Bad things will happen then.
15769 *
15770 * Please read that again if you intend to modify anything here.
15771 */
15772
15773 column = tree_view->priv->edited_column;
15774 gtk_cell_area_stop_editing (gtk_cell_layout_get_area (GTK_CELL_LAYOUT (column)), cancel_editing);
15775 tree_view->priv->edited_column = NULL;
15776 }
15777
15778
15779 /**
15780 * gtk_tree_view_set_hover_selection:
15781 * @tree_view: a #GtkTreeView
15782 * @hover: %TRUE to enable hover selection mode
15783 *
15784 * Enables or disables the hover selection mode of @tree_view.
15785 * Hover selection makes the selected row follow the pointer.
15786 * Currently, this works only for the selection modes
15787 * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
15788 *
15789 * Since: 2.6
15790 **/
15791 void
gtk_tree_view_set_hover_selection(GtkTreeView * tree_view,gboolean hover)15792 gtk_tree_view_set_hover_selection (GtkTreeView *tree_view,
15793 gboolean hover)
15794 {
15795 hover = hover != FALSE;
15796
15797 if (hover != tree_view->priv->hover_selection)
15798 {
15799 tree_view->priv->hover_selection = hover;
15800
15801 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_SELECTION]);
15802 }
15803 }
15804
15805 /**
15806 * gtk_tree_view_get_hover_selection:
15807 * @tree_view: a #GtkTreeView
15808 *
15809 * Returns whether hover selection mode is turned on for @tree_view.
15810 *
15811 * Returns: %TRUE if @tree_view is in hover selection mode
15812 *
15813 * Since: 2.6
15814 **/
15815 gboolean
gtk_tree_view_get_hover_selection(GtkTreeView * tree_view)15816 gtk_tree_view_get_hover_selection (GtkTreeView *tree_view)
15817 {
15818 return tree_view->priv->hover_selection;
15819 }
15820
15821 /**
15822 * gtk_tree_view_set_hover_expand:
15823 * @tree_view: a #GtkTreeView
15824 * @expand: %TRUE to enable hover selection mode
15825 *
15826 * Enables or disables the hover expansion mode of @tree_view.
15827 * Hover expansion makes rows expand or collapse if the pointer
15828 * moves over them.
15829 *
15830 * Since: 2.6
15831 **/
15832 void
gtk_tree_view_set_hover_expand(GtkTreeView * tree_view,gboolean expand)15833 gtk_tree_view_set_hover_expand (GtkTreeView *tree_view,
15834 gboolean expand)
15835 {
15836 expand = expand != FALSE;
15837
15838 if (expand != tree_view->priv->hover_expand)
15839 {
15840 tree_view->priv->hover_expand = expand;
15841
15842 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_HOVER_EXPAND]);
15843 }
15844 }
15845
15846 /**
15847 * gtk_tree_view_get_hover_expand:
15848 * @tree_view: a #GtkTreeView
15849 *
15850 * Returns whether hover expansion mode is turned on for @tree_view.
15851 *
15852 * Returns: %TRUE if @tree_view is in hover expansion mode
15853 *
15854 * Since: 2.6
15855 **/
15856 gboolean
gtk_tree_view_get_hover_expand(GtkTreeView * tree_view)15857 gtk_tree_view_get_hover_expand (GtkTreeView *tree_view)
15858 {
15859 return tree_view->priv->hover_expand;
15860 }
15861
15862 /**
15863 * gtk_tree_view_set_rubber_banding:
15864 * @tree_view: a #GtkTreeView
15865 * @enable: %TRUE to enable rubber banding
15866 *
15867 * Enables or disables rubber banding in @tree_view. If the selection mode
15868 * is #GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select
15869 * multiple rows by dragging the mouse.
15870 *
15871 * Since: 2.10
15872 **/
15873 void
gtk_tree_view_set_rubber_banding(GtkTreeView * tree_view,gboolean enable)15874 gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view,
15875 gboolean enable)
15876 {
15877 enable = enable != FALSE;
15878
15879 if (enable != tree_view->priv->rubber_banding_enable)
15880 {
15881 tree_view->priv->rubber_banding_enable = enable;
15882
15883 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_RUBBER_BANDING]);
15884 }
15885 }
15886
15887 /**
15888 * gtk_tree_view_get_rubber_banding:
15889 * @tree_view: a #GtkTreeView
15890 *
15891 * Returns whether rubber banding is turned on for @tree_view. If the
15892 * selection mode is #GTK_SELECTION_MULTIPLE, rubber banding will allow the
15893 * user to select multiple rows by dragging the mouse.
15894 *
15895 * Returns: %TRUE if rubber banding in @tree_view is enabled.
15896 *
15897 * Since: 2.10
15898 **/
15899 gboolean
gtk_tree_view_get_rubber_banding(GtkTreeView * tree_view)15900 gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view)
15901 {
15902 return tree_view->priv->rubber_banding_enable;
15903 }
15904
15905 /**
15906 * gtk_tree_view_is_rubber_banding_active:
15907 * @tree_view: a #GtkTreeView
15908 *
15909 * Returns whether a rubber banding operation is currently being done
15910 * in @tree_view.
15911 *
15912 * Returns: %TRUE if a rubber banding operation is currently being
15913 * done in @tree_view.
15914 *
15915 * Since: 2.12
15916 **/
15917 gboolean
gtk_tree_view_is_rubber_banding_active(GtkTreeView * tree_view)15918 gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view)
15919 {
15920 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15921
15922 if (tree_view->priv->rubber_banding_enable
15923 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
15924 return TRUE;
15925
15926 return FALSE;
15927 }
15928
15929 /**
15930 * gtk_tree_view_get_row_separator_func: (skip)
15931 * @tree_view: a #GtkTreeView
15932 *
15933 * Returns the current row separator function.
15934 *
15935 * Returns: the current row separator function.
15936 *
15937 * Since: 2.6
15938 **/
15939 GtkTreeViewRowSeparatorFunc
gtk_tree_view_get_row_separator_func(GtkTreeView * tree_view)15940 gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view)
15941 {
15942 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
15943
15944 return tree_view->priv->row_separator_func;
15945 }
15946
15947 /**
15948 * gtk_tree_view_set_row_separator_func:
15949 * @tree_view: a #GtkTreeView
15950 * @func: (allow-none): a #GtkTreeViewRowSeparatorFunc
15951 * @data: (allow-none): user data to pass to @func, or %NULL
15952 * @destroy: (allow-none): destroy notifier for @data, or %NULL
15953 *
15954 * Sets the row separator function, which is used to determine
15955 * whether a row should be drawn as a separator. If the row separator
15956 * function is %NULL, no separators are drawn. This is the default value.
15957 *
15958 * Since: 2.6
15959 **/
15960 void
gtk_tree_view_set_row_separator_func(GtkTreeView * tree_view,GtkTreeViewRowSeparatorFunc func,gpointer data,GDestroyNotify destroy)15961 gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view,
15962 GtkTreeViewRowSeparatorFunc func,
15963 gpointer data,
15964 GDestroyNotify destroy)
15965 {
15966 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15967
15968 if (tree_view->priv->row_separator_destroy)
15969 tree_view->priv->row_separator_destroy (tree_view->priv->row_separator_data);
15970
15971 tree_view->priv->row_separator_func = func;
15972 tree_view->priv->row_separator_data = data;
15973 tree_view->priv->row_separator_destroy = destroy;
15974
15975 /* Have the tree recalculate heights */
15976 _gtk_rbtree_mark_invalid (tree_view->priv->tree);
15977 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
15978 }
15979
15980 /**
15981 * gtk_tree_view_get_grid_lines:
15982 * @tree_view: a #GtkTreeView
15983 *
15984 * Returns which grid lines are enabled in @tree_view.
15985 *
15986 * Returns: a #GtkTreeViewGridLines value indicating which grid lines
15987 * are enabled.
15988 *
15989 * Since: 2.10
15990 */
15991 GtkTreeViewGridLines
gtk_tree_view_get_grid_lines(GtkTreeView * tree_view)15992 gtk_tree_view_get_grid_lines (GtkTreeView *tree_view)
15993 {
15994 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
15995
15996 return tree_view->priv->grid_lines;
15997 }
15998
15999 /**
16000 * gtk_tree_view_set_grid_lines:
16001 * @tree_view: a #GtkTreeView
16002 * @grid_lines: a #GtkTreeViewGridLines value indicating which grid lines to
16003 * enable.
16004 *
16005 * Sets which grid lines to draw in @tree_view.
16006 *
16007 * Since: 2.10
16008 */
16009 void
gtk_tree_view_set_grid_lines(GtkTreeView * tree_view,GtkTreeViewGridLines grid_lines)16010 gtk_tree_view_set_grid_lines (GtkTreeView *tree_view,
16011 GtkTreeViewGridLines grid_lines)
16012 {
16013 GtkTreeViewPrivate *priv;
16014 GtkWidget *widget;
16015 GtkTreeViewGridLines old_grid_lines;
16016
16017 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16018
16019 priv = tree_view->priv;
16020 widget = GTK_WIDGET (tree_view);
16021
16022 old_grid_lines = priv->grid_lines;
16023 priv->grid_lines = grid_lines;
16024
16025 if (gtk_widget_get_realized (widget))
16026 {
16027 if (grid_lines == GTK_TREE_VIEW_GRID_LINES_NONE &&
16028 priv->grid_line_width)
16029 {
16030 priv->grid_line_width = 0;
16031 }
16032
16033 if (grid_lines != GTK_TREE_VIEW_GRID_LINES_NONE &&
16034 !priv->grid_line_width)
16035 {
16036 gint8 *dash_list;
16037
16038 gtk_widget_style_get (widget,
16039 "grid-line-width", &priv->grid_line_width,
16040 "grid-line-pattern", (gchar *)&dash_list,
16041 NULL);
16042
16043 if (dash_list)
16044 {
16045 priv->grid_line_dashes[0] = dash_list[0];
16046 if (dash_list[0])
16047 priv->grid_line_dashes[1] = dash_list[1];
16048
16049 g_free (dash_list);
16050 }
16051 else
16052 {
16053 priv->grid_line_dashes[0] = 1;
16054 priv->grid_line_dashes[1] = 1;
16055 }
16056 }
16057 }
16058
16059 if (old_grid_lines != grid_lines)
16060 {
16061 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
16062
16063 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_GRID_LINES]);
16064 }
16065 }
16066
16067 /**
16068 * gtk_tree_view_get_enable_tree_lines:
16069 * @tree_view: a #GtkTreeView.
16070 *
16071 * Returns whether or not tree lines are drawn in @tree_view.
16072 *
16073 * Returns: %TRUE if tree lines are drawn in @tree_view, %FALSE
16074 * otherwise.
16075 *
16076 * Since: 2.10
16077 */
16078 gboolean
gtk_tree_view_get_enable_tree_lines(GtkTreeView * tree_view)16079 gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view)
16080 {
16081 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
16082
16083 return tree_view->priv->tree_lines_enabled;
16084 }
16085
16086 /**
16087 * gtk_tree_view_set_enable_tree_lines:
16088 * @tree_view: a #GtkTreeView
16089 * @enabled: %TRUE to enable tree line drawing, %FALSE otherwise.
16090 *
16091 * Sets whether to draw lines interconnecting the expanders in @tree_view.
16092 * This does not have any visible effects for lists.
16093 *
16094 * Since: 2.10
16095 */
16096 void
gtk_tree_view_set_enable_tree_lines(GtkTreeView * tree_view,gboolean enabled)16097 gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view,
16098 gboolean enabled)
16099 {
16100 GtkTreeViewPrivate *priv;
16101 GtkWidget *widget;
16102 gboolean was_enabled;
16103
16104 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16105
16106 enabled = enabled != FALSE;
16107
16108 priv = tree_view->priv;
16109 widget = GTK_WIDGET (tree_view);
16110
16111 was_enabled = priv->tree_lines_enabled;
16112
16113 priv->tree_lines_enabled = enabled;
16114
16115 if (gtk_widget_get_realized (widget))
16116 {
16117 if (!enabled && priv->tree_line_width)
16118 {
16119 priv->tree_line_width = 0;
16120 }
16121
16122 if (enabled && !priv->tree_line_width)
16123 {
16124 gint8 *dash_list;
16125 gtk_widget_style_get (widget,
16126 "tree-line-width", &priv->tree_line_width,
16127 "tree-line-pattern", (gchar *)&dash_list,
16128 NULL);
16129
16130 if (dash_list)
16131 {
16132 priv->tree_line_dashes[0] = dash_list[0];
16133 if (dash_list[0])
16134 priv->tree_line_dashes[1] = dash_list[1];
16135
16136 g_free (dash_list);
16137 }
16138 else
16139 {
16140 priv->tree_line_dashes[0] = 1;
16141 priv->tree_line_dashes[1] = 1;
16142 }
16143 }
16144 }
16145
16146 if (was_enabled != enabled)
16147 {
16148 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
16149
16150 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_ENABLE_TREE_LINES]);
16151 }
16152 }
16153
16154
16155 /**
16156 * gtk_tree_view_set_show_expanders:
16157 * @tree_view: a #GtkTreeView
16158 * @enabled: %TRUE to enable expander drawing, %FALSE otherwise.
16159 *
16160 * Sets whether to draw and enable expanders and indent child rows in
16161 * @tree_view. When disabled there will be no expanders visible in trees
16162 * and there will be no way to expand and collapse rows by default. Also
16163 * note that hiding the expanders will disable the default indentation. You
16164 * can set a custom indentation in this case using
16165 * gtk_tree_view_set_level_indentation().
16166 * This does not have any visible effects for lists.
16167 *
16168 * Since: 2.12
16169 */
16170 void
gtk_tree_view_set_show_expanders(GtkTreeView * tree_view,gboolean enabled)16171 gtk_tree_view_set_show_expanders (GtkTreeView *tree_view,
16172 gboolean enabled)
16173 {
16174 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16175
16176 enabled = enabled != FALSE;
16177 if (tree_view->priv->show_expanders != enabled)
16178 {
16179 tree_view->priv->show_expanders = enabled;
16180 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
16181 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_SHOW_EXPANDERS]);
16182 }
16183 }
16184
16185 /**
16186 * gtk_tree_view_get_show_expanders:
16187 * @tree_view: a #GtkTreeView.
16188 *
16189 * Returns whether or not expanders are drawn in @tree_view.
16190 *
16191 * Returns: %TRUE if expanders are drawn in @tree_view, %FALSE
16192 * otherwise.
16193 *
16194 * Since: 2.12
16195 */
16196 gboolean
gtk_tree_view_get_show_expanders(GtkTreeView * tree_view)16197 gtk_tree_view_get_show_expanders (GtkTreeView *tree_view)
16198 {
16199 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
16200
16201 return tree_view->priv->show_expanders;
16202 }
16203
16204 /**
16205 * gtk_tree_view_set_level_indentation:
16206 * @tree_view: a #GtkTreeView
16207 * @indentation: the amount, in pixels, of extra indentation in @tree_view.
16208 *
16209 * Sets the amount of extra indentation for child levels to use in @tree_view
16210 * in addition to the default indentation. The value should be specified in
16211 * pixels, a value of 0 disables this feature and in this case only the default
16212 * indentation will be used.
16213 * This does not have any visible effects for lists.
16214 *
16215 * Since: 2.12
16216 */
16217 void
gtk_tree_view_set_level_indentation(GtkTreeView * tree_view,gint indentation)16218 gtk_tree_view_set_level_indentation (GtkTreeView *tree_view,
16219 gint indentation)
16220 {
16221 tree_view->priv->level_indentation = indentation;
16222
16223 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
16224 }
16225
16226 /**
16227 * gtk_tree_view_get_level_indentation:
16228 * @tree_view: a #GtkTreeView.
16229 *
16230 * Returns the amount, in pixels, of extra indentation for child levels
16231 * in @tree_view.
16232 *
16233 * Returns: the amount of extra indentation for child levels in
16234 * @tree_view. A return value of 0 means that this feature is disabled.
16235 *
16236 * Since: 2.12
16237 */
16238 gint
gtk_tree_view_get_level_indentation(GtkTreeView * tree_view)16239 gtk_tree_view_get_level_indentation (GtkTreeView *tree_view)
16240 {
16241 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
16242
16243 return tree_view->priv->level_indentation;
16244 }
16245
16246 /**
16247 * gtk_tree_view_set_tooltip_row:
16248 * @tree_view: a #GtkTreeView
16249 * @tooltip: a #GtkTooltip
16250 * @path: a #GtkTreePath
16251 *
16252 * Sets the tip area of @tooltip to be the area covered by the row at @path.
16253 * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
16254 * See also gtk_tooltip_set_tip_area().
16255 *
16256 * Since: 2.12
16257 */
16258 void
gtk_tree_view_set_tooltip_row(GtkTreeView * tree_view,GtkTooltip * tooltip,GtkTreePath * path)16259 gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view,
16260 GtkTooltip *tooltip,
16261 GtkTreePath *path)
16262 {
16263 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16264 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
16265
16266 gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
16267 }
16268
16269 /**
16270 * gtk_tree_view_set_tooltip_cell:
16271 * @tree_view: a #GtkTreeView
16272 * @tooltip: a #GtkTooltip
16273 * @path: (allow-none): a #GtkTreePath or %NULL
16274 * @column: (allow-none): a #GtkTreeViewColumn or %NULL
16275 * @cell: (allow-none): a #GtkCellRenderer or %NULL
16276 *
16277 * Sets the tip area of @tooltip to the area @path, @column and @cell have
16278 * in common. For example if @path is %NULL and @column is set, the tip
16279 * area will be set to the full area covered by @column. See also
16280 * gtk_tooltip_set_tip_area().
16281 *
16282 * Note that if @path is not specified and @cell is set and part of a column
16283 * containing the expander, the tooltip might not show and hide at the correct
16284 * position. In such cases @path must be set to the current node under the
16285 * mouse cursor for this function to operate correctly.
16286 *
16287 * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
16288 *
16289 * Since: 2.12
16290 */
16291 void
gtk_tree_view_set_tooltip_cell(GtkTreeView * tree_view,GtkTooltip * tooltip,GtkTreePath * path,GtkTreeViewColumn * column,GtkCellRenderer * cell)16292 gtk_tree_view_set_tooltip_cell (GtkTreeView *tree_view,
16293 GtkTooltip *tooltip,
16294 GtkTreePath *path,
16295 GtkTreeViewColumn *column,
16296 GtkCellRenderer *cell)
16297 {
16298 GtkAllocation allocation;
16299 GdkRectangle rect;
16300
16301 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16302 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
16303 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
16304 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
16305
16306 /* Determine x values. */
16307 if (column && cell)
16308 {
16309 GdkRectangle tmp;
16310 gint start, width;
16311
16312 /* We always pass in path here, whether it is NULL or not.
16313 * For cells in expander columns path must be specified so that
16314 * we can correctly account for the indentation. This also means
16315 * that the tooltip is constrained vertically by the "Determine y
16316 * values" code below; this is not a real problem since cells actually
16317 * don't stretch vertically in constrast to columns.
16318 */
16319 gtk_tree_view_get_cell_area (tree_view, path, column, &tmp);
16320 gtk_tree_view_column_cell_get_position (column, cell, &start, &width);
16321
16322 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
16323 tmp.x + start, 0,
16324 &rect.x, NULL);
16325 rect.width = width;
16326 }
16327 else if (column)
16328 {
16329 GdkRectangle tmp;
16330
16331 gtk_tree_view_get_background_area (tree_view, NULL, column, &tmp);
16332 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
16333 tmp.x, 0,
16334 &rect.x, NULL);
16335 rect.width = tmp.width;
16336 }
16337 else
16338 {
16339 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
16340 rect.x = 0;
16341 rect.width = allocation.width;
16342 }
16343
16344 /* Determine y values. */
16345 if (path)
16346 {
16347 GdkRectangle tmp;
16348
16349 gtk_tree_view_get_background_area (tree_view, path, NULL, &tmp);
16350 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
16351 0, tmp.y,
16352 NULL, &rect.y);
16353 rect.height = tmp.height;
16354 }
16355 else
16356 {
16357 rect.y = 0;
16358 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
16359 }
16360
16361 gtk_tooltip_set_tip_area (tooltip, &rect);
16362 }
16363
16364 /**
16365 * gtk_tree_view_get_tooltip_context:
16366 * @tree_view: a #GtkTreeView
16367 * @x: (inout): the x coordinate (relative to widget coordinates)
16368 * @y: (inout): the y coordinate (relative to widget coordinates)
16369 * @keyboard_tip: whether this is a keyboard tooltip or not
16370 * @model: (out) (optional) (nullable) (transfer none): a pointer to
16371 * receive a #GtkTreeModel or %NULL
16372 * @path: (out) (optional): a pointer to receive a #GtkTreePath or %NULL
16373 * @iter: (out) (optional): a pointer to receive a #GtkTreeIter or %NULL
16374 *
16375 * This function is supposed to be used in a #GtkWidget::query-tooltip
16376 * signal handler for #GtkTreeView. The @x, @y and @keyboard_tip values
16377 * which are received in the signal handler, should be passed to this
16378 * function without modification.
16379 *
16380 * The return value indicates whether there is a tree view row at the given
16381 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
16382 * tooltips the row returned will be the cursor row. When %TRUE, then any of
16383 * @model, @path and @iter which have been provided will be set to point to
16384 * that row and the corresponding model. @x and @y will always be converted
16385 * to be relative to @tree_view’s bin_window if @keyboard_tooltip is %FALSE.
16386 *
16387 * Returns: whether or not the given tooltip context points to a row.
16388 *
16389 * Since: 2.12
16390 */
16391 gboolean
gtk_tree_view_get_tooltip_context(GtkTreeView * tree_view,gint * x,gint * y,gboolean keyboard_tip,GtkTreeModel ** model,GtkTreePath ** path,GtkTreeIter * iter)16392 gtk_tree_view_get_tooltip_context (GtkTreeView *tree_view,
16393 gint *x,
16394 gint *y,
16395 gboolean keyboard_tip,
16396 GtkTreeModel **model,
16397 GtkTreePath **path,
16398 GtkTreeIter *iter)
16399 {
16400 GtkTreePath *tmppath = NULL;
16401
16402 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
16403 g_return_val_if_fail (x != NULL, FALSE);
16404 g_return_val_if_fail (y != NULL, FALSE);
16405
16406 if (keyboard_tip)
16407 {
16408 gtk_tree_view_get_cursor (tree_view, &tmppath, NULL);
16409
16410 if (!tmppath)
16411 return FALSE;
16412 }
16413 else
16414 {
16415 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
16416 x, y);
16417
16418 if (!gtk_tree_view_get_path_at_pos (tree_view, *x, *y,
16419 &tmppath, NULL, NULL, NULL))
16420 return FALSE;
16421 }
16422
16423 if (model)
16424 *model = gtk_tree_view_get_model (tree_view);
16425
16426 if (iter)
16427 gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view),
16428 iter, tmppath);
16429
16430 if (path)
16431 *path = tmppath;
16432 else
16433 gtk_tree_path_free (tmppath);
16434
16435 return TRUE;
16436 }
16437
16438 static gboolean
gtk_tree_view_set_tooltip_query_cb(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip,gpointer data)16439 gtk_tree_view_set_tooltip_query_cb (GtkWidget *widget,
16440 gint x,
16441 gint y,
16442 gboolean keyboard_tip,
16443 GtkTooltip *tooltip,
16444 gpointer data)
16445 {
16446 GValue value = G_VALUE_INIT;
16447 GValue transformed = G_VALUE_INIT;
16448 GtkTreeIter iter;
16449 GtkTreePath *path;
16450 GtkTreeModel *model;
16451 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
16452 const char *transformed_str = NULL;
16453
16454 if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
16455 &x, &y,
16456 keyboard_tip,
16457 &model, &path, &iter))
16458 return FALSE;
16459
16460 gtk_tree_model_get_value (model, &iter,
16461 tree_view->priv->tooltip_column, &value);
16462
16463 g_value_init (&transformed, G_TYPE_STRING);
16464
16465 if (!g_value_transform (&value, &transformed))
16466 {
16467 g_value_unset (&value);
16468 gtk_tree_path_free (path);
16469
16470 return FALSE;
16471 }
16472
16473 g_value_unset (&value);
16474
16475 transformed_str = g_value_get_string (&transformed);
16476 if (transformed_str == NULL || *transformed_str == '\0')
16477 {
16478 g_value_unset (&transformed);
16479 gtk_tree_path_free (path);
16480
16481 return FALSE;
16482 }
16483
16484 gtk_tooltip_set_markup (tooltip, transformed_str);
16485 gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
16486
16487 gtk_tree_path_free (path);
16488 g_value_unset (&transformed);
16489
16490 return TRUE;
16491 }
16492
16493 /**
16494 * gtk_tree_view_set_tooltip_column:
16495 * @tree_view: a #GtkTreeView
16496 * @column: an integer, which is a valid column number for @tree_view’s model
16497 *
16498 * If you only plan to have simple (text-only) tooltips on full rows, you
16499 * can use this function to have #GtkTreeView handle these automatically
16500 * for you. @column should be set to the column in @tree_view’s model
16501 * containing the tooltip texts, or -1 to disable this feature.
16502 *
16503 * When enabled, #GtkWidget:has-tooltip will be set to %TRUE and
16504 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
16505 *
16506 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
16507 * so &, <, etc have to be escaped in the text.
16508 *
16509 * Since: 2.12
16510 */
16511 void
gtk_tree_view_set_tooltip_column(GtkTreeView * tree_view,gint column)16512 gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view,
16513 gint column)
16514 {
16515 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
16516
16517 if (column == tree_view->priv->tooltip_column)
16518 return;
16519
16520 if (column == -1)
16521 {
16522 g_signal_handlers_disconnect_by_func (tree_view,
16523 gtk_tree_view_set_tooltip_query_cb,
16524 NULL);
16525 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
16526 }
16527 else
16528 {
16529 if (tree_view->priv->tooltip_column == -1)
16530 {
16531 g_signal_connect (tree_view, "query-tooltip",
16532 G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL);
16533 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
16534 }
16535 }
16536
16537 tree_view->priv->tooltip_column = column;
16538 g_object_notify_by_pspec (G_OBJECT (tree_view), tree_view_props[PROP_TOOLTIP_COLUMN]);
16539 }
16540
16541 /**
16542 * gtk_tree_view_get_tooltip_column:
16543 * @tree_view: a #GtkTreeView
16544 *
16545 * Returns the column of @tree_view’s model which is being used for
16546 * displaying tooltips on @tree_view’s rows.
16547 *
16548 * Returns: the index of the tooltip column that is currently being
16549 * used, or -1 if this is disabled.
16550 *
16551 * Since: 2.12
16552 */
16553 gint
gtk_tree_view_get_tooltip_column(GtkTreeView * tree_view)16554 gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view)
16555 {
16556 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
16557
16558 return tree_view->priv->tooltip_column;
16559 }
16560
16561 static gboolean
gtk_tree_view_get_border(GtkScrollable * scrollable,GtkBorder * border)16562 gtk_tree_view_get_border (GtkScrollable *scrollable,
16563 GtkBorder *border)
16564 {
16565 border->top = _gtk_tree_view_get_header_height (GTK_TREE_VIEW (scrollable));
16566
16567 return TRUE;
16568 }
16569
16570 static void
gtk_tree_view_scrollable_init(GtkScrollableInterface * iface)16571 gtk_tree_view_scrollable_init (GtkScrollableInterface *iface)
16572 {
16573 iface->get_border = gtk_tree_view_get_border;
16574 }
16575