2 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
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.
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.
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.
21 #include "gtktreeview.h"
22 #include "gtkrbtree.h"
23 #include "gtktreednd.h"
24 #include "gtktreeprivate.h"
25 #include "gtkcellrenderer.h"
26 #include "gtksignal.h"
28 #include "gtkbutton.h"
29 #include "gtkalignment.h"
35 #include <gdk/gdkkeysyms.h>
39 /* The "background" areas of all rows/cells add up to cover the entire tree.
40 * The background includes all inter-row and inter-cell spacing.
41 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
42 * i.e. just the cells, no spacing.
45 #define BACKGROUND_FIRST_PIXEL(tree_view,tree,node) (_gtk_rbtree_node_find_offset ((tree), (node)) + TREE_VIEW_HEADER_HEIGHT ((tree_view)))
46 #define CELL_FIRST_PIXEL(tree_view,tree,node) (BACKGROUND_FIRST_PIXEL (tree_view,tree,node) + TREE_VIEW_VERTICAL_SEPARATOR/2)
48 #define BACKGROUND_HEIGHT(node) (GTK_RBNODE_GET_HEIGHT (node))
49 #define CELL_HEIGHT(node) (BACKGROUND_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPARATOR);
51 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) - TREE_VIEW_HEADER_HEIGHT (tree_view))
52 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) + TREE_VIEW_HEADER_HEIGHT (tree_view))
54 typedef struct _GtkTreeViewChild GtkTreeViewChild;
56 struct _GtkTreeViewChild
70 PROP_HEADERS_CLICKABLE,
75 static void gtk_tree_view_init (GtkTreeView *tree_view);
76 static void gtk_tree_view_class_init (GtkTreeViewClass *klass);
77 static void gtk_tree_view_set_property (GObject *object,
81 const gchar *trailer);
82 static void gtk_tree_view_get_property (GObject *object,
86 const gchar *trailer);
88 static void gtk_tree_view_finalize (GObject *object);
91 static void gtk_tree_view_setup_model (GtkTreeView *tree_view);
92 static void gtk_tree_view_realize (GtkWidget *widget);
93 static void gtk_tree_view_unrealize (GtkWidget *widget);
94 static void gtk_tree_view_map (GtkWidget *widget);
95 static void gtk_tree_view_size_request (GtkWidget *widget,
96 GtkRequisition *requisition);
97 static void gtk_tree_view_size_allocate (GtkWidget *widget,
98 GtkAllocation *allocation);
99 static gboolean gtk_tree_view_expose (GtkWidget *widget,
100 GdkEventExpose *event);
101 static gboolean gtk_tree_view_motion (GtkWidget *widget,
102 GdkEventMotion *event);
103 static gboolean gtk_tree_view_enter_notify (GtkWidget *widget,
104 GdkEventCrossing *event);
105 static gboolean gtk_tree_view_leave_notify (GtkWidget *widget,
106 GdkEventCrossing *event);
107 static gboolean gtk_tree_view_button_press (GtkWidget *widget,
108 GdkEventButton *event);
109 static gboolean gtk_tree_view_button_release (GtkWidget *widget,
110 GdkEventButton *event);
111 static void gtk_tree_view_draw_focus (GtkWidget *widget);
112 static gint gtk_tree_view_focus_in (GtkWidget *widget,
113 GdkEventFocus *event);
114 static gint gtk_tree_view_focus_out (GtkWidget *widget,
115 GdkEventFocus *event);
116 static gint gtk_tree_view_focus (GtkContainer *container,
117 GtkDirectionType direction);
119 /* container signals */
120 static void gtk_tree_view_remove (GtkContainer *container,
122 static void gtk_tree_view_forall (GtkContainer *container,
123 gboolean include_internals,
124 GtkCallback callback,
125 gpointer callback_data);
127 /* Source side drag signals */
128 static void gtk_tree_view_drag_begin (GtkWidget *widget,
129 GdkDragContext *context);
130 static void gtk_tree_view_drag_end (GtkWidget *widget,
131 GdkDragContext *context);
132 static void gtk_tree_view_drag_data_get (GtkWidget *widget,
133 GdkDragContext *context,
134 GtkSelectionData *selection_data,
137 static void gtk_tree_view_drag_data_delete (GtkWidget *widget,
138 GdkDragContext *context);
140 /* Target side drag signals */
141 static void gtk_tree_view_drag_leave (GtkWidget *widget,
142 GdkDragContext *context,
144 static gboolean gtk_tree_view_drag_motion (GtkWidget *widget,
145 GdkDragContext *context,
149 static gboolean gtk_tree_view_drag_drop (GtkWidget *widget,
150 GdkDragContext *context,
154 static void gtk_tree_view_drag_data_received (GtkWidget *widget,
155 GdkDragContext *context,
158 GtkSelectionData *selection_data,
162 /* tree_model signals */
163 static void gtk_tree_view_set_adjustments (GtkTreeView *tree_view,
165 GtkAdjustment *vadj);
166 static void gtk_tree_view_changed (GtkTreeModel *model,
170 static void gtk_tree_view_inserted (GtkTreeModel *model,
174 static void gtk_tree_view_has_child_toggled (GtkTreeModel *model,
178 static void gtk_tree_view_deleted (GtkTreeModel *model,
182 /* Internal functions */
183 static void gtk_tree_view_queue_draw_node (GtkTreeView *tree_view,
186 GdkRectangle *clip_rect);
187 static void gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
189 GdkRectangle *clip_rect);
190 static void gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
195 static void gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
198 static gint gtk_tree_view_new_column_width (GtkTreeView *tree_view,
201 static void gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
202 GtkTreeView *tree_view);
203 static gint gtk_tree_view_insert_iter_height (GtkTreeView *tree_view,
207 static void gtk_tree_view_build_tree (GtkTreeView *tree_view,
212 gboolean calc_bounds);
213 static void gtk_tree_view_calc_size (GtkTreeView *priv,
217 static gboolean gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view,
221 static void gtk_tree_view_discover_dirty (GtkTreeView *tree_view,
225 static void gtk_tree_view_check_dirty (GtkTreeView *tree_view);
227 static void gtk_tree_view_create_button (GtkTreeView *tree_view,
229 static void gtk_tree_view_create_buttons (GtkTreeView *tree_view);
231 static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
234 static gboolean gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view,
235 GdkEventMotion *event);
236 static void _gtk_tree_view_update_col_width (GtkTreeView *tree_view);
239 static GtkContainerClass *parent_class = NULL;
242 /* Class Functions */
244 gtk_tree_view_get_type (void)
246 static GtkType tree_view_type = 0;
250 static const GTypeInfo tree_view_info =
252 sizeof (GtkTreeViewClass),
253 NULL, /* base_init */
254 NULL, /* base_finalize */
255 (GClassInitFunc) gtk_tree_view_class_init,
256 NULL, /* class_finalize */
257 NULL, /* class_data */
258 sizeof (GtkTreeView),
260 (GInstanceInitFunc) gtk_tree_view_init
263 tree_view_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkTreeView", &tree_view_info, 0);
266 return tree_view_type;
270 gtk_tree_view_class_init (GtkTreeViewClass *class)
272 GObjectClass *o_class;
273 GtkObjectClass *object_class;
274 GtkWidgetClass *widget_class;
275 GtkContainerClass *container_class;
277 o_class = (GObjectClass *) class;
278 object_class = (GtkObjectClass *) class;
279 widget_class = (GtkWidgetClass *) class;
280 container_class = (GtkContainerClass *) class;
282 parent_class = g_type_class_peek_parent (class);
284 o_class->finalize = gtk_tree_view_finalize;
285 o_class->set_property = gtk_tree_view_set_property;
286 o_class->get_property = gtk_tree_view_get_property;
288 widget_class->realize = gtk_tree_view_realize;
289 widget_class->unrealize = gtk_tree_view_unrealize;
290 widget_class->map = gtk_tree_view_map;
291 widget_class->size_request = gtk_tree_view_size_request;
292 widget_class->size_allocate = gtk_tree_view_size_allocate;
293 widget_class->expose_event = gtk_tree_view_expose;
294 widget_class->motion_notify_event = gtk_tree_view_motion;
295 widget_class->enter_notify_event = gtk_tree_view_enter_notify;
296 widget_class->leave_notify_event = gtk_tree_view_leave_notify;
297 widget_class->button_press_event = gtk_tree_view_button_press;
298 widget_class->button_release_event = gtk_tree_view_button_release;
299 widget_class->focus_in_event = gtk_tree_view_focus_in;
300 widget_class->focus_out_event = gtk_tree_view_focus_out;
302 widget_class->drag_begin = gtk_tree_view_drag_begin;
303 widget_class->drag_end = gtk_tree_view_drag_end;
304 widget_class->drag_data_get = gtk_tree_view_drag_data_get;
305 widget_class->drag_data_delete = gtk_tree_view_drag_data_delete;
307 widget_class->drag_leave = gtk_tree_view_drag_leave;
308 widget_class->drag_motion = gtk_tree_view_drag_motion;
309 widget_class->drag_drop = gtk_tree_view_drag_drop;
310 widget_class->drag_data_received = gtk_tree_view_drag_data_received;
312 container_class->forall = gtk_tree_view_forall;
313 container_class->remove = gtk_tree_view_remove;
314 container_class->focus = gtk_tree_view_focus;
316 class->set_scroll_adjustments = gtk_tree_view_set_adjustments;
319 /* FIXME, tim needs to support interface prerequisits in GType */
320 g_object_class_install_property (o_class,
322 g_param_spec_object ("model",
324 _("The model for the tree view"),
329 g_object_class_install_property (o_class,
331 g_param_spec_object ("hadjustment",
332 _("Horizontal Adjustment"),
333 _("Horizontal Adjustment for the widget"),
337 g_object_class_install_property (o_class,
339 g_param_spec_object ("vadjustment",
340 _("Vertical Adjustment"),
341 _("Vertical Adjustment for the widget"),
345 g_object_class_install_property (o_class,
346 PROP_HEADERS_VISIBLE,
347 g_param_spec_boolean ("headers_visible",
349 _("Show the column header buttons"),
353 g_object_class_install_property (o_class,
354 PROP_HEADERS_CLICKABLE,
355 g_param_spec_boolean ("headers_clickable",
356 _("Headers Clickable"),
357 _("Column headers respond to click events"),
361 g_object_class_install_property (o_class,
362 PROP_EXPANDER_COLUMN,
363 g_param_spec_uint ("expander_column",
365 _("Set the column number for the expander column"),
371 g_object_class_install_property (o_class,
373 g_param_spec_boolean ("rules_hint",
375 _("Set a hint to the theme engine to draw rows in alternating colors"),
379 widget_class->set_scroll_adjustments_signal =
380 gtk_signal_new ("set_scroll_adjustments",
382 GTK_CLASS_TYPE (object_class),
383 GTK_SIGNAL_OFFSET (GtkTreeViewClass, set_scroll_adjustments),
384 gtk_marshal_VOID__OBJECT_OBJECT,
386 GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
390 gtk_tree_view_init (GtkTreeView *tree_view)
392 tree_view->priv = g_new0 (GtkTreeViewPrivate, 1);
394 GTK_WIDGET_SET_FLAGS (tree_view, GTK_CAN_FOCUS);
396 tree_view->priv->model = NULL;
397 tree_view->priv->flags = GTK_TREE_VIEW_IS_LIST | GTK_TREE_VIEW_SHOW_EXPANDERS | GTK_TREE_VIEW_DRAW_KEYFOCUS | GTK_TREE_VIEW_HEADERS_VISIBLE;
398 tree_view->priv->tab_offset = TREE_VIEW_EXPANDER_WIDTH;
399 tree_view->priv->n_columns = 0;
400 tree_view->priv->columns = NULL;
401 tree_view->priv->button_pressed_node = NULL;
402 tree_view->priv->button_pressed_tree = NULL;
403 tree_view->priv->prelight_node = NULL;
404 tree_view->priv->header_height = 1;
405 tree_view->priv->x_drag = 0;
406 tree_view->priv->drag_pos = -1;
407 tree_view->priv->selection = NULL;
408 tree_view->priv->anchor = NULL;
409 tree_view->priv->cursor = NULL;
411 tree_view->priv->pressed_button = -1;
412 tree_view->priv->press_start_x = -1;
413 tree_view->priv->press_start_y = -1;
415 gtk_tree_view_set_adjustments (tree_view, NULL, NULL);
416 _gtk_tree_view_update_size (tree_view);
424 gtk_tree_view_finalize (GObject *object)
426 GtkTreeView *tree_view = (GtkTreeView *) object;
428 if (tree_view->priv->tree)
429 _gtk_rbtree_free (tree_view->priv->tree);
431 if (tree_view->priv->scroll_to_path != NULL)
432 gtk_tree_path_free (tree_view->priv->scroll_to_path);
434 if (tree_view->priv->drag_dest_row)
435 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
437 g_free (tree_view->priv);
438 if (G_OBJECT_CLASS (parent_class)->finalize)
439 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
446 gtk_tree_view_set_property (GObject *object,
450 const gchar *trailer)
452 GtkTreeView *tree_view;
454 tree_view = GTK_TREE_VIEW (object);
459 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (g_value_get_object (value)));
461 case PROP_HADJUSTMENT:
462 gtk_tree_view_set_hadjustment (tree_view, GTK_ADJUSTMENT (g_value_get_object (value)));
464 case PROP_VADJUSTMENT:
465 gtk_tree_view_set_vadjustment (tree_view, GTK_ADJUSTMENT (g_value_get_object (value)));
467 case PROP_HEADERS_VISIBLE:
468 gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value));
470 case PROP_HEADERS_CLICKABLE:
471 gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
473 case PROP_EXPANDER_COLUMN:
474 gtk_tree_view_set_expander_column (tree_view, g_value_get_uint (value));
476 case PROP_RULES_HINT:
477 gtk_tree_view_set_rules_hint (tree_view, g_value_get_boolean (value));
485 gtk_tree_view_get_property (GObject *object,
489 const gchar *trailer)
491 GtkTreeView *tree_view;
493 tree_view = GTK_TREE_VIEW (object);
498 g_value_set_object (value, G_OBJECT (tree_view->priv->model));
500 case PROP_HADJUSTMENT:
501 g_value_set_object (value, G_OBJECT (tree_view->priv->hadjustment));
503 case PROP_VADJUSTMENT:
504 g_value_set_object (value, G_OBJECT (tree_view->priv->vadjustment));
506 case PROP_HEADERS_VISIBLE:
507 g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view));
509 case PROP_HEADERS_CLICKABLE:
510 /* g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view)); */
512 case PROP_EXPANDER_COLUMN:
513 g_value_set_uint (value, tree_view->priv->expander_column);
515 case PROP_RULES_HINT:
516 g_value_set_boolean (value, tree_view->priv->has_rules);
519 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
528 gtk_tree_view_realize (GtkWidget *widget)
531 GtkTreeView *tree_view;
533 GdkWindowAttr attributes;
534 gint attributes_mask;
536 g_return_if_fail (widget != NULL);
537 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
539 tree_view = GTK_TREE_VIEW (widget);
541 gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget));
542 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
544 /* Make the main, clipping window */
545 attributes.window_type = GDK_WINDOW_CHILD;
546 attributes.x = widget->allocation.x;
547 attributes.y = widget->allocation.y;
548 attributes.width = widget->allocation.width;
549 attributes.height = widget->allocation.height;
550 attributes.wclass = GDK_INPUT_OUTPUT;
551 attributes.visual = gtk_widget_get_visual (widget);
552 attributes.colormap = gtk_widget_get_colormap (widget);
553 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
555 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
557 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
558 &attributes, attributes_mask);
559 gdk_window_set_user_data (widget->window, widget);
561 /* Make the window for the tree */
564 attributes.width = tree_view->priv->width;
565 attributes.height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
566 attributes.event_mask = GDK_EXPOSURE_MASK |
568 GDK_POINTER_MOTION_MASK |
569 GDK_ENTER_NOTIFY_MASK |
570 GDK_LEAVE_NOTIFY_MASK |
571 GDK_BUTTON_PRESS_MASK |
572 GDK_BUTTON_RELEASE_MASK |
573 gtk_widget_get_events (widget);
575 tree_view->priv->bin_window = gdk_window_new (widget->window,
576 &attributes, attributes_mask);
577 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
579 /* Make the column header window */
582 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
583 attributes.height = tree_view->priv->header_height;
584 attributes.event_mask = (GDK_EXPOSURE_MASK |
586 GDK_BUTTON_PRESS_MASK |
587 GDK_BUTTON_RELEASE_MASK |
589 GDK_KEY_RELEASE_MASK) |
590 gtk_widget_get_events (widget);
592 tree_view->priv->header_window = gdk_window_new (widget->window,
593 &attributes, attributes_mask);
594 gdk_window_set_user_data (tree_view->priv->header_window, widget);
597 values.foreground = (widget->style->white.pixel==0 ?
598 widget->style->black:widget->style->white);
599 values.function = GDK_XOR;
600 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
601 tree_view->priv->xor_gc = gdk_gc_new_with_values (widget->window,
606 /* Add them all up. */
607 widget->style = gtk_style_attach (widget->style, widget->window);
608 gdk_window_set_background (widget->window, &widget->style->base[widget->state]);
609 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
610 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
612 tmp_list = tree_view->priv->children;
615 GtkTreeViewChild *child = tmp_list->data;
616 tmp_list = tmp_list->next;
618 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
621 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
622 _gtk_tree_view_column_realize_button (GTK_TREE_VIEW_COLUMN (tmp_list->data));
624 _gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
626 if (tree_view->priv->scroll_to_path != NULL ||
627 tree_view->priv->scroll_to_column != NULL)
629 gtk_tree_view_scroll_to_cell (tree_view,
630 tree_view->priv->scroll_to_path,
631 tree_view->priv->scroll_to_column,
632 tree_view->priv->scroll_to_row_align,
633 tree_view->priv->scroll_to_col_align);
634 if (tree_view->priv->scroll_to_path)
636 gtk_tree_path_free (tree_view->priv->scroll_to_path);
637 tree_view->priv->scroll_to_path = NULL;
639 tree_view->priv->scroll_to_column = NULL;
644 gtk_tree_view_unrealize (GtkWidget *widget)
646 GtkTreeView *tree_view;
649 g_return_if_fail (widget != NULL);
650 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
652 tree_view = GTK_TREE_VIEW (widget);
654 if (tree_view->priv->scroll_timeout != 0)
656 gtk_timeout_remove (tree_view->priv->scroll_timeout);
657 tree_view->priv->scroll_timeout = 0;
660 if (tree_view->priv->open_dest_timeout != 0)
662 gtk_timeout_remove (tree_view->priv->open_dest_timeout);
663 tree_view->priv->open_dest_timeout = 0;
666 for (list = tree_view->priv->columns; list; list = list->next)
667 _gtk_tree_view_column_unrealize_button (GTK_TREE_VIEW_COLUMN (list->data));
669 gdk_window_set_user_data (tree_view->priv->bin_window, NULL);
670 gdk_window_destroy (tree_view->priv->bin_window);
671 tree_view->priv->bin_window = NULL;
673 gdk_window_set_user_data (tree_view->priv->header_window, NULL);
674 gdk_window_destroy (tree_view->priv->header_window);
675 tree_view->priv->header_window = NULL;
677 gdk_cursor_destroy (tree_view->priv->cursor_drag);
678 gdk_gc_destroy (tree_view->priv->xor_gc);
680 /* GtkWidget::unrealize destroys children and widget->window */
682 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
683 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
687 gtk_tree_view_map_buttons (GtkTreeView *tree_view)
691 g_return_if_fail (GTK_WIDGET_MAPPED (tree_view));
693 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
695 GtkTreeViewColumn *column;
697 for (list = tree_view->priv->columns; list; list = list->next)
700 if (GTK_WIDGET_VISIBLE (column->button) &&
701 !GTK_WIDGET_MAPPED (column->button))
702 gtk_widget_map (column->button);
704 for (list = tree_view->priv->columns; list; list = list->next)
707 if (column->visible == FALSE)
709 if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE)
711 gdk_window_raise (column->window);
712 gdk_window_show (column->window);
715 gdk_window_hide (column->window);
717 gdk_window_show (tree_view->priv->header_window);
722 gtk_tree_view_map (GtkWidget *widget)
725 GtkTreeView *tree_view;
727 g_return_if_fail (widget != NULL);
728 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
730 tree_view = GTK_TREE_VIEW (widget);
732 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
734 tmp_list = tree_view->priv->children;
737 GtkTreeViewChild *child = tmp_list->data;
738 tmp_list = tmp_list->next;
740 if (GTK_WIDGET_VISIBLE (child->widget))
742 if (!GTK_WIDGET_MAPPED (child->widget))
743 gtk_widget_map (child->widget);
746 gdk_window_show (tree_view->priv->bin_window);
748 gtk_tree_view_map_buttons (tree_view);
750 gdk_window_show (widget->window);
754 gtk_tree_view_size_request_buttons (GtkTreeView *tree_view)
758 tree_view->priv->header_height = 1;
760 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP))
762 for (list = tree_view->priv->columns; list; list = list->next)
764 GtkRequisition requisition;
765 GtkTreeViewColumn *column;
769 gtk_widget_size_request (column->button, &requisition);
771 gtk_tree_view_column_set_width (column, MAX (column->width, requisition.width));
772 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
778 gtk_tree_view_size_request (GtkWidget *widget,
779 GtkRequisition *requisition)
781 GtkTreeView *tree_view;
784 g_return_if_fail (widget != NULL);
785 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
787 tree_view = GTK_TREE_VIEW (widget);
789 requisition->width = 200;
790 requisition->height = 200;
792 tmp_list = tree_view->priv->children;
796 GtkTreeViewChild *child = tmp_list->data;
797 GtkRequisition child_requisition;
799 tmp_list = tmp_list->next;
801 if (GTK_WIDGET_VISIBLE (child->widget))
802 gtk_widget_size_request (child->widget, &child_requisition);
805 gtk_tree_view_size_request_buttons (tree_view);
809 gtk_tree_view_size_allocate_buttons (GtkWidget *widget)
811 GtkTreeView *tree_view;
813 GtkTreeViewColumn *column;
814 GtkAllocation allocation;
817 tree_view = GTK_TREE_VIEW (widget);
820 allocation.height = tree_view->priv->header_height;
822 for (list = tree_view->priv->columns; list != NULL; list = list->next)
826 if (!column->visible)
829 allocation.x = width;
830 allocation.width = column->displayed_width;
831 width += column->width;
832 gtk_widget_size_allocate (column->button, &allocation);
835 gdk_window_move_resize (column->window,
836 allocation.x + allocation.width - TREE_VIEW_DRAG_WIDTH/2,
838 TREE_VIEW_DRAG_WIDTH, allocation.height);
843 gtk_tree_view_size_allocate (GtkWidget *widget,
844 GtkAllocation *allocation)
847 GtkTreeView *tree_view;
849 g_return_if_fail (widget != NULL);
850 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
852 widget->allocation = *allocation;
854 tree_view = GTK_TREE_VIEW (widget);
856 gtk_tree_view_check_dirty (tree_view);
858 tmp_list = tree_view->priv->children;
862 GtkAllocation allocation;
863 GtkRequisition requisition;
865 GtkTreeViewChild *child = tmp_list->data;
866 tmp_list = tmp_list->next;
868 allocation.x = child->x;
869 allocation.y = child->y;
870 gtk_widget_get_child_requisition (child->widget, &requisition);
871 allocation.width = requisition.width;
872 allocation.height = requisition.height;
874 gtk_widget_size_allocate (child->widget, &allocation);
877 if (GTK_WIDGET_REALIZED (widget))
879 gdk_window_move_resize (widget->window,
880 allocation->x, allocation->y,
881 allocation->width, allocation->height);
883 gdk_window_move_resize (tree_view->priv->header_window,
885 MAX (tree_view->priv->width, allocation->width),
886 tree_view->priv->header_height);
888 if (tree_view->priv->width < allocation->width)
889 gdk_window_resize (tree_view->priv->bin_window,
891 tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view));
893 _gtk_tree_view_update_col_width (tree_view);
896 gtk_tree_view_size_allocate_buttons (widget);
898 tree_view->priv->hadjustment->page_size = allocation->width;
899 tree_view->priv->hadjustment->page_increment = allocation->width / 2;
900 tree_view->priv->hadjustment->lower = 0;
901 tree_view->priv->hadjustment->upper = tree_view->priv->width;
903 if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
904 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
905 gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed");
907 tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
908 tree_view->priv->vadjustment->page_increment = (allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
909 tree_view->priv->vadjustment->lower = 0;
910 tree_view->priv->vadjustment->upper = tree_view->priv->height;
912 if (tree_view->priv->vadjustment->value + allocation->height > tree_view->priv->height)
913 gtk_adjustment_set_value (tree_view->priv->vadjustment,
914 (gfloat) MAX (tree_view->priv->height - allocation->height, 0));
916 gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
921 gtk_tree_view_draw_node_focus_rect (GtkWidget *widget,
924 GtkTreeView *tree_view;
925 GtkRBTree *tree = NULL;
926 GtkRBNode *node = NULL;
927 gint bin_window_width = 0;
929 g_return_if_fail (widget != NULL);
930 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
932 tree_view = GTK_TREE_VIEW (widget);
934 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
939 gdk_drawable_get_size (tree_view->priv->bin_window,
940 &bin_window_width, NULL);
942 /* FIXME need a style function appropriate for this */
943 gdk_draw_rectangle (tree_view->priv->bin_window,
944 widget->style->fg_gc[GTK_STATE_NORMAL],
947 BACKGROUND_FIRST_PIXEL (tree_view, tree, node),
948 bin_window_width - 2,
949 BACKGROUND_HEIGHT (node) - 1);
953 gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view,
959 GtkCellRenderer *cell;
963 GdkRectangle background_area;
966 /* start drawing inside the black outline */
968 GdkDrawable *drawable;
969 gint bin_window_width;
971 widget = GTK_WIDGET (tree_view);
973 depth = gtk_tree_path_get_depth (path);
975 _gtk_tree_view_find_node (tree_view,
983 if (!gtk_tree_model_get_iter (tree_view->priv->model,
990 background_area.y = y;
991 background_area.height = BACKGROUND_HEIGHT (node);
993 gdk_drawable_get_size (tree_view->priv->bin_window,
994 &bin_window_width, NULL);
996 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
997 bin_window_width + 2,
998 background_area.height + 2,
1001 gdk_draw_rectangle (drawable,
1002 widget->style->base_gc[GTK_WIDGET_STATE (widget)],
1005 bin_window_width + 2,
1006 background_area.height + 2);
1008 gdk_draw_rectangle (drawable,
1009 widget->style->black_gc,
1012 bin_window_width + 1,
1013 background_area.height + 1);
1015 for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next)
1017 GtkTreeViewColumn *column = list->data;
1018 GdkRectangle cell_area;
1020 if (!column->visible)
1023 cell = column->cell;
1024 gtk_tree_view_column_set_cell_data (column,
1025 tree_view->priv->model,
1028 background_area.x = cell_offset;
1029 background_area.width = column->displayed_width;
1031 cell_area = background_area;
1033 cell_area.y += TREE_VIEW_VERTICAL_SEPARATOR / 2;
1034 cell_area.height -= TREE_VIEW_VERTICAL_SEPARATOR;
1036 if (i == tree_view->priv->expander_column &&
1037 TREE_VIEW_DRAW_EXPANDERS(tree_view))
1039 cell_area.x += depth * tree_view->priv->tab_offset;
1040 cell_area.width -= depth * tree_view->priv->tab_offset;
1043 gtk_cell_renderer_render (cell,
1051 cell_offset += column->displayed_width;
1057 /* Warning: Very scary function.
1058 * Modify at your own risk
1061 gtk_tree_view_bin_expose (GtkWidget *widget,
1062 GdkEventExpose *event)
1064 GtkTreeView *tree_view;
1069 GtkRBNode *cursor = NULL;
1070 GtkRBTree *cursor_tree = NULL;
1071 GtkRBNode *drag_highlight = NULL;
1072 GtkRBTree *drag_highlight_tree = NULL;
1074 GtkCellRenderer *cell;
1076 gint y_offset, x_offset, cell_offset;
1079 GdkRectangle background_area;
1080 GdkRectangle cell_area;
1083 gint bin_window_width;
1084 GtkTreePath *cursor_path;
1085 GtkTreePath *drag_dest_path;
1088 g_return_val_if_fail (widget != NULL, FALSE);
1089 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
1091 tree_view = GTK_TREE_VIEW (widget);
1093 if (tree_view->priv->tree == NULL)
1096 gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget));
1097 /* we want to account for a potential HEADER offset.
1098 * That is, if the header exists, we want to offset our event by its
1099 * height to find the right node.
1101 new_y = (event->area.y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):event->area.y;
1103 /* y_offset is the */
1105 y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
1106 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y),
1108 &node) + new_y - event->area.y;
1112 /* find the path for the node */
1113 path = _gtk_tree_view_find_path ((GtkTreeView *)widget,
1116 gtk_tree_model_get_iter (tree_view->priv->model,
1119 depth = gtk_tree_path_get_depth (path);
1120 gtk_tree_path_free (path);
1123 drag_dest_path = NULL;
1125 if (tree_view->priv->cursor)
1126 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
1129 _gtk_tree_view_find_node (tree_view, cursor_path,
1130 &cursor_tree, &cursor);
1132 if (tree_view->priv->drag_dest_row)
1133 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
1136 _gtk_tree_view_find_node (tree_view, drag_dest_path,
1137 &drag_highlight_tree, &drag_highlight);
1139 gdk_drawable_get_size (tree_view->priv->bin_window,
1140 &bin_window_width, NULL);
1142 for (last_column = g_list_last (tree_view->priv->columns);
1144 !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) &&
1145 GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button);
1146 last_column = last_column->prev)
1149 /* Actually process the expose event. To do this, we want to
1150 * start at the first node of the event, and walk the tree in
1151 * order, drawing each successive node.
1156 /* Need to think about this more.
1157 if (tree_view->priv->show_expanders)
1158 max_height = MAX (TREE_VIEW_EXPANDER_MIN_HEIGHT, GTK_RBNODE_GET_HEIGHT (node));
1163 max_height = BACKGROUND_HEIGHT (node);
1165 x_offset = -event->area.x;
1167 highlight_x = 0; /* should match x coord of first cell */
1169 background_area.y = y_offset + event->area.y;
1170 background_area.height = max_height;
1173 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT))
1174 flags |= GTK_CELL_RENDERER_PRELIT;
1176 parity = _gtk_rbtree_node_find_parity (tree, node);
1178 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1179 flags |= GTK_CELL_RENDERER_SELECTED;
1181 for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next)
1183 GtkTreeViewColumn *column = list->data;
1184 const gchar *detail = NULL;
1186 if (!column->visible)
1189 if (cell_offset > event->area.x + event->area.width ||
1190 cell_offset + column->displayed_width < event->area.x)
1192 cell_offset += column->displayed_width;
1196 if (column->show_sort_indicator)
1197 flags |= GTK_CELL_RENDERER_SORTED;
1199 flags &= ~GTK_CELL_RENDERER_SORTED;
1201 cell = column->cell;
1202 gtk_tree_view_column_set_cell_data (column,
1203 tree_view->priv->model,
1206 background_area.x = cell_offset;
1207 background_area.width = column->displayed_width;
1209 cell_area = background_area;
1210 cell_area.y += TREE_VIEW_VERTICAL_SEPARATOR / 2;
1211 cell_area.height -= TREE_VIEW_VERTICAL_SEPARATOR;
1213 /* Select the detail for drawing the cell. relevant
1214 * factors are parity, sortedness, and whether to
1218 /* FIXME when we have style properties, clean this up.
1221 if (tree_view->priv->has_rules)
1223 if (flags & GTK_CELL_RENDERER_SORTED)
1226 detail = "cell_odd_ruled_sorted";
1228 detail = "cell_even_ruled_sorted";
1233 detail = "cell_odd_ruled";
1235 detail = "cell_even_ruled";
1240 if (flags & GTK_CELL_RENDERER_SORTED)
1243 detail = "cell_odd_sorted";
1245 detail = "cell_even_sorted";
1250 detail = "cell_odd";
1252 detail = "cell_even";
1258 /* Draw background */
1259 gtk_paint_flat_box (widget->style,
1261 (flags & GTK_CELL_RENDERER_SELECTED) ?
1262 GTK_STATE_SELECTED : GTK_STATE_NORMAL,
1269 background_area.width,
1270 background_area.height);
1272 if (i == tree_view->priv->expander_column &&
1273 TREE_VIEW_DRAW_EXPANDERS(tree_view))
1275 cell_area.x += depth*tree_view->priv->tab_offset;
1276 cell_area.width -= depth*tree_view->priv->tab_offset;
1278 /* If we have an expander column, the highlight underline
1279 * starts with that column, so that it indicates which
1280 * level of the tree we're dropping at.
1282 highlight_x = cell_area.x;
1284 gtk_cell_renderer_render (cell,
1292 if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)
1295 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, 0);
1296 gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
1304 gtk_cell_renderer_render (cell,
1312 cell_offset += column->displayed_width;
1315 if (node == cursor &&
1316 GTK_WIDGET_HAS_FOCUS (widget))
1317 gtk_tree_view_draw_focus (widget);
1319 if (node == drag_highlight)
1321 /* Draw indicator for the drop
1323 gint highlight_y = -1;
1325 switch (tree_view->priv->drag_dest_pos)
1327 case GTK_TREE_VIEW_DROP_BEFORE:
1328 highlight_y = background_area.y - TREE_VIEW_VERTICAL_SEPARATOR/2;
1331 case GTK_TREE_VIEW_DROP_AFTER:
1332 highlight_y = background_area.y + background_area.height + TREE_VIEW_VERTICAL_SEPARATOR/2;
1335 case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
1336 case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
1337 gtk_tree_view_draw_node_focus_rect (widget,
1342 if (highlight_y >= 0)
1344 gdk_draw_line (event->window,
1345 widget->style->black_gc,
1348 bin_window_width - highlight_x,
1353 y_offset += max_height;
1356 GtkTreeIter parent = iter;
1359 tree = node->children;
1362 g_assert (node != tree->nil);
1364 while (node->left != tree->nil)
1366 has_child = gtk_tree_model_iter_children (tree_view->priv->model,
1369 cell = gtk_tree_view_get_column (tree_view, 0)->cell;
1373 TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE);
1377 gboolean done = FALSE;
1380 node = _gtk_rbtree_next (tree, node);
1383 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
1384 cell = gtk_tree_view_get_column (tree_view, 0)->cell;
1388 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
1392 GtkTreeIter parent_iter = iter;
1393 gboolean has_parent;
1395 node = tree->parent_node;
1396 tree = tree->parent_tree;
1398 /* we've run out of tree. It's okay to return though, as
1399 * we'd only break out of the while loop below. */
1401 has_parent = gtk_tree_model_iter_parent (tree_view->priv->model,
1407 TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE);
1413 while (y_offset < event->area.height);
1416 gtk_tree_path_free (cursor_path);
1419 gtk_tree_path_free (drag_dest_path);
1425 gtk_tree_view_expose (GtkWidget *widget,
1426 GdkEventExpose *event)
1428 GtkTreeView *tree_view;
1430 g_return_val_if_fail (widget != NULL, FALSE);
1431 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
1433 tree_view = GTK_TREE_VIEW (widget);
1435 if (event->window == tree_view->priv->bin_window)
1436 return gtk_tree_view_bin_expose (widget, event);
1442 coords_are_over_arrow (GtkTreeView *tree_view,
1445 /* these are in tree window coords */
1452 if (!GTK_WIDGET_REALIZED (tree_view))
1455 if ((node->flags & GTK_RBNODE_IS_PARENT) == 0)
1458 arrow.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
1460 arrow.height = BACKGROUND_HEIGHT (node);
1462 gtk_tree_view_get_arrow_xrange (tree_view, &arrow.x, &x2);
1464 arrow.width = x2 - arrow.x;
1466 return (x >= arrow.x &&
1467 x < (arrow.x + arrow.height) &&
1469 y < (arrow.y + arrow.height));
1473 do_unprelight (GtkTreeView *tree_view,
1474 /* these are in tree window coords */
1478 if (tree_view->priv->prelight_node == NULL)
1481 GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT);
1483 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT) &&
1484 !coords_are_over_arrow (tree_view,
1485 tree_view->priv->prelight_tree,
1486 tree_view->priv->prelight_node,
1489 /* We need to unprelight the old arrow. */
1491 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
1493 gtk_tree_view_draw_arrow (tree_view,
1494 tree_view->priv->prelight_tree,
1495 tree_view->priv->prelight_node,
1501 tree_view->priv->prelight_node = NULL;
1502 tree_view->priv->prelight_tree = NULL;
1506 do_prelight (GtkTreeView *tree_view,
1509 /* these are in tree window coords */
1513 if (coords_are_over_arrow (tree_view, tree, node, x, y))
1514 GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT);
1516 tree_view->priv->prelight_node = node;
1517 tree_view->priv->prelight_tree = tree;
1519 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT);
1523 ensure_unprelighted (GtkTreeView *tree_view)
1525 do_unprelight (tree_view, -1000, -1000); /* coords not possibly over an arrow */
1529 gtk_tree_view_motion (GtkWidget *widget,
1530 GdkEventMotion *event)
1532 GtkTreeView *tree_view;
1536 GtkRBTree *old_prelight_tree;
1537 GtkRBNode *old_prelight_node;
1539 tree_view = (GtkTreeView *) widget;
1541 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
1546 if (event->is_hint || event->window != widget->window)
1547 gtk_widget_get_pointer (widget, &x, NULL);
1551 new_width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), tree_view->priv->drag_pos, &x);
1552 if (x != tree_view->priv->x_drag)
1554 gtk_tree_view_column_set_width (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), tree_view->priv->drag_pos), new_width);
1557 /* FIXME: Do we need to scroll */
1558 _gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
1562 /* Sanity check it */
1563 if (event->window != tree_view->priv->bin_window)
1566 if (tree_view->priv->tree == NULL)
1569 gtk_tree_view_maybe_begin_dragging_row (tree_view, event);
1571 old_prelight_tree = tree_view->priv->prelight_tree;
1572 old_prelight_node = tree_view->priv->prelight_node;
1574 do_unprelight (tree_view, event->x, event->y);
1576 new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
1578 _gtk_rbtree_find_offset (tree_view->priv->tree,
1579 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y),
1586 /* If we are currently pressing down a button, we don't want to prelight anything else. */
1587 if ((tree_view->priv->button_pressed_node != NULL) &&
1588 (tree_view->priv->button_pressed_node != node))
1592 do_prelight (tree_view, tree, node, event->x, new_y);
1594 if (old_prelight_node != tree_view->priv->prelight_node)
1596 if (old_prelight_node)
1597 gtk_tree_view_queue_draw_node (tree_view,
1602 if (tree_view->priv->prelight_node)
1603 gtk_tree_view_queue_draw_node (tree_view,
1604 tree_view->priv->prelight_tree,
1605 tree_view->priv->prelight_node,
1612 /* FIXME Is this function necessary? Can I get an enter_notify event
1613 * w/o either an expose event or a mouse motion event?
1616 gtk_tree_view_enter_notify (GtkWidget *widget,
1617 GdkEventCrossing *event)
1619 GtkTreeView *tree_view;
1624 g_return_val_if_fail (widget != NULL, FALSE);
1625 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
1627 tree_view = GTK_TREE_VIEW (widget);
1629 /* Sanity check it */
1630 if (event->window != tree_view->priv->bin_window)
1633 if (tree_view->priv->tree == NULL)
1636 if ((tree_view->priv->button_pressed_node != NULL) &&
1637 (tree_view->priv->button_pressed_node != node))
1640 /* find the node internally */
1641 new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
1643 _gtk_rbtree_find_offset (tree_view->priv->tree,
1644 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y),
1651 do_prelight (tree_view, tree, node, event->x, new_y);
1653 if (tree_view->priv->prelight_node)
1654 gtk_tree_view_queue_draw_node (tree_view,
1655 tree_view->priv->prelight_tree,
1656 tree_view->priv->prelight_node,
1663 gtk_tree_view_leave_notify (GtkWidget *widget,
1664 GdkEventCrossing *event)
1666 GtkTreeView *tree_view;
1668 g_return_val_if_fail (widget != NULL, FALSE);
1669 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
1671 tree_view = GTK_TREE_VIEW (widget);
1673 if (tree_view->priv->prelight_node)
1674 gtk_tree_view_queue_draw_node (tree_view,
1675 tree_view->priv->prelight_tree,
1676 tree_view->priv->prelight_node,
1679 ensure_unprelighted (tree_view);
1685 gtk_tree_view_button_press (GtkWidget *widget,
1686 GdkEventButton *event)
1688 GtkTreeView *tree_view;
1690 GtkTreeViewColumn *column;
1692 GdkRectangle background_area;
1693 GdkRectangle cell_area;
1695 g_return_val_if_fail (widget != NULL, FALSE);
1696 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
1697 g_return_val_if_fail (event != NULL, FALSE);
1699 tree_view = GTK_TREE_VIEW (widget);
1701 if (event->window == tree_view->priv->bin_window)
1711 if (!GTK_WIDGET_HAS_FOCUS (widget))
1712 gtk_widget_grab_focus (widget);
1713 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
1715 /* are we in an arrow? */
1716 if (tree_view->priv->prelight_node &&
1717 GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
1719 if (event->button == 1)
1721 gtk_grab_add (widget);
1722 tree_view->priv->button_pressed_node = tree_view->priv->prelight_node;
1723 tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree;
1724 gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget),
1725 tree_view->priv->prelight_tree,
1726 tree_view->priv->prelight_node,
1733 /* find the node that was clicked */
1734 new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y;
1735 y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree,
1736 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y),
1738 &node) + new_y - (gint)event->y;
1741 /* We clicked in dead space */
1744 /* Get the path and the node */
1745 path = _gtk_tree_view_find_path (tree_view, tree, node);
1746 depth = gtk_tree_path_get_depth (path);
1747 background_area.y = y_offset + event->y + TREE_VIEW_VERTICAL_SEPARATOR;
1748 background_area.height = GTK_RBNODE_GET_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPARATOR;
1749 background_area.x = 0;
1750 /* Let the cell have a chance at selecting it. */
1752 for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next)
1754 GtkTreeViewColumn *column = list->data;
1755 GtkCellRenderer *cell;
1758 if (!column->visible)
1761 background_area.width = column->displayed_width;
1762 if (i == tree_view->priv->expander_column &&
1763 TREE_VIEW_DRAW_EXPANDERS(tree_view))
1765 cell_area = background_area;
1766 cell_area.x += depth*tree_view->priv->tab_offset;
1767 cell_area.width -= depth*tree_view->priv->tab_offset;
1771 cell_area = background_area;
1774 cell = column->cell;
1776 if ((background_area.x > (gint) event->x) ||
1777 (background_area.y > (gint) event->y) ||
1778 (background_area.x + background_area.width <= (gint) event->x) ||
1779 (background_area.y + background_area.height <= (gint) event->y))
1781 background_area.x += background_area.width;
1785 gtk_tree_model_get_iter (tree_view->priv->model,
1788 gtk_tree_view_column_set_cell_data (column,
1789 tree_view->priv->model,
1792 path_string = gtk_tree_path_to_string (path);
1793 if (gtk_cell_renderer_event (cell,
1802 g_free (path_string);
1803 gtk_tree_path_free (path);
1808 g_free (path_string);
1813 /* Save press to possibly begin a drag
1815 if (tree_view->priv->pressed_button < 0)
1817 tree_view->priv->pressed_button = event->button;
1818 tree_view->priv->press_start_x = event->x;
1819 tree_view->priv->press_start_y = event->y;
1822 /* Handle the selection */
1823 if (tree_view->priv->selection == NULL)
1824 tree_view->priv->selection =
1825 _gtk_tree_selection_new_with_tree_view (tree_view);
1827 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
1832 gtk_tree_path_free (path);
1836 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
1838 column = list->data;
1839 if (event->window == column->window &&
1840 column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE &&
1845 if (gdk_pointer_grab (column->window, FALSE,
1846 GDK_POINTER_MOTION_HINT_MASK |
1847 GDK_BUTTON1_MOTION_MASK |
1848 GDK_BUTTON_RELEASE_MASK,
1849 NULL, NULL, event->time))
1852 gtk_grab_add (widget);
1853 GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
1855 /* block attached dnd signal handler */
1856 drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1858 gtk_signal_handler_block_by_data (GTK_OBJECT (widget), drag_data);
1860 if (!GTK_WIDGET_HAS_FOCUS (widget))
1861 gtk_widget_grab_focus (widget);
1863 tree_view->priv->drag_pos = i;
1864 tree_view->priv->x_drag = (column->button->allocation.x + column->button->allocation.width);
1871 gtk_tree_view_button_release (GtkWidget *widget,
1872 GdkEventButton *event)
1874 GtkTreeView *tree_view;
1876 g_return_val_if_fail (widget != NULL, FALSE);
1877 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
1878 g_return_val_if_fail (event != NULL, FALSE);
1880 tree_view = GTK_TREE_VIEW (widget);
1882 if (tree_view->priv->pressed_button == event->button)
1883 tree_view->priv->pressed_button = -1;
1885 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE))
1892 i = tree_view->priv->drag_pos;
1893 tree_view->priv->drag_pos = -1;
1895 /* unblock attached dnd signal handler */
1896 drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data");
1898 gtk_signal_handler_unblock_by_data (GTK_OBJECT (widget), drag_data);
1900 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE);
1901 gtk_widget_get_pointer (widget, &x, NULL);
1902 gtk_grab_remove (widget);
1903 gdk_pointer_ungrab (event->time);
1905 width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), i, &x);
1906 gtk_tree_view_column_set_width (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), i), width);
1910 if (tree_view->priv->button_pressed_node == NULL)
1913 if (event->button == 1)
1915 gtk_grab_remove (widget);
1916 if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node &&
1917 GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
1922 /* Actually activate the node */
1923 if (tree_view->priv->button_pressed_node->children == NULL)
1926 path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget),
1927 tree_view->priv->button_pressed_tree,
1928 tree_view->priv->button_pressed_node);
1929 tree_view->priv->button_pressed_node->children = _gtk_rbtree_new ();
1930 tree_view->priv->button_pressed_node->children->parent_tree = tree_view->priv->button_pressed_tree;
1931 tree_view->priv->button_pressed_node->children->parent_node = tree_view->priv->button_pressed_node;
1932 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
1934 if (gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter))
1935 gtk_tree_view_build_tree (tree_view,
1936 tree_view->priv->button_pressed_node->children,
1938 gtk_tree_path_get_depth (path) + 1,
1940 GTK_WIDGET_REALIZED (widget));
1944 path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget),
1945 tree_view->priv->button_pressed_node->children,
1946 tree_view->priv->button_pressed_node->children->root);
1947 gtk_tree_model_get_iter (tree_view->priv->model,
1951 gtk_tree_view_discover_dirty (GTK_TREE_VIEW (widget),
1952 tree_view->priv->button_pressed_node->children,
1954 gtk_tree_path_get_depth (path));
1955 _gtk_rbtree_remove (tree_view->priv->button_pressed_node->children);
1957 gtk_tree_path_free (path);
1959 _gtk_tree_view_update_size (GTK_TREE_VIEW (widget));
1962 tree_view->priv->button_pressed_node = NULL;
1970 gtk_tree_view_draw_focus (GtkWidget *widget)
1972 GtkTreeView *tree_view;
1973 GtkTreePath *cursor_path;
1975 g_return_if_fail (widget != NULL);
1976 g_return_if_fail (GTK_IS_TREE_VIEW (widget));
1978 tree_view = GTK_TREE_VIEW (widget);
1980 if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS))
1983 if (tree_view->priv->cursor == NULL)
1986 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
1987 if (cursor_path == NULL)
1990 gtk_tree_view_draw_node_focus_rect (widget, cursor_path);
1995 gtk_tree_view_focus_in (GtkWidget *widget,
1996 GdkEventFocus *event)
1998 GtkTreeView *tree_view;
2000 g_return_val_if_fail (widget != NULL, FALSE);
2001 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
2002 g_return_val_if_fail (event != NULL, FALSE);
2004 tree_view = GTK_TREE_VIEW (widget);
2006 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2008 /* FIXME don't redraw so much */
2009 gtk_widget_queue_draw (widget);
2016 gtk_tree_view_focus_out (GtkWidget *widget,
2017 GdkEventFocus *event)
2019 g_return_val_if_fail (widget != NULL, FALSE);
2020 g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
2021 g_return_val_if_fail (event != NULL, FALSE);
2023 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2025 /* FIXME don't redraw so much */
2026 gtk_widget_queue_draw (widget);
2031 /* FIXME: It would be neat to someday make the headers a seperate widget that
2032 * can be shared between various apps. Wishful thinking, though...
2034 /* Returns TRUE if the focus is within the headers, after the focus operation is
2038 gtk_tree_view_header_focus (GtkTreeView *tree_view,
2039 GtkDirectionType dir)
2041 GtkWidget *focus_child;
2042 GtkContainer *container;
2044 GList *last_column, *first_column;
2047 if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE))
2050 focus_child = GTK_CONTAINER (tree_view)->focus_child;
2051 container = GTK_CONTAINER (tree_view);
2053 for (last_column = g_list_last (tree_view->priv->columns);
2055 !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) &&
2056 GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button);
2057 last_column = last_column->prev)
2060 for (first_column = tree_view->priv->columns;
2062 !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible) &&
2063 GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (first_column->data)->button);
2064 first_column = first_column->next)
2067 /* no headers are visible, or are focussable. We can't focus in or out.
2068 * I wonder if focussable is a real word...
2070 if (last_column == NULL)
2073 /* First thing we want to handle is entering and leaving the headers.
2077 case GTK_DIR_TAB_BACKWARD:
2080 focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
2081 gtk_widget_grab_focus (focus_child);
2084 if (focus_child == GTK_TREE_VIEW_COLUMN (first_column->data)->button)
2091 case GTK_DIR_TAB_FORWARD:
2094 focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
2095 gtk_widget_grab_focus (focus_child);
2098 if (focus_child == GTK_TREE_VIEW_COLUMN (last_column->data)->button)
2108 focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button;
2109 gtk_widget_grab_focus (focus_child);
2112 if (focus_child == GTK_TREE_VIEW_COLUMN (first_column->data)->button)
2122 focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
2123 gtk_widget_grab_focus (focus_child);
2126 if (focus_child == GTK_TREE_VIEW_COLUMN (last_column->data)->button)
2136 focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
2137 gtk_widget_grab_focus (focus_child);
2148 focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button;
2149 gtk_widget_grab_focus (focus_child);
2158 /* We need to move the focus to the next button. */
2161 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2162 if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child)
2164 if (gtk_container_focus (GTK_CONTAINER (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button), dir))
2166 /* The focus moves inside the button. */
2167 /* This is probably a great example of bad UI */
2173 /* We need to move the focus among the row of buttons. */
2176 GtkTreeViewColumn *column;
2178 if (dir == GTK_DIR_RIGHT || dir == GTK_DIR_TAB_FORWARD)
2179 tmp_list = tmp_list->next;
2181 tmp_list = tmp_list->prev;
2183 if (tmp_list == NULL)
2185 g_warning ("Internal button not found");
2188 column = tmp_list->data;
2189 if (column->button &&
2191 GTK_WIDGET_CAN_FOCUS (column->button))
2193 focus_child = column->button;
2194 gtk_widget_grab_focus (column->button);
2201 /* if focus child is non-null, we assume it's been set to the current focus child
2205 /* If the following isn't true, then the view is smaller then the scrollpane.
2207 if ((focus_child->allocation.x + focus_child->allocation.width) <=
2208 (tree_view->priv->hadjustment->upper))
2210 /* Scroll to the button, if needed */
2211 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) <
2212 (focus_child->allocation.x + focus_child->allocation.width))
2213 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2214 focus_child->allocation.x + focus_child->allocation.width -
2215 tree_view->priv->hadjustment->page_size);
2216 else if (tree_view->priv->hadjustment->value > focus_child->allocation.x)
2217 gtk_adjustment_set_value (tree_view->priv->hadjustment,
2218 focus_child->allocation.x);
2222 return (focus_child != NULL);
2225 /* WARNING: Scary function */
2227 gtk_tree_view_focus (GtkContainer *container,
2228 GtkDirectionType direction)
2230 GtkTreeView *tree_view;
2231 GtkWidget *focus_child;
2233 GtkRBTree *cursor_tree;
2234 GtkRBNode *cursor_node;
2235 GtkTreePath *cursor_path;
2237 g_return_val_if_fail (container != NULL, FALSE);
2238 g_return_val_if_fail (GTK_IS_TREE_VIEW (container), FALSE);
2239 g_return_val_if_fail (GTK_WIDGET_VISIBLE (container), FALSE);
2241 tree_view = GTK_TREE_VIEW (container);
2243 if (!GTK_WIDGET_IS_SENSITIVE (container))
2245 if (tree_view->priv->tree == NULL)
2248 focus_child = container->focus_child;
2250 /* Case 1. Headers have focus. */
2256 case GTK_DIR_TAB_BACKWARD:
2257 return (gtk_tree_view_header_focus (tree_view, direction));
2260 case GTK_DIR_TAB_FORWARD:
2263 if (direction != GTK_DIR_DOWN)
2265 if (gtk_tree_view_header_focus (tree_view, direction))
2268 GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
2269 gtk_widget_grab_focus (GTK_WIDGET (container));
2271 if (tree_view->priv->selection == NULL)
2272 tree_view->priv->selection =
2273 _gtk_tree_selection_new_with_tree_view (tree_view);
2275 /* if there is no keyboard focus yet, we select the first node
2280 if (tree_view->priv->cursor)
2281 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
2283 if (cursor_path == NULL)
2285 GtkTreePath *tmp_path = gtk_tree_path_new_root ();
2287 if (tree_view->priv->cursor)
2288 gtk_tree_row_reference_free (tree_view->priv->cursor);
2290 tree_view->priv->cursor =
2291 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path);
2292 cursor_path = tmp_path;
2295 gtk_tree_selection_select_path (tree_view->priv->selection,
2298 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
2300 gtk_tree_path_free (cursor_path);
2306 /* Case 2. We don't have focus at all. */
2307 if (!GTK_WIDGET_HAS_FOCUS (container))
2309 if ((direction == GTK_DIR_TAB_FORWARD) ||
2310 (direction == GTK_DIR_RIGHT) ||
2311 (direction == GTK_DIR_DOWN))
2313 if (gtk_tree_view_header_focus (tree_view, direction))
2317 /* The headers didn't want the focus, so we take it. */
2318 GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
2319 gtk_widget_grab_focus (GTK_WIDGET (container));
2321 if (tree_view->priv->selection == NULL)
2322 tree_view->priv->selection =
2323 _gtk_tree_selection_new_with_tree_view (tree_view);
2326 if (tree_view->priv->cursor)
2327 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
2329 if (cursor_path == NULL)
2331 GtkTreePath *tmp_path = gtk_tree_path_new_root ();
2333 if (tree_view->priv->cursor)
2334 gtk_tree_row_reference_free (tree_view->priv->cursor);
2336 tree_view->priv->cursor =
2337 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path);
2338 cursor_path = tmp_path;
2341 gtk_tree_selection_select_path (tree_view->priv->selection,
2344 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
2346 gtk_tree_path_free (cursor_path);
2352 if (tree_view->priv->cursor)
2353 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
2355 /* Case 3. We have focus already, but no cursor. We pick the first one
2359 if (cursor_path == NULL)
2361 GtkTreePath *tmp_path = gtk_tree_path_new_root ();
2363 if (tree_view->priv->cursor)
2364 gtk_tree_row_reference_free (tree_view->priv->cursor);
2366 tree_view->priv->cursor =
2367 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path);
2368 cursor_path = tmp_path;
2370 gtk_tree_selection_select_path (tree_view->priv->selection,
2373 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
2375 gtk_tree_path_free (cursor_path);
2380 /* Case 4. We have focus already. Move the cursor. */
2381 if (direction == GTK_DIR_LEFT)
2384 val = tree_view->priv->hadjustment->value - tree_view->priv->hadjustment->page_size/2;
2385 val = MAX (val, 0.0);
2386 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val);
2387 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
2389 gtk_tree_path_free (cursor_path);
2393 if (direction == GTK_DIR_RIGHT)
2396 val = tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size/2;
2397 val = MIN (tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size, val);
2398 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val);
2399 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
2401 gtk_tree_path_free (cursor_path);
2409 _gtk_tree_view_find_node (tree_view, cursor_path,
2413 /* undraw the old row */
2414 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
2416 gtk_tree_path_free (cursor_path);
2419 if (tree_view->priv->cursor)
2421 gtk_tree_row_reference_free (tree_view->priv->cursor);
2422 tree_view->priv->cursor = NULL;
2427 case GTK_DIR_TAB_BACKWARD:
2429 _gtk_rbtree_prev_full (cursor_tree,
2434 case GTK_DIR_TAB_FORWARD:
2436 _gtk_rbtree_next_full (cursor_tree,
2447 GdkModifierType state = 0;
2449 event = gtk_get_current_event ();
2451 gdk_event_get_state (event, &state);
2454 gdk_event_free (event);
2456 cursor_path = _gtk_tree_view_find_path (tree_view,
2462 _gtk_tree_selection_internal_select_node (tree_view->priv->selection,
2468 tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, cursor_path);
2471 /* draw the newly-selected row */
2472 gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
2474 gtk_tree_path_free (cursor_path);
2477 gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node);
2478 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
2483 /* At this point, we've progressed beyond the edge of the rows. */
2485 if ((direction == GTK_DIR_LEFT) ||
2486 (direction == GTK_DIR_TAB_BACKWARD) ||
2487 (direction == GTK_DIR_UP))
2488 /* We can't go back anymore. Try the headers */
2489 return (gtk_tree_view_header_focus (tree_view, direction));
2491 /* we've reached the end of the tree. Go on. */
2498 gtk_tree_view_remove (GtkContainer *container,
2501 GtkTreeView *tree_view;
2502 GtkTreeViewChild *child = NULL;
2505 g_return_if_fail (container != NULL);
2506 g_return_if_fail (GTK_IS_TREE_VIEW (container));
2508 tree_view = GTK_TREE_VIEW (container);
2510 tmp_list = tree_view->priv->children;
2513 child = tmp_list->data;
2514 if (child->widget == widget)
2516 gtk_widget_unparent (widget);
2518 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
2519 g_list_free_1 (tmp_list);
2524 tmp_list = tmp_list->next;
2527 tmp_list = tree_view->priv->columns;
2531 GtkTreeViewColumn *column;
2532 column = tmp_list->data;
2533 if (column->button == widget)
2535 gtk_widget_unparent (widget);
2538 tmp_list = tmp_list->next;
2544 gtk_tree_view_forall (GtkContainer *container,
2545 gboolean include_internals,
2546 GtkCallback callback,
2547 gpointer callback_data)
2549 GtkTreeView *tree_view;
2550 GtkTreeViewChild *child = NULL;
2551 GtkTreeViewColumn *column;
2554 g_return_if_fail (container != NULL);
2555 g_return_if_fail (GTK_IS_TREE_VIEW (container));
2556 g_return_if_fail (callback != NULL);
2558 tree_view = GTK_TREE_VIEW (container);
2560 tmp_list = tree_view->priv->children;
2563 child = tmp_list->data;
2564 tmp_list = tmp_list->next;
2566 (* callback) (child->widget, callback_data);
2568 if (include_internals == FALSE)
2571 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2573 column = tmp_list->data;
2576 (* callback) (column->button, callback_data);
2580 /* TreeModel Callbacks
2584 gtk_tree_view_changed (GtkTreeModel *model,
2589 GtkTreeView *tree_view = (GtkTreeView *)data;
2593 gboolean dirty_marked;
2594 gboolean free_path = FALSE;
2596 g_return_if_fail (path != NULL || iter != NULL);
2600 path = gtk_tree_model_get_path (model, iter);
2603 else if (iter == NULL)
2604 gtk_tree_model_get_iter (model, iter, path);
2606 if (_gtk_tree_view_find_node (tree_view,
2610 /* We aren't actually showing the node */
2616 dirty_marked = gtk_tree_view_discover_dirty_iter (tree_view,
2618 gtk_tree_path_get_depth (path),
2621 if (GTK_RBNODE_GET_HEIGHT (node) != height + TREE_VIEW_VERTICAL_SEPARATOR)
2623 _gtk_rbtree_node_set_height (tree, node, height + TREE_VIEW_VERTICAL_SEPARATOR);
2624 gtk_widget_queue_resize (GTK_WIDGET (data));
2628 gtk_widget_queue_resize (GTK_WIDGET (data));
2631 gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL);
2636 gtk_tree_path_free (path);
2640 gtk_tree_view_inserted (GtkTreeModel *model,
2645 GtkTreeView *tree_view = (GtkTreeView *) data;
2647 GtkRBTree *tmptree, *tree;
2648 GtkRBNode *tmpnode = NULL;
2652 gboolean free_path = FALSE;
2654 tmptree = tree = tree_view->priv->tree;
2655 g_return_if_fail (path != NULL || iter != NULL);
2659 path = gtk_tree_model_get_path (model, iter);
2662 else if (iter == NULL)
2663 gtk_tree_model_get_iter (model, iter, path);
2665 /* Update all row-references */
2666 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
2668 depth = gtk_tree_path_get_depth (path);
2669 indices = gtk_tree_path_get_indices (path);
2671 /* First, find the parent tree */
2672 while (i < depth - 1)
2674 if (tmptree == NULL)
2676 /* We aren't showing the node */
2680 tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
2681 if (tmpnode == NULL)
2683 g_warning ("A node was inserted with a parent that's not in the tree.\n" \
2684 "This possibly means that a GtkTreeModel inserted a child node\n" \
2685 "before the parent was inserted.");
2688 else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT))
2690 /* FIXME enforce correct behavior on model, probably */
2691 /* In theory, the model should have emitted has_child_toggled here. We
2692 * try to catch it anyway, just to be safe, in case the model hasn't.
2694 GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view,
2697 gtk_tree_view_has_child_toggled (model, tmppath, NULL, data);
2698 gtk_tree_path_free (tmppath);
2702 tmptree = tmpnode->children;
2711 gtk_tree_model_ref_iter (tree_view->priv->model, iter);
2712 max_height = gtk_tree_view_insert_iter_height (tree_view,
2716 if (indices[depth - 1] == 0)
2718 tmpnode = _gtk_rbtree_find_count (tree, 1);
2719 _gtk_rbtree_insert_before (tree, tmpnode, max_height);
2723 tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]);
2724 _gtk_rbtree_insert_after (tree, tmpnode, max_height);
2727 _gtk_tree_view_update_size (tree_view);
2731 gtk_tree_path_free (path);
2735 gtk_tree_view_has_child_toggled (GtkTreeModel *model,
2740 GtkTreeView *tree_view = (GtkTreeView *)data;
2741 GtkTreeIter real_iter;
2745 gboolean free_path = FALSE;
2747 g_return_if_fail (path != NULL || iter != NULL);
2754 path = gtk_tree_model_get_path (model, iter);
2757 else if (iter == NULL)
2758 gtk_tree_model_get_iter (model, &real_iter, path);
2760 if (_gtk_tree_view_find_node (tree_view,
2764 /* We aren't actually showing the node */
2770 has_child = gtk_tree_model_iter_has_child (model, &real_iter);
2773 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child)
2777 GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT);
2779 GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT);
2781 if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST))
2783 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
2784 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS))
2787 for (list = tree_view->priv->columns; list; list = list->next)
2788 if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
2790 GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE;
2794 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
2798 /* FIXME: Just redraw the node */
2799 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2804 gtk_tree_path_free (path);
2808 gtk_tree_view_deleted (GtkTreeModel *model,
2812 GtkTreeView *tree_view = (GtkTreeView *)data;
2817 g_return_if_fail (path != NULL);
2819 if (_gtk_tree_view_find_node (tree_view, path, &tree, &node))
2825 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
2827 /* next, update the selection */
2828 if (tree_view->priv->anchor)
2830 GtkTreePath *anchor_path;
2832 /* the row reference may not have been updated yet. If it has not,
2833 * then anchor_path and path being equal indicates that the anchor
2834 * row was deleted. If it has, then anchor_path == NULL indicates the
2835 * the anchor row was deleted.
2838 anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor);
2840 if (anchor_path == NULL ||
2841 gtk_tree_path_compare (path, anchor_path) == 0)
2843 if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) &&
2844 tree_view->priv->selection)
2845 gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->selection),
2846 "selection_changed");
2850 gtk_tree_path_free (anchor_path);
2853 for (list = tree_view->priv->columns; list; list = list->next)
2854 if (((GtkTreeViewColumn *)list->data)->visible &&
2855 ((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
2856 ((GtkTreeViewColumn *)list->data)->dirty = TRUE;
2858 /* Ensure we don't have a dangling pointer to a dead node */
2859 ensure_unprelighted (tree_view);
2861 g_assert (tree_view->priv->prelight_node == NULL);
2863 if ((tree->root->count == 1) &&
2864 (tree_view->priv->tree != tree))
2866 _gtk_rbtree_remove (tree);
2870 _gtk_rbtree_remove_node (tree, node);
2873 _gtk_tree_view_update_size (GTK_TREE_VIEW (data));
2876 /* Internal tree functions */
2878 gtk_tree_view_insert_iter_height (GtkTreeView *tree_view,
2883 GtkTreeViewColumn *column;
2884 GtkCellRenderer *cell;
2886 gint max_height = 0;
2891 /* do stuff with node */
2892 for (list = tree_view->priv->columns; list; list = list->next)
2894 gint height = 0, width = 0;
2895 column = list->data;
2897 if (!column->visible)
2900 if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
2906 cell = column->cell;
2907 gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter);
2909 gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height);
2910 max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPARATOR + height);
2912 if (i == tree_view->priv->expander_column &&
2913 TREE_VIEW_DRAW_EXPANDERS (tree_view))
2914 gtk_tree_view_column_set_width (column,
2915 MAX (column->width, depth * tree_view->priv->tab_offset + width));
2917 gtk_tree_view_column_set_width (column,
2918 MAX (column->width, width));
2926 gtk_tree_view_build_tree (GtkTreeView *tree_view,
2931 gboolean calc_bounds)
2933 GtkRBNode *temp = NULL;
2940 max_height = gtk_tree_view_insert_iter_height (tree_view,
2945 gtk_tree_model_ref_iter (tree_view->priv->model, iter);
2946 temp = _gtk_rbtree_insert_after (tree, temp, max_height);
2951 if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter))
2953 temp->children = _gtk_rbtree_new ();
2954 temp->children->parent_tree = tree;
2955 temp->children->parent_node = temp;
2956 gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse, calc_bounds);
2959 if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter))
2961 if ((temp->flags>K_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT)
2962 temp->flags ^= GTK_RBNODE_IS_PARENT;
2963 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST);
2966 while (gtk_tree_model_iter_next (tree_view->priv->model, iter));
2970 gtk_tree_view_calc_size (GtkTreeView *tree_view,
2977 GtkCellRenderer *cell;
2979 GtkTreeViewColumn *column;
2983 TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL);
2986 while (temp->left != tree->nil)
2992 /* Do stuff with node */
2993 for (list = tree_view->priv->columns, i = 0; i < tree_view->priv->n_columns; list = list->next, i++)
2995 gint height = 0, width = 0;
2996 column = list->data;
2998 if (!column->visible)
3001 gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter);
3002 cell = column->cell;
3003 gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height);
3004 max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPARATOR + height);
3006 /* FIXME: I'm getting the width of all nodes here. )-: */
3007 if (column->dirty == FALSE)
3010 if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
3014 if (i == tree_view->priv->expander_column &&
3015 TREE_VIEW_DRAW_EXPANDERS (tree_view))
3016 gtk_tree_view_column_set_width (column,
3017 MAX (column->width, depth * tree_view->priv->tab_offset + width));
3019 gtk_tree_view_column_set_width (column, MAX (column->width, width));
3021 _gtk_rbtree_node_set_height (tree, temp, max_height);
3022 if (temp->children != NULL &&
3023 gtk_tree_model_iter_children (tree_view->priv->model, &child, iter))
3024 gtk_tree_view_calc_size (tree_view, temp->children, &child, depth + 1);
3025 temp = _gtk_rbtree_next (tree, temp);
3027 while (gtk_tree_model_iter_next (tree_view->priv->model, iter));
3031 gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view,
3036 GtkCellRenderer *cell;
3037 GtkTreeViewColumn *column;
3040 gint retval = FALSE;
3046 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
3049 column = list->data;
3050 if (column->dirty == TRUE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED)
3052 if (!column->visible)
3055 cell = column->cell;
3056 gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter);
3060 gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &tmpheight);
3061 *height = MAX (*height, tmpheight);
3065 gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, NULL);
3067 if (i == tree_view->priv->expander_column &&
3068 TREE_VIEW_DRAW_EXPANDERS (tree_view))
3070 if (depth * tree_view->priv->tab_offset + width > column->width)
3072 column->dirty = TRUE;
3078 if (width > column->width)
3080 column->dirty = TRUE;
3090 gtk_tree_view_discover_dirty (GtkTreeView *tree_view,
3095 GtkRBNode *temp = tree->root;
3096 GtkTreeViewColumn *column;
3099 gboolean is_all_dirty;
3101 TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL);
3103 while (temp->left != tree->nil)
3108 is_all_dirty = TRUE;
3109 for (list = tree_view->priv->columns; list; list = list->next)
3111 column = list->data;
3112 if (column->dirty == FALSE)
3114 is_all_dirty = FALSE;
3122 gtk_tree_view_discover_dirty_iter (tree_view,
3126 if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter) &&
3127 temp->children != NULL)
3128 gtk_tree_view_discover_dirty (tree_view, temp->children, &child, depth + 1);
3129 temp = _gtk_rbtree_next (tree, temp);
3131 while (gtk_tree_model_iter_next (tree_view->priv->model, iter));
3136 gtk_tree_view_check_dirty (GtkTreeView *tree_view)
3139 gboolean dirty = FALSE;
3141 GtkTreeViewColumn *column;
3144 if (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP) &&
3145 tree_view->priv->model)
3146 gtk_tree_view_setup_model (tree_view);
3148 for (list = tree_view->priv->columns; list; list = list->next)
3150 column = list->data;
3154 if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
3159 w = MAX (w, column->button->requisition.width);
3161 gtk_tree_view_column_set_width (column, w);
3169 if (tree_view->priv->model == NULL)
3172 path = gtk_tree_path_new_root ();
3173 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path))
3175 gtk_tree_view_calc_size (tree_view, tree_view->priv->tree, &iter, 1);
3176 _gtk_tree_view_update_size (tree_view);
3179 gtk_tree_path_free (path);
3181 for (list = tree_view->priv->columns; list; list = list->next)
3183 column = list->data;
3184 column->dirty = FALSE;
3189 gtk_tree_view_create_buttons (GtkTreeView *tree_view)
3191 GtkWidget *alignment;
3194 GtkTreeViewColumn *column;
3199 /* FIXME this has to be merged with update_button_contents() in
3200 * gtktreeviewcolumn.c
3203 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
3205 column = list->data;
3207 if (column->button != NULL)
3210 gtk_tree_view_create_button (tree_view, i);
3211 alignment = gtk_alignment_new (column->xalign, 0.5, 0.0, 0.0);
3213 hbox = gtk_hbox_new (FALSE, 2);
3214 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
3216 column->arrow = arrow;
3217 column->alignment = alignment;
3220 label = column->child;
3223 label = gtk_label_new (column->title);
3224 gtk_widget_show (label);
3227 if (column->xalign <= 0.5)
3228 gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
3230 gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
3232 gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 0);
3234 gtk_container_add (GTK_CONTAINER (alignment), label);
3235 gtk_container_add (GTK_CONTAINER (column->button), hbox);
3237 gtk_widget_show (hbox);
3238 gtk_widget_show (alignment);
3239 /* don't show the arrow yet */
3242 gtk_tree_view_size_request_buttons (tree_view);
3244 if (GTK_WIDGET_REALIZED (tree_view))
3245 gtk_tree_view_realize_buttons (tree_view);
3247 if (GTK_WIDGET_MAPPED (tree_view))
3248 gtk_tree_view_map_buttons (tree_view);
3252 /* Make sure the node is visible vertically */
3254 gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view,
3260 offset = _gtk_rbtree_node_find_offset (tree, node);
3262 /* we reverse the order, b/c in the unusual case of the
3263 * node's height being taller then the visible area, we'd rather
3264 * have the node flush to the top
3266 if (offset + GTK_RBNODE_GET_HEIGHT (node) >
3267 tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size)
3268 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
3269 offset + GTK_RBNODE_GET_HEIGHT (node) -
3270 tree_view->priv->vadjustment->page_size);
3271 if (offset < tree_view->priv->vadjustment->value)
3272 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
3276 /* This function could be more efficient.
3277 * I'll optimize it if profiling seems to imply that
3281 _gtk_tree_view_find_path (GtkTreeView *tree_view,
3286 GtkRBTree *tmp_tree;
3287 GtkRBNode *tmp_node, *last;
3290 path = gtk_tree_path_new ();
3292 g_return_val_if_fail (node != NULL, path);
3293 g_return_val_if_fail (node != tree->nil, path);
3295 count = 1 + node->left->count;
3298 tmp_node = node->parent;
3302 while (tmp_node != tmp_tree->nil)
3304 if (tmp_node->right == last)
3305 count += 1 + tmp_node->left->count;
3307 tmp_node = tmp_node->parent;
3309 gtk_tree_path_prepend_index (path, count - 1);
3310 last = tmp_tree->parent_node;
3311 tmp_tree = tmp_tree->parent_tree;
3314 count = 1 + last->left->count;
3315 tmp_node = last->parent;
3321 /* Returns TRUE if we ran out of tree before finding the node,
3322 * so the returned node is the last node we saw and the returned
3326 _gtk_tree_view_find_node (GtkTreeView *tree_view,
3331 GtkRBNode *tmpnode = NULL;
3332 GtkRBTree *tmptree = tree_view->priv->tree;
3333 gint *indices = gtk_tree_path_get_indices (path);
3334 gint depth = gtk_tree_path_get_depth (path);
3342 if (tmptree == NULL)
3348 tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1);
3356 tmptree = tmpnode->children;
3362 gtk_tree_view_queue_draw_node (GtkTreeView *tree_view,
3365 GdkRectangle *clip_rect)
3369 if (!GTK_WIDGET_REALIZED (tree_view))
3373 rect.width = tree_view->priv->width;
3375 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
3376 rect.height = BACKGROUND_HEIGHT (node);
3380 GdkRectangle new_rect;
3382 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
3384 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
3388 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
3393 gtk_tree_view_queue_draw_path (GtkTreeView *tree_view,
3395 GdkRectangle *clip_rect)
3397 GtkRBTree *tree = NULL;
3398 GtkRBNode *node = NULL;
3400 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
3403 gtk_tree_view_queue_draw_node (tree_view, tree, node, clip_rect);
3406 /* x and y are the mouse position
3409 gtk_tree_view_draw_arrow (GtkTreeView *tree_view,
3420 if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT))
3423 widget = GTK_WIDGET (tree_view);
3425 gtk_tree_view_get_arrow_xrange (tree_view, &x_offset, NULL);
3428 area.y = CELL_FIRST_PIXEL (tree_view, tree, node);
3429 area.width = tree_view->priv->tab_offset - 2;
3430 area.height = CELL_HEIGHT (node);
3432 if (node == tree_view->priv->button_pressed_node)
3434 if (x >= area.x && x <= (area.x + area.width) &&
3435 y >= area.y && y <= (area.y + area.height))
3436 state = GTK_STATE_ACTIVE;
3438 state = GTK_STATE_NORMAL;
3442 if (node == tree_view->priv->prelight_node &&
3443 GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT))
3444 state = GTK_STATE_PRELIGHT;
3446 state = GTK_STATE_NORMAL;
3449 /* FIXME expander size should come from a style property */
3450 #define EXPANDER_SIZE 8
3451 gtk_paint_expander (widget->style,
3452 tree_view->priv->bin_window,
3458 (area.y + (area.height - EXPANDER_SIZE) / 2 - (area.height + 1) % 2),
3459 node->children != NULL);
3460 #undef EXPANDER_SIZE
3465 _gtk_tree_view_update_col_width (GtkTreeView *tree_view)
3467 GList *list, *last_column;
3468 GtkTreeViewColumn *column;
3471 for (last_column = g_list_last (tree_view->priv->columns);
3473 !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) &&
3474 GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button);
3475 last_column = last_column->prev)
3478 if (last_column == NULL)
3481 for (list = tree_view->priv->columns; list != last_column; list = list->next)
3483 column = GTK_TREE_VIEW_COLUMN (list->data);
3484 if (! column->visible)
3487 width += column->width;
3488 column->displayed_width = (CLAMP (column->width, (column->min_width!=-1)?column->min_width:column->width, (column->max_width!=-1)?column->max_width:column->width));
3490 column = GTK_TREE_VIEW_COLUMN (last_column->data);
3491 column->displayed_width = MAX (GTK_WIDGET (tree_view)->allocation.width, tree_view->priv->width) - width;
3495 _gtk_tree_view_update_size (GtkTreeView *tree_view)
3499 GtkTreeViewColumn *column;
3502 if (tree_view->priv->model == NULL)
3504 tree_view->priv->width = 0;
3505 tree_view->priv->height = 0;
3506 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3511 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
3513 column = list->data;
3514 if (!column->visible)
3516 width += TREE_VIEW_COLUMN_WIDTH (column);
3519 if (tree_view->priv->tree == NULL)
3522 height = tree_view->priv->tree->root->offset + TREE_VIEW_VERTICAL_SEPARATOR;
3524 if (tree_view->priv->width != width)
3526 tree_view->priv->width = width;
3527 tree_view->priv->hadjustment->upper = width;
3528 gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed");
3531 if (tree_view->priv->height != height)
3533 tree_view->priv->height = height;
3534 tree_view->priv->vadjustment->upper = tree_view->priv->height;
3535 gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
3538 if (GTK_WIDGET_REALIZED (tree_view))
3540 gdk_window_resize (tree_view->priv->bin_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), height + TREE_VIEW_HEADER_HEIGHT (tree_view));
3541 gdk_window_resize (tree_view->priv->header_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), tree_view->priv->header_height);
3543 _gtk_tree_view_update_col_width (tree_view);
3546 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
3549 /* this function returns the new width of the column being resized given
3550 * the column and x position of the cursor; the x cursor position is passed
3551 * in as a pointer and automagicly corrected if it's beyond min/max limits
3554 gtk_tree_view_new_column_width (GtkTreeView *tree_view,
3558 GtkTreeViewColumn *column;
3561 /* first translate the x position from widget->window
3562 * to clist->clist_window
3565 column = g_list_nth (tree_view->priv->columns, i)->data;
3566 width = *x - column->button->allocation.x;
3568 /* Clamp down the value */
3569 if (column->min_width == -1)
3570 width = MAX (column->button->requisition.width,
3573 width = MAX (column->min_width,
3575 if (column->max_width != -1)
3576 width = MIN (width, column->max_width != -1);
3577 *x = column->button->allocation.x + width;
3584 gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment,
3585 GtkTreeView *tree_view)
3587 if (GTK_WIDGET_REALIZED (tree_view))
3589 gdk_window_move (tree_view->priv->bin_window,
3590 - tree_view->priv->hadjustment->value,
3591 - tree_view->priv->vadjustment->value);
3592 gdk_window_move (tree_view->priv->header_window,
3593 - tree_view->priv->hadjustment->value,
3596 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
3597 gdk_window_process_updates (tree_view->priv->header_window, TRUE);
3607 * gtk_tree_view_new:
3609 * Creates a new #GtkTreeView widget.
3611 * Return value: A newly created #GtkTreeView widget.
3614 gtk_tree_view_new (void)
3616 GtkTreeView *tree_view;
3618 tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ()));
3620 return GTK_WIDGET (tree_view);
3624 * gtk_tree_view_new_with_model:
3625 * @model: the model.
3627 * Creates a new #GtkTreeView widget with the model initialized to @model.
3629 * Return value: A newly created #GtkTreeView widget.
3632 gtk_tree_view_new_with_model (GtkTreeModel *model)
3634 GtkTreeView *tree_view;
3636 tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ()));
3637 gtk_tree_view_set_model (tree_view, model);
3639 return GTK_WIDGET (tree_view);
3643 * gtk_tree_view_get_model:
3644 * @tree_view: a #GtkTreeView
3646 * Returns the model the the #GtkTreeView is based on. Returns NULL if the
3649 * Return value: A #GtkTreeModel, or NULL if none is currently being used.
3652 gtk_tree_view_get_model (GtkTreeView *tree_view)
3654 g_return_val_if_fail (tree_view != NULL, NULL);
3655 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
3657 return tree_view->priv->model;
3661 gtk_tree_view_setup_model (GtkTreeView *tree_view)
3666 tree_view->priv->tree = _gtk_rbtree_new ();
3668 g_signal_connect (tree_view->priv->model,
3670 gtk_tree_view_changed,
3672 g_signal_connect (tree_view->priv->model,
3674 gtk_tree_view_inserted,
3676 g_signal_connect (tree_view->priv->model,
3677 "has_child_toggled",
3678 gtk_tree_view_has_child_toggled,
3680 g_signal_connect (tree_view->priv->model,
3682 gtk_tree_view_deleted,
3685 if (tree_view->priv->columns == NULL)
3688 path = gtk_tree_path_new_root ();
3690 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path))
3692 gtk_tree_view_build_tree (tree_view, tree_view->priv->tree, &iter, 1, FALSE, GTK_WIDGET_REALIZED (tree_view));
3695 gtk_tree_path_free (path);
3697 // gtk_tree_view_create_buttons (tree_view);
3699 GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP);
3703 * gtk_tree_view_set_model:
3704 * @tree_view: A #GtkTreeNode.
3705 * @model: The model.
3707 * Sets the model for a #GtkTreeView. If the @tree_view already has a model
3708 * set, it will remove it before setting the new model. If @model is NULL, then
3709 * it will unset the old model.
3712 gtk_tree_view_set_model (GtkTreeView *tree_view,
3713 GtkTreeModel *model)
3715 g_return_if_fail (tree_view != NULL);
3716 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
3718 if (tree_view->priv->model != NULL)
3720 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP))
3722 g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model),
3723 G_SIGNAL_MATCH_FUNC,
3725 gtk_tree_view_changed, NULL);
3726 g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model),
3727 G_SIGNAL_MATCH_FUNC,
3729 gtk_tree_view_inserted, NULL);
3730 g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model),
3731 G_SIGNAL_MATCH_FUNC,
3733 gtk_tree_view_has_child_toggled, NULL);
3734 g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model),
3735 G_SIGNAL_MATCH_FUNC,
3737 gtk_tree_view_deleted, NULL);
3738 _gtk_rbtree_free (tree_view->priv->tree);
3741 if (tree_view->priv->drag_dest_row)
3742 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
3744 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP);
3747 tree_view->priv->model = model;
3750 tree_view->priv->tree = NULL;
3751 if (GTK_WIDGET_REALIZED (tree_view))
3752 _gtk_tree_view_update_size (tree_view);
3755 else if (GTK_WIDGET_REALIZED (tree_view))
3757 gtk_tree_view_setup_model (tree_view);
3758 _gtk_tree_view_update_size (tree_view);
3761 g_object_notify (G_OBJECT (tree_view), "model");
3765 * gtk_tree_view_get_selection:
3766 * @tree_view: A #GtkTreeView.
3768 * Gets the #GtkTreeSelection associated with @tree_view.
3770 * Return value: A #GtkTreeSelection object.
3773 gtk_tree_view_get_selection (GtkTreeView *tree_view)
3775 g_return_val_if_fail (tree_view != NULL, NULL);
3776 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
3778 if (tree_view->priv->selection == NULL)
3779 tree_view->priv->selection =
3780 _gtk_tree_selection_new_with_tree_view (tree_view);
3782 return tree_view->priv->selection;
3786 * gtk_tree_view_get_hadjustment:
3787 * @tree_view: A #GtkTreeView
3789 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
3791 * Return value: A #GtkAdjustment object, or NULL if none is currently being
3795 gtk_tree_view_get_hadjustment (GtkTreeView *tree_view)
3797 g_return_val_if_fail (tree_view != NULL, NULL);
3798 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
3800 if (tree_view->priv->hadjustment == NULL)
3801 gtk_tree_view_set_hadjustment (tree_view, NULL);
3803 return tree_view->priv->hadjustment;
3807 * gtk_tree_view_set_hadjustment:
3808 * @tree_view: A #GtkTreeView
3809 * @adjustment: The #GtkAdjustment to set, or NULL
3811 * Sets the #GtkAdjustment for the current horizontal aspect.
3814 gtk_tree_view_set_hadjustment (GtkTreeView *tree_view,
3815 GtkAdjustment *adjustment)
3817 g_return_if_fail (tree_view != NULL);
3818 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
3820 gtk_tree_view_set_adjustments (tree_view,
3822 tree_view->priv->vadjustment);
3824 g_object_notify (G_OBJECT (tree_view), "hadjustment");
3828 * gtk_tree_view_get_vadjustment:
3829 * @tree_view: A #GtkTreeView
3831 * Gets the #GtkAdjustment currently being used for the vertical aspect.
3833 * Return value: A #GtkAdjustment object, or NULL if none is currently being
3837 gtk_tree_view_get_vadjustment (GtkTreeView *tree_view)
3839 g_return_val_if_fail (tree_view != NULL, NULL);
3840 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
3842 if (tree_view->priv->vadjustment == NULL)
3843 gtk_tree_view_set_vadjustment (tree_view, NULL);
3845 return tree_view->priv->vadjustment;
3849 * gtk_tree_view_set_vadjustment:
3850 * @tree_view: A #GtkTreeView
3851 * @adjustment: The #GtkAdjustment to set, or NULL
3853 * Sets the #GtkAdjustment for the current vertical aspect.
3856 gtk_tree_view_set_vadjustment (GtkTreeView *tree_view,
3857 GtkAdjustment *adjustment)
3859 g_return_if_fail (tree_view != NULL);
3860 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
3862 gtk_tree_view_set_adjustments (tree_view,
3863 tree_view->priv->hadjustment,
3866 g_object_notify (G_OBJECT (tree_view), "vadjustment");
3870 * gtk_tree_view_set_adjustments:
3871 * @tree_view: A #GtkTreeView
3872 * @hadj: The horizontal #GtkAdjustment to set, or NULL
3873 * @vadj: The vertical #GtkAdjustment to set, or NULL
3875 * Sets the horizonal and or vertical #GtkAdjustment.
3878 gtk_tree_view_set_adjustments (GtkTreeView *tree_view,
3879 GtkAdjustment *hadj,
3880 GtkAdjustment *vadj)
3882 gboolean need_adjust = FALSE;
3884 g_return_if_fail (tree_view != NULL);
3885 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
3888 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
3890 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3892 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
3894 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3896 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
3898 gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->hadjustment), tree_view);
3899 gtk_object_unref (GTK_OBJECT (tree_view->priv->hadjustment));
3902 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
3904 gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->vadjustment), tree_view);
3905 gtk_object_unref (GTK_OBJECT (tree_view->priv->vadjustment));
3908 if (tree_view->priv->hadjustment != hadj)
3910 tree_view->priv->hadjustment = hadj;
3911 gtk_object_ref (GTK_OBJECT (tree_view->priv->hadjustment));
3912 gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment));
3914 gtk_signal_connect (GTK_OBJECT (tree_view->priv->hadjustment), "value_changed",
3915 (GtkSignalFunc) gtk_tree_view_adjustment_changed,
3920 if (tree_view->priv->vadjustment != vadj)
3922 tree_view->priv->vadjustment = vadj;
3923 gtk_object_ref (GTK_OBJECT (tree_view->priv->vadjustment));
3924 gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment));
3926 gtk_signal_connect (GTK_OBJECT (tree_view->priv->vadjustment), "value_changed",
3927 (GtkSignalFunc) gtk_tree_view_adjustment_changed,
3933 gtk_tree_view_adjustment_changed (NULL, tree_view);
3937 /* Column and header operations */
3940 * gtk_tree_view_get_headers_visible:
3941 * @tree_view: A #GtkTreeView.
3943 * Returns TRUE if the headers on the @tree_view are visible.
3945 * Return value: Whether the headers are visible or not.
3948 gtk_tree_view_get_headers_visible (GtkTreeView *tree_view)
3950 g_return_val_if_fail (tree_view != NULL, FALSE);
3951 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
3953 return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
3957 * gtk_tree_view_set_headers_visible:
3958 * @tree_view: A #GtkTreeView.
3959 * @headers_visible: TRUE if the headers are visible
3961 * Sets the the visibility state of the headers.
3964 gtk_tree_view_set_headers_visible (GtkTreeView *tree_view,
3965 gboolean headers_visible)
3969 GtkTreeViewColumn *column;
3971 g_return_if_fail (tree_view != NULL);
3972 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
3974 headers_visible = !! headers_visible;
3976 if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible)
3979 if (headers_visible)
3980 GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
3982 GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE);
3984 if (GTK_WIDGET_REALIZED (tree_view))
3986 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
3987 if (headers_visible)
3989 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view));
3991 if (GTK_WIDGET_MAPPED (tree_view))
3992 gtk_tree_view_map_buttons (tree_view);
3996 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
3998 for (list = tree_view->priv->columns; list; list = list->next)
4000 column = list->data;
4001 gtk_widget_unmap (column->button);
4003 gdk_window_hide (tree_view->priv->header_window);
4007 tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
4008 tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
4009 tree_view->priv->vadjustment->lower = 0;
4010 tree_view->priv->vadjustment->upper = tree_view->priv->height;
4011 gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed");
4013 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
4015 g_object_notify (G_OBJECT (tree_view), "headers_visible");
4020 * gtk_tree_view_columns_autosize:
4021 * @tree_view: A #GtkTreeView.
4023 * Resizes all columns to their optimal width.
4026 gtk_tree_view_columns_autosize (GtkTreeView *tree_view)
4028 gboolean dirty = FALSE;
4030 GtkTreeViewColumn *column;
4032 g_return_if_fail (tree_view != NULL);
4033 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4035 for (list = tree_view->priv->columns; list; list = list->next)
4037 column = list->data;
4038 if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE)
4040 column->dirty = TRUE;
4045 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
4049 * gtk_tree_view_set_headers_clickable:
4050 * @tree_view: A #GtkTreeView.
4051 * @setting: TRUE if the columns are clickable.
4053 * Allow the column title buttons to be clicked.
4056 gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view,
4061 g_return_if_fail (tree_view != NULL);
4062 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4063 g_return_if_fail (tree_view->priv->model != NULL);
4065 for (list = tree_view->priv->columns; list; list = list->next)
4066 gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting);
4068 g_object_notify (G_OBJECT (tree_view), "headers_clickable");
4072 * gtk_tree_view_append_column:
4073 * @tree_view: A #GtkTreeView.
4074 * @column: The #GtkTreeViewColumn to add.
4076 * Appends @column to the list of columns.
4078 * Return value: The number of columns in @tree_view after appending.
4081 gtk_tree_view_append_column (GtkTreeView *tree_view,
4082 GtkTreeViewColumn *column)
4084 g_return_val_if_fail (tree_view != NULL, -1);
4085 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
4086 g_return_val_if_fail (column != NULL, -1);
4087 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
4088 g_return_val_if_fail (column->tree_view == NULL, -1);
4090 return gtk_tree_view_insert_column (tree_view, column, -1);
4095 * gtk_tree_view_remove_column:
4096 * @tree_view: A #GtkTreeView.
4097 * @column: The #GtkTreeViewColumn to remove.
4099 * Removes @column from @tree_view.
4101 * Return value: The number of columns in @tree_view after removing.
4104 gtk_tree_view_remove_column (GtkTreeView *tree_view,
4105 GtkTreeViewColumn *column)
4107 g_return_val_if_fail (tree_view != NULL, -1);
4108 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
4109 g_return_val_if_fail (column != NULL, -1);
4110 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
4111 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
4113 _gtk_tree_view_column_unset_tree_view (column);
4115 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
4117 g_object_unref (G_OBJECT (column));
4119 tree_view->priv->n_columns--;
4121 if (GTK_WIDGET_REALIZED (tree_view))
4125 for (list = tree_view->priv->columns; list; list = list->next)
4127 column = GTK_TREE_VIEW_COLUMN (list->data);
4128 if (column->visible)
4129 column->dirty = TRUE;
4132 if (tree_view->priv->n_columns == 0 &&
4133 gtk_tree_view_get_headers_visible (tree_view))
4134 gdk_window_hide (tree_view->priv->header_window);
4136 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
4140 return tree_view->priv->n_columns;
4144 * gtk_tree_view_insert_column:
4145 * @tree_view: A #GtkTreeView.
4146 * @column: The #GtkTreeViewColumn to be inserted.
4147 * @position: The position to insert @column in.
4149 * This inserts the @column into the @tree_view at @position. If @position is
4150 * -1, then the column is inserted at the end.
4152 * Return value: The number of columns in @tree_view after insertion.
4155 gtk_tree_view_insert_column (GtkTreeView *tree_view,
4156 GtkTreeViewColumn *column,
4159 g_return_val_if_fail (tree_view != NULL, -1);
4160 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
4161 g_return_val_if_fail (column != NULL, -1);
4162 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1);
4163 g_return_val_if_fail (column->tree_view == NULL, -1);
4165 g_object_ref (G_OBJECT (column));
4167 if (tree_view->priv->n_columns == 0 &&
4168 GTK_WIDGET_REALIZED (tree_view) &&
4169 gtk_tree_view_get_headers_visible (tree_view))
4171 gdk_window_show (tree_view->priv->header_window);
4174 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
4176 _gtk_tree_view_column_set_tree_view (column, tree_view);
4177 _gtk_tree_view_column_create_button (column);
4179 tree_view->priv->n_columns++;
4182 if (GTK_WIDGET_REALIZED (tree_view))
4186 for (list = tree_view->priv->columns; list; list = list->next)
4188 column = GTK_TREE_VIEW_COLUMN (list->data);
4189 if (column->visible)
4190 column->dirty = TRUE;
4192 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
4195 return tree_view->priv->n_columns;
4199 * gtk_tree_view_insert_column_with_attributes:
4200 * @tree_view: A #GtkTreeView
4201 * @position: The position to insert the new column in.
4202 * @title: The title to set the header to.
4203 * @cell: The #GtkCellRenderer.
4204 * @Varargs: A NULL terminated list of attributes.
4206 * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at
4207 * @position. If @position is -1, then the newly created column is inserted at
4208 * the end. The column is initialized with the attributes given.
4210 * Return value: The number of columns in @tree_view after insertion.
4213 gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view,
4216 GtkCellRenderer *cell,
4219 GtkTreeViewColumn *column;
4224 g_return_val_if_fail (tree_view != NULL, -1);
4225 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
4227 column = gtk_tree_view_column_new ();
4229 gtk_tree_view_column_set_title (column, title);
4230 gtk_tree_view_column_set_cell_renderer (column, cell);
4232 va_start (args, cell);
4234 attribute = va_arg (args, gchar *);
4236 while (attribute != NULL)
4238 column_id = va_arg (args, gint);
4239 gtk_tree_view_column_add_attribute (column, attribute, column_id);
4240 attribute = va_arg (args, gchar *);
4245 gtk_tree_view_insert_column (tree_view, column, position);
4246 g_object_unref (column);
4248 return tree_view->priv->n_columns;
4252 * gtk_tree_view_get_column:
4253 * @tree_view: A #GtkTreeView.
4254 * @n: The position of the column, counting from 0.
4256 * Gets the #GtkTreeViewColumn at the given position in the #tree_view.
4258 * Return value: The #GtkTreeViewColumn, or NULL if the position is outside the
4262 gtk_tree_view_get_column (GtkTreeView *tree_view,
4265 g_return_val_if_fail (tree_view != NULL, NULL);
4266 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
4267 g_return_val_if_fail (tree_view->priv->model != NULL, NULL);
4269 if (n < 0 || n >= tree_view->priv->n_columns)
4272 if (tree_view->priv->columns == NULL)
4275 return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
4279 gtk_tree_view_set_expander_column (GtkTreeView *tree_view,
4282 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4284 if (tree_view->priv->expander_column != col)
4286 tree_view->priv->expander_column = col;
4288 g_object_notify (G_OBJECT (tree_view), "expander_column");
4293 gtk_tree_view_get_expander_column (GtkTreeView *tree_view)
4295 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1);
4297 return tree_view->priv->expander_column;
4301 * gtk_tree_view_scroll_to_point:
4302 * @tree_view: a #GtkTreeView
4303 * @tree_x: X coordinate of new top-left pixel of visible area
4304 * @tree_y: Y coordinate of new top-left pixel of visible area
4306 * Scrolls the tree view such that the top-left corner of the visible
4307 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
4308 * in tree window coordinates. The @tree_view must be realized before
4309 * this function is called. If it isn't, you probably want ot be
4310 * using gtk_tree_view_scroll_to_cell.
4313 gtk_tree_view_scroll_to_point (GtkTreeView *tree_view,
4317 GtkAdjustment *hadj;
4318 GtkAdjustment *vadj;
4320 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4321 g_return_if_fail (GTK_WIDGET_REALIZED (tree_view));
4323 hadj = tree_view->priv->hadjustment;
4324 vadj = tree_view->priv->vadjustment;
4326 gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper));
4327 gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper));
4331 * gtk_tree_view_scroll_to_cell
4332 * @tree_view: A #GtkTreeView.
4333 * @path: The path of the row to move to.
4334 * @column: The #GtkTreeViewColumn to move horizontally to.
4335 * @row_align: The vertical alignment of the row specified by @path.
4336 * @col_align: The horizontal alignment of the column specified by @column.
4338 * Moves the alignments of @tree_view to the position specified by
4339 * @column and @path. If @column is NULL, then no horizontal
4340 * scrolling occurs. Likewise, if @path is NULL no vertical scrolling
4341 * occurs. @row_align determines where the row is placed, and
4342 * @col_align determines where @column is placed. Both are expected
4343 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
4344 * right/bottom alignment, 0.5 means center.
4347 gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view,
4349 GtkTreeViewColumn *column,
4353 GdkRectangle cell_rect;
4354 GdkRectangle vis_rect;
4355 gint dest_x, dest_y;
4357 /* FIXME work on unmapped/unrealized trees? maybe implement when
4358 * we do incremental reflow for trees
4361 g_return_if_fail (tree_view != NULL);
4362 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4363 g_return_if_fail (row_align >= 0.0);
4364 g_return_if_fail (row_align <= 1.0);
4365 g_return_if_fail (col_align >= 0.0);
4366 g_return_if_fail (col_align <= 1.0);
4367 g_return_if_fail (path != NULL || column != NULL);
4369 row_align = CLAMP (row_align, 0.0, 1.0);
4370 col_align = CLAMP (col_align, 0.0, 1.0);
4372 if (! GTK_WIDGET_REALIZED (tree_view))
4375 tree_view->priv->scroll_to_path = gtk_tree_path_copy (path);
4377 tree_view->priv->scroll_to_column = column;
4378 tree_view->priv->scroll_to_row_align = row_align;
4379 tree_view->priv->scroll_to_col_align = col_align;
4384 gtk_tree_view_get_cell_area (tree_view, path, column, &cell_rect);
4385 gtk_tree_view_get_visible_rect (tree_view, &vis_rect);
4387 dest_x = vis_rect.x;
4388 dest_y = vis_rect.y;
4392 dest_x = cell_rect.x +
4393 cell_rect.width * row_align -
4394 vis_rect.width * row_align;
4399 dest_y = cell_rect.y +
4400 cell_rect.height * col_align -
4401 vis_rect.height * col_align;
4404 gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y);
4408 * gtk_tree_view_get_path_at_pos:
4409 * @tree_view: A #GtkTreeView.
4410 * @window: The #GdkWindow to check against.
4411 * @x: The x position to be identified.
4412 * @y: The y position to be identified.
4413 * @path: A pointer to a #GtkTreePath pointer to be filled in, or %NULL
4414 * @column: A pointer to a #GtkTreeViewColumn pointer to be filled in, or %NULL
4415 * @cell_x: A pointer where the X coordinate relative to the cell can be placed, or %NULL
4416 * @cell_y: A pointer where the Y coordinate relative to the cell can be placed, or %NULL
4418 * Finds the path at the point (@x, @y) relative to @window. If
4419 * @window is NULL, then the point is found relative to the widget
4420 * coordinates. This function is expected to be called after an
4421 * event, with event->window being passed in as @window. It is
4422 * primarily for things like popup menus. If @path is non-NULL, then
4423 * it will be filled with the #GtkTreePath at that point. This path
4424 * should be freed with #gtk_tree_path_free. If @column is non-NULL,
4425 * then it will be filled with the column at that point. @cell_x and
4426 * @cell_y return the coordinates relative to the cell background
4427 * (i.e. the background_area passed to gtk_cell_renderer_render()).
4429 * Return value: TRUE if a row exists at that coordinate.
4432 gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view,
4437 GtkTreeViewColumn **column,
4445 g_return_val_if_fail (tree_view != NULL, FALSE);
4446 g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE);
4447 g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE);
4450 g_return_val_if_fail (window == tree_view->priv->bin_window, FALSE);
4457 if (x > tree_view->priv->hadjustment->upper)
4463 if (column || cell_x)
4465 GtkTreeViewColumn *tmp_column;
4466 GtkTreeViewColumn *last_column = NULL;
4468 gint remaining_x = x;
4469 gboolean found = FALSE;
4471 for (list = tree_view->priv->columns; list; list = list->next)
4473 tmp_column = list->data;
4475 if (tmp_column->visible == FALSE)
4478 last_column = tmp_column;
4479 if (remaining_x <= tmp_column->width)
4484 *column = tmp_column;
4487 *cell_x = remaining_x;
4491 remaining_x -= tmp_column->width;
4497 *column = last_column;
4500 *cell_x = last_column->width + remaining_x;
4506 y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
4507 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
4512 if (y < TREE_VIEW_HEADER_HEIGHT (tree_view))
4515 y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree,
4516 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y + tree_view->priv->vadjustment->value),
4527 *path = _gtk_tree_view_find_path (tree_view, tree, node);
4534 gtk_tree_view_get_background_xrange (GtkTreeView *tree_view,
4536 GtkTreeViewColumn *column,
4540 GtkTreeViewColumn *tmp_column = NULL;
4551 for (list = tree_view->priv->columns; list; list = list->next)
4553 tmp_column = list->data;
4555 if (tmp_column == column)
4558 if (tmp_column->visible)
4559 total_width += tmp_column->width;
4562 if (tmp_column != column)
4564 g_warning (G_STRLOC": passed-in column isn't in the tree");
4573 if (column->visible)
4574 *x2 = total_width + column->width;
4576 *x2 = total_width; /* width of 0 */
4581 gtk_tree_view_get_cell_xrange (GtkTreeView *tree_view,
4583 GtkTreeViewColumn *column,
4587 GtkTreeViewColumn *tmp_column = NULL;
4600 for (list = tree_view->priv->columns; list; list = list->next)
4602 tmp_column = list->data;
4604 if (tmp_column == column)
4607 if (tmp_column->visible)
4608 total_width += tmp_column->width;
4613 if (tmp_column != column)
4615 g_warning (G_STRLOC": passed-in column isn't in the tree");
4619 /* Remember we're getting the cell range, i.e. the cell_area passed
4620 * to the cell renderer.
4623 if (i == tree_view->priv->expander_column)
4624 total_width += tree_view->priv->tab_offset * _gtk_rbtree_get_depth (tree);
4631 if (column->visible)
4632 *x2 = total_width + column->displayed_width;
4634 *x2 = total_width; /* width of 0 */
4639 gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view,
4645 GtkTreeViewColumn *tmp_column = NULL;
4651 for (list = tree_view->priv->columns; list; list = list->next)
4653 tmp_column = list->data;
4655 if (i == tree_view->priv->expander_column)
4657 x_offset = total_width;
4661 if (tmp_column->visible)
4662 total_width += tmp_column->width;
4670 if (tmp_column && tmp_column->visible)
4672 /* +1 because x2 isn't included in the range. */
4674 *x2 = x_offset + tree_view->priv->tab_offset + 1;
4678 /* return an empty range, the expander column is hidden */
4686 * gtk_tree_view_get_cell_area:
4687 * @tree_view: a #GtkTreeView
4688 * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
4689 * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes
4690 * @rect: rectangle to fill with cell rect
4692 * Fills the bounding rectangle in tree window coordinates for the
4693 * cell at the row specified by @path and the column specified by
4694 * @column. If @path is %NULL, the y and height fields of the
4695 * rectangle will be filled with 0. If @column is %NULL, the x and
4696 * width fields will be filled with 0. The sum of all cell rects does
4697 * not cover the entire tree; there are extra pixels in between rows,
4698 * for example. The returned rectangle is equivalent to the @cell_area
4699 * passed to gtk_cell_renderer_render().
4703 gtk_tree_view_get_cell_area (GtkTreeView *tree_view,
4705 GtkTreeViewColumn *column,
4708 GtkRBTree *tree = NULL;
4709 GtkRBNode *node = NULL;
4711 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4712 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
4713 g_return_if_fail (rect != NULL);
4722 /* Get vertical coords */
4724 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
4728 g_warning (G_STRLOC": no row corresponding to path");
4732 /* Remember that the rbtree stores node height including the vertical
4733 * separator, see comment at top of file.
4735 rect->y = CELL_FIRST_PIXEL (tree_view, tree, node);
4737 rect->height = CELL_HEIGHT (node);
4744 gtk_tree_view_get_cell_xrange (tree_view, tree, column, &rect->x, &x2);
4745 rect->width = x2 - rect->x;
4751 * gtk_tree_view_get_background_area:
4752 * @tree_view: a #GtkTreeView
4753 * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
4754 * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes
4755 * @rect: rectangle to fill with cell background rect
4757 * Fills the bounding rectangle in tree window coordinates for the
4758 * cell at the row specified by @path and the column specified by
4759 * @column. If @path is %NULL, the y and height fields of the
4760 * rectangle will be filled with 0. If @column is %NULL, the x and
4761 * width fields will be filled with 0. The returned rectangle is
4762 * equivalent to the @background_area passed to
4763 * gtk_cell_renderer_render(). These background areas tile to cover
4764 * the entire tree window (except for the area used for header
4765 * buttons). Contrast with the cell_area, returned by
4766 * gtk_tree_view_get_cell_area(), which returns only the cell itself,
4767 * excluding surrounding borders and the tree expander area.
4771 gtk_tree_view_get_background_area (GtkTreeView *tree_view,
4773 GtkTreeViewColumn *column,
4776 GtkRBTree *tree = NULL;
4777 GtkRBNode *node = NULL;
4779 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4780 g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column));
4781 g_return_if_fail (rect != NULL);
4790 /* Get vertical coords */
4792 _gtk_tree_view_find_node (tree_view, path, &tree, &node);
4796 g_warning (G_STRLOC": no row corresponding to path");
4800 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node);
4802 rect->height = BACKGROUND_HEIGHT (node);
4809 gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2);
4810 rect->width = x2 - rect->x;
4815 gtk_tree_view_expand_all_helper (GtkRBTree *tree,
4819 GtkTreeView *tree_view = data;
4822 _gtk_rbtree_traverse (node->children,
4823 node->children->root,
4825 gtk_tree_view_expand_all_helper,
4827 else if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && node->children == NULL)
4833 node->children = _gtk_rbtree_new ();
4834 node->children->parent_tree = tree;
4835 node->children->parent_node = node;
4836 path = _gtk_tree_view_find_path (tree_view, tree, node);
4837 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
4838 gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter);
4839 gtk_tree_view_build_tree (tree_view,
4842 gtk_tree_path_get_depth (path) + 1,
4844 GTK_WIDGET_REALIZED (tree_view));
4845 gtk_tree_path_free (path);
4850 * gtk_tree_view_expand_all:
4851 * @tree_view: A #GtkTreeView.
4853 * Recursively expands all nodes in the @tree_view.
4856 gtk_tree_view_expand_all (GtkTreeView *tree_view)
4858 g_return_if_fail (tree_view != NULL);
4859 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4860 g_return_if_fail (tree_view->priv->tree != NULL);
4862 _gtk_rbtree_traverse (tree_view->priv->tree,
4863 tree_view->priv->tree->root,
4865 gtk_tree_view_expand_all_helper,
4868 _gtk_tree_view_update_size (tree_view);
4872 gtk_tree_view_collapse_all_helper (GtkRBTree *tree,
4881 path = _gtk_tree_view_find_path (GTK_TREE_VIEW (data),
4883 node->children->root);
4884 gtk_tree_model_get_iter (GTK_TREE_VIEW (data)->priv->model,
4887 gtk_tree_view_discover_dirty (GTK_TREE_VIEW (data),
4890 gtk_tree_path_get_depth (path));
4892 /* Ensure we don't have a dangling pointer to a dead node */
4893 ensure_unprelighted (GTK_TREE_VIEW (data));
4895 _gtk_rbtree_remove (node->children);
4896 gtk_tree_path_free (path);
4901 * gtk_tree_view_collapse_all:
4902 * @tree_view: A #GtkTreeView.
4904 * Recursively collapses all visible, expanded nodes in @tree_view.
4907 gtk_tree_view_collapse_all (GtkTreeView *tree_view)
4909 g_return_if_fail (tree_view != NULL);
4910 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
4911 g_return_if_fail (tree_view->priv->tree != NULL);
4913 _gtk_rbtree_traverse (tree_view->priv->tree,
4914 tree_view->priv->tree->root,
4916 gtk_tree_view_collapse_all_helper,
4919 if (GTK_WIDGET_MAPPED (tree_view))
4920 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
4923 /* FIXME the bool return values for expand_row and collapse_row are
4924 * not analagous; they should be TRUE if the row had children and
4925 * was not already in the requested state.
4929 * gtk_tree_view_expand_row:
4930 * @tree_view: a #GtkTreeView
4931 * @path: path to a row
4932 * @open_all: whether to recursively expand, or just expand immediate children
4934 * Opens the row so its children are visible
4936 * Return value: %TRUE if the row existed and had children
4939 gtk_tree_view_expand_row (GtkTreeView *tree_view,
4948 g_return_val_if_fail (tree_view != NULL, FALSE);
4949 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
4950 g_return_val_if_fail (tree_view->priv->model != NULL, FALSE);
4951 g_return_val_if_fail (path != NULL, FALSE);
4953 if (_gtk_tree_view_find_node (tree_view,
4962 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
4963 if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter))
4966 node->children = _gtk_rbtree_new ();
4967 node->children->parent_tree = tree;
4968 node->children->parent_node = node;
4970 gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter);
4971 gtk_tree_view_build_tree (tree_view,
4974 gtk_tree_path_get_depth (path) + 1,
4976 GTK_WIDGET_REALIZED (tree_view));
4978 if (GTK_WIDGET_MAPPED (tree_view))
4979 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
4985 * gtk_tree_view_collapse_row:
4986 * @tree_view: a #GtkTreeView
4987 * @path: path to a row in the @tree_view
4989 * Collapses a row (hides its child rows).
4991 * Return value: %TRUE if the row was expanded
4994 gtk_tree_view_collapse_row (GtkTreeView *tree_view,
5001 g_return_val_if_fail (tree_view != NULL, FALSE);
5002 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
5003 g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE);
5004 g_return_val_if_fail (path != NULL, FALSE);
5006 if (_gtk_tree_view_find_node (tree_view,
5012 if (node->children == NULL)
5015 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5016 gtk_tree_view_discover_dirty (tree_view,
5019 gtk_tree_path_get_depth (path));
5021 /* Ensure we don't have a dangling pointer to a dead node */
5022 ensure_unprelighted (tree_view);
5024 g_assert (tree_view->priv->prelight_node == NULL);
5026 _gtk_rbtree_remove (node->children);
5028 if (GTK_WIDGET_MAPPED (tree_view))
5029 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5035 * gtk_tree_view_get_visible_rect:
5036 * @tree_view: a #GtkTreeView
5037 * @visible_rect: rectangle to fill
5039 * Fills @visible_rect with the currently-visible region of the
5040 * buffer, in tree coordinates. Convert to widget coordinates with
5041 * gtk_tree_view_tree_to_widget_coords(). Tree coordinates start at
5042 * 0,0 for row 0 of the tree, and cover the entire scrollable area of
5046 gtk_tree_view_get_visible_rect (GtkTreeView *tree_view,
5047 GdkRectangle *visible_rect)
5051 g_return_if_fail (tree_view != NULL);
5052 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5054 widget = GTK_WIDGET (tree_view);
5058 visible_rect->x = tree_view->priv->hadjustment->value;
5059 visible_rect->y = tree_view->priv->vadjustment->value;
5060 visible_rect->width = widget->allocation.width;
5061 visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5066 * gtk_tree_view_widget_to_tree_coords:
5067 * @tree_view: a #GtkTreeView
5068 * @wx: widget X coordinate
5069 * @wy: widget Y coordinate
5070 * @tx: return location for tree X coordinate
5071 * @ty: return location for tree Y coordinate
5073 * Converts widget coordinates to coordinates for the
5074 * tree window (the full scrollable area of the tree).
5078 gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view,
5084 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5088 *tx = wx + tree_view->priv->hadjustment->value;
5093 *ty = wy + tree_view->priv->vadjustment->value;
5098 * gtk_tree_view_tree_to_widget_coords:
5099 * @tree_view: a #GtkTreeView
5100 * @tx: tree X coordinate
5101 * @ty: tree Y coordinate
5102 * @wx: return location for widget X coordinate
5103 * @wy: return location for widget Y coordinate
5105 * Converts tree coordinates (coordinates in full scrollable
5106 * area of the tree) to widget coordinates.
5110 gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view,
5116 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5120 *wx = tx - tree_view->priv->hadjustment->value;
5125 *wy = ty - tree_view->priv->vadjustment->value;
5130 * gtk_tree_view_set_rules_hint
5131 * @tree_view: a #GtkTreeView
5132 * @setting: %TRUE if the tree requires reading across rows
5134 * This function tells GTK+ that the user interface for your
5135 * application requires users to read across tree rows and associate
5136 * cells with one another. By default, GTK+ will then render the tree
5137 * with alternating row colors. <emphasis>DO NOT</emphasis> use it
5138 * just because you prefer the appearance of the ruled tree; that's a
5139 * question for the theme. Some themes will draw tree rows in
5140 * alternating colors even when rules are turned off, and users who
5141 * prefer that appearance all the time can choose those themes. You
5142 * should call this function only as a <emphasis>semantic</emphasis>
5143 * hint to the theme engine that your tree makes alternating colors
5144 * useful from a functional standpoint (since it has lots of columns,
5149 gtk_tree_view_set_rules_hint (GtkTreeView *tree_view,
5152 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5154 setting = setting != FALSE;
5156 if (tree_view->priv->has_rules != setting)
5158 tree_view->priv->has_rules = setting;
5159 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5162 g_object_notify (G_OBJECT (tree_view), "rules_hint");
5166 * gtk_tree_view_get_rules_hint
5167 * @tree_view: a #GtkTreeView
5169 * Gets the setting set by gtk_tree_view_set_rules_hint().
5171 * Return value: %TRUE if rules are useful for the user of this tree
5174 gtk_tree_view_get_rules_hint (GtkTreeView *tree_view)
5176 g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
5178 return tree_view->priv->has_rules;
5184 set_source_row (GdkDragContext *context,
5185 GtkTreeModel *model,
5186 GtkTreePath *source_row)
5188 g_object_set_data_full (G_OBJECT (context),
5189 "gtk-tree-view-source-row",
5190 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5191 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5195 get_source_row (GdkDragContext *context)
5197 GtkTreeRowReference *ref =
5198 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5201 return gtk_tree_row_reference_get_path (ref);
5208 set_dest_row (GdkDragContext *context,
5209 GtkTreeModel *model,
5210 GtkTreePath *dest_row)
5212 g_object_set_data_full (G_OBJECT (context),
5213 "gtk-tree-view-dest-row",
5214 dest_row ? gtk_tree_row_reference_new (model, dest_row) : NULL,
5215 (GDestroyNotify) (dest_row ? gtk_tree_row_reference_free : NULL));
5219 get_dest_row (GdkDragContext *context)
5221 GtkTreeRowReference *ref =
5222 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5225 return gtk_tree_row_reference_get_path (ref);
5230 /* Get/set whether drag_motion requested the drag data and
5231 * drag_data_received should thus not actually insert the data,
5232 * since the data doesn't result from a drop.
5235 set_status_pending (GdkDragContext *context,
5236 GdkDragAction suggested_action)
5238 g_object_set_data (G_OBJECT (context),
5239 "gtk-tree-view-status-pending",
5240 GINT_TO_POINTER (suggested_action));
5243 static GdkDragAction
5244 get_status_pending (GdkDragContext *context)
5246 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5247 "gtk-tree-view-status-pending"));
5250 typedef struct _TreeViewDragInfo TreeViewDragInfo;
5252 struct _TreeViewDragInfo
5254 GdkModifierType start_button_mask;
5255 GtkTargetList *source_target_list;
5256 GdkDragAction source_actions;
5257 GClosure *row_draggable_closure;
5259 GtkTargetList *dest_target_list;
5260 GClosure *location_droppable_closure;
5262 guint source_set : 1;
5266 static TreeViewDragInfo*
5267 get_info (GtkTreeView *tree_view)
5269 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5273 clear_source_info (TreeViewDragInfo *di)
5275 if (di->source_target_list)
5276 gtk_target_list_unref (di->source_target_list);
5278 if (di->row_draggable_closure)
5279 g_closure_unref (di->row_draggable_closure);
5281 di->source_target_list = NULL;
5282 di->row_draggable_closure = NULL;
5286 clear_dest_info (TreeViewDragInfo *di)
5288 if (di->location_droppable_closure)
5289 g_closure_unref (di->location_droppable_closure);
5291 if (di->dest_target_list)
5292 gtk_target_list_unref (di->dest_target_list);
5294 di->location_droppable_closure = NULL;
5295 di->dest_target_list = NULL;
5299 destroy_info (TreeViewDragInfo *di)
5301 clear_source_info (di);
5302 clear_dest_info (di);
5306 static TreeViewDragInfo*
5307 ensure_info (GtkTreeView *tree_view)
5309 TreeViewDragInfo *di;
5311 di = get_info (tree_view);
5315 di = g_new0 (TreeViewDragInfo, 1);
5317 g_object_set_data_full (G_OBJECT (tree_view),
5318 "gtk-tree-view-drag-info",
5320 (GDestroyNotify) destroy_info);
5327 remove_info (GtkTreeView *tree_view)
5329 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5332 #define SCROLL_EDGE_SIZE 15
5335 drag_scan_timeout (gpointer data)
5337 GtkTreeView *tree_view;
5339 GdkModifierType state;
5340 GtkTreePath *path = NULL;
5341 GtkTreeViewColumn *column = NULL;
5342 GdkRectangle visible_rect;
5344 tree_view = GTK_TREE_VIEW (data);
5346 gdk_window_get_pointer (tree_view->priv->bin_window,
5349 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
5351 /* See if we are near the edge. */
5352 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5353 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5354 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5355 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5357 gtk_tree_view_get_path_at_pos (tree_view,
5358 tree_view->priv->bin_window,
5367 gtk_tree_view_scroll_to_cell (tree_view,
5372 gtk_tree_path_free (path);
5381 ensure_scroll_timeout (GtkTreeView *tree_view)
5383 if (tree_view->priv->scroll_timeout == 0)
5384 tree_view->priv->scroll_timeout = gtk_timeout_add (50, drag_scan_timeout, tree_view);
5388 remove_scroll_timeout (GtkTreeView *tree_view)
5390 if (tree_view->priv->scroll_timeout != 0)
5392 gtk_timeout_remove (tree_view->priv->scroll_timeout);
5393 tree_view->priv->scroll_timeout = 0;
5398 gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view,
5399 GdkModifierType start_button_mask,
5400 const GtkTargetEntry *targets,
5402 GdkDragAction actions,
5403 GtkTreeViewDraggableFunc row_draggable_func,
5406 TreeViewDragInfo *di;
5408 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5410 di = ensure_info (tree_view);
5411 clear_source_info (di);
5413 di->start_button_mask = start_button_mask;
5414 di->source_target_list = gtk_target_list_new (targets, n_targets);
5415 di->source_actions = actions;
5417 if (row_draggable_func)
5419 di->row_draggable_closure = g_cclosure_new ((GCallback) row_draggable_func,
5421 g_closure_ref (di->row_draggable_closure);
5422 g_closure_sink (di->row_draggable_closure);
5425 di->source_set = TRUE;
5429 gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view,
5430 const GtkTargetEntry *targets,
5432 GdkDragAction actions,
5433 GtkTreeViewDroppableFunc location_droppable_func,
5436 TreeViewDragInfo *di;
5438 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5440 gtk_drag_dest_set (GTK_WIDGET (tree_view),
5446 di = ensure_info (tree_view);
5447 clear_dest_info (di);
5450 di->dest_target_list = gtk_target_list_new (targets, n_targets);
5452 if (location_droppable_func)
5454 di->location_droppable_closure = g_cclosure_new ((GCallback) location_droppable_func,
5456 g_closure_ref (di->location_droppable_closure);
5457 g_closure_sink (di->location_droppable_closure);
5460 di->dest_set = TRUE;
5464 gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view)
5466 TreeViewDragInfo *di;
5468 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5470 di = get_info (tree_view);
5476 clear_source_info (di);
5477 di->source_set = FALSE;
5480 if (!di->dest_set && !di->source_set)
5481 remove_info (tree_view);
5486 gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view)
5488 TreeViewDragInfo *di;
5490 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5492 di = get_info (tree_view);
5498 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
5499 clear_dest_info (di);
5500 di->dest_set = FALSE;
5503 if (!di->dest_set && !di->source_set)
5504 remove_info (tree_view);
5509 gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view,
5511 GtkTreeViewDropPosition pos)
5513 GtkTreePath *current_dest;
5514 /* Note; this function is exported to allow a custom DND
5515 * implementation, so it can't touch TreeViewDragInfo
5518 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5520 current_dest = NULL;
5522 if (tree_view->priv->drag_dest_row)
5523 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
5527 gtk_tree_view_queue_draw_path (tree_view, current_dest, NULL);
5528 gtk_tree_path_free (current_dest);
5531 if (tree_view->priv->drag_dest_row)
5532 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
5534 tree_view->priv->drag_dest_pos = pos;
5538 tree_view->priv->drag_dest_row =
5539 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5540 gtk_tree_view_queue_draw_path (tree_view, path, NULL);
5543 tree_view->priv->drag_dest_row = NULL;
5547 gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view,
5549 GtkTreeViewDropPosition *pos)
5551 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
5555 if (tree_view->priv->drag_dest_row)
5556 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
5562 *pos = tree_view->priv->drag_dest_pos;
5566 gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view,
5570 GtkTreeViewDropPosition *pos)
5573 gdouble offset_into_row;
5577 GtkTreeViewColumn *column = NULL;
5578 GtkTreePath *tmp_path = NULL;
5580 /* Note; this function is exported to allow a custom DND
5581 * implementation, so it can't touch TreeViewDragInfo
5584 g_return_val_if_fail (tree_view != NULL, FALSE);
5585 g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE);
5586 g_return_val_if_fail (drag_x >= 0, FALSE);
5587 g_return_val_if_fail (drag_y >= 0, FALSE);
5588 g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE);
5593 /* remember that drag_x and drag_y are in widget coords, convert to tree window */
5595 gtk_tree_view_widget_to_tree_coords (tree_view, drag_x, drag_y,
5598 /* If in the top quarter of a row, we drop before that row; if
5599 * in the bottom quarter, drop after that row; if in the middle,
5600 * and the row has children, drop into the row.
5603 if (!gtk_tree_view_get_path_at_pos (tree_view,
5604 tree_view->priv->bin_window,
5612 gtk_tree_view_get_background_area (tree_view, tmp_path, column,
5615 offset_into_row = cell_y;
5620 gtk_tree_path_free (tmp_path);
5624 quarter = cell.height / 4.0;
5628 if (offset_into_row < quarter)
5630 *pos = GTK_TREE_VIEW_DROP_BEFORE;
5632 else if (offset_into_row < quarter * 2)
5634 *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
5636 else if (offset_into_row < quarter * 3)
5638 *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
5642 *pos = GTK_TREE_VIEW_DROP_AFTER;
5650 gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view,
5651 GdkEventMotion *event)
5653 GdkDragContext *context;
5654 TreeViewDragInfo *di;
5655 GtkTreePath *path = NULL;
5657 gint cell_x, cell_y;
5658 GtkTreeModel *model;
5660 di = get_info (tree_view);
5665 if (tree_view->priv->pressed_button < 0)
5668 if (!gtk_drag_check_threshold (GTK_WIDGET (tree_view),
5669 tree_view->priv->press_start_x,
5670 tree_view->priv->press_start_y,
5671 event->x, event->y))
5674 model = gtk_tree_view_get_model (tree_view);
5679 button = tree_view->priv->pressed_button;
5680 tree_view->priv->pressed_button = -1;
5682 gtk_tree_view_get_path_at_pos (tree_view,
5683 tree_view->priv->bin_window,
5684 tree_view->priv->press_start_x,
5685 tree_view->priv->press_start_y,
5694 /* FIXME if the path doesn't match the row_draggable predicate,
5695 * return FALSE and free path
5698 /* FIXME Check whether we're a start button, if not return FALSE and
5702 context = gtk_drag_begin (GTK_WIDGET (tree_view),
5703 di->source_target_list,
5708 gtk_drag_set_icon_default (context);
5713 row_pix = gtk_tree_view_create_row_drag_icon (tree_view,
5716 gtk_drag_set_icon_pixmap (context,
5717 gdk_drawable_get_colormap (row_pix),
5720 /* the + 1 is for the black border in the icon */
5721 tree_view->priv->press_start_x + 1,
5724 gdk_pixmap_unref (row_pix);
5727 set_source_row (context, model, path);
5728 gtk_tree_path_free (path);
5733 /* Default signal implementations for the drag signals */
5736 gtk_tree_view_drag_begin (GtkWidget *widget,
5737 GdkDragContext *context)
5743 gtk_tree_view_drag_end (GtkWidget *widget,
5744 GdkDragContext *context)
5750 gtk_tree_view_drag_data_get (GtkWidget *widget,
5751 GdkDragContext *context,
5752 GtkSelectionData *selection_data,
5756 GtkTreeView *tree_view;
5757 GtkTreeModel *model;
5758 TreeViewDragInfo *di;
5759 GtkTreePath *source_row;
5761 tree_view = GTK_TREE_VIEW (widget);
5763 model = gtk_tree_view_get_model (tree_view);
5768 di = get_info (GTK_TREE_VIEW (widget));
5773 source_row = get_source_row (context);
5775 if (source_row == NULL)
5778 /* We can implement the GTK_TREE_MODEL_ROW target generically for
5779 * any model; for DragSource models there are some other targets
5783 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
5784 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
5789 /* If drag_data_get does nothing, try providing row data. */
5790 if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
5792 gtk_selection_data_set_tree_row (selection_data,
5798 gtk_tree_path_free (source_row);
5803 check_model_dnd (GtkTreeModel *model,
5804 GType required_iface,
5805 const gchar *signal)
5807 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5809 g_warning ("You must override the default '%s' handler "
5810 "on GtkTreeView when using models that don't support "
5811 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5812 "is to connect to '%s' and call "
5813 "gtk_signal_emit_stop_by_name() in your signal handler to prevent "
5814 "the default handler from running. Look at the source code "
5815 "for the default handler in gtktreeview.c to get an idea what "
5816 "your handler should do. (gtktreeview.c is in the GTK source "
5817 "code.) If you're using GTK from a language other than C, "
5818 "there may be a more natural way to override default handlers, e.g. via derivation.",
5819 signal, g_type_name (required_iface), signal);
5827 gtk_tree_view_drag_data_delete (GtkWidget *widget,
5828 GdkDragContext *context)
5830 TreeViewDragInfo *di;
5831 GtkTreeModel *model;
5832 GtkTreeView *tree_view;
5833 GtkTreePath *source_row;
5835 tree_view = GTK_TREE_VIEW (widget);
5836 model = gtk_tree_view_get_model (tree_view);
5838 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
5841 di = get_info (tree_view);
5846 source_row = get_source_row (context);
5848 if (source_row == NULL)
5851 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
5854 gtk_tree_path_free (source_row);
5856 set_source_row (context, NULL, NULL);
5860 remove_open_timeout (GtkTreeView *tree_view)
5862 if (tree_view->priv->open_dest_timeout != 0)
5864 gtk_timeout_remove (tree_view->priv->open_dest_timeout);
5865 tree_view->priv->open_dest_timeout = 0;
5870 gtk_tree_view_drag_leave (GtkWidget *widget,
5871 GdkDragContext *context,
5874 TreeViewDragInfo *di;
5876 di = get_info (GTK_TREE_VIEW (widget));
5878 /* unset any highlight row */
5879 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
5881 GTK_TREE_VIEW_DROP_BEFORE);
5883 remove_scroll_timeout (GTK_TREE_VIEW (widget));
5884 remove_open_timeout (GTK_TREE_VIEW (widget));
5888 open_row_timeout (gpointer data)
5890 GtkTreeView *tree_view = data;
5891 GtkTreePath *dest_path = NULL;
5892 GtkTreeViewDropPosition pos;
5894 gtk_tree_view_get_drag_dest_row (tree_view,
5899 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
5900 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
5902 gtk_tree_view_expand_row (tree_view,
5905 tree_view->priv->open_dest_timeout = 0;
5907 gtk_tree_path_free (dest_path);
5914 gtk_tree_path_free (dest_path);
5919 /* Returns TRUE if event should not be propagated to parent widgets */
5921 set_destination_row (GtkTreeView *tree_view,
5922 GdkDragContext *context,
5925 GdkDragAction *suggested_action,
5928 GtkTreePath *path = NULL;
5929 GtkTreeViewDropPosition pos;
5930 GtkTreeViewDropPosition old_pos;
5931 TreeViewDragInfo *di;
5933 GtkTreePath *old_dest_path = NULL;
5935 *suggested_action = 0;
5938 widget = GTK_WIDGET (tree_view);
5940 di = get_info (tree_view);
5944 /* someone unset us as a drag dest, note that if
5945 * we return FALSE drag_leave isn't called
5948 gtk_tree_view_set_drag_dest_row (tree_view,
5950 GTK_TREE_VIEW_DROP_BEFORE);
5952 remove_scroll_timeout (GTK_TREE_VIEW (widget));
5953 remove_open_timeout (GTK_TREE_VIEW (widget));
5955 return FALSE; /* no longer a drop site */
5958 *target = gtk_drag_dest_find_target (widget, context, di->dest_target_list);
5959 if (*target == GDK_NONE)
5964 if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
5969 /* can't drop here */
5970 remove_open_timeout (tree_view);
5972 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
5974 GTK_TREE_VIEW_DROP_BEFORE);
5976 /* don't propagate to parent though */
5982 /* If we left the current row's "open" zone, unset the timeout for
5985 gtk_tree_view_get_drag_dest_row (tree_view,
5989 if (old_dest_path &&
5990 (gtk_tree_path_compare (path, old_dest_path) != 0 ||
5991 !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
5992 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)))
5993 remove_open_timeout (tree_view);
5996 gtk_tree_path_free (old_dest_path);
5998 if (TRUE /* FIXME if the location droppable predicate */)
6000 GtkWidget *source_widget;
6002 *suggested_action = context->suggested_action;
6004 source_widget = gtk_drag_get_source_widget (context);
6006 if (source_widget == widget)
6008 /* Default to MOVE, unless the user has
6009 * pressed ctrl or alt to affect available actions
6011 if ((context->actions & GDK_ACTION_MOVE) != 0)
6012 *suggested_action = GDK_ACTION_MOVE;
6015 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
6020 /* can't drop here */
6021 remove_open_timeout (tree_view);
6023 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
6025 GTK_TREE_VIEW_DROP_BEFORE);
6032 gtk_tree_view_drag_motion (GtkWidget *widget,
6033 GdkDragContext *context,
6038 GtkTreePath *path = NULL;
6039 GtkTreeViewDropPosition pos;
6040 GtkTreeView *tree_view;
6041 GdkDragAction suggested_action = 0;
6044 tree_view = GTK_TREE_VIEW (widget);
6046 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6049 ensure_scroll_timeout (tree_view);
6051 gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
6055 /* Can't drop here. */
6056 gdk_drag_status (context, 0, time);
6060 if (tree_view->priv->open_dest_timeout == 0 &&
6061 (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER ||
6062 pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))
6064 tree_view->priv->open_dest_timeout =
6065 gtk_timeout_add (500, open_row_timeout, tree_view);
6068 if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
6070 /* Request data so we can use the source row when
6071 * determining whether to accept the drop
6073 set_status_pending (context, suggested_action);
6074 gtk_drag_get_data (widget, context, target, time);
6078 set_status_pending (context, 0);
6079 gdk_drag_status (context, suggested_action, time);
6084 gtk_tree_path_free (path);
6090 get_logical_dest_row (GtkTreeView *tree_view)
6093 /* adjust path to point to the row the drop goes in front of */
6094 GtkTreePath *path = NULL;
6095 GtkTreeViewDropPosition pos;
6097 gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos);
6102 if (pos == GTK_TREE_VIEW_DROP_BEFORE)
6104 else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE ||
6105 pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
6107 /* get first child, drop before it */
6108 gtk_tree_path_append_index (path, 0);
6112 g_assert (pos == GTK_TREE_VIEW_DROP_AFTER);
6113 gtk_tree_path_next (path);
6120 gtk_tree_view_drag_drop (GtkWidget *widget,
6121 GdkDragContext *context,
6126 GtkTreeView *tree_view;
6128 GdkDragAction suggested_action = 0;
6129 GdkAtom target = GDK_NONE;
6130 TreeViewDragInfo *di;
6131 GtkTreeModel *model;
6133 tree_view = GTK_TREE_VIEW (widget);
6135 model = gtk_tree_view_get_model (tree_view);
6137 remove_scroll_timeout (GTK_TREE_VIEW (widget));
6138 remove_open_timeout (GTK_TREE_VIEW (widget));
6140 di = get_info (tree_view);
6145 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6148 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6151 path = get_logical_dest_row (tree_view);
6153 if (target != GDK_NONE && path != NULL)
6155 /* in case a motion had requested drag data, change things so we
6156 * treat drag data receives as a drop.
6158 set_status_pending (context, 0);
6160 set_dest_row (context, model, path);
6164 gtk_tree_path_free (path);
6166 /* Unset this thing */
6167 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
6169 GTK_TREE_VIEW_DROP_BEFORE);
6171 if (target != GDK_NONE)
6173 gtk_drag_get_data (widget, context, target, time);
6181 gtk_tree_view_drag_data_received (GtkWidget *widget,
6182 GdkDragContext *context,
6185 GtkSelectionData *selection_data,
6190 TreeViewDragInfo *di;
6191 gboolean accepted = FALSE;
6192 GtkTreeModel *model;
6193 GtkTreeView *tree_view;
6194 GtkTreePath *dest_row;
6195 GdkDragAction suggested_action;
6197 tree_view = GTK_TREE_VIEW (widget);
6199 model = gtk_tree_view_get_model (tree_view);
6201 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6204 di = get_info (tree_view);
6209 suggested_action = get_status_pending (context);
6211 if (suggested_action)
6213 /* We are getting this data due to a request in drag_motion,
6214 * rather than due to a request in drag_drop, so we are just
6215 * supposed to call drag_status, not actually paste in the
6218 path = get_logical_dest_row (tree_view);
6221 suggested_action = 0;
6223 if (suggested_action)
6225 GtkTreeModel *src_model = NULL;
6226 GtkTreePath *src_path = NULL;
6228 if (!gtk_selection_data_get_tree_row (selection_data,
6231 suggested_action = 0;
6233 if (suggested_action)
6235 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6239 suggested_action = 0;
6241 gtk_tree_path_free (src_path);
6245 gdk_drag_status (context, suggested_action, time);
6248 gtk_tree_path_free (path);
6250 /* If you can't drop, remove user drop indicator until the next motion */
6251 if (suggested_action == 0)
6252 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
6254 GTK_TREE_VIEW_DROP_BEFORE);
6259 dest_row = get_dest_row (context);
6261 if (dest_row == NULL)
6264 if (selection_data->length >= 0)
6266 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6272 gtk_drag_finish (context,
6274 (context->action == GDK_ACTION_MOVE),
6277 gtk_tree_path_free (dest_row);
6280 set_dest_row (context, NULL, NULL);