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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 
21 #include "config.h"
22 #include <string.h>
23 #include <gdk/gdkkeysyms.h>
24 
25 #include "gtktreeview.h"
26 #include "gtkrbtree.h"
27 #include "gtktreednd.h"
28 #include "gtktreeprivate.h"
29 #include "gtkcellrenderer.h"
30 #include "gtkmain.h"
31 #include "gtkmarshalers.h"
32 #include "gtkbuildable.h"
33 #include "gtkbutton.h"
34 #include "gtkalignment.h"
35 #include "gtklabel.h"
36 #include "gtkhbox.h"
37 #include "gtkvbox.h"
38 #include "gtkarrow.h"
39 #include "gtkintl.h"
40 #include "gtkbindings.h"
41 #include "gtkcontainer.h"
42 #include "gtkentry.h"
43 #include "gtkframe.h"
44 #include "gtktreemodelsort.h"
45 #include "gtktooltip.h"
46 #include "gtkprivate.h"
47 #include "gtkalias.h"
48 
49 #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
50 #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2)
51 #define GTK_TREE_VIEW_TIME_MS_PER_IDLE 30
52 #define SCROLL_EDGE_SIZE 15
53 #define EXPANDER_EXTRA_PADDING 4
54 #define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000
55 #define AUTO_EXPAND_TIMEOUT 500
56 
57 /* The "background" areas of all rows/cells add up to cover the entire tree.
58  * The background includes all inter-row and inter-cell spacing.
59  * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
60  * i.e. just the cells, no spacing.
61  */
62 
63 #define BACKGROUND_HEIGHT(node) (GTK_RBNODE_GET_HEIGHT (node))
64 #define CELL_HEIGHT(node, separator) ((BACKGROUND_HEIGHT (node)) - (separator))
65 
66 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
67  * vice versa.
68  */
69 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
70 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
71 
72 /* This is in bin_window coordinates */
73 #define BACKGROUND_FIRST_PIXEL(tree_view,tree,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, _gtk_rbtree_node_find_offset ((tree), (node))))
74 #define CELL_FIRST_PIXEL(tree_view,tree,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,tree,node) + separator/2)
75 
76 #define ROW_HEIGHT(tree_view,height) \
77   ((height > 0) ? (height) : (tree_view)->priv->expander_size)
78 
79 
80 typedef struct _GtkTreeViewChild GtkTreeViewChild;
81 struct _GtkTreeViewChild
82 {
83   GtkWidget *widget;
84   gint x;
85   gint y;
86   gint width;
87   gint height;
88 };
89 
90 
91 typedef struct _TreeViewDragInfo TreeViewDragInfo;
92 struct _TreeViewDragInfo
93 {
94   GdkModifierType start_button_mask;
95   GtkTargetList *_unused_source_target_list;
96   GdkDragAction source_actions;
97 
98   GtkTargetList *_unused_dest_target_list;
99 
100   guint source_set : 1;
101   guint dest_set : 1;
102 };
103 
104 
105 /* Signals */
106 enum
107 {
108   ROW_ACTIVATED,
109   TEST_EXPAND_ROW,
110   TEST_COLLAPSE_ROW,
111   ROW_EXPANDED,
112   ROW_COLLAPSED,
113   COLUMNS_CHANGED,
114   CURSOR_CHANGED,
115   MOVE_CURSOR,
116   SELECT_ALL,
117   UNSELECT_ALL,
118   SELECT_CURSOR_ROW,
119   TOGGLE_CURSOR_ROW,
120   EXPAND_COLLAPSE_CURSOR_ROW,
121   SELECT_CURSOR_PARENT,
122   START_INTERACTIVE_SEARCH,
123   LAST_SIGNAL
124 };
125 
126 /* Properties */
127 enum {
128   PROP_0,
129   PROP_MODEL,
130   PROP_HADJUSTMENT,
131   PROP_VADJUSTMENT,
132   PROP_HEADERS_VISIBLE,
133   PROP_HEADERS_CLICKABLE,
134   PROP_EXPANDER_COLUMN,
135   PROP_REORDERABLE,
136   PROP_RULES_HINT,
137   PROP_ENABLE_SEARCH,
138   PROP_SEARCH_COLUMN,
139   PROP_FIXED_HEIGHT_MODE,
140   PROP_HOVER_SELECTION,
141   PROP_HOVER_EXPAND,
142   PROP_SHOW_EXPANDERS,
143   PROP_LEVEL_INDENTATION,
144   PROP_RUBBER_BANDING,
145   PROP_ENABLE_GRID_LINES,
146   PROP_ENABLE_TREE_LINES,
147   PROP_TOOLTIP_COLUMN
148 };
149 
150 /* object signals */
151 static void     gtk_tree_view_finalize             (GObject          *object);
152 static void     gtk_tree_view_set_property         (GObject         *object,
153 						    guint            prop_id,
154 						    const GValue    *value,
155 						    GParamSpec      *pspec);
156 static void     gtk_tree_view_get_property         (GObject         *object,
157 						    guint            prop_id,
158 						    GValue          *value,
159 						    GParamSpec      *pspec);
160 
161 /* gtkobject signals */
162 static void     gtk_tree_view_destroy              (GtkObject        *object);
163 
164 /* gtkwidget signals */
165 static void     gtk_tree_view_realize              (GtkWidget        *widget);
166 static void     gtk_tree_view_unrealize            (GtkWidget        *widget);
167 static void     gtk_tree_view_map                  (GtkWidget        *widget);
168 static void     gtk_tree_view_size_request         (GtkWidget        *widget,
169 						    GtkRequisition   *requisition);
170 static void     gtk_tree_view_size_allocate        (GtkWidget        *widget,
171 						    GtkAllocation    *allocation);
172 static gboolean gtk_tree_view_expose               (GtkWidget        *widget,
173 						    GdkEventExpose   *event);
174 static gboolean gtk_tree_view_key_press            (GtkWidget        *widget,
175 						    GdkEventKey      *event);
176 static gboolean gtk_tree_view_key_release          (GtkWidget        *widget,
177 						    GdkEventKey      *event);
178 static gboolean gtk_tree_view_motion               (GtkWidget        *widget,
179 						    GdkEventMotion   *event);
180 static gboolean gtk_tree_view_enter_notify         (GtkWidget        *widget,
181 						    GdkEventCrossing *event);
182 static gboolean gtk_tree_view_leave_notify         (GtkWidget        *widget,
183 						    GdkEventCrossing *event);
184 static gboolean gtk_tree_view_button_press         (GtkWidget        *widget,
185 						    GdkEventButton   *event);
186 static gboolean gtk_tree_view_button_release       (GtkWidget        *widget,
187 						    GdkEventButton   *event);
188 static gboolean gtk_tree_view_grab_broken          (GtkWidget          *widget,
189 						    GdkEventGrabBroken *event);
190 #if 0
191 static gboolean gtk_tree_view_configure            (GtkWidget         *widget,
192 						    GdkEventConfigure *event);
193 #endif
194 
195 static void     gtk_tree_view_set_focus_child      (GtkContainer     *container,
196 						    GtkWidget        *child);
197 static gint     gtk_tree_view_focus_out            (GtkWidget        *widget,
198 						    GdkEventFocus    *event);
199 static gint     gtk_tree_view_focus                (GtkWidget        *widget,
200 						    GtkDirectionType  direction);
201 static void     gtk_tree_view_grab_focus           (GtkWidget        *widget);
202 static void     gtk_tree_view_style_set            (GtkWidget        *widget,
203 						    GtkStyle         *previous_style);
204 static void     gtk_tree_view_grab_notify          (GtkWidget        *widget,
205 						    gboolean          was_grabbed);
206 static void     gtk_tree_view_state_changed        (GtkWidget        *widget,
207 						    GtkStateType      previous_state);
208 
209 /* container signals */
210 static void     gtk_tree_view_remove               (GtkContainer     *container,
211 						    GtkWidget        *widget);
212 static void     gtk_tree_view_forall               (GtkContainer     *container,
213 						    gboolean          include_internals,
214 						    GtkCallback       callback,
215 						    gpointer          callback_data);
216 
217 /* Source side drag signals */
218 static void gtk_tree_view_drag_begin       (GtkWidget        *widget,
219                                             GdkDragContext   *context);
220 static void gtk_tree_view_drag_end         (GtkWidget        *widget,
221                                             GdkDragContext   *context);
222 static void gtk_tree_view_drag_data_get    (GtkWidget        *widget,
223                                             GdkDragContext   *context,
224                                             GtkSelectionData *selection_data,
225                                             guint             info,
226                                             guint             time);
227 static void gtk_tree_view_drag_data_delete (GtkWidget        *widget,
228                                             GdkDragContext   *context);
229 
230 /* Target side drag signals */
231 static void     gtk_tree_view_drag_leave         (GtkWidget        *widget,
232                                                   GdkDragContext   *context,
233                                                   guint             time);
234 static gboolean gtk_tree_view_drag_motion        (GtkWidget        *widget,
235                                                   GdkDragContext   *context,
236                                                   gint              x,
237                                                   gint              y,
238                                                   guint             time);
239 static gboolean gtk_tree_view_drag_drop          (GtkWidget        *widget,
240                                                   GdkDragContext   *context,
241                                                   gint              x,
242                                                   gint              y,
243                                                   guint             time);
244 static void     gtk_tree_view_drag_data_received (GtkWidget        *widget,
245                                                   GdkDragContext   *context,
246                                                   gint              x,
247                                                   gint              y,
248                                                   GtkSelectionData *selection_data,
249                                                   guint             info,
250                                                   guint             time);
251 
252 /* tree_model signals */
253 static void gtk_tree_view_set_adjustments                 (GtkTreeView     *tree_view,
254 							   GtkAdjustment   *hadj,
255 							   GtkAdjustment   *vadj);
256 static gboolean gtk_tree_view_real_move_cursor            (GtkTreeView     *tree_view,
257 							   GtkMovementStep  step,
258 							   gint             count);
259 static gboolean gtk_tree_view_real_select_all             (GtkTreeView     *tree_view);
260 static gboolean gtk_tree_view_real_unselect_all           (GtkTreeView     *tree_view);
261 static gboolean gtk_tree_view_real_select_cursor_row      (GtkTreeView     *tree_view,
262 							   gboolean         start_editing);
263 static gboolean gtk_tree_view_real_toggle_cursor_row      (GtkTreeView     *tree_view);
264 static gboolean gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView     *tree_view,
265 							       gboolean         logical,
266 							       gboolean         expand,
267 							       gboolean         open_all);
268 static gboolean gtk_tree_view_real_select_cursor_parent   (GtkTreeView     *tree_view);
269 static void gtk_tree_view_row_changed                     (GtkTreeModel    *model,
270 							   GtkTreePath     *path,
271 							   GtkTreeIter     *iter,
272 							   gpointer         data);
273 static void gtk_tree_view_row_inserted                    (GtkTreeModel    *model,
274 							   GtkTreePath     *path,
275 							   GtkTreeIter     *iter,
276 							   gpointer         data);
277 static void gtk_tree_view_row_has_child_toggled           (GtkTreeModel    *model,
278 							   GtkTreePath     *path,
279 							   GtkTreeIter     *iter,
280 							   gpointer         data);
281 static void gtk_tree_view_row_deleted                     (GtkTreeModel    *model,
282 							   GtkTreePath     *path,
283 							   gpointer         data);
284 static void gtk_tree_view_rows_reordered                  (GtkTreeModel    *model,
285 							   GtkTreePath     *parent,
286 							   GtkTreeIter     *iter,
287 							   gint            *new_order,
288 							   gpointer         data);
289 
290 /* Incremental reflow */
291 static gboolean validate_row             (GtkTreeView *tree_view,
292 					  GtkRBTree   *tree,
293 					  GtkRBNode   *node,
294 					  GtkTreeIter *iter,
295 					  GtkTreePath *path);
296 static void     validate_visible_area    (GtkTreeView *tree_view);
297 static gboolean validate_rows_handler    (GtkTreeView *tree_view);
298 static gboolean do_validate_rows         (GtkTreeView *tree_view,
299 					  gboolean     size_request);
300 static gboolean validate_rows            (GtkTreeView *tree_view);
301 static gboolean presize_handler_callback (gpointer     data);
302 static void     install_presize_handler  (GtkTreeView *tree_view);
303 static void     install_scroll_sync_handler (GtkTreeView *tree_view);
304 static void     gtk_tree_view_set_top_row   (GtkTreeView *tree_view,
305 					     GtkTreePath *path,
306 					     gint         offset);
307 static void	gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view);
308 static void     gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view);
309 static void     invalidate_empty_focus      (GtkTreeView *tree_view);
310 
311 /* Internal functions */
312 static gboolean gtk_tree_view_is_expander_column             (GtkTreeView        *tree_view,
313 							      GtkTreeViewColumn  *column);
314 static void     gtk_tree_view_add_move_binding               (GtkBindingSet      *binding_set,
315 							      guint               keyval,
316 							      guint               modmask,
317 							      gboolean            add_shifted_binding,
318 							      GtkMovementStep     step,
319 							      gint                count);
320 static gint     gtk_tree_view_unref_and_check_selection_tree (GtkTreeView        *tree_view,
321 							      GtkRBTree          *tree);
322 static void     gtk_tree_view_queue_draw_path                (GtkTreeView        *tree_view,
323 							      GtkTreePath        *path,
324 							      const GdkRectangle *clip_rect);
325 static void     gtk_tree_view_queue_draw_arrow               (GtkTreeView        *tree_view,
326 							      GtkRBTree          *tree,
327 							      GtkRBNode          *node,
328 							      const GdkRectangle *clip_rect);
329 static void     gtk_tree_view_draw_arrow                     (GtkTreeView        *tree_view,
330 							      GtkRBTree          *tree,
331 							      GtkRBNode          *node,
332 							      gint                x,
333 							      gint                y);
334 static void     gtk_tree_view_get_arrow_xrange               (GtkTreeView        *tree_view,
335 							      GtkRBTree          *tree,
336 							      gint               *x1,
337 							      gint               *x2);
338 static gint     gtk_tree_view_new_column_width               (GtkTreeView        *tree_view,
339 							      gint                i,
340 							      gint               *x);
341 static void     gtk_tree_view_adjustment_changed             (GtkAdjustment      *adjustment,
342 							      GtkTreeView        *tree_view);
343 static void     gtk_tree_view_build_tree                     (GtkTreeView        *tree_view,
344 							      GtkRBTree          *tree,
345 							      GtkTreeIter        *iter,
346 							      gint                depth,
347 							      gboolean            recurse);
348 static void     gtk_tree_view_clamp_node_visible             (GtkTreeView        *tree_view,
349 							      GtkRBTree          *tree,
350 							      GtkRBNode          *node);
351 static void     gtk_tree_view_clamp_column_visible           (GtkTreeView        *tree_view,
352 							      GtkTreeViewColumn  *column,
353 							      gboolean            focus_to_cell);
354 static gboolean gtk_tree_view_maybe_begin_dragging_row       (GtkTreeView        *tree_view,
355 							      GdkEventMotion     *event);
356 static void     gtk_tree_view_focus_to_cursor                (GtkTreeView        *tree_view);
357 static void     gtk_tree_view_move_cursor_up_down            (GtkTreeView        *tree_view,
358 							      gint                count);
359 static void     gtk_tree_view_move_cursor_page_up_down       (GtkTreeView        *tree_view,
360 							      gint                count);
361 static void     gtk_tree_view_move_cursor_left_right         (GtkTreeView        *tree_view,
362 							      gint                count);
363 static void     gtk_tree_view_move_cursor_start_end          (GtkTreeView        *tree_view,
364 							      gint                count);
365 static gboolean gtk_tree_view_real_collapse_row              (GtkTreeView        *tree_view,
366 							      GtkTreePath        *path,
367 							      GtkRBTree          *tree,
368 							      GtkRBNode          *node,
369 							      gboolean            animate);
370 static gboolean gtk_tree_view_real_expand_row                (GtkTreeView        *tree_view,
371 							      GtkTreePath        *path,
372 							      GtkRBTree          *tree,
373 							      GtkRBNode          *node,
374 							      gboolean            open_all,
375 							      gboolean            animate);
376 static void     gtk_tree_view_real_set_cursor                (GtkTreeView        *tree_view,
377 							      GtkTreePath        *path,
378 							      gboolean            clear_and_select,
379 							      gboolean            clamp_node);
380 static gboolean gtk_tree_view_has_special_cell               (GtkTreeView        *tree_view);
381 static void     column_sizing_notify                         (GObject            *object,
382                                                               GParamSpec         *pspec,
383                                                               gpointer            data);
384 static gboolean expand_collapse_timeout                      (gpointer            data);
385 static void     add_expand_collapse_timeout                  (GtkTreeView        *tree_view,
386                                                               GtkRBTree          *tree,
387                                                               GtkRBNode          *node,
388                                                               gboolean            expand);
389 static void     remove_expand_collapse_timeout               (GtkTreeView        *tree_view);
390 static void     cancel_arrow_animation                       (GtkTreeView        *tree_view);
391 static gboolean do_expand_collapse                           (GtkTreeView        *tree_view);
392 static void     gtk_tree_view_stop_rubber_band               (GtkTreeView        *tree_view);
393 static void     update_prelight                              (GtkTreeView        *tree_view,
394                                                               int                 x,
395                                                               int                 y);
396 
397 /* interactive search */
398 static void     gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
399 static void     gtk_tree_view_search_dialog_hide     (GtkWidget        *search_dialog,
400 							 GtkTreeView      *tree_view);
401 static void     gtk_tree_view_search_position_func      (GtkTreeView      *tree_view,
402 							 GtkWidget        *search_dialog,
403 							 gpointer          user_data);
404 static void     gtk_tree_view_search_disable_popdown    (GtkEntry         *entry,
405 							 GtkMenu          *menu,
406 							 gpointer          data);
407 static void     gtk_tree_view_search_preedit_changed    (GtkIMContext     *im_context,
408 							 GtkTreeView      *tree_view);
409 static void     gtk_tree_view_search_activate           (GtkEntry         *entry,
410 							 GtkTreeView      *tree_view);
411 static gboolean gtk_tree_view_real_search_enable_popdown(gpointer          data);
412 static void     gtk_tree_view_search_enable_popdown     (GtkWidget        *widget,
413 							 gpointer          data);
414 static gboolean gtk_tree_view_search_delete_event       (GtkWidget        *widget,
415 							 GdkEventAny      *event,
416 							 GtkTreeView      *tree_view);
417 static gboolean gtk_tree_view_search_button_press_event (GtkWidget        *widget,
418 							 GdkEventButton   *event,
419 							 GtkTreeView      *tree_view);
420 static gboolean gtk_tree_view_search_scroll_event       (GtkWidget        *entry,
421 							 GdkEventScroll   *event,
422 							 GtkTreeView      *tree_view);
423 static gboolean gtk_tree_view_search_key_press_event    (GtkWidget        *entry,
424 							 GdkEventKey      *event,
425 							 GtkTreeView      *tree_view);
426 static gboolean gtk_tree_view_search_move               (GtkWidget        *window,
427 							 GtkTreeView      *tree_view,
428 							 gboolean          up);
429 static gboolean gtk_tree_view_search_equal_func         (GtkTreeModel     *model,
430 							 gint              column,
431 							 const gchar      *key,
432 							 GtkTreeIter      *iter,
433 							 gpointer          search_data);
434 static gboolean gtk_tree_view_search_iter               (GtkTreeModel     *model,
435 							 GtkTreeSelection *selection,
436 							 GtkTreeIter      *iter,
437 							 const gchar      *text,
438 							 gint             *count,
439 							 gint              n);
440 static void     gtk_tree_view_search_init               (GtkWidget        *entry,
441 							 GtkTreeView      *tree_view);
442 static void     gtk_tree_view_put                       (GtkTreeView      *tree_view,
443 							 GtkWidget        *child_widget,
444 							 gint              x,
445 							 gint              y,
446 							 gint              width,
447 							 gint              height);
448 static gboolean gtk_tree_view_start_editing             (GtkTreeView      *tree_view,
449 							 GtkTreePath      *cursor_path);
450 static void gtk_tree_view_real_start_editing (GtkTreeView       *tree_view,
451 					      GtkTreeViewColumn *column,
452 					      GtkTreePath       *path,
453 					      GtkCellEditable   *cell_editable,
454 					      GdkRectangle      *cell_area,
455 					      GdkEvent          *event,
456 					      guint              flags);
457 static void gtk_tree_view_stop_editing                  (GtkTreeView *tree_view,
458 							 gboolean     cancel_editing);
459 static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
460 							     gboolean     keybinding);
461 static gboolean gtk_tree_view_start_interactive_search      (GtkTreeView *tree_view);
462 static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView       *tree_view,
463 							 GtkTreeViewColumn *column,
464 							 gint               drop_position);
465 
466 /* GtkBuildable */
467 static void     gtk_tree_view_buildable_add_child          (GtkBuildable      *tree_view,
468 							    GtkBuilder        *builder,
469 							    GObject           *child,
470 							    const gchar       *type);
471 static GObject *gtk_tree_view_buildable_get_internal_child (GtkBuildable      *buildable,
472 							    GtkBuilder        *builder,
473 							    const gchar       *childname);
474 static void     gtk_tree_view_buildable_init               (GtkBuildableIface *iface);
475 
476 
477 static gboolean scroll_row_timeout                   (gpointer     data);
478 static void     add_scroll_timeout                   (GtkTreeView *tree_view);
479 static void     remove_scroll_timeout                (GtkTreeView *tree_view);
480 
481 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
482 
483 
484 
485 /* GType Methods
486  */
487 
G_DEFINE_TYPE_WITH_CODE(GtkTreeView,gtk_tree_view,GTK_TYPE_CONTAINER,G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_tree_view_buildable_init))488 G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER,
489 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
490 						gtk_tree_view_buildable_init))
491 
492 static void
493 gtk_tree_view_class_init (GtkTreeViewClass *class)
494 {
495   GObjectClass *o_class;
496   GtkObjectClass *object_class;
497   GtkWidgetClass *widget_class;
498   GtkContainerClass *container_class;
499   GtkBindingSet *binding_set;
500 
501   binding_set = gtk_binding_set_by_class (class);
502 
503   o_class = (GObjectClass *) class;
504   object_class = (GtkObjectClass *) class;
505   widget_class = (GtkWidgetClass *) class;
506   container_class = (GtkContainerClass *) class;
507 
508   /* GObject signals */
509   o_class->set_property = gtk_tree_view_set_property;
510   o_class->get_property = gtk_tree_view_get_property;
511   o_class->finalize = gtk_tree_view_finalize;
512 
513   /* GtkObject signals */
514   object_class->destroy = gtk_tree_view_destroy;
515 
516   /* GtkWidget signals */
517   widget_class->map = gtk_tree_view_map;
518   widget_class->realize = gtk_tree_view_realize;
519   widget_class->unrealize = gtk_tree_view_unrealize;
520   widget_class->size_request = gtk_tree_view_size_request;
521   widget_class->size_allocate = gtk_tree_view_size_allocate;
522   widget_class->button_press_event = gtk_tree_view_button_press;
523   widget_class->button_release_event = gtk_tree_view_button_release;
524   widget_class->grab_broken_event = gtk_tree_view_grab_broken;
525   /*widget_class->configure_event = gtk_tree_view_configure;*/
526   widget_class->motion_notify_event = gtk_tree_view_motion;
527   widget_class->expose_event = gtk_tree_view_expose;
528   widget_class->key_press_event = gtk_tree_view_key_press;
529   widget_class->key_release_event = gtk_tree_view_key_release;
530   widget_class->enter_notify_event = gtk_tree_view_enter_notify;
531   widget_class->leave_notify_event = gtk_tree_view_leave_notify;
532   widget_class->focus_out_event = gtk_tree_view_focus_out;
533   widget_class->drag_begin = gtk_tree_view_drag_begin;
534   widget_class->drag_end = gtk_tree_view_drag_end;
535   widget_class->drag_data_get = gtk_tree_view_drag_data_get;
536   widget_class->drag_data_delete = gtk_tree_view_drag_data_delete;
537   widget_class->drag_leave = gtk_tree_view_drag_leave;
538   widget_class->drag_motion = gtk_tree_view_drag_motion;
539   widget_class->drag_drop = gtk_tree_view_drag_drop;
540   widget_class->drag_data_received = gtk_tree_view_drag_data_received;
541   widget_class->focus = gtk_tree_view_focus;
542   widget_class->grab_focus = gtk_tree_view_grab_focus;
543   widget_class->style_set = gtk_tree_view_style_set;
544   widget_class->grab_notify = gtk_tree_view_grab_notify;
545   widget_class->state_changed = gtk_tree_view_state_changed;
546 
547   /* GtkContainer signals */
548   container_class->remove = gtk_tree_view_remove;
549   container_class->forall = gtk_tree_view_forall;
550   container_class->set_focus_child = gtk_tree_view_set_focus_child;
551 
552   class->set_scroll_adjustments = gtk_tree_view_set_adjustments;
553   class->move_cursor = gtk_tree_view_real_move_cursor;
554   class->select_all = gtk_tree_view_real_select_all;
555   class->unselect_all = gtk_tree_view_real_unselect_all;
556   class->select_cursor_row = gtk_tree_view_real_select_cursor_row;
557   class->toggle_cursor_row = gtk_tree_view_real_toggle_cursor_row;
558   class->expand_collapse_cursor_row = gtk_tree_view_real_expand_collapse_cursor_row;
559   class->select_cursor_parent = gtk_tree_view_real_select_cursor_parent;
560   class->start_interactive_search = gtk_tree_view_start_interactive_search;
561 
562   /* Properties */
563 
564   g_object_class_install_property (o_class,
565                                    PROP_MODEL,
566                                    g_param_spec_object ("model",
567 							P_("TreeView Model"),
568 							P_("The model for the tree view"),
569 							GTK_TYPE_TREE_MODEL,
570 							GTK_PARAM_READWRITE));
571 
572   g_object_class_install_property (o_class,
573                                    PROP_HADJUSTMENT,
574                                    g_param_spec_object ("hadjustment",
575 							P_("Horizontal Adjustment"),
576                                                         P_("Horizontal Adjustment for the widget"),
577                                                         GTK_TYPE_ADJUSTMENT,
578                                                         GTK_PARAM_READWRITE));
579 
580   g_object_class_install_property (o_class,
581                                    PROP_VADJUSTMENT,
582                                    g_param_spec_object ("vadjustment",
583 							P_("Vertical Adjustment"),
584                                                         P_("Vertical Adjustment for the widget"),
585                                                         GTK_TYPE_ADJUSTMENT,
586                                                         GTK_PARAM_READWRITE));
587 
588   g_object_class_install_property (o_class,
589                                    PROP_HEADERS_VISIBLE,
590                                    g_param_spec_boolean ("headers-visible",
591 							 P_("Headers Visible"),
592 							 P_("Show the column header buttons"),
593 							 TRUE,
594 							 GTK_PARAM_READWRITE));
595 
596   g_object_class_install_property (o_class,
597                                    PROP_HEADERS_CLICKABLE,
598                                    g_param_spec_boolean ("headers-clickable",
599 							 P_("Headers Clickable"),
600 							 P_("Column headers respond to click events"),
601 							 TRUE,
602 							 GTK_PARAM_READWRITE));
603 
604   g_object_class_install_property (o_class,
605                                    PROP_EXPANDER_COLUMN,
606                                    g_param_spec_object ("expander-column",
607 							P_("Expander Column"),
608 							P_("Set the column for the expander column"),
609 							GTK_TYPE_TREE_VIEW_COLUMN,
610 							GTK_PARAM_READWRITE));
611 
612   g_object_class_install_property (o_class,
613                                    PROP_REORDERABLE,
614                                    g_param_spec_boolean ("reorderable",
615 							 P_("Reorderable"),
616 							 P_("View is reorderable"),
617 							 FALSE,
618 							 GTK_PARAM_READWRITE));
619 
620   g_object_class_install_property (o_class,
621                                    PROP_RULES_HINT,
622                                    g_param_spec_boolean ("rules-hint",
623 							 P_("Rules Hint"),
624 							 P_("Set a hint to the theme engine to draw rows in alternating colors"),
625 							 FALSE,
626 							 GTK_PARAM_READWRITE));
627 
628     g_object_class_install_property (o_class,
629 				     PROP_ENABLE_SEARCH,
630 				     g_param_spec_boolean ("enable-search",
631 							   P_("Enable Search"),
632 							   P_("View allows user to search through columns interactively"),
633 							   TRUE,
634 							   GTK_PARAM_READWRITE));
635 
636     g_object_class_install_property (o_class,
637 				     PROP_SEARCH_COLUMN,
638 				     g_param_spec_int ("search-column",
639 						       P_("Search Column"),
640 						       P_("Model column to search through during interactive search"),
641 						       -1,
642 						       G_MAXINT,
643 						       -1,
644 						       GTK_PARAM_READWRITE));
645 
646     /**
647      * GtkTreeView:fixed-height-mode:
648      *
649      * Setting the ::fixed-height-mode property to %TRUE speeds up
650      * #GtkTreeView by assuming that all rows have the same height.
651      * Only enable this option if all rows are the same height.
652      * Please see gtk_tree_view_set_fixed_height_mode() for more
653      * information on this option.
654      *
655      * Since: 2.4
656      **/
657     g_object_class_install_property (o_class,
658                                      PROP_FIXED_HEIGHT_MODE,
659                                      g_param_spec_boolean ("fixed-height-mode",
660                                                            P_("Fixed Height Mode"),
661                                                            P_("Speeds up GtkTreeView by assuming that all rows have the same height"),
662                                                            FALSE,
663                                                            GTK_PARAM_READWRITE));
664 
665     /**
666      * GtkTreeView:hover-selection:
667      *
668      * Enables of disables the hover selection mode of @tree_view.
669      * Hover selection makes the selected row follow the pointer.
670      * Currently, this works only for the selection modes
671      * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
672      *
673      * This mode is primarily intended for treeviews in popups, e.g.
674      * in #GtkComboBox or #GtkEntryCompletion.
675      *
676      * Since: 2.6
677      */
678     g_object_class_install_property (o_class,
679                                      PROP_HOVER_SELECTION,
680                                      g_param_spec_boolean ("hover-selection",
681                                                            P_("Hover Selection"),
682                                                            P_("Whether the selection should follow the pointer"),
683                                                            FALSE,
684                                                            GTK_PARAM_READWRITE));
685 
686     /**
687      * GtkTreeView:hover-expand:
688      *
689      * Enables of disables the hover expansion mode of @tree_view.
690      * Hover expansion makes rows expand or collapse if the pointer moves
691      * over them.
692      *
693      * This mode is primarily intended for treeviews in popups, e.g.
694      * in #GtkComboBox or #GtkEntryCompletion.
695      *
696      * Since: 2.6
697      */
698     g_object_class_install_property (o_class,
699                                      PROP_HOVER_EXPAND,
700                                      g_param_spec_boolean ("hover-expand",
701                                                            P_("Hover Expand"),
702                                                            P_("Whether rows should be expanded/collapsed when the pointer moves over them"),
703                                                            FALSE,
704                                                            GTK_PARAM_READWRITE));
705 
706     /**
707      * GtkTreeView:show-expanders:
708      *
709      * %TRUE if the view has expanders.
710      *
711      * Since: 2.12
712      */
713     g_object_class_install_property (o_class,
714 				     PROP_SHOW_EXPANDERS,
715 				     g_param_spec_boolean ("show-expanders",
716 							   P_("Show Expanders"),
717 							   P_("View has expanders"),
718 							   TRUE,
719 							   GTK_PARAM_READWRITE));
720 
721     /**
722      * GtkTreeView:level-indentation:
723      *
724      * Extra indentation for each level.
725      *
726      * Since: 2.12
727      */
728     g_object_class_install_property (o_class,
729 				     PROP_LEVEL_INDENTATION,
730 				     g_param_spec_int ("level-indentation",
731 						       P_("Level Indentation"),
732 						       P_("Extra indentation for each level"),
733 						       0,
734 						       G_MAXINT,
735 						       0,
736 						       GTK_PARAM_READWRITE));
737 
738     g_object_class_install_property (o_class,
739                                      PROP_RUBBER_BANDING,
740                                      g_param_spec_boolean ("rubber-banding",
741                                                            P_("Rubber Banding"),
742                                                            P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
743                                                            FALSE,
744                                                            GTK_PARAM_READWRITE));
745 
746     g_object_class_install_property (o_class,
747                                      PROP_ENABLE_GRID_LINES,
748                                      g_param_spec_enum ("enable-grid-lines",
749 							P_("Enable Grid Lines"),
750 							P_("Whether grid lines should be drawn in the tree view"),
751 							GTK_TYPE_TREE_VIEW_GRID_LINES,
752 							GTK_TREE_VIEW_GRID_LINES_NONE,
753 							GTK_PARAM_READWRITE));
754 
755     g_object_class_install_property (o_class,
756                                      PROP_ENABLE_TREE_LINES,
757                                      g_param_spec_boolean ("enable-tree-lines",
758                                                            P_("Enable Tree Lines"),
759                                                            P_("Whether tree lines should be drawn in the tree view"),
760                                                            FALSE,
761                                                            GTK_PARAM_READWRITE));
762 
763     g_object_class_install_property (o_class,
764 				     PROP_TOOLTIP_COLUMN,
765 				     g_param_spec_int ("tooltip-column",
766 						       P_("Tooltip Column"),
767 						       P_("The column in the model containing the tooltip texts for the rows"),
768 						       -1,
769 						       G_MAXINT,
770 						       -1,
771 						       GTK_PARAM_READWRITE));
772 
773   /* Style properties */
774 #define _TREE_VIEW_EXPANDER_SIZE 12
775 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
776 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
777 
778   gtk_widget_class_install_style_property (widget_class,
779 					   g_param_spec_int ("expander-size",
780 							     P_("Expander Size"),
781 							     P_("Size of the expander arrow"),
782 							     0,
783 							     G_MAXINT,
784 							     _TREE_VIEW_EXPANDER_SIZE,
785 							     GTK_PARAM_READABLE));
786 
787   gtk_widget_class_install_style_property (widget_class,
788 					   g_param_spec_int ("vertical-separator",
789 							     P_("Vertical Separator Width"),
790 							     P_("Vertical space between cells.  Must be an even number"),
791 							     0,
792 							     G_MAXINT,
793 							     _TREE_VIEW_VERTICAL_SEPARATOR,
794 							     GTK_PARAM_READABLE));
795 
796   gtk_widget_class_install_style_property (widget_class,
797 					   g_param_spec_int ("horizontal-separator",
798 							     P_("Horizontal Separator Width"),
799 							     P_("Horizontal space between cells.  Must be an even number"),
800 							     0,
801 							     G_MAXINT,
802 							     _TREE_VIEW_HORIZONTAL_SEPARATOR,
803 							     GTK_PARAM_READABLE));
804 
805   gtk_widget_class_install_style_property (widget_class,
806 					   g_param_spec_boolean ("allow-rules",
807 								 P_("Allow Rules"),
808 								 P_("Allow drawing of alternating color rows"),
809 								 TRUE,
810 								 GTK_PARAM_READABLE));
811 
812   gtk_widget_class_install_style_property (widget_class,
813 					   g_param_spec_boolean ("indent-expanders",
814 								 P_("Indent Expanders"),
815 								 P_("Make the expanders indented"),
816 								 TRUE,
817 								 GTK_PARAM_READABLE));
818 
819   gtk_widget_class_install_style_property (widget_class,
820                                            g_param_spec_boxed ("even-row-color",
821                                                                P_("Even Row Color"),
822                                                                P_("Color to use for even rows"),
823 							       GDK_TYPE_COLOR,
824 							       GTK_PARAM_READABLE));
825 
826   gtk_widget_class_install_style_property (widget_class,
827                                            g_param_spec_boxed ("odd-row-color",
828                                                                P_("Odd Row Color"),
829                                                                P_("Color to use for odd rows"),
830 							       GDK_TYPE_COLOR,
831 							       GTK_PARAM_READABLE));
832 
833   /**
834    * GtkTreeView:row-ending-details:
835    *
836    * Enable extended row background themeing
837    *
838    * Deprecated: 2.22: This style property will be removed in GTK+ 3
839    */
840   gtk_widget_class_install_style_property (widget_class,
841 					   g_param_spec_boolean ("row-ending-details",
842 								 P_("Row Ending details"),
843 								 P_("Enable extended row background theming"),
844 								 FALSE,
845 								 GTK_PARAM_READABLE));
846 
847   gtk_widget_class_install_style_property (widget_class,
848 					   g_param_spec_int ("grid-line-width",
849 							     P_("Grid line width"),
850 							     P_("Width, in pixels, of the tree view grid lines"),
851 							     0, G_MAXINT, 1,
852 							     GTK_PARAM_READABLE));
853 
854   gtk_widget_class_install_style_property (widget_class,
855 					   g_param_spec_int ("tree-line-width",
856 							     P_("Tree line width"),
857 							     P_("Width, in pixels, of the tree view lines"),
858 							     0, G_MAXINT, 1,
859 							     GTK_PARAM_READABLE));
860 
861   gtk_widget_class_install_style_property (widget_class,
862 					   g_param_spec_string ("grid-line-pattern",
863 								P_("Grid line pattern"),
864 								P_("Dash pattern used to draw the tree view grid lines"),
865 								"\1\1",
866 								GTK_PARAM_READABLE));
867 
868   gtk_widget_class_install_style_property (widget_class,
869 					   g_param_spec_string ("tree-line-pattern",
870 								P_("Tree line pattern"),
871 								P_("Dash pattern used to draw the tree view lines"),
872 								"\1\1",
873 								GTK_PARAM_READABLE));
874 
875   /* Signals */
876   /**
877    * GtkTreeView::set-scroll-adjustments
878    * @horizontal: the horizontal #GtkAdjustment
879    * @vertical: the vertical #GtkAdjustment
880    *
881    * Set the scroll adjustments for the tree view. Usually scrolled containers
882    * like #GtkScrolledWindow will emit this signal to connect two instances
883    * of #GtkScrollbar to the scroll directions of the #GtkTreeView.
884    */
885   widget_class->set_scroll_adjustments_signal =
886     g_signal_new (I_("set-scroll-adjustments"),
887 		  G_TYPE_FROM_CLASS (o_class),
888 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
889 		  G_STRUCT_OFFSET (GtkTreeViewClass, set_scroll_adjustments),
890 		  NULL, NULL,
891 		  _gtk_marshal_VOID__OBJECT_OBJECT,
892 		  G_TYPE_NONE, 2,
893 		  GTK_TYPE_ADJUSTMENT,
894 		  GTK_TYPE_ADJUSTMENT);
895 
896   /**
897    * GtkTreeView::row-activated:
898    * @tree_view: the object on which the signal is emitted
899    * @path: the #GtkTreePath for the activated row
900    * @column: the #GtkTreeViewColumn in which the activation occurred
901    *
902    * The "row-activated" signal is emitted when the method
903    * gtk_tree_view_row_activated() is called or the user double clicks
904    * a treeview row. It is also emitted when a non-editable row is
905    * selected and one of the keys: Space, Shift+Space, Return or
906    * Enter is pressed.
907    *
908    * For selection handling refer to the <link linkend="TreeWidget">tree
909    * widget conceptual overview</link> as well as #GtkTreeSelection.
910    */
911   tree_view_signals[ROW_ACTIVATED] =
912     g_signal_new (I_("row-activated"),
913 		  G_TYPE_FROM_CLASS (o_class),
914 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
915 		  G_STRUCT_OFFSET (GtkTreeViewClass, row_activated),
916 		  NULL, NULL,
917 		  _gtk_marshal_VOID__BOXED_OBJECT,
918 		  G_TYPE_NONE, 2,
919 		  GTK_TYPE_TREE_PATH,
920 		  GTK_TYPE_TREE_VIEW_COLUMN);
921 
922   /**
923    * GtkTreeView::test-expand-row:
924    * @tree_view: the object on which the signal is emitted
925    * @iter: the tree iter of the row to expand
926    * @path: a tree path that points to the row
927    *
928    * The given row is about to be expanded (show its children nodes). Use this
929    * signal if you need to control the expandability of individual rows.
930    *
931    * Returns: %FALSE to allow expansion, %TRUE to reject
932    */
933   tree_view_signals[TEST_EXPAND_ROW] =
934     g_signal_new (I_("test-expand-row"),
935 		  G_TYPE_FROM_CLASS (o_class),
936 		  G_SIGNAL_RUN_LAST,
937 		  G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row),
938 		  _gtk_boolean_handled_accumulator, NULL,
939 		  _gtk_marshal_BOOLEAN__BOXED_BOXED,
940 		  G_TYPE_BOOLEAN, 2,
941 		  GTK_TYPE_TREE_ITER,
942 		  GTK_TYPE_TREE_PATH);
943 
944   /**
945    * GtkTreeView::test-collapse-row:
946    * @tree_view: the object on which the signal is emitted
947    * @iter: the tree iter of the row to collapse
948    * @path: a tree path that points to the row
949    *
950    * The given row is about to be collapsed (hide its children nodes). Use this
951    * signal if you need to control the collapsibility of individual rows.
952    *
953    * Returns: %FALSE to allow collapsing, %TRUE to reject
954    */
955   tree_view_signals[TEST_COLLAPSE_ROW] =
956     g_signal_new (I_("test-collapse-row"),
957 		  G_TYPE_FROM_CLASS (o_class),
958 		  G_SIGNAL_RUN_LAST,
959 		  G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row),
960 		  _gtk_boolean_handled_accumulator, NULL,
961 		  _gtk_marshal_BOOLEAN__BOXED_BOXED,
962 		  G_TYPE_BOOLEAN, 2,
963 		  GTK_TYPE_TREE_ITER,
964 		  GTK_TYPE_TREE_PATH);
965 
966   /**
967    * GtkTreeView::row-expanded:
968    * @tree_view: the object on which the signal is emitted
969    * @iter: the tree iter of the expanded row
970    * @path: a tree path that points to the row
971    *
972    * The given row has been expanded (child nodes are shown).
973    */
974   tree_view_signals[ROW_EXPANDED] =
975     g_signal_new (I_("row-expanded"),
976 		  G_TYPE_FROM_CLASS (o_class),
977 		  G_SIGNAL_RUN_LAST,
978 		  G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded),
979 		  NULL, NULL,
980 		  _gtk_marshal_VOID__BOXED_BOXED,
981 		  G_TYPE_NONE, 2,
982 		  GTK_TYPE_TREE_ITER,
983 		  GTK_TYPE_TREE_PATH);
984 
985   /**
986    * GtkTreeView::row-collapsed:
987    * @tree_view: the object on which the signal is emitted
988    * @iter: the tree iter of the collapsed row
989    * @path: a tree path that points to the row
990    *
991    * The given row has been collapsed (child nodes are hidden).
992    */
993   tree_view_signals[ROW_COLLAPSED] =
994     g_signal_new (I_("row-collapsed"),
995 		  G_TYPE_FROM_CLASS (o_class),
996 		  G_SIGNAL_RUN_LAST,
997 		  G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed),
998 		  NULL, NULL,
999 		  _gtk_marshal_VOID__BOXED_BOXED,
1000 		  G_TYPE_NONE, 2,
1001 		  GTK_TYPE_TREE_ITER,
1002 		  GTK_TYPE_TREE_PATH);
1003 
1004   /**
1005    * GtkTreeView::columns-changed:
1006    * @tree_view: the object on which the signal is emitted
1007    *
1008    * The number of columns of the treeview has changed.
1009    */
1010   tree_view_signals[COLUMNS_CHANGED] =
1011     g_signal_new (I_("columns-changed"),
1012 		  G_TYPE_FROM_CLASS (o_class),
1013 		  G_SIGNAL_RUN_LAST,
1014 		  G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed),
1015 		  NULL, NULL,
1016 		  _gtk_marshal_VOID__VOID,
1017 		  G_TYPE_NONE, 0);
1018 
1019   /**
1020    * GtkTreeView::cursor-changed:
1021    * @tree_view: the object on which the signal is emitted
1022    *
1023    * The position of the cursor (focused cell) has changed.
1024    */
1025   tree_view_signals[CURSOR_CHANGED] =
1026     g_signal_new (I_("cursor-changed"),
1027 		  G_TYPE_FROM_CLASS (o_class),
1028 		  G_SIGNAL_RUN_LAST,
1029 		  G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed),
1030 		  NULL, NULL,
1031 		  _gtk_marshal_VOID__VOID,
1032 		  G_TYPE_NONE, 0);
1033 
1034   tree_view_signals[MOVE_CURSOR] =
1035     g_signal_new (I_("move-cursor"),
1036 		  G_TYPE_FROM_CLASS (object_class),
1037 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1038 		  G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor),
1039 		  NULL, NULL,
1040 		  _gtk_marshal_BOOLEAN__ENUM_INT,
1041 		  G_TYPE_BOOLEAN, 2,
1042 		  GTK_TYPE_MOVEMENT_STEP,
1043 		  G_TYPE_INT);
1044 
1045   tree_view_signals[SELECT_ALL] =
1046     g_signal_new (I_("select-all"),
1047 		  G_TYPE_FROM_CLASS (object_class),
1048 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1049 		  G_STRUCT_OFFSET (GtkTreeViewClass, select_all),
1050 		  NULL, NULL,
1051 		  _gtk_marshal_BOOLEAN__VOID,
1052 		  G_TYPE_BOOLEAN, 0);
1053 
1054   tree_view_signals[UNSELECT_ALL] =
1055     g_signal_new (I_("unselect-all"),
1056 		  G_TYPE_FROM_CLASS (object_class),
1057 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1058 		  G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all),
1059 		  NULL, NULL,
1060 		  _gtk_marshal_BOOLEAN__VOID,
1061 		  G_TYPE_BOOLEAN, 0);
1062 
1063   tree_view_signals[SELECT_CURSOR_ROW] =
1064     g_signal_new (I_("select-cursor-row"),
1065 		  G_TYPE_FROM_CLASS (object_class),
1066 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1067 		  G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row),
1068 		  NULL, NULL,
1069 		  _gtk_marshal_BOOLEAN__BOOLEAN,
1070 		  G_TYPE_BOOLEAN, 1,
1071 		  G_TYPE_BOOLEAN);
1072 
1073   tree_view_signals[TOGGLE_CURSOR_ROW] =
1074     g_signal_new (I_("toggle-cursor-row"),
1075 		  G_TYPE_FROM_CLASS (object_class),
1076 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1077 		  G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row),
1078 		  NULL, NULL,
1079 		  _gtk_marshal_BOOLEAN__VOID,
1080 		  G_TYPE_BOOLEAN, 0);
1081 
1082   tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] =
1083     g_signal_new (I_("expand-collapse-cursor-row"),
1084 		  G_TYPE_FROM_CLASS (object_class),
1085 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1086 		  G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row),
1087 		  NULL, NULL,
1088 		  _gtk_marshal_BOOLEAN__BOOLEAN_BOOLEAN_BOOLEAN,
1089 		  G_TYPE_BOOLEAN, 3,
1090 		  G_TYPE_BOOLEAN,
1091 		  G_TYPE_BOOLEAN,
1092 		  G_TYPE_BOOLEAN);
1093 
1094   tree_view_signals[SELECT_CURSOR_PARENT] =
1095     g_signal_new (I_("select-cursor-parent"),
1096 		  G_TYPE_FROM_CLASS (object_class),
1097 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1098 		  G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent),
1099 		  NULL, NULL,
1100 		  _gtk_marshal_BOOLEAN__VOID,
1101 		  G_TYPE_BOOLEAN, 0);
1102 
1103   tree_view_signals[START_INTERACTIVE_SEARCH] =
1104     g_signal_new (I_("start-interactive-search"),
1105 		  G_TYPE_FROM_CLASS (object_class),
1106 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1107 		  G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search),
1108 		  NULL, NULL,
1109 		  _gtk_marshal_BOOLEAN__VOID,
1110 		  G_TYPE_BOOLEAN, 0);
1111 
1112   /* Key bindings */
1113   gtk_tree_view_add_move_binding (binding_set, GDK_Up, 0, TRUE,
1114 				  GTK_MOVEMENT_DISPLAY_LINES, -1);
1115   gtk_tree_view_add_move_binding (binding_set, GDK_KP_Up, 0, TRUE,
1116 				  GTK_MOVEMENT_DISPLAY_LINES, -1);
1117 
1118   gtk_tree_view_add_move_binding (binding_set, GDK_Down, 0, TRUE,
1119 				  GTK_MOVEMENT_DISPLAY_LINES, 1);
1120   gtk_tree_view_add_move_binding (binding_set, GDK_KP_Down, 0, TRUE,
1121 				  GTK_MOVEMENT_DISPLAY_LINES, 1);
1122 
1123   gtk_tree_view_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, FALSE,
1124 				  GTK_MOVEMENT_DISPLAY_LINES, -1);
1125 
1126   gtk_tree_view_add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK, FALSE,
1127 				  GTK_MOVEMENT_DISPLAY_LINES, 1);
1128 
1129   gtk_tree_view_add_move_binding (binding_set, GDK_Home, 0, TRUE,
1130 				  GTK_MOVEMENT_BUFFER_ENDS, -1);
1131   gtk_tree_view_add_move_binding (binding_set, GDK_KP_Home, 0, TRUE,
1132 				  GTK_MOVEMENT_BUFFER_ENDS, -1);
1133 
1134   gtk_tree_view_add_move_binding (binding_set, GDK_End, 0, TRUE,
1135 				  GTK_MOVEMENT_BUFFER_ENDS, 1);
1136   gtk_tree_view_add_move_binding (binding_set, GDK_KP_End, 0, TRUE,
1137 				  GTK_MOVEMENT_BUFFER_ENDS, 1);
1138 
1139   gtk_tree_view_add_move_binding (binding_set, GDK_Page_Up, 0, TRUE,
1140 				  GTK_MOVEMENT_PAGES, -1);
1141   gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Up, 0, TRUE,
1142 				  GTK_MOVEMENT_PAGES, -1);
1143 
1144   gtk_tree_view_add_move_binding (binding_set, GDK_Page_Down, 0, TRUE,
1145 				  GTK_MOVEMENT_PAGES, 1);
1146   gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Down, 0, TRUE,
1147 				  GTK_MOVEMENT_PAGES, 1);
1148 
1149 
1150   gtk_binding_entry_add_signal (binding_set, GDK_Right, 0, "move-cursor", 2,
1151 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1152 				G_TYPE_INT, 1);
1153 
1154   gtk_binding_entry_add_signal (binding_set, GDK_Left, 0, "move-cursor", 2,
1155 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1156 				G_TYPE_INT, -1);
1157 
1158   gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0, "move-cursor", 2,
1159 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1160 				G_TYPE_INT, 1);
1161 
1162   gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0, "move-cursor", 2,
1163 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1164 				G_TYPE_INT, -1);
1165 
1166   gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_CONTROL_MASK,
1167                                 "move-cursor", 2,
1168 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1169 				G_TYPE_INT, 1);
1170 
1171   gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_CONTROL_MASK,
1172                                 "move-cursor", 2,
1173 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1174 				G_TYPE_INT, -1);
1175 
1176   gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
1177                                 "move-cursor", 2,
1178 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1179 				G_TYPE_INT, 1);
1180 
1181   gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
1182                                 "move-cursor", 2,
1183 				G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1184 				G_TYPE_INT, -1);
1185 
1186   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1187   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1188 
1189   gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1190   gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1191 
1192   gtk_binding_entry_add_signal (binding_set, GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1193   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1194 
1195   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1196 				G_TYPE_BOOLEAN, TRUE);
1197   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1198 				G_TYPE_BOOLEAN, TRUE);
1199 
1200   gtk_binding_entry_add_signal (binding_set, GDK_space, 0, "select-cursor-row", 1,
1201 				G_TYPE_BOOLEAN, TRUE);
1202   gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, 0, "select-cursor-row", 1,
1203 				G_TYPE_BOOLEAN, TRUE);
1204   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "select-cursor-row", 1,
1205 				G_TYPE_BOOLEAN, TRUE);
1206   gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0, "select-cursor-row", 1,
1207 				G_TYPE_BOOLEAN, TRUE);
1208   gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "select-cursor-row", 1,
1209 				G_TYPE_BOOLEAN, TRUE);
1210 
1211   /* expand and collapse rows */
1212   gtk_binding_entry_add_signal (binding_set, GDK_plus, 0, "expand-collapse-cursor-row", 3,
1213 				G_TYPE_BOOLEAN, TRUE,
1214 				G_TYPE_BOOLEAN, TRUE,
1215 				G_TYPE_BOOLEAN, FALSE);
1216 
1217   gtk_binding_entry_add_signal (binding_set, GDK_asterisk, 0,
1218                                 "expand-collapse-cursor-row", 3,
1219                                 G_TYPE_BOOLEAN, TRUE,
1220                                 G_TYPE_BOOLEAN, TRUE,
1221                                 G_TYPE_BOOLEAN, TRUE);
1222   gtk_binding_entry_add_signal (binding_set, GDK_KP_Multiply, 0,
1223                                 "expand-collapse-cursor-row", 3,
1224                                 G_TYPE_BOOLEAN, TRUE,
1225                                 G_TYPE_BOOLEAN, TRUE,
1226                                 G_TYPE_BOOLEAN, TRUE);
1227 
1228   gtk_binding_entry_add_signal (binding_set, GDK_slash, 0,
1229                                 "expand-collapse-cursor-row", 3,
1230                                 G_TYPE_BOOLEAN, TRUE,
1231                                 G_TYPE_BOOLEAN, FALSE,
1232                                 G_TYPE_BOOLEAN, FALSE);
1233   gtk_binding_entry_add_signal (binding_set, GDK_KP_Divide, 0,
1234                                 "expand-collapse-cursor-row", 3,
1235                                 G_TYPE_BOOLEAN, TRUE,
1236                                 G_TYPE_BOOLEAN, FALSE,
1237                                 G_TYPE_BOOLEAN, FALSE);
1238 
1239   /* Not doable on US keyboards */
1240   gtk_binding_entry_add_signal (binding_set, GDK_plus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1241 				G_TYPE_BOOLEAN, TRUE,
1242 				G_TYPE_BOOLEAN, TRUE,
1243 				G_TYPE_BOOLEAN, TRUE);
1244   gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, 0, "expand-collapse-cursor-row", 3,
1245 				G_TYPE_BOOLEAN, TRUE,
1246 				G_TYPE_BOOLEAN, TRUE,
1247 				G_TYPE_BOOLEAN, FALSE);
1248   gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1249 				G_TYPE_BOOLEAN, TRUE,
1250 				G_TYPE_BOOLEAN, TRUE,
1251 				G_TYPE_BOOLEAN, TRUE);
1252   gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1253 				G_TYPE_BOOLEAN, TRUE,
1254 				G_TYPE_BOOLEAN, TRUE,
1255 				G_TYPE_BOOLEAN, TRUE);
1256   gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_SHIFT_MASK,
1257                                 "expand-collapse-cursor-row", 3,
1258 				G_TYPE_BOOLEAN, FALSE,
1259 				G_TYPE_BOOLEAN, TRUE,
1260 				G_TYPE_BOOLEAN, TRUE);
1261   gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, GDK_SHIFT_MASK,
1262                                 "expand-collapse-cursor-row", 3,
1263 				G_TYPE_BOOLEAN, FALSE,
1264 				G_TYPE_BOOLEAN, TRUE,
1265 				G_TYPE_BOOLEAN, TRUE);
1266   gtk_binding_entry_add_signal (binding_set, GDK_Right,
1267                                 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1268                                 "expand-collapse-cursor-row", 3,
1269 				G_TYPE_BOOLEAN, FALSE,
1270 				G_TYPE_BOOLEAN, TRUE,
1271 				G_TYPE_BOOLEAN, TRUE);
1272   gtk_binding_entry_add_signal (binding_set, GDK_KP_Right,
1273                                 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1274                                 "expand-collapse-cursor-row", 3,
1275 				G_TYPE_BOOLEAN, FALSE,
1276 				G_TYPE_BOOLEAN, TRUE,
1277 				G_TYPE_BOOLEAN, TRUE);
1278 
1279   gtk_binding_entry_add_signal (binding_set, GDK_minus, 0, "expand-collapse-cursor-row", 3,
1280 				G_TYPE_BOOLEAN, TRUE,
1281 				G_TYPE_BOOLEAN, FALSE,
1282 				G_TYPE_BOOLEAN, FALSE);
1283   gtk_binding_entry_add_signal (binding_set, GDK_minus, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1284 				G_TYPE_BOOLEAN, TRUE,
1285 				G_TYPE_BOOLEAN, FALSE,
1286 				G_TYPE_BOOLEAN, TRUE);
1287   gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, 0, "expand-collapse-cursor-row", 3,
1288 				G_TYPE_BOOLEAN, TRUE,
1289 				G_TYPE_BOOLEAN, FALSE,
1290 				G_TYPE_BOOLEAN, FALSE);
1291   gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, GDK_SHIFT_MASK, "expand-collapse-cursor-row", 3,
1292 				G_TYPE_BOOLEAN, TRUE,
1293 				G_TYPE_BOOLEAN, FALSE,
1294 				G_TYPE_BOOLEAN, TRUE);
1295   gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_SHIFT_MASK,
1296                                 "expand-collapse-cursor-row", 3,
1297 				G_TYPE_BOOLEAN, FALSE,
1298 				G_TYPE_BOOLEAN, FALSE,
1299 				G_TYPE_BOOLEAN, TRUE);
1300   gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, GDK_SHIFT_MASK,
1301                                 "expand-collapse-cursor-row", 3,
1302 				G_TYPE_BOOLEAN, FALSE,
1303 				G_TYPE_BOOLEAN, FALSE,
1304 				G_TYPE_BOOLEAN, TRUE);
1305   gtk_binding_entry_add_signal (binding_set, GDK_Left,
1306                                 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1307                                 "expand-collapse-cursor-row", 3,
1308 				G_TYPE_BOOLEAN, FALSE,
1309 				G_TYPE_BOOLEAN, FALSE,
1310 				G_TYPE_BOOLEAN, TRUE);
1311   gtk_binding_entry_add_signal (binding_set, GDK_KP_Left,
1312                                 GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1313                                 "expand-collapse-cursor-row", 3,
1314 				G_TYPE_BOOLEAN, FALSE,
1315 				G_TYPE_BOOLEAN, FALSE,
1316 				G_TYPE_BOOLEAN, TRUE);
1317 
1318   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, "select-cursor-parent", 0);
1319   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1320 
1321   gtk_binding_entry_add_signal (binding_set, GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1322 
1323   gtk_binding_entry_add_signal (binding_set, GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1324 
1325   g_type_class_add_private (o_class, sizeof (GtkTreeViewPrivate));
1326 }
1327 
1328 static void
gtk_tree_view_init(GtkTreeView * tree_view)1329 gtk_tree_view_init (GtkTreeView *tree_view)
1330 {
1331   tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, GTK_TYPE_TREE_VIEW, GtkTreeViewPrivate);
1332 
1333   gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1334   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1335 
1336   tree_view->priv->flags =  GTK_TREE_VIEW_SHOW_EXPANDERS
1337                             | GTK_TREE_VIEW_DRAW_KEYFOCUS
1338                             | GTK_TREE_VIEW_HEADERS_VISIBLE;
1339 
1340   /* We need some padding */
1341   tree_view->priv->dy = 0;
1342   tree_view->priv->cursor_offset = 0;
1343   tree_view->priv->n_columns = 0;
1344   tree_view->priv->header_height = 1;
1345   tree_view->priv->x_drag = 0;
1346   tree_view->priv->drag_pos = -1;
1347   tree_view->priv->header_has_focus = FALSE;
1348   tree_view->priv->pressed_button = -1;
1349   tree_view->priv->press_start_x = -1;
1350   tree_view->priv->press_start_y = -1;
1351   tree_view->priv->reorderable = FALSE;
1352   tree_view->priv->presize_handler_timer = 0;
1353   tree_view->priv->scroll_sync_timer = 0;
1354   tree_view->priv->fixed_height = -1;
1355   tree_view->priv->fixed_height_mode = FALSE;
1356   tree_view->priv->fixed_height_check = 0;
1357   gtk_tree_view_set_adjustments (tree_view, NULL, NULL);
1358   tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view);
1359   tree_view->priv->enable_search = TRUE;
1360   tree_view->priv->search_column = -1;
1361   tree_view->priv->search_position_func = gtk_tree_view_search_position_func;
1362   tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func;
1363   tree_view->priv->search_custom_entry_set = FALSE;
1364   tree_view->priv->typeselect_flush_timeout = 0;
1365   tree_view->priv->init_hadjust_value = TRUE;
1366   tree_view->priv->width = 0;
1367 
1368   tree_view->priv->hover_selection = FALSE;
1369   tree_view->priv->hover_expand = FALSE;
1370 
1371   tree_view->priv->level_indentation = 0;
1372 
1373   tree_view->priv->rubber_banding_enable = FALSE;
1374 
1375   tree_view->priv->grid_lines = GTK_TREE_VIEW_GRID_LINES_NONE;
1376   tree_view->priv->tree_lines_enabled = FALSE;
1377 
1378   tree_view->priv->tooltip_column = -1;
1379 
1380   tree_view->priv->post_validation_flag = FALSE;
1381 
1382   tree_view->priv->last_button_x = -1;
1383   tree_view->priv->last_button_y = -1;
1384 
1385   tree_view->priv->event_last_x = -10000;
1386   tree_view->priv->event_last_y = -10000;
1387 }
1388 
1389 
1390 
1391 /* GObject Methods
1392  */
1393 
1394 static void
gtk_tree_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1395 gtk_tree_view_set_property (GObject         *object,
1396 			    guint            prop_id,
1397 			    const GValue    *value,
1398 			    GParamSpec      *pspec)
1399 {
1400   GtkTreeView *tree_view;
1401 
1402   tree_view = GTK_TREE_VIEW (object);
1403 
1404   switch (prop_id)
1405     {
1406     case PROP_MODEL:
1407       gtk_tree_view_set_model (tree_view, g_value_get_object (value));
1408       break;
1409     case PROP_HADJUSTMENT:
1410       gtk_tree_view_set_hadjustment (tree_view, g_value_get_object (value));
1411       break;
1412     case PROP_VADJUSTMENT:
1413       gtk_tree_view_set_vadjustment (tree_view, g_value_get_object (value));
1414       break;
1415     case PROP_HEADERS_VISIBLE:
1416       gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1417       break;
1418     case PROP_HEADERS_CLICKABLE:
1419       gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1420       break;
1421     case PROP_EXPANDER_COLUMN:
1422       gtk_tree_view_set_expander_column (tree_view, g_value_get_object (value));
1423       break;
1424     case PROP_REORDERABLE:
1425       gtk_tree_view_set_reorderable (tree_view, g_value_get_boolean (value));
1426       break;
1427     case PROP_RULES_HINT:
1428       gtk_tree_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1429       break;
1430     case PROP_ENABLE_SEARCH:
1431       gtk_tree_view_set_enable_search (tree_view, g_value_get_boolean (value));
1432       break;
1433     case PROP_SEARCH_COLUMN:
1434       gtk_tree_view_set_search_column (tree_view, g_value_get_int (value));
1435       break;
1436     case PROP_FIXED_HEIGHT_MODE:
1437       gtk_tree_view_set_fixed_height_mode (tree_view, g_value_get_boolean (value));
1438       break;
1439     case PROP_HOVER_SELECTION:
1440       tree_view->priv->hover_selection = g_value_get_boolean (value);
1441       break;
1442     case PROP_HOVER_EXPAND:
1443       tree_view->priv->hover_expand = g_value_get_boolean (value);
1444       break;
1445     case PROP_SHOW_EXPANDERS:
1446       gtk_tree_view_set_show_expanders (tree_view, g_value_get_boolean (value));
1447       break;
1448     case PROP_LEVEL_INDENTATION:
1449       tree_view->priv->level_indentation = g_value_get_int (value);
1450       break;
1451     case PROP_RUBBER_BANDING:
1452       tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1453       break;
1454     case PROP_ENABLE_GRID_LINES:
1455       gtk_tree_view_set_grid_lines (tree_view, g_value_get_enum (value));
1456       break;
1457     case PROP_ENABLE_TREE_LINES:
1458       gtk_tree_view_set_enable_tree_lines (tree_view, g_value_get_boolean (value));
1459       break;
1460     case PROP_TOOLTIP_COLUMN:
1461       gtk_tree_view_set_tooltip_column (tree_view, g_value_get_int (value));
1462       break;
1463     default:
1464       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1465       break;
1466     }
1467 }
1468 
1469 static void
gtk_tree_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1470 gtk_tree_view_get_property (GObject    *object,
1471 			    guint       prop_id,
1472 			    GValue     *value,
1473 			    GParamSpec *pspec)
1474 {
1475   GtkTreeView *tree_view;
1476 
1477   tree_view = GTK_TREE_VIEW (object);
1478 
1479   switch (prop_id)
1480     {
1481     case PROP_MODEL:
1482       g_value_set_object (value, tree_view->priv->model);
1483       break;
1484     case PROP_HADJUSTMENT:
1485       g_value_set_object (value, tree_view->priv->hadjustment);
1486       break;
1487     case PROP_VADJUSTMENT:
1488       g_value_set_object (value, tree_view->priv->vadjustment);
1489       break;
1490     case PROP_HEADERS_VISIBLE:
1491       g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view));
1492       break;
1493     case PROP_HEADERS_CLICKABLE:
1494       g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view));
1495       break;
1496     case PROP_EXPANDER_COLUMN:
1497       g_value_set_object (value, tree_view->priv->expander_column);
1498       break;
1499     case PROP_REORDERABLE:
1500       g_value_set_boolean (value, tree_view->priv->reorderable);
1501       break;
1502     case PROP_RULES_HINT:
1503       g_value_set_boolean (value, tree_view->priv->has_rules);
1504       break;
1505     case PROP_ENABLE_SEARCH:
1506       g_value_set_boolean (value, tree_view->priv->enable_search);
1507       break;
1508     case PROP_SEARCH_COLUMN:
1509       g_value_set_int (value, tree_view->priv->search_column);
1510       break;
1511     case PROP_FIXED_HEIGHT_MODE:
1512       g_value_set_boolean (value, tree_view->priv->fixed_height_mode);
1513       break;
1514     case PROP_HOVER_SELECTION:
1515       g_value_set_boolean (value, tree_view->priv->hover_selection);
1516       break;
1517     case PROP_HOVER_EXPAND:
1518       g_value_set_boolean (value, tree_view->priv->hover_expand);
1519       break;
1520     case PROP_SHOW_EXPANDERS:
1521       g_value_set_boolean (value, GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS));
1522       break;
1523     case PROP_LEVEL_INDENTATION:
1524       g_value_set_int (value, tree_view->priv->level_indentation);
1525       break;
1526     case PROP_RUBBER_BANDING:
1527       g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1528       break;
1529     case PROP_ENABLE_GRID_LINES:
1530       g_value_set_enum (value, tree_view->priv->grid_lines);
1531       break;
1532     case PROP_ENABLE_TREE_LINES:
1533       g_value_set_boolean (value, tree_view->priv->tree_lines_enabled);
1534       break;
1535     case PROP_TOOLTIP_COLUMN:
1536       g_value_set_int (value, tree_view->priv->tooltip_column);
1537       break;
1538     default:
1539       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1540       break;
1541     }
1542 }
1543 
1544 static void
gtk_tree_view_finalize(GObject * object)1545 gtk_tree_view_finalize (GObject *object)
1546 {
1547   G_OBJECT_CLASS (gtk_tree_view_parent_class)->finalize (object);
1548 }
1549 
1550 
1551 static GtkBuildableIface *parent_buildable_iface;
1552 
1553 static void
gtk_tree_view_buildable_init(GtkBuildableIface * iface)1554 gtk_tree_view_buildable_init (GtkBuildableIface *iface)
1555 {
1556   parent_buildable_iface = g_type_interface_peek_parent (iface);
1557   iface->add_child = gtk_tree_view_buildable_add_child;
1558   iface->get_internal_child = gtk_tree_view_buildable_get_internal_child;
1559 }
1560 
1561 static void
gtk_tree_view_buildable_add_child(GtkBuildable * tree_view,GtkBuilder * builder,GObject * child,const gchar * type)1562 gtk_tree_view_buildable_add_child (GtkBuildable *tree_view,
1563 				   GtkBuilder  *builder,
1564 				   GObject     *child,
1565 				   const gchar *type)
1566 {
1567   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child));
1568 }
1569 
1570 static GObject *
gtk_tree_view_buildable_get_internal_child(GtkBuildable * buildable,GtkBuilder * builder,const gchar * childname)1571 gtk_tree_view_buildable_get_internal_child (GtkBuildable      *buildable,
1572 					    GtkBuilder        *builder,
1573 					    const gchar       *childname)
1574 {
1575     if (strcmp (childname, "selection") == 0)
1576       return G_OBJECT (GTK_TREE_VIEW (buildable)->priv->selection);
1577 
1578     return parent_buildable_iface->get_internal_child (buildable,
1579 						       builder,
1580 						       childname);
1581 }
1582 
1583 /* GtkObject Methods
1584  */
1585 
1586 static void
gtk_tree_view_free_rbtree(GtkTreeView * tree_view)1587 gtk_tree_view_free_rbtree (GtkTreeView *tree_view)
1588 {
1589   _gtk_rbtree_free (tree_view->priv->tree);
1590 
1591   tree_view->priv->tree = NULL;
1592   tree_view->priv->button_pressed_node = NULL;
1593   tree_view->priv->button_pressed_tree = NULL;
1594   tree_view->priv->prelight_tree = NULL;
1595   tree_view->priv->prelight_node = NULL;
1596   tree_view->priv->expanded_collapsed_node = NULL;
1597   tree_view->priv->expanded_collapsed_tree = NULL;
1598 }
1599 
1600 static void
gtk_tree_view_destroy(GtkObject * object)1601 gtk_tree_view_destroy (GtkObject *object)
1602 {
1603   GtkTreeView *tree_view = GTK_TREE_VIEW (object);
1604   GList *list;
1605 
1606   gtk_tree_view_stop_editing (tree_view, TRUE);
1607 
1608   if (tree_view->priv->columns != NULL)
1609     {
1610       list = tree_view->priv->columns;
1611       while (list)
1612 	{
1613 	  GtkTreeViewColumn *column;
1614 	  column = GTK_TREE_VIEW_COLUMN (list->data);
1615 	  list = list->next;
1616 	  gtk_tree_view_remove_column (tree_view, column);
1617 	}
1618       tree_view->priv->columns = NULL;
1619     }
1620 
1621   if (tree_view->priv->tree != NULL)
1622     {
1623       gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree);
1624 
1625       gtk_tree_view_free_rbtree (tree_view);
1626     }
1627 
1628   if (tree_view->priv->selection != NULL)
1629     {
1630       _gtk_tree_selection_set_tree_view (tree_view->priv->selection, NULL);
1631       g_object_unref (tree_view->priv->selection);
1632       tree_view->priv->selection = NULL;
1633     }
1634 
1635   if (tree_view->priv->scroll_to_path != NULL)
1636     {
1637       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1638       tree_view->priv->scroll_to_path = NULL;
1639     }
1640 
1641   if (tree_view->priv->drag_dest_row != NULL)
1642     {
1643       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1644       tree_view->priv->drag_dest_row = NULL;
1645     }
1646 
1647   if (tree_view->priv->top_row != NULL)
1648     {
1649       gtk_tree_row_reference_free (tree_view->priv->top_row);
1650       tree_view->priv->top_row = NULL;
1651     }
1652 
1653   if (tree_view->priv->column_drop_func_data &&
1654       tree_view->priv->column_drop_func_data_destroy)
1655     {
1656       tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1657       tree_view->priv->column_drop_func_data = NULL;
1658     }
1659 
1660   if (tree_view->priv->destroy_count_destroy &&
1661       tree_view->priv->destroy_count_data)
1662     {
1663       tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1664       tree_view->priv->destroy_count_data = NULL;
1665     }
1666 
1667   gtk_tree_row_reference_free (tree_view->priv->cursor);
1668   tree_view->priv->cursor = NULL;
1669 
1670   gtk_tree_row_reference_free (tree_view->priv->anchor);
1671   tree_view->priv->anchor = NULL;
1672 
1673   /* destroy interactive search dialog */
1674   if (tree_view->priv->search_window)
1675     {
1676       gtk_widget_destroy (tree_view->priv->search_window);
1677       tree_view->priv->search_window = NULL;
1678       tree_view->priv->search_entry = NULL;
1679       if (tree_view->priv->typeselect_flush_timeout)
1680 	{
1681 	  g_source_remove (tree_view->priv->typeselect_flush_timeout);
1682 	  tree_view->priv->typeselect_flush_timeout = 0;
1683 	}
1684     }
1685 
1686   if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1687     {
1688       tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1689       tree_view->priv->search_user_data = NULL;
1690     }
1691 
1692   if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1693     {
1694       tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1695       tree_view->priv->search_position_user_data = NULL;
1696     }
1697 
1698   if (tree_view->priv->row_separator_destroy && tree_view->priv->row_separator_data)
1699     {
1700       tree_view->priv->row_separator_destroy (tree_view->priv->row_separator_data);
1701       tree_view->priv->row_separator_data = NULL;
1702     }
1703 
1704   gtk_tree_view_set_model (tree_view, NULL);
1705 
1706   if (tree_view->priv->hadjustment)
1707     {
1708       g_object_unref (tree_view->priv->hadjustment);
1709       tree_view->priv->hadjustment = NULL;
1710     }
1711   if (tree_view->priv->vadjustment)
1712     {
1713       g_object_unref (tree_view->priv->vadjustment);
1714       tree_view->priv->vadjustment = NULL;
1715     }
1716 
1717   GTK_OBJECT_CLASS (gtk_tree_view_parent_class)->destroy (object);
1718 }
1719 
1720 
1721 
1722 /* GtkWidget Methods
1723  */
1724 
1725 /* GtkWidget::map helper */
1726 static void
gtk_tree_view_map_buttons(GtkTreeView * tree_view)1727 gtk_tree_view_map_buttons (GtkTreeView *tree_view)
1728 {
1729   GList *list;
1730 
1731   g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1732 
1733   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
1734     {
1735       GtkTreeViewColumn *column;
1736 
1737       for (list = tree_view->priv->columns; list; list = list->next)
1738 	{
1739 	  column = list->data;
1740           if (gtk_widget_get_visible (column->button) &&
1741               !gtk_widget_get_mapped (column->button))
1742             gtk_widget_map (column->button);
1743 	}
1744       for (list = tree_view->priv->columns; list; list = list->next)
1745 	{
1746 	  column = list->data;
1747 	  if (column->visible == FALSE)
1748 	    continue;
1749 	  if (column->resizable)
1750 	    {
1751 	      gdk_window_raise (column->window);
1752 	      gdk_window_show (column->window);
1753 	    }
1754 	  else
1755 	    gdk_window_hide (column->window);
1756 	}
1757       gdk_window_show (tree_view->priv->header_window);
1758     }
1759 }
1760 
1761 static void
gtk_tree_view_map(GtkWidget * widget)1762 gtk_tree_view_map (GtkWidget *widget)
1763 {
1764   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1765   GList *tmp_list;
1766 
1767   gtk_widget_set_mapped (widget, TRUE);
1768 
1769   tmp_list = tree_view->priv->children;
1770   while (tmp_list)
1771     {
1772       GtkTreeViewChild *child = tmp_list->data;
1773       tmp_list = tmp_list->next;
1774 
1775       if (gtk_widget_get_visible (child->widget))
1776 	{
1777 	  if (!gtk_widget_get_mapped (child->widget))
1778 	    gtk_widget_map (child->widget);
1779 	}
1780     }
1781   gdk_window_show (tree_view->priv->bin_window);
1782 
1783   gtk_tree_view_map_buttons (tree_view);
1784 
1785   gdk_window_show (widget->window);
1786 }
1787 
1788 static void
gtk_tree_view_realize(GtkWidget * widget)1789 gtk_tree_view_realize (GtkWidget *widget)
1790 {
1791   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1792   GList *tmp_list;
1793   GdkWindowAttr attributes;
1794   gint attributes_mask;
1795 
1796   gtk_widget_set_realized (widget, TRUE);
1797 
1798   /* Make the main, clipping window */
1799   attributes.window_type = GDK_WINDOW_CHILD;
1800   attributes.x = widget->allocation.x;
1801   attributes.y = widget->allocation.y;
1802   attributes.width = widget->allocation.width;
1803   attributes.height = widget->allocation.height;
1804   attributes.wclass = GDK_INPUT_OUTPUT;
1805   attributes.visual = gtk_widget_get_visual (widget);
1806   attributes.colormap = gtk_widget_get_colormap (widget);
1807   attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1808 
1809   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1810 
1811   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1812 				   &attributes, attributes_mask);
1813   gdk_window_set_user_data (widget->window, widget);
1814 
1815   /* Make the window for the tree */
1816   attributes.x = 0;
1817   attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1818   attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1819   attributes.height = widget->allocation.height;
1820   attributes.event_mask = (GDK_EXPOSURE_MASK |
1821                            GDK_SCROLL_MASK |
1822                            GDK_POINTER_MOTION_MASK |
1823                            GDK_ENTER_NOTIFY_MASK |
1824                            GDK_LEAVE_NOTIFY_MASK |
1825                            GDK_BUTTON_PRESS_MASK |
1826                            GDK_BUTTON_RELEASE_MASK |
1827                            gtk_widget_get_events (widget));
1828 
1829   tree_view->priv->bin_window = gdk_window_new (widget->window,
1830 						&attributes, attributes_mask);
1831   gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1832 
1833   /* Make the column header window */
1834   attributes.x = 0;
1835   attributes.y = 0;
1836   attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1837   attributes.height = tree_view->priv->header_height;
1838   attributes.event_mask = (GDK_EXPOSURE_MASK |
1839                            GDK_SCROLL_MASK |
1840                            GDK_ENTER_NOTIFY_MASK |
1841                            GDK_LEAVE_NOTIFY_MASK |
1842                            GDK_BUTTON_PRESS_MASK |
1843                            GDK_BUTTON_RELEASE_MASK |
1844                            GDK_KEY_PRESS_MASK |
1845                            GDK_KEY_RELEASE_MASK |
1846                            gtk_widget_get_events (widget));
1847 
1848   tree_view->priv->header_window = gdk_window_new (widget->window,
1849 						   &attributes, attributes_mask);
1850   gdk_window_set_user_data (tree_view->priv->header_window, widget);
1851 
1852   /* Add them all up. */
1853   widget->style = gtk_style_attach (widget->style, widget->window);
1854   gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
1855   gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
1856   gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
1857 
1858   tmp_list = tree_view->priv->children;
1859   while (tmp_list)
1860     {
1861       GtkTreeViewChild *child = tmp_list->data;
1862       tmp_list = tmp_list->next;
1863 
1864       gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1865     }
1866 
1867   for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1868     _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data));
1869 
1870   /* Need to call those here, since they create GCs */
1871   gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1872   gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
1873 
1874   install_presize_handler (tree_view);
1875 }
1876 
1877 static void
gtk_tree_view_unrealize(GtkWidget * widget)1878 gtk_tree_view_unrealize (GtkWidget *widget)
1879 {
1880   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
1881   GtkTreeViewPrivate *priv = tree_view->priv;
1882   GList *list;
1883 
1884   if (priv->scroll_timeout != 0)
1885     {
1886       g_source_remove (priv->scroll_timeout);
1887       priv->scroll_timeout = 0;
1888     }
1889 
1890   if (priv->auto_expand_timeout != 0)
1891     {
1892       g_source_remove (priv->auto_expand_timeout);
1893       priv->auto_expand_timeout = 0;
1894     }
1895 
1896   if (priv->open_dest_timeout != 0)
1897     {
1898       g_source_remove (priv->open_dest_timeout);
1899       priv->open_dest_timeout = 0;
1900     }
1901 
1902   remove_expand_collapse_timeout (tree_view);
1903 
1904   if (priv->presize_handler_timer != 0)
1905     {
1906       g_source_remove (priv->presize_handler_timer);
1907       priv->presize_handler_timer = 0;
1908     }
1909 
1910   if (priv->validate_rows_timer != 0)
1911     {
1912       g_source_remove (priv->validate_rows_timer);
1913       priv->validate_rows_timer = 0;
1914     }
1915 
1916   if (priv->scroll_sync_timer != 0)
1917     {
1918       g_source_remove (priv->scroll_sync_timer);
1919       priv->scroll_sync_timer = 0;
1920     }
1921 
1922   if (priv->typeselect_flush_timeout)
1923     {
1924       g_source_remove (priv->typeselect_flush_timeout);
1925       priv->typeselect_flush_timeout = 0;
1926     }
1927 
1928   for (list = priv->columns; list; list = list->next)
1929     _gtk_tree_view_column_unrealize_button (GTK_TREE_VIEW_COLUMN (list->data));
1930 
1931   gdk_window_set_user_data (priv->bin_window, NULL);
1932   gdk_window_destroy (priv->bin_window);
1933   priv->bin_window = NULL;
1934 
1935   gdk_window_set_user_data (priv->header_window, NULL);
1936   gdk_window_destroy (priv->header_window);
1937   priv->header_window = NULL;
1938 
1939   if (priv->drag_window)
1940     {
1941       gdk_window_set_user_data (priv->drag_window, NULL);
1942       gdk_window_destroy (priv->drag_window);
1943       priv->drag_window = NULL;
1944     }
1945 
1946   if (priv->drag_highlight_window)
1947     {
1948       gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1949       gdk_window_destroy (priv->drag_highlight_window);
1950       priv->drag_highlight_window = NULL;
1951     }
1952 
1953   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->unrealize (widget);
1954 }
1955 
1956 /* GtkWidget::size_request helper */
1957 static void
gtk_tree_view_size_request_columns(GtkTreeView * tree_view)1958 gtk_tree_view_size_request_columns (GtkTreeView *tree_view)
1959 {
1960   GList *list;
1961 
1962   tree_view->priv->header_height = 0;
1963 
1964   if (tree_view->priv->model)
1965     {
1966       for (list = tree_view->priv->columns; list; list = list->next)
1967         {
1968           GtkRequisition requisition;
1969           GtkTreeViewColumn *column = list->data;
1970 
1971 	  if (column->button == NULL)
1972 	    continue;
1973 
1974           column = list->data;
1975 
1976           gtk_widget_size_request (column->button, &requisition);
1977 	  column->button_request = requisition.width;
1978           tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1979         }
1980     }
1981 }
1982 
1983 
1984 /* Called only by ::size_request */
1985 static void
gtk_tree_view_update_size(GtkTreeView * tree_view)1986 gtk_tree_view_update_size (GtkTreeView *tree_view)
1987 {
1988   GList *list;
1989   GtkTreeViewColumn *column;
1990   gint i;
1991 
1992   if (tree_view->priv->model == NULL)
1993     {
1994       tree_view->priv->width = 0;
1995       tree_view->priv->prev_width = 0;
1996       tree_view->priv->height = 0;
1997       return;
1998     }
1999 
2000   tree_view->priv->prev_width = tree_view->priv->width;
2001   tree_view->priv->width = 0;
2002 
2003   /* keep this in sync with size_allocate below */
2004   for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
2005     {
2006       gint real_requested_width = 0;
2007       column = list->data;
2008       if (!column->visible)
2009 	continue;
2010 
2011       if (column->use_resized_width)
2012 	{
2013 	  real_requested_width = column->resized_width;
2014 	}
2015       else if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
2016 	{
2017 	  real_requested_width = column->fixed_width;
2018 	}
2019       else if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
2020 	{
2021 	  real_requested_width = MAX (column->requested_width, column->button_request);
2022 	}
2023       else
2024 	{
2025 	  real_requested_width = column->requested_width;
2026 	}
2027 
2028       if (column->min_width != -1)
2029 	real_requested_width = MAX (real_requested_width, column->min_width);
2030       if (column->max_width != -1)
2031 	real_requested_width = MIN (real_requested_width, column->max_width);
2032 
2033       tree_view->priv->width += real_requested_width;
2034     }
2035 
2036   if (tree_view->priv->tree == NULL)
2037     tree_view->priv->height = 0;
2038   else
2039     tree_view->priv->height = tree_view->priv->tree->root->offset;
2040 }
2041 
2042 static void
gtk_tree_view_size_request(GtkWidget * widget,GtkRequisition * requisition)2043 gtk_tree_view_size_request (GtkWidget      *widget,
2044 			    GtkRequisition *requisition)
2045 {
2046   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2047   GList *tmp_list;
2048 
2049   /* we validate some rows initially just to make sure we have some size.
2050    * In practice, with a lot of static lists, this should get a good width.
2051    */
2052   do_validate_rows (tree_view, FALSE);
2053   gtk_tree_view_size_request_columns (tree_view);
2054   gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
2055 
2056   requisition->width = tree_view->priv->width;
2057   requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
2058 
2059   tmp_list = tree_view->priv->children;
2060 
2061   while (tmp_list)
2062     {
2063       GtkTreeViewChild *child = tmp_list->data;
2064       GtkRequisition child_requisition;
2065 
2066       tmp_list = tmp_list->next;
2067 
2068       if (gtk_widget_get_visible (child->widget))
2069         gtk_widget_size_request (child->widget, &child_requisition);
2070     }
2071 }
2072 
2073 static int
gtk_tree_view_calculate_width_before_expander(GtkTreeView * tree_view)2074 gtk_tree_view_calculate_width_before_expander (GtkTreeView *tree_view)
2075 {
2076   int width = 0;
2077   GList *list;
2078   gboolean rtl;
2079 
2080   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2081   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2082        list->data != tree_view->priv->expander_column;
2083        list = (rtl ? list->prev : list->next))
2084     {
2085       GtkTreeViewColumn *column = list->data;
2086 
2087       width += column->width;
2088     }
2089 
2090   return width;
2091 }
2092 
2093 static void
invalidate_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)2094 invalidate_column (GtkTreeView       *tree_view,
2095                    GtkTreeViewColumn *column)
2096 {
2097   gint column_offset = 0;
2098   GList *list;
2099   GtkWidget *widget = GTK_WIDGET (tree_view);
2100   gboolean rtl;
2101 
2102   if (!gtk_widget_get_realized (widget))
2103     return;
2104 
2105   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2106   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2107        list;
2108        list = (rtl ? list->prev : list->next))
2109     {
2110       GtkTreeViewColumn *tmpcolumn = list->data;
2111       if (tmpcolumn == column)
2112 	{
2113 	  GdkRectangle invalid_rect;
2114 
2115 	  invalid_rect.x = column_offset;
2116 	  invalid_rect.y = 0;
2117 	  invalid_rect.width = column->width;
2118 	  invalid_rect.height = widget->allocation.height;
2119 
2120 	  gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE);
2121 	  break;
2122 	}
2123 
2124       column_offset += tmpcolumn->width;
2125     }
2126 }
2127 
2128 static void
invalidate_last_column(GtkTreeView * tree_view)2129 invalidate_last_column (GtkTreeView *tree_view)
2130 {
2131   GList *last_column;
2132   gboolean rtl;
2133 
2134   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2135 
2136   for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
2137        last_column;
2138        last_column = (rtl ? last_column->next : last_column->prev))
2139     {
2140       if (GTK_TREE_VIEW_COLUMN (last_column->data)->visible)
2141         {
2142           invalidate_column (tree_view, last_column->data);
2143           return;
2144         }
2145     }
2146 }
2147 
2148 static gint
gtk_tree_view_get_real_requested_width_from_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)2149 gtk_tree_view_get_real_requested_width_from_column (GtkTreeView       *tree_view,
2150                                                     GtkTreeViewColumn *column)
2151 {
2152   gint real_requested_width;
2153 
2154   if (column->use_resized_width)
2155     {
2156       real_requested_width = column->resized_width;
2157     }
2158   else if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
2159     {
2160       real_requested_width = column->fixed_width;
2161     }
2162   else if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
2163     {
2164       real_requested_width = MAX (column->requested_width, column->button_request);
2165     }
2166   else
2167     {
2168       real_requested_width = column->requested_width;
2169       if (real_requested_width < 0)
2170         real_requested_width = 0;
2171     }
2172 
2173   if (column->min_width != -1)
2174     real_requested_width = MAX (real_requested_width, column->min_width);
2175   if (column->max_width != -1)
2176     real_requested_width = MIN (real_requested_width, column->max_width);
2177 
2178   return real_requested_width;
2179 }
2180 
2181 /* GtkWidget::size_allocate helper */
2182 static void
gtk_tree_view_size_allocate_columns(GtkWidget * widget,gboolean * width_changed)2183 gtk_tree_view_size_allocate_columns (GtkWidget *widget,
2184 				     gboolean  *width_changed)
2185 {
2186   GtkTreeView *tree_view;
2187   GList *list, *first_column, *last_column;
2188   GtkTreeViewColumn *column;
2189   GtkAllocation allocation;
2190   gint width = 0;
2191   gint extra, extra_per_column, extra_for_last;
2192   gint full_requested_width = 0;
2193   gint number_of_expand_columns = 0;
2194   gboolean column_changed = FALSE;
2195   gboolean rtl;
2196   gboolean update_expand;
2197 
2198   tree_view = GTK_TREE_VIEW (widget);
2199 
2200   for (last_column = g_list_last (tree_view->priv->columns);
2201        last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible);
2202        last_column = last_column->prev)
2203     ;
2204   if (last_column == NULL)
2205     return;
2206 
2207   for (first_column = g_list_first (tree_view->priv->columns);
2208        first_column && !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible);
2209        first_column = first_column->next)
2210     ;
2211 
2212   allocation.y = 0;
2213   allocation.height = tree_view->priv->header_height;
2214 
2215   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2216 
2217   /* find out how many extra space and expandable columns we have */
2218   for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
2219     {
2220       column = (GtkTreeViewColumn *)list->data;
2221 
2222       if (!column->visible)
2223 	continue;
2224 
2225       full_requested_width += gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
2226 
2227       if (column->expand)
2228 	number_of_expand_columns++;
2229     }
2230 
2231   /* Only update the expand value if the width of the widget has changed,
2232    * or the number of expand columns has changed, or if there are no expand
2233    * columns, or if we didn't have an size-allocation yet after the
2234    * last validated node.
2235    */
2236   update_expand = (width_changed && *width_changed == TRUE)
2237       || number_of_expand_columns != tree_view->priv->last_number_of_expand_columns
2238       || number_of_expand_columns == 0
2239       || tree_view->priv->post_validation_flag == TRUE;
2240 
2241   tree_view->priv->post_validation_flag = FALSE;
2242 
2243   if (!update_expand)
2244     {
2245       extra = tree_view->priv->last_extra_space;
2246       extra_for_last = MAX (widget->allocation.width - full_requested_width - extra, 0);
2247     }
2248   else
2249     {
2250       extra = MAX (widget->allocation.width - full_requested_width, 0);
2251       extra_for_last = 0;
2252 
2253       tree_view->priv->last_extra_space = extra;
2254     }
2255 
2256   if (number_of_expand_columns > 0)
2257     extra_per_column = extra/number_of_expand_columns;
2258   else
2259     extra_per_column = 0;
2260 
2261   if (update_expand)
2262     {
2263       tree_view->priv->last_extra_space_per_column = extra_per_column;
2264       tree_view->priv->last_number_of_expand_columns = number_of_expand_columns;
2265     }
2266 
2267   for (list = (rtl ? last_column : first_column);
2268        list != (rtl ? first_column->prev : last_column->next);
2269        list = (rtl ? list->prev : list->next))
2270     {
2271       gint real_requested_width = 0;
2272       gint old_width;
2273 
2274       column = list->data;
2275       old_width = column->width;
2276 
2277       if (!column->visible)
2278 	continue;
2279 
2280       /* We need to handle the dragged button specially.
2281        */
2282       if (column == tree_view->priv->drag_column)
2283 	{
2284 	  GtkAllocation drag_allocation;
2285           drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
2286           drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
2287 	  drag_allocation.x = 0;
2288 	  drag_allocation.y = 0;
2289 	  gtk_widget_size_allocate (tree_view->priv->drag_column->button,
2290 				    &drag_allocation);
2291 	  width += drag_allocation.width;
2292 	  continue;
2293 	}
2294 
2295       real_requested_width = gtk_tree_view_get_real_requested_width_from_column (tree_view, column);
2296 
2297       allocation.x = width;
2298       column->width = real_requested_width;
2299 
2300       if (column->expand)
2301 	{
2302 	  if (number_of_expand_columns == 1)
2303 	    {
2304 	      /* We add the remander to the last column as
2305 	       * */
2306 	      column->width += extra;
2307 	    }
2308 	  else
2309 	    {
2310 	      column->width += extra_per_column;
2311 	      extra -= extra_per_column;
2312 	      number_of_expand_columns --;
2313 	    }
2314 	}
2315       else if (number_of_expand_columns == 0 &&
2316 	       list == last_column)
2317 	{
2318 	  column->width += extra;
2319 	}
2320 
2321       /* In addition to expand, the last column can get even more
2322        * extra space so all available space is filled up.
2323        */
2324       if (extra_for_last > 0 && list == last_column)
2325 	column->width += extra_for_last;
2326 
2327       g_object_notify (G_OBJECT (column), "width");
2328 
2329       allocation.width = column->width;
2330       width += column->width;
2331 
2332       if (column->width > old_width)
2333         column_changed = TRUE;
2334 
2335       gtk_widget_size_allocate (column->button, &allocation);
2336 
2337       if (column->window)
2338 	gdk_window_move_resize (column->window,
2339                                 allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
2340 				allocation.y,
2341                                 TREE_VIEW_DRAG_WIDTH, allocation.height);
2342     }
2343 
2344   /* We change the width here.  The user might have been resizing columns,
2345    * so the total width of the tree view changes.
2346    */
2347   tree_view->priv->width = width;
2348   if (width_changed)
2349     *width_changed = TRUE;
2350 
2351   if (column_changed)
2352     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2353 }
2354 
2355 
2356 static void
gtk_tree_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)2357 gtk_tree_view_size_allocate (GtkWidget     *widget,
2358 			     GtkAllocation *allocation)
2359 {
2360   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2361   GList *tmp_list;
2362   gboolean width_changed = FALSE;
2363   gint old_width = widget->allocation.width;
2364 
2365   if (allocation->width != widget->allocation.width)
2366     width_changed = TRUE;
2367 
2368   widget->allocation = *allocation;
2369 
2370   tmp_list = tree_view->priv->children;
2371 
2372   while (tmp_list)
2373     {
2374       GtkAllocation allocation;
2375 
2376       GtkTreeViewChild *child = tmp_list->data;
2377       tmp_list = tmp_list->next;
2378 
2379       /* totally ignore our child's requisition */
2380       allocation.x = child->x;
2381       allocation.y = child->y;
2382       allocation.width = child->width;
2383       allocation.height = child->height;
2384       gtk_widget_size_allocate (child->widget, &allocation);
2385     }
2386 
2387   /* We size-allocate the columns first because the width of the
2388    * tree view (used in updating the adjustments below) might change.
2389    */
2390   gtk_tree_view_size_allocate_columns (widget, &width_changed);
2391 
2392   tree_view->priv->hadjustment->page_size = allocation->width;
2393   tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
2394   tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
2395   tree_view->priv->hadjustment->lower = 0;
2396   tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width);
2397 
2398   if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2399     {
2400       if (allocation->width < tree_view->priv->width)
2401         {
2402 	  if (tree_view->priv->init_hadjust_value)
2403 	    {
2404 	      tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2405 	      tree_view->priv->init_hadjust_value = FALSE;
2406 	    }
2407 	  else if (allocation->width != old_width)
2408 	    {
2409 	      tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
2410 	    }
2411 	  else
2412 	    tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width);
2413 	}
2414       else
2415         {
2416 	  tree_view->priv->hadjustment->value = 0;
2417 	  tree_view->priv->init_hadjust_value = TRUE;
2418 	}
2419     }
2420   else
2421     if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
2422       tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2423 
2424   gtk_adjustment_changed (tree_view->priv->hadjustment);
2425 
2426   tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
2427   tree_view->priv->vadjustment->step_increment = tree_view->priv->vadjustment->page_size * 0.1;
2428   tree_view->priv->vadjustment->page_increment = tree_view->priv->vadjustment->page_size * 0.9;
2429   tree_view->priv->vadjustment->lower = 0;
2430   tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
2431 
2432   gtk_adjustment_changed (tree_view->priv->vadjustment);
2433 
2434   /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2435   if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
2436     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2437   else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
2438     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2439                               tree_view->priv->height - tree_view->priv->vadjustment->page_size);
2440   else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2441     gtk_tree_view_top_row_to_dy (tree_view);
2442   else
2443     gtk_tree_view_dy_to_top_row (tree_view);
2444 
2445   if (gtk_widget_get_realized (widget))
2446     {
2447       gdk_window_move_resize (widget->window,
2448 			      allocation->x, allocation->y,
2449 			      allocation->width, allocation->height);
2450       gdk_window_move_resize (tree_view->priv->header_window,
2451 			      - (gint) tree_view->priv->hadjustment->value,
2452 			      0,
2453 			      MAX (tree_view->priv->width, allocation->width),
2454 			      tree_view->priv->header_height);
2455       gdk_window_move_resize (tree_view->priv->bin_window,
2456 			      - (gint) tree_view->priv->hadjustment->value,
2457 			      TREE_VIEW_HEADER_HEIGHT (tree_view),
2458 			      MAX (tree_view->priv->width, allocation->width),
2459 			      allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2460     }
2461 
2462   if (tree_view->priv->tree == NULL)
2463     invalidate_empty_focus (tree_view);
2464 
2465   if (gtk_widget_get_realized (widget))
2466     {
2467       gboolean has_expand_column = FALSE;
2468       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2469 	{
2470 	  if (gtk_tree_view_column_get_expand (GTK_TREE_VIEW_COLUMN (tmp_list->data)))
2471 	    {
2472 	      has_expand_column = TRUE;
2473 	      break;
2474 	    }
2475 	}
2476 
2477       if (width_changed && tree_view->priv->expander_column)
2478         {
2479           /* Might seem awkward, but is the best heuristic I could come up
2480            * with.  Only if the width of the columns before the expander
2481            * changes, we will update the prelight status.  It is this
2482            * width that makes the expander move vertically.  Always updating
2483            * prelight status causes trouble with hover selections.
2484            */
2485           gint width_before_expander;
2486 
2487           width_before_expander = gtk_tree_view_calculate_width_before_expander (tree_view);
2488 
2489           if (tree_view->priv->prev_width_before_expander
2490               != width_before_expander)
2491               update_prelight (tree_view,
2492                                tree_view->priv->event_last_x,
2493                                tree_view->priv->event_last_y);
2494 
2495           tree_view->priv->prev_width_before_expander = width_before_expander;
2496         }
2497 
2498       /* This little hack only works if we have an LTR locale, and no column has the  */
2499       if (width_changed)
2500 	{
2501 	  if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2502 	      ! has_expand_column)
2503 	    invalidate_last_column (tree_view);
2504 	  else
2505 	    gtk_widget_queue_draw (widget);
2506 	}
2507     }
2508 }
2509 
2510 /* Grabs the focus and unsets the GTK_TREE_VIEW_DRAW_KEYFOCUS flag */
2511 static void
grab_focus_and_unset_draw_keyfocus(GtkTreeView * tree_view)2512 grab_focus_and_unset_draw_keyfocus (GtkTreeView *tree_view)
2513 {
2514   GtkWidget *widget = GTK_WIDGET (tree_view);
2515 
2516   if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2517     gtk_widget_grab_focus (widget);
2518   GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
2519 }
2520 
2521 static inline gboolean
row_is_separator(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)2522 row_is_separator (GtkTreeView *tree_view,
2523 		  GtkTreeIter *iter,
2524 		  GtkTreePath *path)
2525 {
2526   gboolean is_separator = FALSE;
2527 
2528   if (tree_view->priv->row_separator_func)
2529     {
2530       GtkTreeIter tmpiter;
2531 
2532       if (iter)
2533         tmpiter = *iter;
2534       else
2535         {
2536           if (!gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, path))
2537             return FALSE;
2538         }
2539 
2540       is_separator = tree_view->priv->row_separator_func (tree_view->priv->model,
2541                                                           &tmpiter,
2542                                                           tree_view->priv->row_separator_data);
2543     }
2544 
2545   return is_separator;
2546 }
2547 
2548 static gboolean
gtk_tree_view_button_press(GtkWidget * widget,GdkEventButton * event)2549 gtk_tree_view_button_press (GtkWidget      *widget,
2550 			    GdkEventButton *event)
2551 {
2552   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
2553   GList *list;
2554   GtkTreeViewColumn *column = NULL;
2555   gint i;
2556   GdkRectangle background_area;
2557   GdkRectangle cell_area;
2558   gint vertical_separator;
2559   gint horizontal_separator;
2560   gboolean path_is_selectable;
2561   gboolean rtl;
2562 
2563   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2564   gtk_tree_view_stop_editing (tree_view, FALSE);
2565   gtk_widget_style_get (widget,
2566 			"vertical-separator", &vertical_separator,
2567 			"horizontal-separator", &horizontal_separator,
2568 			NULL);
2569 
2570 
2571   /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2572    * we're done handling the button press.
2573    */
2574 
2575   if (event->window == tree_view->priv->bin_window)
2576     {
2577       GtkRBNode *node;
2578       GtkRBTree *tree;
2579       GtkTreePath *path;
2580       gchar *path_string;
2581       gint depth;
2582       gint new_y;
2583       gint y_offset;
2584       gint dval;
2585       gint pre_val, aft_val;
2586       GtkTreeViewColumn *column = NULL;
2587       GtkCellRenderer *focus_cell = NULL;
2588       gint column_handled_click = FALSE;
2589       gboolean row_double_click = FALSE;
2590       gboolean rtl;
2591       gboolean node_selected;
2592 
2593       /* Empty tree? */
2594       if (tree_view->priv->tree == NULL)
2595 	{
2596 	  grab_focus_and_unset_draw_keyfocus (tree_view);
2597 	  return TRUE;
2598 	}
2599 
2600       /* are we in an arrow? */
2601       if (tree_view->priv->prelight_node &&
2602           GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT) &&
2603 	  TREE_VIEW_DRAW_EXPANDERS (tree_view))
2604 	{
2605 	  if (event->button == 1)
2606 	    {
2607 	      gtk_grab_add (widget);
2608 	      tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
2609 	      tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
2610 	      gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
2611 					tree_view->priv->prelight_tree,
2612 					tree_view->priv->prelight_node,
2613 					event->x,
2614 					event->y);
2615 	    }
2616 
2617 	  grab_focus_and_unset_draw_keyfocus (tree_view);
2618 	  return TRUE;
2619 	}
2620 
2621       /* find the node that was clicked */
2622       new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
2623       if (new_y < 0)
2624 	new_y = 0;
2625       y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
2626 
2627       if (node == NULL)
2628 	{
2629 	  /* We clicked in dead space */
2630 	  grab_focus_and_unset_draw_keyfocus (tree_view);
2631 	  return TRUE;
2632 	}
2633 
2634       /* Get the path and the node */
2635       path = _gtk_tree_view_find_path (tree_view, tree, node);
2636       path_is_selectable = !row_is_separator (tree_view, NULL, path);
2637 
2638       if (!path_is_selectable)
2639 	{
2640 	  gtk_tree_path_free (path);
2641 	  grab_focus_and_unset_draw_keyfocus (tree_view);
2642 	  return TRUE;
2643 	}
2644 
2645       depth = gtk_tree_path_get_depth (path);
2646       background_area.y = y_offset + event->y;
2647       background_area.height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
2648       background_area.x = 0;
2649 
2650 
2651       /* Let the column have a chance at selecting it. */
2652       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2653       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2654 	   list; list = (rtl ? list->prev : list->next))
2655 	{
2656 	  GtkTreeViewColumn *candidate = list->data;
2657 
2658 	  if (!candidate->visible)
2659 	    continue;
2660 
2661 	  background_area.width = candidate->width;
2662 	  if ((background_area.x > (gint) event->x) ||
2663 	      (background_area.x + background_area.width <= (gint) event->x))
2664 	    {
2665 	      background_area.x += background_area.width;
2666 	      continue;
2667 	    }
2668 
2669 	  /* we found the focus column */
2670 	  column = candidate;
2671 	  cell_area = background_area;
2672 	  cell_area.width -= horizontal_separator;
2673 	  cell_area.height -= vertical_separator;
2674 	  cell_area.x += horizontal_separator/2;
2675 	  cell_area.y += vertical_separator/2;
2676 	  if (gtk_tree_view_is_expander_column (tree_view, column))
2677 	    {
2678 	      if (!rtl)
2679 		cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
2680 	      cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
2681 
2682               if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
2683 	        {
2684 		  if (!rtl)
2685 		    cell_area.x += depth * tree_view->priv->expander_size;
2686 	          cell_area.width -= depth * tree_view->priv->expander_size;
2687 		}
2688 	    }
2689 	  break;
2690 	}
2691 
2692       if (column == NULL)
2693 	{
2694 	  gtk_tree_path_free (path);
2695 	  grab_focus_and_unset_draw_keyfocus (tree_view);
2696 	  return FALSE;
2697 	}
2698 
2699       tree_view->priv->focus_column = column;
2700 
2701       /* decide if we edit */
2702       if (event->type == GDK_BUTTON_PRESS && event->button == 1 &&
2703 	  !(event->state & gtk_accelerator_get_default_mod_mask ()))
2704 	{
2705 	  GtkTreePath *anchor;
2706 	  GtkTreeIter iter;
2707 
2708 	  gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2709 	  gtk_tree_view_column_cell_set_cell_data (column,
2710 						   tree_view->priv->model,
2711 						   &iter,
2712 						   GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
2713 						   node->children?TRUE:FALSE);
2714 
2715 	  if (tree_view->priv->anchor)
2716 	    anchor = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
2717 	  else
2718 	    anchor = NULL;
2719 
2720 	  if ((anchor && !gtk_tree_path_compare (anchor, path))
2721 	      || !_gtk_tree_view_column_has_editable_cell (column))
2722 	    {
2723 	      GtkCellEditable *cell_editable = NULL;
2724 
2725 	      /* FIXME: get the right flags */
2726 	      guint flags = 0;
2727 
2728 	      path_string = gtk_tree_path_to_string (path);
2729 
2730 	      if (_gtk_tree_view_column_cell_event (column,
2731 						    &cell_editable,
2732 						    (GdkEvent *)event,
2733 						    path_string,
2734 						    &background_area,
2735 						    &cell_area, flags))
2736 		{
2737 		  if (cell_editable != NULL)
2738 		    {
2739 		      gint left, right;
2740 		      GdkRectangle area;
2741 
2742 		      area = cell_area;
2743 		      _gtk_tree_view_column_get_neighbor_sizes (column,	_gtk_tree_view_column_get_edited_cell (column), &left, &right);
2744 
2745 		      area.x += left;
2746 		      area.width -= right + left;
2747 
2748 		      gtk_tree_view_real_start_editing (tree_view,
2749 							column,
2750 							path,
2751 							cell_editable,
2752 							&area,
2753 							(GdkEvent *)event,
2754 							flags);
2755 		      g_free (path_string);
2756 		      gtk_tree_path_free (path);
2757 		      gtk_tree_path_free (anchor);
2758 		      return TRUE;
2759 		    }
2760 		  column_handled_click = TRUE;
2761 		}
2762 	      g_free (path_string);
2763 	    }
2764 	  if (anchor)
2765 	    gtk_tree_path_free (anchor);
2766 	}
2767 
2768       /* select */
2769       node_selected = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
2770       pre_val = tree_view->priv->vadjustment->value;
2771 
2772       /* we only handle selection modifications on the first button press
2773        */
2774       if (event->type == GDK_BUTTON_PRESS)
2775         {
2776           if ((event->state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
2777             tree_view->priv->modify_selection_pressed = TRUE;
2778           if ((event->state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
2779             tree_view->priv->extend_selection_pressed = TRUE;
2780 
2781           focus_cell = _gtk_tree_view_column_get_cell_at_pos (column, event->x - background_area.x);
2782           if (focus_cell)
2783             gtk_tree_view_column_focus_cell (column, focus_cell);
2784 
2785           if (event->state & GTK_MODIFY_SELECTION_MOD_MASK)
2786             {
2787               gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
2788               gtk_tree_view_real_toggle_cursor_row (tree_view);
2789             }
2790           else if (event->state & GTK_EXTEND_SELECTION_MOD_MASK)
2791             {
2792               gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
2793               gtk_tree_view_real_select_cursor_row (tree_view, FALSE);
2794             }
2795           else
2796             {
2797               gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2798             }
2799 
2800           tree_view->priv->modify_selection_pressed = FALSE;
2801           tree_view->priv->extend_selection_pressed = FALSE;
2802         }
2803 
2804       /* the treeview may have been scrolled because of _set_cursor,
2805        * correct here
2806        */
2807 
2808       aft_val = tree_view->priv->vadjustment->value;
2809       dval = pre_val - aft_val;
2810 
2811       cell_area.y += dval;
2812       background_area.y += dval;
2813 
2814       /* Save press to possibly begin a drag
2815        */
2816       if (!column_handled_click &&
2817 	  !tree_view->priv->in_grab &&
2818 	  tree_view->priv->pressed_button < 0)
2819         {
2820           tree_view->priv->pressed_button = event->button;
2821           tree_view->priv->press_start_x = event->x;
2822           tree_view->priv->press_start_y = event->y;
2823 
2824 	  if (tree_view->priv->rubber_banding_enable
2825 	      && !node_selected
2826 	      && tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE)
2827 	    {
2828 	      tree_view->priv->press_start_y += tree_view->priv->dy;
2829 	      tree_view->priv->rubber_band_x = event->x;
2830 	      tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2831 	      tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2832 
2833 	      if ((event->state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
2834 		tree_view->priv->rubber_band_modify = TRUE;
2835 	      if ((event->state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
2836 		tree_view->priv->rubber_band_extend = TRUE;
2837 	    }
2838         }
2839 
2840       /* Test if a double click happened on the same row. */
2841       if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2842         {
2843           int double_click_time, double_click_distance;
2844 
2845           g_object_get (gtk_settings_get_default (),
2846                         "gtk-double-click-time", &double_click_time,
2847                         "gtk-double-click-distance", &double_click_distance,
2848                         NULL);
2849 
2850           /* Same conditions as _gdk_event_button_generate */
2851           if (tree_view->priv->last_button_x != -1 &&
2852               (event->time < tree_view->priv->last_button_time + double_click_time) &&
2853               (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2854               (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2855             {
2856               /* We do no longer compare paths of this row and the
2857                * row clicked previously.  We use the double click
2858                * distance to decide whether this is a valid click,
2859                * allowing the mouse to slightly move over another row.
2860                */
2861               row_double_click = TRUE;
2862 
2863               tree_view->priv->last_button_time = 0;
2864               tree_view->priv->last_button_x = -1;
2865               tree_view->priv->last_button_y = -1;
2866             }
2867           else
2868             {
2869               tree_view->priv->last_button_time = event->time;
2870               tree_view->priv->last_button_x = event->x;
2871               tree_view->priv->last_button_y = event->y;
2872             }
2873         }
2874 
2875       if (row_double_click)
2876 	{
2877 	  gtk_grab_remove (widget);
2878 	  gtk_tree_view_row_activated (tree_view, path, column);
2879 
2880           if (tree_view->priv->pressed_button == event->button)
2881             tree_view->priv->pressed_button = -1;
2882 	}
2883 
2884       gtk_tree_path_free (path);
2885 
2886       /* If we activated the row through a double click we don't want to grab
2887        * focus back, as moving focus to another widget is pretty common.
2888        */
2889       if (!row_double_click)
2890 	grab_focus_and_unset_draw_keyfocus (tree_view);
2891 
2892       return TRUE;
2893     }
2894 
2895   /* We didn't click in the window.  Let's check to see if we clicked on a column resize window.
2896    */
2897   for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2898     {
2899       column = list->data;
2900       if (event->window == column->window &&
2901 	  column->resizable &&
2902 	  column->window)
2903 	{
2904 	  gpointer drag_data;
2905 
2906 	  if (event->type == GDK_2BUTTON_PRESS &&
2907 	      gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE)
2908 	    {
2909 	      column->use_resized_width = FALSE;
2910 	      _gtk_tree_view_column_autosize (tree_view, column);
2911 	      return TRUE;
2912 	    }
2913 
2914 	  if (gdk_pointer_grab (column->window, FALSE,
2915 				GDK_POINTER_MOTION_HINT_MASK |
2916 				GDK_BUTTON1_MOTION_MASK |
2917 				GDK_BUTTON_RELEASE_MASK,
2918 				NULL, NULL, event->time))
2919 	    return FALSE;
2920 
2921 	  gtk_grab_add (widget);
2922 	  GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
2923 	  column->resized_width = column->width - tree_view->priv->last_extra_space_per_column;
2924 
2925 	  /* block attached dnd signal handler */
2926 	  drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2927 	  if (drag_data)
2928 	    g_signal_handlers_block_matched (widget,
2929 					     G_SIGNAL_MATCH_DATA,
2930 					     0, 0, NULL, NULL,
2931 					     drag_data);
2932 
2933 	  tree_view->priv->drag_pos = i;
2934 	  tree_view->priv->x_drag = column->button->allocation.x + (rtl ? 0 : column->button->allocation.width);
2935 
2936 	  if (!gtk_widget_has_focus (widget))
2937 	    gtk_widget_grab_focus (widget);
2938 
2939 	  return TRUE;
2940 	}
2941     }
2942   return FALSE;
2943 }
2944 
2945 /* GtkWidget::button_release_event helper */
2946 static gboolean
gtk_tree_view_button_release_drag_column(GtkWidget * widget,GdkEventButton * event)2947 gtk_tree_view_button_release_drag_column (GtkWidget      *widget,
2948 					  GdkEventButton *event)
2949 {
2950   GtkTreeView *tree_view;
2951   GList *l;
2952   gboolean rtl;
2953 
2954   tree_view = GTK_TREE_VIEW (widget);
2955 
2956   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2957   gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2958   gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2959 
2960   /* Move the button back */
2961   g_object_ref (tree_view->priv->drag_column->button);
2962   gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2963   gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2964   gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2965   g_object_unref (tree_view->priv->drag_column->button);
2966   gtk_widget_queue_resize (widget);
2967   if (tree_view->priv->drag_column->resizable)
2968     {
2969       gdk_window_raise (tree_view->priv->drag_column->window);
2970       gdk_window_show (tree_view->priv->drag_column->window);
2971     }
2972   else
2973     gdk_window_hide (tree_view->priv->drag_column->window);
2974 
2975   gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2976 
2977   if (rtl)
2978     {
2979       if (tree_view->priv->cur_reorder &&
2980 	  tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2981 	gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column,
2982 					 tree_view->priv->cur_reorder->right_column);
2983     }
2984   else
2985     {
2986       if (tree_view->priv->cur_reorder &&
2987 	  tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2988 	gtk_tree_view_move_column_after (tree_view, tree_view->priv->drag_column,
2989 					 tree_view->priv->cur_reorder->left_column);
2990     }
2991   tree_view->priv->drag_column = NULL;
2992   gdk_window_hide (tree_view->priv->drag_window);
2993 
2994   for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2995     g_slice_free (GtkTreeViewColumnReorder, l->data);
2996   g_list_free (tree_view->priv->column_drag_info);
2997   tree_view->priv->column_drag_info = NULL;
2998   tree_view->priv->cur_reorder = NULL;
2999 
3000   if (tree_view->priv->drag_highlight_window)
3001     gdk_window_hide (tree_view->priv->drag_highlight_window);
3002 
3003   /* Reset our flags */
3004   tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
3005   GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG);
3006 
3007   return TRUE;
3008 }
3009 
3010 /* GtkWidget::button_release_event helper */
3011 static gboolean
gtk_tree_view_button_release_column_resize(GtkWidget * widget,GdkEventButton * event)3012 gtk_tree_view_button_release_column_resize (GtkWidget      *widget,
3013 					    GdkEventButton *event)
3014 {
3015   GtkTreeView *tree_view;
3016   gpointer drag_data;
3017 
3018   tree_view = GTK_TREE_VIEW (widget);
3019 
3020   tree_view->priv->drag_pos = -1;
3021 
3022   /* unblock attached dnd signal handler */
3023   drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
3024   if (drag_data)
3025     g_signal_handlers_unblock_matched (widget,
3026 				       G_SIGNAL_MATCH_DATA,
3027 				       0, 0, NULL, NULL,
3028 				       drag_data);
3029 
3030   GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
3031   gtk_grab_remove (widget);
3032   gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
3033 			      event->time);
3034   return TRUE;
3035 }
3036 
3037 static gboolean
gtk_tree_view_button_release(GtkWidget * widget,GdkEventButton * event)3038 gtk_tree_view_button_release (GtkWidget      *widget,
3039 			      GdkEventButton *event)
3040 {
3041   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
3042 
3043   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
3044     return gtk_tree_view_button_release_drag_column (widget, event);
3045 
3046   if (tree_view->priv->rubber_band_status)
3047     gtk_tree_view_stop_rubber_band (tree_view);
3048 
3049   if (tree_view->priv->pressed_button == event->button)
3050     tree_view->priv->pressed_button = -1;
3051 
3052   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
3053     return gtk_tree_view_button_release_column_resize (widget, event);
3054 
3055   if (tree_view->priv->button_pressed_node == NULL)
3056     return FALSE;
3057 
3058   if (event->button == 1)
3059     {
3060       if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node &&
3061           GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
3062 	{
3063 	  GtkTreePath *path = NULL;
3064 
3065 	  path = _gtk_tree_view_find_path (tree_view,
3066 					   tree_view->priv->button_pressed_tree,
3067 					   tree_view->priv->button_pressed_node);
3068 	  /* Actually activate the node */
3069 	  if (tree_view->priv->button_pressed_node->children == NULL)
3070 	    gtk_tree_view_real_expand_row (tree_view, path,
3071 					   tree_view->priv->button_pressed_tree,
3072 					   tree_view->priv->button_pressed_node,
3073 					   FALSE, TRUE);
3074 	  else
3075 	    gtk_tree_view_real_collapse_row (GTK_TREE_VIEW (widget), path,
3076 					     tree_view->priv->button_pressed_tree,
3077 					     tree_view->priv->button_pressed_node, TRUE);
3078 	  gtk_tree_path_free (path);
3079 	}
3080 
3081       tree_view->priv->button_pressed_tree = NULL;
3082       tree_view->priv->button_pressed_node = NULL;
3083 
3084       gtk_grab_remove (widget);
3085     }
3086 
3087   return TRUE;
3088 }
3089 
3090 static gboolean
gtk_tree_view_grab_broken(GtkWidget * widget,GdkEventGrabBroken * event)3091 gtk_tree_view_grab_broken (GtkWidget          *widget,
3092 			   GdkEventGrabBroken *event)
3093 {
3094   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
3095 
3096   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
3097     gtk_tree_view_button_release_drag_column (widget, (GdkEventButton *)event);
3098 
3099   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
3100     gtk_tree_view_button_release_column_resize (widget, (GdkEventButton *)event);
3101 
3102   return TRUE;
3103 }
3104 
3105 #if 0
3106 static gboolean
3107 gtk_tree_view_configure (GtkWidget *widget,
3108 			 GdkEventConfigure *event)
3109 {
3110   GtkTreeView *tree_view;
3111 
3112   tree_view = GTK_TREE_VIEW (widget);
3113   tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window);
3114 
3115   return FALSE;
3116 }
3117 #endif
3118 
3119 /* GtkWidget::motion_event function set.
3120  */
3121 
3122 static gboolean
coords_are_over_arrow(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)3123 coords_are_over_arrow (GtkTreeView *tree_view,
3124                        GtkRBTree   *tree,
3125                        GtkRBNode   *node,
3126                        /* these are in bin window coords */
3127                        gint         x,
3128                        gint         y)
3129 {
3130   GdkRectangle arrow;
3131   gint x2;
3132 
3133   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
3134     return FALSE;
3135 
3136   if ((node->flags & GTK_RBNODE_IS_PARENT) == 0)
3137     return FALSE;
3138 
3139   arrow.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
3140 
3141   arrow.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
3142 
3143   gtk_tree_view_get_arrow_xrange (tree_view, tree, &arrow.x, &x2);
3144 
3145   arrow.width = x2 - arrow.x;
3146 
3147   return (x >= arrow.x &&
3148           x < (arrow.x + arrow.width) &&
3149 	  y >= arrow.y &&
3150 	  y < (arrow.y + arrow.height));
3151 }
3152 
3153 static gboolean
auto_expand_timeout(gpointer data)3154 auto_expand_timeout (gpointer data)
3155 {
3156   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
3157   GtkTreePath *path;
3158 
3159   if (tree_view->priv->prelight_node)
3160     {
3161       path = _gtk_tree_view_find_path (tree_view,
3162 				       tree_view->priv->prelight_tree,
3163 				       tree_view->priv->prelight_node);
3164 
3165       if (tree_view->priv->prelight_node->children)
3166 	gtk_tree_view_collapse_row (tree_view, path);
3167       else
3168 	gtk_tree_view_expand_row (tree_view, path, FALSE);
3169 
3170       gtk_tree_path_free (path);
3171     }
3172 
3173   tree_view->priv->auto_expand_timeout = 0;
3174 
3175   return FALSE;
3176 }
3177 
3178 static void
remove_auto_expand_timeout(GtkTreeView * tree_view)3179 remove_auto_expand_timeout (GtkTreeView *tree_view)
3180 {
3181   if (tree_view->priv->auto_expand_timeout != 0)
3182     {
3183       g_source_remove (tree_view->priv->auto_expand_timeout);
3184       tree_view->priv->auto_expand_timeout = 0;
3185     }
3186 }
3187 
3188 static void
do_prelight(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)3189 do_prelight (GtkTreeView *tree_view,
3190              GtkRBTree   *tree,
3191              GtkRBNode   *node,
3192 	     /* these are in bin_window coords */
3193              gint         x,
3194              gint         y)
3195 {
3196   if (tree_view->priv->prelight_tree == tree &&
3197       tree_view->priv->prelight_node == node)
3198     {
3199       /*  We are still on the same node,
3200 	  but we might need to take care of the arrow  */
3201 
3202       if (tree && node && TREE_VIEW_DRAW_EXPANDERS (tree_view))
3203 	{
3204 	  gboolean over_arrow;
3205 	  gboolean flag_set;
3206 
3207 	  over_arrow = coords_are_over_arrow (tree_view, tree, node, x, y);
3208 	  flag_set = GTK_TREE_VIEW_FLAG_SET (tree_view,
3209 					     GTK_TREE_VIEW_ARROW_PRELIT);
3210 
3211 	  if (over_arrow != flag_set)
3212 	    {
3213 	      if (over_arrow)
3214 		GTK_TREE_VIEW_SET_FLAG (tree_view,
3215 					GTK_TREE_VIEW_ARROW_PRELIT);
3216 	      else
3217 		GTK_TREE_VIEW_UNSET_FLAG (tree_view,
3218 					  GTK_TREE_VIEW_ARROW_PRELIT);
3219 
3220 	      gtk_tree_view_draw_arrow (tree_view, tree, node, x, y);
3221 	    }
3222 	}
3223 
3224       return;
3225     }
3226 
3227   if (tree_view->priv->prelight_tree && tree_view->priv->prelight_node)
3228     {
3229       /*  Unprelight the old node and arrow  */
3230 
3231       GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node,
3232 			     GTK_RBNODE_IS_PRELIT);
3233 
3234       if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)
3235 	  && TREE_VIEW_DRAW_EXPANDERS (tree_view))
3236 	{
3237 	  GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
3238 
3239 	  gtk_tree_view_draw_arrow (tree_view,
3240 				    tree_view->priv->prelight_tree,
3241 				    tree_view->priv->prelight_node,
3242 				    x,
3243 				    y);
3244 	}
3245 
3246       _gtk_tree_view_queue_draw_node (tree_view,
3247 				      tree_view->priv->prelight_tree,
3248 				      tree_view->priv->prelight_node,
3249 				      NULL);
3250     }
3251 
3252 
3253   if (tree_view->priv->hover_expand)
3254     remove_auto_expand_timeout (tree_view);
3255 
3256   /*  Set the new prelight values  */
3257   tree_view->priv->prelight_node = node;
3258   tree_view->priv->prelight_tree = tree;
3259 
3260   if (!node || !tree)
3261     return;
3262 
3263   /*  Prelight the new node and arrow  */
3264 
3265   if (TREE_VIEW_DRAW_EXPANDERS (tree_view)
3266       && coords_are_over_arrow (tree_view, tree, node, x, y))
3267     {
3268       GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
3269 
3270       gtk_tree_view_draw_arrow (tree_view, tree, node, x, y);
3271     }
3272 
3273   GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
3274 
3275   _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
3276 
3277   if (tree_view->priv->hover_expand)
3278     {
3279       tree_view->priv->auto_expand_timeout =
3280 	gdk_threads_add_timeout (AUTO_EXPAND_TIMEOUT, auto_expand_timeout, tree_view);
3281     }
3282 }
3283 
3284 static void
prelight_or_select(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)3285 prelight_or_select (GtkTreeView *tree_view,
3286 		    GtkRBTree   *tree,
3287 		    GtkRBNode   *node,
3288 		    /* these are in bin_window coords */
3289 		    gint         x,
3290 		    gint         y)
3291 {
3292   GtkSelectionMode mode = gtk_tree_selection_get_mode (tree_view->priv->selection);
3293 
3294   if (tree_view->priv->hover_selection &&
3295       (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) &&
3296       !(tree_view->priv->edited_column &&
3297 	tree_view->priv->edited_column->editable_widget))
3298     {
3299       if (node)
3300 	{
3301 	  if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
3302 	    {
3303 	      GtkTreePath *path;
3304 
3305 	      path = _gtk_tree_view_find_path (tree_view, tree, node);
3306 	      gtk_tree_selection_select_path (tree_view->priv->selection, path);
3307 	      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
3308 		{
3309 		  GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
3310 		  gtk_tree_view_real_set_cursor (tree_view, path, FALSE, FALSE);
3311 		}
3312 	      gtk_tree_path_free (path);
3313 	    }
3314 	}
3315 
3316       else if (mode == GTK_SELECTION_SINGLE)
3317 	gtk_tree_selection_unselect_all (tree_view->priv->selection);
3318     }
3319 
3320     do_prelight (tree_view, tree, node, x, y);
3321 }
3322 
3323 static void
ensure_unprelighted(GtkTreeView * tree_view)3324 ensure_unprelighted (GtkTreeView *tree_view)
3325 {
3326   do_prelight (tree_view,
3327 	       NULL, NULL,
3328 	       -1000, -1000); /* coords not possibly over an arrow */
3329 
3330   g_assert (tree_view->priv->prelight_node == NULL);
3331 }
3332 
3333 static void
update_prelight(GtkTreeView * tree_view,gint x,gint y)3334 update_prelight (GtkTreeView *tree_view,
3335                  gint         x,
3336                  gint         y)
3337 {
3338   int new_y;
3339   GtkRBTree *tree;
3340   GtkRBNode *node;
3341 
3342   if (tree_view->priv->tree == NULL)
3343     return;
3344 
3345   if (x == -10000)
3346     {
3347       ensure_unprelighted (tree_view);
3348       return;
3349     }
3350 
3351   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
3352   if (new_y < 0)
3353     new_y = 0;
3354 
3355   _gtk_rbtree_find_offset (tree_view->priv->tree,
3356                            new_y, &tree, &node);
3357 
3358   if (node)
3359     prelight_or_select (tree_view, tree, node, x, y);
3360 }
3361 
3362 
3363 
3364 
3365 /* Our motion arrow is either a box (in the case of the original spot)
3366  * or an arrow.  It is expander_size wide.
3367  */
3368 /*
3369  * 11111111111111
3370  * 01111111111110
3371  * 00111111111100
3372  * 00011111111000
3373  * 00001111110000
3374  * 00000111100000
3375  * 00000111100000
3376  * 00000111100000
3377  * ~ ~ ~ ~ ~ ~ ~
3378  * 00000111100000
3379  * 00000111100000
3380  * 00000111100000
3381  * 00001111110000
3382  * 00011111111000
3383  * 00111111111100
3384  * 01111111111110
3385  * 11111111111111
3386  */
3387 
3388 static void
gtk_tree_view_motion_draw_column_motion_arrow(GtkTreeView * tree_view)3389 gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view)
3390 {
3391   GtkTreeViewColumnReorder *reorder = tree_view->priv->cur_reorder;
3392   GtkWidget *widget = GTK_WIDGET (tree_view);
3393   GdkBitmap *mask = NULL;
3394   gint x;
3395   gint y;
3396   gint width;
3397   gint height;
3398   gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
3399   GdkWindowAttr attributes;
3400   guint attributes_mask;
3401   cairo_t *cr;
3402 
3403   if (!reorder ||
3404       reorder->left_column == tree_view->priv->drag_column ||
3405       reorder->right_column == tree_view->priv->drag_column)
3406     arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3407   else if (reorder->left_column || reorder->right_column)
3408     {
3409       GdkRectangle visible_rect;
3410       gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
3411       if (reorder->left_column)
3412 	x = reorder->left_column->button->allocation.x + reorder->left_column->button->allocation.width;
3413       else
3414 	x = reorder->right_column->button->allocation.x;
3415 
3416       if (x < visible_rect.x)
3417 	arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
3418       else if (x > visible_rect.x + visible_rect.width)
3419 	arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
3420       else
3421         arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
3422     }
3423 
3424   /* We want to draw the rectangle over the initial location. */
3425   if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3426     {
3427       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3428 	{
3429 	  if (tree_view->priv->drag_highlight_window)
3430 	    {
3431 	      gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3432 					NULL);
3433 	      gdk_window_destroy (tree_view->priv->drag_highlight_window);
3434 	    }
3435 
3436 	  attributes.window_type = GDK_WINDOW_CHILD;
3437 	  attributes.wclass = GDK_INPUT_OUTPUT;
3438           attributes.x = tree_view->priv->drag_column_x;
3439           attributes.y = 0;
3440 	  width = attributes.width = tree_view->priv->drag_column->button->allocation.width;
3441 	  height = attributes.height = tree_view->priv->drag_column->button->allocation.height;
3442 	  attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3443 	  attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3444 	  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3445 	  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3446 	  tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3447 	  gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3448 
3449 	  mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3450           cr = gdk_cairo_create (mask);
3451 
3452           cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
3453           cairo_paint (cr);
3454           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
3455           cairo_rectangle (cr, 1, 1, width - 2, height - 2);
3456           cairo_stroke (cr);
3457           cairo_destroy (cr);
3458 
3459 	  gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3460 					 mask, 0, 0);
3461 	  if (mask) g_object_unref (mask);
3462 	  tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3463 	}
3464     }
3465   else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3466     {
3467       width = tree_view->priv->expander_size;
3468 
3469       /* Get x, y, width, height of arrow */
3470       gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3471       if (reorder->left_column)
3472 	{
3473 	  x += reorder->left_column->button->allocation.x + reorder->left_column->button->allocation.width - width/2;
3474 	  height = reorder->left_column->button->allocation.height;
3475 	}
3476       else
3477 	{
3478 	  x += reorder->right_column->button->allocation.x - width/2;
3479 	  height = reorder->right_column->button->allocation.height;
3480 	}
3481       y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3482       height += tree_view->priv->expander_size;
3483 
3484       /* Create the new window */
3485       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3486 	{
3487 	  if (tree_view->priv->drag_highlight_window)
3488 	    {
3489 	      gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3490 					NULL);
3491 	      gdk_window_destroy (tree_view->priv->drag_highlight_window);
3492 	    }
3493 
3494 	  attributes.window_type = GDK_WINDOW_TEMP;
3495 	  attributes.wclass = GDK_INPUT_OUTPUT;
3496 	  attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3497 	  attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3498 	  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3499 	  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3500           attributes.x = x;
3501           attributes.y = y;
3502 	  attributes.width = width;
3503 	  attributes.height = height;
3504 	  tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3505 								   &attributes, attributes_mask);
3506 	  gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3507 
3508 	  mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3509           cr = gdk_cairo_create (mask);
3510 
3511           cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
3512           cairo_paint (cr);
3513           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
3514           cairo_move_to (cr, 0, 0);
3515           cairo_line_to (cr, width, 0);
3516           cairo_line_to (cr, width / 2., width / 2);
3517           cairo_move_to (cr, 0, height);
3518           cairo_line_to (cr, width, height);
3519           cairo_line_to (cr, width / 2., height - width / 2.);
3520           cairo_fill (cr);
3521 
3522           cairo_destroy (cr);
3523 	  gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3524 					 mask, 0, 0);
3525 	  if (mask) g_object_unref (mask);
3526 	}
3527 
3528       tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3529       gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3530     }
3531   else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3532 	   arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3533     {
3534       width = tree_view->priv->expander_size;
3535 
3536       /* Get x, y, width, height of arrow */
3537       width = width/2; /* remember, the arrow only takes half the available width */
3538       gdk_window_get_origin (widget->window, &x, &y);
3539       if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3540 	x += widget->allocation.width - width;
3541 
3542       if (reorder->left_column)
3543 	height = reorder->left_column->button->allocation.height;
3544       else
3545 	height = reorder->right_column->button->allocation.height;
3546 
3547       y -= tree_view->priv->expander_size;
3548       height += 2*tree_view->priv->expander_size;
3549 
3550       /* Create the new window */
3551       if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3552 	  tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3553 	{
3554 	  if (tree_view->priv->drag_highlight_window)
3555 	    {
3556 	      gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3557 					NULL);
3558 	      gdk_window_destroy (tree_view->priv->drag_highlight_window);
3559 	    }
3560 
3561 	  attributes.window_type = GDK_WINDOW_TEMP;
3562 	  attributes.wclass = GDK_INPUT_OUTPUT;
3563 	  attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3564 	  attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3565 	  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3566 	  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3567           attributes.x = x;
3568           attributes.y = y;
3569 	  attributes.width = width;
3570 	  attributes.height = height;
3571 	  tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3572 	  gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3573 
3574 	  mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3575           cr = gdk_cairo_create (mask);
3576 
3577           cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
3578           cairo_paint (cr);
3579           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
3580           /* mirror if we're on the left */
3581           if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3582             {
3583               cairo_translate (cr, width, 0);
3584               cairo_scale (cr, -1, 1);
3585             }
3586           cairo_move_to (cr, 0, 0);
3587           cairo_line_to (cr, width, width);
3588           cairo_line_to (cr, 0, tree_view->priv->expander_size);
3589           cairo_move_to (cr, 0, height);
3590           cairo_line_to (cr, width, height - width);
3591           cairo_line_to (cr, 0, height - tree_view->priv->expander_size);
3592           cairo_fill (cr);
3593 
3594           cairo_destroy (cr);
3595 	  gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3596 					 mask, 0, 0);
3597 	  if (mask) g_object_unref (mask);
3598 	}
3599 
3600       tree_view->priv->drag_column_window_state = arrow_type;
3601       gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3602    }
3603   else
3604     {
3605       g_warning (G_STRLOC"Invalid GtkTreeViewColumnReorder struct");
3606       gdk_window_hide (tree_view->priv->drag_highlight_window);
3607       return;
3608     }
3609 
3610   gdk_window_show (tree_view->priv->drag_highlight_window);
3611   gdk_window_raise (tree_view->priv->drag_highlight_window);
3612 }
3613 
3614 static gboolean
gtk_tree_view_motion_resize_column(GtkWidget * widget,GdkEventMotion * event)3615 gtk_tree_view_motion_resize_column (GtkWidget      *widget,
3616 				    GdkEventMotion *event)
3617 {
3618   gint x;
3619   gint new_width;
3620   GtkTreeViewColumn *column;
3621   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
3622 
3623   column = gtk_tree_view_get_column (tree_view, tree_view->priv->drag_pos);
3624 
3625   if (event->is_hint || event->window != widget->window)
3626     gtk_widget_get_pointer (widget, &x, NULL);
3627   else
3628     x = event->x;
3629 
3630   if (tree_view->priv->hadjustment)
3631     x += tree_view->priv->hadjustment->value;
3632 
3633   new_width = gtk_tree_view_new_column_width (tree_view,
3634 					      tree_view->priv->drag_pos, &x);
3635   if (x != tree_view->priv->x_drag &&
3636       (new_width != column->fixed_width))
3637     {
3638       column->use_resized_width = TRUE;
3639       column->resized_width = new_width;
3640       if (column->expand)
3641 	column->resized_width -= tree_view->priv->last_extra_space_per_column;
3642       gtk_widget_queue_resize (widget);
3643     }
3644 
3645   return FALSE;
3646 }
3647 
3648 
3649 static void
gtk_tree_view_update_current_reorder(GtkTreeView * tree_view)3650 gtk_tree_view_update_current_reorder (GtkTreeView *tree_view)
3651 {
3652   GtkTreeViewColumnReorder *reorder = NULL;
3653   GList *list;
3654   gint mouse_x;
3655 
3656   gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3657   for (list = tree_view->priv->column_drag_info; list; list = list->next)
3658     {
3659       reorder = (GtkTreeViewColumnReorder *) list->data;
3660       if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3661 	break;
3662       reorder = NULL;
3663     }
3664 
3665   /*  if (reorder && reorder == tree_view->priv->cur_reorder)
3666       return;*/
3667 
3668   tree_view->priv->cur_reorder = reorder;
3669   gtk_tree_view_motion_draw_column_motion_arrow (tree_view);
3670 }
3671 
3672 static void
gtk_tree_view_vertical_autoscroll(GtkTreeView * tree_view)3673 gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view)
3674 {
3675   GdkRectangle visible_rect;
3676   gint y;
3677   gint offset;
3678   gfloat value;
3679 
3680   gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3681   y += tree_view->priv->dy;
3682 
3683   gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
3684 
3685   /* see if we are near the edge. */
3686   offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3687   if (offset > 0)
3688     {
3689       offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3690       if (offset < 0)
3691 	return;
3692     }
3693 
3694   value = CLAMP (tree_view->priv->vadjustment->value + offset, 0.0,
3695 		 tree_view->priv->vadjustment->upper - tree_view->priv->vadjustment->page_size);
3696   gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3697 }
3698 
3699 static gboolean
gtk_tree_view_horizontal_autoscroll(GtkTreeView * tree_view)3700 gtk_tree_view_horizontal_autoscroll (GtkTreeView *tree_view)
3701 {
3702   GdkRectangle visible_rect;
3703   gint x;
3704   gint offset;
3705   gfloat value;
3706 
3707   gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3708 
3709   gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
3710 
3711   /* See if we are near the edge. */
3712   offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3713   if (offset > 0)
3714     {
3715       offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3716       if (offset < 0)
3717 	return TRUE;
3718     }
3719   offset = offset/3;
3720 
3721   value = CLAMP (tree_view->priv->hadjustment->value + offset,
3722 		 0.0, tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size);
3723   gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3724 
3725   return TRUE;
3726 
3727 }
3728 
3729 static gboolean
gtk_tree_view_motion_drag_column(GtkWidget * widget,GdkEventMotion * event)3730 gtk_tree_view_motion_drag_column (GtkWidget      *widget,
3731 				  GdkEventMotion *event)
3732 {
3733   GtkTreeView *tree_view = (GtkTreeView *) widget;
3734   GtkTreeViewColumn *column = tree_view->priv->drag_column;
3735   gint x, y;
3736 
3737   /* Sanity Check */
3738   if ((column == NULL) ||
3739       (event->window != tree_view->priv->drag_window))
3740     return FALSE;
3741 
3742   /* Handle moving the header */
3743   gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3744   x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3745 	     MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->button->allocation.width);
3746   gdk_window_move (tree_view->priv->drag_window, x, y);
3747 
3748   /* autoscroll, if needed */
3749   gtk_tree_view_horizontal_autoscroll (tree_view);
3750   /* Update the current reorder position and arrow; */
3751   gtk_tree_view_update_current_reorder (tree_view);
3752 
3753   return TRUE;
3754 }
3755 
3756 static void
gtk_tree_view_stop_rubber_band(GtkTreeView * tree_view)3757 gtk_tree_view_stop_rubber_band (GtkTreeView *tree_view)
3758 {
3759   remove_scroll_timeout (tree_view);
3760   gtk_grab_remove (GTK_WIDGET (tree_view));
3761 
3762   if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3763     {
3764       GtkTreePath *tmp_path;
3765 
3766       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3767 
3768       /* The anchor path should be set to the start path */
3769       tmp_path = _gtk_tree_view_find_path (tree_view,
3770 					   tree_view->priv->rubber_band_start_tree,
3771 					   tree_view->priv->rubber_band_start_node);
3772 
3773       if (tree_view->priv->anchor)
3774 	gtk_tree_row_reference_free (tree_view->priv->anchor);
3775 
3776       tree_view->priv->anchor =
3777 	gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3778 					  tree_view->priv->model,
3779 					  tmp_path);
3780 
3781       gtk_tree_path_free (tmp_path);
3782 
3783       /* ... and the cursor to the end path */
3784       tmp_path = _gtk_tree_view_find_path (tree_view,
3785 					   tree_view->priv->rubber_band_end_tree,
3786 					   tree_view->priv->rubber_band_end_node);
3787       gtk_tree_view_real_set_cursor (GTK_TREE_VIEW (tree_view), tmp_path, FALSE, FALSE);
3788       gtk_tree_path_free (tmp_path);
3789 
3790       _gtk_tree_selection_emit_changed (tree_view->priv->selection);
3791     }
3792 
3793   /* Clear status variables */
3794   tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3795   tree_view->priv->rubber_band_extend = FALSE;
3796   tree_view->priv->rubber_band_modify = FALSE;
3797 
3798   tree_view->priv->rubber_band_start_node = NULL;
3799   tree_view->priv->rubber_band_start_tree = NULL;
3800   tree_view->priv->rubber_band_end_node = NULL;
3801   tree_view->priv->rubber_band_end_tree = NULL;
3802 }
3803 
3804 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)3805 gtk_tree_view_update_rubber_band_selection_range (GtkTreeView *tree_view,
3806 						 GtkRBTree   *start_tree,
3807 						 GtkRBNode   *start_node,
3808 						 GtkRBTree   *end_tree,
3809 						 GtkRBNode   *end_node,
3810 						 gboolean     select,
3811 						 gboolean     skip_start,
3812 						 gboolean     skip_end)
3813 {
3814   if (start_node == end_node)
3815     return;
3816 
3817   /* We skip the first node and jump inside the loop */
3818   if (skip_start)
3819     goto skip_first;
3820 
3821   do
3822     {
3823       /* Small optimization by assuming insensitive nodes are never
3824        * selected.
3825        */
3826       if (!GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
3827         {
3828 	  GtkTreePath *path;
3829 	  gboolean selectable;
3830 
3831 	  path = _gtk_tree_view_find_path (tree_view, start_tree, start_node);
3832 	  selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection, start_node, path);
3833 	  gtk_tree_path_free (path);
3834 
3835 	  if (!selectable)
3836 	    goto node_not_selectable;
3837 	}
3838 
3839       if (select)
3840         {
3841 	  if (tree_view->priv->rubber_band_extend)
3842             GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3843 	  else if (tree_view->priv->rubber_band_modify)
3844 	    {
3845 	      /* Toggle the selection state */
3846 	      if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
3847 		GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3848 	      else
3849 		GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3850 	    }
3851 	  else
3852 	    GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3853 	}
3854       else
3855         {
3856 	  /* Mirror the above */
3857 	  if (tree_view->priv->rubber_band_extend)
3858 	    GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3859 	  else if (tree_view->priv->rubber_band_modify)
3860 	    {
3861 	      /* Toggle the selection state */
3862 	      if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
3863 		GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3864 	      else
3865 		GTK_RBNODE_SET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3866 	    }
3867 	  else
3868 	    GTK_RBNODE_UNSET_FLAG (start_node, GTK_RBNODE_IS_SELECTED);
3869 	}
3870 
3871       _gtk_tree_view_queue_draw_node (tree_view, start_tree, start_node, NULL);
3872 
3873 node_not_selectable:
3874       if (start_node == end_node)
3875 	break;
3876 
3877 skip_first:
3878 
3879       if (start_node->children)
3880         {
3881 	  start_tree = start_node->children;
3882 	  start_node = start_tree->root;
3883 	  while (start_node->left != start_tree->nil)
3884 	    start_node = start_node->left;
3885 	}
3886       else
3887         {
3888 	  _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
3889 
3890 	  if (!start_tree)
3891 	    /* Ran out of tree */
3892 	    break;
3893 	}
3894 
3895       if (skip_end && start_node == end_node)
3896 	break;
3897     }
3898   while (TRUE);
3899 }
3900 
3901 static void
gtk_tree_view_update_rubber_band_selection(GtkTreeView * tree_view)3902 gtk_tree_view_update_rubber_band_selection (GtkTreeView *tree_view)
3903 {
3904   GtkRBTree *start_tree, *end_tree;
3905   GtkRBNode *start_node, *end_node;
3906 
3907   _gtk_rbtree_find_offset (tree_view->priv->tree, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_tree, &start_node);
3908   _gtk_rbtree_find_offset (tree_view->priv->tree, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_tree, &end_node);
3909 
3910   /* Handle the start area first */
3911   if (!tree_view->priv->rubber_band_start_node)
3912     {
3913       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3914 						       start_tree,
3915 						       start_node,
3916 						       end_tree,
3917 						       end_node,
3918 						       TRUE,
3919 						       FALSE,
3920 						       FALSE);
3921     }
3922   else if (_gtk_rbtree_node_find_offset (start_tree, start_node) <
3923            _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_start_tree, tree_view->priv->rubber_band_start_node))
3924     {
3925       /* New node is above the old one; selection became bigger */
3926       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3927 						       start_tree,
3928 						       start_node,
3929 						       tree_view->priv->rubber_band_start_tree,
3930 						       tree_view->priv->rubber_band_start_node,
3931 						       TRUE,
3932 						       FALSE,
3933 						       TRUE);
3934     }
3935   else if (_gtk_rbtree_node_find_offset (start_tree, start_node) >
3936            _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_start_tree, tree_view->priv->rubber_band_start_node))
3937     {
3938       /* New node is below the old one; selection became smaller */
3939       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3940 						       tree_view->priv->rubber_band_start_tree,
3941 						       tree_view->priv->rubber_band_start_node,
3942 						       start_tree,
3943 						       start_node,
3944 						       FALSE,
3945 						       FALSE,
3946 						       TRUE);
3947     }
3948 
3949   tree_view->priv->rubber_band_start_tree = start_tree;
3950   tree_view->priv->rubber_band_start_node = start_node;
3951 
3952   /* Next, handle the end area */
3953   if (!tree_view->priv->rubber_band_end_node)
3954     {
3955       /* In the event this happens, start_node was also NULL; this case is
3956        * handled above.
3957        */
3958     }
3959   else if (!end_node)
3960     {
3961       /* Find the last node in the tree */
3962       _gtk_rbtree_find_offset (tree_view->priv->tree, tree_view->priv->height - 1,
3963 			       &end_tree, &end_node);
3964 
3965       /* Selection reached end of the tree */
3966       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3967 						       tree_view->priv->rubber_band_end_tree,
3968 						       tree_view->priv->rubber_band_end_node,
3969 						       end_tree,
3970 						       end_node,
3971 						       TRUE,
3972 						       TRUE,
3973 						       FALSE);
3974     }
3975   else if (_gtk_rbtree_node_find_offset (end_tree, end_node) >
3976            _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_end_tree, tree_view->priv->rubber_band_end_node))
3977     {
3978       /* New node is below the old one; selection became bigger */
3979       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3980 						       tree_view->priv->rubber_band_end_tree,
3981 						       tree_view->priv->rubber_band_end_node,
3982 						       end_tree,
3983 						       end_node,
3984 						       TRUE,
3985 						       TRUE,
3986 						       FALSE);
3987     }
3988   else if (_gtk_rbtree_node_find_offset (end_tree, end_node) <
3989            _gtk_rbtree_node_find_offset (tree_view->priv->rubber_band_end_tree, tree_view->priv->rubber_band_end_node))
3990     {
3991       /* New node is above the old one; selection became smaller */
3992       gtk_tree_view_update_rubber_band_selection_range (tree_view,
3993 						       end_tree,
3994 						       end_node,
3995 						       tree_view->priv->rubber_band_end_tree,
3996 						       tree_view->priv->rubber_band_end_node,
3997 						       FALSE,
3998 						       TRUE,
3999 						       FALSE);
4000     }
4001 
4002   tree_view->priv->rubber_band_end_tree = end_tree;
4003   tree_view->priv->rubber_band_end_node = end_node;
4004 }
4005 
4006 static void
gtk_tree_view_update_rubber_band(GtkTreeView * tree_view)4007 gtk_tree_view_update_rubber_band (GtkTreeView *tree_view)
4008 {
4009   gint x, y;
4010   GdkRectangle old_area;
4011   GdkRectangle new_area;
4012   GdkRectangle common;
4013   GdkRegion *invalid_region;
4014 
4015   old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
4016   old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
4017   old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
4018   old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
4019 
4020   gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
4021 
4022   x = MAX (x, 0);
4023   y = MAX (y, 0) + tree_view->priv->dy;
4024 
4025   new_area.x = MIN (tree_view->priv->press_start_x, x);
4026   new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
4027   new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
4028   new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
4029 
4030   invalid_region = gdk_region_rectangle (&old_area);
4031   gdk_region_union_with_rect (invalid_region, &new_area);
4032 
4033   gdk_rectangle_intersect (&old_area, &new_area, &common);
4034   if (common.width > 2 && common.height > 2)
4035     {
4036       GdkRegion *common_region;
4037 
4038       /* make sure the border is invalidated */
4039       common.x += 1;
4040       common.y += 1;
4041       common.width -= 2;
4042       common.height -= 2;
4043 
4044       common_region = gdk_region_rectangle (&common);
4045 
4046       gdk_region_subtract (invalid_region, common_region);
4047       gdk_region_destroy (common_region);
4048     }
4049 
4050   gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
4051 
4052   gdk_region_destroy (invalid_region);
4053 
4054   tree_view->priv->rubber_band_x = x;
4055   tree_view->priv->rubber_band_y = y;
4056 
4057   gtk_tree_view_update_rubber_band_selection (tree_view);
4058 }
4059 
4060 static void
gtk_tree_view_paint_rubber_band(GtkTreeView * tree_view,GdkRectangle * area)4061 gtk_tree_view_paint_rubber_band (GtkTreeView  *tree_view,
4062 				GdkRectangle *area)
4063 {
4064   cairo_t *cr;
4065   GdkRectangle rect;
4066   GdkRectangle rubber_rect;
4067 
4068   rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
4069   rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
4070   rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
4071   rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
4072 
4073   if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
4074     return;
4075 
4076   cr = gdk_cairo_create (tree_view->priv->bin_window);
4077   cairo_set_line_width (cr, 1.0);
4078 
4079   cairo_set_source_rgba (cr,
4080 			 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
4081 			 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
4082 			 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.,
4083 			 .25);
4084 
4085   gdk_cairo_rectangle (cr, &rect);
4086   cairo_clip (cr);
4087   cairo_paint (cr);
4088 
4089   cairo_set_source_rgb (cr,
4090 			GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
4091 			GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
4092 			GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.);
4093 
4094   cairo_rectangle (cr,
4095 		   rubber_rect.x + 0.5, rubber_rect.y + 0.5,
4096 		   rubber_rect.width - 1, rubber_rect.height - 1);
4097   cairo_stroke (cr);
4098 
4099   cairo_destroy (cr);
4100 }
4101 
4102 static gboolean
gtk_tree_view_motion_bin_window(GtkWidget * widget,GdkEventMotion * event)4103 gtk_tree_view_motion_bin_window (GtkWidget      *widget,
4104 				 GdkEventMotion *event)
4105 {
4106   GtkTreeView *tree_view;
4107   GtkRBTree *tree;
4108   GtkRBNode *node;
4109   gint new_y;
4110 
4111   tree_view = (GtkTreeView *) widget;
4112 
4113   if (tree_view->priv->tree == NULL)
4114     return FALSE;
4115 
4116   if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
4117     {
4118       gtk_grab_add (GTK_WIDGET (tree_view));
4119       gtk_tree_view_update_rubber_band (tree_view);
4120 
4121       tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
4122     }
4123   else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4124     {
4125       gtk_tree_view_update_rubber_band (tree_view);
4126 
4127       add_scroll_timeout (tree_view);
4128     }
4129 
4130   /* only check for an initiated drag when a button is pressed */
4131   if (tree_view->priv->pressed_button >= 0
4132       && !tree_view->priv->rubber_band_status)
4133     gtk_tree_view_maybe_begin_dragging_row (tree_view, event);
4134 
4135   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4136   if (new_y < 0)
4137     new_y = 0;
4138 
4139   _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
4140 
4141   /* If we are currently pressing down a button, we don't want to prelight anything else. */
4142   if ((tree_view->priv->button_pressed_node != NULL) &&
4143       (tree_view->priv->button_pressed_node != node))
4144     node = NULL;
4145 
4146   tree_view->priv->event_last_x = event->x;
4147   tree_view->priv->event_last_y = event->y;
4148 
4149   prelight_or_select (tree_view, tree, node, event->x, event->y);
4150 
4151   return TRUE;
4152 }
4153 
4154 static gboolean
gtk_tree_view_motion(GtkWidget * widget,GdkEventMotion * event)4155 gtk_tree_view_motion (GtkWidget      *widget,
4156 		      GdkEventMotion *event)
4157 {
4158   GtkTreeView *tree_view;
4159 
4160   tree_view = (GtkTreeView *) widget;
4161 
4162   /* Resizing a column */
4163   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
4164     return gtk_tree_view_motion_resize_column (widget, event);
4165 
4166   /* Drag column */
4167   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
4168     return gtk_tree_view_motion_drag_column (widget, event);
4169 
4170   /* Sanity check it */
4171   if (event->window == tree_view->priv->bin_window)
4172     return gtk_tree_view_motion_bin_window (widget, event);
4173 
4174   return FALSE;
4175 }
4176 
4177 /* Invalidate the focus rectangle near the edge of the bin_window; used when
4178  * the tree is empty.
4179  */
4180 static void
invalidate_empty_focus(GtkTreeView * tree_view)4181 invalidate_empty_focus (GtkTreeView *tree_view)
4182 {
4183   GdkRectangle area;
4184 
4185   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
4186     return;
4187 
4188   area.x = 0;
4189   area.y = 0;
4190   area.width = gdk_window_get_width (tree_view->priv->bin_window);
4191   area.height = gdk_window_get_height (tree_view->priv->bin_window);
4192   gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
4193 }
4194 
4195 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
4196  * is empty.
4197  */
4198 static void
draw_empty_focus(GtkTreeView * tree_view,GdkRectangle * clip_area)4199 draw_empty_focus (GtkTreeView *tree_view, GdkRectangle *clip_area)
4200 {
4201   GtkWidget *widget = GTK_WIDGET (tree_view);
4202   gint w, h;
4203 
4204   if (!gtk_widget_has_focus (widget))
4205     return;
4206 
4207   w = gdk_window_get_width (tree_view->priv->bin_window);
4208   h = gdk_window_get_height (tree_view->priv->bin_window);
4209 
4210   w -= 2;
4211   h -= 2;
4212 
4213   if (w > 0 && h > 0)
4214     gtk_paint_focus (gtk_widget_get_style (widget),
4215 		     tree_view->priv->bin_window,
4216 		     gtk_widget_get_state (widget),
4217 		     clip_area,
4218 		     widget,
4219 		     NULL,
4220 		     1, 1, w, h);
4221 }
4222 
4223 typedef enum {
4224   GTK_TREE_VIEW_GRID_LINE,
4225   GTK_TREE_VIEW_TREE_LINE,
4226   GTK_TREE_VIEW_FOREGROUND_LINE
4227 } GtkTreeViewLineType;
4228 
4229 static void
gtk_tree_view_draw_line(GtkTreeView * tree_view,GdkWindow * window,GtkTreeViewLineType type,int x1,int y1,int x2,int y2)4230 gtk_tree_view_draw_line (GtkTreeView         *tree_view,
4231                          GdkWindow           *window,
4232                          GtkTreeViewLineType  type,
4233                          int                  x1,
4234                          int                  y1,
4235                          int                  x2,
4236                          int                  y2)
4237 {
4238   cairo_t *cr;
4239 
4240   cr = gdk_cairo_create (window);
4241 
4242   switch (type)
4243     {
4244     case GTK_TREE_VIEW_TREE_LINE:
4245       cairo_set_source_rgb (cr, 0, 0, 0);
4246       cairo_set_line_width (cr, tree_view->priv->tree_line_width);
4247       if (tree_view->priv->tree_line_dashes[0])
4248         cairo_set_dash (cr,
4249                         tree_view->priv->tree_line_dashes,
4250                         2, 0.5);
4251       break;
4252     case GTK_TREE_VIEW_GRID_LINE:
4253       cairo_set_source_rgb (cr, 0, 0, 0);
4254       cairo_set_line_width (cr, tree_view->priv->grid_line_width);
4255       if (tree_view->priv->grid_line_dashes[0])
4256         cairo_set_dash (cr,
4257                         tree_view->priv->grid_line_dashes,
4258                         2, 0.5);
4259       break;
4260     default:
4261       g_assert_not_reached ();
4262       /* fall through */
4263     case GTK_TREE_VIEW_FOREGROUND_LINE:
4264       cairo_set_line_width (cr, 1.0);
4265       gdk_cairo_set_source_color (cr,
4266           &GTK_WIDGET (tree_view)->style->fg[gtk_widget_get_state (GTK_WIDGET (tree_view))]);
4267       break;
4268     }
4269 
4270   cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
4271   cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
4272   cairo_stroke (cr);
4273 
4274   cairo_destroy (cr);
4275 }
4276 
4277 static void
gtk_tree_view_draw_grid_lines(GtkTreeView * tree_view,GdkEventExpose * event,gint n_visible_columns)4278 gtk_tree_view_draw_grid_lines (GtkTreeView    *tree_view,
4279 			       GdkEventExpose *event,
4280 			       gint            n_visible_columns)
4281 {
4282   GList *list = tree_view->priv->columns;
4283   gint i = 0;
4284   gint current_x = 0;
4285 
4286   if (tree_view->priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_VERTICAL
4287       && tree_view->priv->grid_lines != GTK_TREE_VIEW_GRID_LINES_BOTH)
4288     return;
4289 
4290   /* Only draw the lines for visible rows and columns */
4291   for (list = tree_view->priv->columns; list; list = list->next)
4292     {
4293       GtkTreeViewColumn *column = list->data;
4294 
4295       /* We don't want a line for the last column */
4296       if (i == n_visible_columns - 1)
4297 	break;
4298 
4299       if (! column->visible)
4300 	continue;
4301 
4302       i++;
4303 
4304       current_x += column->width;
4305 
4306       gtk_tree_view_draw_line (tree_view, event->window,
4307                                GTK_TREE_VIEW_GRID_LINE,
4308                                current_x - 1, 0,
4309                                current_x - 1, tree_view->priv->height);
4310     }
4311 }
4312 
4313 /* Warning: Very scary function.
4314  * Modify at your own risk
4315  *
4316  * KEEP IN SYNC WITH gtk_tree_view_create_row_drag_icon()!
4317  * FIXME: It's not...
4318  */
4319 static gboolean
gtk_tree_view_bin_expose(GtkWidget * widget,GdkEventExpose * event)4320 gtk_tree_view_bin_expose (GtkWidget      *widget,
4321 			  GdkEventExpose *event)
4322 {
4323   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
4324   GtkTreePath *path;
4325   GtkRBTree *tree;
4326   GList *list;
4327   GtkRBNode *node;
4328   GtkRBNode *cursor = NULL;
4329   GtkRBTree *cursor_tree = NULL;
4330   GtkRBNode *drag_highlight = NULL;
4331   GtkRBTree *drag_highlight_tree = NULL;
4332   GtkTreeIter iter;
4333   gint new_y;
4334   gint y_offset, cell_offset;
4335   gint max_height;
4336   gint depth;
4337   GdkRectangle background_area;
4338   GdkRectangle cell_area;
4339   guint flags;
4340   gint highlight_x;
4341   gint expander_cell_width;
4342   gint bin_window_width;
4343   gint bin_window_height;
4344   GtkTreePath *cursor_path;
4345   GtkTreePath *drag_dest_path;
4346   GList *first_column, *last_column;
4347   gint vertical_separator;
4348   gint horizontal_separator;
4349   gint focus_line_width;
4350   gboolean allow_rules;
4351   gboolean has_special_cell;
4352   gboolean rtl;
4353   gint n_visible_columns;
4354   gint pointer_x, pointer_y;
4355   gint grid_line_width;
4356   gboolean got_pointer = FALSE;
4357   gboolean row_ending_details;
4358   gboolean draw_vgrid_lines, draw_hgrid_lines;
4359 
4360   rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4361 
4362   gtk_widget_style_get (widget,
4363 			"horizontal-separator", &horizontal_separator,
4364 			"vertical-separator", &vertical_separator,
4365 			"allow-rules", &allow_rules,
4366 			"focus-line-width", &focus_line_width,
4367 			"row-ending-details", &row_ending_details,
4368 			NULL);
4369 
4370   if (tree_view->priv->tree == NULL)
4371     {
4372       draw_empty_focus (tree_view, &event->area);
4373       return TRUE;
4374     }
4375 
4376   /* clip event->area to the visible area */
4377   if (event->area.height < 0)
4378     return TRUE;
4379 
4380   validate_visible_area (tree_view);
4381 
4382   new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, event->area.y);
4383 
4384   if (new_y < 0)
4385     new_y = 0;
4386   y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
4387   bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
4388   bin_window_height = gdk_window_get_height (tree_view->priv->bin_window);
4389 
4390   if (tree_view->priv->height < bin_window_height)
4391     {
4392       gtk_paint_flat_box (widget->style,
4393                           event->window,
4394                           widget->state,
4395                           GTK_SHADOW_NONE,
4396                           &event->area,
4397                           widget,
4398                           "cell_even",
4399                           0, tree_view->priv->height,
4400                           bin_window_width,
4401                           bin_window_height - tree_view->priv->height);
4402     }
4403 
4404   if (node == NULL)
4405     return TRUE;
4406 
4407   /* find the path for the node */
4408   path = _gtk_tree_view_find_path ((GtkTreeView *)widget,
4409 				   tree,
4410 				   node);
4411   gtk_tree_model_get_iter (tree_view->priv->model,
4412 			   &iter,
4413 			   path);
4414   depth = gtk_tree_path_get_depth (path);
4415   gtk_tree_path_free (path);
4416 
4417   cursor_path = NULL;
4418   drag_dest_path = NULL;
4419 
4420   if (tree_view->priv->cursor)
4421     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
4422 
4423   if (cursor_path)
4424     _gtk_tree_view_find_node (tree_view, cursor_path,
4425                               &cursor_tree, &cursor);
4426 
4427   if (tree_view->priv->drag_dest_row)
4428     drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
4429 
4430   if (drag_dest_path)
4431     _gtk_tree_view_find_node (tree_view, drag_dest_path,
4432                               &drag_highlight_tree, &drag_highlight);
4433 
4434   draw_vgrid_lines =
4435     tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
4436     || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
4437   draw_hgrid_lines =
4438     tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
4439     || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
4440 
4441   if (draw_vgrid_lines || draw_hgrid_lines)
4442     gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
4443 
4444   n_visible_columns = 0;
4445   for (list = tree_view->priv->columns; list; list = list->next)
4446     {
4447       if (! GTK_TREE_VIEW_COLUMN (list->data)->visible)
4448 	continue;
4449       n_visible_columns ++;
4450     }
4451 
4452   /* Find the last column */
4453   for (last_column = g_list_last (tree_view->priv->columns);
4454        last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible);
4455        last_column = last_column->prev)
4456     ;
4457 
4458   /* and the first */
4459   for (first_column = g_list_first (tree_view->priv->columns);
4460        first_column && !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible);
4461        first_column = first_column->next)
4462     ;
4463 
4464   /* Actually process the expose event.  To do this, we want to
4465    * start at the first node of the event, and walk the tree in
4466    * order, drawing each successive node.
4467    */
4468 
4469   do
4470     {
4471       gboolean parity;
4472       gboolean is_separator = FALSE;
4473       gboolean is_first = FALSE;
4474       gboolean is_last = FALSE;
4475 
4476       is_separator = row_is_separator (tree_view, &iter, NULL);
4477 
4478       max_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
4479 
4480       cell_offset = 0;
4481       highlight_x = 0; /* should match x coord of first cell */
4482       expander_cell_width = 0;
4483 
4484       background_area.y = y_offset + event->area.y;
4485       background_area.height = max_height;
4486 
4487       flags = 0;
4488 
4489       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT))
4490 	flags |= GTK_CELL_RENDERER_PRELIT;
4491 
4492       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
4493         flags |= GTK_CELL_RENDERER_SELECTED;
4494 
4495       parity = _gtk_rbtree_node_find_parity (tree, node);
4496 
4497       /* we *need* to set cell data on all cells before the call
4498        * to _has_special_cell, else _has_special_cell() does not
4499        * return a correct value.
4500        */
4501       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4502 	   list;
4503 	   list = (rtl ? list->prev : list->next))
4504         {
4505 	  GtkTreeViewColumn *column = list->data;
4506 	  gtk_tree_view_column_cell_set_cell_data (column,
4507 						   tree_view->priv->model,
4508 						   &iter,
4509 						   GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
4510 						   node->children?TRUE:FALSE);
4511         }
4512 
4513       has_special_cell = gtk_tree_view_has_special_cell (tree_view);
4514 
4515       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4516 	   list;
4517 	   list = (rtl ? list->prev : list->next))
4518 	{
4519 	  GtkTreeViewColumn *column = list->data;
4520 	  const gchar *detail = NULL;
4521 	  GtkStateType state;
4522 
4523 	  if (!column->visible)
4524             continue;
4525 
4526 	  if (cell_offset > event->area.x + event->area.width ||
4527 	      cell_offset + column->width < event->area.x)
4528 	    {
4529 	      cell_offset += column->width;
4530 	      continue;
4531 	    }
4532 
4533           if (column->show_sort_indicator)
4534 	    flags |= GTK_CELL_RENDERER_SORTED;
4535           else
4536             flags &= ~GTK_CELL_RENDERER_SORTED;
4537 
4538 	  if (cursor == node)
4539             flags |= GTK_CELL_RENDERER_FOCUSED;
4540           else
4541             flags &= ~GTK_CELL_RENDERER_FOCUSED;
4542 
4543 	  background_area.x = cell_offset;
4544 	  background_area.width = column->width;
4545 
4546           cell_area = background_area;
4547           cell_area.y += vertical_separator / 2;
4548           cell_area.x += horizontal_separator / 2;
4549           cell_area.height -= vertical_separator;
4550 	  cell_area.width -= horizontal_separator;
4551 
4552 	  if (draw_vgrid_lines)
4553 	    {
4554 	      if (list == first_column)
4555 	        {
4556 		  cell_area.width -= grid_line_width / 2;
4557 		}
4558 	      else if (list == last_column)
4559 	        {
4560 		  cell_area.x += grid_line_width / 2;
4561 		  cell_area.width -= grid_line_width / 2;
4562 		}
4563 	      else
4564 	        {
4565 	          cell_area.x += grid_line_width / 2;
4566 	          cell_area.width -= grid_line_width;
4567 		}
4568 	    }
4569 
4570 	  if (draw_hgrid_lines)
4571 	    {
4572 	      cell_area.y += grid_line_width / 2;
4573 	      cell_area.height -= grid_line_width;
4574 	    }
4575 
4576 	  if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4577 	    {
4578 	      cell_offset += column->width;
4579 	      continue;
4580 	    }
4581 
4582 	  gtk_tree_view_column_cell_set_cell_data (column,
4583 						   tree_view->priv->model,
4584 						   &iter,
4585 						   GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
4586 						   node->children?TRUE:FALSE);
4587 
4588           /* Select the detail for drawing the cell.  relevant
4589            * factors are parity, sortedness, and whether to
4590            * display rules.
4591            */
4592           if (allow_rules && tree_view->priv->has_rules)
4593             {
4594               if ((flags & GTK_CELL_RENDERER_SORTED) &&
4595 		  n_visible_columns >= 3)
4596                 {
4597                   if (parity)
4598                     detail = "cell_odd_ruled_sorted";
4599                   else
4600                     detail = "cell_even_ruled_sorted";
4601                 }
4602               else
4603                 {
4604                   if (parity)
4605                     detail = "cell_odd_ruled";
4606                   else
4607                     detail = "cell_even_ruled";
4608                 }
4609             }
4610           else
4611             {
4612               if ((flags & GTK_CELL_RENDERER_SORTED) &&
4613 		  n_visible_columns >= 3)
4614                 {
4615                   if (parity)
4616                     detail = "cell_odd_sorted";
4617                   else
4618                     detail = "cell_even_sorted";
4619                 }
4620               else
4621                 {
4622                   if (parity)
4623                     detail = "cell_odd";
4624                   else
4625                     detail = "cell_even";
4626                 }
4627             }
4628 
4629           g_assert (detail);
4630 
4631 	  if (widget->state == GTK_STATE_INSENSITIVE)
4632 	    state = GTK_STATE_INSENSITIVE;
4633 	  else if (flags & GTK_CELL_RENDERER_SELECTED)
4634 	    state = GTK_STATE_SELECTED;
4635 	  else
4636 	    state = GTK_STATE_NORMAL;
4637 
4638 	  /* Draw background */
4639 	  if (row_ending_details)
4640 	    {
4641 	      char new_detail[128];
4642 
4643 	      is_first = (rtl ? !list->next : !list->prev);
4644 	      is_last = (rtl ? !list->prev : !list->next);
4645 
4646 	      /* (I don't like the snprintfs either, but couldn't find a
4647 	       * less messy way).
4648 	       */
4649 	      if (is_first && is_last)
4650 		g_snprintf (new_detail, 127, "%s", detail);
4651 	      else if (is_first)
4652 		g_snprintf (new_detail, 127, "%s_start", detail);
4653 	      else if (is_last)
4654 		g_snprintf (new_detail, 127, "%s_end", detail);
4655 	      else
4656 		g_snprintf (new_detail, 128, "%s_middle", detail);
4657 
4658 	      gtk_paint_flat_box (widget->style,
4659 				  event->window,
4660 				  state,
4661 				  GTK_SHADOW_NONE,
4662 				  &event->area,
4663 				  widget,
4664 				  new_detail,
4665 				  background_area.x,
4666 				  background_area.y,
4667 				  background_area.width,
4668 				  background_area.height);
4669 	    }
4670 	  else
4671 	    {
4672 	      gtk_paint_flat_box (widget->style,
4673 				  event->window,
4674 				  state,
4675 				  GTK_SHADOW_NONE,
4676 				  &event->area,
4677 				  widget,
4678 				  detail,
4679 				  background_area.x,
4680 				  background_area.y,
4681 				  background_area.width,
4682 				  background_area.height);
4683 	    }
4684 
4685 	  if (gtk_tree_view_is_expander_column (tree_view, column))
4686 	    {
4687 	      if (!rtl)
4688 		cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
4689 	      cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
4690 
4691               if (TREE_VIEW_DRAW_EXPANDERS(tree_view))
4692 	        {
4693 	          if (!rtl)
4694 		    cell_area.x += depth * tree_view->priv->expander_size;
4695 		  cell_area.width -= depth * tree_view->priv->expander_size;
4696 		}
4697 
4698               /* If we have an expander column, the highlight underline
4699                * starts with that column, so that it indicates which
4700                * level of the tree we're dropping at.
4701                */
4702               highlight_x = cell_area.x;
4703 	      expander_cell_width = cell_area.width;
4704 
4705 	      if (is_separator)
4706 		gtk_paint_hline (widget->style,
4707 				 event->window,
4708 				 state,
4709 				 &cell_area,
4710 				 widget,
4711 				 NULL,
4712 				 cell_area.x,
4713 				 cell_area.x + cell_area.width,
4714 				 cell_area.y + cell_area.height / 2);
4715 	      else
4716 		_gtk_tree_view_column_cell_render (column,
4717 						   event->window,
4718 						   &background_area,
4719 						   &cell_area,
4720 						   &event->area,
4721 						   flags);
4722 	      if (TREE_VIEW_DRAW_EXPANDERS(tree_view)
4723 		  && (node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
4724 		{
4725 		  if (!got_pointer)
4726 		    {
4727 		      gdk_window_get_pointer (tree_view->priv->bin_window,
4728 					      &pointer_x, &pointer_y, NULL);
4729 		      got_pointer = TRUE;
4730 		    }
4731 
4732 		  gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
4733 					    tree,
4734 					    node,
4735 					    pointer_x, pointer_y);
4736 		}
4737 	    }
4738 	  else
4739 	    {
4740 	      if (is_separator)
4741 		gtk_paint_hline (widget->style,
4742 				 event->window,
4743 				 state,
4744 				 &cell_area,
4745 				 widget,
4746 				 NULL,
4747 				 cell_area.x,
4748 				 cell_area.x + cell_area.width,
4749 				 cell_area.y + cell_area.height / 2);
4750 	      else
4751 		_gtk_tree_view_column_cell_render (column,
4752 						   event->window,
4753 						   &background_area,
4754 						   &cell_area,
4755 						   &event->area,
4756 						   flags);
4757 	    }
4758 
4759 	  if (draw_hgrid_lines)
4760 	    {
4761 	      if (background_area.y > 0)
4762                 gtk_tree_view_draw_line (tree_view, event->window,
4763                                          GTK_TREE_VIEW_GRID_LINE,
4764                                          background_area.x, background_area.y,
4765                                          background_area.x + background_area.width,
4766 			                 background_area.y);
4767 
4768 	      if (y_offset + max_height >= event->area.height)
4769                 gtk_tree_view_draw_line (tree_view, event->window,
4770                                          GTK_TREE_VIEW_GRID_LINE,
4771                                          background_area.x, background_area.y + max_height,
4772                                          background_area.x + background_area.width,
4773 			                 background_area.y + max_height);
4774 	    }
4775 
4776 	  if (gtk_tree_view_is_expander_column (tree_view, column) &&
4777 	      tree_view->priv->tree_lines_enabled)
4778 	    {
4779 	      gint x = background_area.x;
4780 	      gint mult = rtl ? -1 : 1;
4781 	      gint y0 = background_area.y;
4782 	      gint y1 = background_area.y + background_area.height/2;
4783 	      gint y2 = background_area.y + background_area.height;
4784 
4785 	      if (rtl)
4786 		x += background_area.width - 1;
4787 
4788 	      if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT
4789 		  && depth > 1)
4790 	        {
4791                   gtk_tree_view_draw_line (tree_view, event->window,
4792                                            GTK_TREE_VIEW_TREE_LINE,
4793                                            x + tree_view->priv->expander_size * (depth - 1.5) * mult,
4794                                            y1,
4795                                            x + tree_view->priv->expander_size * (depth - 1.1) * mult,
4796                                            y1);
4797 	        }
4798 	      else if (depth > 1)
4799 	        {
4800                   gtk_tree_view_draw_line (tree_view, event->window,
4801                                            GTK_TREE_VIEW_TREE_LINE,
4802                                            x + tree_view->priv->expander_size * (depth - 1.5) * mult,
4803                                            y1,
4804                                            x + tree_view->priv->expander_size * (depth - 0.5) * mult,
4805                                            y1);
4806 		}
4807 
4808 	      if (depth > 1)
4809 	        {
4810 		  gint i;
4811 		  GtkRBNode *tmp_node;
4812 		  GtkRBTree *tmp_tree;
4813 
4814 	          if (!_gtk_rbtree_next (tree, node))
4815                     gtk_tree_view_draw_line (tree_view, event->window,
4816                                              GTK_TREE_VIEW_TREE_LINE,
4817                                              x + tree_view->priv->expander_size * (depth - 1.5) * mult,
4818                                              y0,
4819                                              x + tree_view->priv->expander_size * (depth - 1.5) * mult,
4820                                              y1);
4821 		  else
4822                     gtk_tree_view_draw_line (tree_view, event->window,
4823                                              GTK_TREE_VIEW_TREE_LINE,
4824                                              x + tree_view->priv->expander_size * (depth - 1.5) * mult,
4825                                              y0,
4826                                              x + tree_view->priv->expander_size * (depth - 1.5) * mult,
4827                                              y2);
4828 
4829 		  tmp_node = tree->parent_node;
4830 		  tmp_tree = tree->parent_tree;
4831 
4832 		  for (i = depth - 2; i > 0; i--)
4833 		    {
4834 	              if (_gtk_rbtree_next (tmp_tree, tmp_node))
4835                         gtk_tree_view_draw_line (tree_view, event->window,
4836                                                  GTK_TREE_VIEW_TREE_LINE,
4837                                                  x + tree_view->priv->expander_size * (i - 0.5) * mult,
4838                                                  y0,
4839                                                  x + tree_view->priv->expander_size * (i - 0.5) * mult,
4840                                                  y2);
4841 
4842 		      tmp_node = tmp_tree->parent_node;
4843 		      tmp_tree = tmp_tree->parent_tree;
4844 		    }
4845 		}
4846 	    }
4847 
4848 	  if (node == cursor && has_special_cell &&
4849 	      ((column == tree_view->priv->focus_column &&
4850 		GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS) &&
4851 		gtk_widget_has_focus (widget)) ||
4852 	       (column == tree_view->priv->edited_column)))
4853 	    {
4854 	      _gtk_tree_view_column_cell_draw_focus (column,
4855 						     event->window,
4856 						     &background_area,
4857 						     &cell_area,
4858 						     &event->area,
4859 						     flags);
4860 	    }
4861 
4862 	  cell_offset += column->width;
4863 	}
4864 
4865       if (node == drag_highlight)
4866         {
4867           /* Draw indicator for the drop
4868            */
4869           gint highlight_y = -1;
4870 	  GtkRBTree *tree = NULL;
4871 	  GtkRBNode *node = NULL;
4872 	  gint width;
4873 
4874           switch (tree_view->priv->drag_dest_pos)
4875             {
4876             case GTK_TREE_VIEW_DROP_BEFORE:
4877               highlight_y = background_area.y - 1;
4878 	      if (highlight_y < 0)
4879 		      highlight_y = 0;
4880               break;
4881 
4882             case GTK_TREE_VIEW_DROP_AFTER:
4883               highlight_y = background_area.y + background_area.height - 1;
4884               break;
4885 
4886             case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
4887             case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
4888 	      _gtk_tree_view_find_node (tree_view, drag_dest_path, &tree, &node);
4889 
4890 	      if (tree == NULL)
4891 		break;
4892               width = gdk_window_get_width (tree_view->priv->bin_window);
4893 
4894 	      if (row_ending_details)
4895 		gtk_paint_focus (widget->style,
4896 			         tree_view->priv->bin_window,
4897 				 gtk_widget_get_state (widget),
4898 				 &event->area,
4899 				 widget,
4900 				 (is_first
4901 				  ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4902 				  : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4903 				 0, BACKGROUND_FIRST_PIXEL (tree_view, tree, node)
4904 				 - focus_line_width / 2,
4905 				 width, ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node))
4906 			       - focus_line_width + 1);
4907 	      else
4908 		gtk_paint_focus (widget->style,
4909 			         tree_view->priv->bin_window,
4910 				 gtk_widget_get_state (widget),
4911 				 &event->area,
4912 				 widget,
4913 				 "treeview-drop-indicator",
4914 				 0, BACKGROUND_FIRST_PIXEL (tree_view, tree, node)
4915 				 - focus_line_width / 2,
4916 				 width, ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node))
4917 				 - focus_line_width + 1);
4918               break;
4919             }
4920 
4921           if (highlight_y >= 0)
4922             {
4923               gtk_tree_view_draw_line (tree_view, event->window,
4924                                        GTK_TREE_VIEW_FOREGROUND_LINE,
4925                                        rtl ? highlight_x + expander_cell_width : highlight_x,
4926                                        highlight_y,
4927                                        rtl ? 0 : bin_window_width,
4928                                        highlight_y);
4929             }
4930         }
4931 
4932       /* draw the big row-spanning focus rectangle, if needed */
4933       if (!has_special_cell && node == cursor &&
4934 	  GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS) &&
4935 	  gtk_widget_has_focus (widget))
4936         {
4937 	  gint tmp_y, tmp_height;
4938 	  gint width;
4939 	  GtkStateType focus_rect_state;
4940 
4941 	  focus_rect_state =
4942 	    flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4943 	    (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4944 	     (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4945 	      GTK_STATE_NORMAL));
4946 
4947           width = gdk_window_get_width (tree_view->priv->bin_window);
4948 
4949 	  if (draw_hgrid_lines)
4950 	    {
4951 	      tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node) + grid_line_width / 2;
4952 	      tmp_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node)) - grid_line_width;
4953 	    }
4954 	  else
4955 	    {
4956 	      tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
4957 	      tmp_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
4958 	    }
4959 
4960 	  if (row_ending_details)
4961 	    gtk_paint_focus (widget->style,
4962 			     tree_view->priv->bin_window,
4963 			     focus_rect_state,
4964 			     &event->area,
4965 			     widget,
4966 			     (is_first
4967 			      ? (is_last ? "treeview" : "treeview-left" )
4968 			      : (is_last ? "treeview-right" : "treeview-middle" )),
4969 			     0, tmp_y,
4970 			     width, tmp_height);
4971 	  else
4972 	    gtk_paint_focus (widget->style,
4973 			     tree_view->priv->bin_window,
4974 			     focus_rect_state,
4975 			     &event->area,
4976 			     widget,
4977 			     "treeview",
4978 			     0, tmp_y,
4979 			     width, tmp_height);
4980 	}
4981 
4982       y_offset += max_height;
4983       if (node->children)
4984 	{
4985 	  GtkTreeIter parent = iter;
4986 	  gboolean has_child;
4987 
4988 	  tree = node->children;
4989 	  node = tree->root;
4990 
4991           g_assert (node != tree->nil);
4992 
4993 	  while (node->left != tree->nil)
4994 	    node = node->left;
4995 	  has_child = gtk_tree_model_iter_children (tree_view->priv->model,
4996 						    &iter,
4997 						    &parent);
4998 	  depth++;
4999 
5000 	  /* Sanity Check! */
5001 	  TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
5002 	}
5003       else
5004 	{
5005 	  gboolean done = FALSE;
5006 
5007 	  do
5008 	    {
5009 	      node = _gtk_rbtree_next (tree, node);
5010 	      if (node != NULL)
5011 		{
5012 		  gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5013 		  done = TRUE;
5014 
5015 		  /* Sanity Check! */
5016 		  TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
5017 		}
5018 	      else
5019 		{
5020 		  GtkTreeIter parent_iter = iter;
5021 		  gboolean has_parent;
5022 
5023 		  node = tree->parent_node;
5024 		  tree = tree->parent_tree;
5025 		  if (tree == NULL)
5026 		    /* we should go to done to free some memory */
5027 		    goto done;
5028 		  has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
5029 							   &iter,
5030 							   &parent_iter);
5031 		  depth--;
5032 
5033 		  /* Sanity check */
5034 		  TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
5035 		}
5036 	    }
5037 	  while (!done);
5038 	}
5039     }
5040   while (y_offset < event->area.height);
5041 
5042 done:
5043   gtk_tree_view_draw_grid_lines (tree_view, event, n_visible_columns);
5044 
5045  if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5046    {
5047      GdkRectangle *rectangles;
5048      gint n_rectangles;
5049 
5050      gdk_region_get_rectangles (event->region,
5051 				&rectangles,
5052 				&n_rectangles);
5053 
5054      while (n_rectangles--)
5055        gtk_tree_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
5056 
5057      g_free (rectangles);
5058    }
5059 
5060   if (cursor_path)
5061     gtk_tree_path_free (cursor_path);
5062 
5063   if (drag_dest_path)
5064     gtk_tree_path_free (drag_dest_path);
5065 
5066   return FALSE;
5067 }
5068 
5069 static gboolean
gtk_tree_view_expose(GtkWidget * widget,GdkEventExpose * event)5070 gtk_tree_view_expose (GtkWidget      *widget,
5071 		      GdkEventExpose *event)
5072 {
5073   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5074 
5075   if (event->window == tree_view->priv->bin_window)
5076     {
5077       gboolean retval;
5078       GList *tmp_list;
5079 
5080       retval = gtk_tree_view_bin_expose (widget, event);
5081 
5082       /* We can't just chain up to Container::expose as it will try to send the
5083        * event to the headers, so we handle propagating it to our children
5084        * (eg. widgets being edited) ourselves.
5085        */
5086       tmp_list = tree_view->priv->children;
5087       while (tmp_list)
5088 	{
5089 	  GtkTreeViewChild *child = tmp_list->data;
5090 	  tmp_list = tmp_list->next;
5091 
5092 	  gtk_container_propagate_expose (GTK_CONTAINER (tree_view), child->widget, event);
5093 	}
5094 
5095       return retval;
5096     }
5097 
5098   else if (event->window == tree_view->priv->header_window)
5099     {
5100       GList *list;
5101 
5102       for (list = tree_view->priv->columns; list != NULL; list = list->next)
5103 	{
5104 	  GtkTreeViewColumn *column = list->data;
5105 
5106 	  if (column == tree_view->priv->drag_column)
5107 	    continue;
5108 
5109 	  if (column->visible)
5110 	    gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
5111 					    column->button,
5112 					    event);
5113 	}
5114     }
5115   else if (event->window == tree_view->priv->drag_window)
5116     {
5117       gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
5118 				      tree_view->priv->drag_column->button,
5119 				      event);
5120     }
5121   return TRUE;
5122 }
5123 
5124 enum
5125 {
5126   DROP_HOME,
5127   DROP_RIGHT,
5128   DROP_LEFT,
5129   DROP_END
5130 };
5131 
5132 /* returns 0x1 when no column has been found -- yes it's hackish */
5133 static GtkTreeViewColumn *
gtk_tree_view_get_drop_column(GtkTreeView * tree_view,GtkTreeViewColumn * column,gint drop_position)5134 gtk_tree_view_get_drop_column (GtkTreeView       *tree_view,
5135 			       GtkTreeViewColumn *column,
5136 			       gint               drop_position)
5137 {
5138   GtkTreeViewColumn *left_column = NULL;
5139   GtkTreeViewColumn *cur_column = NULL;
5140   GList *tmp_list;
5141 
5142   if (!column->reorderable)
5143     return (GtkTreeViewColumn *)0x1;
5144 
5145   switch (drop_position)
5146     {
5147       case DROP_HOME:
5148 	/* find first column where we can drop */
5149 	tmp_list = tree_view->priv->columns;
5150 	if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5151 	  return (GtkTreeViewColumn *)0x1;
5152 
5153 	while (tmp_list)
5154 	  {
5155 	    g_assert (tmp_list);
5156 
5157 	    cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5158 	    tmp_list = tmp_list->next;
5159 
5160 	    if (left_column && left_column->visible == FALSE)
5161 	      continue;
5162 
5163 	    if (!tree_view->priv->column_drop_func)
5164 	      return left_column;
5165 
5166 	    if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5167 	      {
5168 		left_column = cur_column;
5169 		continue;
5170 	      }
5171 
5172 	    return left_column;
5173 	  }
5174 
5175 	if (!tree_view->priv->column_drop_func)
5176 	  return left_column;
5177 
5178 	if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
5179 	  return left_column;
5180 	else
5181 	  return (GtkTreeViewColumn *)0x1;
5182 	break;
5183 
5184       case DROP_RIGHT:
5185 	/* find first column after column where we can drop */
5186 	tmp_list = tree_view->priv->columns;
5187 
5188 	for (; tmp_list; tmp_list = tmp_list->next)
5189 	  if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5190 	    break;
5191 
5192 	if (!tmp_list || !tmp_list->next)
5193 	  return (GtkTreeViewColumn *)0x1;
5194 
5195 	tmp_list = tmp_list->next;
5196 	left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5197 	tmp_list = tmp_list->next;
5198 
5199 	while (tmp_list)
5200 	  {
5201 	    g_assert (tmp_list);
5202 
5203 	    cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5204 	    tmp_list = tmp_list->next;
5205 
5206 	    if (left_column && left_column->visible == FALSE)
5207 	      {
5208 		left_column = cur_column;
5209 		if (tmp_list)
5210 		  tmp_list = tmp_list->next;
5211 	        continue;
5212 	      }
5213 
5214 	    if (!tree_view->priv->column_drop_func)
5215 	      return left_column;
5216 
5217 	    if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5218 	      {
5219 		left_column = cur_column;
5220 		continue;
5221 	      }
5222 
5223 	    return left_column;
5224 	  }
5225 
5226 	if (!tree_view->priv->column_drop_func)
5227 	  return left_column;
5228 
5229 	if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
5230 	  return left_column;
5231 	else
5232 	  return (GtkTreeViewColumn *)0x1;
5233 	break;
5234 
5235       case DROP_LEFT:
5236 	/* find first column before column where we can drop */
5237 	tmp_list = tree_view->priv->columns;
5238 
5239 	for (; tmp_list; tmp_list = tmp_list->next)
5240 	  if (GTK_TREE_VIEW_COLUMN (tmp_list->data) == column)
5241 	    break;
5242 
5243 	if (!tmp_list || !tmp_list->prev)
5244 	  return (GtkTreeViewColumn *)0x1;
5245 
5246 	tmp_list = tmp_list->prev;
5247 	cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5248 	tmp_list = tmp_list->prev;
5249 
5250 	while (tmp_list)
5251 	  {
5252 	    g_assert (tmp_list);
5253 
5254 	    left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5255 
5256 	    if (left_column && !left_column->visible)
5257 	      {
5258 		/*if (!tmp_list->prev)
5259 		  return (GtkTreeViewColumn *)0x1;
5260 		  */
5261 /*
5262 		cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->prev->data);
5263 		tmp_list = tmp_list->prev->prev;
5264 		continue;*/
5265 
5266 		cur_column = left_column;
5267 		if (tmp_list)
5268 		  tmp_list = tmp_list->prev;
5269 		continue;
5270 	      }
5271 
5272 	    if (!tree_view->priv->column_drop_func)
5273 	      return left_column;
5274 
5275 	    if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5276 	      return left_column;
5277 
5278 	    cur_column = left_column;
5279 	    tmp_list = tmp_list->prev;
5280 	  }
5281 
5282 	if (!tree_view->priv->column_drop_func)
5283 	  return NULL;
5284 
5285 	if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
5286 	  return NULL;
5287 	else
5288 	  return (GtkTreeViewColumn *)0x1;
5289 	break;
5290 
5291       case DROP_END:
5292 	/* same as DROP_HOME case, but doing it backwards */
5293 	tmp_list = g_list_last (tree_view->priv->columns);
5294 	cur_column = NULL;
5295 
5296 	if (column == GTK_TREE_VIEW_COLUMN (tmp_list->data))
5297 	  return (GtkTreeViewColumn *)0x1;
5298 
5299 	while (tmp_list)
5300 	  {
5301 	    g_assert (tmp_list);
5302 
5303 	    left_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
5304 
5305 	    if (left_column && !left_column->visible)
5306 	      {
5307 		cur_column = left_column;
5308 		tmp_list = tmp_list->prev;
5309 	      }
5310 
5311 	    if (!tree_view->priv->column_drop_func)
5312 	      return left_column;
5313 
5314 	    if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
5315 	      return left_column;
5316 
5317 	    cur_column = left_column;
5318 	    tmp_list = tmp_list->prev;
5319 	  }
5320 
5321 	if (!tree_view->priv->column_drop_func)
5322 	  return NULL;
5323 
5324 	if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
5325 	  return NULL;
5326 	else
5327 	  return (GtkTreeViewColumn *)0x1;
5328 	break;
5329     }
5330 
5331   return (GtkTreeViewColumn *)0x1;
5332 }
5333 
5334 static gboolean
gtk_tree_view_key_press(GtkWidget * widget,GdkEventKey * event)5335 gtk_tree_view_key_press (GtkWidget   *widget,
5336 			 GdkEventKey *event)
5337 {
5338   GtkTreeView *tree_view = (GtkTreeView *) widget;
5339 
5340   if (tree_view->priv->rubber_band_status)
5341     {
5342       if (event->keyval == GDK_Escape)
5343 	gtk_tree_view_stop_rubber_band (tree_view);
5344 
5345       return TRUE;
5346     }
5347 
5348   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG))
5349     {
5350       if (event->keyval == GDK_Escape)
5351 	{
5352 	  tree_view->priv->cur_reorder = NULL;
5353 	  gtk_tree_view_button_release_drag_column (widget, NULL);
5354 	}
5355       return TRUE;
5356     }
5357 
5358   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
5359     {
5360       GList *focus_column;
5361       gboolean rtl;
5362 
5363       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
5364 
5365       for (focus_column = tree_view->priv->columns;
5366            focus_column;
5367            focus_column = focus_column->next)
5368         {
5369           GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5370 
5371           if (gtk_widget_has_focus (column->button))
5372             break;
5373         }
5374 
5375       if (focus_column &&
5376           (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
5377           (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
5378            || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
5379         {
5380           GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5381 
5382           if (!column->resizable)
5383             {
5384               gtk_widget_error_bell (widget);
5385               return TRUE;
5386             }
5387 
5388           if (event->keyval == (rtl ? GDK_Right : GDK_Left)
5389               || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
5390             {
5391               gint old_width = column->resized_width;
5392 
5393               column->resized_width = MAX (column->resized_width,
5394                                            column->width);
5395               column->resized_width -= 2;
5396               if (column->resized_width < 0)
5397                 column->resized_width = 0;
5398 
5399               if (column->min_width == -1)
5400                 column->resized_width = MAX (column->button->requisition.width,
5401                                              column->resized_width);
5402               else
5403                 column->resized_width = MAX (column->min_width,
5404                                              column->resized_width);
5405 
5406               if (column->max_width != -1)
5407                 column->resized_width = MIN (column->resized_width,
5408                                              column->max_width);
5409 
5410               column->use_resized_width = TRUE;
5411 
5412               if (column->resized_width != old_width)
5413                 gtk_widget_queue_resize (widget);
5414               else
5415                 gtk_widget_error_bell (widget);
5416             }
5417           else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
5418                    || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
5419             {
5420               gint old_width = column->resized_width;
5421 
5422               column->resized_width = MAX (column->resized_width,
5423                                            column->width);
5424               column->resized_width += 2;
5425 
5426               if (column->max_width != -1)
5427                 column->resized_width = MIN (column->resized_width,
5428                                              column->max_width);
5429 
5430               column->use_resized_width = TRUE;
5431 
5432               if (column->resized_width != old_width)
5433                 gtk_widget_queue_resize (widget);
5434               else
5435                 gtk_widget_error_bell (widget);
5436             }
5437 
5438           return TRUE;
5439         }
5440 
5441       if (focus_column &&
5442           (event->state & GDK_MOD1_MASK) &&
5443           (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
5444            || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
5445            || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
5446            || event->keyval == GDK_End || event->keyval == GDK_KP_End))
5447         {
5448           GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (focus_column->data);
5449 
5450           if (event->keyval == (rtl ? GDK_Right : GDK_Left)
5451               || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
5452             {
5453               GtkTreeViewColumn *col;
5454               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_LEFT);
5455               if (col != (GtkTreeViewColumn *)0x1)
5456                 gtk_tree_view_move_column_after (tree_view, column, col);
5457               else
5458                 gtk_widget_error_bell (widget);
5459             }
5460           else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
5461                    || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
5462             {
5463               GtkTreeViewColumn *col;
5464               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_RIGHT);
5465               if (col != (GtkTreeViewColumn *)0x1)
5466                 gtk_tree_view_move_column_after (tree_view, column, col);
5467               else
5468                 gtk_widget_error_bell (widget);
5469             }
5470           else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
5471             {
5472               GtkTreeViewColumn *col;
5473               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_HOME);
5474               if (col != (GtkTreeViewColumn *)0x1)
5475                 gtk_tree_view_move_column_after (tree_view, column, col);
5476               else
5477                 gtk_widget_error_bell (widget);
5478             }
5479           else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
5480             {
5481               GtkTreeViewColumn *col;
5482               col = gtk_tree_view_get_drop_column (tree_view, column, DROP_END);
5483               if (col != (GtkTreeViewColumn *)0x1)
5484                 gtk_tree_view_move_column_after (tree_view, column, col);
5485               else
5486                 gtk_widget_error_bell (widget);
5487             }
5488 
5489           return TRUE;
5490         }
5491     }
5492 
5493   /* Chain up to the parent class.  It handles the keybindings. */
5494   if (GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->key_press_event (widget, event))
5495     return TRUE;
5496 
5497   if (tree_view->priv->search_entry_avoid_unhandled_binding)
5498     {
5499       tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
5500       return FALSE;
5501     }
5502 
5503   /* We pass the event to the search_entry.  If its text changes, then we start
5504    * the typeahead find capabilities. */
5505   if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
5506       && tree_view->priv->enable_search
5507       && !tree_view->priv->search_custom_entry_set)
5508     {
5509       GdkEvent *new_event;
5510       char *old_text;
5511       const char *new_text;
5512       gboolean retval;
5513       GdkScreen *screen;
5514       gboolean text_modified;
5515       gulong popup_menu_id;
5516 
5517       gtk_tree_view_ensure_interactive_directory (tree_view);
5518 
5519       /* Make a copy of the current text */
5520       old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
5521       new_event = gdk_event_copy ((GdkEvent *) event);
5522       g_object_unref (((GdkEventKey *) new_event)->window);
5523       ((GdkEventKey *) new_event)->window = g_object_ref (tree_view->priv->search_window->window);
5524       gtk_widget_realize (tree_view->priv->search_window);
5525 
5526       popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
5527 					"popup-menu", G_CALLBACK (gtk_true),
5528                                         NULL);
5529 
5530       /* Move the entry off screen */
5531       screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
5532       gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
5533 		       gdk_screen_get_width (screen) + 1,
5534 		       gdk_screen_get_height (screen) + 1);
5535       gtk_widget_show (tree_view->priv->search_window);
5536 
5537       /* Send the event to the window.  If the preedit_changed signal is emitted
5538        * during this event, we will set priv->imcontext_changed  */
5539       tree_view->priv->imcontext_changed = FALSE;
5540       retval = gtk_widget_event (tree_view->priv->search_window, new_event);
5541       gdk_event_free (new_event);
5542       gtk_widget_hide (tree_view->priv->search_window);
5543 
5544       g_signal_handler_disconnect (tree_view->priv->search_entry,
5545 				   popup_menu_id);
5546 
5547       /* We check to make sure that the entry tried to handle the text, and that
5548        * the text has changed.
5549        */
5550       new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
5551       text_modified = strcmp (old_text, new_text) != 0;
5552       g_free (old_text);
5553       if (tree_view->priv->imcontext_changed ||    /* we're in a preedit */
5554 	  (retval && text_modified))               /* ...or the text was modified */
5555 	{
5556 	  if (gtk_tree_view_real_start_interactive_search (tree_view, FALSE))
5557 	    {
5558 	      gtk_widget_grab_focus (GTK_WIDGET (tree_view));
5559 	      return TRUE;
5560 	    }
5561 	  else
5562 	    {
5563 	      gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
5564 	      return FALSE;
5565 	    }
5566 	}
5567     }
5568 
5569   return FALSE;
5570 }
5571 
5572 static gboolean
gtk_tree_view_key_release(GtkWidget * widget,GdkEventKey * event)5573 gtk_tree_view_key_release (GtkWidget   *widget,
5574 			   GdkEventKey *event)
5575 {
5576   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5577 
5578   if (tree_view->priv->rubber_band_status)
5579     return TRUE;
5580 
5581   return GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->key_release_event (widget, event);
5582 }
5583 
5584 /* FIXME Is this function necessary? Can I get an enter_notify event
5585  * w/o either an expose event or a mouse motion event?
5586  */
5587 static gboolean
gtk_tree_view_enter_notify(GtkWidget * widget,GdkEventCrossing * event)5588 gtk_tree_view_enter_notify (GtkWidget        *widget,
5589 			    GdkEventCrossing *event)
5590 {
5591   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
5592   GtkRBTree *tree;
5593   GtkRBNode *node;
5594   gint new_y;
5595 
5596   /* Sanity check it */
5597   if (event->window != tree_view->priv->bin_window)
5598     return FALSE;
5599 
5600   if (tree_view->priv->tree == NULL)
5601     return FALSE;
5602 
5603   if (event->mode == GDK_CROSSING_GRAB ||
5604       event->mode == GDK_CROSSING_GTK_GRAB ||
5605       event->mode == GDK_CROSSING_GTK_UNGRAB ||
5606       event->mode == GDK_CROSSING_STATE_CHANGED)
5607     return TRUE;
5608 
5609   /* find the node internally */
5610   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5611   if (new_y < 0)
5612     new_y = 0;
5613   _gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node);
5614 
5615   tree_view->priv->event_last_x = event->x;
5616   tree_view->priv->event_last_y = event->y;
5617 
5618   if ((tree_view->priv->button_pressed_node == NULL) ||
5619       (tree_view->priv->button_pressed_node == node))
5620     prelight_or_select (tree_view, tree, node, event->x, event->y);
5621 
5622   return TRUE;
5623 }
5624 
5625 static gboolean
gtk_tree_view_leave_notify(GtkWidget * widget,GdkEventCrossing * event)5626 gtk_tree_view_leave_notify (GtkWidget        *widget,
5627 			    GdkEventCrossing *event)
5628 {
5629   GtkTreeView *tree_view;
5630 
5631   if (event->mode == GDK_CROSSING_GRAB)
5632     return TRUE;
5633 
5634   tree_view = GTK_TREE_VIEW (widget);
5635 
5636   if (tree_view->priv->prelight_node)
5637     _gtk_tree_view_queue_draw_node (tree_view,
5638                                    tree_view->priv->prelight_tree,
5639                                    tree_view->priv->prelight_node,
5640                                    NULL);
5641 
5642   tree_view->priv->event_last_x = -10000;
5643   tree_view->priv->event_last_y = -10000;
5644 
5645   prelight_or_select (tree_view,
5646 		      NULL, NULL,
5647 		      -1000, -1000); /* coords not possibly over an arrow */
5648 
5649   return TRUE;
5650 }
5651 
5652 
5653 static gint
gtk_tree_view_focus_out(GtkWidget * widget,GdkEventFocus * event)5654 gtk_tree_view_focus_out (GtkWidget     *widget,
5655 			 GdkEventFocus *event)
5656 {
5657   GtkTreeView *tree_view;
5658 
5659   tree_view = GTK_TREE_VIEW (widget);
5660 
5661   gtk_widget_queue_draw (widget);
5662 
5663   /* destroy interactive search dialog */
5664   if (tree_view->priv->search_window)
5665     gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5666 
5667   return FALSE;
5668 }
5669 
5670 
5671 /* Incremental Reflow
5672  */
5673 
5674 static void
gtk_tree_view_node_queue_redraw(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)5675 gtk_tree_view_node_queue_redraw (GtkTreeView *tree_view,
5676 				 GtkRBTree   *tree,
5677 				 GtkRBNode   *node)
5678 {
5679   gint y;
5680 
5681   y = _gtk_rbtree_node_find_offset (tree, node)
5682     - tree_view->priv->vadjustment->value
5683     + TREE_VIEW_HEADER_HEIGHT (tree_view);
5684 
5685   gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5686 			      0, y,
5687 			      GTK_WIDGET (tree_view)->allocation.width,
5688 			      GTK_RBNODE_GET_HEIGHT (node));
5689 }
5690 
5691 static gboolean
node_is_visible(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)5692 node_is_visible (GtkTreeView *tree_view,
5693 		 GtkRBTree   *tree,
5694 		 GtkRBNode   *node)
5695 {
5696   int y;
5697   int height;
5698 
5699   y = _gtk_rbtree_node_find_offset (tree, node);
5700   height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
5701 
5702   if (y >= tree_view->priv->vadjustment->value &&
5703       y + height <= (tree_view->priv->vadjustment->value
5704 	             + tree_view->priv->vadjustment->page_size))
5705     return TRUE;
5706 
5707   return FALSE;
5708 }
5709 
5710 /* Returns TRUE if it updated the size
5711  */
5712 static gboolean
validate_row(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,GtkTreeIter * iter,GtkTreePath * path)5713 validate_row (GtkTreeView *tree_view,
5714 	      GtkRBTree   *tree,
5715 	      GtkRBNode   *node,
5716 	      GtkTreeIter *iter,
5717 	      GtkTreePath *path)
5718 {
5719   GtkTreeViewColumn *column;
5720   GList *list, *first_column, *last_column;
5721   gint height = 0;
5722   gint horizontal_separator;
5723   gint vertical_separator;
5724   gint focus_line_width;
5725   gint depth = gtk_tree_path_get_depth (path);
5726   gboolean retval = FALSE;
5727   gboolean is_separator = FALSE;
5728   gboolean draw_vgrid_lines, draw_hgrid_lines;
5729   gint focus_pad;
5730   gint grid_line_width;
5731   gboolean wide_separators;
5732   gint separator_height;
5733 
5734   /* double check the row needs validating */
5735   if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) &&
5736       ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
5737     return FALSE;
5738 
5739   is_separator = row_is_separator (tree_view, iter, NULL);
5740 
5741   gtk_widget_style_get (GTK_WIDGET (tree_view),
5742 			"focus-padding", &focus_pad,
5743 			"focus-line-width", &focus_line_width,
5744 			"horizontal-separator", &horizontal_separator,
5745 			"vertical-separator", &vertical_separator,
5746 			"grid-line-width", &grid_line_width,
5747                         "wide-separators",  &wide_separators,
5748                         "separator-height", &separator_height,
5749 			NULL);
5750 
5751   draw_vgrid_lines =
5752     tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_VERTICAL
5753     || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
5754   draw_hgrid_lines =
5755     tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_HORIZONTAL
5756     || tree_view->priv->grid_lines == GTK_TREE_VIEW_GRID_LINES_BOTH;
5757 
5758   for (last_column = g_list_last (tree_view->priv->columns);
5759        last_column && !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible);
5760        last_column = last_column->prev)
5761     ;
5762 
5763   for (first_column = g_list_first (tree_view->priv->columns);
5764        first_column && !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible);
5765        first_column = first_column->next)
5766     ;
5767 
5768   for (list = tree_view->priv->columns; list; list = list->next)
5769     {
5770       gint tmp_width;
5771       gint tmp_height;
5772 
5773       column = list->data;
5774 
5775       if (! column->visible)
5776 	continue;
5777 
5778       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) && !column->dirty)
5779 	continue;
5780 
5781       gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter,
5782 					       GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
5783 					       node->children?TRUE:FALSE);
5784       gtk_tree_view_column_cell_get_size (column,
5785 					  NULL, NULL, NULL,
5786 					  &tmp_width, &tmp_height);
5787 
5788       if (!is_separator)
5789 	{
5790           tmp_height += vertical_separator;
5791 	  height = MAX (height, tmp_height);
5792 	  height = MAX (height, tree_view->priv->expander_size);
5793 	}
5794       else
5795         {
5796           if (wide_separators)
5797             height = separator_height + 2 * focus_pad;
5798           else
5799             height = 2 + 2 * focus_pad;
5800         }
5801 
5802       if (gtk_tree_view_is_expander_column (tree_view, column))
5803         {
5804 	  tmp_width = tmp_width + horizontal_separator + (depth - 1) * tree_view->priv->level_indentation;
5805 
5806 	  if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
5807 	    tmp_width += depth * tree_view->priv->expander_size;
5808 	}
5809       else
5810 	tmp_width = tmp_width + horizontal_separator;
5811 
5812       if (draw_vgrid_lines)
5813         {
5814 	  if (list->data == first_column || list->data == last_column)
5815 	    tmp_width += grid_line_width / 2.0;
5816 	  else
5817 	    tmp_width += grid_line_width;
5818 	}
5819 
5820       if (tmp_width > column->requested_width)
5821 	{
5822 	  retval = TRUE;
5823 	  column->requested_width = tmp_width;
5824 	}
5825     }
5826 
5827   if (draw_hgrid_lines)
5828     height += grid_line_width;
5829 
5830   if (height != GTK_RBNODE_GET_HEIGHT (node))
5831     {
5832       retval = TRUE;
5833       _gtk_rbtree_node_set_height (tree, node, height);
5834     }
5835   _gtk_rbtree_node_mark_valid (tree, node);
5836   tree_view->priv->post_validation_flag = TRUE;
5837 
5838   return retval;
5839 }
5840 
5841 
5842 static void
validate_visible_area(GtkTreeView * tree_view)5843 validate_visible_area (GtkTreeView *tree_view)
5844 {
5845   GtkTreePath *path = NULL;
5846   GtkTreePath *above_path = NULL;
5847   GtkTreeIter iter;
5848   GtkRBTree *tree = NULL;
5849   GtkRBNode *node = NULL;
5850   gboolean need_redraw = FALSE;
5851   gboolean size_changed = FALSE;
5852   gint total_height;
5853   gint area_above = 0;
5854   gint area_below = 0;
5855 
5856   if (tree_view->priv->tree == NULL)
5857     return;
5858 
5859   if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID) &&
5860       tree_view->priv->scroll_to_path == NULL)
5861     return;
5862 
5863   total_height = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5864 
5865   if (total_height == 0)
5866     return;
5867 
5868   /* First, we check to see if we need to scroll anywhere
5869    */
5870   if (tree_view->priv->scroll_to_path)
5871     {
5872       path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5873       if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node))
5874 	{
5875           /* we are going to scroll, and will update dy */
5876 	  gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5877 	  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
5878 	      GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
5879 	    {
5880 	      _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
5881 	      if (validate_row (tree_view, tree, node, &iter, path))
5882 		size_changed = TRUE;
5883 	    }
5884 
5885 	  if (tree_view->priv->scroll_to_use_align)
5886 	    {
5887 	      gint height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
5888 	      area_above = (total_height - height) *
5889 		tree_view->priv->scroll_to_row_align;
5890 	      area_below = total_height - area_above - height;
5891 	      area_above = MAX (area_above, 0);
5892 	      area_below = MAX (area_below, 0);
5893 	    }
5894 	  else
5895 	    {
5896 	      /* two cases:
5897 	       * 1) row not visible
5898 	       * 2) row visible
5899 	       */
5900 	      gint dy;
5901 	      gint height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
5902 
5903 	      dy = _gtk_rbtree_node_find_offset (tree, node);
5904 
5905 	      if (dy >= tree_view->priv->vadjustment->value &&
5906 		  dy + height <= (tree_view->priv->vadjustment->value
5907 		                  + tree_view->priv->vadjustment->page_size))
5908 	        {
5909 		  /* row visible: keep the row at the same position */
5910 		  area_above = dy - tree_view->priv->vadjustment->value;
5911 		  area_below = (tree_view->priv->vadjustment->value +
5912 		                tree_view->priv->vadjustment->page_size)
5913 		               - dy - height;
5914 		}
5915 	      else
5916 	        {
5917 		  /* row not visible */
5918 		  if (dy >= 0
5919 		      && dy + height <= tree_view->priv->vadjustment->page_size)
5920 		    {
5921 		      /* row at the beginning -- fixed */
5922 		      area_above = dy;
5923 		      area_below = tree_view->priv->vadjustment->page_size
5924 				   - area_above - height;
5925 		    }
5926 		  else if (dy >= (tree_view->priv->vadjustment->upper -
5927 			          tree_view->priv->vadjustment->page_size))
5928 		    {
5929 		      /* row at the end -- fixed */
5930 		      area_above = dy - (tree_view->priv->vadjustment->upper -
5931 			           tree_view->priv->vadjustment->page_size);
5932                       area_below = tree_view->priv->vadjustment->page_size -
5933                                    area_above - height;
5934 
5935                       if (area_below < 0)
5936                         {
5937 			  area_above = tree_view->priv->vadjustment->page_size - height;
5938                           area_below = 0;
5939                         }
5940 		    }
5941 		  else
5942 		    {
5943 		      /* row somewhere in the middle, bring it to the top
5944 		       * of the view
5945 		       */
5946 		      area_above = 0;
5947 		      area_below = total_height - height;
5948 		    }
5949 		}
5950 	    }
5951 	}
5952       else
5953 	/* the scroll to isn't valid; ignore it.
5954 	 */
5955 	{
5956 	  if (tree_view->priv->scroll_to_path && !path)
5957 	    {
5958 	      gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5959 	      tree_view->priv->scroll_to_path = NULL;
5960 	    }
5961 	  if (path)
5962 	    gtk_tree_path_free (path);
5963 	  path = NULL;
5964 	}
5965     }
5966 
5967   /* We didn't have a scroll_to set, so we just handle things normally
5968    */
5969   if (path == NULL)
5970     {
5971       gint offset;
5972 
5973       offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
5974 					TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
5975 					&tree, &node);
5976       if (node == NULL)
5977 	{
5978 	  /* In this case, nothing has been validated */
5979 	  path = gtk_tree_path_new_first ();
5980 	  _gtk_tree_view_find_node (tree_view, path, &tree, &node);
5981 	}
5982       else
5983 	{
5984 	  path = _gtk_tree_view_find_path (tree_view, tree, node);
5985 	  total_height += offset;
5986 	}
5987 
5988       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5989 
5990       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
5991 	  GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
5992 	{
5993 	  _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
5994 	  if (validate_row (tree_view, tree, node, &iter, path))
5995 	    size_changed = TRUE;
5996 	}
5997       area_above = 0;
5998       area_below = total_height - ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
5999     }
6000 
6001   above_path = gtk_tree_path_copy (path);
6002 
6003   /* if we do not validate any row above the new top_row, we will make sure
6004    * that the row immediately above top_row has been validated. (if we do not
6005    * do this, _gtk_rbtree_find_offset will find the row above top_row, because
6006    * when invalidated that row's height will be zero. and this will mess up
6007    * scrolling).
6008    */
6009   if (area_above == 0)
6010     {
6011       GtkRBTree *tmptree;
6012       GtkRBNode *tmpnode;
6013 
6014       _gtk_tree_view_find_node (tree_view, above_path, &tmptree, &tmpnode);
6015       _gtk_rbtree_prev_full (tmptree, tmpnode, &tmptree, &tmpnode);
6016 
6017       if (tmpnode)
6018         {
6019 	  GtkTreePath *tmppath;
6020 	  GtkTreeIter tmpiter;
6021 
6022 	  tmppath = _gtk_tree_view_find_path (tree_view, tmptree, tmpnode);
6023 	  gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, tmppath);
6024 
6025 	  if (GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_INVALID) ||
6026 	      GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_COLUMN_INVALID))
6027 	    {
6028 	      _gtk_tree_view_queue_draw_node (tree_view, tmptree, tmpnode, NULL);
6029 	      if (validate_row (tree_view, tmptree, tmpnode, &tmpiter, tmppath))
6030 		size_changed = TRUE;
6031 	    }
6032 
6033 	  gtk_tree_path_free (tmppath);
6034 	}
6035     }
6036 
6037   /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
6038    * backwards is much slower then forward, as there is no iter_prev function.
6039    * We go forwards first in case we run out of tree.  Then we go backwards to
6040    * fill out the top.
6041    */
6042   while (node && area_below > 0)
6043     {
6044       if (node->children)
6045 	{
6046 	  GtkTreeIter parent = iter;
6047 	  gboolean has_child;
6048 
6049 	  tree = node->children;
6050 	  node = tree->root;
6051 
6052           g_assert (node != tree->nil);
6053 
6054 	  while (node->left != tree->nil)
6055 	    node = node->left;
6056 	  has_child = gtk_tree_model_iter_children (tree_view->priv->model,
6057 						    &iter,
6058 						    &parent);
6059 	  TREE_VIEW_INTERNAL_ASSERT_VOID (has_child);
6060 	  gtk_tree_path_down (path);
6061 	}
6062       else
6063 	{
6064 	  gboolean done = FALSE;
6065 	  do
6066 	    {
6067 	      node = _gtk_rbtree_next (tree, node);
6068 	      if (node != NULL)
6069 		{
6070 		  gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
6071 		  done = TRUE;
6072 		  gtk_tree_path_next (path);
6073 
6074 		  /* Sanity Check! */
6075 		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
6076 		}
6077 	      else
6078 		{
6079 		  GtkTreeIter parent_iter = iter;
6080 		  gboolean has_parent;
6081 
6082 		  node = tree->parent_node;
6083 		  tree = tree->parent_tree;
6084 		  if (tree == NULL)
6085 		    break;
6086 		  has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
6087 							   &iter,
6088 							   &parent_iter);
6089 		  gtk_tree_path_up (path);
6090 
6091 		  /* Sanity check */
6092 		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
6093 		}
6094 	    }
6095 	  while (!done);
6096 	}
6097 
6098       if (!node)
6099         break;
6100 
6101       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6102 	  GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6103 	{
6104 	  _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6105 	  if (validate_row (tree_view, tree, node, &iter, path))
6106 	      size_changed = TRUE;
6107 	}
6108 
6109       area_below -= ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
6110     }
6111   gtk_tree_path_free (path);
6112 
6113   /* If we ran out of tree, and have extra area_below left, we need to add it
6114    * to area_above */
6115   if (area_below > 0)
6116     area_above += area_below;
6117 
6118   _gtk_tree_view_find_node (tree_view, above_path, &tree, &node);
6119 
6120   /* We walk backwards */
6121   while (area_above > 0)
6122     {
6123       _gtk_rbtree_prev_full (tree, node, &tree, &node);
6124 
6125       /* Always find the new path in the tree.  We cannot just assume
6126        * a gtk_tree_path_prev() is enough here, as there might be children
6127        * in between this node and the previous sibling node.  If this
6128        * appears to be a performance hotspot in profiles, we can look into
6129        * intrigate logic for keeping path, node and iter in sync like
6130        * we do for forward walks.  (Which will be hard because of the lacking
6131        * iter_prev).
6132        */
6133 
6134       if (node == NULL)
6135 	break;
6136 
6137       gtk_tree_path_free (above_path);
6138       above_path = _gtk_tree_view_find_path (tree_view, tree, node);
6139 
6140       gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
6141 
6142       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6143 	  GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6144 	{
6145 	  _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
6146 	  if (validate_row (tree_view, tree, node, &iter, above_path))
6147 	    size_changed = TRUE;
6148 	}
6149       area_above -= ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
6150     }
6151 
6152   /* if we scrolled to a path, we need to set the dy here,
6153    * and sync the top row accordingly
6154    */
6155   if (tree_view->priv->scroll_to_path)
6156     {
6157       gtk_tree_view_set_top_row (tree_view, above_path, -area_above);
6158       gtk_tree_view_top_row_to_dy (tree_view);
6159 
6160       need_redraw = TRUE;
6161     }
6162   else if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
6163     {
6164       /* when we are not scrolling, we should never set dy to something
6165        * else than zero. we update top_row to be in sync with dy = 0.
6166        */
6167       gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
6168       gtk_tree_view_dy_to_top_row (tree_view);
6169     }
6170   else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
6171     {
6172       gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), tree_view->priv->height - tree_view->priv->vadjustment->page_size);
6173       gtk_tree_view_dy_to_top_row (tree_view);
6174     }
6175   else
6176     gtk_tree_view_top_row_to_dy (tree_view);
6177 
6178   /* update width/height and queue a resize */
6179   if (size_changed)
6180     {
6181       GtkRequisition requisition;
6182 
6183       /* We temporarily guess a size, under the assumption that it will be the
6184        * same when we get our next size_allocate.  If we don't do this, we'll be
6185        * in an inconsistent state if we call top_row_to_dy. */
6186 
6187       gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
6188       tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
6189       tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
6190       gtk_adjustment_changed (tree_view->priv->hadjustment);
6191       gtk_adjustment_changed (tree_view->priv->vadjustment);
6192       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6193     }
6194 
6195   if (tree_view->priv->scroll_to_path)
6196     {
6197       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
6198       tree_view->priv->scroll_to_path = NULL;
6199     }
6200 
6201   if (above_path)
6202     gtk_tree_path_free (above_path);
6203 
6204   if (tree_view->priv->scroll_to_column)
6205     {
6206       tree_view->priv->scroll_to_column = NULL;
6207     }
6208   if (need_redraw)
6209     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
6210 }
6211 
6212 static void
initialize_fixed_height_mode(GtkTreeView * tree_view)6213 initialize_fixed_height_mode (GtkTreeView *tree_view)
6214 {
6215   if (!tree_view->priv->tree)
6216     return;
6217 
6218   if (tree_view->priv->fixed_height < 0)
6219     {
6220       GtkTreeIter iter;
6221       GtkTreePath *path;
6222 
6223       GtkRBTree *tree = NULL;
6224       GtkRBNode *node = NULL;
6225 
6226       tree = tree_view->priv->tree;
6227       node = tree->root;
6228 
6229       path = _gtk_tree_view_find_path (tree_view, tree, node);
6230       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6231 
6232       validate_row (tree_view, tree, node, &iter, path);
6233 
6234       gtk_tree_path_free (path);
6235 
6236       tree_view->priv->fixed_height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
6237     }
6238 
6239    _gtk_rbtree_set_fixed_height (tree_view->priv->tree,
6240                                  tree_view->priv->fixed_height, TRUE);
6241 }
6242 
6243 /* Our strategy for finding nodes to validate is a little convoluted.  We find
6244  * the left-most uninvalidated node.  We then try walking right, validating
6245  * nodes.  Once we find a valid node, we repeat the previous process of finding
6246  * the first invalid node.
6247  */
6248 
6249 static gboolean
do_validate_rows(GtkTreeView * tree_view,gboolean queue_resize)6250 do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize)
6251 {
6252   GtkRBTree *tree = NULL;
6253   GtkRBNode *node = NULL;
6254   gboolean validated_area = FALSE;
6255   gint retval = TRUE;
6256   GtkTreePath *path = NULL;
6257   GtkTreeIter iter;
6258   GTimer *timer;
6259   gint i = 0;
6260 
6261   gint prev_height = -1;
6262   gboolean fixed_height = TRUE;
6263 
6264   g_assert (tree_view);
6265 
6266   if (tree_view->priv->tree == NULL)
6267       return FALSE;
6268 
6269   if (tree_view->priv->fixed_height_mode)
6270     {
6271       if (tree_view->priv->fixed_height < 0)
6272         initialize_fixed_height_mode (tree_view);
6273 
6274       return FALSE;
6275     }
6276 
6277   timer = g_timer_new ();
6278   g_timer_start (timer);
6279 
6280   do
6281     {
6282       if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID))
6283 	{
6284 	  retval = FALSE;
6285 	  goto done;
6286 	}
6287 
6288       if (path != NULL)
6289 	{
6290 	  node = _gtk_rbtree_next (tree, node);
6291 	  if (node != NULL)
6292 	    {
6293 	      TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_next (tree_view->priv->model, &iter), FALSE);
6294 	      gtk_tree_path_next (path);
6295 	    }
6296 	  else
6297 	    {
6298 	      gtk_tree_path_free (path);
6299 	      path = NULL;
6300 	    }
6301 	}
6302 
6303       if (path == NULL)
6304 	{
6305 	  tree = tree_view->priv->tree;
6306 	  node = tree_view->priv->tree->root;
6307 
6308 	  g_assert (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_DESCENDANTS_INVALID));
6309 
6310 	  do
6311 	    {
6312 	      if (node->left != tree->nil &&
6313 		  GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID))
6314 		{
6315 		  node = node->left;
6316 		}
6317 	      else if (node->right != tree->nil &&
6318 		       GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID))
6319 		{
6320 		  node = node->right;
6321 		}
6322 	      else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
6323 		       GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
6324 		{
6325 		  break;
6326 		}
6327 	      else if (node->children != NULL)
6328 		{
6329 		  tree = node->children;
6330 		  node = tree->root;
6331 		}
6332 	      else
6333 		/* RBTree corruption!  All bad */
6334 		g_assert_not_reached ();
6335 	    }
6336 	  while (TRUE);
6337 	  path = _gtk_tree_view_find_path (tree_view, tree, node);
6338 	  gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
6339 	}
6340 
6341       validated_area = validate_row (tree_view, tree, node, &iter, path) ||
6342                        validated_area;
6343 
6344       if (!tree_view->priv->fixed_height_check)
6345         {
6346 	  gint height;
6347 
6348 	  height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
6349 	  if (prev_height < 0)
6350 	    prev_height = height;
6351 	  else if (prev_height != height)
6352 	    fixed_height = FALSE;
6353 	}
6354 
6355       i++;
6356     }
6357   while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.);
6358 
6359   if (!tree_view->priv->fixed_height_check)
6360    {
6361      if (fixed_height)
6362        _gtk_rbtree_set_fixed_height (tree_view->priv->tree, prev_height, FALSE);
6363 
6364      tree_view->priv->fixed_height_check = 1;
6365    }
6366 
6367  done:
6368   if (validated_area)
6369     {
6370       GtkRequisition requisition;
6371       /* We temporarily guess a size, under the assumption that it will be the
6372        * same when we get our next size_allocate.  If we don't do this, we'll be
6373        * in an inconsistent state when we call top_row_to_dy. */
6374 
6375       gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
6376       tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
6377       tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
6378       gtk_adjustment_changed (tree_view->priv->hadjustment);
6379       gtk_adjustment_changed (tree_view->priv->vadjustment);
6380 
6381       if (queue_resize)
6382         gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6383     }
6384 
6385   if (path) gtk_tree_path_free (path);
6386   g_timer_destroy (timer);
6387 
6388   return retval;
6389 }
6390 
6391 static gboolean
validate_rows(GtkTreeView * tree_view)6392 validate_rows (GtkTreeView *tree_view)
6393 {
6394   gboolean retval;
6395 
6396   retval = do_validate_rows (tree_view, TRUE);
6397 
6398   if (! retval && tree_view->priv->validate_rows_timer)
6399     {
6400       g_source_remove (tree_view->priv->validate_rows_timer);
6401       tree_view->priv->validate_rows_timer = 0;
6402     }
6403 
6404   return retval;
6405 }
6406 
6407 static gboolean
validate_rows_handler(GtkTreeView * tree_view)6408 validate_rows_handler (GtkTreeView *tree_view)
6409 {
6410   gboolean retval;
6411 
6412   retval = do_validate_rows (tree_view, TRUE);
6413   if (! retval && tree_view->priv->validate_rows_timer)
6414     {
6415       g_source_remove (tree_view->priv->validate_rows_timer);
6416       tree_view->priv->validate_rows_timer = 0;
6417     }
6418 
6419   return retval;
6420 }
6421 
6422 static gboolean
do_presize_handler(GtkTreeView * tree_view)6423 do_presize_handler (GtkTreeView *tree_view)
6424 {
6425   if (tree_view->priv->mark_rows_col_dirty)
6426     {
6427       if (tree_view->priv->tree)
6428 	_gtk_rbtree_column_invalid (tree_view->priv->tree);
6429       tree_view->priv->mark_rows_col_dirty = FALSE;
6430     }
6431   validate_visible_area (tree_view);
6432   tree_view->priv->presize_handler_timer = 0;
6433 
6434   if (tree_view->priv->fixed_height_mode)
6435     {
6436       GtkRequisition requisition;
6437 
6438       gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
6439 
6440       tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
6441       tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
6442       gtk_adjustment_changed (tree_view->priv->hadjustment);
6443       gtk_adjustment_changed (tree_view->priv->vadjustment);
6444       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6445     }
6446 
6447   return FALSE;
6448 }
6449 
6450 static gboolean
presize_handler_callback(gpointer data)6451 presize_handler_callback (gpointer data)
6452 {
6453   do_presize_handler (GTK_TREE_VIEW (data));
6454 
6455   return FALSE;
6456 }
6457 
6458 static void
install_presize_handler(GtkTreeView * tree_view)6459 install_presize_handler (GtkTreeView *tree_view)
6460 {
6461   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
6462     return;
6463 
6464   if (! tree_view->priv->presize_handler_timer)
6465     {
6466       tree_view->priv->presize_handler_timer =
6467 	gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
6468     }
6469   if (! tree_view->priv->validate_rows_timer)
6470     {
6471       tree_view->priv->validate_rows_timer =
6472 	gdk_threads_add_idle_full (GTK_TREE_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
6473     }
6474 }
6475 
6476 static gboolean
scroll_sync_handler(GtkTreeView * tree_view)6477 scroll_sync_handler (GtkTreeView *tree_view)
6478 {
6479   if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
6480     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
6481   else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
6482     gtk_tree_view_top_row_to_dy (tree_view);
6483   else
6484     gtk_tree_view_dy_to_top_row (tree_view);
6485 
6486   tree_view->priv->scroll_sync_timer = 0;
6487 
6488   return FALSE;
6489 }
6490 
6491 static void
install_scroll_sync_handler(GtkTreeView * tree_view)6492 install_scroll_sync_handler (GtkTreeView *tree_view)
6493 {
6494   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
6495     return;
6496 
6497   if (!tree_view->priv->scroll_sync_timer)
6498     {
6499       tree_view->priv->scroll_sync_timer =
6500 	gdk_threads_add_idle_full (GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
6501     }
6502 }
6503 
6504 static void
gtk_tree_view_set_top_row(GtkTreeView * tree_view,GtkTreePath * path,gint offset)6505 gtk_tree_view_set_top_row (GtkTreeView *tree_view,
6506 			   GtkTreePath *path,
6507 			   gint         offset)
6508 {
6509   gtk_tree_row_reference_free (tree_view->priv->top_row);
6510 
6511   if (!path)
6512     {
6513       tree_view->priv->top_row = NULL;
6514       tree_view->priv->top_row_dy = 0;
6515     }
6516   else
6517     {
6518       tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
6519       tree_view->priv->top_row_dy = offset;
6520     }
6521 }
6522 
6523 /* Always call this iff dy is in the visible range.  If the tree is empty, then
6524  * it's set to be NULL, and top_row_dy is 0;
6525  */
6526 static void
gtk_tree_view_dy_to_top_row(GtkTreeView * tree_view)6527 gtk_tree_view_dy_to_top_row (GtkTreeView *tree_view)
6528 {
6529   gint offset;
6530   GtkTreePath *path;
6531   GtkRBTree *tree;
6532   GtkRBNode *node;
6533 
6534   if (tree_view->priv->tree == NULL)
6535     {
6536       gtk_tree_view_set_top_row (tree_view, NULL, 0);
6537     }
6538   else
6539     {
6540       offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
6541 					tree_view->priv->dy,
6542 					&tree, &node);
6543 
6544       if (tree == NULL)
6545         {
6546 	  gtk_tree_view_set_top_row (tree_view, NULL, 0);
6547 	}
6548       else
6549         {
6550 	  path = _gtk_tree_view_find_path (tree_view, tree, node);
6551 	  gtk_tree_view_set_top_row (tree_view, path, offset);
6552 	  gtk_tree_path_free (path);
6553 	}
6554     }
6555 }
6556 
6557 static void
gtk_tree_view_top_row_to_dy(GtkTreeView * tree_view)6558 gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view)
6559 {
6560   GtkTreePath *path;
6561   GtkRBTree *tree;
6562   GtkRBNode *node;
6563   int new_dy;
6564 
6565   /* Avoid recursive calls */
6566   if (tree_view->priv->in_top_row_to_dy)
6567     return;
6568 
6569   if (tree_view->priv->top_row)
6570     path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
6571   else
6572     path = NULL;
6573 
6574   if (!path)
6575     tree = NULL;
6576   else
6577     _gtk_tree_view_find_node (tree_view, path, &tree, &node);
6578 
6579   if (path)
6580     gtk_tree_path_free (path);
6581 
6582   if (tree == NULL)
6583     {
6584       /* keep dy and set new toprow */
6585       gtk_tree_row_reference_free (tree_view->priv->top_row);
6586       tree_view->priv->top_row = NULL;
6587       tree_view->priv->top_row_dy = 0;
6588       /* DO NOT install the idle handler */
6589       gtk_tree_view_dy_to_top_row (tree_view);
6590       return;
6591     }
6592 
6593   if (ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node))
6594       < tree_view->priv->top_row_dy)
6595     {
6596       /* new top row -- do NOT install the idle handler */
6597       gtk_tree_view_dy_to_top_row (tree_view);
6598       return;
6599     }
6600 
6601   new_dy = _gtk_rbtree_node_find_offset (tree, node);
6602   new_dy += tree_view->priv->top_row_dy;
6603 
6604   if (new_dy + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
6605     new_dy = tree_view->priv->height - tree_view->priv->vadjustment->page_size;
6606 
6607   new_dy = MAX (0, new_dy);
6608 
6609   tree_view->priv->in_top_row_to_dy = TRUE;
6610   gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
6611   tree_view->priv->in_top_row_to_dy = FALSE;
6612 }
6613 
6614 
6615 void
_gtk_tree_view_install_mark_rows_col_dirty(GtkTreeView * tree_view)6616 _gtk_tree_view_install_mark_rows_col_dirty (GtkTreeView *tree_view)
6617 {
6618   tree_view->priv->mark_rows_col_dirty = TRUE;
6619 
6620   install_presize_handler (tree_view);
6621 }
6622 
6623 /*
6624  * This function works synchronously (due to the while (validate_rows...)
6625  * loop).
6626  *
6627  * There was a check for column_type != GTK_TREE_VIEW_COLUMN_AUTOSIZE
6628  * here. You now need to check that yourself.
6629  */
6630 void
_gtk_tree_view_column_autosize(GtkTreeView * tree_view,GtkTreeViewColumn * column)6631 _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
6632 			        GtkTreeViewColumn *column)
6633 {
6634   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
6635   g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
6636 
6637   _gtk_tree_view_column_cell_set_dirty (column, FALSE);
6638 
6639   do_presize_handler (tree_view);
6640   while (validate_rows (tree_view));
6641 
6642   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
6643 }
6644 
6645 /* Drag-and-drop */
6646 
6647 static void
set_source_row(GdkDragContext * context,GtkTreeModel * model,GtkTreePath * source_row)6648 set_source_row (GdkDragContext *context,
6649                 GtkTreeModel   *model,
6650                 GtkTreePath    *source_row)
6651 {
6652   g_object_set_data_full (G_OBJECT (context),
6653                           I_("gtk-tree-view-source-row"),
6654                           source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
6655                           (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
6656 }
6657 
6658 static GtkTreePath*
get_source_row(GdkDragContext * context)6659 get_source_row (GdkDragContext *context)
6660 {
6661   GtkTreeRowReference *ref =
6662     g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
6663 
6664   if (ref)
6665     return gtk_tree_row_reference_get_path (ref);
6666   else
6667     return NULL;
6668 }
6669 
6670 typedef struct
6671 {
6672   GtkTreeRowReference *dest_row;
6673   guint                path_down_mode   : 1;
6674   guint                empty_view_drop  : 1;
6675   guint                drop_append_mode : 1;
6676 }
6677 DestRow;
6678 
6679 static void
dest_row_free(gpointer data)6680 dest_row_free (gpointer data)
6681 {
6682   DestRow *dr = (DestRow *)data;
6683 
6684   gtk_tree_row_reference_free (dr->dest_row);
6685   g_slice_free (DestRow, dr);
6686 }
6687 
6688 static void
set_dest_row(GdkDragContext * context,GtkTreeModel * model,GtkTreePath * dest_row,gboolean path_down_mode,gboolean empty_view_drop,gboolean drop_append_mode)6689 set_dest_row (GdkDragContext *context,
6690               GtkTreeModel   *model,
6691               GtkTreePath    *dest_row,
6692               gboolean        path_down_mode,
6693               gboolean        empty_view_drop,
6694               gboolean        drop_append_mode)
6695 {
6696   DestRow *dr;
6697 
6698   if (!dest_row)
6699     {
6700       g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"),
6701                               NULL, NULL);
6702       return;
6703     }
6704 
6705   dr = g_slice_new (DestRow);
6706 
6707   dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
6708   dr->path_down_mode = path_down_mode != FALSE;
6709   dr->empty_view_drop = empty_view_drop != FALSE;
6710   dr->drop_append_mode = drop_append_mode != FALSE;
6711 
6712   g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"),
6713                           dr, (GDestroyNotify) dest_row_free);
6714 }
6715 
6716 static GtkTreePath*
get_dest_row(GdkDragContext * context,gboolean * path_down_mode)6717 get_dest_row (GdkDragContext *context,
6718               gboolean       *path_down_mode)
6719 {
6720   DestRow *dr =
6721     g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
6722 
6723   if (dr)
6724     {
6725       GtkTreePath *path = NULL;
6726 
6727       if (path_down_mode)
6728         *path_down_mode = dr->path_down_mode;
6729 
6730       if (dr->dest_row)
6731         path = gtk_tree_row_reference_get_path (dr->dest_row);
6732       else if (dr->empty_view_drop)
6733         path = gtk_tree_path_new_from_indices (0, -1);
6734       else
6735         path = NULL;
6736 
6737       if (path && dr->drop_append_mode)
6738         gtk_tree_path_next (path);
6739 
6740       return path;
6741     }
6742   else
6743     return NULL;
6744 }
6745 
6746 /* Get/set whether drag_motion requested the drag data and
6747  * drag_data_received should thus not actually insert the data,
6748  * since the data doesn't result from a drop.
6749  */
6750 static void
set_status_pending(GdkDragContext * context,GdkDragAction suggested_action)6751 set_status_pending (GdkDragContext *context,
6752                     GdkDragAction   suggested_action)
6753 {
6754   g_object_set_data (G_OBJECT (context),
6755                      I_("gtk-tree-view-status-pending"),
6756                      GINT_TO_POINTER (suggested_action));
6757 }
6758 
6759 static GdkDragAction
get_status_pending(GdkDragContext * context)6760 get_status_pending (GdkDragContext *context)
6761 {
6762   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
6763                                              "gtk-tree-view-status-pending"));
6764 }
6765 
6766 static TreeViewDragInfo*
get_info(GtkTreeView * tree_view)6767 get_info (GtkTreeView *tree_view)
6768 {
6769   return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
6770 }
6771 
6772 static void
destroy_info(TreeViewDragInfo * di)6773 destroy_info (TreeViewDragInfo *di)
6774 {
6775   g_slice_free (TreeViewDragInfo, di);
6776 }
6777 
6778 static TreeViewDragInfo*
ensure_info(GtkTreeView * tree_view)6779 ensure_info (GtkTreeView *tree_view)
6780 {
6781   TreeViewDragInfo *di;
6782 
6783   di = get_info (tree_view);
6784 
6785   if (di == NULL)
6786     {
6787       di = g_slice_new0 (TreeViewDragInfo);
6788 
6789       g_object_set_data_full (G_OBJECT (tree_view),
6790                               I_("gtk-tree-view-drag-info"),
6791                               di,
6792                               (GDestroyNotify) destroy_info);
6793     }
6794 
6795   return di;
6796 }
6797 
6798 static void
remove_info(GtkTreeView * tree_view)6799 remove_info (GtkTreeView *tree_view)
6800 {
6801   g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL);
6802 }
6803 
6804 #if 0
6805 static gint
6806 drag_scan_timeout (gpointer data)
6807 {
6808   GtkTreeView *tree_view;
6809   gint x, y;
6810   GdkModifierType state;
6811   GtkTreePath *path = NULL;
6812   GtkTreeViewColumn *column = NULL;
6813   GdkRectangle visible_rect;
6814 
6815   GDK_THREADS_ENTER ();
6816 
6817   tree_view = GTK_TREE_VIEW (data);
6818 
6819   gdk_window_get_pointer (tree_view->priv->bin_window,
6820                           &x, &y, &state);
6821 
6822   gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
6823 
6824   /* See if we are near the edge. */
6825   if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
6826       (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
6827       (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
6828       (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
6829     {
6830       gtk_tree_view_get_path_at_pos (tree_view,
6831                                      tree_view->priv->bin_window,
6832                                      x, y,
6833                                      &path,
6834                                      &column,
6835                                      NULL,
6836                                      NULL);
6837 
6838       if (path != NULL)
6839         {
6840           gtk_tree_view_scroll_to_cell (tree_view,
6841                                         path,
6842                                         column,
6843 					TRUE,
6844                                         0.5, 0.5);
6845 
6846           gtk_tree_path_free (path);
6847         }
6848     }
6849 
6850   GDK_THREADS_LEAVE ();
6851 
6852   return TRUE;
6853 }
6854 #endif /* 0 */
6855 
6856 static void
add_scroll_timeout(GtkTreeView * tree_view)6857 add_scroll_timeout (GtkTreeView *tree_view)
6858 {
6859   if (tree_view->priv->scroll_timeout == 0)
6860     {
6861       tree_view->priv->scroll_timeout =
6862 	gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
6863     }
6864 }
6865 
6866 static void
remove_scroll_timeout(GtkTreeView * tree_view)6867 remove_scroll_timeout (GtkTreeView *tree_view)
6868 {
6869   if (tree_view->priv->scroll_timeout != 0)
6870     {
6871       g_source_remove (tree_view->priv->scroll_timeout);
6872       tree_view->priv->scroll_timeout = 0;
6873     }
6874 }
6875 
6876 static gboolean
check_model_dnd(GtkTreeModel * model,GType required_iface,const gchar * signal)6877 check_model_dnd (GtkTreeModel *model,
6878                  GType         required_iface,
6879                  const gchar  *signal)
6880 {
6881   if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
6882     {
6883       g_warning ("You must override the default '%s' handler "
6884                  "on GtkTreeView when using models that don't support "
6885                  "the %s interface and enabling drag-and-drop. The simplest way to do this "
6886                  "is to connect to '%s' and call "
6887                  "g_signal_stop_emission_by_name() in your signal handler to prevent "
6888                  "the default handler from running. Look at the source code "
6889                  "for the default handler in gtktreeview.c to get an idea what "
6890                  "your handler should do. (gtktreeview.c is in the GTK source "
6891                  "code.) If you're using GTK from a language other than C, "
6892                  "there may be a more natural way to override default handlers, e.g. via derivation.",
6893                  signal, g_type_name (required_iface), signal);
6894       return FALSE;
6895     }
6896   else
6897     return TRUE;
6898 }
6899 
6900 static void
remove_open_timeout(GtkTreeView * tree_view)6901 remove_open_timeout (GtkTreeView *tree_view)
6902 {
6903   if (tree_view->priv->open_dest_timeout != 0)
6904     {
6905       g_source_remove (tree_view->priv->open_dest_timeout);
6906       tree_view->priv->open_dest_timeout = 0;
6907     }
6908 }
6909 
6910 
6911 static gint
open_row_timeout(gpointer data)6912 open_row_timeout (gpointer data)
6913 {
6914   GtkTreeView *tree_view = data;
6915   GtkTreePath *dest_path = NULL;
6916   GtkTreeViewDropPosition pos;
6917   gboolean result = FALSE;
6918 
6919   gtk_tree_view_get_drag_dest_row (tree_view,
6920                                    &dest_path,
6921                                    &pos);
6922 
6923   if (dest_path &&
6924       (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
6925        pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
6926     {
6927       gtk_tree_view_expand_row (tree_view, dest_path, FALSE);
6928       tree_view->priv->open_dest_timeout = 0;
6929 
6930       gtk_tree_path_free (dest_path);
6931     }
6932   else
6933     {
6934       if (dest_path)
6935         gtk_tree_path_free (dest_path);
6936 
6937       result = TRUE;
6938     }
6939 
6940   return result;
6941 }
6942 
6943 static gboolean
scroll_row_timeout(gpointer data)6944 scroll_row_timeout (gpointer data)
6945 {
6946   GtkTreeView *tree_view = data;
6947 
6948   gtk_tree_view_vertical_autoscroll (tree_view);
6949 
6950   if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
6951     gtk_tree_view_update_rubber_band (tree_view);
6952 
6953   return TRUE;
6954 }
6955 
6956 /* Returns TRUE if event should not be propagated to parent widgets */
6957 static gboolean
set_destination_row(GtkTreeView * tree_view,GdkDragContext * context,gint x,gint y,GdkDragAction * suggested_action,GdkAtom * target)6958 set_destination_row (GtkTreeView    *tree_view,
6959                      GdkDragContext *context,
6960                      /* coordinates relative to the widget */
6961                      gint            x,
6962                      gint            y,
6963                      GdkDragAction  *suggested_action,
6964                      GdkAtom        *target)
6965 {
6966   GtkTreePath *path = NULL;
6967   GtkTreeViewDropPosition pos;
6968   GtkTreeViewDropPosition old_pos;
6969   TreeViewDragInfo *di;
6970   GtkWidget *widget;
6971   GtkTreePath *old_dest_path = NULL;
6972   gboolean can_drop = FALSE;
6973 
6974   *suggested_action = 0;
6975   *target = GDK_NONE;
6976 
6977   widget = GTK_WIDGET (tree_view);
6978 
6979   di = get_info (tree_view);
6980 
6981   if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
6982     {
6983       /* someone unset us as a drag dest, note that if
6984        * we return FALSE drag_leave isn't called
6985        */
6986 
6987       gtk_tree_view_set_drag_dest_row (tree_view,
6988                                        NULL,
6989                                        GTK_TREE_VIEW_DROP_BEFORE);
6990 
6991       remove_scroll_timeout (GTK_TREE_VIEW (widget));
6992       remove_open_timeout (GTK_TREE_VIEW (widget));
6993 
6994       return FALSE; /* no longer a drop site */
6995     }
6996 
6997   *target = gtk_drag_dest_find_target (widget, context,
6998                                        gtk_drag_dest_get_target_list (widget));
6999   if (*target == GDK_NONE)
7000     {
7001       return FALSE;
7002     }
7003 
7004   if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
7005                                           x, y,
7006                                           &path,
7007                                           &pos))
7008     {
7009       gint n_children;
7010       GtkTreeModel *model;
7011 
7012       remove_open_timeout (tree_view);
7013 
7014       /* the row got dropped on empty space, let's setup a special case
7015        */
7016 
7017       if (path)
7018 	gtk_tree_path_free (path);
7019 
7020       model = gtk_tree_view_get_model (tree_view);
7021 
7022       n_children = gtk_tree_model_iter_n_children (model, NULL);
7023       if (n_children)
7024         {
7025           pos = GTK_TREE_VIEW_DROP_AFTER;
7026           path = gtk_tree_path_new_from_indices (n_children - 1, -1);
7027         }
7028       else
7029         {
7030           pos = GTK_TREE_VIEW_DROP_BEFORE;
7031           path = gtk_tree_path_new_from_indices (0, -1);
7032         }
7033 
7034       can_drop = TRUE;
7035 
7036       goto out;
7037     }
7038 
7039   g_assert (path);
7040 
7041   /* If we left the current row's "open" zone, unset the timeout for
7042    * opening the row
7043    */
7044   gtk_tree_view_get_drag_dest_row (tree_view,
7045                                    &old_dest_path,
7046                                    &old_pos);
7047 
7048   if (old_dest_path &&
7049       (gtk_tree_path_compare (path, old_dest_path) != 0 ||
7050        !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7051          pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)))
7052     remove_open_timeout (tree_view);
7053 
7054   if (old_dest_path)
7055     gtk_tree_path_free (old_dest_path);
7056 
7057   if (TRUE /* FIXME if the location droppable predicate */)
7058     {
7059       can_drop = TRUE;
7060     }
7061 
7062 out:
7063   if (can_drop)
7064     {
7065       GtkWidget *source_widget;
7066 
7067       *suggested_action = gdk_drag_context_get_suggested_action (context);
7068       source_widget = gtk_drag_get_source_widget (context);
7069 
7070       if (source_widget == widget)
7071         {
7072           /* Default to MOVE, unless the user has
7073            * pressed ctrl or shift to affect available actions
7074            */
7075           if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
7076             *suggested_action = GDK_ACTION_MOVE;
7077         }
7078 
7079       gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7080                                        path, pos);
7081     }
7082   else
7083     {
7084       /* can't drop here */
7085       remove_open_timeout (tree_view);
7086 
7087       gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7088                                        NULL,
7089                                        GTK_TREE_VIEW_DROP_BEFORE);
7090     }
7091 
7092   if (path)
7093     gtk_tree_path_free (path);
7094 
7095   return TRUE;
7096 }
7097 
7098 static GtkTreePath*
get_logical_dest_row(GtkTreeView * tree_view,gboolean * path_down_mode,gboolean * drop_append_mode)7099 get_logical_dest_row (GtkTreeView *tree_view,
7100                       gboolean    *path_down_mode,
7101                       gboolean    *drop_append_mode)
7102 {
7103   /* adjust path to point to the row the drop goes in front of */
7104   GtkTreePath *path = NULL;
7105   GtkTreeViewDropPosition pos;
7106 
7107   g_return_val_if_fail (path_down_mode != NULL, NULL);
7108   g_return_val_if_fail (drop_append_mode != NULL, NULL);
7109 
7110   *path_down_mode = FALSE;
7111   *drop_append_mode = 0;
7112 
7113   gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
7114 
7115   if (path == NULL)
7116     return NULL;
7117 
7118   if (pos == GTK_TREE_VIEW_DROP_BEFORE)
7119     ; /* do nothing */
7120   else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
7121            pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
7122     *path_down_mode = TRUE;
7123   else
7124     {
7125       GtkTreeIter iter;
7126       GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
7127 
7128       g_assert (pos == GTK_TREE_VIEW_DROP_AFTER);
7129 
7130       if (!gtk_tree_model_get_iter (model, &iter, path) ||
7131           !gtk_tree_model_iter_next (model, &iter))
7132         *drop_append_mode = 1;
7133       else
7134         {
7135           *drop_append_mode = 0;
7136           gtk_tree_path_next (path);
7137         }
7138     }
7139 
7140   return path;
7141 }
7142 
7143 static gboolean
gtk_tree_view_maybe_begin_dragging_row(GtkTreeView * tree_view,GdkEventMotion * event)7144 gtk_tree_view_maybe_begin_dragging_row (GtkTreeView      *tree_view,
7145                                         GdkEventMotion   *event)
7146 {
7147   GtkWidget *widget = GTK_WIDGET (tree_view);
7148   GdkDragContext *context;
7149   TreeViewDragInfo *di;
7150   GtkTreePath *path = NULL;
7151   gint button;
7152   gint cell_x, cell_y;
7153   GtkTreeModel *model;
7154   gboolean retval = FALSE;
7155 
7156   di = get_info (tree_view);
7157 
7158   if (di == NULL || !di->source_set)
7159     goto out;
7160 
7161   if (tree_view->priv->pressed_button < 0)
7162     goto out;
7163 
7164   if (!gtk_drag_check_threshold (widget,
7165                                  tree_view->priv->press_start_x,
7166                                  tree_view->priv->press_start_y,
7167                                  event->x, event->y))
7168     goto out;
7169 
7170   model = gtk_tree_view_get_model (tree_view);
7171 
7172   if (model == NULL)
7173     goto out;
7174 
7175   button = tree_view->priv->pressed_button;
7176   tree_view->priv->pressed_button = -1;
7177 
7178   gtk_tree_view_get_path_at_pos (tree_view,
7179                                  tree_view->priv->press_start_x,
7180                                  tree_view->priv->press_start_y,
7181                                  &path,
7182                                  NULL,
7183                                  &cell_x,
7184                                  &cell_y);
7185 
7186   if (path == NULL)
7187     goto out;
7188 
7189   if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7190       !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7191 					   path))
7192     goto out;
7193 
7194   if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
7195     goto out;
7196 
7197   /* Now we can begin the drag */
7198 
7199   retval = TRUE;
7200 
7201   context = gtk_drag_begin (widget,
7202                             gtk_drag_source_get_target_list (widget),
7203                             di->source_actions,
7204                             button,
7205                             (GdkEvent*)event);
7206 
7207   set_source_row (context, model, path);
7208 
7209  out:
7210   if (path)
7211     gtk_tree_path_free (path);
7212 
7213   return retval;
7214 }
7215 
7216 
7217 static void
gtk_tree_view_drag_begin(GtkWidget * widget,GdkDragContext * context)7218 gtk_tree_view_drag_begin (GtkWidget      *widget,
7219                           GdkDragContext *context)
7220 {
7221   GtkTreeView *tree_view;
7222   GtkTreePath *path = NULL;
7223   gint cell_x, cell_y;
7224   GdkPixmap *row_pix;
7225   TreeViewDragInfo *di;
7226 
7227   tree_view = GTK_TREE_VIEW (widget);
7228 
7229   /* if the user uses a custom DND source impl, we don't set the icon here */
7230   di = get_info (tree_view);
7231 
7232   if (di == NULL || !di->source_set)
7233     return;
7234 
7235   gtk_tree_view_get_path_at_pos (tree_view,
7236                                  tree_view->priv->press_start_x,
7237                                  tree_view->priv->press_start_y,
7238                                  &path,
7239                                  NULL,
7240                                  &cell_x,
7241                                  &cell_y);
7242 
7243   g_return_if_fail (path != NULL);
7244 
7245   row_pix = gtk_tree_view_create_row_drag_icon (tree_view,
7246                                                 path);
7247 
7248   gtk_drag_set_icon_pixmap (context,
7249                             gdk_drawable_get_colormap (row_pix),
7250                             row_pix,
7251                             NULL,
7252                             /* the + 1 is for the black border in the icon */
7253                             tree_view->priv->press_start_x + 1,
7254                             cell_y + 1);
7255 
7256   g_object_unref (row_pix);
7257   gtk_tree_path_free (path);
7258 }
7259 
7260 static void
gtk_tree_view_drag_end(GtkWidget * widget,GdkDragContext * context)7261 gtk_tree_view_drag_end (GtkWidget      *widget,
7262                         GdkDragContext *context)
7263 {
7264   /* do nothing */
7265 }
7266 
7267 /* Default signal implementations for the drag signals */
7268 static void
gtk_tree_view_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)7269 gtk_tree_view_drag_data_get (GtkWidget        *widget,
7270                              GdkDragContext   *context,
7271                              GtkSelectionData *selection_data,
7272                              guint             info,
7273                              guint             time)
7274 {
7275   GtkTreeView *tree_view;
7276   GtkTreeModel *model;
7277   TreeViewDragInfo *di;
7278   GtkTreePath *source_row;
7279 
7280   tree_view = GTK_TREE_VIEW (widget);
7281 
7282   model = gtk_tree_view_get_model (tree_view);
7283 
7284   if (model == NULL)
7285     return;
7286 
7287   di = get_info (GTK_TREE_VIEW (widget));
7288 
7289   if (di == NULL)
7290     return;
7291 
7292   source_row = get_source_row (context);
7293 
7294   if (source_row == NULL)
7295     return;
7296 
7297   /* We can implement the GTK_TREE_MODEL_ROW target generically for
7298    * any model; for DragSource models there are some other targets
7299    * we also support.
7300    */
7301 
7302   if (GTK_IS_TREE_DRAG_SOURCE (model) &&
7303       gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
7304                                           source_row,
7305                                           selection_data))
7306     goto done;
7307 
7308   /* If drag_data_get does nothing, try providing row data. */
7309   if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
7310     {
7311       gtk_tree_set_row_drag_data (selection_data,
7312 				  model,
7313 				  source_row);
7314     }
7315 
7316  done:
7317   gtk_tree_path_free (source_row);
7318 }
7319 
7320 
7321 static void
gtk_tree_view_drag_data_delete(GtkWidget * widget,GdkDragContext * context)7322 gtk_tree_view_drag_data_delete (GtkWidget      *widget,
7323                                 GdkDragContext *context)
7324 {
7325   TreeViewDragInfo *di;
7326   GtkTreeModel *model;
7327   GtkTreeView *tree_view;
7328   GtkTreePath *source_row;
7329 
7330   tree_view = GTK_TREE_VIEW (widget);
7331   model = gtk_tree_view_get_model (tree_view);
7332 
7333   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
7334     return;
7335 
7336   di = get_info (tree_view);
7337 
7338   if (di == NULL)
7339     return;
7340 
7341   source_row = get_source_row (context);
7342 
7343   if (source_row == NULL)
7344     return;
7345 
7346   gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
7347                                          source_row);
7348 
7349   gtk_tree_path_free (source_row);
7350 
7351   set_source_row (context, NULL, NULL);
7352 }
7353 
7354 static void
gtk_tree_view_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)7355 gtk_tree_view_drag_leave (GtkWidget      *widget,
7356                           GdkDragContext *context,
7357                           guint             time)
7358 {
7359   /* unset any highlight row */
7360   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7361                                    NULL,
7362                                    GTK_TREE_VIEW_DROP_BEFORE);
7363 
7364   remove_scroll_timeout (GTK_TREE_VIEW (widget));
7365   remove_open_timeout (GTK_TREE_VIEW (widget));
7366 }
7367 
7368 
7369 static gboolean
gtk_tree_view_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)7370 gtk_tree_view_drag_motion (GtkWidget        *widget,
7371                            GdkDragContext   *context,
7372 			   /* coordinates relative to the widget */
7373                            gint              x,
7374                            gint              y,
7375                            guint             time)
7376 {
7377   gboolean empty;
7378   GtkTreePath *path = NULL;
7379   GtkTreeViewDropPosition pos;
7380   GtkTreeView *tree_view;
7381   GdkDragAction suggested_action = 0;
7382   GdkAtom target;
7383 
7384   tree_view = GTK_TREE_VIEW (widget);
7385 
7386   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
7387     return FALSE;
7388 
7389   gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
7390 
7391   /* we only know this *after* set_desination_row */
7392   empty = tree_view->priv->empty_view_drop;
7393 
7394   if (path == NULL && !empty)
7395     {
7396       /* Can't drop here. */
7397       gdk_drag_status (context, 0, time);
7398     }
7399   else
7400     {
7401       if (tree_view->priv->open_dest_timeout == 0 &&
7402           (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
7403            pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
7404         {
7405           tree_view->priv->open_dest_timeout =
7406             gdk_threads_add_timeout (AUTO_EXPAND_TIMEOUT, open_row_timeout, tree_view);
7407         }
7408       else
7409         {
7410 	  add_scroll_timeout (tree_view);
7411 	}
7412 
7413       if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
7414         {
7415           /* Request data so we can use the source row when
7416            * determining whether to accept the drop
7417            */
7418           set_status_pending (context, suggested_action);
7419           gtk_drag_get_data (widget, context, target, time);
7420         }
7421       else
7422         {
7423           set_status_pending (context, 0);
7424           gdk_drag_status (context, suggested_action, time);
7425         }
7426     }
7427 
7428   if (path)
7429     gtk_tree_path_free (path);
7430 
7431   return TRUE;
7432 }
7433 
7434 
7435 static gboolean
gtk_tree_view_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)7436 gtk_tree_view_drag_drop (GtkWidget        *widget,
7437                          GdkDragContext   *context,
7438 			 /* coordinates relative to the widget */
7439                          gint              x,
7440                          gint              y,
7441                          guint             time)
7442 {
7443   GtkTreeView *tree_view;
7444   GtkTreePath *path;
7445   GdkDragAction suggested_action = 0;
7446   GdkAtom target = GDK_NONE;
7447   TreeViewDragInfo *di;
7448   GtkTreeModel *model;
7449   gboolean path_down_mode;
7450   gboolean drop_append_mode;
7451 
7452   tree_view = GTK_TREE_VIEW (widget);
7453 
7454   model = gtk_tree_view_get_model (tree_view);
7455 
7456   remove_scroll_timeout (GTK_TREE_VIEW (widget));
7457   remove_open_timeout (GTK_TREE_VIEW (widget));
7458 
7459   di = get_info (tree_view);
7460 
7461   if (di == NULL)
7462     return FALSE;
7463 
7464   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
7465     return FALSE;
7466 
7467   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
7468     return FALSE;
7469 
7470   path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
7471 
7472   if (target != GDK_NONE && path != NULL)
7473     {
7474       /* in case a motion had requested drag data, change things so we
7475        * treat drag data receives as a drop.
7476        */
7477       set_status_pending (context, 0);
7478       set_dest_row (context, model, path,
7479                     path_down_mode, tree_view->priv->empty_view_drop,
7480                     drop_append_mode);
7481     }
7482 
7483   if (path)
7484     gtk_tree_path_free (path);
7485 
7486   /* Unset this thing */
7487   gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7488                                    NULL,
7489                                    GTK_TREE_VIEW_DROP_BEFORE);
7490 
7491   if (target != GDK_NONE)
7492     {
7493       gtk_drag_get_data (widget, context, target, time);
7494       return TRUE;
7495     }
7496   else
7497     return FALSE;
7498 }
7499 
7500 static void
gtk_tree_view_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)7501 gtk_tree_view_drag_data_received (GtkWidget        *widget,
7502                                   GdkDragContext   *context,
7503 				  /* coordinates relative to the widget */
7504                                   gint              x,
7505                                   gint              y,
7506                                   GtkSelectionData *selection_data,
7507                                   guint             info,
7508                                   guint             time)
7509 {
7510   GtkTreePath *path;
7511   TreeViewDragInfo *di;
7512   gboolean accepted = FALSE;
7513   GtkTreeModel *model;
7514   GtkTreeView *tree_view;
7515   GtkTreePath *dest_row;
7516   GdkDragAction suggested_action;
7517   gboolean path_down_mode;
7518   gboolean drop_append_mode;
7519 
7520   tree_view = GTK_TREE_VIEW (widget);
7521 
7522   model = gtk_tree_view_get_model (tree_view);
7523 
7524   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
7525     return;
7526 
7527   di = get_info (tree_view);
7528 
7529   if (di == NULL)
7530     return;
7531 
7532   suggested_action = get_status_pending (context);
7533 
7534   if (suggested_action)
7535     {
7536       /* We are getting this data due to a request in drag_motion,
7537        * rather than due to a request in drag_drop, so we are just
7538        * supposed to call drag_status, not actually paste in the
7539        * data.
7540        */
7541       path = get_logical_dest_row (tree_view, &path_down_mode,
7542                                    &drop_append_mode);
7543 
7544       if (path == NULL)
7545         suggested_action = 0;
7546       else if (path_down_mode)
7547         gtk_tree_path_down (path);
7548 
7549       if (suggested_action)
7550         {
7551 	  if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7552 						     path,
7553 						     selection_data))
7554             {
7555               if (path_down_mode)
7556                 {
7557                   path_down_mode = FALSE;
7558                   gtk_tree_path_up (path);
7559 
7560                   if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7561                                                              path,
7562                                                              selection_data))
7563                     suggested_action = 0;
7564                 }
7565               else
7566 	        suggested_action = 0;
7567             }
7568         }
7569 
7570       gdk_drag_status (context, suggested_action, time);
7571 
7572       if (path)
7573         gtk_tree_path_free (path);
7574 
7575       /* If you can't drop, remove user drop indicator until the next motion */
7576       if (suggested_action == 0)
7577         gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
7578                                          NULL,
7579                                          GTK_TREE_VIEW_DROP_BEFORE);
7580 
7581       return;
7582     }
7583 
7584   dest_row = get_dest_row (context, &path_down_mode);
7585 
7586   if (dest_row == NULL)
7587     return;
7588 
7589   if (selection_data->length >= 0)
7590     {
7591       if (path_down_mode)
7592         {
7593           gtk_tree_path_down (dest_row);
7594           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7595                                                      dest_row, selection_data))
7596             gtk_tree_path_up (dest_row);
7597         }
7598     }
7599 
7600   if (selection_data->length >= 0)
7601     {
7602       if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
7603                                                  dest_row,
7604                                                  selection_data))
7605         accepted = TRUE;
7606     }
7607 
7608   gtk_drag_finish (context,
7609                    accepted,
7610                    (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
7611                    time);
7612 
7613   if (gtk_tree_path_get_depth (dest_row) == 1
7614       && gtk_tree_path_get_indices (dest_row)[0] == 0)
7615     {
7616       /* special special case drag to "0", scroll to first item */
7617       if (!tree_view->priv->scroll_to_path)
7618         gtk_tree_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
7619     }
7620 
7621   gtk_tree_path_free (dest_row);
7622 
7623   /* drop dest_row */
7624   set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
7625 }
7626 
7627 
7628 
7629 /* GtkContainer Methods
7630  */
7631 
7632 
7633 static void
gtk_tree_view_remove(GtkContainer * container,GtkWidget * widget)7634 gtk_tree_view_remove (GtkContainer *container,
7635 		      GtkWidget    *widget)
7636 {
7637   GtkTreeView *tree_view = GTK_TREE_VIEW (container);
7638   GtkTreeViewChild *child = NULL;
7639   GList *tmp_list;
7640 
7641   tmp_list = tree_view->priv->children;
7642   while (tmp_list)
7643     {
7644       child = tmp_list->data;
7645       if (child->widget == widget)
7646 	{
7647 	  gtk_widget_unparent (widget);
7648 
7649 	  tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
7650 	  g_list_free_1 (tmp_list);
7651 	  g_slice_free (GtkTreeViewChild, child);
7652 	  return;
7653 	}
7654 
7655       tmp_list = tmp_list->next;
7656     }
7657 
7658   tmp_list = tree_view->priv->columns;
7659 
7660   while (tmp_list)
7661     {
7662       GtkTreeViewColumn *column;
7663       column = tmp_list->data;
7664       if (column->button == widget)
7665 	{
7666 	  gtk_widget_unparent (widget);
7667 	  return;
7668 	}
7669       tmp_list = tmp_list->next;
7670     }
7671 }
7672 
7673 static void
gtk_tree_view_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)7674 gtk_tree_view_forall (GtkContainer *container,
7675 		      gboolean      include_internals,
7676 		      GtkCallback   callback,
7677 		      gpointer      callback_data)
7678 {
7679   GtkTreeView *tree_view = GTK_TREE_VIEW (container);
7680   GtkTreeViewChild *child = NULL;
7681   GtkTreeViewColumn *column;
7682   GList *tmp_list;
7683 
7684   tmp_list = tree_view->priv->children;
7685   while (tmp_list)
7686     {
7687       child = tmp_list->data;
7688       tmp_list = tmp_list->next;
7689 
7690       (* callback) (child->widget, callback_data);
7691     }
7692   if (include_internals == FALSE)
7693     return;
7694 
7695   for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
7696     {
7697       column = tmp_list->data;
7698 
7699       if (column->button)
7700 	(* callback) (column->button, callback_data);
7701     }
7702 }
7703 
7704 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
7705  * cells. If so we draw one big row-spanning focus rectangle.
7706  */
7707 static gboolean
gtk_tree_view_has_special_cell(GtkTreeView * tree_view)7708 gtk_tree_view_has_special_cell (GtkTreeView *tree_view)
7709 {
7710   GList *list;
7711 
7712   for (list = tree_view->priv->columns; list; list = list->next)
7713     {
7714       if (!((GtkTreeViewColumn *)list->data)->visible)
7715 	continue;
7716       if (_gtk_tree_view_column_count_special_cells (list->data))
7717 	return TRUE;
7718     }
7719 
7720   return FALSE;
7721 }
7722 
7723 static void
column_sizing_notify(GObject * object,GParamSpec * pspec,gpointer data)7724 column_sizing_notify (GObject    *object,
7725                       GParamSpec *pspec,
7726                       gpointer    data)
7727 {
7728   GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (object);
7729 
7730   if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED)
7731     /* disable fixed height mode */
7732     g_object_set (data, "fixed-height-mode", FALSE, NULL);
7733 }
7734 
7735 /**
7736  * gtk_tree_view_set_fixed_height_mode:
7737  * @tree_view: a #GtkTreeView
7738  * @enable: %TRUE to enable fixed height mode
7739  *
7740  * Enables or disables the fixed height mode of @tree_view.
7741  * Fixed height mode speeds up #GtkTreeView by assuming that all
7742  * rows have the same height.
7743  * Only enable this option if all rows are the same height and all
7744  * columns are of type %GTK_TREE_VIEW_COLUMN_FIXED.
7745  *
7746  * Since: 2.6
7747  **/
7748 void
gtk_tree_view_set_fixed_height_mode(GtkTreeView * tree_view,gboolean enable)7749 gtk_tree_view_set_fixed_height_mode (GtkTreeView *tree_view,
7750                                      gboolean     enable)
7751 {
7752   GList *l;
7753 
7754   enable = enable != FALSE;
7755 
7756   if (enable == tree_view->priv->fixed_height_mode)
7757     return;
7758 
7759   if (!enable)
7760     {
7761       tree_view->priv->fixed_height_mode = 0;
7762       tree_view->priv->fixed_height = -1;
7763 
7764       /* force a revalidation */
7765       install_presize_handler (tree_view);
7766     }
7767   else
7768     {
7769       /* make sure all columns are of type FIXED */
7770       for (l = tree_view->priv->columns; l; l = l->next)
7771 	{
7772 	  GtkTreeViewColumn *c = l->data;
7773 
7774 	  g_return_if_fail (gtk_tree_view_column_get_sizing (c) == GTK_TREE_VIEW_COLUMN_FIXED);
7775 	}
7776 
7777       /* yes, we really have to do this is in a separate loop */
7778       for (l = tree_view->priv->columns; l; l = l->next)
7779 	g_signal_connect (l->data, "notify::sizing",
7780 			  G_CALLBACK (column_sizing_notify), tree_view);
7781 
7782       tree_view->priv->fixed_height_mode = 1;
7783       tree_view->priv->fixed_height = -1;
7784 
7785       if (tree_view->priv->tree)
7786 	initialize_fixed_height_mode (tree_view);
7787     }
7788 
7789   g_object_notify (G_OBJECT (tree_view), "fixed-height-mode");
7790 }
7791 
7792 /**
7793  * gtk_tree_view_get_fixed_height_mode:
7794  * @tree_view: a #GtkTreeView
7795  *
7796  * Returns whether fixed height mode is turned on for @tree_view.
7797  *
7798  * Return value: %TRUE if @tree_view is in fixed height mode
7799  *
7800  * Since: 2.6
7801  **/
7802 gboolean
gtk_tree_view_get_fixed_height_mode(GtkTreeView * tree_view)7803 gtk_tree_view_get_fixed_height_mode (GtkTreeView *tree_view)
7804 {
7805   return tree_view->priv->fixed_height_mode;
7806 }
7807 
7808 /* Returns TRUE if the focus is within the headers, after the focus operation is
7809  * done
7810  */
7811 static gboolean
gtk_tree_view_header_focus(GtkTreeView * tree_view,GtkDirectionType dir,gboolean clamp_column_visible)7812 gtk_tree_view_header_focus (GtkTreeView      *tree_view,
7813 			    GtkDirectionType  dir,
7814 			    gboolean          clamp_column_visible)
7815 {
7816   GtkWidget *focus_child;
7817 
7818   GList *last_column, *first_column;
7819   GList *tmp_list;
7820   gboolean rtl;
7821 
7822   if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
7823     return FALSE;
7824 
7825   focus_child = GTK_CONTAINER (tree_view)->focus_child;
7826 
7827   first_column = tree_view->priv->columns;
7828   while (first_column)
7829     {
7830       if (gtk_widget_get_can_focus (GTK_TREE_VIEW_COLUMN (first_column->data)->button) &&
7831 	  GTK_TREE_VIEW_COLUMN (first_column->data)->visible &&
7832 	  (GTK_TREE_VIEW_COLUMN (first_column->data)->clickable ||
7833 	   GTK_TREE_VIEW_COLUMN (first_column->data)->reorderable))
7834 	break;
7835       first_column = first_column->next;
7836     }
7837 
7838   /* No headers are visible, or are focusable.  We can't focus in or out.
7839    */
7840   if (first_column == NULL)
7841     return FALSE;
7842 
7843   last_column = g_list_last (tree_view->priv->columns);
7844   while (last_column)
7845     {
7846       if (gtk_widget_get_can_focus (GTK_TREE_VIEW_COLUMN (last_column->data)->button) &&
7847 	  GTK_TREE_VIEW_COLUMN (last_column->data)->visible &&
7848 	  (GTK_TREE_VIEW_COLUMN (last_column->data)->clickable ||
7849 	   GTK_TREE_VIEW_COLUMN (last_column->data)->reorderable))
7850 	break;
7851       last_column = last_column->prev;
7852     }
7853 
7854 
7855   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7856 
7857   switch (dir)
7858     {
7859     case GTK_DIR_TAB_BACKWARD:
7860     case GTK_DIR_TAB_FORWARD:
7861     case GTK_DIR_UP:
7862     case GTK_DIR_DOWN:
7863       if (focus_child == NULL)
7864 	{
7865 	  if (tree_view->priv->focus_column != NULL &&
7866               gtk_widget_get_can_focus (tree_view->priv->focus_column->button))
7867 	    focus_child = tree_view->priv->focus_column->button;
7868 	  else
7869 	    focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
7870 	  gtk_widget_grab_focus (focus_child);
7871 	  break;
7872 	}
7873       return FALSE;
7874 
7875     case GTK_DIR_LEFT:
7876     case GTK_DIR_RIGHT:
7877       if (focus_child == NULL)
7878 	{
7879 	  if (tree_view->priv->focus_column != NULL)
7880 	    focus_child = tree_view->priv->focus_column->button;
7881 	  else if (dir == GTK_DIR_LEFT)
7882 	    focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
7883 	  else
7884 	    focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
7885 	  gtk_widget_grab_focus (focus_child);
7886 	  break;
7887 	}
7888 
7889       if (gtk_widget_child_focus (focus_child, dir))
7890 	{
7891 	  /* The focus moves inside the button. */
7892 	  /* This is probably a great example of bad UI */
7893 	  break;
7894 	}
7895 
7896       /* We need to move the focus among the row of buttons. */
7897       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
7898 	if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
7899 	  break;
7900 
7901       if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
7902 	  || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
7903         {
7904 	  gtk_widget_error_bell (GTK_WIDGET (tree_view));
7905 	  break;
7906 	}
7907 
7908       while (tmp_list)
7909 	{
7910 	  GtkTreeViewColumn *column;
7911 
7912 	  if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
7913 	    tmp_list = tmp_list->next;
7914 	  else
7915 	    tmp_list = tmp_list->prev;
7916 
7917 	  if (tmp_list == NULL)
7918 	    {
7919 	      g_warning ("Internal button not found");
7920 	      break;
7921 	    }
7922 	  column = tmp_list->data;
7923 	  if (column->button &&
7924 	      column->visible &&
7925 	      gtk_widget_get_can_focus (column->button))
7926 	    {
7927 	      focus_child = column->button;
7928 	      gtk_widget_grab_focus (column->button);
7929 	      break;
7930 	    }
7931 	}
7932       break;
7933     default:
7934       g_assert_not_reached ();
7935       break;
7936     }
7937 
7938   /* if focus child is non-null, we assume it's been set to the current focus child
7939    */
7940   if (focus_child)
7941     {
7942       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
7943 	if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
7944 	  {
7945 	    tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
7946 	    break;
7947 	  }
7948 
7949       if (clamp_column_visible)
7950         {
7951 	  gtk_tree_view_clamp_column_visible (tree_view,
7952 					      tree_view->priv->focus_column,
7953 					      FALSE);
7954 	}
7955     }
7956 
7957   return (focus_child != NULL);
7958 }
7959 
7960 /* This function returns in 'path' the first focusable path, if the given path
7961  * is already focusable, it's the returned one.
7962  */
7963 static gboolean
search_first_focusable_path(GtkTreeView * tree_view,GtkTreePath ** path,gboolean search_forward,GtkRBTree ** new_tree,GtkRBNode ** new_node)7964 search_first_focusable_path (GtkTreeView  *tree_view,
7965 			     GtkTreePath **path,
7966 			     gboolean      search_forward,
7967 			     GtkRBTree   **new_tree,
7968 			     GtkRBNode   **new_node)
7969 {
7970   GtkRBTree *tree = NULL;
7971   GtkRBNode *node = NULL;
7972 
7973   if (!path || !*path)
7974     return FALSE;
7975 
7976   _gtk_tree_view_find_node (tree_view, *path, &tree, &node);
7977 
7978   if (!tree || !node)
7979     return FALSE;
7980 
7981   while (node && row_is_separator (tree_view, NULL, *path))
7982     {
7983       if (search_forward)
7984 	_gtk_rbtree_next_full (tree, node, &tree, &node);
7985       else
7986 	_gtk_rbtree_prev_full (tree, node, &tree, &node);
7987 
7988       if (*path)
7989 	gtk_tree_path_free (*path);
7990 
7991       if (node)
7992 	*path = _gtk_tree_view_find_path (tree_view, tree, node);
7993       else
7994 	*path = NULL;
7995     }
7996 
7997   if (new_tree)
7998     *new_tree = tree;
7999 
8000   if (new_node)
8001     *new_node = node;
8002 
8003   return (*path != NULL);
8004 }
8005 
8006 static gint
gtk_tree_view_focus(GtkWidget * widget,GtkDirectionType direction)8007 gtk_tree_view_focus (GtkWidget        *widget,
8008 		     GtkDirectionType  direction)
8009 {
8010   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8011   GtkContainer *container = GTK_CONTAINER (widget);
8012   GtkWidget *focus_child;
8013 
8014   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
8015     return FALSE;
8016 
8017   focus_child = container->focus_child;
8018 
8019   gtk_tree_view_stop_editing (GTK_TREE_VIEW (widget), FALSE);
8020   /* Case 1.  Headers currently have focus. */
8021   if (focus_child)
8022     {
8023       switch (direction)
8024 	{
8025 	case GTK_DIR_LEFT:
8026 	case GTK_DIR_RIGHT:
8027 	  gtk_tree_view_header_focus (tree_view, direction, TRUE);
8028 	  return TRUE;
8029 	case GTK_DIR_TAB_BACKWARD:
8030 	case GTK_DIR_UP:
8031 	  return FALSE;
8032 	case GTK_DIR_TAB_FORWARD:
8033 	case GTK_DIR_DOWN:
8034 	  gtk_widget_grab_focus (widget);
8035 	  return TRUE;
8036 	default:
8037 	  g_assert_not_reached ();
8038 	  return FALSE;
8039 	}
8040     }
8041 
8042   /* Case 2. We don't have focus at all. */
8043   if (!gtk_widget_has_focus (widget))
8044     {
8045       gtk_widget_grab_focus (widget);
8046       return TRUE;
8047     }
8048 
8049   /* Case 3. We have focus already. */
8050   if (direction == GTK_DIR_TAB_BACKWARD)
8051     return (gtk_tree_view_header_focus (tree_view, direction, FALSE));
8052   else if (direction == GTK_DIR_TAB_FORWARD)
8053     return FALSE;
8054 
8055   /* Other directions caught by the keybindings */
8056   gtk_widget_grab_focus (widget);
8057   return TRUE;
8058 }
8059 
8060 static void
gtk_tree_view_grab_focus(GtkWidget * widget)8061 gtk_tree_view_grab_focus (GtkWidget *widget)
8062 {
8063   GTK_WIDGET_CLASS (gtk_tree_view_parent_class)->grab_focus (widget);
8064 
8065   gtk_tree_view_focus_to_cursor (GTK_TREE_VIEW (widget));
8066 }
8067 
8068 static void
gtk_tree_view_style_set(GtkWidget * widget,GtkStyle * previous_style)8069 gtk_tree_view_style_set (GtkWidget *widget,
8070 			 GtkStyle *previous_style)
8071 {
8072   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
8073   GList *list;
8074   GtkTreeViewColumn *column;
8075 
8076   if (gtk_widget_get_realized (widget))
8077     {
8078       gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
8079       gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
8080       gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
8081 
8082       gtk_tree_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
8083       gtk_tree_view_set_enable_tree_lines (tree_view, tree_view->priv->tree_lines_enabled);
8084     }
8085 
8086   gtk_widget_style_get (widget,
8087 			"expander-size", &tree_view->priv->expander_size,
8088 			NULL);
8089   tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
8090 
8091   for (list = tree_view->priv->columns; list; list = list->next)
8092     {
8093       column = list->data;
8094       _gtk_tree_view_column_cell_set_dirty (column, TRUE);
8095     }
8096 
8097   tree_view->priv->fixed_height = -1;
8098   _gtk_rbtree_mark_invalid (tree_view->priv->tree);
8099 
8100   gtk_widget_queue_resize (widget);
8101 }
8102 
8103 
8104 static void
gtk_tree_view_set_focus_child(GtkContainer * container,GtkWidget * child)8105 gtk_tree_view_set_focus_child (GtkContainer *container,
8106 			       GtkWidget    *child)
8107 {
8108   GtkTreeView *tree_view = GTK_TREE_VIEW (container);
8109   GList *list;
8110 
8111   for (list = tree_view->priv->columns; list; list = list->next)
8112     {
8113       if (GTK_TREE_VIEW_COLUMN (list->data)->button == child)
8114 	{
8115 	  tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
8116 	  break;
8117 	}
8118     }
8119 
8120   GTK_CONTAINER_CLASS (gtk_tree_view_parent_class)->set_focus_child (container, child);
8121 }
8122 
8123 static void
gtk_tree_view_set_adjustments(GtkTreeView * tree_view,GtkAdjustment * hadj,GtkAdjustment * vadj)8124 gtk_tree_view_set_adjustments (GtkTreeView   *tree_view,
8125 			       GtkAdjustment *hadj,
8126 			       GtkAdjustment *vadj)
8127 {
8128   gboolean need_adjust = FALSE;
8129 
8130   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
8131 
8132   if (hadj)
8133     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
8134   else
8135     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
8136   if (vadj)
8137     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
8138   else
8139     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
8140 
8141   if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
8142     {
8143       g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
8144 					    gtk_tree_view_adjustment_changed,
8145 					    tree_view);
8146       g_object_unref (tree_view->priv->hadjustment);
8147     }
8148 
8149   if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
8150     {
8151       g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
8152 					    gtk_tree_view_adjustment_changed,
8153 					    tree_view);
8154       g_object_unref (tree_view->priv->vadjustment);
8155     }
8156 
8157   if (tree_view->priv->hadjustment != hadj)
8158     {
8159       tree_view->priv->hadjustment = hadj;
8160       g_object_ref_sink (tree_view->priv->hadjustment);
8161 
8162       g_signal_connect (tree_view->priv->hadjustment, "value-changed",
8163 			G_CALLBACK (gtk_tree_view_adjustment_changed),
8164 			tree_view);
8165       need_adjust = TRUE;
8166     }
8167 
8168   if (tree_view->priv->vadjustment != vadj)
8169     {
8170       tree_view->priv->vadjustment = vadj;
8171       g_object_ref_sink (tree_view->priv->vadjustment);
8172 
8173       g_signal_connect (tree_view->priv->vadjustment, "value-changed",
8174 			G_CALLBACK (gtk_tree_view_adjustment_changed),
8175 			tree_view);
8176       need_adjust = TRUE;
8177     }
8178 
8179   if (need_adjust)
8180     gtk_tree_view_adjustment_changed (NULL, tree_view);
8181 }
8182 
8183 
8184 static gboolean
gtk_tree_view_real_move_cursor(GtkTreeView * tree_view,GtkMovementStep step,gint count)8185 gtk_tree_view_real_move_cursor (GtkTreeView       *tree_view,
8186 				GtkMovementStep    step,
8187 				gint               count)
8188 {
8189   GdkModifierType state;
8190 
8191   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
8192   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
8193 			step == GTK_MOVEMENT_VISUAL_POSITIONS ||
8194 			step == GTK_MOVEMENT_DISPLAY_LINES ||
8195 			step == GTK_MOVEMENT_PAGES ||
8196 			step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
8197 
8198   if (tree_view->priv->tree == NULL)
8199     return FALSE;
8200   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8201     return FALSE;
8202 
8203   gtk_tree_view_stop_editing (tree_view, FALSE);
8204   GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
8205   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8206 
8207   if (gtk_get_current_event_state (&state))
8208     {
8209       if ((state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
8210         tree_view->priv->modify_selection_pressed = TRUE;
8211       if ((state & GTK_EXTEND_SELECTION_MOD_MASK) == GTK_EXTEND_SELECTION_MOD_MASK)
8212         tree_view->priv->extend_selection_pressed = TRUE;
8213     }
8214   /* else we assume not pressed */
8215 
8216   switch (step)
8217     {
8218       /* currently we make no distinction.  When we go bi-di, we need to */
8219     case GTK_MOVEMENT_LOGICAL_POSITIONS:
8220     case GTK_MOVEMENT_VISUAL_POSITIONS:
8221       gtk_tree_view_move_cursor_left_right (tree_view, count);
8222       break;
8223     case GTK_MOVEMENT_DISPLAY_LINES:
8224       gtk_tree_view_move_cursor_up_down (tree_view, count);
8225       break;
8226     case GTK_MOVEMENT_PAGES:
8227       gtk_tree_view_move_cursor_page_up_down (tree_view, count);
8228       break;
8229     case GTK_MOVEMENT_BUFFER_ENDS:
8230       gtk_tree_view_move_cursor_start_end (tree_view, count);
8231       break;
8232     default:
8233       g_assert_not_reached ();
8234     }
8235 
8236   tree_view->priv->modify_selection_pressed = FALSE;
8237   tree_view->priv->extend_selection_pressed = FALSE;
8238 
8239   return TRUE;
8240 }
8241 
8242 static void
gtk_tree_view_put(GtkTreeView * tree_view,GtkWidget * child_widget,gint x,gint y,gint width,gint height)8243 gtk_tree_view_put (GtkTreeView *tree_view,
8244 		   GtkWidget   *child_widget,
8245 		   /* in bin_window coordinates */
8246 		   gint         x,
8247 		   gint         y,
8248 		   gint         width,
8249 		   gint         height)
8250 {
8251   GtkTreeViewChild *child;
8252 
8253   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
8254   g_return_if_fail (GTK_IS_WIDGET (child_widget));
8255 
8256   child = g_slice_new (GtkTreeViewChild);
8257 
8258   child->widget = child_widget;
8259   child->x = x;
8260   child->y = y;
8261   child->width = width;
8262   child->height = height;
8263 
8264   tree_view->priv->children = g_list_append (tree_view->priv->children, child);
8265 
8266   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8267     gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
8268 
8269   gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
8270 }
8271 
8272 void
_gtk_tree_view_child_move_resize(GtkTreeView * tree_view,GtkWidget * widget,gint x,gint y,gint width,gint height)8273 _gtk_tree_view_child_move_resize (GtkTreeView *tree_view,
8274 				  GtkWidget   *widget,
8275 				  /* in tree coordinates */
8276 				  gint         x,
8277 				  gint         y,
8278 				  gint         width,
8279 				  gint         height)
8280 {
8281   GtkTreeViewChild *child = NULL;
8282   GList *list;
8283   GdkRectangle allocation;
8284 
8285   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
8286   g_return_if_fail (GTK_IS_WIDGET (widget));
8287 
8288   for (list = tree_view->priv->children; list; list = list->next)
8289     {
8290       if (((GtkTreeViewChild *)list->data)->widget == widget)
8291 	{
8292 	  child = list->data;
8293 	  break;
8294 	}
8295     }
8296   if (child == NULL)
8297     return;
8298 
8299   allocation.x = child->x = x;
8300   allocation.y = child->y = y;
8301   allocation.width = child->width = width;
8302   allocation.height = child->height = height;
8303 
8304   if (gtk_widget_get_realized (widget))
8305     gtk_widget_size_allocate (widget, &allocation);
8306 }
8307 
8308 
8309 /* TreeModel Callbacks
8310  */
8311 
8312 static void
gtk_tree_view_row_changed(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8313 gtk_tree_view_row_changed (GtkTreeModel *model,
8314 			   GtkTreePath  *path,
8315 			   GtkTreeIter  *iter,
8316 			   gpointer      data)
8317 {
8318   GtkTreeView *tree_view = (GtkTreeView *)data;
8319   GtkRBTree *tree;
8320   GtkRBNode *node;
8321   gboolean free_path = FALSE;
8322   GList *list;
8323   GtkTreePath *cursor_path;
8324 
8325   g_return_if_fail (path != NULL || iter != NULL);
8326 
8327   if (tree_view->priv->cursor != NULL)
8328     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8329   else
8330     cursor_path = NULL;
8331 
8332   if (tree_view->priv->edited_column &&
8333       (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
8334     gtk_tree_view_stop_editing (tree_view, TRUE);
8335 
8336   if (cursor_path != NULL)
8337     gtk_tree_path_free (cursor_path);
8338 
8339   if (path == NULL)
8340     {
8341       path = gtk_tree_model_get_path (model, iter);
8342       free_path = TRUE;
8343     }
8344   else if (iter == NULL)
8345     gtk_tree_model_get_iter (model, iter, path);
8346 
8347   if (_gtk_tree_view_find_node (tree_view,
8348 				path,
8349 				&tree,
8350 				&node))
8351     /* We aren't actually showing the node */
8352     goto done;
8353 
8354   if (tree == NULL)
8355     goto done;
8356 
8357   if (tree_view->priv->fixed_height_mode
8358       && tree_view->priv->fixed_height >= 0)
8359     {
8360       _gtk_rbtree_node_set_height (tree, node, tree_view->priv->fixed_height);
8361       if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8362 	gtk_tree_view_node_queue_redraw (tree_view, tree, node);
8363     }
8364   else
8365     {
8366       _gtk_rbtree_node_mark_invalid (tree, node);
8367       for (list = tree_view->priv->columns; list; list = list->next)
8368         {
8369           GtkTreeViewColumn *column;
8370 
8371           column = list->data;
8372           if (! column->visible)
8373             continue;
8374 
8375           if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
8376             {
8377               _gtk_tree_view_column_cell_set_dirty (column, TRUE);
8378             }
8379         }
8380     }
8381 
8382  done:
8383   if (!tree_view->priv->fixed_height_mode &&
8384       gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8385     install_presize_handler (tree_view);
8386   if (free_path)
8387     gtk_tree_path_free (path);
8388 }
8389 
8390 static void
gtk_tree_view_row_inserted(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8391 gtk_tree_view_row_inserted (GtkTreeModel *model,
8392 			    GtkTreePath  *path,
8393 			    GtkTreeIter  *iter,
8394 			    gpointer      data)
8395 {
8396   GtkTreeView *tree_view = (GtkTreeView *) data;
8397   gint *indices;
8398   GtkRBTree *tmptree, *tree;
8399   GtkRBNode *tmpnode = NULL;
8400   gint depth;
8401   gint i = 0;
8402   gint height;
8403   gboolean free_path = FALSE;
8404   gboolean node_visible = TRUE;
8405 
8406   g_return_if_fail (path != NULL || iter != NULL);
8407 
8408   if (tree_view->priv->fixed_height_mode
8409       && tree_view->priv->fixed_height >= 0)
8410     height = tree_view->priv->fixed_height;
8411   else
8412     height = 0;
8413 
8414   if (path == NULL)
8415     {
8416       path = gtk_tree_model_get_path (model, iter);
8417       free_path = TRUE;
8418     }
8419   else if (iter == NULL)
8420     gtk_tree_model_get_iter (model, iter, path);
8421 
8422   if (tree_view->priv->tree == NULL)
8423     tree_view->priv->tree = _gtk_rbtree_new ();
8424 
8425   tmptree = tree = tree_view->priv->tree;
8426 
8427   /* Update all row-references */
8428   gtk_tree_row_reference_inserted (G_OBJECT (data), path);
8429   depth = gtk_tree_path_get_depth (path);
8430   indices = gtk_tree_path_get_indices (path);
8431 
8432   /* First, find the parent tree */
8433   while (i < depth - 1)
8434     {
8435       if (tmptree == NULL)
8436 	{
8437 	  /* We aren't showing the node */
8438 	  node_visible = FALSE;
8439           goto done;
8440 	}
8441 
8442       tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
8443       if (tmpnode == NULL)
8444 	{
8445 	  g_warning ("A node was inserted with a parent that's not in the tree.\n" \
8446 		     "This possibly means that a GtkTreeModel inserted a child node\n" \
8447 		     "before the parent was inserted.");
8448           goto done;
8449 	}
8450       else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT))
8451 	{
8452           /* FIXME enforce correct behavior on model, probably */
8453 	  /* In theory, the model should have emitted has_child_toggled here.  We
8454 	   * try to catch it anyway, just to be safe, in case the model hasn't.
8455 	   */
8456 	  GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view,
8457 							   tree,
8458 							   tmpnode);
8459 	  gtk_tree_view_row_has_child_toggled (model, tmppath, NULL, data);
8460 	  gtk_tree_path_free (tmppath);
8461           goto done;
8462 	}
8463 
8464       tmptree = tmpnode->children;
8465       tree = tmptree;
8466       i++;
8467     }
8468 
8469   if (tree == NULL)
8470     {
8471       node_visible = FALSE;
8472       goto done;
8473     }
8474 
8475   /* ref the node */
8476   gtk_tree_model_ref_node (tree_view->priv->model, iter);
8477   if (indices[depth - 1] == 0)
8478     {
8479       tmpnode = _gtk_rbtree_find_count (tree, 1);
8480       tmpnode = _gtk_rbtree_insert_before (tree, tmpnode, height, FALSE);
8481     }
8482   else
8483     {
8484       tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]);
8485       tmpnode = _gtk_rbtree_insert_after (tree, tmpnode, height, FALSE);
8486     }
8487 
8488  done:
8489   if (height > 0)
8490     {
8491       if (tree)
8492         _gtk_rbtree_node_mark_valid (tree, tmpnode);
8493 
8494       if (node_visible && node_is_visible (tree_view, tree, tmpnode))
8495 	gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8496       else
8497 	gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
8498     }
8499   else
8500     install_presize_handler (tree_view);
8501   if (free_path)
8502     gtk_tree_path_free (path);
8503 }
8504 
8505 static void
gtk_tree_view_row_has_child_toggled(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)8506 gtk_tree_view_row_has_child_toggled (GtkTreeModel *model,
8507 				     GtkTreePath  *path,
8508 				     GtkTreeIter  *iter,
8509 				     gpointer      data)
8510 {
8511   GtkTreeView *tree_view = (GtkTreeView *)data;
8512   GtkTreeIter real_iter;
8513   gboolean has_child;
8514   GtkRBTree *tree;
8515   GtkRBNode *node;
8516   gboolean free_path = FALSE;
8517 
8518   g_return_if_fail (path != NULL || iter != NULL);
8519 
8520   if (iter)
8521     real_iter = *iter;
8522 
8523   if (path == NULL)
8524     {
8525       path = gtk_tree_model_get_path (model, iter);
8526       free_path = TRUE;
8527     }
8528   else if (iter == NULL)
8529     gtk_tree_model_get_iter (model, &real_iter, path);
8530 
8531   if (_gtk_tree_view_find_node (tree_view,
8532 				path,
8533 				&tree,
8534 				&node))
8535     /* We aren't actually showing the node */
8536     goto done;
8537 
8538   if (tree == NULL)
8539     goto done;
8540 
8541   has_child = gtk_tree_model_iter_has_child (model, &real_iter);
8542   /* Sanity check.
8543    */
8544   if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child)
8545     goto done;
8546 
8547   if (has_child)
8548     GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT);
8549   else
8550     GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
8551 
8552   if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
8553     {
8554       GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
8555       if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
8556 	{
8557 	  GList *list;
8558 
8559 	  for (list = tree_view->priv->columns; list; list = list->next)
8560 	    if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
8561 	      {
8562 		GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE;
8563 		_gtk_tree_view_column_cell_set_dirty (GTK_TREE_VIEW_COLUMN (list->data), TRUE);
8564 		break;
8565 	      }
8566 	}
8567       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8568     }
8569   else
8570     {
8571       _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
8572     }
8573 
8574  done:
8575   if (free_path)
8576     gtk_tree_path_free (path);
8577 }
8578 
8579 static void
count_children_helper(GtkRBTree * tree,GtkRBNode * node,gpointer data)8580 count_children_helper (GtkRBTree *tree,
8581 		       GtkRBNode *node,
8582 		       gpointer   data)
8583 {
8584   if (node->children)
8585     _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, data);
8586   (*((gint *)data))++;
8587 }
8588 
8589 static void
check_selection_helper(GtkRBTree * tree,GtkRBNode * node,gpointer data)8590 check_selection_helper (GtkRBTree *tree,
8591                         GtkRBNode *node,
8592                         gpointer   data)
8593 {
8594   gint *value = (gint *)data;
8595 
8596   *value = GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
8597 
8598   if (node->children && !*value)
8599     _gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, check_selection_helper, data);
8600 }
8601 
8602 static void
gtk_tree_view_row_deleted(GtkTreeModel * model,GtkTreePath * path,gpointer data)8603 gtk_tree_view_row_deleted (GtkTreeModel *model,
8604 			   GtkTreePath  *path,
8605 			   gpointer      data)
8606 {
8607   GtkTreeView *tree_view = (GtkTreeView *)data;
8608   GtkRBTree *tree;
8609   GtkRBNode *node;
8610   GList *list;
8611   gint selection_changed = FALSE;
8612 
8613   g_return_if_fail (path != NULL);
8614 
8615   gtk_tree_row_reference_deleted (G_OBJECT (data), path);
8616 
8617   if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
8618     return;
8619 
8620   if (tree == NULL)
8621     return;
8622 
8623   /* check if the selection has been changed */
8624   _gtk_rbtree_traverse (tree, node, G_POST_ORDER,
8625                         check_selection_helper, &selection_changed);
8626 
8627   for (list = tree_view->priv->columns; list; list = list->next)
8628     if (((GtkTreeViewColumn *)list->data)->visible &&
8629 	((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
8630       _gtk_tree_view_column_cell_set_dirty ((GtkTreeViewColumn *)list->data, TRUE);
8631 
8632   /* Ensure we don't have a dangling pointer to a dead node */
8633   ensure_unprelighted (tree_view);
8634 
8635   /* Cancel editting if we've started */
8636   gtk_tree_view_stop_editing (tree_view, TRUE);
8637 
8638   /* If we have a node expanded/collapsed timeout, remove it */
8639   remove_expand_collapse_timeout (tree_view);
8640 
8641   if (tree_view->priv->destroy_count_func)
8642     {
8643       gint child_count = 0;
8644       if (node->children)
8645 	_gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, &child_count);
8646       tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
8647     }
8648 
8649   if (tree->root->count == 1)
8650     {
8651       if (tree_view->priv->tree == tree)
8652 	tree_view->priv->tree = NULL;
8653 
8654       _gtk_rbtree_remove (tree);
8655     }
8656   else
8657     {
8658       _gtk_rbtree_remove_node (tree, node);
8659     }
8660 
8661   if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
8662     {
8663       gtk_tree_row_reference_free (tree_view->priv->top_row);
8664       tree_view->priv->top_row = NULL;
8665     }
8666 
8667   install_scroll_sync_handler (tree_view);
8668 
8669   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8670 
8671   if (selection_changed)
8672     g_signal_emit_by_name (tree_view->priv->selection, "changed");
8673 }
8674 
8675 static void
gtk_tree_view_rows_reordered(GtkTreeModel * model,GtkTreePath * parent,GtkTreeIter * iter,gint * new_order,gpointer data)8676 gtk_tree_view_rows_reordered (GtkTreeModel *model,
8677 			      GtkTreePath  *parent,
8678 			      GtkTreeIter  *iter,
8679 			      gint         *new_order,
8680 			      gpointer      data)
8681 {
8682   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
8683   GtkRBTree *tree;
8684   GtkRBNode *node;
8685   gint len;
8686 
8687   len = gtk_tree_model_iter_n_children (model, iter);
8688 
8689   if (len < 2)
8690     return;
8691 
8692   gtk_tree_row_reference_reordered (G_OBJECT (data),
8693 				    parent,
8694 				    iter,
8695 				    new_order);
8696 
8697   if (_gtk_tree_view_find_node (tree_view,
8698 				parent,
8699 				&tree,
8700 				&node))
8701     return;
8702 
8703   /* We need to special case the parent path */
8704   if (tree == NULL)
8705     tree = tree_view->priv->tree;
8706   else
8707     tree = node->children;
8708 
8709   if (tree == NULL)
8710     return;
8711 
8712   if (tree_view->priv->edited_column)
8713     gtk_tree_view_stop_editing (tree_view, TRUE);
8714 
8715   /* we need to be unprelighted */
8716   ensure_unprelighted (tree_view);
8717 
8718   /* clear the timeout */
8719   cancel_arrow_animation (tree_view);
8720 
8721   _gtk_rbtree_reorder (tree, new_order, len);
8722 
8723   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
8724 
8725   gtk_tree_view_dy_to_top_row (tree_view);
8726 }
8727 
8728 
8729 /* Internal tree functions
8730  */
8731 
8732 
8733 static void
gtk_tree_view_get_background_xrange(GtkTreeView * tree_view,GtkRBTree * tree,GtkTreeViewColumn * column,gint * x1,gint * x2)8734 gtk_tree_view_get_background_xrange (GtkTreeView       *tree_view,
8735                                      GtkRBTree         *tree,
8736                                      GtkTreeViewColumn *column,
8737                                      gint              *x1,
8738                                      gint              *x2)
8739 {
8740   GtkTreeViewColumn *tmp_column = NULL;
8741   gint total_width;
8742   GList *list;
8743   gboolean rtl;
8744 
8745   if (x1)
8746     *x1 = 0;
8747 
8748   if (x2)
8749     *x2 = 0;
8750 
8751   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8752 
8753   total_width = 0;
8754   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
8755        list;
8756        list = (rtl ? list->prev : list->next))
8757     {
8758       tmp_column = list->data;
8759 
8760       if (tmp_column == column)
8761         break;
8762 
8763       if (tmp_column->visible)
8764         total_width += tmp_column->width;
8765     }
8766 
8767   if (tmp_column != column)
8768     {
8769       g_warning (G_STRLOC": passed-in column isn't in the tree");
8770       return;
8771     }
8772 
8773   if (x1)
8774     *x1 = total_width;
8775 
8776   if (x2)
8777     {
8778       if (column->visible)
8779         *x2 = total_width + column->width;
8780       else
8781         *x2 = total_width; /* width of 0 */
8782     }
8783 }
8784 static void
gtk_tree_view_get_arrow_xrange(GtkTreeView * tree_view,GtkRBTree * tree,gint * x1,gint * x2)8785 gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
8786 				GtkRBTree   *tree,
8787                                 gint        *x1,
8788                                 gint        *x2)
8789 {
8790   gint x_offset = 0;
8791   GList *list;
8792   GtkTreeViewColumn *tmp_column = NULL;
8793   gint total_width;
8794   gboolean indent_expanders;
8795   gboolean rtl;
8796 
8797   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8798 
8799   total_width = 0;
8800   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
8801        list;
8802        list = (rtl ? list->prev : list->next))
8803     {
8804       tmp_column = list->data;
8805 
8806       if (gtk_tree_view_is_expander_column (tree_view, tmp_column))
8807         {
8808 	  if (rtl)
8809 	    x_offset = total_width + tmp_column->width - tree_view->priv->expander_size;
8810 	  else
8811 	    x_offset = total_width;
8812           break;
8813         }
8814 
8815       if (tmp_column->visible)
8816         total_width += tmp_column->width;
8817     }
8818 
8819   gtk_widget_style_get (GTK_WIDGET (tree_view),
8820 			"indent-expanders", &indent_expanders,
8821 			NULL);
8822 
8823   if (indent_expanders)
8824     {
8825       if (rtl)
8826 	x_offset -= tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree);
8827       else
8828 	x_offset += tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree);
8829     }
8830 
8831   *x1 = x_offset;
8832 
8833   if (tmp_column && tmp_column->visible)
8834     /* +1 because x2 isn't included in the range. */
8835     *x2 = *x1 + tree_view->priv->expander_size + 1;
8836   else
8837     *x2 = *x1;
8838 }
8839 
8840 static void
gtk_tree_view_build_tree(GtkTreeView * tree_view,GtkRBTree * tree,GtkTreeIter * iter,gint depth,gboolean recurse)8841 gtk_tree_view_build_tree (GtkTreeView *tree_view,
8842 			  GtkRBTree   *tree,
8843 			  GtkTreeIter *iter,
8844 			  gint         depth,
8845 			  gboolean     recurse)
8846 {
8847   GtkRBNode *temp = NULL;
8848   GtkTreePath *path = NULL;
8849   gboolean is_list = GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST);
8850 
8851   do
8852     {
8853       gtk_tree_model_ref_node (tree_view->priv->model, iter);
8854       temp = _gtk_rbtree_insert_after (tree, temp, 0, FALSE);
8855 
8856       if (tree_view->priv->fixed_height > 0)
8857         {
8858           if (GTK_RBNODE_FLAG_SET (temp, GTK_RBNODE_INVALID))
8859 	    {
8860               _gtk_rbtree_node_set_height (tree, temp, tree_view->priv->fixed_height);
8861 	      _gtk_rbtree_node_mark_valid (tree, temp);
8862 	    }
8863         }
8864 
8865       if (is_list)
8866         continue;
8867 
8868       if (recurse)
8869 	{
8870 	  GtkTreeIter child;
8871 
8872 	  if (!path)
8873 	    path = gtk_tree_model_get_path (tree_view->priv->model, iter);
8874 	  else
8875 	    gtk_tree_path_next (path);
8876 
8877 	  if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter))
8878 	    {
8879 	      gboolean expand;
8880 
8881 	      g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, iter, path, &expand);
8882 
8883 	      if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter)
8884 		  && !expand)
8885 	        {
8886 	          temp->children = _gtk_rbtree_new ();
8887 	          temp->children->parent_tree = tree;
8888 	          temp->children->parent_node = temp;
8889 	          gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse);
8890 		}
8891 	    }
8892 	}
8893 
8894       if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter))
8895 	{
8896 	  if ((temp->flags&GTK_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT)
8897 	    temp->flags ^= GTK_RBNODE_IS_PARENT;
8898 	}
8899     }
8900   while (gtk_tree_model_iter_next (tree_view->priv->model, iter));
8901 
8902   if (path)
8903     gtk_tree_path_free (path);
8904 }
8905 
8906 /* Make sure the node is visible vertically */
8907 static void
gtk_tree_view_clamp_node_visible(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)8908 gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
8909 				  GtkRBTree   *tree,
8910 				  GtkRBNode   *node)
8911 {
8912   gint node_dy, height;
8913   GtkTreePath *path = NULL;
8914 
8915   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8916     return;
8917 
8918   /* just return if the node is visible, avoiding a costly expose */
8919   node_dy = _gtk_rbtree_node_find_offset (tree, node);
8920   height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
8921   if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID)
8922       && node_dy >= tree_view->priv->vadjustment->value
8923       && node_dy + height <= (tree_view->priv->vadjustment->value
8924                               + tree_view->priv->vadjustment->page_size))
8925     return;
8926 
8927   path = _gtk_tree_view_find_path (tree_view, tree, node);
8928   if (path)
8929     {
8930       /* We process updates because we want to clear old selected items when we scroll.
8931        * if this is removed, we get a "selection streak" at the bottom. */
8932       gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
8933       gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
8934       gtk_tree_path_free (path);
8935     }
8936 }
8937 
8938 static void
gtk_tree_view_clamp_column_visible(GtkTreeView * tree_view,GtkTreeViewColumn * column,gboolean focus_to_cell)8939 gtk_tree_view_clamp_column_visible (GtkTreeView       *tree_view,
8940 				    GtkTreeViewColumn *column,
8941 				    gboolean           focus_to_cell)
8942 {
8943   gint x, width;
8944 
8945   if (column == NULL)
8946     return;
8947 
8948   x = column->button->allocation.x;
8949   width = column->button->allocation.width;
8950 
8951   if (width > tree_view->priv->hadjustment->page_size)
8952     {
8953       /* The column is larger than the horizontal page size.  If the
8954        * column has cells which can be focussed individually, then we make
8955        * sure the cell which gets focus is fully visible (if even the
8956        * focus cell is bigger than the page size, we make sure the
8957        * left-hand side of the cell is visible).
8958        *
8959        * If the column does not have those so-called special cells, we
8960        * make sure the left-hand side of the column is visible.
8961        */
8962 
8963       if (focus_to_cell && gtk_tree_view_has_special_cell (tree_view))
8964         {
8965 	  GtkTreePath *cursor_path;
8966 	  GdkRectangle background_area, cell_area, focus_area;
8967 
8968 	  cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8969 
8970 	  gtk_tree_view_get_cell_area (tree_view,
8971 				       cursor_path, column, &cell_area);
8972 	  gtk_tree_view_get_background_area (tree_view,
8973 					     cursor_path, column,
8974 					     &background_area);
8975 
8976 	  gtk_tree_path_free (cursor_path);
8977 
8978 	  _gtk_tree_view_column_get_focus_area (column,
8979 						&background_area,
8980 						&cell_area,
8981 						&focus_area);
8982 
8983 	  x = focus_area.x;
8984 	  width = focus_area.width;
8985 
8986 	  if (width < tree_view->priv->hadjustment->page_size)
8987 	    {
8988 	      if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
8989 		gtk_adjustment_set_value (tree_view->priv->hadjustment,
8990 					  x + width - tree_view->priv->hadjustment->page_size);
8991 	      else if (tree_view->priv->hadjustment->value > x)
8992 		gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
8993 	    }
8994 	}
8995 
8996       gtk_adjustment_set_value (tree_view->priv->hadjustment,
8997 				CLAMP (x,
8998 				       tree_view->priv->hadjustment->lower,
8999 				       tree_view->priv->hadjustment->upper
9000 				       - tree_view->priv->hadjustment->page_size));
9001     }
9002   else
9003     {
9004       if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
9005 	  gtk_adjustment_set_value (tree_view->priv->hadjustment,
9006 				    x + width - tree_view->priv->hadjustment->page_size);
9007       else if (tree_view->priv->hadjustment->value > x)
9008 	gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
9009   }
9010 }
9011 
9012 /* This function could be more efficient.  I'll optimize it if profiling seems
9013  * to imply that it is important */
9014 GtkTreePath *
_gtk_tree_view_find_path(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node)9015 _gtk_tree_view_find_path (GtkTreeView *tree_view,
9016 			  GtkRBTree   *tree,
9017 			  GtkRBNode   *node)
9018 {
9019   GtkTreePath *path;
9020   GtkRBTree *tmp_tree;
9021   GtkRBNode *tmp_node, *last;
9022   gint count;
9023 
9024   path = gtk_tree_path_new ();
9025 
9026   g_return_val_if_fail (node != NULL, path);
9027   g_return_val_if_fail (node != tree->nil, path);
9028 
9029   count = 1 + node->left->count;
9030 
9031   last = node;
9032   tmp_node = node->parent;
9033   tmp_tree = tree;
9034   while (tmp_tree)
9035     {
9036       while (tmp_node != tmp_tree->nil)
9037 	{
9038 	  if (tmp_node->right == last)
9039 	    count += 1 + tmp_node->left->count;
9040 	  last = tmp_node;
9041 	  tmp_node = tmp_node->parent;
9042 	}
9043       gtk_tree_path_prepend_index (path, count - 1);
9044       last = tmp_tree->parent_node;
9045       tmp_tree = tmp_tree->parent_tree;
9046       if (last)
9047 	{
9048 	  count = 1 + last->left->count;
9049 	  tmp_node = last->parent;
9050 	}
9051     }
9052   return path;
9053 }
9054 
9055 /* Returns TRUE if we ran out of tree before finding the path.  If the path is
9056  * invalid (ie. points to a node that's not in the tree), *tree and *node are
9057  * both set to NULL.
9058  */
9059 gboolean
_gtk_tree_view_find_node(GtkTreeView * tree_view,GtkTreePath * path,GtkRBTree ** tree,GtkRBNode ** node)9060 _gtk_tree_view_find_node (GtkTreeView  *tree_view,
9061 			  GtkTreePath  *path,
9062 			  GtkRBTree   **tree,
9063 			  GtkRBNode   **node)
9064 {
9065   GtkRBNode *tmpnode = NULL;
9066   GtkRBTree *tmptree = tree_view->priv->tree;
9067   gint *indices = gtk_tree_path_get_indices (path);
9068   gint depth = gtk_tree_path_get_depth (path);
9069   gint i = 0;
9070 
9071   *node = NULL;
9072   *tree = NULL;
9073 
9074   if (depth == 0 || tmptree == NULL)
9075     return FALSE;
9076   do
9077     {
9078       tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
9079       ++i;
9080       if (tmpnode == NULL)
9081 	{
9082 	  *tree = NULL;
9083 	  *node = NULL;
9084 	  return FALSE;
9085 	}
9086       if (i >= depth)
9087 	{
9088 	  *tree = tmptree;
9089 	  *node = tmpnode;
9090 	  return FALSE;
9091 	}
9092       *tree = tmptree;
9093       *node = tmpnode;
9094       tmptree = tmpnode->children;
9095       if (tmptree == NULL)
9096 	return TRUE;
9097     }
9098   while (1);
9099 }
9100 
9101 static gboolean
gtk_tree_view_is_expander_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)9102 gtk_tree_view_is_expander_column (GtkTreeView       *tree_view,
9103 				  GtkTreeViewColumn *column)
9104 {
9105   GList *list;
9106 
9107   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
9108     return FALSE;
9109 
9110   if (tree_view->priv->expander_column != NULL)
9111     {
9112       if (tree_view->priv->expander_column == column)
9113 	return TRUE;
9114       return FALSE;
9115     }
9116   else
9117     {
9118       for (list = tree_view->priv->columns;
9119 	   list;
9120 	   list = list->next)
9121 	if (((GtkTreeViewColumn *)list->data)->visible)
9122 	  break;
9123       if (list && list->data == column)
9124 	return TRUE;
9125     }
9126   return FALSE;
9127 }
9128 
9129 static void
gtk_tree_view_add_move_binding(GtkBindingSet * binding_set,guint keyval,guint modmask,gboolean add_shifted_binding,GtkMovementStep step,gint count)9130 gtk_tree_view_add_move_binding (GtkBindingSet  *binding_set,
9131 				guint           keyval,
9132 				guint           modmask,
9133 				gboolean        add_shifted_binding,
9134 				GtkMovementStep step,
9135 				gint            count)
9136 {
9137 
9138   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
9139                                 "move-cursor", 2,
9140                                 G_TYPE_ENUM, step,
9141                                 G_TYPE_INT, count);
9142 
9143   if (add_shifted_binding)
9144     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
9145 				  "move-cursor", 2,
9146 				  G_TYPE_ENUM, step,
9147 				  G_TYPE_INT, count);
9148 
9149   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
9150    return;
9151 
9152   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
9153                                 "move-cursor", 2,
9154                                 G_TYPE_ENUM, step,
9155                                 G_TYPE_INT, count);
9156 
9157   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
9158                                 "move-cursor", 2,
9159                                 G_TYPE_ENUM, step,
9160                                 G_TYPE_INT, count);
9161 }
9162 
9163 static gint
gtk_tree_view_unref_tree_helper(GtkTreeModel * model,GtkTreeIter * iter,GtkRBTree * tree,GtkRBNode * node)9164 gtk_tree_view_unref_tree_helper (GtkTreeModel *model,
9165 				 GtkTreeIter  *iter,
9166 				 GtkRBTree    *tree,
9167 				 GtkRBNode    *node)
9168 {
9169   gint retval = FALSE;
9170   do
9171     {
9172       g_return_val_if_fail (node != NULL, FALSE);
9173 
9174       if (node->children)
9175 	{
9176 	  GtkTreeIter child;
9177 	  GtkRBTree *new_tree;
9178 	  GtkRBNode *new_node;
9179 
9180 	  new_tree = node->children;
9181 	  new_node = new_tree->root;
9182 
9183 	  while (new_node && new_node->left != new_tree->nil)
9184 	    new_node = new_node->left;
9185 
9186 	  if (!gtk_tree_model_iter_children (model, &child, iter))
9187 	    return FALSE;
9188 
9189 	  retval = retval || gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node);
9190 	}
9191 
9192       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
9193 	retval = TRUE;
9194       gtk_tree_model_unref_node (model, iter);
9195       node = _gtk_rbtree_next (tree, node);
9196     }
9197   while (gtk_tree_model_iter_next (model, iter));
9198 
9199   return retval;
9200 }
9201 
9202 static gint
gtk_tree_view_unref_and_check_selection_tree(GtkTreeView * tree_view,GtkRBTree * tree)9203 gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view,
9204 					      GtkRBTree   *tree)
9205 {
9206   GtkTreeIter iter;
9207   GtkTreePath *path;
9208   GtkRBNode *node;
9209   gint retval;
9210 
9211   if (!tree)
9212     return FALSE;
9213 
9214   node = tree->root;
9215   while (node && node->left != tree->nil)
9216     node = node->left;
9217 
9218   g_return_val_if_fail (node != NULL, FALSE);
9219   path = _gtk_tree_view_find_path (tree_view, tree, node);
9220   gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model),
9221 			   &iter, path);
9222   retval = gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (tree_view->priv->model), &iter, tree, node);
9223   gtk_tree_path_free (path);
9224 
9225   return retval;
9226 }
9227 
9228 static void
gtk_tree_view_set_column_drag_info(GtkTreeView * tree_view,GtkTreeViewColumn * column)9229 gtk_tree_view_set_column_drag_info (GtkTreeView       *tree_view,
9230 				    GtkTreeViewColumn *column)
9231 {
9232   GtkTreeViewColumn *left_column;
9233   GtkTreeViewColumn *cur_column = NULL;
9234   GtkTreeViewColumnReorder *reorder;
9235   gboolean rtl;
9236   GList *tmp_list;
9237   gint left;
9238 
9239   /* We want to precalculate the motion list such that we know what column slots
9240    * are available.
9241    */
9242   left_column = NULL;
9243   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9244 
9245   /* First, identify all possible drop spots */
9246   if (rtl)
9247     tmp_list = g_list_last (tree_view->priv->columns);
9248   else
9249     tmp_list = g_list_first (tree_view->priv->columns);
9250 
9251   while (tmp_list)
9252     {
9253       cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data);
9254       tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
9255 
9256       if (cur_column->visible == FALSE)
9257 	continue;
9258 
9259       /* If it's not the column moving and func tells us to skip over the column, we continue. */
9260       if (left_column != column && cur_column != column &&
9261 	  tree_view->priv->column_drop_func &&
9262 	  ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
9263 	{
9264 	  left_column = cur_column;
9265 	  continue;
9266 	}
9267       reorder = g_slice_new0 (GtkTreeViewColumnReorder);
9268       reorder->left_column = left_column;
9269       left_column = reorder->right_column = cur_column;
9270 
9271       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
9272     }
9273 
9274   /* Add the last one */
9275   if (tree_view->priv->column_drop_func == NULL ||
9276       ((left_column != column) &&
9277        tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
9278     {
9279       reorder = g_slice_new0 (GtkTreeViewColumnReorder);
9280       reorder->left_column = left_column;
9281       reorder->right_column = NULL;
9282       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
9283     }
9284 
9285   /* We quickly check to see if it even makes sense to reorder columns. */
9286   /* If there is nothing that can be moved, then we return */
9287 
9288   if (tree_view->priv->column_drag_info == NULL)
9289     return;
9290 
9291   /* We know there are always 2 slots possbile, as you can always return column. */
9292   /* If that's all there is, return */
9293   if (tree_view->priv->column_drag_info->next == NULL ||
9294       (tree_view->priv->column_drag_info->next->next == NULL &&
9295        ((GtkTreeViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
9296        ((GtkTreeViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
9297     {
9298       for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
9299 	g_slice_free (GtkTreeViewColumnReorder, tmp_list->data);
9300       g_list_free (tree_view->priv->column_drag_info);
9301       tree_view->priv->column_drag_info = NULL;
9302       return;
9303     }
9304   /* We fill in the ranges for the columns, now that we've isolated them */
9305   left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
9306 
9307   for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
9308     {
9309       reorder = (GtkTreeViewColumnReorder *) tmp_list->data;
9310 
9311       reorder->left_align = left;
9312       if (tmp_list->next != NULL)
9313 	{
9314 	  g_assert (tmp_list->next->data);
9315 	  left = reorder->right_align = (reorder->right_column->button->allocation.x +
9316 					 reorder->right_column->button->allocation.width +
9317 					 ((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column->button->allocation.x)/2;
9318 	}
9319       else
9320 	{
9321 	  gint width;
9322 
9323           width = gdk_window_get_width (tree_view->priv->header_window);
9324 	  reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
9325 	}
9326     }
9327 }
9328 
9329 void
_gtk_tree_view_column_start_drag(GtkTreeView * tree_view,GtkTreeViewColumn * column)9330 _gtk_tree_view_column_start_drag (GtkTreeView       *tree_view,
9331 				  GtkTreeViewColumn *column)
9332 {
9333   GdkEvent *send_event;
9334   GtkAllocation allocation;
9335   gint x, y, width, height;
9336   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
9337   GdkDisplay *display = gdk_screen_get_display (screen);
9338 
9339   g_return_if_fail (tree_view->priv->column_drag_info == NULL);
9340   g_return_if_fail (tree_view->priv->cur_reorder == NULL);
9341 
9342   gtk_tree_view_set_column_drag_info (tree_view, column);
9343 
9344   if (tree_view->priv->column_drag_info == NULL)
9345     return;
9346 
9347   if (tree_view->priv->drag_window == NULL)
9348     {
9349       GdkWindowAttr attributes;
9350       guint attributes_mask;
9351 
9352       attributes.window_type = GDK_WINDOW_CHILD;
9353       attributes.wclass = GDK_INPUT_OUTPUT;
9354       attributes.x = column->button->allocation.x;
9355       attributes.y = 0;
9356       attributes.width = column->button->allocation.width;
9357       attributes.height = column->button->allocation.height;
9358       attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
9359       attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
9360       attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
9361       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
9362 
9363       tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
9364 						     &attributes,
9365 						     attributes_mask);
9366       gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
9367     }
9368 
9369   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
9370   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
9371 
9372   gtk_grab_remove (column->button);
9373 
9374   send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
9375   send_event->crossing.send_event = TRUE;
9376   send_event->crossing.window = g_object_ref (GTK_BUTTON (column->button)->event_window);
9377   send_event->crossing.subwindow = NULL;
9378   send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
9379   send_event->crossing.time = GDK_CURRENT_TIME;
9380 
9381   gtk_propagate_event (column->button, send_event);
9382   gdk_event_free (send_event);
9383 
9384   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
9385   send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
9386   send_event->button.send_event = TRUE;
9387   send_event->button.time = GDK_CURRENT_TIME;
9388   send_event->button.x = -1;
9389   send_event->button.y = -1;
9390   send_event->button.axes = NULL;
9391   send_event->button.state = 0;
9392   send_event->button.button = 1;
9393   send_event->button.device = gdk_display_get_core_pointer (display);
9394   send_event->button.x_root = 0;
9395   send_event->button.y_root = 0;
9396 
9397   gtk_propagate_event (column->button, send_event);
9398   gdk_event_free (send_event);
9399 
9400   /* Kids, don't try this at home */
9401   g_object_ref (column->button);
9402   gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
9403   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
9404   gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
9405   g_object_unref (column->button);
9406 
9407   tree_view->priv->drag_column_x = column->button->allocation.x;
9408   allocation = column->button->allocation;
9409   allocation.x = 0;
9410   gtk_widget_size_allocate (column->button, &allocation);
9411   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
9412 
9413   tree_view->priv->drag_column = column;
9414   gdk_window_show (tree_view->priv->drag_window);
9415 
9416   gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
9417   width = gdk_window_get_width (tree_view->priv->header_window);
9418   height = gdk_window_get_height (tree_view->priv->header_window);
9419 
9420   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9421   while (gtk_events_pending ())
9422     gtk_main_iteration ();
9423 
9424   GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG);
9425   gdk_pointer_grab (tree_view->priv->drag_window,
9426 		    FALSE,
9427 		    GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
9428 		    NULL, NULL, GDK_CURRENT_TIME);
9429   gdk_keyboard_grab (tree_view->priv->drag_window,
9430 		     FALSE,
9431 		     GDK_CURRENT_TIME);
9432 }
9433 
9434 static void
gtk_tree_view_queue_draw_arrow(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,const GdkRectangle * clip_rect)9435 gtk_tree_view_queue_draw_arrow (GtkTreeView        *tree_view,
9436 				GtkRBTree          *tree,
9437 				GtkRBNode          *node,
9438 				const GdkRectangle *clip_rect)
9439 {
9440   GdkRectangle rect;
9441 
9442   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9443     return;
9444 
9445   rect.x = 0;
9446   rect.width = MAX (tree_view->priv->expander_size, MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width));
9447 
9448   rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
9449   rect.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
9450 
9451   if (clip_rect)
9452     {
9453       GdkRectangle new_rect;
9454 
9455       gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
9456 
9457       gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
9458     }
9459   else
9460     {
9461       gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
9462     }
9463 }
9464 
9465 void
_gtk_tree_view_queue_draw_node(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,const GdkRectangle * clip_rect)9466 _gtk_tree_view_queue_draw_node (GtkTreeView        *tree_view,
9467 				GtkRBTree          *tree,
9468 				GtkRBNode          *node,
9469 				const GdkRectangle *clip_rect)
9470 {
9471   GdkRectangle rect;
9472 
9473   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9474     return;
9475 
9476   rect.x = 0;
9477   rect.width = MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width);
9478 
9479   rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
9480   rect.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
9481 
9482   if (clip_rect)
9483     {
9484       GdkRectangle new_rect;
9485 
9486       gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
9487 
9488       gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
9489     }
9490   else
9491     {
9492       gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
9493     }
9494 }
9495 
9496 static void
gtk_tree_view_queue_draw_path(GtkTreeView * tree_view,GtkTreePath * path,const GdkRectangle * clip_rect)9497 gtk_tree_view_queue_draw_path (GtkTreeView        *tree_view,
9498                                GtkTreePath        *path,
9499                                const GdkRectangle *clip_rect)
9500 {
9501   GtkRBTree *tree = NULL;
9502   GtkRBNode *node = NULL;
9503 
9504   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
9505 
9506   if (tree)
9507     _gtk_tree_view_queue_draw_node (tree_view, tree, node, clip_rect);
9508 }
9509 
9510 /* x and y are the mouse position
9511  */
9512 static void
gtk_tree_view_draw_arrow(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gint x,gint y)9513 gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
9514                           GtkRBTree   *tree,
9515 			  GtkRBNode   *node,
9516 			  /* in bin_window coordinates */
9517 			  gint         x,
9518 			  gint         y)
9519 {
9520   GdkRectangle area;
9521   GtkStateType state;
9522   GtkWidget *widget;
9523   gint x_offset = 0;
9524   gint x2;
9525   gint vertical_separator;
9526   gint expander_size;
9527   GtkExpanderStyle expander_style;
9528 
9529   widget = GTK_WIDGET (tree_view);
9530 
9531   gtk_widget_style_get (widget,
9532 			"vertical-separator", &vertical_separator,
9533 			NULL);
9534   expander_size = tree_view->priv->expander_size - EXPANDER_EXTRA_PADDING;
9535 
9536   if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
9537     return;
9538 
9539   gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2);
9540 
9541   area.x = x_offset;
9542   area.y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator);
9543   area.width = expander_size + 2;
9544   area.height = MAX (CELL_HEIGHT (node, vertical_separator), (expander_size - vertical_separator));
9545 
9546   if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
9547     {
9548       state = GTK_STATE_INSENSITIVE;
9549     }
9550   else if (node == tree_view->priv->button_pressed_node)
9551     {
9552       if (x >= area.x && x <= (area.x + area.width) &&
9553 	  y >= area.y && y <= (area.y + area.height))
9554         state = GTK_STATE_ACTIVE;
9555       else
9556         state = GTK_STATE_NORMAL;
9557     }
9558   else
9559     {
9560       if (node == tree_view->priv->prelight_node &&
9561 	  GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
9562 	state = GTK_STATE_PRELIGHT;
9563       else
9564 	state = GTK_STATE_NORMAL;
9565     }
9566 
9567   if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_EXPANDED))
9568     expander_style = GTK_EXPANDER_SEMI_EXPANDED;
9569   else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_COLLAPSED))
9570     expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
9571   else if (node->children != NULL)
9572     expander_style = GTK_EXPANDER_EXPANDED;
9573   else
9574     expander_style = GTK_EXPANDER_COLLAPSED;
9575 
9576   gtk_paint_expander (widget->style,
9577                       tree_view->priv->bin_window,
9578                       state,
9579                       &area,
9580                       widget,
9581                       "treeview",
9582 		      area.x + area.width / 2,
9583 		      area.y + area.height / 2,
9584 		      expander_style);
9585 }
9586 
9587 static void
gtk_tree_view_focus_to_cursor(GtkTreeView * tree_view)9588 gtk_tree_view_focus_to_cursor (GtkTreeView *tree_view)
9589 
9590 {
9591   GtkTreePath *cursor_path;
9592 
9593   if ((tree_view->priv->tree == NULL) ||
9594       (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
9595     return;
9596 
9597   cursor_path = NULL;
9598   if (tree_view->priv->cursor)
9599     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9600 
9601   if (cursor_path == NULL)
9602     {
9603       /* Consult the selection before defaulting to the
9604        * first focusable element
9605        */
9606       GList *selected_rows;
9607       GtkTreeModel *model;
9608       GtkTreeSelection *selection;
9609 
9610       selection = gtk_tree_view_get_selection (tree_view);
9611       selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
9612 
9613       if (selected_rows)
9614 	{
9615           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
9616 	  g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
9617 	  g_list_free (selected_rows);
9618         }
9619       else
9620 	{
9621 	  cursor_path = gtk_tree_path_new_first ();
9622 	  search_first_focusable_path (tree_view, &cursor_path,
9623 				       TRUE, NULL, NULL);
9624 	}
9625 
9626       gtk_tree_row_reference_free (tree_view->priv->cursor);
9627       tree_view->priv->cursor = NULL;
9628 
9629       if (cursor_path)
9630 	{
9631 	  if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE)
9632 	    gtk_tree_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
9633 	  else
9634 	    gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
9635 	}
9636     }
9637 
9638   if (cursor_path)
9639     {
9640       GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
9641 
9642       gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
9643       gtk_tree_path_free (cursor_path);
9644 
9645       if (tree_view->priv->focus_column == NULL)
9646 	{
9647 	  GList *list;
9648 	  for (list = tree_view->priv->columns; list; list = list->next)
9649 	    {
9650 	      if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
9651 		{
9652 		  tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
9653 		  break;
9654 		}
9655 	    }
9656 	}
9657     }
9658 }
9659 
9660 static void
gtk_tree_view_move_cursor_up_down(GtkTreeView * tree_view,gint count)9661 gtk_tree_view_move_cursor_up_down (GtkTreeView *tree_view,
9662 				   gint         count)
9663 {
9664   gint selection_count;
9665   GtkRBTree *cursor_tree = NULL;
9666   GtkRBNode *cursor_node = NULL;
9667   GtkRBTree *new_cursor_tree = NULL;
9668   GtkRBNode *new_cursor_node = NULL;
9669   GtkTreePath *cursor_path = NULL;
9670   gboolean grab_focus = TRUE;
9671   gboolean selectable;
9672 
9673   if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9674     return;
9675 
9676   cursor_path = NULL;
9677   if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
9678     /* FIXME: we lost the cursor; should we get the first? */
9679     return;
9680 
9681   cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9682   _gtk_tree_view_find_node (tree_view, cursor_path,
9683 			    &cursor_tree, &cursor_node);
9684 
9685   if (cursor_tree == NULL)
9686     /* FIXME: we lost the cursor; should we get the first? */
9687     return;
9688 
9689   selection_count = gtk_tree_selection_count_selected_rows (tree_view->priv->selection);
9690   selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection,
9691 						      cursor_node,
9692 						      cursor_path);
9693 
9694   if (selection_count == 0
9695       && tree_view->priv->selection->type != GTK_SELECTION_NONE
9696       && !tree_view->priv->modify_selection_pressed
9697       && selectable)
9698     {
9699       /* Don't move the cursor, but just select the current node */
9700       new_cursor_tree = cursor_tree;
9701       new_cursor_node = cursor_node;
9702     }
9703   else
9704     {
9705       if (count == -1)
9706 	_gtk_rbtree_prev_full (cursor_tree, cursor_node,
9707 			       &new_cursor_tree, &new_cursor_node);
9708       else
9709 	_gtk_rbtree_next_full (cursor_tree, cursor_node,
9710 			       &new_cursor_tree, &new_cursor_node);
9711     }
9712 
9713   gtk_tree_path_free (cursor_path);
9714 
9715   if (new_cursor_node)
9716     {
9717       cursor_path = _gtk_tree_view_find_path (tree_view,
9718 					      new_cursor_tree, new_cursor_node);
9719 
9720       search_first_focusable_path (tree_view, &cursor_path,
9721 				   (count != -1),
9722 				   &new_cursor_tree,
9723 				   &new_cursor_node);
9724 
9725       if (cursor_path)
9726 	gtk_tree_path_free (cursor_path);
9727     }
9728 
9729   /*
9730    * If the list has only one item and multi-selection is set then select
9731    * the row (if not yet selected).
9732    */
9733   if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE &&
9734       new_cursor_node == NULL)
9735     {
9736       if (count == -1)
9737         _gtk_rbtree_next_full (cursor_tree, cursor_node,
9738     			       &new_cursor_tree, &new_cursor_node);
9739       else
9740         _gtk_rbtree_prev_full (cursor_tree, cursor_node,
9741 			       &new_cursor_tree, &new_cursor_node);
9742 
9743       if (new_cursor_node == NULL
9744 	  && !GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_SELECTED))
9745         {
9746           new_cursor_node = cursor_node;
9747           new_cursor_tree = cursor_tree;
9748         }
9749       else
9750         {
9751           new_cursor_node = NULL;
9752         }
9753     }
9754 
9755   if (new_cursor_node)
9756     {
9757       cursor_path = _gtk_tree_view_find_path (tree_view, new_cursor_tree, new_cursor_node);
9758       gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
9759       gtk_tree_path_free (cursor_path);
9760     }
9761   else
9762     {
9763       gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
9764 
9765       if (!tree_view->priv->extend_selection_pressed)
9766         {
9767           if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
9768                                           count < 0 ?
9769                                           GTK_DIR_UP : GTK_DIR_DOWN))
9770             {
9771               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
9772 
9773               if (toplevel)
9774                 gtk_widget_child_focus (toplevel,
9775                                         count < 0 ?
9776                                         GTK_DIR_TAB_BACKWARD :
9777                                         GTK_DIR_TAB_FORWARD);
9778 
9779               grab_focus = FALSE;
9780             }
9781         }
9782       else
9783         {
9784           gtk_widget_error_bell (GTK_WIDGET (tree_view));
9785         }
9786     }
9787 
9788   if (grab_focus)
9789     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9790 }
9791 
9792 static void
gtk_tree_view_move_cursor_page_up_down(GtkTreeView * tree_view,gint count)9793 gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view,
9794 					gint         count)
9795 {
9796   GtkRBTree *cursor_tree = NULL;
9797   GtkRBNode *cursor_node = NULL;
9798   GtkTreePath *old_cursor_path = NULL;
9799   GtkTreePath *cursor_path = NULL;
9800   GtkRBTree *start_cursor_tree = NULL;
9801   GtkRBNode *start_cursor_node = NULL;
9802   gint y;
9803   gint window_y;
9804   gint vertical_separator;
9805 
9806   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9807     return;
9808 
9809   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9810     old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9811   else
9812     /* This is sorta weird.  Focus in should give us a cursor */
9813     return;
9814 
9815   gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
9816   _gtk_tree_view_find_node (tree_view, old_cursor_path,
9817 			    &cursor_tree, &cursor_node);
9818 
9819   if (cursor_tree == NULL)
9820     {
9821       /* FIXME: we lost the cursor.  Should we try to get one? */
9822       gtk_tree_path_free (old_cursor_path);
9823       return;
9824     }
9825   g_return_if_fail (cursor_node != NULL);
9826 
9827   y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
9828   window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
9829   y += tree_view->priv->cursor_offset;
9830   y += count * (int)tree_view->priv->vadjustment->page_increment;
9831   y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower,  (gint)tree_view->priv->vadjustment->upper - vertical_separator);
9832 
9833   if (y >= tree_view->priv->height)
9834     y = tree_view->priv->height - 1;
9835 
9836   tree_view->priv->cursor_offset =
9837     _gtk_rbtree_find_offset (tree_view->priv->tree, y,
9838 			     &cursor_tree, &cursor_node);
9839 
9840   if (cursor_tree == NULL)
9841     {
9842       /* FIXME: we lost the cursor.  Should we try to get one? */
9843       gtk_tree_path_free (old_cursor_path);
9844       return;
9845     }
9846 
9847   if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (cursor_node))
9848     {
9849       _gtk_rbtree_next_full (cursor_tree, cursor_node,
9850 			     &cursor_tree, &cursor_node);
9851       tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (cursor_node);
9852     }
9853 
9854   y -= tree_view->priv->cursor_offset;
9855   cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
9856 
9857   start_cursor_tree = cursor_tree;
9858   start_cursor_node = cursor_node;
9859 
9860   if (! search_first_focusable_path (tree_view, &cursor_path,
9861 				     (count != -1),
9862 				     &cursor_tree, &cursor_node))
9863     {
9864       /* It looks like we reached the end of the view without finding
9865        * a focusable row.  We will step backwards to find the last
9866        * focusable row.
9867        */
9868       cursor_tree = start_cursor_tree;
9869       cursor_node = start_cursor_node;
9870       cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
9871 
9872       search_first_focusable_path (tree_view, &cursor_path,
9873 				   (count == -1),
9874 				   &cursor_tree, &cursor_node);
9875     }
9876 
9877   if (!cursor_path)
9878     goto cleanup;
9879 
9880   /* update y */
9881   y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
9882 
9883   gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
9884 
9885   y -= window_y;
9886   gtk_tree_view_scroll_to_point (tree_view, -1, y);
9887   gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
9888   _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
9889 
9890   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
9891     gtk_widget_error_bell (GTK_WIDGET (tree_view));
9892 
9893   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9894 
9895 cleanup:
9896   gtk_tree_path_free (old_cursor_path);
9897   gtk_tree_path_free (cursor_path);
9898 }
9899 
9900 static void
gtk_tree_view_move_cursor_left_right(GtkTreeView * tree_view,gint count)9901 gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view,
9902 				      gint         count)
9903 {
9904   GtkRBTree *cursor_tree = NULL;
9905   GtkRBNode *cursor_node = NULL;
9906   GtkTreePath *cursor_path = NULL;
9907   GtkTreeViewColumn *column;
9908   GtkTreeIter iter;
9909   GList *list;
9910   gboolean found_column = FALSE;
9911   gboolean rtl;
9912 
9913   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
9914 
9915   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
9916     return;
9917 
9918   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9919     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9920   else
9921     return;
9922 
9923   _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node);
9924   if (cursor_tree == NULL)
9925     return;
9926   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
9927     {
9928       gtk_tree_path_free (cursor_path);
9929       return;
9930     }
9931   gtk_tree_path_free (cursor_path);
9932 
9933   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
9934   if (tree_view->priv->focus_column)
9935     {
9936       for (; list; list = (rtl ? list->prev : list->next))
9937 	{
9938 	  if (list->data == tree_view->priv->focus_column)
9939 	    break;
9940 	}
9941     }
9942 
9943   while (list)
9944     {
9945       gboolean left, right;
9946 
9947       column = list->data;
9948       if (column->visible == FALSE)
9949 	goto loop_end;
9950 
9951       gtk_tree_view_column_cell_set_cell_data (column,
9952 					       tree_view->priv->model,
9953 					       &iter,
9954 					       GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
9955 					       cursor_node->children?TRUE:FALSE);
9956 
9957       if (rtl)
9958         {
9959 	  right = list->prev ? TRUE : FALSE;
9960 	  left = list->next ? TRUE : FALSE;
9961 	}
9962       else
9963         {
9964 	  left = list->prev ? TRUE : FALSE;
9965 	  right = list->next ? TRUE : FALSE;
9966         }
9967 
9968       if (_gtk_tree_view_column_cell_focus (column, count, left, right))
9969 	{
9970 	  tree_view->priv->focus_column = column;
9971 	  found_column = TRUE;
9972 	  break;
9973 	}
9974     loop_end:
9975       if (count == 1)
9976 	list = rtl ? list->prev : list->next;
9977       else
9978 	list = rtl ? list->next : list->prev;
9979     }
9980 
9981   if (found_column)
9982     {
9983       if (!gtk_tree_view_has_special_cell (tree_view))
9984 	_gtk_tree_view_queue_draw_node (tree_view,
9985 				        cursor_tree,
9986 				        cursor_node,
9987 				        NULL);
9988       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9989       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
9990     }
9991   else
9992     {
9993       gtk_widget_error_bell (GTK_WIDGET (tree_view));
9994     }
9995 
9996   gtk_tree_view_clamp_column_visible (tree_view,
9997 				      tree_view->priv->focus_column, TRUE);
9998 }
9999 
10000 static void
gtk_tree_view_move_cursor_start_end(GtkTreeView * tree_view,gint count)10001 gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view,
10002 				     gint         count)
10003 {
10004   GtkRBTree *cursor_tree;
10005   GtkRBNode *cursor_node;
10006   GtkTreePath *path;
10007   GtkTreePath *old_path;
10008 
10009   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10010     return;
10011 
10012   g_return_if_fail (tree_view->priv->tree != NULL);
10013 
10014   gtk_tree_view_get_cursor (tree_view, &old_path, NULL);
10015 
10016   cursor_tree = tree_view->priv->tree;
10017   cursor_node = cursor_tree->root;
10018 
10019   if (count == -1)
10020     {
10021       while (cursor_node && cursor_node->left != cursor_tree->nil)
10022 	cursor_node = cursor_node->left;
10023 
10024       /* Now go forward to find the first focusable row. */
10025       path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
10026       search_first_focusable_path (tree_view, &path,
10027 				   TRUE, &cursor_tree, &cursor_node);
10028     }
10029   else
10030     {
10031       do
10032 	{
10033 	  while (cursor_node && cursor_node->right != cursor_tree->nil)
10034 	    cursor_node = cursor_node->right;
10035 	  if (cursor_node->children == NULL)
10036 	    break;
10037 
10038 	  cursor_tree = cursor_node->children;
10039 	  cursor_node = cursor_tree->root;
10040 	}
10041       while (1);
10042 
10043       /* Now go backwards to find last focusable row. */
10044       path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
10045       search_first_focusable_path (tree_view, &path,
10046 				   FALSE, &cursor_tree, &cursor_node);
10047     }
10048 
10049   if (!path)
10050     goto cleanup;
10051 
10052   if (gtk_tree_path_compare (old_path, path))
10053     {
10054       gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
10055       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10056     }
10057   else
10058     {
10059       gtk_widget_error_bell (GTK_WIDGET (tree_view));
10060     }
10061 
10062 cleanup:
10063   gtk_tree_path_free (old_path);
10064   gtk_tree_path_free (path);
10065 }
10066 
10067 static gboolean
gtk_tree_view_real_select_all(GtkTreeView * tree_view)10068 gtk_tree_view_real_select_all (GtkTreeView *tree_view)
10069 {
10070   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10071     return FALSE;
10072 
10073   if (tree_view->priv->selection->type != GTK_SELECTION_MULTIPLE)
10074     return FALSE;
10075 
10076   gtk_tree_selection_select_all (tree_view->priv->selection);
10077 
10078   return TRUE;
10079 }
10080 
10081 static gboolean
gtk_tree_view_real_unselect_all(GtkTreeView * tree_view)10082 gtk_tree_view_real_unselect_all (GtkTreeView *tree_view)
10083 {
10084   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10085     return FALSE;
10086 
10087   if (tree_view->priv->selection->type != GTK_SELECTION_MULTIPLE)
10088     return FALSE;
10089 
10090   gtk_tree_selection_unselect_all (tree_view->priv->selection);
10091 
10092   return TRUE;
10093 }
10094 
10095 static gboolean
gtk_tree_view_real_select_cursor_row(GtkTreeView * tree_view,gboolean start_editing)10096 gtk_tree_view_real_select_cursor_row (GtkTreeView *tree_view,
10097 				      gboolean     start_editing)
10098 {
10099   GtkRBTree *new_tree = NULL;
10100   GtkRBNode *new_node = NULL;
10101   GtkRBTree *cursor_tree = NULL;
10102   GtkRBNode *cursor_node = NULL;
10103   GtkTreePath *cursor_path = NULL;
10104   GtkTreeSelectMode mode = 0;
10105 
10106   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10107     return FALSE;
10108 
10109   if (tree_view->priv->cursor)
10110     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10111 
10112   if (cursor_path == NULL)
10113     return FALSE;
10114 
10115   _gtk_tree_view_find_node (tree_view, cursor_path,
10116 			    &cursor_tree, &cursor_node);
10117 
10118   if (cursor_tree == NULL)
10119     {
10120       gtk_tree_path_free (cursor_path);
10121       return FALSE;
10122     }
10123 
10124   if (!tree_view->priv->extend_selection_pressed && start_editing &&
10125       tree_view->priv->focus_column)
10126     {
10127       if (gtk_tree_view_start_editing (tree_view, cursor_path))
10128 	{
10129 	  gtk_tree_path_free (cursor_path);
10130 	  return TRUE;
10131 	}
10132     }
10133 
10134   if (tree_view->priv->modify_selection_pressed)
10135     mode |= GTK_TREE_SELECT_MODE_TOGGLE;
10136   if (tree_view->priv->extend_selection_pressed)
10137     mode |= GTK_TREE_SELECT_MODE_EXTEND;
10138 
10139   _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
10140 					    cursor_node,
10141 					    cursor_tree,
10142 					    cursor_path,
10143                                             mode,
10144 					    FALSE);
10145 
10146   /* We bail out if the original (tree, node) don't exist anymore after
10147    * handling the selection-changed callback.  We do return TRUE because
10148    * the key press has been handled at this point.
10149    */
10150   _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
10151 
10152   if (cursor_tree != new_tree || cursor_node != new_node)
10153     return FALSE;
10154 
10155   gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10156 
10157   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10158   _gtk_tree_view_queue_draw_node (tree_view, cursor_tree, cursor_node, NULL);
10159 
10160   if (!tree_view->priv->extend_selection_pressed)
10161     gtk_tree_view_row_activated (tree_view, cursor_path,
10162                                  tree_view->priv->focus_column);
10163 
10164   gtk_tree_path_free (cursor_path);
10165 
10166   return TRUE;
10167 }
10168 
10169 static gboolean
gtk_tree_view_real_toggle_cursor_row(GtkTreeView * tree_view)10170 gtk_tree_view_real_toggle_cursor_row (GtkTreeView *tree_view)
10171 {
10172   GtkRBTree *new_tree = NULL;
10173   GtkRBNode *new_node = NULL;
10174   GtkRBTree *cursor_tree = NULL;
10175   GtkRBNode *cursor_node = NULL;
10176   GtkTreePath *cursor_path = NULL;
10177 
10178   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10179     return FALSE;
10180 
10181   cursor_path = NULL;
10182   if (tree_view->priv->cursor)
10183     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10184 
10185   if (cursor_path == NULL)
10186     return FALSE;
10187 
10188   _gtk_tree_view_find_node (tree_view, cursor_path,
10189 			    &cursor_tree, &cursor_node);
10190   if (cursor_tree == NULL)
10191     {
10192       gtk_tree_path_free (cursor_path);
10193       return FALSE;
10194     }
10195 
10196   _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
10197 					    cursor_node,
10198 					    cursor_tree,
10199 					    cursor_path,
10200                                             GTK_TREE_SELECT_MODE_TOGGLE,
10201 					    FALSE);
10202 
10203   /* We bail out if the original (tree, node) don't exist anymore after
10204    * handling the selection-changed callback.  We do return TRUE because
10205    * the key press has been handled at this point.
10206    */
10207   _gtk_tree_view_find_node (tree_view, cursor_path, &new_tree, &new_node);
10208 
10209   if (cursor_tree != new_tree || cursor_node != new_node)
10210     return FALSE;
10211 
10212   gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10213 
10214   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10215   gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10216   gtk_tree_path_free (cursor_path);
10217 
10218   return TRUE;
10219 }
10220 
10221 static gboolean
gtk_tree_view_real_expand_collapse_cursor_row(GtkTreeView * tree_view,gboolean logical,gboolean expand,gboolean open_all)10222 gtk_tree_view_real_expand_collapse_cursor_row (GtkTreeView *tree_view,
10223 					       gboolean     logical,
10224 					       gboolean     expand,
10225 					       gboolean     open_all)
10226 {
10227   GtkTreePath *cursor_path = NULL;
10228   GtkRBTree *tree;
10229   GtkRBNode *node;
10230 
10231   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10232     return FALSE;
10233 
10234   cursor_path = NULL;
10235   if (tree_view->priv->cursor)
10236     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10237 
10238   if (cursor_path == NULL)
10239     return FALSE;
10240 
10241   if (_gtk_tree_view_find_node (tree_view, cursor_path, &tree, &node))
10242     return FALSE;
10243 
10244   /* Don't handle the event if we aren't an expander */
10245   if (!((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT))
10246     return FALSE;
10247 
10248   if (!logical
10249       && gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL)
10250     expand = !expand;
10251 
10252   if (expand)
10253     gtk_tree_view_real_expand_row (tree_view, cursor_path, tree, node, open_all, TRUE);
10254   else
10255     gtk_tree_view_real_collapse_row (tree_view, cursor_path, tree, node, TRUE);
10256 
10257   gtk_tree_path_free (cursor_path);
10258 
10259   return TRUE;
10260 }
10261 
10262 static gboolean
gtk_tree_view_real_select_cursor_parent(GtkTreeView * tree_view)10263 gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
10264 {
10265   GtkRBTree *cursor_tree = NULL;
10266   GtkRBNode *cursor_node = NULL;
10267   GtkTreePath *cursor_path = NULL;
10268   GdkModifierType state;
10269 
10270   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10271     goto out;
10272 
10273   cursor_path = NULL;
10274   if (tree_view->priv->cursor)
10275     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10276 
10277   if (cursor_path == NULL)
10278     goto out;
10279 
10280   _gtk_tree_view_find_node (tree_view, cursor_path,
10281 			    &cursor_tree, &cursor_node);
10282   if (cursor_tree == NULL)
10283     {
10284       gtk_tree_path_free (cursor_path);
10285       goto out;
10286     }
10287 
10288   if (cursor_tree->parent_node)
10289     {
10290       gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10291       cursor_node = cursor_tree->parent_node;
10292       cursor_tree = cursor_tree->parent_tree;
10293 
10294       gtk_tree_path_up (cursor_path);
10295 
10296       if (gtk_get_current_event_state (&state))
10297 	{
10298 	  if ((state & GTK_MODIFY_SELECTION_MOD_MASK) == GTK_MODIFY_SELECTION_MOD_MASK)
10299 	    tree_view->priv->modify_selection_pressed = TRUE;
10300 	}
10301 
10302       gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
10303       gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
10304 
10305       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
10306       gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
10307       gtk_tree_path_free (cursor_path);
10308 
10309       tree_view->priv->modify_selection_pressed = FALSE;
10310 
10311       return TRUE;
10312     }
10313 
10314  out:
10315 
10316   tree_view->priv->search_entry_avoid_unhandled_binding = TRUE;
10317   return FALSE;
10318 }
10319 
10320 static gboolean
gtk_tree_view_search_entry_flush_timeout(GtkTreeView * tree_view)10321 gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
10322 {
10323   gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
10324   tree_view->priv->typeselect_flush_timeout = 0;
10325 
10326   return FALSE;
10327 }
10328 
10329 /* Cut and paste from gtkwindow.c */
10330 static void
send_focus_change(GtkWidget * widget,gboolean in)10331 send_focus_change (GtkWidget *widget,
10332 		   gboolean   in)
10333 {
10334   GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
10335 
10336   fevent->focus_change.type = GDK_FOCUS_CHANGE;
10337   fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
10338   fevent->focus_change.in = in;
10339 
10340   gtk_widget_send_focus_change (widget, fevent);
10341 
10342   gdk_event_free (fevent);
10343 }
10344 
10345 static void
gtk_tree_view_ensure_interactive_directory(GtkTreeView * tree_view)10346 gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
10347 {
10348   GtkWidget *frame, *vbox, *toplevel;
10349   GdkScreen *screen;
10350 
10351   if (tree_view->priv->search_custom_entry_set)
10352     return;
10353 
10354   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
10355   screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
10356 
10357    if (tree_view->priv->search_window != NULL)
10358      {
10359        if (GTK_WINDOW (toplevel)->group)
10360 	 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
10361 				      GTK_WINDOW (tree_view->priv->search_window));
10362        else if (GTK_WINDOW (tree_view->priv->search_window)->group)
10363 	 gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group,
10364 					 GTK_WINDOW (tree_view->priv->search_window));
10365        gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
10366        return;
10367      }
10368 
10369   tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
10370   gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
10371 
10372   if (GTK_WINDOW (toplevel)->group)
10373     gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
10374 				 GTK_WINDOW (tree_view->priv->search_window));
10375 
10376   gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
10377 			    GDK_WINDOW_TYPE_HINT_UTILITY);
10378   gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
10379   g_signal_connect (tree_view->priv->search_window, "delete-event",
10380 		    G_CALLBACK (gtk_tree_view_search_delete_event),
10381 		    tree_view);
10382   g_signal_connect (tree_view->priv->search_window, "key-press-event",
10383 		    G_CALLBACK (gtk_tree_view_search_key_press_event),
10384 		    tree_view);
10385   g_signal_connect (tree_view->priv->search_window, "button-press-event",
10386 		    G_CALLBACK (gtk_tree_view_search_button_press_event),
10387 		    tree_view);
10388   g_signal_connect (tree_view->priv->search_window, "scroll-event",
10389 		    G_CALLBACK (gtk_tree_view_search_scroll_event),
10390 		    tree_view);
10391 
10392   frame = gtk_frame_new (NULL);
10393   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
10394   gtk_widget_show (frame);
10395   gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
10396 
10397   vbox = gtk_vbox_new (FALSE, 0);
10398   gtk_widget_show (vbox);
10399   gtk_container_add (GTK_CONTAINER (frame), vbox);
10400   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
10401 
10402   /* add entry */
10403   tree_view->priv->search_entry = gtk_entry_new ();
10404   gtk_widget_show (tree_view->priv->search_entry);
10405   g_signal_connect (tree_view->priv->search_entry, "populate-popup",
10406 		    G_CALLBACK (gtk_tree_view_search_disable_popdown),
10407 		    tree_view);
10408   g_signal_connect (tree_view->priv->search_entry,
10409 		    "activate", G_CALLBACK (gtk_tree_view_search_activate),
10410 		    tree_view);
10411   g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
10412 		    "preedit-changed",
10413 		    G_CALLBACK (gtk_tree_view_search_preedit_changed),
10414 		    tree_view);
10415   gtk_container_add (GTK_CONTAINER (vbox),
10416 		     tree_view->priv->search_entry);
10417 
10418   gtk_widget_realize (tree_view->priv->search_entry);
10419 }
10420 
10421 /* Pops up the interactive search entry.  If keybinding is TRUE then the user
10422  * started this by typing the start_interactive_search keybinding.  Otherwise, it came from
10423  */
10424 static gboolean
gtk_tree_view_real_start_interactive_search(GtkTreeView * tree_view,gboolean keybinding)10425 gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
10426 					     gboolean     keybinding)
10427 {
10428   /* We only start interactive search if we have focus or the columns
10429    * have focus.  If one of our children have focus, we don't want to
10430    * start the search.
10431    */
10432   GList *list;
10433   gboolean found_focus = FALSE;
10434   GtkWidgetClass *entry_parent_class;
10435 
10436   if (!tree_view->priv->enable_search && !keybinding)
10437     return FALSE;
10438 
10439   if (tree_view->priv->search_custom_entry_set)
10440     return FALSE;
10441 
10442   if (tree_view->priv->search_window != NULL &&
10443       gtk_widget_get_visible (tree_view->priv->search_window))
10444     return TRUE;
10445 
10446   for (list = tree_view->priv->columns; list; list = list->next)
10447     {
10448       GtkTreeViewColumn *column;
10449 
10450       column = list->data;
10451       if (! column->visible)
10452 	continue;
10453 
10454       if (gtk_widget_has_focus (column->button))
10455 	{
10456 	  found_focus = TRUE;
10457 	  break;
10458 	}
10459     }
10460 
10461   if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
10462     found_focus = TRUE;
10463 
10464   if (!found_focus)
10465     return FALSE;
10466 
10467   if (tree_view->priv->search_column < 0)
10468     return FALSE;
10469 
10470   gtk_tree_view_ensure_interactive_directory (tree_view);
10471 
10472   if (keybinding)
10473     gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
10474 
10475   /* done, show it */
10476   tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
10477   gtk_widget_show (tree_view->priv->search_window);
10478   if (tree_view->priv->search_entry_changed_id == 0)
10479     {
10480       tree_view->priv->search_entry_changed_id =
10481 	g_signal_connect (tree_view->priv->search_entry, "changed",
10482 			  G_CALLBACK (gtk_tree_view_search_init),
10483 			  tree_view);
10484     }
10485 
10486   tree_view->priv->typeselect_flush_timeout =
10487     gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
10488 		   (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
10489 		   tree_view);
10490 
10491   /* Grab focus will select all the text.  We don't want that to happen, so we
10492    * call the parent instance and bypass the selection change.  This is probably
10493    * really non-kosher. */
10494   entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
10495   (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
10496 
10497   /* send focus-in event */
10498   send_focus_change (tree_view->priv->search_entry, TRUE);
10499 
10500   /* search first matching iter */
10501   gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
10502 
10503   return TRUE;
10504 }
10505 
10506 static gboolean
gtk_tree_view_start_interactive_search(GtkTreeView * tree_view)10507 gtk_tree_view_start_interactive_search (GtkTreeView *tree_view)
10508 {
10509   return gtk_tree_view_real_start_interactive_search (tree_view, TRUE);
10510 }
10511 
10512 /* this function returns the new width of the column being resized given
10513  * the column and x position of the cursor; the x cursor position is passed
10514  * in as a pointer and automagicly corrected if it's beyond min/max limits
10515  */
10516 static gint
gtk_tree_view_new_column_width(GtkTreeView * tree_view,gint i,gint * x)10517 gtk_tree_view_new_column_width (GtkTreeView *tree_view,
10518 				gint       i,
10519 				gint      *x)
10520 {
10521   GtkTreeViewColumn *column;
10522   gint width;
10523   gboolean rtl;
10524 
10525   /* first translate the x position from widget->window
10526    * to clist->clist_window
10527    */
10528   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10529   column = g_list_nth (tree_view->priv->columns, i)->data;
10530   width = rtl ? (column->button->allocation.x + column->button->allocation.width - *x) : (*x - column->button->allocation.x);
10531 
10532   /* Clamp down the value */
10533   if (column->min_width == -1)
10534     width = MAX (column->button->requisition.width,
10535 		 width);
10536   else
10537     width = MAX (column->min_width,
10538 		 width);
10539   if (column->max_width != -1)
10540     width = MIN (width, column->max_width);
10541 
10542   *x = rtl ? (column->button->allocation.x + column->button->allocation.width - width) : (column->button->allocation.x + width);
10543 
10544   return width;
10545 }
10546 
10547 
10548 /* FIXME this adjust_allocation is a big cut-and-paste from
10549  * GtkCList, needs to be some "official" way to do this
10550  * factored out.
10551  */
10552 typedef struct
10553 {
10554   GdkWindow *window;
10555   int dx;
10556   int dy;
10557 } ScrollData;
10558 
10559 /* The window to which widget->window is relative */
10560 #define ALLOCATION_WINDOW(widget)		\
10561    (!gtk_widget_get_has_window (widget) ?		\
10562     (widget)->window :                          \
10563      gdk_window_get_parent ((widget)->window))
10564 
10565 static void
adjust_allocation_recurse(GtkWidget * widget,gpointer data)10566 adjust_allocation_recurse (GtkWidget *widget,
10567 			   gpointer   data)
10568 {
10569   ScrollData *scroll_data = data;
10570 
10571   /* Need to really size allocate instead of just poking
10572    * into widget->allocation if the widget is not realized.
10573    * FIXME someone figure out why this was.
10574    */
10575   if (!gtk_widget_get_realized (widget))
10576     {
10577       if (gtk_widget_get_visible (widget))
10578 	{
10579 	  GdkRectangle tmp_rectangle = widget->allocation;
10580 	  tmp_rectangle.x += scroll_data->dx;
10581           tmp_rectangle.y += scroll_data->dy;
10582 
10583 	  gtk_widget_size_allocate (widget, &tmp_rectangle);
10584 	}
10585     }
10586   else
10587     {
10588       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
10589 	{
10590 	  widget->allocation.x += scroll_data->dx;
10591           widget->allocation.y += scroll_data->dy;
10592 
10593 	  if (GTK_IS_CONTAINER (widget))
10594 	    gtk_container_forall (GTK_CONTAINER (widget),
10595 				  adjust_allocation_recurse,
10596 				  data);
10597 	}
10598     }
10599 }
10600 
10601 static void
adjust_allocation(GtkWidget * widget,int dx,int dy)10602 adjust_allocation (GtkWidget *widget,
10603 		   int        dx,
10604                    int        dy)
10605 {
10606   ScrollData scroll_data;
10607 
10608   if (gtk_widget_get_realized (widget))
10609     scroll_data.window = ALLOCATION_WINDOW (widget);
10610   else
10611     scroll_data.window = NULL;
10612 
10613   scroll_data.dx = dx;
10614   scroll_data.dy = dy;
10615 
10616   adjust_allocation_recurse (widget, &scroll_data);
10617 }
10618 
10619 /* Callbacks */
10620 static void
gtk_tree_view_adjustment_changed(GtkAdjustment * adjustment,GtkTreeView * tree_view)10621 gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
10622 				  GtkTreeView   *tree_view)
10623 {
10624   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10625     {
10626       gint dy;
10627 
10628       gdk_window_move (tree_view->priv->bin_window,
10629 		       - tree_view->priv->hadjustment->value,
10630 		       TREE_VIEW_HEADER_HEIGHT (tree_view));
10631       gdk_window_move (tree_view->priv->header_window,
10632 		       - tree_view->priv->hadjustment->value,
10633 		       0);
10634       dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
10635       if (dy)
10636 	{
10637           update_prelight (tree_view,
10638                            tree_view->priv->event_last_x,
10639                            tree_view->priv->event_last_y - dy);
10640 
10641 	  if (tree_view->priv->edited_column &&
10642               GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
10643 	    {
10644 	      GList *list;
10645 	      GtkWidget *widget;
10646 	      GtkTreeViewChild *child = NULL;
10647 
10648 	      widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
10649 	      adjust_allocation (widget, 0, dy);
10650 
10651 	      for (list = tree_view->priv->children; list; list = list->next)
10652 		{
10653 		  child = (GtkTreeViewChild *)list->data;
10654 		  if (child->widget == widget)
10655 		    {
10656 		      child->y += dy;
10657 		      break;
10658 		    }
10659 		}
10660 	    }
10661 	}
10662       gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
10663 
10664       if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
10665         {
10666           /* update our dy and top_row */
10667           tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
10668 
10669           if (!tree_view->priv->in_top_row_to_dy)
10670             gtk_tree_view_dy_to_top_row (tree_view);
10671 	}
10672 
10673       gdk_window_process_updates (tree_view->priv->header_window, TRUE);
10674       gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
10675     }
10676 }
10677 
10678 
10679 
10680 /* Public methods
10681  */
10682 
10683 /**
10684  * gtk_tree_view_new:
10685  *
10686  * Creates a new #GtkTreeView widget.
10687  *
10688  * Return value: A newly created #GtkTreeView widget.
10689  **/
10690 GtkWidget *
gtk_tree_view_new(void)10691 gtk_tree_view_new (void)
10692 {
10693   return g_object_new (GTK_TYPE_TREE_VIEW, NULL);
10694 }
10695 
10696 /**
10697  * gtk_tree_view_new_with_model:
10698  * @model: the model.
10699  *
10700  * Creates a new #GtkTreeView widget with the model initialized to @model.
10701  *
10702  * Return value: A newly created #GtkTreeView widget.
10703  **/
10704 GtkWidget *
gtk_tree_view_new_with_model(GtkTreeModel * model)10705 gtk_tree_view_new_with_model (GtkTreeModel *model)
10706 {
10707   return g_object_new (GTK_TYPE_TREE_VIEW, "model", model, NULL);
10708 }
10709 
10710 /* Public Accessors
10711  */
10712 
10713 /**
10714  * gtk_tree_view_get_model:
10715  * @tree_view: a #GtkTreeView
10716  *
10717  * Returns the model the #GtkTreeView is based on.  Returns %NULL if the
10718  * model is unset.
10719  *
10720  * Return value: (transfer none): A #GtkTreeModel, or %NULL if none is currently being used.
10721  **/
10722 GtkTreeModel *
gtk_tree_view_get_model(GtkTreeView * tree_view)10723 gtk_tree_view_get_model (GtkTreeView *tree_view)
10724 {
10725   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10726 
10727   return tree_view->priv->model;
10728 }
10729 
10730 /**
10731  * gtk_tree_view_set_model:
10732  * @tree_view: A #GtkTreeNode.
10733  * @model: (allow-none): The model.
10734  *
10735  * Sets the model for a #GtkTreeView.  If the @tree_view already has a model
10736  * set, it will remove it before setting the new model.  If @model is %NULL,
10737  * then it will unset the old model.
10738  **/
10739 void
gtk_tree_view_set_model(GtkTreeView * tree_view,GtkTreeModel * model)10740 gtk_tree_view_set_model (GtkTreeView  *tree_view,
10741 			 GtkTreeModel *model)
10742 {
10743   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10744   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
10745 
10746   if (model == tree_view->priv->model)
10747     return;
10748 
10749   if (tree_view->priv->scroll_to_path)
10750     {
10751       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
10752       tree_view->priv->scroll_to_path = NULL;
10753     }
10754 
10755   if (tree_view->priv->rubber_band_status)
10756     gtk_tree_view_stop_rubber_band (tree_view);
10757 
10758   if (tree_view->priv->model)
10759     {
10760       GList *tmplist = tree_view->priv->columns;
10761 
10762       gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree);
10763       gtk_tree_view_stop_editing (tree_view, TRUE);
10764 
10765       remove_expand_collapse_timeout (tree_view);
10766 
10767       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
10768 					    gtk_tree_view_row_changed,
10769 					    tree_view);
10770       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
10771 					    gtk_tree_view_row_inserted,
10772 					    tree_view);
10773       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
10774 					    gtk_tree_view_row_has_child_toggled,
10775 					    tree_view);
10776       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
10777 					    gtk_tree_view_row_deleted,
10778 					    tree_view);
10779       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
10780 					    gtk_tree_view_rows_reordered,
10781 					    tree_view);
10782 
10783       for (; tmplist; tmplist = tmplist->next)
10784 	_gtk_tree_view_column_unset_model (tmplist->data,
10785 					   tree_view->priv->model);
10786 
10787       if (tree_view->priv->tree)
10788 	gtk_tree_view_free_rbtree (tree_view);
10789 
10790       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10791       tree_view->priv->drag_dest_row = NULL;
10792       gtk_tree_row_reference_free (tree_view->priv->cursor);
10793       tree_view->priv->cursor = NULL;
10794       gtk_tree_row_reference_free (tree_view->priv->anchor);
10795       tree_view->priv->anchor = NULL;
10796       gtk_tree_row_reference_free (tree_view->priv->top_row);
10797       tree_view->priv->top_row = NULL;
10798       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
10799       tree_view->priv->scroll_to_path = NULL;
10800 
10801       tree_view->priv->scroll_to_column = NULL;
10802 
10803       g_object_unref (tree_view->priv->model);
10804 
10805       tree_view->priv->search_column = -1;
10806       tree_view->priv->fixed_height_check = 0;
10807       tree_view->priv->fixed_height = -1;
10808       tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
10809       tree_view->priv->last_button_x = -1;
10810       tree_view->priv->last_button_y = -1;
10811     }
10812 
10813   tree_view->priv->model = model;
10814 
10815   if (tree_view->priv->model)
10816     {
10817       gint i;
10818       GtkTreePath *path;
10819       GtkTreeIter iter;
10820       GtkTreeModelFlags flags;
10821 
10822       if (tree_view->priv->search_column == -1)
10823 	{
10824 	  for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
10825 	    {
10826 	      GType type = gtk_tree_model_get_column_type (model, i);
10827 
10828 	      if (g_value_type_transformable (type, G_TYPE_STRING))
10829 		{
10830 		  tree_view->priv->search_column = i;
10831 		  break;
10832 		}
10833 	    }
10834 	}
10835 
10836       g_object_ref (tree_view->priv->model);
10837       g_signal_connect (tree_view->priv->model,
10838 			"row-changed",
10839 			G_CALLBACK (gtk_tree_view_row_changed),
10840 			tree_view);
10841       g_signal_connect (tree_view->priv->model,
10842 			"row-inserted",
10843 			G_CALLBACK (gtk_tree_view_row_inserted),
10844 			tree_view);
10845       g_signal_connect (tree_view->priv->model,
10846 			"row-has-child-toggled",
10847 			G_CALLBACK (gtk_tree_view_row_has_child_toggled),
10848 			tree_view);
10849       g_signal_connect (tree_view->priv->model,
10850 			"row-deleted",
10851 			G_CALLBACK (gtk_tree_view_row_deleted),
10852 			tree_view);
10853       g_signal_connect (tree_view->priv->model,
10854 			"rows-reordered",
10855 			G_CALLBACK (gtk_tree_view_rows_reordered),
10856 			tree_view);
10857 
10858       flags = gtk_tree_model_get_flags (tree_view->priv->model);
10859       if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
10860         GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
10861       else
10862         GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
10863 
10864       path = gtk_tree_path_new_first ();
10865       if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path))
10866 	{
10867 	  tree_view->priv->tree = _gtk_rbtree_new ();
10868 	  gtk_tree_view_build_tree (tree_view, tree_view->priv->tree, &iter, 1, FALSE);
10869 	}
10870       gtk_tree_path_free (path);
10871 
10872       /*  FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */
10873       install_presize_handler (tree_view);
10874     }
10875 
10876   g_object_notify (G_OBJECT (tree_view), "model");
10877 
10878   if (tree_view->priv->selection)
10879   _gtk_tree_selection_emit_changed (tree_view->priv->selection);
10880 
10881   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
10882     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
10883 }
10884 
10885 /**
10886  * gtk_tree_view_get_selection:
10887  * @tree_view: A #GtkTreeView.
10888  *
10889  * Gets the #GtkTreeSelection associated with @tree_view.
10890  *
10891  * Return value: (transfer none): A #GtkTreeSelection object.
10892  **/
10893 GtkTreeSelection *
gtk_tree_view_get_selection(GtkTreeView * tree_view)10894 gtk_tree_view_get_selection (GtkTreeView *tree_view)
10895 {
10896   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10897 
10898   return tree_view->priv->selection;
10899 }
10900 
10901 /**
10902  * gtk_tree_view_get_hadjustment:
10903  * @tree_view: A #GtkTreeView
10904  *
10905  * Gets the #GtkAdjustment currently being used for the horizontal aspect.
10906  *
10907  * Return value: (transfer none): A #GtkAdjustment object, or %NULL
10908  *     if none is currently being used.
10909  **/
10910 GtkAdjustment *
gtk_tree_view_get_hadjustment(GtkTreeView * tree_view)10911 gtk_tree_view_get_hadjustment (GtkTreeView *tree_view)
10912 {
10913   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10914 
10915   if (tree_view->priv->hadjustment == NULL)
10916     gtk_tree_view_set_hadjustment (tree_view, NULL);
10917 
10918   return tree_view->priv->hadjustment;
10919 }
10920 
10921 /**
10922  * gtk_tree_view_set_hadjustment:
10923  * @tree_view: A #GtkTreeView
10924  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
10925  *
10926  * Sets the #GtkAdjustment for the current horizontal aspect.
10927  **/
10928 void
gtk_tree_view_set_hadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)10929 gtk_tree_view_set_hadjustment (GtkTreeView   *tree_view,
10930 			       GtkAdjustment *adjustment)
10931 {
10932   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10933 
10934   gtk_tree_view_set_adjustments (tree_view,
10935 				 adjustment,
10936 				 tree_view->priv->vadjustment);
10937 
10938   g_object_notify (G_OBJECT (tree_view), "hadjustment");
10939 }
10940 
10941 /**
10942  * gtk_tree_view_get_vadjustment:
10943  * @tree_view: A #GtkTreeView
10944  *
10945  * Gets the #GtkAdjustment currently being used for the vertical aspect.
10946  *
10947  * Return value: (transfer none): A #GtkAdjustment object, or %NULL
10948  *     if none is currently being used.
10949  **/
10950 GtkAdjustment *
gtk_tree_view_get_vadjustment(GtkTreeView * tree_view)10951 gtk_tree_view_get_vadjustment (GtkTreeView *tree_view)
10952 {
10953   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
10954 
10955   if (tree_view->priv->vadjustment == NULL)
10956     gtk_tree_view_set_vadjustment (tree_view, NULL);
10957 
10958   return tree_view->priv->vadjustment;
10959 }
10960 
10961 /**
10962  * gtk_tree_view_set_vadjustment:
10963  * @tree_view: A #GtkTreeView
10964  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
10965  *
10966  * Sets the #GtkAdjustment for the current vertical aspect.
10967  **/
10968 void
gtk_tree_view_set_vadjustment(GtkTreeView * tree_view,GtkAdjustment * adjustment)10969 gtk_tree_view_set_vadjustment (GtkTreeView   *tree_view,
10970 			       GtkAdjustment *adjustment)
10971 {
10972   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
10973 
10974   gtk_tree_view_set_adjustments (tree_view,
10975 				 tree_view->priv->hadjustment,
10976 				 adjustment);
10977 
10978   g_object_notify (G_OBJECT (tree_view), "vadjustment");
10979 }
10980 
10981 /* Column and header operations */
10982 
10983 /**
10984  * gtk_tree_view_get_headers_visible:
10985  * @tree_view: A #GtkTreeView.
10986  *
10987  * Returns %TRUE if the headers on the @tree_view are visible.
10988  *
10989  * Return value: Whether the headers are visible or not.
10990  **/
10991 gboolean
gtk_tree_view_get_headers_visible(GtkTreeView * tree_view)10992 gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
10993 {
10994   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
10995 
10996   return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
10997 }
10998 
10999 /**
11000  * gtk_tree_view_set_headers_visible:
11001  * @tree_view: A #GtkTreeView.
11002  * @headers_visible: %TRUE if the headers are visible
11003  *
11004  * Sets the visibility state of the headers.
11005  **/
11006 void
gtk_tree_view_set_headers_visible(GtkTreeView * tree_view,gboolean headers_visible)11007 gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
11008 				   gboolean     headers_visible)
11009 {
11010   gint x, y;
11011   GList *list;
11012   GtkTreeViewColumn *column;
11013 
11014   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11015 
11016   headers_visible = !! headers_visible;
11017 
11018   if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible)
11019     return;
11020 
11021   if (headers_visible)
11022     GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
11023   else
11024     GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
11025 
11026   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11027     {
11028       gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
11029       if (headers_visible)
11030 	{
11031 	  gdk_window_move_resize (tree_view->priv->bin_window, x, y  + TREE_VIEW_HEADER_HEIGHT (tree_view), tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.height -  + TREE_VIEW_HEADER_HEIGHT (tree_view));
11032 
11033           if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
11034             gtk_tree_view_map_buttons (tree_view);
11035  	}
11036       else
11037 	{
11038 	  gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
11039 
11040 	  for (list = tree_view->priv->columns; list; list = list->next)
11041 	    {
11042 	      column = list->data;
11043 	      gtk_widget_unmap (column->button);
11044 	    }
11045 	  gdk_window_hide (tree_view->priv->header_window);
11046 	}
11047     }
11048 
11049   tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
11050   tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
11051   tree_view->priv->vadjustment->lower = 0;
11052   tree_view->priv->vadjustment->upper = tree_view->priv->height;
11053   gtk_adjustment_changed (tree_view->priv->vadjustment);
11054 
11055   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11056 
11057   g_object_notify (G_OBJECT (tree_view), "headers-visible");
11058 }
11059 
11060 /**
11061  * gtk_tree_view_columns_autosize:
11062  * @tree_view: A #GtkTreeView.
11063  *
11064  * Resizes all columns to their optimal width. Only works after the
11065  * treeview has been realized.
11066  **/
11067 void
gtk_tree_view_columns_autosize(GtkTreeView * tree_view)11068 gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
11069 {
11070   gboolean dirty = FALSE;
11071   GList *list;
11072   GtkTreeViewColumn *column;
11073 
11074   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11075 
11076   for (list = tree_view->priv->columns; list; list = list->next)
11077     {
11078       column = list->data;
11079       if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
11080 	continue;
11081       _gtk_tree_view_column_cell_set_dirty (column, TRUE);
11082       dirty = TRUE;
11083     }
11084 
11085   if (dirty)
11086     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11087 }
11088 
11089 /**
11090  * gtk_tree_view_set_headers_clickable:
11091  * @tree_view: A #GtkTreeView.
11092  * @setting: %TRUE if the columns are clickable.
11093  *
11094  * Allow the column title buttons to be clicked.
11095  **/
11096 void
gtk_tree_view_set_headers_clickable(GtkTreeView * tree_view,gboolean setting)11097 gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view,
11098 				     gboolean   setting)
11099 {
11100   GList *list;
11101 
11102   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11103 
11104   for (list = tree_view->priv->columns; list; list = list->next)
11105     gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting);
11106 
11107   g_object_notify (G_OBJECT (tree_view), "headers-clickable");
11108 }
11109 
11110 
11111 /**
11112  * gtk_tree_view_get_headers_clickable:
11113  * @tree_view: A #GtkTreeView.
11114  *
11115  * Returns whether all header columns are clickable.
11116  *
11117  * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
11118  *
11119  * Since: 2.10
11120  **/
11121 gboolean
gtk_tree_view_get_headers_clickable(GtkTreeView * tree_view)11122 gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view)
11123 {
11124   GList *list;
11125 
11126   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11127 
11128   for (list = tree_view->priv->columns; list; list = list->next)
11129     if (!GTK_TREE_VIEW_COLUMN (list->data)->clickable)
11130       return FALSE;
11131 
11132   return TRUE;
11133 }
11134 
11135 /**
11136  * gtk_tree_view_set_rules_hint
11137  * @tree_view: a #GtkTreeView
11138  * @setting: %TRUE if the tree requires reading across rows
11139  *
11140  * This function tells GTK+ that the user interface for your
11141  * application requires users to read across tree rows and associate
11142  * cells with one another. By default, GTK+ will then render the tree
11143  * with alternating row colors. Do <emphasis>not</emphasis> use it
11144  * just because you prefer the appearance of the ruled tree; that's a
11145  * question for the theme. Some themes will draw tree rows in
11146  * alternating colors even when rules are turned off, and users who
11147  * prefer that appearance all the time can choose those themes. You
11148  * should call this function only as a <emphasis>semantic</emphasis>
11149  * hint to the theme engine that your tree makes alternating colors
11150  * useful from a functional standpoint (since it has lots of columns,
11151  * generally).
11152  *
11153  **/
11154 void
gtk_tree_view_set_rules_hint(GtkTreeView * tree_view,gboolean setting)11155 gtk_tree_view_set_rules_hint (GtkTreeView  *tree_view,
11156                               gboolean      setting)
11157 {
11158   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11159 
11160   setting = setting != FALSE;
11161 
11162   if (tree_view->priv->has_rules != setting)
11163     {
11164       tree_view->priv->has_rules = setting;
11165       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
11166     }
11167 
11168   g_object_notify (G_OBJECT (tree_view), "rules-hint");
11169 }
11170 
11171 /**
11172  * gtk_tree_view_get_rules_hint
11173  * @tree_view: a #GtkTreeView
11174  *
11175  * Gets the setting set by gtk_tree_view_set_rules_hint().
11176  *
11177  * Return value: %TRUE if rules are useful for the user of this tree
11178  **/
11179 gboolean
gtk_tree_view_get_rules_hint(GtkTreeView * tree_view)11180 gtk_tree_view_get_rules_hint (GtkTreeView  *tree_view)
11181 {
11182   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
11183 
11184   return tree_view->priv->has_rules;
11185 }
11186 
11187 /* Public Column functions
11188  */
11189 
11190 /**
11191  * gtk_tree_view_append_column:
11192  * @tree_view: A #GtkTreeView.
11193  * @column: The #GtkTreeViewColumn to add.
11194  *
11195  * Appends @column to the list of columns. If @tree_view has "fixed_height"
11196  * mode enabled, then @column must have its "sizing" property set to be
11197  * GTK_TREE_VIEW_COLUMN_FIXED.
11198  *
11199  * Return value: The number of columns in @tree_view after appending.
11200  **/
11201 gint
gtk_tree_view_append_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)11202 gtk_tree_view_append_column (GtkTreeView       *tree_view,
11203 			     GtkTreeViewColumn *column)
11204 {
11205   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11206   g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
11207   g_return_val_if_fail (column->tree_view == NULL, -1);
11208 
11209   return gtk_tree_view_insert_column (tree_view, column, -1);
11210 }
11211 
11212 
11213 /**
11214  * gtk_tree_view_remove_column:
11215  * @tree_view: A #GtkTreeView.
11216  * @column: The #GtkTreeViewColumn to remove.
11217  *
11218  * Removes @column from @tree_view.
11219  *
11220  * Return value: The number of columns in @tree_view after removing.
11221  **/
11222 gint
gtk_tree_view_remove_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)11223 gtk_tree_view_remove_column (GtkTreeView       *tree_view,
11224                              GtkTreeViewColumn *column)
11225 {
11226   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11227   g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
11228   g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
11229 
11230   if (tree_view->priv->focus_column == column)
11231     tree_view->priv->focus_column = NULL;
11232 
11233   if (tree_view->priv->edited_column == column)
11234     {
11235       gtk_tree_view_stop_editing (tree_view, TRUE);
11236 
11237       /* no need to, but just to be sure ... */
11238       tree_view->priv->edited_column = NULL;
11239     }
11240 
11241   if (tree_view->priv->expander_column == column)
11242     tree_view->priv->expander_column = NULL;
11243 
11244   g_signal_handlers_disconnect_by_func (column,
11245                                         G_CALLBACK (column_sizing_notify),
11246                                         tree_view);
11247 
11248   _gtk_tree_view_column_unset_tree_view (column);
11249 
11250   tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
11251   tree_view->priv->n_columns--;
11252 
11253   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11254     {
11255       GList *list;
11256 
11257       _gtk_tree_view_column_unrealize_button (column);
11258       for (list = tree_view->priv->columns; list; list = list->next)
11259 	{
11260 	  GtkTreeViewColumn *tmp_column;
11261 
11262 	  tmp_column = GTK_TREE_VIEW_COLUMN (list->data);
11263 	  if (tmp_column->visible)
11264 	    _gtk_tree_view_column_cell_set_dirty (tmp_column, TRUE);
11265 	}
11266 
11267       if (tree_view->priv->n_columns == 0 &&
11268 	  gtk_tree_view_get_headers_visible (tree_view))
11269 	gdk_window_hide (tree_view->priv->header_window);
11270 
11271       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11272     }
11273 
11274   g_object_unref (column);
11275   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
11276 
11277   return tree_view->priv->n_columns;
11278 }
11279 
11280 /**
11281  * gtk_tree_view_insert_column:
11282  * @tree_view: A #GtkTreeView.
11283  * @column: The #GtkTreeViewColumn to be inserted.
11284  * @position: The position to insert @column in.
11285  *
11286  * This inserts the @column into the @tree_view at @position.  If @position is
11287  * -1, then the column is inserted at the end. If @tree_view has
11288  * "fixed_height" mode enabled, then @column must have its "sizing" property
11289  * set to be GTK_TREE_VIEW_COLUMN_FIXED.
11290  *
11291  * Return value: The number of columns in @tree_view after insertion.
11292  **/
11293 gint
gtk_tree_view_insert_column(GtkTreeView * tree_view,GtkTreeViewColumn * column,gint position)11294 gtk_tree_view_insert_column (GtkTreeView       *tree_view,
11295                              GtkTreeViewColumn *column,
11296                              gint               position)
11297 {
11298   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11299   g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
11300   g_return_val_if_fail (column->tree_view == NULL, -1);
11301 
11302   if (tree_view->priv->fixed_height_mode)
11303     g_return_val_if_fail (gtk_tree_view_column_get_sizing (column)
11304                           == GTK_TREE_VIEW_COLUMN_FIXED, -1);
11305 
11306   g_object_ref_sink (column);
11307 
11308   if (tree_view->priv->n_columns == 0 &&
11309       gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
11310       gtk_tree_view_get_headers_visible (tree_view))
11311     {
11312       gdk_window_show (tree_view->priv->header_window);
11313     }
11314 
11315   g_signal_connect (column, "notify::sizing",
11316                     G_CALLBACK (column_sizing_notify), tree_view);
11317 
11318   tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
11319 					    column, position);
11320   tree_view->priv->n_columns++;
11321 
11322   _gtk_tree_view_column_set_tree_view (column, tree_view);
11323 
11324   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11325     {
11326       GList *list;
11327 
11328       _gtk_tree_view_column_realize_button (column);
11329 
11330       for (list = tree_view->priv->columns; list; list = list->next)
11331 	{
11332 	  column = GTK_TREE_VIEW_COLUMN (list->data);
11333 	  if (column->visible)
11334 	    _gtk_tree_view_column_cell_set_dirty (column, TRUE);
11335 	}
11336       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11337     }
11338 
11339   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
11340 
11341   return tree_view->priv->n_columns;
11342 }
11343 
11344 /**
11345  * gtk_tree_view_insert_column_with_attributes:
11346  * @tree_view: A #GtkTreeView
11347  * @position: The position to insert the new column in.
11348  * @title: The title to set the header to.
11349  * @cell: The #GtkCellRenderer.
11350  * @Varargs: A %NULL-terminated list of attributes.
11351  *
11352  * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at
11353  * @position.  If @position is -1, then the newly created column is inserted at
11354  * the end.  The column is initialized with the attributes given. If @tree_view
11355  * has "fixed_height" mode enabled, then the new column will have its sizing
11356  * property set to be GTK_TREE_VIEW_COLUMN_FIXED.
11357  *
11358  * Return value: The number of columns in @tree_view after insertion.
11359  **/
11360 gint
gtk_tree_view_insert_column_with_attributes(GtkTreeView * tree_view,gint position,const gchar * title,GtkCellRenderer * cell,...)11361 gtk_tree_view_insert_column_with_attributes (GtkTreeView     *tree_view,
11362 					     gint             position,
11363 					     const gchar     *title,
11364 					     GtkCellRenderer *cell,
11365 					     ...)
11366 {
11367   GtkTreeViewColumn *column;
11368   gchar *attribute;
11369   va_list args;
11370   gint column_id;
11371 
11372   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11373 
11374   column = gtk_tree_view_column_new ();
11375   if (tree_view->priv->fixed_height_mode)
11376     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
11377 
11378   gtk_tree_view_column_set_title (column, title);
11379   gtk_tree_view_column_pack_start (column, cell, TRUE);
11380 
11381   va_start (args, cell);
11382 
11383   attribute = va_arg (args, gchar *);
11384 
11385   while (attribute != NULL)
11386     {
11387       column_id = va_arg (args, gint);
11388       gtk_tree_view_column_add_attribute (column, cell, attribute, column_id);
11389       attribute = va_arg (args, gchar *);
11390     }
11391 
11392   va_end (args);
11393 
11394   gtk_tree_view_insert_column (tree_view, column, position);
11395 
11396   return tree_view->priv->n_columns;
11397 }
11398 
11399 /**
11400  * gtk_tree_view_insert_column_with_data_func:
11401  * @tree_view: a #GtkTreeView
11402  * @position: Position to insert, -1 for append
11403  * @title: column title
11404  * @cell: cell renderer for column
11405  * @func: function to set attributes of cell renderer
11406  * @data: data for @func
11407  * @dnotify: destroy notifier for @data
11408  *
11409  * Convenience function that inserts a new column into the #GtkTreeView
11410  * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
11411  * attributes (normally using data from the model). See also
11412  * gtk_tree_view_column_set_cell_data_func(), gtk_tree_view_column_pack_start().
11413  * If @tree_view has "fixed_height" mode enabled, then the new column will have its
11414  * "sizing" property set to be GTK_TREE_VIEW_COLUMN_FIXED.
11415  *
11416  * Return value: number of columns in the tree view post-insert
11417  **/
11418 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)11419 gtk_tree_view_insert_column_with_data_func  (GtkTreeView               *tree_view,
11420                                              gint                       position,
11421                                              const gchar               *title,
11422                                              GtkCellRenderer           *cell,
11423                                              GtkTreeCellDataFunc        func,
11424                                              gpointer                   data,
11425                                              GDestroyNotify             dnotify)
11426 {
11427   GtkTreeViewColumn *column;
11428 
11429   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
11430 
11431   column = gtk_tree_view_column_new ();
11432   if (tree_view->priv->fixed_height_mode)
11433     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
11434 
11435   gtk_tree_view_column_set_title (column, title);
11436   gtk_tree_view_column_pack_start (column, cell, TRUE);
11437   gtk_tree_view_column_set_cell_data_func (column, cell, func, data, dnotify);
11438 
11439   gtk_tree_view_insert_column (tree_view, column, position);
11440 
11441   return tree_view->priv->n_columns;
11442 }
11443 
11444 /**
11445  * gtk_tree_view_get_column:
11446  * @tree_view: A #GtkTreeView.
11447  * @n: The position of the column, counting from 0.
11448  *
11449  * Gets the #GtkTreeViewColumn at the given position in the #tree_view.
11450  *
11451  * Return value: (transfer none): The #GtkTreeViewColumn, or %NULL if the
11452  *     position is outside the range of columns.
11453  **/
11454 GtkTreeViewColumn *
gtk_tree_view_get_column(GtkTreeView * tree_view,gint n)11455 gtk_tree_view_get_column (GtkTreeView *tree_view,
11456 			  gint         n)
11457 {
11458   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11459 
11460   if (n < 0 || n >= tree_view->priv->n_columns)
11461     return NULL;
11462 
11463   if (tree_view->priv->columns == NULL)
11464     return NULL;
11465 
11466   return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
11467 }
11468 
11469 /**
11470  * gtk_tree_view_get_columns:
11471  * @tree_view: A #GtkTreeView
11472  *
11473  * Returns a #GList of all the #GtkTreeViewColumn s currently in @tree_view.
11474  * The returned list must be freed with g_list_free ().
11475  *
11476  * Return value: (element-type GtkTreeViewColumn) (transfer container): A list of #GtkTreeViewColumn s
11477  **/
11478 GList *
gtk_tree_view_get_columns(GtkTreeView * tree_view)11479 gtk_tree_view_get_columns (GtkTreeView *tree_view)
11480 {
11481   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11482 
11483   return g_list_copy (tree_view->priv->columns);
11484 }
11485 
11486 /**
11487  * gtk_tree_view_move_column_after:
11488  * @tree_view: A #GtkTreeView
11489  * @column: The #GtkTreeViewColumn to be moved.
11490  * @base_column: (allow-none): The #GtkTreeViewColumn to be moved relative to, or %NULL.
11491  *
11492  * Moves @column to be after to @base_column.  If @base_column is %NULL, then
11493  * @column is placed in the first position.
11494  **/
11495 void
gtk_tree_view_move_column_after(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkTreeViewColumn * base_column)11496 gtk_tree_view_move_column_after (GtkTreeView       *tree_view,
11497 				 GtkTreeViewColumn *column,
11498 				 GtkTreeViewColumn *base_column)
11499 {
11500   GList *column_list_el, *base_el = NULL;
11501 
11502   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11503 
11504   column_list_el = g_list_find (tree_view->priv->columns, column);
11505   g_return_if_fail (column_list_el != NULL);
11506 
11507   if (base_column)
11508     {
11509       base_el = g_list_find (tree_view->priv->columns, base_column);
11510       g_return_if_fail (base_el != NULL);
11511     }
11512 
11513   if (column_list_el->prev == base_el)
11514     return;
11515 
11516   tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
11517   if (base_el == NULL)
11518     {
11519       column_list_el->prev = NULL;
11520       column_list_el->next = tree_view->priv->columns;
11521       if (column_list_el->next)
11522 	column_list_el->next->prev = column_list_el;
11523       tree_view->priv->columns = column_list_el;
11524     }
11525   else
11526     {
11527       column_list_el->prev = base_el;
11528       column_list_el->next = base_el->next;
11529       if (column_list_el->next)
11530 	column_list_el->next->prev = column_list_el;
11531       base_el->next = column_list_el;
11532     }
11533 
11534   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
11535     {
11536       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
11537       gtk_tree_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
11538     }
11539 
11540   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
11541 }
11542 
11543 /**
11544  * gtk_tree_view_set_expander_column:
11545  * @tree_view: A #GtkTreeView
11546  * @column: %NULL, or the column to draw the expander arrow at.
11547  *
11548  * Sets the column to draw the expander arrow at. It must be in @tree_view.
11549  * If @column is %NULL, then the expander arrow is always at the first
11550  * visible column.
11551  *
11552  * If you do not want expander arrow to appear in your tree, set the
11553  * expander column to a hidden column.
11554  **/
11555 void
gtk_tree_view_set_expander_column(GtkTreeView * tree_view,GtkTreeViewColumn * column)11556 gtk_tree_view_set_expander_column (GtkTreeView       *tree_view,
11557                                    GtkTreeViewColumn *column)
11558 {
11559   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11560   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
11561 
11562   if (tree_view->priv->expander_column != column)
11563     {
11564       GList *list;
11565 
11566       if (column)
11567 	{
11568 	  /* Confirm that column is in tree_view */
11569 	  for (list = tree_view->priv->columns; list; list = list->next)
11570 	    if (list->data == column)
11571 	      break;
11572 	  g_return_if_fail (list != NULL);
11573 	}
11574 
11575       tree_view->priv->expander_column = column;
11576       g_object_notify (G_OBJECT (tree_view), "expander-column");
11577     }
11578 }
11579 
11580 /**
11581  * gtk_tree_view_get_expander_column:
11582  * @tree_view: A #GtkTreeView
11583  *
11584  * Returns the column that is the current expander column.
11585  * This column has the expander arrow drawn next to it.
11586  *
11587  * Return value: (transfer none): The expander column.
11588  **/
11589 GtkTreeViewColumn *
gtk_tree_view_get_expander_column(GtkTreeView * tree_view)11590 gtk_tree_view_get_expander_column (GtkTreeView *tree_view)
11591 {
11592   GList *list;
11593 
11594   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
11595 
11596   for (list = tree_view->priv->columns; list; list = list->next)
11597     if (gtk_tree_view_is_expander_column (tree_view, GTK_TREE_VIEW_COLUMN (list->data)))
11598       return (GtkTreeViewColumn *) list->data;
11599   return NULL;
11600 }
11601 
11602 
11603 /**
11604  * gtk_tree_view_set_column_drag_function:
11605  * @tree_view: A #GtkTreeView.
11606  * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
11607  * @user_data: (allow-none): User data to be passed to @func, or %NULL
11608  * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
11609  *
11610  * Sets a user function for determining where a column may be dropped when
11611  * dragged.  This function is called on every column pair in turn at the
11612  * beginning of a column drag to determine where a drop can take place.  The
11613  * arguments passed to @func are: the @tree_view, the #GtkTreeViewColumn being
11614  * dragged, the two #GtkTreeViewColumn s determining the drop spot, and
11615  * @user_data.  If either of the #GtkTreeViewColumn arguments for the drop spot
11616  * are %NULL, then they indicate an edge.  If @func is set to be %NULL, then
11617  * @tree_view reverts to the default behavior of allowing all columns to be
11618  * dropped everywhere.
11619  **/
11620 void
gtk_tree_view_set_column_drag_function(GtkTreeView * tree_view,GtkTreeViewColumnDropFunc func,gpointer user_data,GDestroyNotify destroy)11621 gtk_tree_view_set_column_drag_function (GtkTreeView               *tree_view,
11622 					GtkTreeViewColumnDropFunc  func,
11623 					gpointer                   user_data,
11624 					GDestroyNotify             destroy)
11625 {
11626   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11627 
11628   if (tree_view->priv->column_drop_func_data_destroy)
11629     tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
11630 
11631   tree_view->priv->column_drop_func = func;
11632   tree_view->priv->column_drop_func_data = user_data;
11633   tree_view->priv->column_drop_func_data_destroy = destroy;
11634 }
11635 
11636 /**
11637  * gtk_tree_view_scroll_to_point:
11638  * @tree_view: a #GtkTreeView
11639  * @tree_x: X coordinate of new top-left pixel of visible area, or -1
11640  * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
11641  *
11642  * Scrolls the tree view such that the top-left corner of the visible
11643  * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
11644  * in tree coordinates.  The @tree_view must be realized before
11645  * this function is called.  If it isn't, you probably want to be
11646  * using gtk_tree_view_scroll_to_cell().
11647  *
11648  * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
11649  **/
11650 void
gtk_tree_view_scroll_to_point(GtkTreeView * tree_view,gint tree_x,gint tree_y)11651 gtk_tree_view_scroll_to_point (GtkTreeView *tree_view,
11652                                gint         tree_x,
11653                                gint         tree_y)
11654 {
11655   GtkAdjustment *hadj;
11656   GtkAdjustment *vadj;
11657 
11658   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11659   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
11660 
11661   hadj = tree_view->priv->hadjustment;
11662   vadj = tree_view->priv->vadjustment;
11663 
11664   if (tree_x != -1)
11665     gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper - hadj->page_size));
11666   if (tree_y != -1)
11667     gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper - vadj->page_size));
11668 }
11669 
11670 /**
11671  * gtk_tree_view_scroll_to_cell:
11672  * @tree_view: A #GtkTreeView.
11673  * @path: (allow-none): The path of the row to move to, or %NULL.
11674  * @column: (allow-none): The #GtkTreeViewColumn to move horizontally to, or %NULL.
11675  * @use_align: whether to use alignment arguments, or %FALSE.
11676  * @row_align: The vertical alignment of the row specified by @path.
11677  * @col_align: The horizontal alignment of the column specified by @column.
11678  *
11679  * Moves the alignments of @tree_view to the position specified by @column and
11680  * @path.  If @column is %NULL, then no horizontal scrolling occurs.  Likewise,
11681  * if @path is %NULL no vertical scrolling occurs.  At a minimum, one of @column
11682  * or @path need to be non-%NULL.  @row_align determines where the row is
11683  * placed, and @col_align determines where @column is placed.  Both are expected
11684  * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
11685  * right/bottom alignment, 0.5 means center.
11686  *
11687  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
11688  * tree does the minimum amount of work to scroll the cell onto the screen.
11689  * This means that the cell will be scrolled to the edge closest to its current
11690  * position.  If the cell is currently visible on the screen, nothing is done.
11691  *
11692  * This function only works if the model is set, and @path is a valid row on the
11693  * model.  If the model changes before the @tree_view is realized, the centered
11694  * path will be modified to reflect this change.
11695  **/
11696 void
gtk_tree_view_scroll_to_cell(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gboolean use_align,gfloat row_align,gfloat col_align)11697 gtk_tree_view_scroll_to_cell (GtkTreeView       *tree_view,
11698                               GtkTreePath       *path,
11699                               GtkTreeViewColumn *column,
11700 			      gboolean           use_align,
11701                               gfloat             row_align,
11702                               gfloat             col_align)
11703 {
11704   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11705   g_return_if_fail (tree_view->priv->model != NULL);
11706   g_return_if_fail (tree_view->priv->tree != NULL);
11707   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
11708   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
11709   g_return_if_fail (path != NULL || column != NULL);
11710 
11711 #if 0
11712   g_print ("gtk_tree_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
11713 	   gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
11714 #endif
11715   row_align = CLAMP (row_align, 0.0, 1.0);
11716   col_align = CLAMP (col_align, 0.0, 1.0);
11717 
11718 
11719   /* Note: Despite the benefits that come from having one code path for the
11720    * scrolling code, we short-circuit validate_visible_area's immplementation as
11721    * it is much slower than just going to the point.
11722    */
11723   if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
11724       !gtk_widget_get_realized (GTK_WIDGET (tree_view)) ||
11725       GTK_WIDGET_ALLOC_NEEDED (tree_view) ||
11726       GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID))
11727     {
11728       if (tree_view->priv->scroll_to_path)
11729 	gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
11730 
11731       tree_view->priv->scroll_to_path = NULL;
11732       tree_view->priv->scroll_to_column = NULL;
11733 
11734       if (path)
11735 	tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11736       if (column)
11737 	tree_view->priv->scroll_to_column = column;
11738       tree_view->priv->scroll_to_use_align = use_align;
11739       tree_view->priv->scroll_to_row_align = row_align;
11740       tree_view->priv->scroll_to_col_align = col_align;
11741 
11742       install_presize_handler (tree_view);
11743     }
11744   else
11745     {
11746       GdkRectangle cell_rect;
11747       GdkRectangle vis_rect;
11748       gint dest_x, dest_y;
11749 
11750       gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect);
11751       gtk_tree_view_get_visible_rect (tree_view, &vis_rect);
11752 
11753       cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
11754 
11755       dest_x = vis_rect.x;
11756       dest_y = vis_rect.y;
11757 
11758       if (column)
11759 	{
11760 	  if (use_align)
11761 	    {
11762 	      dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
11763 	    }
11764 	  else
11765 	    {
11766 	      if (cell_rect.x < vis_rect.x)
11767 		dest_x = cell_rect.x;
11768 	      if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
11769 		dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
11770 	    }
11771 	}
11772 
11773       if (path)
11774 	{
11775 	  if (use_align)
11776 	    {
11777 	      dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
11778 	      dest_y = MAX (dest_y, 0);
11779 	    }
11780 	  else
11781 	    {
11782 	      if (cell_rect.y < vis_rect.y)
11783 		dest_y = cell_rect.y;
11784 	      if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
11785 		dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
11786 	    }
11787 	}
11788 
11789       gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y);
11790     }
11791 }
11792 
11793 /**
11794  * gtk_tree_view_row_activated:
11795  * @tree_view: A #GtkTreeView
11796  * @path: The #GtkTreePath to be activated.
11797  * @column: The #GtkTreeViewColumn to be activated.
11798  *
11799  * Activates the cell determined by @path and @column.
11800  **/
11801 void
gtk_tree_view_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column)11802 gtk_tree_view_row_activated (GtkTreeView       *tree_view,
11803 			     GtkTreePath       *path,
11804 			     GtkTreeViewColumn *column)
11805 {
11806   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11807 
11808   g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
11809 }
11810 
11811 
11812 static void
gtk_tree_view_expand_all_emission_helper(GtkRBTree * tree,GtkRBNode * node,gpointer data)11813 gtk_tree_view_expand_all_emission_helper (GtkRBTree *tree,
11814                                           GtkRBNode *node,
11815                                           gpointer   data)
11816 {
11817   GtkTreeView *tree_view = data;
11818 
11819   if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT &&
11820       node->children)
11821     {
11822       GtkTreePath *path;
11823       GtkTreeIter iter;
11824 
11825       path = _gtk_tree_view_find_path (tree_view, tree, node);
11826       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
11827 
11828       g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
11829 
11830       gtk_tree_path_free (path);
11831     }
11832 
11833   if (node->children)
11834     _gtk_rbtree_traverse (node->children,
11835                           node->children->root,
11836                           G_PRE_ORDER,
11837                           gtk_tree_view_expand_all_emission_helper,
11838                           tree_view);
11839 }
11840 
11841 /**
11842  * gtk_tree_view_expand_all:
11843  * @tree_view: A #GtkTreeView.
11844  *
11845  * Recursively expands all nodes in the @tree_view.
11846  **/
11847 void
gtk_tree_view_expand_all(GtkTreeView * tree_view)11848 gtk_tree_view_expand_all (GtkTreeView *tree_view)
11849 {
11850   GtkTreePath *path;
11851   GtkRBTree *tree;
11852   GtkRBNode *node;
11853 
11854   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
11855 
11856   if (tree_view->priv->tree == NULL)
11857     return;
11858 
11859   path = gtk_tree_path_new_first ();
11860   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
11861 
11862   while (node)
11863     {
11864       gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE, FALSE);
11865       node = _gtk_rbtree_next (tree, node);
11866       gtk_tree_path_next (path);
11867   }
11868 
11869   gtk_tree_path_free (path);
11870 }
11871 
11872 /* Timeout to animate the expander during expands and collapses */
11873 static gboolean
expand_collapse_timeout(gpointer data)11874 expand_collapse_timeout (gpointer data)
11875 {
11876   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
11877   gboolean retval = do_expand_collapse (data);
11878 
11879   if (! retval)
11880     remove_expand_collapse_timeout (tree_view);
11881 
11882   return retval;
11883 }
11884 
11885 static void
add_expand_collapse_timeout(GtkTreeView * tree_view,GtkRBTree * tree,GtkRBNode * node,gboolean expand)11886 add_expand_collapse_timeout (GtkTreeView *tree_view,
11887                              GtkRBTree   *tree,
11888                              GtkRBNode   *node,
11889                              gboolean     expand)
11890 {
11891   if (tree_view->priv->expand_collapse_timeout != 0)
11892     return;
11893 
11894   tree_view->priv->expand_collapse_timeout =
11895       gdk_threads_add_timeout (50, expand_collapse_timeout, tree_view);
11896   tree_view->priv->expanded_collapsed_tree = tree;
11897   tree_view->priv->expanded_collapsed_node = node;
11898 
11899   if (expand)
11900     GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
11901   else
11902     GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
11903 }
11904 
11905 static void
remove_expand_collapse_timeout(GtkTreeView * tree_view)11906 remove_expand_collapse_timeout (GtkTreeView *tree_view)
11907 {
11908   if (tree_view->priv->expand_collapse_timeout)
11909     {
11910       g_source_remove (tree_view->priv->expand_collapse_timeout);
11911       tree_view->priv->expand_collapse_timeout = 0;
11912     }
11913 
11914   if (tree_view->priv->expanded_collapsed_node != NULL)
11915     {
11916       GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_EXPANDED);
11917       GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_COLLAPSED);
11918 
11919       tree_view->priv->expanded_collapsed_node = NULL;
11920     }
11921 }
11922 
11923 static void
cancel_arrow_animation(GtkTreeView * tree_view)11924 cancel_arrow_animation (GtkTreeView *tree_view)
11925 {
11926   if (tree_view->priv->expand_collapse_timeout)
11927     {
11928       while (do_expand_collapse (tree_view));
11929 
11930       remove_expand_collapse_timeout (tree_view);
11931     }
11932 }
11933 
11934 static gboolean
do_expand_collapse(GtkTreeView * tree_view)11935 do_expand_collapse (GtkTreeView *tree_view)
11936 {
11937   GtkRBNode *node;
11938   GtkRBTree *tree;
11939   gboolean expanding;
11940   gboolean redraw;
11941 
11942   redraw = FALSE;
11943   expanding = TRUE;
11944 
11945   node = tree_view->priv->expanded_collapsed_node;
11946   tree = tree_view->priv->expanded_collapsed_tree;
11947 
11948   if (node->children == NULL)
11949     expanding = FALSE;
11950 
11951   if (expanding)
11952     {
11953       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_COLLAPSED))
11954 	{
11955 	  GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
11956 	  GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
11957 
11958 	  redraw = TRUE;
11959 
11960 	}
11961       else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_EXPANDED))
11962 	{
11963 	  GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
11964 
11965 	  redraw = TRUE;
11966 	}
11967     }
11968   else
11969     {
11970       if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_EXPANDED))
11971 	{
11972 	  GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_EXPANDED);
11973 	  GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
11974 
11975 	  redraw = TRUE;
11976 	}
11977       else if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SEMI_COLLAPSED))
11978 	{
11979 	  GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SEMI_COLLAPSED);
11980 
11981 	  redraw = TRUE;
11982 
11983 	}
11984     }
11985 
11986   if (redraw)
11987     {
11988       gtk_tree_view_queue_draw_arrow (tree_view, tree, node, NULL);
11989 
11990       return TRUE;
11991     }
11992 
11993   return FALSE;
11994 }
11995 
11996 /**
11997  * gtk_tree_view_collapse_all:
11998  * @tree_view: A #GtkTreeView.
11999  *
12000  * Recursively collapses all visible, expanded nodes in @tree_view.
12001  **/
12002 void
gtk_tree_view_collapse_all(GtkTreeView * tree_view)12003 gtk_tree_view_collapse_all (GtkTreeView *tree_view)
12004 {
12005   GtkRBTree *tree;
12006   GtkRBNode *node;
12007   GtkTreePath *path;
12008   gint *indices;
12009 
12010   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12011 
12012   if (tree_view->priv->tree == NULL)
12013     return;
12014 
12015   path = gtk_tree_path_new ();
12016   gtk_tree_path_down (path);
12017   indices = gtk_tree_path_get_indices (path);
12018 
12019   tree = tree_view->priv->tree;
12020   node = tree->root;
12021   while (node && node->left != tree->nil)
12022     node = node->left;
12023 
12024   while (node)
12025     {
12026       if (node->children)
12027 	gtk_tree_view_real_collapse_row (tree_view, path, tree, node, FALSE);
12028       indices[0]++;
12029       node = _gtk_rbtree_next (tree, node);
12030     }
12031 
12032   gtk_tree_path_free (path);
12033 }
12034 
12035 /**
12036  * gtk_tree_view_expand_to_path:
12037  * @tree_view: A #GtkTreeView.
12038  * @path: path to a row.
12039  *
12040  * Expands the row at @path. This will also expand all parent rows of
12041  * @path as necessary.
12042  *
12043  * Since: 2.2
12044  **/
12045 void
gtk_tree_view_expand_to_path(GtkTreeView * tree_view,GtkTreePath * path)12046 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
12047 			      GtkTreePath *path)
12048 {
12049   gint i, depth;
12050   gint *indices;
12051   GtkTreePath *tmp;
12052 
12053   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12054   g_return_if_fail (path != NULL);
12055 
12056   depth = gtk_tree_path_get_depth (path);
12057   indices = gtk_tree_path_get_indices (path);
12058 
12059   tmp = gtk_tree_path_new ();
12060   g_return_if_fail (tmp != NULL);
12061 
12062   for (i = 0; i < depth; i++)
12063     {
12064       gtk_tree_path_append_index (tmp, indices[i]);
12065       gtk_tree_view_expand_row (tree_view, tmp, FALSE);
12066     }
12067 
12068   gtk_tree_path_free (tmp);
12069 }
12070 
12071 /* FIXME the bool return values for expand_row and collapse_row are
12072  * not analagous; they should be TRUE if the row had children and
12073  * was not already in the requested state.
12074  */
12075 
12076 
12077 static gboolean
gtk_tree_view_real_expand_row(GtkTreeView * tree_view,GtkTreePath * path,GtkRBTree * tree,GtkRBNode * node,gboolean open_all,gboolean animate)12078 gtk_tree_view_real_expand_row (GtkTreeView *tree_view,
12079 			       GtkTreePath *path,
12080 			       GtkRBTree   *tree,
12081 			       GtkRBNode   *node,
12082 			       gboolean     open_all,
12083 			       gboolean     animate)
12084 {
12085   GtkTreeIter iter;
12086   GtkTreeIter temp;
12087   gboolean expand;
12088 
12089   if (animate)
12090     g_object_get (gtk_widget_get_settings (GTK_WIDGET (tree_view)),
12091                   "gtk-enable-animations", &animate,
12092                   NULL);
12093 
12094   remove_auto_expand_timeout (tree_view);
12095 
12096   if (node->children && !open_all)
12097     return FALSE;
12098 
12099   if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
12100     return FALSE;
12101 
12102   gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12103   if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
12104     return FALSE;
12105 
12106 
12107    if (node->children && open_all)
12108     {
12109       gboolean retval = FALSE;
12110       GtkTreePath *tmp_path = gtk_tree_path_copy (path);
12111 
12112       gtk_tree_path_append_index (tmp_path, 0);
12113       tree = node->children;
12114       node = tree->root;
12115       while (node->left != tree->nil)
12116 	node = node->left;
12117       /* try to expand the children */
12118       do
12119         {
12120          gboolean t;
12121 	 t = gtk_tree_view_real_expand_row (tree_view, tmp_path, tree, node,
12122 					    TRUE, animate);
12123          if (t)
12124            retval = TRUE;
12125 
12126          gtk_tree_path_next (tmp_path);
12127 	 node = _gtk_rbtree_next (tree, node);
12128        }
12129       while (node != NULL);
12130 
12131       gtk_tree_path_free (tmp_path);
12132 
12133       return retval;
12134     }
12135 
12136   g_signal_emit (tree_view, tree_view_signals[TEST_EXPAND_ROW], 0, &iter, path, &expand);
12137 
12138   if (!gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
12139     return FALSE;
12140 
12141   if (expand)
12142     return FALSE;
12143 
12144   node->children = _gtk_rbtree_new ();
12145   node->children->parent_tree = tree;
12146   node->children->parent_node = node;
12147 
12148   gtk_tree_model_iter_children (tree_view->priv->model, &temp, &iter);
12149 
12150   gtk_tree_view_build_tree (tree_view,
12151 			    node->children,
12152 			    &temp,
12153 			    gtk_tree_path_get_depth (path) + 1,
12154 			    open_all);
12155 
12156   remove_expand_collapse_timeout (tree_view);
12157 
12158   if (animate)
12159     add_expand_collapse_timeout (tree_view, tree, node, TRUE);
12160 
12161   install_presize_handler (tree_view);
12162 
12163   g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path);
12164   if (open_all && node->children)
12165     {
12166       _gtk_rbtree_traverse (node->children,
12167                             node->children->root,
12168                             G_PRE_ORDER,
12169                             gtk_tree_view_expand_all_emission_helper,
12170                             tree_view);
12171     }
12172   return TRUE;
12173 }
12174 
12175 
12176 /**
12177  * gtk_tree_view_expand_row:
12178  * @tree_view: a #GtkTreeView
12179  * @path: path to a row
12180  * @open_all: whether to recursively expand, or just expand immediate children
12181  *
12182  * Opens the row so its children are visible.
12183  *
12184  * Return value: %TRUE if the row existed and had children
12185  **/
12186 gboolean
gtk_tree_view_expand_row(GtkTreeView * tree_view,GtkTreePath * path,gboolean open_all)12187 gtk_tree_view_expand_row (GtkTreeView *tree_view,
12188 			  GtkTreePath *path,
12189 			  gboolean     open_all)
12190 {
12191   GtkRBTree *tree;
12192   GtkRBNode *node;
12193 
12194   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12195   g_return_val_if_fail (tree_view->priv->model != NULL, FALSE);
12196   g_return_val_if_fail (path != NULL, FALSE);
12197 
12198   if (_gtk_tree_view_find_node (tree_view,
12199 				path,
12200 				&tree,
12201 				&node))
12202     return FALSE;
12203 
12204   if (tree != NULL)
12205     return gtk_tree_view_real_expand_row (tree_view, path, tree, node, open_all, FALSE);
12206   else
12207     return FALSE;
12208 }
12209 
12210 static gboolean
gtk_tree_view_real_collapse_row(GtkTreeView * tree_view,GtkTreePath * path,GtkRBTree * tree,GtkRBNode * node,gboolean animate)12211 gtk_tree_view_real_collapse_row (GtkTreeView *tree_view,
12212 				 GtkTreePath *path,
12213 				 GtkRBTree   *tree,
12214 				 GtkRBNode   *node,
12215 				 gboolean     animate)
12216 {
12217   GtkTreeIter iter;
12218   GtkTreeIter children;
12219   gboolean collapse;
12220   gint x, y;
12221   GList *list;
12222   GdkWindow *child, *parent;
12223 
12224   if (animate)
12225     g_object_get (gtk_widget_get_settings (GTK_WIDGET (tree_view)),
12226                   "gtk-enable-animations", &animate,
12227                   NULL);
12228 
12229   remove_auto_expand_timeout (tree_view);
12230 
12231   if (node->children == NULL)
12232     return FALSE;
12233 
12234   gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
12235 
12236   g_signal_emit (tree_view, tree_view_signals[TEST_COLLAPSE_ROW], 0, &iter, path, &collapse);
12237 
12238   if (collapse)
12239     return FALSE;
12240 
12241   /* if the prelighted node is a child of us, we want to unprelight it.  We have
12242    * a chance to prelight the correct node below */
12243 
12244   if (tree_view->priv->prelight_tree)
12245     {
12246       GtkRBTree *parent_tree;
12247       GtkRBNode *parent_node;
12248 
12249       parent_tree = tree_view->priv->prelight_tree->parent_tree;
12250       parent_node = tree_view->priv->prelight_tree->parent_node;
12251       while (parent_tree)
12252 	{
12253 	  if (parent_tree == tree && parent_node == node)
12254 	    {
12255 	      ensure_unprelighted (tree_view);
12256 	      break;
12257 	    }
12258 	  parent_node = parent_tree->parent_node;
12259 	  parent_tree = parent_tree->parent_tree;
12260 	}
12261     }
12262 
12263   TREE_VIEW_INTERNAL_ASSERT (gtk_tree_model_iter_children (tree_view->priv->model, &children, &iter), FALSE);
12264 
12265   for (list = tree_view->priv->columns; list; list = list->next)
12266     {
12267       GtkTreeViewColumn *column = list->data;
12268 
12269       if (column->visible == FALSE)
12270 	continue;
12271       if (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
12272 	_gtk_tree_view_column_cell_set_dirty (column, TRUE);
12273     }
12274 
12275   if (tree_view->priv->destroy_count_func)
12276     {
12277       GtkTreePath *child_path;
12278       gint child_count = 0;
12279       child_path = gtk_tree_path_copy (path);
12280       gtk_tree_path_down (child_path);
12281       if (node->children)
12282 	_gtk_rbtree_traverse (node->children, node->children->root, G_POST_ORDER, count_children_helper, &child_count);
12283       tree_view->priv->destroy_count_func (tree_view, child_path, child_count, tree_view->priv->destroy_count_data);
12284       gtk_tree_path_free (child_path);
12285     }
12286 
12287   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
12288     {
12289       GtkTreePath *cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
12290 
12291       if (gtk_tree_path_is_ancestor (path, cursor_path))
12292 	{
12293 	  gtk_tree_row_reference_free (tree_view->priv->cursor);
12294 	  tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
12295 								      tree_view->priv->model,
12296 								      path);
12297 	}
12298       gtk_tree_path_free (cursor_path);
12299     }
12300 
12301   if (gtk_tree_row_reference_valid (tree_view->priv->anchor))
12302     {
12303       GtkTreePath *anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
12304       if (gtk_tree_path_is_ancestor (path, anchor_path))
12305 	{
12306 	  gtk_tree_row_reference_free (tree_view->priv->anchor);
12307 	  tree_view->priv->anchor = NULL;
12308 	}
12309       gtk_tree_path_free (anchor_path);
12310     }
12311 
12312   /* Stop a pending double click */
12313   tree_view->priv->last_button_x = -1;
12314   tree_view->priv->last_button_y = -1;
12315 
12316   remove_expand_collapse_timeout (tree_view);
12317 
12318   if (gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children))
12319     {
12320       _gtk_rbtree_remove (node->children);
12321       g_signal_emit_by_name (tree_view->priv->selection, "changed");
12322     }
12323   else
12324     _gtk_rbtree_remove (node->children);
12325 
12326   if (animate)
12327     add_expand_collapse_timeout (tree_view, tree, node, FALSE);
12328 
12329   if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
12330     {
12331       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
12332     }
12333 
12334   g_signal_emit (tree_view, tree_view_signals[ROW_COLLAPSED], 0, &iter, path);
12335 
12336   if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
12337     {
12338       /* now that we've collapsed all rows, we want to try to set the prelight
12339        * again. To do this, we fake a motion event and send it to ourselves. */
12340 
12341       child = tree_view->priv->bin_window;
12342       parent = gdk_window_get_parent (child);
12343 
12344       if (gdk_window_get_pointer (parent, &x, &y, NULL) == child)
12345 	{
12346 	  GdkEventMotion event;
12347 	  gint child_x, child_y;
12348 
12349 	  gdk_window_get_position (child, &child_x, &child_y);
12350 
12351 	  event.window = tree_view->priv->bin_window;
12352 	  event.x = x - child_x;
12353 	  event.y = y - child_y;
12354 
12355 	  /* despite the fact this isn't a real event, I'm almost positive it will
12356 	   * never trigger a drag event.  maybe_drag is the only function that uses
12357 	   * more than just event.x and event.y. */
12358 	  gtk_tree_view_motion_bin_window (GTK_WIDGET (tree_view), &event);
12359 	}
12360     }
12361 
12362   return TRUE;
12363 }
12364 
12365 /**
12366  * gtk_tree_view_collapse_row:
12367  * @tree_view: a #GtkTreeView
12368  * @path: path to a row in the @tree_view
12369  *
12370  * Collapses a row (hides its child rows, if they exist).
12371  *
12372  * Return value: %TRUE if the row was collapsed.
12373  **/
12374 gboolean
gtk_tree_view_collapse_row(GtkTreeView * tree_view,GtkTreePath * path)12375 gtk_tree_view_collapse_row (GtkTreeView *tree_view,
12376 			    GtkTreePath *path)
12377 {
12378   GtkRBTree *tree;
12379   GtkRBNode *node;
12380 
12381   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12382   g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE);
12383   g_return_val_if_fail (path != NULL, FALSE);
12384 
12385   if (_gtk_tree_view_find_node (tree_view,
12386 				path,
12387 				&tree,
12388 				&node))
12389     return FALSE;
12390 
12391   if (tree == NULL || node->children == NULL)
12392     return FALSE;
12393 
12394   return gtk_tree_view_real_collapse_row (tree_view, path, tree, node, FALSE);
12395 }
12396 
12397 static void
gtk_tree_view_map_expanded_rows_helper(GtkTreeView * tree_view,GtkRBTree * tree,GtkTreePath * path,GtkTreeViewMappingFunc func,gpointer user_data)12398 gtk_tree_view_map_expanded_rows_helper (GtkTreeView            *tree_view,
12399 					GtkRBTree              *tree,
12400 					GtkTreePath            *path,
12401 					GtkTreeViewMappingFunc  func,
12402 					gpointer                user_data)
12403 {
12404   GtkRBNode *node;
12405 
12406   if (tree == NULL || tree->root == NULL)
12407     return;
12408 
12409   node = tree->root;
12410 
12411   while (node && node->left != tree->nil)
12412     node = node->left;
12413 
12414   while (node)
12415     {
12416       if (node->children)
12417 	{
12418 	  (* func) (tree_view, path, user_data);
12419 	  gtk_tree_path_down (path);
12420 	  gtk_tree_view_map_expanded_rows_helper (tree_view, node->children, path, func, user_data);
12421 	  gtk_tree_path_up (path);
12422 	}
12423       gtk_tree_path_next (path);
12424       node = _gtk_rbtree_next (tree, node);
12425     }
12426 }
12427 
12428 /**
12429  * gtk_tree_view_map_expanded_rows:
12430  * @tree_view: A #GtkTreeView
12431  * @func: (scope call): A function to be called
12432  * @data: User data to be passed to the function.
12433  *
12434  * Calls @func on all expanded rows.
12435  **/
12436 void
gtk_tree_view_map_expanded_rows(GtkTreeView * tree_view,GtkTreeViewMappingFunc func,gpointer user_data)12437 gtk_tree_view_map_expanded_rows (GtkTreeView            *tree_view,
12438 				 GtkTreeViewMappingFunc  func,
12439 				 gpointer                user_data)
12440 {
12441   GtkTreePath *path;
12442 
12443   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12444   g_return_if_fail (func != NULL);
12445 
12446   path = gtk_tree_path_new_first ();
12447 
12448   gtk_tree_view_map_expanded_rows_helper (tree_view,
12449 					  tree_view->priv->tree,
12450 					  path, func, user_data);
12451 
12452   gtk_tree_path_free (path);
12453 }
12454 
12455 /**
12456  * gtk_tree_view_row_expanded:
12457  * @tree_view: A #GtkTreeView.
12458  * @path: A #GtkTreePath to test expansion state.
12459  *
12460  * Returns %TRUE if the node pointed to by @path is expanded in @tree_view.
12461  *
12462  * Return value: %TRUE if #path is expanded.
12463  **/
12464 gboolean
gtk_tree_view_row_expanded(GtkTreeView * tree_view,GtkTreePath * path)12465 gtk_tree_view_row_expanded (GtkTreeView *tree_view,
12466 			    GtkTreePath *path)
12467 {
12468   GtkRBTree *tree;
12469   GtkRBNode *node;
12470 
12471   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12472   g_return_val_if_fail (path != NULL, FALSE);
12473 
12474   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
12475 
12476   if (node == NULL)
12477     return FALSE;
12478 
12479   return (node->children != NULL);
12480 }
12481 
12482 /**
12483  * gtk_tree_view_get_reorderable:
12484  * @tree_view: a #GtkTreeView
12485  *
12486  * Retrieves whether the user can reorder the tree via drag-and-drop. See
12487  * gtk_tree_view_set_reorderable().
12488  *
12489  * Return value: %TRUE if the tree can be reordered.
12490  **/
12491 gboolean
gtk_tree_view_get_reorderable(GtkTreeView * tree_view)12492 gtk_tree_view_get_reorderable (GtkTreeView *tree_view)
12493 {
12494   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
12495 
12496   return tree_view->priv->reorderable;
12497 }
12498 
12499 /**
12500  * gtk_tree_view_set_reorderable:
12501  * @tree_view: A #GtkTreeView.
12502  * @reorderable: %TRUE, if the tree can be reordered.
12503  *
12504  * This function is a convenience function to allow you to reorder
12505  * models that support the #GtkDragSourceIface and the
12506  * #GtkDragDestIface.  Both #GtkTreeStore and #GtkListStore support
12507  * these.  If @reorderable is %TRUE, then the user can reorder the
12508  * model by dragging and dropping rows. The developer can listen to
12509  * these changes by connecting to the model's row_inserted and
12510  * row_deleted signals. The reordering is implemented by setting up
12511  * the tree view as a drag source and destination. Therefore, drag and
12512  * drop can not be used in a reorderable view for any other purpose.
12513  *
12514  * This function does not give you any degree of control over the order -- any
12515  * reordering is allowed.  If more control is needed, you should probably
12516  * handle drag and drop manually.
12517  **/
12518 void
gtk_tree_view_set_reorderable(GtkTreeView * tree_view,gboolean reorderable)12519 gtk_tree_view_set_reorderable (GtkTreeView *tree_view,
12520 			       gboolean     reorderable)
12521 {
12522   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12523 
12524   reorderable = reorderable != FALSE;
12525 
12526   if (tree_view->priv->reorderable == reorderable)
12527     return;
12528 
12529   if (reorderable)
12530     {
12531       const GtkTargetEntry row_targets[] = {
12532         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
12533       };
12534 
12535       gtk_tree_view_enable_model_drag_source (tree_view,
12536 					      GDK_BUTTON1_MASK,
12537 					      row_targets,
12538 					      G_N_ELEMENTS (row_targets),
12539 					      GDK_ACTION_MOVE);
12540       gtk_tree_view_enable_model_drag_dest (tree_view,
12541 					    row_targets,
12542 					    G_N_ELEMENTS (row_targets),
12543 					    GDK_ACTION_MOVE);
12544     }
12545   else
12546     {
12547       gtk_tree_view_unset_rows_drag_source (tree_view);
12548       gtk_tree_view_unset_rows_drag_dest (tree_view);
12549     }
12550 
12551   tree_view->priv->reorderable = reorderable;
12552 
12553   g_object_notify (G_OBJECT (tree_view), "reorderable");
12554 }
12555 
12556 static void
gtk_tree_view_real_set_cursor(GtkTreeView * tree_view,GtkTreePath * path,gboolean clear_and_select,gboolean clamp_node)12557 gtk_tree_view_real_set_cursor (GtkTreeView     *tree_view,
12558 			       GtkTreePath     *path,
12559 			       gboolean         clear_and_select,
12560 			       gboolean         clamp_node)
12561 {
12562   GtkRBTree *tree = NULL;
12563   GtkRBNode *node = NULL;
12564 
12565   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
12566     {
12567       GtkTreePath *cursor_path;
12568       cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
12569       gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
12570       gtk_tree_path_free (cursor_path);
12571     }
12572 
12573   gtk_tree_row_reference_free (tree_view->priv->cursor);
12574   tree_view->priv->cursor = NULL;
12575 
12576   /* One cannot set the cursor on a separator.   Also, if
12577    * _gtk_tree_view_find_node returns TRUE, it ran out of tree
12578    * before finding the tree and node belonging to path.  The
12579    * path maps to a non-existing path and we will silently bail out.
12580    * We unset tree and node to avoid further processing.
12581    */
12582   if (!row_is_separator (tree_view, NULL, path)
12583       && _gtk_tree_view_find_node (tree_view, path, &tree, &node) == FALSE)
12584     {
12585       tree_view->priv->cursor =
12586           gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
12587                                             tree_view->priv->model,
12588                                             path);
12589     }
12590   else
12591     {
12592       tree = NULL;
12593       node = NULL;
12594     }
12595 
12596   if (tree != NULL)
12597     {
12598       GtkRBTree *new_tree = NULL;
12599       GtkRBNode *new_node = NULL;
12600 
12601       if (clear_and_select && !tree_view->priv->modify_selection_pressed)
12602         {
12603           GtkTreeSelectMode mode = 0;
12604 
12605           if (tree_view->priv->modify_selection_pressed)
12606             mode |= GTK_TREE_SELECT_MODE_TOGGLE;
12607           if (tree_view->priv->extend_selection_pressed)
12608             mode |= GTK_TREE_SELECT_MODE_EXTEND;
12609 
12610           _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
12611                                                     node, tree, path, mode,
12612                                                     FALSE);
12613         }
12614 
12615       /* We have to re-find tree and node here again, somebody might have
12616        * cleared the node or the whole tree in the GtkTreeSelection::changed
12617        * callback. If the nodes differ we bail out here.
12618        */
12619       _gtk_tree_view_find_node (tree_view, path, &new_tree, &new_node);
12620 
12621       if (tree != new_tree || node != new_node)
12622         return;
12623 
12624       if (clamp_node)
12625         {
12626 	  gtk_tree_view_clamp_node_visible (tree_view, tree, node);
12627 	  _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
12628 	}
12629     }
12630 
12631   g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
12632 }
12633 
12634 /**
12635  * gtk_tree_view_get_cursor:
12636  * @tree_view: A #GtkTreeView
12637  * @path: (out) (transfer full) (allow-none): A pointer to be filled with the current cursor path, or %NULL
12638  * @focus_column: (out) (transfer none) (allow-none): A pointer to be filled with the current focus column, or %NULL
12639  *
12640  * Fills in @path and @focus_column with the current path and focus column.  If
12641  * the cursor isn't currently set, then *@path will be %NULL.  If no column
12642  * currently has focus, then *@focus_column will be %NULL.
12643  *
12644  * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
12645  * you are done with it.
12646  **/
12647 void
gtk_tree_view_get_cursor(GtkTreeView * tree_view,GtkTreePath ** path,GtkTreeViewColumn ** focus_column)12648 gtk_tree_view_get_cursor (GtkTreeView        *tree_view,
12649 			  GtkTreePath       **path,
12650 			  GtkTreeViewColumn **focus_column)
12651 {
12652   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12653 
12654   if (path)
12655     {
12656       if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
12657 	*path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
12658       else
12659 	*path = NULL;
12660     }
12661 
12662   if (focus_column)
12663     {
12664       *focus_column = tree_view->priv->focus_column;
12665     }
12666 }
12667 
12668 /**
12669  * gtk_tree_view_set_cursor:
12670  * @tree_view: A #GtkTreeView
12671  * @path: A #GtkTreePath
12672  * @focus_column: (allow-none): A #GtkTreeViewColumn, or %NULL
12673  * @start_editing: %TRUE if the specified cell should start being edited.
12674  *
12675  * Sets the current keyboard focus to be at @path, and selects it.  This is
12676  * useful when you want to focus the user's attention on a particular row.  If
12677  * @focus_column is not %NULL, then focus is given to the column specified by
12678  * it. Additionally, if @focus_column is specified, and @start_editing is
12679  * %TRUE, then editing should be started in the specified cell.
12680  * This function is often followed by @gtk_widget_grab_focus (@tree_view)
12681  * in order to give keyboard focus to the widget.  Please note that editing
12682  * can only happen when the widget is realized.
12683  *
12684  * If @path is invalid for @model, the current cursor (if any) will be unset
12685  * and the function will return without failing.
12686  **/
12687 void
gtk_tree_view_set_cursor(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * focus_column,gboolean start_editing)12688 gtk_tree_view_set_cursor (GtkTreeView       *tree_view,
12689 			  GtkTreePath       *path,
12690 			  GtkTreeViewColumn *focus_column,
12691 			  gboolean           start_editing)
12692 {
12693   gtk_tree_view_set_cursor_on_cell (tree_view, path, focus_column,
12694 				    NULL, start_editing);
12695 }
12696 
12697 /**
12698  * gtk_tree_view_set_cursor_on_cell:
12699  * @tree_view: A #GtkTreeView
12700  * @path: A #GtkTreePath
12701  * @focus_column: (allow-none): A #GtkTreeViewColumn, or %NULL
12702  * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
12703  * @start_editing: %TRUE if the specified cell should start being edited.
12704  *
12705  * Sets the current keyboard focus to be at @path, and selects it.  This is
12706  * useful when you want to focus the user's attention on a particular row.  If
12707  * @focus_column is not %NULL, then focus is given to the column specified by
12708  * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
12709  * contains 2 or more editable or activatable cells, then focus is given to
12710  * the cell specified by @focus_cell. Additionally, if @focus_column is
12711  * specified, and @start_editing is %TRUE, then editing should be started in
12712  * the specified cell.  This function is often followed by
12713  * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
12714  * widget.  Please note that editing can only happen when the widget is
12715  * realized.
12716  *
12717  * If @path is invalid for @model, the current cursor (if any) will be unset
12718  * and the function will return without failing.
12719  *
12720  * Since: 2.2
12721  **/
12722 void
gtk_tree_view_set_cursor_on_cell(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * focus_column,GtkCellRenderer * focus_cell,gboolean start_editing)12723 gtk_tree_view_set_cursor_on_cell (GtkTreeView       *tree_view,
12724 				  GtkTreePath       *path,
12725 				  GtkTreeViewColumn *focus_column,
12726 				  GtkCellRenderer   *focus_cell,
12727 				  gboolean           start_editing)
12728 {
12729   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12730   g_return_if_fail (path != NULL);
12731   g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column));
12732 
12733   if (!tree_view->priv->model)
12734     return;
12735 
12736   if (focus_cell)
12737     {
12738       g_return_if_fail (focus_column);
12739       g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
12740     }
12741 
12742   /* cancel the current editing, if it exists */
12743   if (tree_view->priv->edited_column &&
12744       tree_view->priv->edited_column->editable_widget)
12745     gtk_tree_view_stop_editing (tree_view, TRUE);
12746 
12747   gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE);
12748 
12749   if (focus_column && focus_column->visible)
12750     {
12751       GList *list;
12752       gboolean column_in_tree = FALSE;
12753 
12754       for (list = tree_view->priv->columns; list; list = list->next)
12755 	if (list->data == focus_column)
12756 	  {
12757 	    column_in_tree = TRUE;
12758 	    break;
12759 	  }
12760       g_return_if_fail (column_in_tree);
12761       tree_view->priv->focus_column = focus_column;
12762       if (focus_cell)
12763 	gtk_tree_view_column_focus_cell (focus_column, focus_cell);
12764       if (start_editing)
12765 	gtk_tree_view_start_editing (tree_view, path);
12766     }
12767 }
12768 
12769 /**
12770  * gtk_tree_view_get_bin_window:
12771  * @tree_view: A #GtkTreeView
12772  *
12773  * Returns the window that @tree_view renders to.
12774  * This is used primarily to compare to <literal>event->window</literal>
12775  * to confirm that the event on @tree_view is on the right window.
12776  *
12777  * Return value: (transfer none): A #GdkWindow, or %NULL when @tree_view
12778  *     hasn't been realized yet
12779  **/
12780 GdkWindow *
gtk_tree_view_get_bin_window(GtkTreeView * tree_view)12781 gtk_tree_view_get_bin_window (GtkTreeView *tree_view)
12782 {
12783   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
12784 
12785   return tree_view->priv->bin_window;
12786 }
12787 
12788 /**
12789  * gtk_tree_view_get_path_at_pos:
12790  * @tree_view: A #GtkTreeView.
12791  * @x: The x position to be identified (relative to bin_window).
12792  * @y: The y position to be identified (relative to bin_window).
12793  * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
12794  * @column: (out) (transfer none) (allow-none): A pointer to a #GtkTreeViewColumn pointer to be filled in, or %NULL
12795  * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
12796  * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
12797  *
12798  * Finds the path at the point (@x, @y), relative to bin_window coordinates
12799  * (please see gtk_tree_view_get_bin_window()).
12800  * That is, @x and @y are relative to an events coordinates. @x and @y must
12801  * come from an event on the @tree_view only where <literal>event->window ==
12802  * gtk_tree_view_get_bin_window (<!-- -->)</literal>. It is primarily for
12803  * things like popup menus. If @path is non-%NULL, then it will be filled
12804  * with the #GtkTreePath at that point.  This path should be freed with
12805  * gtk_tree_path_free().  If @column is non-%NULL, then it will be filled
12806  * with the column at that point.  @cell_x and @cell_y return the coordinates
12807  * relative to the cell background (i.e. the @background_area passed to
12808  * gtk_cell_renderer_render()).  This function is only meaningful if
12809  * @tree_view is realized.  Therefore this function will always return %FALSE
12810  * if @tree_view is not realized or does not have a model.
12811  *
12812  * For converting widget coordinates (eg. the ones you get from
12813  * GtkWidget::query-tooltip), please see
12814  * gtk_tree_view_convert_widget_to_bin_window_coords().
12815  *
12816  * Return value: %TRUE if a row exists at that coordinate.
12817  **/
12818 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)12819 gtk_tree_view_get_path_at_pos (GtkTreeView        *tree_view,
12820 			       gint                x,
12821 			       gint                y,
12822 			       GtkTreePath       **path,
12823 			       GtkTreeViewColumn **column,
12824                                gint               *cell_x,
12825                                gint               *cell_y)
12826 {
12827   GtkRBTree *tree;
12828   GtkRBNode *node;
12829   gint y_offset;
12830 
12831   g_return_val_if_fail (tree_view != NULL, FALSE);
12832 
12833   if (path)
12834     *path = NULL;
12835   if (column)
12836     *column = NULL;
12837 
12838   if (tree_view->priv->bin_window == NULL)
12839     return FALSE;
12840 
12841   if (tree_view->priv->tree == NULL)
12842     return FALSE;
12843 
12844   if (x > tree_view->priv->hadjustment->upper)
12845     return FALSE;
12846 
12847   if (x < 0 || y < 0)
12848     return FALSE;
12849 
12850   if (column || cell_x)
12851     {
12852       GtkTreeViewColumn *tmp_column;
12853       GtkTreeViewColumn *last_column = NULL;
12854       GList *list;
12855       gint remaining_x = x;
12856       gboolean found = FALSE;
12857       gboolean rtl;
12858 
12859       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
12860       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
12861 	   list;
12862 	   list = (rtl ? list->prev : list->next))
12863 	{
12864 	  tmp_column = list->data;
12865 
12866 	  if (tmp_column->visible == FALSE)
12867 	    continue;
12868 
12869 	  last_column = tmp_column;
12870 	  if (remaining_x <= tmp_column->width)
12871 	    {
12872               found = TRUE;
12873 
12874               if (column)
12875                 *column = tmp_column;
12876 
12877               if (cell_x)
12878                 *cell_x = remaining_x;
12879 
12880 	      break;
12881 	    }
12882 	  remaining_x -= tmp_column->width;
12883 	}
12884 
12885       /* If found is FALSE and there is a last_column, then it the remainder
12886        * space is in that area
12887        */
12888       if (!found)
12889         {
12890 	  if (last_column)
12891 	    {
12892 	      if (column)
12893 		*column = last_column;
12894 
12895 	      if (cell_x)
12896 		*cell_x = last_column->width + remaining_x;
12897 	    }
12898 	  else
12899 	    {
12900 	      return FALSE;
12901 	    }
12902 	}
12903     }
12904 
12905   y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
12906 				      TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
12907 				      &tree, &node);
12908 
12909   if (tree == NULL)
12910     return FALSE;
12911 
12912   if (cell_y)
12913     *cell_y = y_offset;
12914 
12915   if (path)
12916     *path = _gtk_tree_view_find_path (tree_view, tree, node);
12917 
12918   return TRUE;
12919 }
12920 
12921 
12922 /**
12923  * gtk_tree_view_get_cell_area:
12924  * @tree_view: a #GtkTreeView
12925  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
12926  * @column: (allow-none): a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordinates
12927  * @rect: (out): rectangle to fill with cell rect
12928  *
12929  * Fills the bounding rectangle in bin_window coordinates for the cell at the
12930  * row specified by @path and the column specified by @column.  If @path is
12931  * %NULL, or points to a path not currently displayed, the @y and @height fields
12932  * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
12933  * fields will be filled with 0.  The sum of all cell rects does not cover the
12934  * entire tree; there are extra pixels in between rows, for example. The
12935  * returned rectangle is equivalent to the @cell_area passed to
12936  * gtk_cell_renderer_render().  This function is only valid if @tree_view is
12937  * realized.
12938  **/
12939 void
gtk_tree_view_get_cell_area(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GdkRectangle * rect)12940 gtk_tree_view_get_cell_area (GtkTreeView        *tree_view,
12941                              GtkTreePath        *path,
12942                              GtkTreeViewColumn  *column,
12943                              GdkRectangle       *rect)
12944 {
12945   GtkRBTree *tree = NULL;
12946   GtkRBNode *node = NULL;
12947   gint vertical_separator;
12948   gint horizontal_separator;
12949 
12950   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
12951   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
12952   g_return_if_fail (rect != NULL);
12953   g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
12954   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
12955 
12956   gtk_widget_style_get (GTK_WIDGET (tree_view),
12957 			"vertical-separator", &vertical_separator,
12958 			"horizontal-separator", &horizontal_separator,
12959 			NULL);
12960 
12961   rect->x = 0;
12962   rect->y = 0;
12963   rect->width = 0;
12964   rect->height = 0;
12965 
12966   if (column)
12967     {
12968       rect->x = column->button->allocation.x + horizontal_separator/2;
12969       rect->width = column->button->allocation.width - horizontal_separator;
12970     }
12971 
12972   if (path)
12973     {
12974       gboolean ret = _gtk_tree_view_find_node (tree_view, path, &tree, &node);
12975 
12976       /* Get vertical coords */
12977       if ((!ret && tree == NULL) || ret)
12978 	return;
12979 
12980       rect->y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator);
12981       rect->height = MAX (CELL_HEIGHT (node, vertical_separator), tree_view->priv->expander_size - vertical_separator);
12982 
12983       if (column &&
12984 	  gtk_tree_view_is_expander_column (tree_view, column))
12985 	{
12986 	  gint depth = gtk_tree_path_get_depth (path);
12987 	  gboolean rtl;
12988 
12989 	  rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
12990 
12991 	  if (!rtl)
12992 	    rect->x += (depth - 1) * tree_view->priv->level_indentation;
12993 	  rect->width -= (depth - 1) * tree_view->priv->level_indentation;
12994 
12995 	  if (TREE_VIEW_DRAW_EXPANDERS (tree_view))
12996 	    {
12997 	      if (!rtl)
12998 		rect->x += depth * tree_view->priv->expander_size;
12999 	      rect->width -= depth * tree_view->priv->expander_size;
13000 	    }
13001 
13002 	  rect->width = MAX (rect->width, 0);
13003 	}
13004     }
13005 }
13006 
13007 /**
13008  * gtk_tree_view_get_background_area:
13009  * @tree_view: a #GtkTreeView
13010  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
13011  * @column: (allow-none): a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes
13012  * @rect: (out): rectangle to fill with cell background rect
13013  *
13014  * Fills the bounding rectangle in bin_window coordinates for the cell at the
13015  * row specified by @path and the column specified by @column.  If @path is
13016  * %NULL, or points to a node not found in the tree, the @y and @height fields of
13017  * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
13018  * fields will be filled with 0.  The returned rectangle is equivalent to the
13019  * @background_area passed to gtk_cell_renderer_render().  These background
13020  * areas tile to cover the entire bin window.  Contrast with the @cell_area,
13021  * returned by gtk_tree_view_get_cell_area(), which returns only the cell
13022  * itself, excluding surrounding borders and the tree expander area.
13023  *
13024  **/
13025 void
gtk_tree_view_get_background_area(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,GdkRectangle * rect)13026 gtk_tree_view_get_background_area (GtkTreeView        *tree_view,
13027                                    GtkTreePath        *path,
13028                                    GtkTreeViewColumn  *column,
13029                                    GdkRectangle       *rect)
13030 {
13031   GtkRBTree *tree = NULL;
13032   GtkRBNode *node = NULL;
13033 
13034   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13035   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
13036   g_return_if_fail (rect != NULL);
13037 
13038   rect->x = 0;
13039   rect->y = 0;
13040   rect->width = 0;
13041   rect->height = 0;
13042 
13043   if (path)
13044     {
13045       /* Get vertical coords */
13046 
13047       if (!_gtk_tree_view_find_node (tree_view, path, &tree, &node) &&
13048 	  tree == NULL)
13049 	return;
13050 
13051       rect->y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
13052 
13053       rect->height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
13054     }
13055 
13056   if (column)
13057     {
13058       gint x2 = 0;
13059 
13060       gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2);
13061       rect->width = x2 - rect->x;
13062     }
13063 }
13064 
13065 /**
13066  * gtk_tree_view_get_visible_rect:
13067  * @tree_view: a #GtkTreeView
13068  * @visible_rect: (out): rectangle to fill
13069  *
13070  * Fills @visible_rect with the currently-visible region of the
13071  * buffer, in tree coordinates. Convert to bin_window coordinates with
13072  * gtk_tree_view_convert_tree_to_bin_window_coords().
13073  * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
13074  * scrollable area of the tree.
13075  **/
13076 void
gtk_tree_view_get_visible_rect(GtkTreeView * tree_view,GdkRectangle * visible_rect)13077 gtk_tree_view_get_visible_rect (GtkTreeView  *tree_view,
13078                                 GdkRectangle *visible_rect)
13079 {
13080   GtkWidget *widget;
13081 
13082   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13083 
13084   widget = GTK_WIDGET (tree_view);
13085 
13086   if (visible_rect)
13087     {
13088       visible_rect->x = tree_view->priv->hadjustment->value;
13089       visible_rect->y = tree_view->priv->vadjustment->value;
13090       visible_rect->width = widget->allocation.width;
13091       visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
13092     }
13093 }
13094 
13095 /**
13096  * gtk_tree_view_widget_to_tree_coords:
13097  * @tree_view: a #GtkTreeView
13098  * @wx: X coordinate relative to bin_window
13099  * @wy: Y coordinate relative to bin_window
13100  * @tx: return location for tree X coordinate
13101  * @ty: return location for tree Y coordinate
13102  *
13103  * Converts bin_window coordinates to coordinates for the
13104  * tree (the full scrollable area of the tree).
13105  *
13106  * Deprecated: 2.12: Due to historial reasons the name of this function is
13107  * incorrect.  For converting coordinates relative to the widget to
13108  * bin_window coordinates, please see
13109  * gtk_tree_view_convert_widget_to_bin_window_coords().
13110  *
13111  **/
13112 void
gtk_tree_view_widget_to_tree_coords(GtkTreeView * tree_view,gint wx,gint wy,gint * tx,gint * ty)13113 gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view,
13114 				      gint         wx,
13115 				      gint         wy,
13116 				      gint        *tx,
13117 				      gint        *ty)
13118 {
13119   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13120 
13121   if (tx)
13122     *tx = wx + tree_view->priv->hadjustment->value;
13123   if (ty)
13124     *ty = wy + tree_view->priv->dy;
13125 }
13126 
13127 /**
13128  * gtk_tree_view_tree_to_widget_coords:
13129  * @tree_view: a #GtkTreeView
13130  * @tx: tree X coordinate
13131  * @ty: tree Y coordinate
13132  * @wx: return location for X coordinate relative to bin_window
13133  * @wy: return location for Y coordinate relative to bin_window
13134  *
13135  * Converts tree coordinates (coordinates in full scrollable area of the tree)
13136  * to bin_window coordinates.
13137  *
13138  * Deprecated: 2.12: Due to historial reasons the name of this function is
13139  * incorrect.  For converting bin_window coordinates to coordinates relative
13140  * to bin_window, please see
13141  * gtk_tree_view_convert_bin_window_to_widget_coords().
13142  *
13143  **/
13144 void
gtk_tree_view_tree_to_widget_coords(GtkTreeView * tree_view,gint tx,gint ty,gint * wx,gint * wy)13145 gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view,
13146                                      gint         tx,
13147                                      gint         ty,
13148                                      gint        *wx,
13149                                      gint        *wy)
13150 {
13151   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13152 
13153   if (wx)
13154     *wx = tx - tree_view->priv->hadjustment->value;
13155   if (wy)
13156     *wy = ty - tree_view->priv->dy;
13157 }
13158 
13159 
13160 /**
13161  * gtk_tree_view_convert_widget_to_tree_coords:
13162  * @tree_view: a #GtkTreeView
13163  * @wx: X coordinate relative to the widget
13164  * @wy: Y coordinate relative to the widget
13165  * @tx: (out): return location for tree X coordinate
13166  * @ty: (out): return location for tree Y coordinate
13167  *
13168  * Converts widget coordinates to coordinates for the
13169  * tree (the full scrollable area of the tree).
13170  *
13171  * Since: 2.12
13172  **/
13173 void
gtk_tree_view_convert_widget_to_tree_coords(GtkTreeView * tree_view,gint wx,gint wy,gint * tx,gint * ty)13174 gtk_tree_view_convert_widget_to_tree_coords (GtkTreeView *tree_view,
13175                                              gint         wx,
13176                                              gint         wy,
13177                                              gint        *tx,
13178                                              gint        *ty)
13179 {
13180   gint x, y;
13181 
13182   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13183 
13184   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view,
13185 						     wx, wy,
13186 						     &x, &y);
13187   gtk_tree_view_convert_bin_window_to_tree_coords (tree_view,
13188 						   x, y,
13189 						   tx, ty);
13190 }
13191 
13192 /**
13193  * gtk_tree_view_convert_tree_to_widget_coords:
13194  * @tree_view: a #GtkTreeView
13195  * @tx: X coordinate relative to the tree
13196  * @ty: Y coordinate relative to the tree
13197  * @wx: (out): return location for widget X coordinate
13198  * @wy: (out): return location for widget Y coordinate
13199  *
13200  * Converts tree coordinates (coordinates in full scrollable area of the tree)
13201  * to widget coordinates.
13202  *
13203  * Since: 2.12
13204  **/
13205 void
gtk_tree_view_convert_tree_to_widget_coords(GtkTreeView * tree_view,gint tx,gint ty,gint * wx,gint * wy)13206 gtk_tree_view_convert_tree_to_widget_coords (GtkTreeView *tree_view,
13207                                              gint         tx,
13208                                              gint         ty,
13209                                              gint        *wx,
13210                                              gint        *wy)
13211 {
13212   gint x, y;
13213 
13214   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13215 
13216   gtk_tree_view_convert_tree_to_bin_window_coords (tree_view,
13217 						   tx, ty,
13218 						   &x, &y);
13219   gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
13220 						     x, y,
13221 						     wx, wy);
13222 }
13223 
13224 /**
13225  * gtk_tree_view_convert_widget_to_bin_window_coords:
13226  * @tree_view: a #GtkTreeView
13227  * @wx: X coordinate relative to the widget
13228  * @wy: Y coordinate relative to the widget
13229  * @bx: (out): return location for bin_window X coordinate
13230  * @by: (out): return location for bin_window Y coordinate
13231  *
13232  * Converts widget coordinates to coordinates for the bin_window
13233  * (see gtk_tree_view_get_bin_window()).
13234  *
13235  * Since: 2.12
13236  **/
13237 void
gtk_tree_view_convert_widget_to_bin_window_coords(GtkTreeView * tree_view,gint wx,gint wy,gint * bx,gint * by)13238 gtk_tree_view_convert_widget_to_bin_window_coords (GtkTreeView *tree_view,
13239                                                    gint         wx,
13240                                                    gint         wy,
13241                                                    gint        *bx,
13242                                                    gint        *by)
13243 {
13244   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13245 
13246   if (bx)
13247     *bx = wx + tree_view->priv->hadjustment->value;
13248   if (by)
13249     *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
13250 }
13251 
13252 /**
13253  * gtk_tree_view_convert_bin_window_to_widget_coords:
13254  * @tree_view: a #GtkTreeView
13255  * @bx: bin_window X coordinate
13256  * @by: bin_window Y coordinate
13257  * @wx: (out): return location for widget X coordinate
13258  * @wy: (out): return location for widget Y coordinate
13259  *
13260  * Converts bin_window coordinates (see gtk_tree_view_get_bin_window())
13261  * to widget relative coordinates.
13262  *
13263  * Since: 2.12
13264  **/
13265 void
gtk_tree_view_convert_bin_window_to_widget_coords(GtkTreeView * tree_view,gint bx,gint by,gint * wx,gint * wy)13266 gtk_tree_view_convert_bin_window_to_widget_coords (GtkTreeView *tree_view,
13267                                                    gint         bx,
13268                                                    gint         by,
13269                                                    gint        *wx,
13270                                                    gint        *wy)
13271 {
13272   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13273 
13274   if (wx)
13275     *wx = bx - tree_view->priv->hadjustment->value;
13276   if (wy)
13277     *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
13278 }
13279 
13280 /**
13281  * gtk_tree_view_convert_tree_to_bin_window_coords:
13282  * @tree_view: a #GtkTreeView
13283  * @tx: tree X coordinate
13284  * @ty: tree Y coordinate
13285  * @bx: (out): return location for X coordinate relative to bin_window
13286  * @by: (out): return location for Y coordinate relative to bin_window
13287  *
13288  * Converts tree coordinates (coordinates in full scrollable area of the tree)
13289  * to bin_window coordinates.
13290  *
13291  * Since: 2.12
13292  **/
13293 void
gtk_tree_view_convert_tree_to_bin_window_coords(GtkTreeView * tree_view,gint tx,gint ty,gint * bx,gint * by)13294 gtk_tree_view_convert_tree_to_bin_window_coords (GtkTreeView *tree_view,
13295                                                  gint         tx,
13296                                                  gint         ty,
13297                                                  gint        *bx,
13298                                                  gint        *by)
13299 {
13300   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13301 
13302   if (bx)
13303     *bx = tx;
13304   if (by)
13305     *by = ty - tree_view->priv->dy;
13306 }
13307 
13308 /**
13309  * gtk_tree_view_convert_bin_window_to_tree_coords:
13310  * @tree_view: a #GtkTreeView
13311  * @bx: X coordinate relative to bin_window
13312  * @by: Y coordinate relative to bin_window
13313  * @tx: (out): return location for tree X coordinate
13314  * @ty: (out): return location for tree Y coordinate
13315  *
13316  * Converts bin_window coordinates to coordinates for the
13317  * tree (the full scrollable area of the tree).
13318  *
13319  * Since: 2.12
13320  **/
13321 void
gtk_tree_view_convert_bin_window_to_tree_coords(GtkTreeView * tree_view,gint bx,gint by,gint * tx,gint * ty)13322 gtk_tree_view_convert_bin_window_to_tree_coords (GtkTreeView *tree_view,
13323                                                  gint         bx,
13324                                                  gint         by,
13325                                                  gint        *tx,
13326                                                  gint        *ty)
13327 {
13328   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13329 
13330   if (tx)
13331     *tx = bx;
13332   if (ty)
13333     *ty = by + tree_view->priv->dy;
13334 }
13335 
13336 
13337 
13338 /**
13339  * gtk_tree_view_get_visible_range:
13340  * @tree_view: A #GtkTreeView
13341  * @start_path: (out) (allow-none): Return location for start of region,
13342  *              or %NULL.
13343  * @end_path: (out) (allow-none): Return location for end of region, or %NULL.
13344  *
13345  * Sets @start_path and @end_path to be the first and last visible path.
13346  * Note that there may be invisible paths in between.
13347  *
13348  * The paths should be freed with gtk_tree_path_free() after use.
13349  *
13350  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
13351  *
13352  * Since: 2.8
13353  **/
13354 gboolean
gtk_tree_view_get_visible_range(GtkTreeView * tree_view,GtkTreePath ** start_path,GtkTreePath ** end_path)13355 gtk_tree_view_get_visible_range (GtkTreeView  *tree_view,
13356                                  GtkTreePath **start_path,
13357                                  GtkTreePath **end_path)
13358 {
13359   GtkRBTree *tree;
13360   GtkRBNode *node;
13361   gboolean retval;
13362 
13363   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13364 
13365   if (!tree_view->priv->tree)
13366     return FALSE;
13367 
13368   retval = TRUE;
13369 
13370   if (start_path)
13371     {
13372       _gtk_rbtree_find_offset (tree_view->priv->tree,
13373                                TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
13374                                &tree, &node);
13375       if (node)
13376         *start_path = _gtk_tree_view_find_path (tree_view, tree, node);
13377       else
13378         retval = FALSE;
13379     }
13380 
13381   if (end_path)
13382     {
13383       gint y;
13384 
13385       if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
13386         y = tree_view->priv->height - 1;
13387       else
13388         y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
13389 
13390       _gtk_rbtree_find_offset (tree_view->priv->tree, y, &tree, &node);
13391       if (node)
13392         *end_path = _gtk_tree_view_find_path (tree_view, tree, node);
13393       else
13394         retval = FALSE;
13395     }
13396 
13397   return retval;
13398 }
13399 
13400 static void
unset_reorderable(GtkTreeView * tree_view)13401 unset_reorderable (GtkTreeView *tree_view)
13402 {
13403   if (tree_view->priv->reorderable)
13404     {
13405       tree_view->priv->reorderable = FALSE;
13406       g_object_notify (G_OBJECT (tree_view), "reorderable");
13407     }
13408 }
13409 
13410 /**
13411  * gtk_tree_view_enable_model_drag_source:
13412  * @tree_view: a #GtkTreeView
13413  * @start_button_mask: Mask of allowed buttons to start drag
13414  * @targets: (array length=n_targets): the table of targets that the drag will support
13415  * @n_targets: the number of items in @targets
13416  * @actions: the bitmask of possible actions for a drag from this
13417  *    widget
13418  *
13419  * Turns @tree_view into a drag source for automatic DND. Calling this
13420  * method sets #GtkTreeView:reorderable to %FALSE.
13421  **/
13422 void
gtk_tree_view_enable_model_drag_source(GtkTreeView * tree_view,GdkModifierType start_button_mask,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)13423 gtk_tree_view_enable_model_drag_source (GtkTreeView              *tree_view,
13424 					GdkModifierType           start_button_mask,
13425 					const GtkTargetEntry     *targets,
13426 					gint                      n_targets,
13427 					GdkDragAction             actions)
13428 {
13429   TreeViewDragInfo *di;
13430 
13431   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13432 
13433   gtk_drag_source_set (GTK_WIDGET (tree_view),
13434 		       0,
13435 		       targets,
13436 		       n_targets,
13437 		       actions);
13438 
13439   di = ensure_info (tree_view);
13440 
13441   di->start_button_mask = start_button_mask;
13442   di->source_actions = actions;
13443   di->source_set = TRUE;
13444 
13445   unset_reorderable (tree_view);
13446 }
13447 
13448 /**
13449  * gtk_tree_view_enable_model_drag_dest:
13450  * @tree_view: a #GtkTreeView
13451  * @targets: (array length=n_targets): the table of targets that the drag will support
13452  * @n_targets: the number of items in @targets
13453  * @actions: the bitmask of possible actions for a drag from this
13454  *    widget
13455  *
13456  * Turns @tree_view into a drop destination for automatic DND. Calling
13457  * this method sets #GtkTreeView:reorderable to %FALSE.
13458  **/
13459 void
gtk_tree_view_enable_model_drag_dest(GtkTreeView * tree_view,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)13460 gtk_tree_view_enable_model_drag_dest (GtkTreeView              *tree_view,
13461 				      const GtkTargetEntry     *targets,
13462 				      gint                      n_targets,
13463 				      GdkDragAction             actions)
13464 {
13465   TreeViewDragInfo *di;
13466 
13467   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13468 
13469   gtk_drag_dest_set (GTK_WIDGET (tree_view),
13470                      0,
13471                      targets,
13472                      n_targets,
13473                      actions);
13474 
13475   di = ensure_info (tree_view);
13476   di->dest_set = TRUE;
13477 
13478   unset_reorderable (tree_view);
13479 }
13480 
13481 /**
13482  * gtk_tree_view_unset_rows_drag_source:
13483  * @tree_view: a #GtkTreeView
13484  *
13485  * Undoes the effect of
13486  * gtk_tree_view_enable_model_drag_source(). Calling this method sets
13487  * #GtkTreeView:reorderable to %FALSE.
13488  **/
13489 void
gtk_tree_view_unset_rows_drag_source(GtkTreeView * tree_view)13490 gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view)
13491 {
13492   TreeViewDragInfo *di;
13493 
13494   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13495 
13496   di = get_info (tree_view);
13497 
13498   if (di)
13499     {
13500       if (di->source_set)
13501         {
13502           gtk_drag_source_unset (GTK_WIDGET (tree_view));
13503           di->source_set = FALSE;
13504         }
13505 
13506       if (!di->dest_set && !di->source_set)
13507         remove_info (tree_view);
13508     }
13509 
13510   unset_reorderable (tree_view);
13511 }
13512 
13513 /**
13514  * gtk_tree_view_unset_rows_drag_dest:
13515  * @tree_view: a #GtkTreeView
13516  *
13517  * Undoes the effect of
13518  * gtk_tree_view_enable_model_drag_dest(). Calling this method sets
13519  * #GtkTreeView:reorderable to %FALSE.
13520  **/
13521 void
gtk_tree_view_unset_rows_drag_dest(GtkTreeView * tree_view)13522 gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view)
13523 {
13524   TreeViewDragInfo *di;
13525 
13526   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13527 
13528   di = get_info (tree_view);
13529 
13530   if (di)
13531     {
13532       if (di->dest_set)
13533         {
13534           gtk_drag_dest_unset (GTK_WIDGET (tree_view));
13535           di->dest_set = FALSE;
13536         }
13537 
13538       if (!di->dest_set && !di->source_set)
13539         remove_info (tree_view);
13540     }
13541 
13542   unset_reorderable (tree_view);
13543 }
13544 
13545 /**
13546  * gtk_tree_view_set_drag_dest_row:
13547  * @tree_view: a #GtkTreeView
13548  * @path: (allow-none): The path of the row to highlight, or %NULL.
13549  * @pos: Specifies whether to drop before, after or into the row
13550  *
13551  * Sets the row that is highlighted for feedback.
13552  **/
13553 void
gtk_tree_view_set_drag_dest_row(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewDropPosition pos)13554 gtk_tree_view_set_drag_dest_row (GtkTreeView            *tree_view,
13555                                  GtkTreePath            *path,
13556                                  GtkTreeViewDropPosition pos)
13557 {
13558   GtkTreePath *current_dest;
13559 
13560   /* Note; this function is exported to allow a custom DND
13561    * implementation, so it can't touch TreeViewDragInfo
13562    */
13563 
13564   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13565 
13566   current_dest = NULL;
13567 
13568   if (tree_view->priv->drag_dest_row)
13569     {
13570       current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
13571       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
13572     }
13573 
13574   /* special case a drop on an empty model */
13575   tree_view->priv->empty_view_drop = 0;
13576 
13577   if (pos == GTK_TREE_VIEW_DROP_BEFORE && path
13578       && gtk_tree_path_get_depth (path) == 1
13579       && gtk_tree_path_get_indices (path)[0] == 0)
13580     {
13581       gint n_children;
13582 
13583       n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
13584                                                    NULL);
13585 
13586       if (!n_children)
13587         tree_view->priv->empty_view_drop = 1;
13588     }
13589 
13590   tree_view->priv->drag_dest_pos = pos;
13591 
13592   if (path)
13593     {
13594       tree_view->priv->drag_dest_row =
13595         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
13596       gtk_tree_view_queue_draw_path (tree_view, path, NULL);
13597     }
13598   else
13599     tree_view->priv->drag_dest_row = NULL;
13600 
13601   if (current_dest)
13602     {
13603       GtkRBTree *tree, *new_tree;
13604       GtkRBNode *node, *new_node;
13605 
13606       _gtk_tree_view_find_node (tree_view, current_dest, &tree, &node);
13607       _gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
13608 
13609       if (tree && node)
13610 	{
13611 	  _gtk_rbtree_next_full (tree, node, &new_tree, &new_node);
13612 	  if (new_tree && new_node)
13613 	    _gtk_tree_view_queue_draw_node (tree_view, new_tree, new_node, NULL);
13614 
13615 	  _gtk_rbtree_prev_full (tree, node, &new_tree, &new_node);
13616 	  if (new_tree && new_node)
13617 	    _gtk_tree_view_queue_draw_node (tree_view, new_tree, new_node, NULL);
13618 	}
13619       gtk_tree_path_free (current_dest);
13620     }
13621 }
13622 
13623 /**
13624  * gtk_tree_view_get_drag_dest_row:
13625  * @tree_view: a #GtkTreeView
13626  * @path: (out) (allow-none): Return location for the path of the highlighted row, or %NULL.
13627  * @pos: (out) (allow-none): Return location for the drop position, or %NULL
13628  *
13629  * Gets information about the row that is highlighted for feedback.
13630  **/
13631 void
gtk_tree_view_get_drag_dest_row(GtkTreeView * tree_view,GtkTreePath ** path,GtkTreeViewDropPosition * pos)13632 gtk_tree_view_get_drag_dest_row (GtkTreeView              *tree_view,
13633                                  GtkTreePath             **path,
13634                                  GtkTreeViewDropPosition  *pos)
13635 {
13636   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13637 
13638   if (path)
13639     {
13640       if (tree_view->priv->drag_dest_row)
13641         *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
13642       else
13643         {
13644           if (tree_view->priv->empty_view_drop)
13645             *path = gtk_tree_path_new_from_indices (0, -1);
13646           else
13647             *path = NULL;
13648         }
13649     }
13650 
13651   if (pos)
13652     *pos = tree_view->priv->drag_dest_pos;
13653 }
13654 
13655 /**
13656  * gtk_tree_view_get_dest_row_at_pos:
13657  * @tree_view: a #GtkTreeView
13658  * @drag_x: the position to determine the destination row for
13659  * @drag_y: the position to determine the destination row for
13660  * @path: (out) (allow-none): Return location for the path of the highlighted row, or %NULL.
13661  * @pos: (out) (allow-none): Return location for the drop position, or %NULL
13662  *
13663  * Determines the destination row for a given position.  @drag_x and
13664  * @drag_y are expected to be in widget coordinates.  This function is only
13665  * meaningful if @tree_view is realized.  Therefore this function will always
13666  * return %FALSE if @tree_view is not realized or does not have a model.
13667  *
13668  * Return value: whether there is a row at the given position, %TRUE if this
13669  * is indeed the case.
13670  **/
13671 gboolean
gtk_tree_view_get_dest_row_at_pos(GtkTreeView * tree_view,gint drag_x,gint drag_y,GtkTreePath ** path,GtkTreeViewDropPosition * pos)13672 gtk_tree_view_get_dest_row_at_pos (GtkTreeView             *tree_view,
13673                                    gint                     drag_x,
13674                                    gint                     drag_y,
13675                                    GtkTreePath            **path,
13676                                    GtkTreeViewDropPosition *pos)
13677 {
13678   gint cell_y;
13679   gint bin_x, bin_y;
13680   gdouble offset_into_row;
13681   gdouble third;
13682   GdkRectangle cell;
13683   GtkTreeViewColumn *column = NULL;
13684   GtkTreePath *tmp_path = NULL;
13685 
13686   /* Note; this function is exported to allow a custom DND
13687    * implementation, so it can't touch TreeViewDragInfo
13688    */
13689 
13690   g_return_val_if_fail (tree_view != NULL, FALSE);
13691   g_return_val_if_fail (drag_x >= 0, FALSE);
13692   g_return_val_if_fail (drag_y >= 0, FALSE);
13693 
13694   if (path)
13695     *path = NULL;
13696 
13697   if (tree_view->priv->bin_window == NULL)
13698     return FALSE;
13699 
13700   if (tree_view->priv->tree == NULL)
13701     return FALSE;
13702 
13703   /* If in the top third of a row, we drop before that row; if
13704    * in the bottom third, drop after that row; if in the middle,
13705    * and the row has children, drop into the row.
13706    */
13707   gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
13708 						     &bin_x, &bin_y);
13709 
13710   if (!gtk_tree_view_get_path_at_pos (tree_view,
13711 				      bin_x,
13712 				      bin_y,
13713                                       &tmp_path,
13714                                       &column,
13715                                       NULL,
13716                                       &cell_y))
13717     return FALSE;
13718 
13719   gtk_tree_view_get_background_area (tree_view, tmp_path, column,
13720                                      &cell);
13721 
13722   offset_into_row = cell_y;
13723 
13724   if (path)
13725     *path = tmp_path;
13726   else
13727     gtk_tree_path_free (tmp_path);
13728 
13729   tmp_path = NULL;
13730 
13731   third = cell.height / 3.0;
13732 
13733   if (pos)
13734     {
13735       if (offset_into_row < third)
13736         {
13737           *pos = GTK_TREE_VIEW_DROP_BEFORE;
13738         }
13739       else if (offset_into_row < (cell.height / 2.0))
13740         {
13741           *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
13742         }
13743       else if (offset_into_row < third * 2.0)
13744         {
13745           *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
13746         }
13747       else
13748         {
13749           *pos = GTK_TREE_VIEW_DROP_AFTER;
13750         }
13751     }
13752 
13753   return TRUE;
13754 }
13755 
13756 
13757 
13758 /* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */
13759 /**
13760  * gtk_tree_view_create_row_drag_icon:
13761  * @tree_view: a #GtkTreeView
13762  * @path: a #GtkTreePath in @tree_view
13763  *
13764  * Creates a #GdkPixmap representation of the row at @path.
13765  * This image is used for a drag icon.
13766  *
13767  * Return value: (transfer none): a newly-allocated pixmap of the drag icon.
13768  **/
13769 GdkPixmap *
gtk_tree_view_create_row_drag_icon(GtkTreeView * tree_view,GtkTreePath * path)13770 gtk_tree_view_create_row_drag_icon (GtkTreeView  *tree_view,
13771                                     GtkTreePath  *path)
13772 {
13773   GtkTreeIter   iter;
13774   GtkRBTree    *tree;
13775   GtkRBNode    *node;
13776   gint cell_offset;
13777   GList *list;
13778   GdkRectangle background_area;
13779   GdkRectangle expose_area;
13780   GtkWidget *widget;
13781   gint depth;
13782   /* start drawing inside the black outline */
13783   gint x = 1, y = 1;
13784   GdkDrawable *drawable;
13785   gint bin_window_width;
13786   gboolean is_separator = FALSE;
13787   gboolean rtl;
13788   cairo_t *cr;
13789 
13790   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
13791   g_return_val_if_fail (path != NULL, NULL);
13792 
13793   widget = GTK_WIDGET (tree_view);
13794 
13795   if (!gtk_widget_get_realized (widget))
13796     return NULL;
13797 
13798   depth = gtk_tree_path_get_depth (path);
13799 
13800   _gtk_tree_view_find_node (tree_view,
13801                             path,
13802                             &tree,
13803                             &node);
13804 
13805   if (tree == NULL)
13806     return NULL;
13807 
13808   if (!gtk_tree_model_get_iter (tree_view->priv->model,
13809                                 &iter,
13810                                 path))
13811     return NULL;
13812 
13813   is_separator = row_is_separator (tree_view, &iter, NULL);
13814 
13815   cell_offset = x;
13816 
13817   background_area.y = y;
13818   background_area.height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
13819 
13820   bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
13821 
13822   drawable = gdk_pixmap_new (tree_view->priv->bin_window,
13823                              bin_window_width + 2,
13824                              background_area.height + 2,
13825                              -1);
13826 
13827   expose_area.x = 0;
13828   expose_area.y = 0;
13829   expose_area.width = bin_window_width + 2;
13830   expose_area.height = background_area.height + 2;
13831 
13832   cr = gdk_cairo_create (drawable);
13833   gdk_cairo_set_source_color (cr, &widget->style->base [gtk_widget_get_state (widget)]);
13834   cairo_paint (cr);
13835 
13836   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
13837 
13838   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
13839       list;
13840       list = (rtl ? list->prev : list->next))
13841     {
13842       GtkTreeViewColumn *column = list->data;
13843       GdkRectangle cell_area;
13844       gint vertical_separator;
13845 
13846       if (!column->visible)
13847         continue;
13848 
13849       gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter,
13850 					       GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT),
13851 					       node->children?TRUE:FALSE);
13852 
13853       background_area.x = cell_offset;
13854       background_area.width = column->width;
13855 
13856       gtk_widget_style_get (widget,
13857 			    "vertical-separator", &vertical_separator,
13858 			    NULL);
13859 
13860       cell_area = background_area;
13861 
13862       cell_area.y += vertical_separator / 2;
13863       cell_area.height -= vertical_separator;
13864 
13865       if (gtk_tree_view_is_expander_column (tree_view, column))
13866         {
13867 	  if (!rtl)
13868 	    cell_area.x += (depth - 1) * tree_view->priv->level_indentation;
13869 	  cell_area.width -= (depth - 1) * tree_view->priv->level_indentation;
13870 
13871           if (TREE_VIEW_DRAW_EXPANDERS(tree_view))
13872 	    {
13873 	      if (!rtl)
13874 		cell_area.x += depth * tree_view->priv->expander_size;
13875 	      cell_area.width -= depth * tree_view->priv->expander_size;
13876 	    }
13877         }
13878 
13879       if (gtk_tree_view_column_cell_is_visible (column))
13880 	{
13881 	  if (is_separator)
13882 	    gtk_paint_hline (widget->style,
13883 			     drawable,
13884 			     GTK_STATE_NORMAL,
13885 			     &cell_area,
13886 			     widget,
13887 			     NULL,
13888 			     cell_area.x,
13889 			     cell_area.x + cell_area.width,
13890 			     cell_area.y + cell_area.height / 2);
13891 	  else
13892 	    _gtk_tree_view_column_cell_render (column,
13893 					       drawable,
13894 					       &background_area,
13895 					       &cell_area,
13896 					       &expose_area,
13897 					       0);
13898 	}
13899       cell_offset += column->width;
13900     }
13901 
13902   cairo_set_source_rgb (cr, 0, 0, 0);
13903   cairo_rectangle (cr,
13904                    0.5, 0.5,
13905                    bin_window_width + 1,
13906                    background_area.height + 1);
13907   cairo_set_line_width (cr, 1.0);
13908   cairo_stroke (cr);
13909 
13910   cairo_destroy (cr);
13911 
13912   return drawable;
13913 }
13914 
13915 
13916 /**
13917  * gtk_tree_view_set_destroy_count_func:
13918  * @tree_view: A #GtkTreeView
13919  * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
13920  * @data: (allow-none): User data to be passed to @func, or %NULL
13921  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
13922  *
13923  * This function should almost never be used.  It is meant for private use by
13924  * ATK for determining the number of visible children that are removed when the
13925  * user collapses a row, or a row is deleted.
13926  **/
13927 void
gtk_tree_view_set_destroy_count_func(GtkTreeView * tree_view,GtkTreeDestroyCountFunc func,gpointer data,GDestroyNotify destroy)13928 gtk_tree_view_set_destroy_count_func (GtkTreeView             *tree_view,
13929 				      GtkTreeDestroyCountFunc  func,
13930 				      gpointer                 data,
13931 				      GDestroyNotify           destroy)
13932 {
13933   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13934 
13935   if (tree_view->priv->destroy_count_destroy)
13936     tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
13937 
13938   tree_view->priv->destroy_count_func = func;
13939   tree_view->priv->destroy_count_data = data;
13940   tree_view->priv->destroy_count_destroy = destroy;
13941 }
13942 
13943 
13944 /*
13945  * Interactive search
13946  */
13947 
13948 /**
13949  * gtk_tree_view_set_enable_search:
13950  * @tree_view: A #GtkTreeView
13951  * @enable_search: %TRUE, if the user can search interactively
13952  *
13953  * If @enable_search is set, then the user can type in text to search through
13954  * the tree interactively (this is sometimes called "typeahead find").
13955  *
13956  * Note that even if this is %FALSE, the user can still initiate a search
13957  * using the "start-interactive-search" key binding.
13958  */
13959 void
gtk_tree_view_set_enable_search(GtkTreeView * tree_view,gboolean enable_search)13960 gtk_tree_view_set_enable_search (GtkTreeView *tree_view,
13961 				 gboolean     enable_search)
13962 {
13963   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
13964 
13965   enable_search = !!enable_search;
13966 
13967   if (tree_view->priv->enable_search != enable_search)
13968     {
13969        tree_view->priv->enable_search = enable_search;
13970        g_object_notify (G_OBJECT (tree_view), "enable-search");
13971     }
13972 }
13973 
13974 /**
13975  * gtk_tree_view_get_enable_search:
13976  * @tree_view: A #GtkTreeView
13977  *
13978  * Returns whether or not the tree allows to start interactive searching
13979  * by typing in text.
13980  *
13981  * Return value: whether or not to let the user search interactively
13982  */
13983 gboolean
gtk_tree_view_get_enable_search(GtkTreeView * tree_view)13984 gtk_tree_view_get_enable_search (GtkTreeView *tree_view)
13985 {
13986   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
13987 
13988   return tree_view->priv->enable_search;
13989 }
13990 
13991 
13992 /**
13993  * gtk_tree_view_get_search_column:
13994  * @tree_view: A #GtkTreeView
13995  *
13996  * Gets the column searched on by the interactive search code.
13997  *
13998  * Return value: the column the interactive search code searches in.
13999  */
14000 gint
gtk_tree_view_get_search_column(GtkTreeView * tree_view)14001 gtk_tree_view_get_search_column (GtkTreeView *tree_view)
14002 {
14003   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
14004 
14005   return (tree_view->priv->search_column);
14006 }
14007 
14008 /**
14009  * gtk_tree_view_set_search_column:
14010  * @tree_view: A #GtkTreeView
14011  * @column: the column of the model to search in, or -1 to disable searching
14012  *
14013  * Sets @column as the column where the interactive search code should
14014  * search in for the current model.
14015  *
14016  * If the search column is set, users can use the "start-interactive-search"
14017  * key binding to bring up search popup. The enable-search property controls
14018  * whether simply typing text will also start an interactive search.
14019  *
14020  * Note that @column refers to a column of the current model. The search
14021  * column is reset to -1 when the model is changed.
14022  */
14023 void
gtk_tree_view_set_search_column(GtkTreeView * tree_view,gint column)14024 gtk_tree_view_set_search_column (GtkTreeView *tree_view,
14025 				 gint         column)
14026 {
14027   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14028   g_return_if_fail (column >= -1);
14029 
14030   if (tree_view->priv->search_column == column)
14031     return;
14032 
14033   tree_view->priv->search_column = column;
14034   g_object_notify (G_OBJECT (tree_view), "search-column");
14035 }
14036 
14037 /**
14038  * gtk_tree_view_get_search_equal_func:
14039  * @tree_view: A #GtkTreeView
14040  *
14041  * Returns the compare function currently in use.
14042  *
14043  * Return value: the currently used compare function for the search code.
14044  */
14045 
14046 GtkTreeViewSearchEqualFunc
gtk_tree_view_get_search_equal_func(GtkTreeView * tree_view)14047 gtk_tree_view_get_search_equal_func (GtkTreeView *tree_view)
14048 {
14049   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14050 
14051   return tree_view->priv->search_equal_func;
14052 }
14053 
14054 /**
14055  * gtk_tree_view_set_search_equal_func:
14056  * @tree_view: A #GtkTreeView
14057  * @search_equal_func: the compare function to use during the search
14058  * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
14059  * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
14060  *
14061  * Sets the compare function for the interactive search capabilities; note
14062  * that somewhat like strcmp() returning 0 for equality
14063  * #GtkTreeViewSearchEqualFunc returns %FALSE on matches.
14064  **/
14065 void
gtk_tree_view_set_search_equal_func(GtkTreeView * tree_view,GtkTreeViewSearchEqualFunc search_equal_func,gpointer search_user_data,GDestroyNotify search_destroy)14066 gtk_tree_view_set_search_equal_func (GtkTreeView                *tree_view,
14067 				     GtkTreeViewSearchEqualFunc  search_equal_func,
14068 				     gpointer                    search_user_data,
14069 				     GDestroyNotify              search_destroy)
14070 {
14071   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14072   g_return_if_fail (search_equal_func != NULL);
14073 
14074   if (tree_view->priv->search_destroy)
14075     tree_view->priv->search_destroy (tree_view->priv->search_user_data);
14076 
14077   tree_view->priv->search_equal_func = search_equal_func;
14078   tree_view->priv->search_user_data = search_user_data;
14079   tree_view->priv->search_destroy = search_destroy;
14080   if (tree_view->priv->search_equal_func == NULL)
14081     tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func;
14082 }
14083 
14084 /**
14085  * gtk_tree_view_get_search_entry:
14086  * @tree_view: A #GtkTreeView
14087  *
14088  * Returns the #GtkEntry which is currently in use as interactive search
14089  * entry for @tree_view.  In case the built-in entry is being used, %NULL
14090  * will be returned.
14091  *
14092  * Return value: (transfer none): the entry currently in use as search entry.
14093  *
14094  * Since: 2.10
14095  */
14096 GtkEntry *
gtk_tree_view_get_search_entry(GtkTreeView * tree_view)14097 gtk_tree_view_get_search_entry (GtkTreeView *tree_view)
14098 {
14099   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14100 
14101   if (tree_view->priv->search_custom_entry_set)
14102     return GTK_ENTRY (tree_view->priv->search_entry);
14103 
14104   return NULL;
14105 }
14106 
14107 /**
14108  * gtk_tree_view_set_search_entry:
14109  * @tree_view: A #GtkTreeView
14110  * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
14111  *
14112  * Sets the entry which the interactive search code will use for this
14113  * @tree_view.  This is useful when you want to provide a search entry
14114  * in our interface at all time at a fixed position.  Passing %NULL for
14115  * @entry will make the interactive search code use the built-in popup
14116  * entry again.
14117  *
14118  * Since: 2.10
14119  */
14120 void
gtk_tree_view_set_search_entry(GtkTreeView * tree_view,GtkEntry * entry)14121 gtk_tree_view_set_search_entry (GtkTreeView *tree_view,
14122 				GtkEntry    *entry)
14123 {
14124   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14125   g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
14126 
14127   if (tree_view->priv->search_custom_entry_set)
14128     {
14129       if (tree_view->priv->search_entry_changed_id)
14130         {
14131 	  g_signal_handler_disconnect (tree_view->priv->search_entry,
14132 				       tree_view->priv->search_entry_changed_id);
14133 	  tree_view->priv->search_entry_changed_id = 0;
14134 	}
14135       g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
14136 					    G_CALLBACK (gtk_tree_view_search_key_press_event),
14137 					    tree_view);
14138 
14139       g_object_unref (tree_view->priv->search_entry);
14140     }
14141   else if (tree_view->priv->search_window)
14142     {
14143       gtk_widget_destroy (tree_view->priv->search_window);
14144 
14145       tree_view->priv->search_window = NULL;
14146     }
14147 
14148   if (entry)
14149     {
14150       tree_view->priv->search_entry = g_object_ref (entry);
14151       tree_view->priv->search_custom_entry_set = TRUE;
14152 
14153       if (tree_view->priv->search_entry_changed_id == 0)
14154         {
14155           tree_view->priv->search_entry_changed_id =
14156 	    g_signal_connect (tree_view->priv->search_entry, "changed",
14157 			      G_CALLBACK (gtk_tree_view_search_init),
14158 			      tree_view);
14159 	}
14160 
14161         g_signal_connect (tree_view->priv->search_entry, "key-press-event",
14162 		          G_CALLBACK (gtk_tree_view_search_key_press_event),
14163 		          tree_view);
14164 
14165 	gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
14166     }
14167   else
14168     {
14169       tree_view->priv->search_entry = NULL;
14170       tree_view->priv->search_custom_entry_set = FALSE;
14171     }
14172 }
14173 
14174 /**
14175  * gtk_tree_view_set_search_position_func:
14176  * @tree_view: A #GtkTreeView
14177  * @func: (allow-none): the function to use to position the search dialog, or %NULL
14178  *    to use the default search position function
14179  * @data: (allow-none): user data to pass to @func, or %NULL
14180  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
14181  *
14182  * Sets the function to use when positioning the search dialog.
14183  *
14184  * Since: 2.10
14185  **/
14186 void
gtk_tree_view_set_search_position_func(GtkTreeView * tree_view,GtkTreeViewSearchPositionFunc func,gpointer user_data,GDestroyNotify destroy)14187 gtk_tree_view_set_search_position_func (GtkTreeView                   *tree_view,
14188 				        GtkTreeViewSearchPositionFunc  func,
14189 				        gpointer                       user_data,
14190 				        GDestroyNotify                 destroy)
14191 {
14192   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14193 
14194   if (tree_view->priv->search_position_destroy)
14195     tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
14196 
14197   tree_view->priv->search_position_func = func;
14198   tree_view->priv->search_position_user_data = user_data;
14199   tree_view->priv->search_position_destroy = destroy;
14200   if (tree_view->priv->search_position_func == NULL)
14201     tree_view->priv->search_position_func = gtk_tree_view_search_position_func;
14202 }
14203 
14204 /**
14205  * gtk_tree_view_get_search_position_func:
14206  * @tree_view: A #GtkTreeView
14207  *
14208  * Returns the positioning function currently in use.
14209  *
14210  * Return value: the currently used function for positioning the search dialog.
14211  *
14212  * Since: 2.10
14213  */
14214 GtkTreeViewSearchPositionFunc
gtk_tree_view_get_search_position_func(GtkTreeView * tree_view)14215 gtk_tree_view_get_search_position_func (GtkTreeView *tree_view)
14216 {
14217   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
14218 
14219   return tree_view->priv->search_position_func;
14220 }
14221 
14222 
14223 static void
gtk_tree_view_search_dialog_hide(GtkWidget * search_dialog,GtkTreeView * tree_view)14224 gtk_tree_view_search_dialog_hide (GtkWidget   *search_dialog,
14225 				  GtkTreeView *tree_view)
14226 {
14227   if (tree_view->priv->disable_popdown)
14228     return;
14229 
14230   if (tree_view->priv->search_entry_changed_id)
14231     {
14232       g_signal_handler_disconnect (tree_view->priv->search_entry,
14233 				   tree_view->priv->search_entry_changed_id);
14234       tree_view->priv->search_entry_changed_id = 0;
14235     }
14236   if (tree_view->priv->typeselect_flush_timeout)
14237     {
14238       g_source_remove (tree_view->priv->typeselect_flush_timeout);
14239       tree_view->priv->typeselect_flush_timeout = 0;
14240     }
14241 
14242   if (gtk_widget_get_visible (search_dialog))
14243     {
14244       /* send focus-in event */
14245       send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
14246       gtk_widget_hide (search_dialog);
14247       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
14248       send_focus_change (GTK_WIDGET (tree_view), TRUE);
14249     }
14250 }
14251 
14252 static void
gtk_tree_view_search_position_func(GtkTreeView * tree_view,GtkWidget * search_dialog,gpointer user_data)14253 gtk_tree_view_search_position_func (GtkTreeView *tree_view,
14254 				    GtkWidget   *search_dialog,
14255 				    gpointer     user_data)
14256 {
14257   gint x, y;
14258   gint tree_x, tree_y;
14259   gint tree_width, tree_height;
14260   GdkWindow *tree_window = GTK_WIDGET (tree_view)->window;
14261   GdkScreen *screen = gdk_window_get_screen (tree_window);
14262   GtkRequisition requisition;
14263   gint monitor_num;
14264   GdkRectangle monitor;
14265 
14266   monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
14267   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
14268 
14269   gtk_widget_realize (search_dialog);
14270 
14271   gdk_window_get_origin (tree_window, &tree_x, &tree_y);
14272   tree_width = gdk_window_get_width (tree_window);
14273   tree_height = gdk_window_get_height (tree_window);
14274   gtk_widget_size_request (search_dialog, &requisition);
14275 
14276   if (tree_x + tree_width > gdk_screen_get_width (screen))
14277     x = gdk_screen_get_width (screen) - requisition.width;
14278   else if (tree_x + tree_width - requisition.width < 0)
14279     x = 0;
14280   else
14281     x = tree_x + tree_width - requisition.width;
14282 
14283   if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
14284     y = gdk_screen_get_height (screen) - requisition.height;
14285   else if (tree_y + tree_height < 0) /* isn't really possible ... */
14286     y = 0;
14287   else
14288     y = tree_y + tree_height;
14289 
14290   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
14291 }
14292 
14293 static void
gtk_tree_view_search_disable_popdown(GtkEntry * entry,GtkMenu * menu,gpointer data)14294 gtk_tree_view_search_disable_popdown (GtkEntry *entry,
14295 				      GtkMenu  *menu,
14296 				      gpointer  data)
14297 {
14298   GtkTreeView *tree_view = (GtkTreeView *)data;
14299 
14300   tree_view->priv->disable_popdown = 1;
14301   g_signal_connect (menu, "hide",
14302 		    G_CALLBACK (gtk_tree_view_search_enable_popdown), data);
14303 }
14304 
14305 /* Because we're visible but offscreen, we just set a flag in the preedit
14306  * callback.
14307  */
14308 static void
gtk_tree_view_search_preedit_changed(GtkIMContext * im_context,GtkTreeView * tree_view)14309 gtk_tree_view_search_preedit_changed (GtkIMContext *im_context,
14310 				      GtkTreeView  *tree_view)
14311 {
14312   tree_view->priv->imcontext_changed = 1;
14313   if (tree_view->priv->typeselect_flush_timeout)
14314     {
14315       g_source_remove (tree_view->priv->typeselect_flush_timeout);
14316       tree_view->priv->typeselect_flush_timeout =
14317 	gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
14318 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
14319 		       tree_view);
14320     }
14321 
14322 }
14323 
14324 static void
gtk_tree_view_search_activate(GtkEntry * entry,GtkTreeView * tree_view)14325 gtk_tree_view_search_activate (GtkEntry    *entry,
14326 			       GtkTreeView *tree_view)
14327 {
14328   GtkTreePath *path;
14329   GtkRBNode *node;
14330   GtkRBTree *tree;
14331 
14332   gtk_tree_view_search_dialog_hide (tree_view->priv->search_window,
14333 				    tree_view);
14334 
14335   /* If we have a row selected and it's the cursor row, we activate
14336    * the row XXX */
14337   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
14338     {
14339       path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
14340 
14341       _gtk_tree_view_find_node (tree_view, path, &tree, &node);
14342 
14343       if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
14344 	gtk_tree_view_row_activated (tree_view, path, tree_view->priv->focus_column);
14345 
14346       gtk_tree_path_free (path);
14347     }
14348 }
14349 
14350 static gboolean
gtk_tree_view_real_search_enable_popdown(gpointer data)14351 gtk_tree_view_real_search_enable_popdown (gpointer data)
14352 {
14353   GtkTreeView *tree_view = (GtkTreeView *)data;
14354 
14355   tree_view->priv->disable_popdown = 0;
14356 
14357   return FALSE;
14358 }
14359 
14360 static void
gtk_tree_view_search_enable_popdown(GtkWidget * widget,gpointer data)14361 gtk_tree_view_search_enable_popdown (GtkWidget *widget,
14362 				     gpointer   data)
14363 {
14364   gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, gtk_tree_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
14365 }
14366 
14367 static gboolean
gtk_tree_view_search_delete_event(GtkWidget * widget,GdkEventAny * event,GtkTreeView * tree_view)14368 gtk_tree_view_search_delete_event (GtkWidget *widget,
14369 				   GdkEventAny *event,
14370 				   GtkTreeView *tree_view)
14371 {
14372   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
14373 
14374   gtk_tree_view_search_dialog_hide (widget, tree_view);
14375 
14376   return TRUE;
14377 }
14378 
14379 static gboolean
gtk_tree_view_search_button_press_event(GtkWidget * widget,GdkEventButton * event,GtkTreeView * tree_view)14380 gtk_tree_view_search_button_press_event (GtkWidget *widget,
14381 					 GdkEventButton *event,
14382 					 GtkTreeView *tree_view)
14383 {
14384   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
14385 
14386   gtk_tree_view_search_dialog_hide (widget, tree_view);
14387 
14388   if (event->window == tree_view->priv->bin_window)
14389     gtk_tree_view_button_press (GTK_WIDGET (tree_view), event);
14390 
14391   return TRUE;
14392 }
14393 
14394 static gboolean
gtk_tree_view_search_scroll_event(GtkWidget * widget,GdkEventScroll * event,GtkTreeView * tree_view)14395 gtk_tree_view_search_scroll_event (GtkWidget *widget,
14396 				   GdkEventScroll *event,
14397 				   GtkTreeView *tree_view)
14398 {
14399   gboolean retval = FALSE;
14400 
14401   if (event->direction == GDK_SCROLL_UP)
14402     {
14403       gtk_tree_view_search_move (widget, tree_view, TRUE);
14404       retval = TRUE;
14405     }
14406   else if (event->direction == GDK_SCROLL_DOWN)
14407     {
14408       gtk_tree_view_search_move (widget, tree_view, FALSE);
14409       retval = TRUE;
14410     }
14411 
14412   /* renew the flush timeout */
14413   if (retval && tree_view->priv->typeselect_flush_timeout
14414       && !tree_view->priv->search_custom_entry_set)
14415     {
14416       g_source_remove (tree_view->priv->typeselect_flush_timeout);
14417       tree_view->priv->typeselect_flush_timeout =
14418 	gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
14419 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
14420 		       tree_view);
14421     }
14422 
14423   return retval;
14424 }
14425 
14426 static gboolean
gtk_tree_view_search_key_press_event(GtkWidget * widget,GdkEventKey * event,GtkTreeView * tree_view)14427 gtk_tree_view_search_key_press_event (GtkWidget *widget,
14428 				      GdkEventKey *event,
14429 				      GtkTreeView *tree_view)
14430 {
14431   gboolean retval = FALSE;
14432 
14433   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
14434   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
14435 
14436   /* close window and cancel the search */
14437   if (!tree_view->priv->search_custom_entry_set
14438       && (event->keyval == GDK_Escape ||
14439           event->keyval == GDK_Tab ||
14440 	    event->keyval == GDK_KP_Tab ||
14441 	    event->keyval == GDK_ISO_Left_Tab))
14442     {
14443       gtk_tree_view_search_dialog_hide (widget, tree_view);
14444       return TRUE;
14445     }
14446 
14447   /* select previous matching iter */
14448   if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
14449     {
14450       if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
14451         gtk_widget_error_bell (widget);
14452 
14453       retval = TRUE;
14454     }
14455 
14456   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
14457       && (event->keyval == GDK_g || event->keyval == GDK_G))
14458     {
14459       if (!gtk_tree_view_search_move (widget, tree_view, TRUE))
14460         gtk_widget_error_bell (widget);
14461 
14462       retval = TRUE;
14463     }
14464 
14465   /* select next matching iter */
14466   if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
14467     {
14468       if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
14469         gtk_widget_error_bell (widget);
14470 
14471       retval = TRUE;
14472     }
14473 
14474   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
14475       && (event->keyval == GDK_g || event->keyval == GDK_G))
14476     {
14477       if (!gtk_tree_view_search_move (widget, tree_view, FALSE))
14478         gtk_widget_error_bell (widget);
14479 
14480       retval = TRUE;
14481     }
14482 
14483   /* renew the flush timeout */
14484   if (retval && tree_view->priv->typeselect_flush_timeout
14485       && !tree_view->priv->search_custom_entry_set)
14486     {
14487       g_source_remove (tree_view->priv->typeselect_flush_timeout);
14488       tree_view->priv->typeselect_flush_timeout =
14489 	gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
14490 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
14491 		       tree_view);
14492     }
14493 
14494   return retval;
14495 }
14496 
14497 /*  this function returns FALSE if there is a search string but
14498  *  nothing was found, and TRUE otherwise.
14499  */
14500 static gboolean
gtk_tree_view_search_move(GtkWidget * window,GtkTreeView * tree_view,gboolean up)14501 gtk_tree_view_search_move (GtkWidget   *window,
14502 			   GtkTreeView *tree_view,
14503 			   gboolean     up)
14504 {
14505   gboolean ret;
14506   gint len;
14507   gint count = 0;
14508   const gchar *text;
14509   GtkTreeIter iter;
14510   GtkTreeModel *model;
14511   GtkTreeSelection *selection;
14512 
14513   text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
14514 
14515   g_return_val_if_fail (text != NULL, FALSE);
14516 
14517   len = strlen (text);
14518 
14519   if (up && tree_view->priv->selected_iter == 1)
14520     return strlen (text) < 1;
14521 
14522   len = strlen (text);
14523 
14524   if (len < 1)
14525     return TRUE;
14526 
14527   model = gtk_tree_view_get_model (tree_view);
14528   selection = gtk_tree_view_get_selection (tree_view);
14529 
14530   /* search */
14531   gtk_tree_selection_unselect_all (selection);
14532   if (!gtk_tree_model_get_iter_first (model, &iter))
14533     return TRUE;
14534 
14535   ret = gtk_tree_view_search_iter (model, selection, &iter, text,
14536 				   &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
14537 
14538   if (ret)
14539     {
14540       /* found */
14541       tree_view->priv->selected_iter += up?(-1):(1);
14542       return TRUE;
14543     }
14544   else
14545     {
14546       /* return to old iter */
14547       count = 0;
14548       gtk_tree_model_get_iter_first (model, &iter);
14549       gtk_tree_view_search_iter (model, selection,
14550 				 &iter, text,
14551 				 &count, tree_view->priv->selected_iter);
14552       return FALSE;
14553     }
14554 }
14555 
14556 static gboolean
gtk_tree_view_search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)14557 gtk_tree_view_search_equal_func (GtkTreeModel *model,
14558 				 gint          column,
14559 				 const gchar  *key,
14560 				 GtkTreeIter  *iter,
14561 				 gpointer      search_data)
14562 {
14563   gboolean retval = TRUE;
14564   const gchar *str;
14565   gchar *normalized_string;
14566   gchar *normalized_key;
14567   gchar *case_normalized_string = NULL;
14568   gchar *case_normalized_key = NULL;
14569   GValue value = {0,};
14570   GValue transformed = {0,};
14571 
14572   gtk_tree_model_get_value (model, iter, column, &value);
14573 
14574   g_value_init (&transformed, G_TYPE_STRING);
14575 
14576   if (!g_value_transform (&value, &transformed))
14577     {
14578       g_value_unset (&value);
14579       return TRUE;
14580     }
14581 
14582   g_value_unset (&value);
14583 
14584   str = g_value_get_string (&transformed);
14585   if (!str)
14586     {
14587       g_value_unset (&transformed);
14588       return TRUE;
14589     }
14590 
14591   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
14592   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
14593 
14594   if (normalized_string && normalized_key)
14595     {
14596       case_normalized_string = g_utf8_casefold (normalized_string, -1);
14597       case_normalized_key = g_utf8_casefold (normalized_key, -1);
14598 
14599       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
14600         retval = FALSE;
14601     }
14602 
14603   g_value_unset (&transformed);
14604   g_free (normalized_key);
14605   g_free (normalized_string);
14606   g_free (case_normalized_key);
14607   g_free (case_normalized_string);
14608 
14609   return retval;
14610 }
14611 
14612 static gboolean
gtk_tree_view_search_iter(GtkTreeModel * model,GtkTreeSelection * selection,GtkTreeIter * iter,const gchar * text,gint * count,gint n)14613 gtk_tree_view_search_iter (GtkTreeModel     *model,
14614 			   GtkTreeSelection *selection,
14615 			   GtkTreeIter      *iter,
14616 			   const gchar      *text,
14617 			   gint             *count,
14618 			   gint              n)
14619 {
14620   GtkRBTree *tree = NULL;
14621   GtkRBNode *node = NULL;
14622   GtkTreePath *path;
14623 
14624   GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
14625 
14626   path = gtk_tree_model_get_path (model, iter);
14627   _gtk_tree_view_find_node (tree_view, path, &tree, &node);
14628 
14629   do
14630     {
14631       if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
14632         {
14633           (*count)++;
14634           if (*count == n)
14635             {
14636               gtk_tree_view_scroll_to_cell (tree_view, path, NULL,
14637 					    TRUE, 0.5, 0.0);
14638               gtk_tree_selection_select_iter (selection, iter);
14639               gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
14640 
14641 	      if (path)
14642 		gtk_tree_path_free (path);
14643 
14644               return TRUE;
14645             }
14646         }
14647 
14648       if (node->children)
14649 	{
14650 	  gboolean has_child;
14651 	  GtkTreeIter tmp;
14652 
14653 	  tree = node->children;
14654 	  node = tree->root;
14655 
14656 	  while (node->left != tree->nil)
14657 	    node = node->left;
14658 
14659 	  tmp = *iter;
14660 	  has_child = gtk_tree_model_iter_children (model, iter, &tmp);
14661 	  gtk_tree_path_down (path);
14662 
14663 	  /* sanity check */
14664 	  TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
14665 	}
14666       else
14667 	{
14668 	  gboolean done = FALSE;
14669 
14670 	  do
14671 	    {
14672 	      node = _gtk_rbtree_next (tree, node);
14673 
14674 	      if (node)
14675 		{
14676 		  gboolean has_next;
14677 
14678 		  has_next = gtk_tree_model_iter_next (model, iter);
14679 
14680 		  done = TRUE;
14681 		  gtk_tree_path_next (path);
14682 
14683 		  /* sanity check */
14684 		  TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
14685 		}
14686 	      else
14687 		{
14688 		  gboolean has_parent;
14689 		  GtkTreeIter tmp_iter = *iter;
14690 
14691 		  node = tree->parent_node;
14692 		  tree = tree->parent_tree;
14693 
14694 		  if (!tree)
14695 		    {
14696 		      if (path)
14697 			gtk_tree_path_free (path);
14698 
14699 		      /* we've run out of tree, done with this func */
14700 		      return FALSE;
14701 		    }
14702 
14703 		  has_parent = gtk_tree_model_iter_parent (model,
14704 							   iter,
14705 							   &tmp_iter);
14706 		  gtk_tree_path_up (path);
14707 
14708 		  /* sanity check */
14709 		  TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
14710 		}
14711 	    }
14712 	  while (!done);
14713 	}
14714     }
14715   while (1);
14716 
14717   return FALSE;
14718 }
14719 
14720 static void
gtk_tree_view_search_init(GtkWidget * entry,GtkTreeView * tree_view)14721 gtk_tree_view_search_init (GtkWidget   *entry,
14722 			   GtkTreeView *tree_view)
14723 {
14724   gint ret;
14725   gint count = 0;
14726   const gchar *text;
14727   GtkTreeIter iter;
14728   GtkTreeModel *model;
14729   GtkTreeSelection *selection;
14730 
14731   g_return_if_fail (GTK_IS_ENTRY (entry));
14732   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
14733 
14734   text = gtk_entry_get_text (GTK_ENTRY (entry));
14735 
14736   model = gtk_tree_view_get_model (tree_view);
14737   selection = gtk_tree_view_get_selection (tree_view);
14738 
14739   /* search */
14740   gtk_tree_selection_unselect_all (selection);
14741   if (tree_view->priv->typeselect_flush_timeout
14742       && !tree_view->priv->search_custom_entry_set)
14743     {
14744       g_source_remove (tree_view->priv->typeselect_flush_timeout);
14745       tree_view->priv->typeselect_flush_timeout =
14746 	gdk_threads_add_timeout (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT,
14747 		       (GSourceFunc) gtk_tree_view_search_entry_flush_timeout,
14748 		       tree_view);
14749     }
14750 
14751   if (*text == '\0')
14752     return;
14753 
14754   if (!gtk_tree_model_get_iter_first (model, &iter))
14755     return;
14756 
14757   ret = gtk_tree_view_search_iter (model, selection,
14758 				   &iter, text,
14759 				   &count, 1);
14760 
14761   if (ret)
14762     tree_view->priv->selected_iter = 1;
14763 }
14764 
14765 static void
gtk_tree_view_remove_widget(GtkCellEditable * cell_editable,GtkTreeView * tree_view)14766 gtk_tree_view_remove_widget (GtkCellEditable *cell_editable,
14767 			     GtkTreeView     *tree_view)
14768 {
14769   if (tree_view->priv->edited_column == NULL)
14770     return;
14771 
14772   _gtk_tree_view_column_stop_editing (tree_view->priv->edited_column);
14773   tree_view->priv->edited_column = NULL;
14774 
14775   if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
14776     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
14777 
14778   g_signal_handlers_disconnect_by_func (cell_editable,
14779 					gtk_tree_view_remove_widget,
14780 					tree_view);
14781 
14782   gtk_container_remove (GTK_CONTAINER (tree_view),
14783 			GTK_WIDGET (cell_editable));
14784 
14785   /* FIXME should only redraw a single node */
14786   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
14787 }
14788 
14789 static gboolean
gtk_tree_view_start_editing(GtkTreeView * tree_view,GtkTreePath * cursor_path)14790 gtk_tree_view_start_editing (GtkTreeView *tree_view,
14791 			     GtkTreePath *cursor_path)
14792 {
14793   GtkTreeIter iter;
14794   GdkRectangle background_area;
14795   GdkRectangle cell_area;
14796   GtkCellEditable *editable_widget = NULL;
14797   gchar *path_string;
14798   guint flags = 0; /* can be 0, as the flags are primarily for rendering */
14799   gint retval = FALSE;
14800   GtkRBTree *cursor_tree;
14801   GtkRBNode *cursor_node;
14802 
14803   g_assert (tree_view->priv->focus_column);
14804 
14805   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
14806     return FALSE;
14807 
14808   if (_gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node) ||
14809       cursor_node == NULL)
14810     return FALSE;
14811 
14812   path_string = gtk_tree_path_to_string (cursor_path);
14813   gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
14814 
14815   validate_row (tree_view, cursor_tree, cursor_node, &iter, cursor_path);
14816 
14817   gtk_tree_view_column_cell_set_cell_data (tree_view->priv->focus_column,
14818 					   tree_view->priv->model,
14819 					   &iter,
14820 					   GTK_RBNODE_FLAG_SET (cursor_node, GTK_RBNODE_IS_PARENT),
14821 					   cursor_node->children?TRUE:FALSE);
14822   gtk_tree_view_get_background_area (tree_view,
14823 				     cursor_path,
14824 				     tree_view->priv->focus_column,
14825 				     &background_area);
14826   gtk_tree_view_get_cell_area (tree_view,
14827 			       cursor_path,
14828 			       tree_view->priv->focus_column,
14829 			       &cell_area);
14830 
14831   if (_gtk_tree_view_column_cell_event (tree_view->priv->focus_column,
14832 					&editable_widget,
14833 					NULL,
14834 					path_string,
14835 					&background_area,
14836 					&cell_area,
14837 					flags))
14838     {
14839       retval = TRUE;
14840       if (editable_widget != NULL)
14841 	{
14842 	  gint left, right;
14843 	  GdkRectangle area;
14844 	  GtkCellRenderer *cell;
14845 
14846 	  area = cell_area;
14847 	  cell = _gtk_tree_view_column_get_edited_cell (tree_view->priv->focus_column);
14848 
14849 	  _gtk_tree_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
14850 
14851 	  area.x += left;
14852 	  area.width -= right + left;
14853 
14854 	  gtk_tree_view_real_start_editing (tree_view,
14855 					    tree_view->priv->focus_column,
14856 					    cursor_path,
14857 					    editable_widget,
14858 					    &area,
14859 					    NULL,
14860 					    flags);
14861 	}
14862 
14863     }
14864   g_free (path_string);
14865   return retval;
14866 }
14867 
14868 static void
gtk_tree_view_real_start_editing(GtkTreeView * tree_view,GtkTreeViewColumn * column,GtkTreePath * path,GtkCellEditable * cell_editable,GdkRectangle * cell_area,GdkEvent * event,guint flags)14869 gtk_tree_view_real_start_editing (GtkTreeView       *tree_view,
14870 				  GtkTreeViewColumn *column,
14871 				  GtkTreePath       *path,
14872 				  GtkCellEditable   *cell_editable,
14873 				  GdkRectangle      *cell_area,
14874 				  GdkEvent          *event,
14875 				  guint              flags)
14876 {
14877   gint pre_val = tree_view->priv->vadjustment->value;
14878   GtkRequisition requisition;
14879 
14880   tree_view->priv->edited_column = column;
14881   _gtk_tree_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
14882 
14883   gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE);
14884   cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
14885 
14886   gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
14887 
14888   GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
14889 
14890   if (requisition.height < cell_area->height)
14891     {
14892       gint diff = cell_area->height - requisition.height;
14893       gtk_tree_view_put (tree_view,
14894 			 GTK_WIDGET (cell_editable),
14895 			 cell_area->x, cell_area->y + diff/2,
14896 			 cell_area->width, requisition.height);
14897     }
14898   else
14899     {
14900       gtk_tree_view_put (tree_view,
14901 			 GTK_WIDGET (cell_editable),
14902 			 cell_area->x, cell_area->y,
14903 			 cell_area->width, cell_area->height);
14904     }
14905 
14906   gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
14907 				   (GdkEvent *)event);
14908 
14909   gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
14910   g_signal_connect (cell_editable, "remove-widget",
14911 		    G_CALLBACK (gtk_tree_view_remove_widget), tree_view);
14912 }
14913 
14914 static void
gtk_tree_view_stop_editing(GtkTreeView * tree_view,gboolean cancel_editing)14915 gtk_tree_view_stop_editing (GtkTreeView *tree_view,
14916 			    gboolean     cancel_editing)
14917 {
14918   GtkTreeViewColumn *column;
14919   GtkCellRenderer *cell;
14920 
14921   if (tree_view->priv->edited_column == NULL)
14922     return;
14923 
14924   /*
14925    * This is very evil. We need to do this, because
14926    * gtk_cell_editable_editing_done may trigger gtk_tree_view_row_changed
14927    * later on. If gtk_tree_view_row_changed notices
14928    * tree_view->priv->edited_column != NULL, it'll call
14929    * gtk_tree_view_stop_editing again. Bad things will happen then.
14930    *
14931    * Please read that again if you intend to modify anything here.
14932    */
14933 
14934   column = tree_view->priv->edited_column;
14935   tree_view->priv->edited_column = NULL;
14936 
14937   cell = _gtk_tree_view_column_get_edited_cell (column);
14938   gtk_cell_renderer_stop_editing (cell, cancel_editing);
14939 
14940   if (!cancel_editing)
14941     gtk_cell_editable_editing_done (column->editable_widget);
14942 
14943   tree_view->priv->edited_column = column;
14944 
14945   gtk_cell_editable_remove_widget (column->editable_widget);
14946 }
14947 
14948 
14949 /**
14950  * gtk_tree_view_set_hover_selection:
14951  * @tree_view: a #GtkTreeView
14952  * @hover: %TRUE to enable hover selection mode
14953  *
14954  * Enables of disables the hover selection mode of @tree_view.
14955  * Hover selection makes the selected row follow the pointer.
14956  * Currently, this works only for the selection modes
14957  * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE.
14958  *
14959  * Since: 2.6
14960  **/
14961 void
gtk_tree_view_set_hover_selection(GtkTreeView * tree_view,gboolean hover)14962 gtk_tree_view_set_hover_selection (GtkTreeView *tree_view,
14963 				   gboolean     hover)
14964 {
14965   hover = hover != FALSE;
14966 
14967   if (hover != tree_view->priv->hover_selection)
14968     {
14969       tree_view->priv->hover_selection = hover;
14970 
14971       g_object_notify (G_OBJECT (tree_view), "hover-selection");
14972     }
14973 }
14974 
14975 /**
14976  * gtk_tree_view_get_hover_selection:
14977  * @tree_view: a #GtkTreeView
14978  *
14979  * Returns whether hover selection mode is turned on for @tree_view.
14980  *
14981  * Return value: %TRUE if @tree_view is in hover selection mode
14982  *
14983  * Since: 2.6
14984  **/
14985 gboolean
gtk_tree_view_get_hover_selection(GtkTreeView * tree_view)14986 gtk_tree_view_get_hover_selection (GtkTreeView *tree_view)
14987 {
14988   return tree_view->priv->hover_selection;
14989 }
14990 
14991 /**
14992  * gtk_tree_view_set_hover_expand:
14993  * @tree_view: a #GtkTreeView
14994  * @expand: %TRUE to enable hover selection mode
14995  *
14996  * Enables of disables the hover expansion mode of @tree_view.
14997  * Hover expansion makes rows expand or collapse if the pointer
14998  * moves over them.
14999  *
15000  * Since: 2.6
15001  **/
15002 void
gtk_tree_view_set_hover_expand(GtkTreeView * tree_view,gboolean expand)15003 gtk_tree_view_set_hover_expand (GtkTreeView *tree_view,
15004 				gboolean     expand)
15005 {
15006   expand = expand != FALSE;
15007 
15008   if (expand != tree_view->priv->hover_expand)
15009     {
15010       tree_view->priv->hover_expand = expand;
15011 
15012       g_object_notify (G_OBJECT (tree_view), "hover-expand");
15013     }
15014 }
15015 
15016 /**
15017  * gtk_tree_view_get_hover_expand:
15018  * @tree_view: a #GtkTreeView
15019  *
15020  * Returns whether hover expansion mode is turned on for @tree_view.
15021  *
15022  * Return value: %TRUE if @tree_view is in hover expansion mode
15023  *
15024  * Since: 2.6
15025  **/
15026 gboolean
gtk_tree_view_get_hover_expand(GtkTreeView * tree_view)15027 gtk_tree_view_get_hover_expand (GtkTreeView *tree_view)
15028 {
15029   return tree_view->priv->hover_expand;
15030 }
15031 
15032 /**
15033  * gtk_tree_view_set_rubber_banding:
15034  * @tree_view: a #GtkTreeView
15035  * @enable: %TRUE to enable rubber banding
15036  *
15037  * Enables or disables rubber banding in @tree_view.  If the selection mode
15038  * is #GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select
15039  * multiple rows by dragging the mouse.
15040  *
15041  * Since: 2.10
15042  **/
15043 void
gtk_tree_view_set_rubber_banding(GtkTreeView * tree_view,gboolean enable)15044 gtk_tree_view_set_rubber_banding (GtkTreeView *tree_view,
15045 				  gboolean     enable)
15046 {
15047   enable = enable != FALSE;
15048 
15049   if (enable != tree_view->priv->rubber_banding_enable)
15050     {
15051       tree_view->priv->rubber_banding_enable = enable;
15052 
15053       g_object_notify (G_OBJECT (tree_view), "rubber-banding");
15054     }
15055 }
15056 
15057 /**
15058  * gtk_tree_view_get_rubber_banding:
15059  * @tree_view: a #GtkTreeView
15060  *
15061  * Returns whether rubber banding is turned on for @tree_view.  If the
15062  * selection mode is #GTK_SELECTION_MULTIPLE, rubber banding will allow the
15063  * user to select multiple rows by dragging the mouse.
15064  *
15065  * Return value: %TRUE if rubber banding in @tree_view is enabled.
15066  *
15067  * Since: 2.10
15068  **/
15069 gboolean
gtk_tree_view_get_rubber_banding(GtkTreeView * tree_view)15070 gtk_tree_view_get_rubber_banding (GtkTreeView *tree_view)
15071 {
15072   return tree_view->priv->rubber_banding_enable;
15073 }
15074 
15075 /**
15076  * gtk_tree_view_is_rubber_banding_active:
15077  * @tree_view: a #GtkTreeView
15078  *
15079  * Returns whether a rubber banding operation is currently being done
15080  * in @tree_view.
15081  *
15082  * Return value: %TRUE if a rubber banding operation is currently being
15083  * done in @tree_view.
15084  *
15085  * Since: 2.12
15086  **/
15087 gboolean
gtk_tree_view_is_rubber_banding_active(GtkTreeView * tree_view)15088 gtk_tree_view_is_rubber_banding_active (GtkTreeView *tree_view)
15089 {
15090   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15091 
15092   if (tree_view->priv->rubber_banding_enable
15093       && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
15094     return TRUE;
15095 
15096   return FALSE;
15097 }
15098 
15099 /**
15100  * gtk_tree_view_get_row_separator_func:
15101  * @tree_view: a #GtkTreeView
15102  *
15103  * Returns the current row separator function.
15104  *
15105  * Return value: the current row separator function.
15106  *
15107  * Since: 2.6
15108  **/
15109 GtkTreeViewRowSeparatorFunc
gtk_tree_view_get_row_separator_func(GtkTreeView * tree_view)15110 gtk_tree_view_get_row_separator_func (GtkTreeView *tree_view)
15111 {
15112   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
15113 
15114   return tree_view->priv->row_separator_func;
15115 }
15116 
15117 /**
15118  * gtk_tree_view_set_row_separator_func:
15119  * @tree_view: a #GtkTreeView
15120  * @func: a #GtkTreeViewRowSeparatorFunc
15121  * @data: (allow-none): user data to pass to @func, or %NULL
15122  * @destroy: (allow-none): destroy notifier for @data, or %NULL
15123  *
15124  * Sets the row separator function, which is used to determine
15125  * whether a row should be drawn as a separator. If the row separator
15126  * function is %NULL, no separators are drawn. This is the default value.
15127  *
15128  * Since: 2.6
15129  **/
15130 void
gtk_tree_view_set_row_separator_func(GtkTreeView * tree_view,GtkTreeViewRowSeparatorFunc func,gpointer data,GDestroyNotify destroy)15131 gtk_tree_view_set_row_separator_func (GtkTreeView                 *tree_view,
15132 				      GtkTreeViewRowSeparatorFunc  func,
15133 				      gpointer                     data,
15134 				      GDestroyNotify               destroy)
15135 {
15136   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15137 
15138   if (tree_view->priv->row_separator_destroy)
15139     tree_view->priv->row_separator_destroy (tree_view->priv->row_separator_data);
15140 
15141   tree_view->priv->row_separator_func = func;
15142   tree_view->priv->row_separator_data = data;
15143   tree_view->priv->row_separator_destroy = destroy;
15144 
15145   /* Have the tree recalculate heights */
15146   _gtk_rbtree_mark_invalid (tree_view->priv->tree);
15147   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
15148 }
15149 
15150 
15151 static void
gtk_tree_view_grab_notify(GtkWidget * widget,gboolean was_grabbed)15152 gtk_tree_view_grab_notify (GtkWidget *widget,
15153 			   gboolean   was_grabbed)
15154 {
15155   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
15156 
15157   tree_view->priv->in_grab = !was_grabbed;
15158 
15159   if (!was_grabbed)
15160     {
15161       tree_view->priv->pressed_button = -1;
15162 
15163       if (tree_view->priv->rubber_band_status)
15164 	gtk_tree_view_stop_rubber_band (tree_view);
15165     }
15166 }
15167 
15168 static void
gtk_tree_view_state_changed(GtkWidget * widget,GtkStateType previous_state)15169 gtk_tree_view_state_changed (GtkWidget      *widget,
15170 		 	     GtkStateType    previous_state)
15171 {
15172   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
15173 
15174   if (gtk_widget_get_realized (widget))
15175     {
15176       gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
15177       gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
15178     }
15179 
15180   gtk_widget_queue_draw (widget);
15181 }
15182 
15183 /**
15184  * gtk_tree_view_get_grid_lines:
15185  * @tree_view: a #GtkTreeView
15186  *
15187  * Returns which grid lines are enabled in @tree_view.
15188  *
15189  * Return value: a #GtkTreeViewGridLines value indicating which grid lines
15190  * are enabled.
15191  *
15192  * Since: 2.10
15193  */
15194 GtkTreeViewGridLines
gtk_tree_view_get_grid_lines(GtkTreeView * tree_view)15195 gtk_tree_view_get_grid_lines (GtkTreeView *tree_view)
15196 {
15197   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
15198 
15199   return tree_view->priv->grid_lines;
15200 }
15201 
15202 /**
15203  * gtk_tree_view_set_grid_lines:
15204  * @tree_view: a #GtkTreeView
15205  * @grid_lines: a #GtkTreeViewGridLines value indicating which grid lines to
15206  * enable.
15207  *
15208  * Sets which grid lines to draw in @tree_view.
15209  *
15210  * Since: 2.10
15211  */
15212 void
gtk_tree_view_set_grid_lines(GtkTreeView * tree_view,GtkTreeViewGridLines grid_lines)15213 gtk_tree_view_set_grid_lines (GtkTreeView           *tree_view,
15214 			      GtkTreeViewGridLines   grid_lines)
15215 {
15216   GtkTreeViewPrivate *priv;
15217   GtkWidget *widget;
15218   GtkTreeViewGridLines old_grid_lines;
15219 
15220   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15221 
15222   priv = tree_view->priv;
15223   widget = GTK_WIDGET (tree_view);
15224 
15225   old_grid_lines = priv->grid_lines;
15226   priv->grid_lines = grid_lines;
15227 
15228   if (gtk_widget_get_realized (widget))
15229     {
15230       if (grid_lines == GTK_TREE_VIEW_GRID_LINES_NONE &&
15231 	  priv->grid_line_width)
15232 	{
15233 	  priv->grid_line_width = 0;
15234 	}
15235 
15236       if (grid_lines != GTK_TREE_VIEW_GRID_LINES_NONE &&
15237 	  !priv->grid_line_width)
15238 	{
15239 	  gint8 *dash_list;
15240 
15241 	  gtk_widget_style_get (widget,
15242 				"grid-line-width", &priv->grid_line_width,
15243 				"grid-line-pattern", (gchar *)&dash_list,
15244 				NULL);
15245 
15246           if (dash_list)
15247             {
15248               priv->grid_line_dashes[0] = dash_list[0];
15249               if (dash_list[0])
15250                 priv->grid_line_dashes[1] = dash_list[1];
15251 
15252               g_free (dash_list);
15253             }
15254           else
15255             {
15256               priv->grid_line_dashes[0] = 1;
15257               priv->grid_line_dashes[1] = 1;
15258             }
15259 	}
15260     }
15261 
15262   if (old_grid_lines != grid_lines)
15263     {
15264       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15265 
15266       g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
15267     }
15268 }
15269 
15270 /**
15271  * gtk_tree_view_get_enable_tree_lines:
15272  * @tree_view: a #GtkTreeView.
15273  *
15274  * Returns whether or not tree lines are drawn in @tree_view.
15275  *
15276  * Return value: %TRUE if tree lines are drawn in @tree_view, %FALSE
15277  * otherwise.
15278  *
15279  * Since: 2.10
15280  */
15281 gboolean
gtk_tree_view_get_enable_tree_lines(GtkTreeView * tree_view)15282 gtk_tree_view_get_enable_tree_lines (GtkTreeView *tree_view)
15283 {
15284   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15285 
15286   return tree_view->priv->tree_lines_enabled;
15287 }
15288 
15289 /**
15290  * gtk_tree_view_set_enable_tree_lines:
15291  * @tree_view: a #GtkTreeView
15292  * @enabled: %TRUE to enable tree line drawing, %FALSE otherwise.
15293  *
15294  * Sets whether to draw lines interconnecting the expanders in @tree_view.
15295  * This does not have any visible effects for lists.
15296  *
15297  * Since: 2.10
15298  */
15299 void
gtk_tree_view_set_enable_tree_lines(GtkTreeView * tree_view,gboolean enabled)15300 gtk_tree_view_set_enable_tree_lines (GtkTreeView *tree_view,
15301 				     gboolean     enabled)
15302 {
15303   GtkTreeViewPrivate *priv;
15304   GtkWidget *widget;
15305   gboolean was_enabled;
15306 
15307   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15308 
15309   enabled = enabled != FALSE;
15310 
15311   priv = tree_view->priv;
15312   widget = GTK_WIDGET (tree_view);
15313 
15314   was_enabled = priv->tree_lines_enabled;
15315 
15316   priv->tree_lines_enabled = enabled;
15317 
15318   if (gtk_widget_get_realized (widget))
15319     {
15320       if (!enabled && priv->tree_line_width)
15321 	{
15322           priv->tree_line_width = 0;
15323 	}
15324 
15325       if (enabled && !priv->tree_line_width)
15326 	{
15327 	  gint8 *dash_list;
15328 	  gtk_widget_style_get (widget,
15329 				"tree-line-width", &priv->tree_line_width,
15330 				"tree-line-pattern", (gchar *)&dash_list,
15331 				NULL);
15332 
15333           if (dash_list)
15334             {
15335               priv->tree_line_dashes[0] = dash_list[0];
15336               if (dash_list[0])
15337                 priv->tree_line_dashes[1] = dash_list[1];
15338 
15339               g_free (dash_list);
15340             }
15341           else
15342             {
15343               priv->tree_line_dashes[0] = 1;
15344               priv->tree_line_dashes[1] = 1;
15345             }
15346 	}
15347     }
15348 
15349   if (was_enabled != enabled)
15350     {
15351       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15352 
15353       g_object_notify (G_OBJECT (tree_view), "enable-tree-lines");
15354     }
15355 }
15356 
15357 
15358 /**
15359  * gtk_tree_view_set_show_expanders:
15360  * @tree_view: a #GtkTreeView
15361  * @enabled: %TRUE to enable expander drawing, %FALSE otherwise.
15362  *
15363  * Sets whether to draw and enable expanders and indent child rows in
15364  * @tree_view.  When disabled there will be no expanders visible in trees
15365  * and there will be no way to expand and collapse rows by default.  Also
15366  * note that hiding the expanders will disable the default indentation.  You
15367  * can set a custom indentation in this case using
15368  * gtk_tree_view_set_level_indentation().
15369  * This does not have any visible effects for lists.
15370  *
15371  * Since: 2.12
15372  */
15373 void
gtk_tree_view_set_show_expanders(GtkTreeView * tree_view,gboolean enabled)15374 gtk_tree_view_set_show_expanders (GtkTreeView *tree_view,
15375 				  gboolean     enabled)
15376 {
15377   gboolean was_enabled;
15378 
15379   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15380 
15381   enabled = enabled != FALSE;
15382   was_enabled = GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
15383 
15384   if (enabled)
15385     GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
15386   else
15387     GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
15388 
15389   if (enabled != was_enabled)
15390     gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15391 }
15392 
15393 /**
15394  * gtk_tree_view_get_show_expanders:
15395  * @tree_view: a #GtkTreeView.
15396  *
15397  * Returns whether or not expanders are drawn in @tree_view.
15398  *
15399  * Return value: %TRUE if expanders are drawn in @tree_view, %FALSE
15400  * otherwise.
15401  *
15402  * Since: 2.12
15403  */
15404 gboolean
gtk_tree_view_get_show_expanders(GtkTreeView * tree_view)15405 gtk_tree_view_get_show_expanders (GtkTreeView *tree_view)
15406 {
15407   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15408 
15409   return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS);
15410 }
15411 
15412 /**
15413  * gtk_tree_view_set_level_indentation:
15414  * @tree_view: a #GtkTreeView
15415  * @indentation: the amount, in pixels, of extra indentation in @tree_view.
15416  *
15417  * Sets the amount of extra indentation for child levels to use in @tree_view
15418  * in addition to the default indentation.  The value should be specified in
15419  * pixels, a value of 0 disables this feature and in this case only the default
15420  * indentation will be used.
15421  * This does not have any visible effects for lists.
15422  *
15423  * Since: 2.12
15424  */
15425 void
gtk_tree_view_set_level_indentation(GtkTreeView * tree_view,gint indentation)15426 gtk_tree_view_set_level_indentation (GtkTreeView *tree_view,
15427 				     gint         indentation)
15428 {
15429   tree_view->priv->level_indentation = indentation;
15430 
15431   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
15432 }
15433 
15434 /**
15435  * gtk_tree_view_get_level_indentation:
15436  * @tree_view: a #GtkTreeView.
15437  *
15438  * Returns the amount, in pixels, of extra indentation for child levels
15439  * in @tree_view.
15440  *
15441  * Return value: the amount of extra indentation for child levels in
15442  * @tree_view.  A return value of 0 means that this feature is disabled.
15443  *
15444  * Since: 2.12
15445  */
15446 gint
gtk_tree_view_get_level_indentation(GtkTreeView * tree_view)15447 gtk_tree_view_get_level_indentation (GtkTreeView *tree_view)
15448 {
15449   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
15450 
15451   return tree_view->priv->level_indentation;
15452 }
15453 
15454 /**
15455  * gtk_tree_view_set_tooltip_row:
15456  * @tree_view: a #GtkTreeView
15457  * @tooltip: a #GtkTooltip
15458  * @path: a #GtkTreePath
15459  *
15460  * Sets the tip area of @tooltip to be the area covered by the row at @path.
15461  * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
15462  * See also gtk_tooltip_set_tip_area().
15463  *
15464  * Since: 2.12
15465  */
15466 void
gtk_tree_view_set_tooltip_row(GtkTreeView * tree_view,GtkTooltip * tooltip,GtkTreePath * path)15467 gtk_tree_view_set_tooltip_row (GtkTreeView *tree_view,
15468 			       GtkTooltip  *tooltip,
15469 			       GtkTreePath *path)
15470 {
15471   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15472   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
15473 
15474   gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
15475 }
15476 
15477 /**
15478  * gtk_tree_view_set_tooltip_cell:
15479  * @tree_view: a #GtkTreeView
15480  * @tooltip: a #GtkTooltip
15481  * @path: (allow-none): a #GtkTreePath or %NULL
15482  * @column: (allow-none): a #GtkTreeViewColumn or %NULL
15483  * @cell: (allow-none): a #GtkCellRenderer or %NULL
15484  *
15485  * Sets the tip area of @tooltip to the area @path, @column and @cell have
15486  * in common.  For example if @path is %NULL and @column is set, the tip
15487  * area will be set to the full area covered by @column.  See also
15488  * gtk_tooltip_set_tip_area().
15489  *
15490  * Note that if @path is not specified and @cell is set and part of a column
15491  * containing the expander, the tooltip might not show and hide at the correct
15492  * position.  In such cases @path must be set to the current node under the
15493  * mouse cursor for this function to operate correctly.
15494  *
15495  * See also gtk_tree_view_set_tooltip_column() for a simpler alternative.
15496  *
15497  * Since: 2.12
15498  */
15499 void
gtk_tree_view_set_tooltip_cell(GtkTreeView * tree_view,GtkTooltip * tooltip,GtkTreePath * path,GtkTreeViewColumn * column,GtkCellRenderer * cell)15500 gtk_tree_view_set_tooltip_cell (GtkTreeView       *tree_view,
15501 				GtkTooltip        *tooltip,
15502 				GtkTreePath       *path,
15503 				GtkTreeViewColumn *column,
15504 				GtkCellRenderer   *cell)
15505 {
15506   GdkRectangle rect;
15507 
15508   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15509   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
15510   g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
15511   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
15512 
15513   /* Determine x values. */
15514   if (column && cell)
15515     {
15516       GdkRectangle tmp;
15517       gint start, width;
15518 
15519       /* We always pass in path here, whether it is NULL or not.
15520        * For cells in expander columns path must be specified so that
15521        * we can correctly account for the indentation.  This also means
15522        * that the tooltip is constrained vertically by the "Determine y
15523        * values" code below; this is not a real problem since cells actually
15524        * don't stretch vertically in constrast to columns.
15525        */
15526       gtk_tree_view_get_cell_area (tree_view, path, column, &tmp);
15527       gtk_tree_view_column_cell_get_position (column, cell, &start, &width);
15528 
15529       gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
15530 							 tmp.x + start, 0,
15531 							 &rect.x, NULL);
15532       rect.width = width;
15533     }
15534   else if (column)
15535     {
15536       GdkRectangle tmp;
15537 
15538       gtk_tree_view_get_background_area (tree_view, NULL, column, &tmp);
15539       gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
15540 							 tmp.x, 0,
15541 							 &rect.x, NULL);
15542       rect.width = tmp.width;
15543     }
15544   else
15545     {
15546       rect.x = 0;
15547       rect.width = GTK_WIDGET (tree_view)->allocation.width;
15548     }
15549 
15550   /* Determine y values. */
15551   if (path)
15552     {
15553       GdkRectangle tmp;
15554 
15555       gtk_tree_view_get_background_area (tree_view, path, NULL, &tmp);
15556       gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
15557 							 0, tmp.y,
15558 							 NULL, &rect.y);
15559       rect.height = tmp.height;
15560     }
15561   else
15562     {
15563       rect.y = 0;
15564       rect.height = tree_view->priv->vadjustment->page_size;
15565     }
15566 
15567   gtk_tooltip_set_tip_area (tooltip, &rect);
15568 }
15569 
15570 /**
15571  * gtk_tree_view_get_tooltip_context:
15572  * @tree_view: a #GtkTreeView
15573  * @x: (inout): the x coordinate (relative to widget coordinates)
15574  * @y: (inout): the y coordinate (relative to widget coordinates)
15575  * @keyboard_tip: whether this is a keyboard tooltip or not
15576  * @model: (out) (allow-none): a pointer to receive a #GtkTreeModel or %NULL
15577  * @path: (out) (allow-none): a pointer to receive a #GtkTreePath or %NULL
15578  * @iter: (out) (allow-none): a pointer to receive a #GtkTreeIter or %NULL
15579  *
15580  * This function is supposed to be used in a #GtkWidget::query-tooltip
15581  * signal handler for #GtkTreeView.  The @x, @y and @keyboard_tip values
15582  * which are received in the signal handler, should be passed to this
15583  * function without modification.
15584  *
15585  * The return value indicates whether there is a tree view row at the given
15586  * coordinates (%TRUE) or not (%FALSE) for mouse tooltips.  For keyboard
15587  * tooltips the row returned will be the cursor row.  When %TRUE, then any of
15588  * @model, @path and @iter which have been provided will be set to point to
15589  * that row and the corresponding model.  @x and @y will always be converted
15590  * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
15591  *
15592  * Return value: whether or not the given tooltip context points to a row.
15593  *
15594  * Since: 2.12
15595  */
15596 gboolean
gtk_tree_view_get_tooltip_context(GtkTreeView * tree_view,gint * x,gint * y,gboolean keyboard_tip,GtkTreeModel ** model,GtkTreePath ** path,GtkTreeIter * iter)15597 gtk_tree_view_get_tooltip_context (GtkTreeView   *tree_view,
15598 				   gint          *x,
15599 				   gint          *y,
15600 				   gboolean       keyboard_tip,
15601 				   GtkTreeModel **model,
15602 				   GtkTreePath  **path,
15603 				   GtkTreeIter   *iter)
15604 {
15605   GtkTreePath *tmppath = NULL;
15606 
15607   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
15608   g_return_val_if_fail (x != NULL, FALSE);
15609   g_return_val_if_fail (y != NULL, FALSE);
15610 
15611   if (keyboard_tip)
15612     {
15613       gtk_tree_view_get_cursor (tree_view, &tmppath, NULL);
15614 
15615       if (!tmppath)
15616 	return FALSE;
15617     }
15618   else
15619     {
15620       gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
15621 							 x, y);
15622 
15623       if (!gtk_tree_view_get_path_at_pos (tree_view, *x, *y,
15624 					  &tmppath, NULL, NULL, NULL))
15625 	return FALSE;
15626     }
15627 
15628   if (model)
15629     *model = gtk_tree_view_get_model (tree_view);
15630 
15631   if (iter)
15632     gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view),
15633 			     iter, tmppath);
15634 
15635   if (path)
15636     *path = tmppath;
15637   else
15638     gtk_tree_path_free (tmppath);
15639 
15640   return TRUE;
15641 }
15642 
15643 static gboolean
gtk_tree_view_set_tooltip_query_cb(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip,gpointer data)15644 gtk_tree_view_set_tooltip_query_cb (GtkWidget  *widget,
15645 				    gint        x,
15646 				    gint        y,
15647 				    gboolean    keyboard_tip,
15648 				    GtkTooltip *tooltip,
15649 				    gpointer    data)
15650 {
15651   GValue value = { 0, };
15652   GValue transformed = { 0, };
15653   GtkTreeIter iter;
15654   GtkTreePath *path;
15655   GtkTreeModel *model;
15656   GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
15657 
15658   if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget),
15659 					  &x, &y,
15660 					  keyboard_tip,
15661 					  &model, &path, &iter))
15662     return FALSE;
15663 
15664   gtk_tree_model_get_value (model, &iter,
15665                             tree_view->priv->tooltip_column, &value);
15666 
15667   g_value_init (&transformed, G_TYPE_STRING);
15668 
15669   if (!g_value_transform (&value, &transformed))
15670     {
15671       g_value_unset (&value);
15672       gtk_tree_path_free (path);
15673 
15674       return FALSE;
15675     }
15676 
15677   g_value_unset (&value);
15678 
15679   if (!g_value_get_string (&transformed))
15680     {
15681       g_value_unset (&transformed);
15682       gtk_tree_path_free (path);
15683 
15684       return FALSE;
15685     }
15686 
15687   gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
15688   gtk_tree_view_set_tooltip_row (tree_view, tooltip, path);
15689 
15690   gtk_tree_path_free (path);
15691   g_value_unset (&transformed);
15692 
15693   return TRUE;
15694 }
15695 
15696 /**
15697  * gtk_tree_view_set_tooltip_column:
15698  * @tree_view: a #GtkTreeView
15699  * @column: an integer, which is a valid column number for @tree_view's model
15700  *
15701  * If you only plan to have simple (text-only) tooltips on full rows, you
15702  * can use this function to have #GtkTreeView handle these automatically
15703  * for you. @column should be set to the column in @tree_view's model
15704  * containing the tooltip texts, or -1 to disable this feature.
15705  *
15706  * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
15707  * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
15708  *
15709  * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
15710  * so &amp;, &lt;, etc have to be escaped in the text.
15711  *
15712  * Since: 2.12
15713  */
15714 void
gtk_tree_view_set_tooltip_column(GtkTreeView * tree_view,gint column)15715 gtk_tree_view_set_tooltip_column (GtkTreeView *tree_view,
15716 			          gint         column)
15717 {
15718   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
15719 
15720   if (column == tree_view->priv->tooltip_column)
15721     return;
15722 
15723   if (column == -1)
15724     {
15725       g_signal_handlers_disconnect_by_func (tree_view,
15726 	  				    gtk_tree_view_set_tooltip_query_cb,
15727 					    NULL);
15728       gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
15729     }
15730   else
15731     {
15732       if (tree_view->priv->tooltip_column == -1)
15733         {
15734           g_signal_connect (tree_view, "query-tooltip",
15735 		            G_CALLBACK (gtk_tree_view_set_tooltip_query_cb), NULL);
15736           gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
15737         }
15738     }
15739 
15740   tree_view->priv->tooltip_column = column;
15741   g_object_notify (G_OBJECT (tree_view), "tooltip-column");
15742 }
15743 
15744 /**
15745  * gtk_tree_view_get_tooltip_column:
15746  * @tree_view: a #GtkTreeView
15747  *
15748  * Returns the column of @tree_view's model which is being used for
15749  * displaying tooltips on @tree_view's rows.
15750  *
15751  * Return value: the index of the tooltip column that is currently being
15752  * used, or -1 if this is disabled.
15753  *
15754  * Since: 2.12
15755  */
15756 gint
gtk_tree_view_get_tooltip_column(GtkTreeView * tree_view)15757 gtk_tree_view_get_tooltip_column (GtkTreeView *tree_view)
15758 {
15759   g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
15760 
15761   return tree_view->priv->tooltip_column;
15762 }
15763 
15764 #define __GTK_TREE_VIEW_C__
15765 #include "gtkaliasdef.c"
15766