diff options
author | Havoc Pennington <hp@redhat.com> | 2001-01-19 22:39:19 +0000 |
---|---|---|
committer | Havoc Pennington <hp@src.gnome.org> | 2001-01-19 22:39:19 +0000 |
commit | e248e4e79eb81478d62a42df12e9c11a837dd068 (patch) | |
tree | 1c1001af13a5e32ee9dd9637c8d70ea080af3eab /gtk | |
parent | 3da8e3c7dd846e48d745c9a1b4cafd399fd49517 (diff) | |
download | gtk+-e248e4e79eb81478d62a42df12e9c11a837dd068.tar.gz |
sync to tree changes
2001-01-19 Havoc Pennington <hp@redhat.com>
* demos/gtk-demo/main.c (button_press_event_cb): sync to tree changes
* gtk/gtkrbtree.c (_gtk_rbtree_node_find_offset): fix this
function
* gtk/gtktreeviewcolumn.c (gtk_tree_view_column_set_widget):
implement
* gtk/gtktreeview.c (gtk_tree_view_move_to): rename
scroll_to_cell, matches TextView scroll functions better
(gtk_tree_view_tree_to_widget_coords): new function
(gtk_tree_view_widget_to_tree_coords): new function
(gtk_tree_view_get_visible_rect): new function
(gtk_tree_view_get_path_at_pos): accept negative coordinates
(gtk_tree_view_draw_node_focus_rect): new function moved from
draw_focus, also, use width of bin_window as width of the focus
rect
(gtk_tree_view_expand_row): fix bug where it didn't recognize
already-expanded rows
(gtk_tree_view_get_cell_rect): new function
(gtk_tree_view_get_path_at_pos): return the click position
relative to the passed-in cell
(gtk_tree_view_set_expander_column): new function
* configure.in: remove gtk-config-2.0 chmod
* gtk/gtktextview.c (gtk_text_view_drag_motion): small cleanups,
and properly handle drags with targets we don't understand
(gtk_text_view_drag_end): don't stop scrolling, the source isn't
scrolling anyway
(gtk_text_view_drag_drop): stop scrolling here though, and set the
mark invisible
* gtk/gtkdnd.c (gtk_drag_dest_find_target): export as a public
function
(gtk_drag_dest_get_target_list): new function
(gtk_drag_dest_set_target_list): new function
* gtk/gtktreeview.c: Add a bunch of drag-and-drop implementation
* gtk/gtktreeprivate.h (struct _GtkTreeViewPrivate): add fields
related to drag-and-drop
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/gtkdnd.c | 96 | ||||
-rw-r--r-- | gtk/gtkdnd.h | 11 | ||||
-rw-r--r-- | gtk/gtkrbtree.c | 9 | ||||
-rw-r--r-- | gtk/gtkrbtree.h | 17 | ||||
-rw-r--r-- | gtk/gtktextview.c | 79 | ||||
-rw-r--r-- | gtk/gtktreeprivate.h | 15 | ||||
-rw-r--r-- | gtk/gtktreeview.c | 1724 | ||||
-rw-r--r-- | gtk/gtktreeview.h | 104 | ||||
-rw-r--r-- | gtk/gtktreeviewcolumn.c | 111 | ||||
-rw-r--r-- | gtk/gtktreeviewcolumn.h | 1 |
10 files changed, 1861 insertions, 306 deletions
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c index 78ca1245ef..9877213452 100644 --- a/gtk/gtkdnd.c +++ b/gtk/gtkdnd.c @@ -187,9 +187,6 @@ static gboolean gtk_drag_highlight_expose (GtkWidget *widget, GdkEventExpose *event, gpointer data); -static GdkAtom gtk_drag_dest_find_target (GtkWidget *widget, - GtkDragDestSite *site, - GdkDragContext *context); static void gtk_drag_selection_received (GtkWidget *widget, GtkSelectionData *selection_data, guint32 time, @@ -943,6 +940,58 @@ gtk_drag_dest_unset (GtkWidget *widget) gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL); } +/** + * gtk_drag_dest_get_target_list: + * @widget: a #GtkWidget + * + * Returns the list of targets this widget can accept from + * drag-and-drop. + * + * Return value: the #GtkTargetList, or %NULL if none + **/ +GtkTargetList* +gtk_drag_dest_get_target_list (GtkWidget *widget) +{ + GtkDragDestSite *site; + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + + return site ? site->target_list : NULL; +} + +/** + * gtk_drag_dest_set_target_list: + * @widget: a #GtkWidget that's a drag destination + * @target_list: list of droppable targets, or %NULL for none + * + * Sets the target types that this widget can accept from drag-and-drop. + * The widget must first be made into a drag destination with + * gtk_drag_dest_set(). + **/ +void +gtk_drag_dest_set_target_list (GtkWidget *widget, + GtkTargetList *target_list) +{ + GtkDragDestSite *site; + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + + if (site == NULL) + { + g_warning ("can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination"); + return; + } + + if (target_list) + gtk_target_list_ref (site->target_list); + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + site->target_list = target_list; +} + + /************************************************************* * gtk_drag_dest_handle_event: * Called from widget event handling code on Drag events @@ -1042,25 +1091,35 @@ gtk_drag_dest_handle_event (GtkWidget *toplevel, } } -/************************************************************* +/** * gtk_drag_dest_find_target: - * Decide on a target for the drag. - * arguments: - * site: - * context: - * results: - *************************************************************/ - -static GdkAtom -gtk_drag_dest_find_target (GtkWidget *widget, - GtkDragDestSite *site, - GdkDragContext *context) + * @widget: drag destination widget + * @context: drag context + * @dest_target_list: list of droppable targets + * + * Looks for a match between @context->targets and the + * @dest_target_list, returning the first matching target, otherwise + * returning %GDK_NONE. @dest_target_list should usually be the return + * value from gtk_drag_dest_get_target_list(), but some widgets may + * have different valid targets for different parts of the widget; in + * that case, they will have to implement a drag_motion handler that + * passes the correct target list to this function. + * + * Return value: first target that the source offers and the dest can accept, or %GDK_NONE + **/ +GdkAtom +gtk_drag_dest_find_target (GtkWidget *widget, + GdkDragContext *context, + GtkTargetList *dest_target_list) { GList *tmp_target; GList *tmp_source = NULL; GtkWidget *source_widget = gtk_drag_get_source_widget (context); - tmp_target = site->target_list->list; + if (dest_target_list == NULL) + return GDK_NONE; + + tmp_target = dest_target_list->list; while (tmp_target) { GtkTargetPair *pair = tmp_target->data; @@ -1532,7 +1591,7 @@ gtk_drag_dest_motion (GtkWidget *widget, } } - if (action && gtk_drag_dest_find_target (widget, site, context)) + if (action && gtk_drag_dest_find_target (widget, context, site->target_list)) { if (!site->have_drag) { @@ -1639,7 +1698,7 @@ gtk_drag_dest_drop (GtkWidget *widget, if (site->flags & GTK_DEST_DEFAULT_DROP) { - GdkAtom target = gtk_drag_dest_find_target (widget, site, context); + GdkAtom target = gtk_drag_dest_find_target (widget, context, site->target_list); if (target == GDK_NONE) return FALSE; @@ -2453,6 +2512,7 @@ gtk_drag_source_event_cb (GtkWidget *widget, i, event); info = gtk_drag_get_source_info (context, FALSE); + if (!info->icon_window) { if (site->pixmap) diff --git a/gtk/gtkdnd.h b/gtk/gtkdnd.h index e1c23b942b..1a589d349b 100644 --- a/gtk/gtkdnd.h +++ b/gtk/gtkdnd.h @@ -77,12 +77,15 @@ void gtk_drag_dest_set_proxy (GtkWidget *widget, GdkDragProtocol protocol, gboolean use_coordinates); -/* There probably should be functions for setting the targets - * as a GtkTargetList - */ - void gtk_drag_dest_unset (GtkWidget *widget); +GdkAtom gtk_drag_dest_find_target (GtkWidget *widget, + GdkDragContext *context, + GtkTargetList *target_list); +GtkTargetList* gtk_drag_dest_get_target_list (GtkWidget *widget); +void gtk_drag_dest_set_target_list (GtkWidget *widget, + GtkTargetList *target_list); + /* Source side */ void gtk_drag_source_set (GtkWidget *widget, diff --git a/gtk/gtkrbtree.c b/gtk/gtkrbtree.c index 0cc35821ef..8259b91fde 100644 --- a/gtk/gtkrbtree.c +++ b/gtk/gtkrbtree.c @@ -647,14 +647,19 @@ _gtk_rbtree_node_find_offset (GtkRBTree *tree, { last = node; node = node->parent; + + /* Add left branch, plus children, iff we came from the right */ if (node->right == last) - retval += node->left->offset + GTK_RBNODE_GET_HEIGHT (node); + retval += node->offset - node->right->offset; + if (node == tree->nil) { node = tree->parent_node; tree = tree->parent_tree; + + /* Add the parent node, plus the left branch. */ if (node) - retval += node->left->offset; + retval += node->left->offset + GTK_RBNODE_GET_HEIGHT (node); } } return retval; diff --git a/gtk/gtkrbtree.h b/gtk/gtkrbtree.h index 40b4c451b5..32aed44178 100644 --- a/gtk/gtkrbtree.h +++ b/gtk/gtkrbtree.h @@ -57,14 +57,17 @@ struct _GtkRBNode GtkRBNode *left; GtkRBNode *right; GtkRBNode *parent; - gint count; /* aggregate number of children we have */ - gint offset; /* aggregate of the heights of all our children */ - GtkRBTree *children; -}; -struct _GtkRBNodeView -{ - GtkRBNode parent; + /* count is the number of nodes beneath us, plus 1 for ourselves. + * i.e. node->left->count + node->right->count + 1 + */ + gint count; + + /* this is the total of sizes of + * node->left, node->right, our own height, and the height + * of all trees in ->children, iff children exists because + * the thing is expanded. + */ gint offset; GtkRBTree *children; }; diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index d1e21374ec..b895960dc2 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -4235,19 +4235,13 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view, gtk_target_list_unref (target_list); gtk_drag_set_icon_default (context); - - /* We're inside the selection, so start without being able - * to accept the drag. - */ - gdk_drag_status (context, 0, event->time); - gtk_text_mark_set_visible (text_view->dnd_mark, FALSE); } static void gtk_text_view_drag_begin (GtkWidget *widget, GdkDragContext *context) { - + /* do nothing */ } static void @@ -4257,14 +4251,6 @@ gtk_text_view_drag_end (GtkWidget *widget, GtkTextView *text_view; text_view = GTK_TEXT_VIEW (widget); - - gtk_text_mark_set_visible (text_view->dnd_mark, FALSE); - - if (text_view->scroll_timeout != 0) - { - gtk_timeout_remove (text_view->scroll_timeout); - text_view->scroll_timeout = 0; - } } static void @@ -4353,6 +4339,7 @@ gtk_text_view_drag_motion (GtkWidget *widget, GtkTextIter end; GdkRectangle target_rect; gint bx, by; + GdkDragAction suggested_action = 0; text_view = GTK_TEXT_VIEW (widget); @@ -4362,7 +4349,7 @@ gtk_text_view_drag_motion (GtkWidget *widget, y < target_rect.y || x > (target_rect.x + target_rect.width) || y > (target_rect.y + target_rect.height)) - return FALSE; /* outside the text window */ + return FALSE; /* outside the text window, allow parent widgets to handle event */ gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_WIDGET, @@ -4372,21 +4359,23 @@ gtk_text_view_drag_motion (GtkWidget *widget, gtk_text_layout_get_iter_at_pixel (text_view->layout, &newplace, bx, by); - - if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view), + + if (gtk_drag_dest_find_target (widget, context, + gtk_drag_dest_get_target_list (widget)) == GDK_NONE) + { + /* can't accept any of the offered targets */ + } + else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view), &start, &end) && gtk_text_iter_in_range (&newplace, &start, &end)) { /* We're inside the selection. */ - gdk_drag_status (context, 0, time); - gtk_text_mark_set_visible (text_view->dnd_mark, FALSE); } else { if (gtk_text_iter_editable (&newplace, text_view->editable)) { GtkWidget *source_widget; - GdkDragAction suggested_action; suggested_action = context->suggested_action; @@ -4400,20 +4389,26 @@ gtk_text_view_drag_motion (GtkWidget *widget, if ((context->actions & GDK_ACTION_MOVE) != 0) suggested_action = GDK_ACTION_MOVE; } - - gtk_text_mark_set_visible (text_view->dnd_mark, - text_view->cursor_visible); - - gdk_drag_status (context, suggested_action, time); } else { /* Can't drop here. */ - gdk_drag_status (context, 0, time); - gtk_text_mark_set_visible (text_view->dnd_mark, FALSE); } } + if (suggested_action != 0) + { + gtk_text_mark_set_visible (text_view->dnd_mark, + text_view->cursor_visible); + + gdk_drag_status (context, suggested_action, time); + } + else + { + gdk_drag_status (context, 0, time); + gtk_text_mark_set_visible (text_view->dnd_mark, FALSE); + } + gtk_text_buffer_move_mark (get_buffer (text_view), text_view->dnd_mark, &newplace); @@ -4427,7 +4422,10 @@ gtk_text_view_drag_motion (GtkWidget *widget, text_view->scroll_timeout = gtk_timeout_add (50, drag_scan_timeout, text_view); - + + /* TRUE return means don't propagate the drag motion to parent + * widgets that may also be drop sites. + */ return TRUE; } @@ -4438,18 +4436,17 @@ gtk_text_view_drag_drop (GtkWidget *widget, gint y, guint time) { -#if 0 - /* called automatically. */ - if (context->targets) - { - gtk_drag_get_data (widget, context, - GPOINTER_TO_INT (context->targets->data), - time); - return TRUE; - } - else - return FALSE; -#endif + GtkTextView *text_view; + + text_view = GTK_TEXT_VIEW (widget); + + if (text_view->scroll_timeout != 0) + gtk_timeout_remove (text_view->scroll_timeout); + + text_view->scroll_timeout = 0; + + gtk_text_mark_set_visible (text_view->dnd_mark, FALSE); + return TRUE; } diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h index c58e6acd4a..5debb33f66 100644 --- a/gtk/gtktreeprivate.h +++ b/gtk/gtktreeprivate.h @@ -69,6 +69,8 @@ struct _GtkTreeViewPrivate GdkWindow *bin_window; GdkWindow *header_window; + gint expander_column; + /* Selection stuff */ GtkTreePath *anchor; GtkTreePath *cursor; @@ -82,7 +84,6 @@ struct _GtkTreeViewPrivate /* Prelight information */ GtkRBNode *prelight_node; GtkRBTree *prelight_tree; - gint prelight_offset; /* Selection information */ GtkTreeSelection *selection; @@ -91,6 +92,18 @@ struct _GtkTreeViewPrivate gint n_columns; GList *columns; gint header_height; + + /* Scroll timeout (e.g. during dnd) */ + guint scroll_timeout; + + /* Row drag-and-drop */ + GtkTreePath *drag_dest_row; + GtkTreeViewDropPosition drag_dest_pos; + guint open_dest_timeout; + + gint pressed_button; + gint press_start_x; + gint press_start_y; }; #ifdef __GNUC__ diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 2b78f1ebe6..4836b0e28f 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -89,6 +89,41 @@ static void gtk_tree_view_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); +/* Source side drag signals */ +static void gtk_tree_view_drag_begin (GtkWidget *widget, + GdkDragContext *context); +static void gtk_tree_view_drag_end (GtkWidget *widget, + GdkDragContext *context); +static void gtk_tree_view_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time); +static void gtk_tree_view_drag_data_delete (GtkWidget *widget, + GdkDragContext *context); + +/* Target side drag signals */ +static void gtk_tree_view_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time); +static gboolean gtk_tree_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static gboolean gtk_tree_view_drag_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static void gtk_tree_view_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time); + /* tree_model signals */ static void gtk_tree_view_set_adjustments (GtkTreeView *tree_view, GtkAdjustment *hadj, @@ -111,10 +146,13 @@ static void gtk_tree_view_deleted (GtkTreeModel *model, /* Internal functions */ static void gtk_tree_view_draw_arrow (GtkTreeView *tree_view, + GtkRBTree *tree, GtkRBNode *node, - gint offset, gint x, gint y); +static void gtk_tree_view_get_arrow_range (GtkTreeView *tree_view, + gint *x1, + gint *x2); static gint gtk_tree_view_new_column_width (GtkTreeView *tree_view, gint i, gint *x); @@ -151,8 +189,8 @@ static void gtk_tree_view_button_clicked (GtkWidget *widget, static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, GtkRBTree *tree, GtkRBNode *node); - - +static gboolean gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, + GdkEventMotion *event); static GtkContainerClass *parent_class = NULL; @@ -211,6 +249,16 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) widget_class->focus_in_event = gtk_tree_view_focus_in; widget_class->focus_out_event = gtk_tree_view_focus_out; + widget_class->drag_begin = gtk_tree_view_drag_begin; + widget_class->drag_end = gtk_tree_view_drag_end; + widget_class->drag_data_get = gtk_tree_view_drag_data_get; + widget_class->drag_data_delete = gtk_tree_view_drag_data_delete; + + widget_class->drag_leave = gtk_tree_view_drag_leave; + widget_class->drag_motion = gtk_tree_view_drag_motion; + widget_class->drag_drop = gtk_tree_view_drag_drop; + widget_class->drag_data_received = gtk_tree_view_drag_data_received; + container_class->forall = gtk_tree_view_forall; container_class->remove = gtk_tree_view_remove; container_class->focus = gtk_tree_view_focus; @@ -241,13 +289,17 @@ gtk_tree_view_init (GtkTreeView *tree_view) tree_view->priv->button_pressed_node = NULL; tree_view->priv->button_pressed_tree = NULL; tree_view->priv->prelight_node = NULL; - tree_view->priv->prelight_offset = 0; tree_view->priv->header_height = 1; tree_view->priv->x_drag = 0; tree_view->priv->drag_pos = -1; tree_view->priv->selection = NULL; tree_view->priv->anchor = NULL; tree_view->priv->cursor = NULL; + + tree_view->priv->pressed_button = -1; + tree_view->priv->press_start_x = -1; + tree_view->priv->press_start_y = -1; + gtk_tree_view_set_adjustments (tree_view, NULL, NULL); _gtk_tree_view_set_size (tree_view, 0, 0); } @@ -417,6 +469,18 @@ gtk_tree_view_unrealize (GtkWidget *widget) tree_view = GTK_TREE_VIEW (widget); + if (tree_view->priv->scroll_timeout != 0) + { + gtk_timeout_remove (tree_view->priv->scroll_timeout); + tree_view->priv->scroll_timeout = 0; + } + + if (tree_view->priv->open_dest_timeout != 0) + { + gtk_timeout_remove (tree_view->priv->open_dest_timeout); + tree_view->priv->open_dest_timeout = 0; + } + /* FIXME where do we clear column->window for each column? */ gdk_window_set_user_data (tree_view->priv->bin_window, NULL); @@ -427,8 +491,8 @@ gtk_tree_view_unrealize (GtkWidget *widget) gdk_window_destroy (tree_view->priv->header_window); tree_view->priv->header_window = NULL; - gdk_gc_destroy (tree_view->priv->xor_gc); - + gdk_gc_destroy (tree_view->priv->xor_gc); + /* GtkWidget::unrealize destroys children and widget->window */ if (GTK_WIDGET_CLASS (parent_class)->unrealize) @@ -678,6 +742,141 @@ gtk_tree_view_size_allocate (GtkWidget *widget, gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed"); } +static void +gtk_tree_view_draw_node_focus_rect (GtkWidget *widget, + GtkTreePath *path) +{ + GtkTreeView *tree_view; + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + gint bin_window_width = 0; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_TREE_VIEW (widget)); + + tree_view = GTK_TREE_VIEW (widget); + + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + if (tree == NULL) + return; + + gdk_drawable_get_size (tree_view->priv->bin_window, + &bin_window_width, NULL); + + /* FIXME need a style function appropriate for this */ + gdk_draw_rectangle (tree_view->priv->bin_window, + widget->style->fg_gc[GTK_STATE_NORMAL], + FALSE, + 0, + _gtk_rbtree_node_find_offset (tree, node) + TREE_VIEW_HEADER_HEIGHT (tree_view), + bin_window_width - 2, + GTK_RBNODE_GET_HEIGHT (node)); +} + +GdkPixmap* +gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GtkTreeIter iter; + GtkRBTree *tree; + GtkRBNode *node; + GtkCellRenderer *cell; + gint i; + gint cell_offset; + gint max_height; + GList *list; + GdkRectangle background_area; + GtkWidget *widget; + gint depth; + /* start drawing inside the black outline */ + gint x = 1, y = 1; + GdkDrawable *drawable; + gint bin_window_width; + + widget = GTK_WIDGET (tree_view); + + depth = gtk_tree_path_get_depth (path); + + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + return NULL; + + if (!gtk_tree_model_get_iter (tree_view->priv->model, + &iter, + path)) + return NULL; + + max_height = GTK_RBNODE_GET_HEIGHT (node); + + cell_offset = x; + + background_area.y = y + TREE_VIEW_VERTICAL_SEPARATOR; + background_area.height = max_height - TREE_VIEW_VERTICAL_SEPARATOR; + + gdk_drawable_get_size (tree_view->priv->bin_window, + &bin_window_width, NULL); + + drawable = gdk_pixmap_new (tree_view->priv->bin_window, + bin_window_width + 2, + max_height + 2, + -1); + + gdk_draw_rectangle (drawable, + widget->style->base_gc[GTK_WIDGET_STATE (widget)], + TRUE, + 0, 0, + bin_window_width + 2, + max_height + 2); + + gdk_draw_rectangle (drawable, + widget->style->black_gc, + FALSE, + 0, 0, + bin_window_width + 1, + max_height + 1); + + for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) + { + GtkTreeViewColumn *column = list->data; + GdkRectangle cell_area; + + if (!column->visible) + continue; + + cell = column->cell; + gtk_tree_view_column_set_cell_data (column, + tree_view->priv->model, + &iter); + + background_area.x = cell_offset; + background_area.width = TREE_VIEW_COLUMN_WIDTH (column); + + cell_area = background_area; + + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS(tree_view)) + { + cell_area.x += depth * tree_view->priv->tab_offset; + cell_area.width -= depth * tree_view->priv->tab_offset; + } + + gtk_cell_renderer_render (cell, + drawable, + widget, + &background_area, + &cell_area, + NULL, + 0); + + cell_offset += TREE_VIEW_COLUMN_WIDTH (column); + } + + return drawable; +} + /* Warning: Very scary function. * Modify at your own risk */ @@ -692,6 +891,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, GtkRBNode *node, *last_node = NULL; GtkRBNode *cursor = NULL; GtkRBTree *cursor_tree = NULL, *last_tree = NULL; + GtkRBNode *drag_highlight = NULL; + GtkRBTree *drag_highlight_tree = NULL; GtkTreeIter iter; GtkCellRenderer *cell; gint new_y; @@ -702,7 +903,9 @@ gtk_tree_view_bin_expose (GtkWidget *widget, GdkRectangle cell_area; guint flags; gboolean last_selected; - + gint highlight_x; + gint bin_window_width; + g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); @@ -710,7 +913,7 @@ gtk_tree_view_bin_expose (GtkWidget *widget, if (tree_view->priv->tree == NULL) return TRUE; - + gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget)); /* we want to account for a potential HEADER offset. * That is, if the header exists, we want to offset our event by its @@ -741,6 +944,13 @@ gtk_tree_view_bin_expose (GtkWidget *widget, if (tree_view->priv->cursor) _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor); + if (tree_view->priv->drag_dest_row) + _gtk_tree_view_find_node (tree_view, tree_view->priv->drag_dest_row, + &drag_highlight_tree, &drag_highlight); + + gdk_drawable_get_size (tree_view->priv->bin_window, + &bin_window_width, NULL); + /* Actually process the expose event. To do this, we want to * start at the first node of the event, and walk the tree in * order, drawing each successive node. @@ -757,7 +967,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, x_offset = -event->area.x; cell_offset = 0; - + highlight_x = 0; /* should match x coord of first cell */ + background_area.y = y_offset + event->area.y + TREE_VIEW_VERTICAL_SEPARATOR; background_area.height = max_height - TREE_VIEW_VERTICAL_SEPARATOR; flags = 0; @@ -798,11 +1009,19 @@ gtk_tree_view_bin_expose (GtkWidget *widget, background_area.x = cell_offset; background_area.width = TREE_VIEW_COLUMN_WIDTH (column); - if (i == 0 && TREE_VIEW_DRAW_EXPANDERS(tree_view)) + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS(tree_view)) { cell_area = background_area; cell_area.x += depth*tree_view->priv->tab_offset; cell_area.width -= depth*tree_view->priv->tab_offset; + + /* If we have an expander column, the highlight underline + * starts with that column, so that it indicates which + * level of the tree we're dropping at. + */ + highlight_x = cell_area.x; + gtk_cell_renderer_render (cell, event->window, widget, @@ -815,8 +1034,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, gint x, y; gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, 0); gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), + tree, node, - event->area.y + y_offset, x, y); } } @@ -838,6 +1057,40 @@ gtk_tree_view_bin_expose (GtkWidget *widget, GTK_WIDGET_HAS_FOCUS (widget)) gtk_tree_view_draw_focus (widget); + if (node == drag_highlight) + { + /* Draw indicator for the drop + */ + gint highlight_y = -1; + + switch (tree_view->priv->drag_dest_pos) + { + case GTK_TREE_VIEW_DROP_BEFORE: + highlight_y = background_area.y - TREE_VIEW_VERTICAL_SEPARATOR/2; + break; + + case GTK_TREE_VIEW_DROP_AFTER: + highlight_y = background_area.y + background_area.height + TREE_VIEW_VERTICAL_SEPARATOR/2; + break; + + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + gtk_tree_view_draw_node_focus_rect (widget, + tree_view->priv->drag_dest_row); + break; + } + + if (highlight_y >= 0) + { + gdk_draw_line (event->window, + widget->style->black_gc, + highlight_x, + highlight_y, + bin_window_width - highlight_x, + highlight_y); + } + } + y_offset += max_height; if (node->children) { @@ -918,6 +1171,101 @@ gtk_tree_view_expose (GtkWidget *widget, } static gboolean +coords_are_over_arrow (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node, + /* these are in tree window coords */ + gint x, + gint y) +{ + GdkRectangle arrow; + gint x2; + + if ((node->flags & GTK_RBNODE_IS_PARENT) == 0) + return FALSE; + + arrow.y = _gtk_rbtree_node_find_offset (tree, node) + TREE_VIEW_HEADER_HEIGHT (tree_view); + + arrow.height = GTK_RBNODE_GET_HEIGHT (node); + + gtk_tree_view_get_arrow_range (tree_view, &arrow.x, &x2); + + arrow.width = x2 - arrow.x; + + return (x >= arrow.x && + x < (arrow.x + arrow.height) && + y >= arrow.y && + y < (arrow.y + arrow.height)); +} + +static void +do_unprelight (GtkTreeView *tree_view, + /* these are in tree window coords */ + gint x, + gint y) +{ + gint y1, y2; + + if (tree_view->priv->prelight_node == NULL) + return; + + y1 = _gtk_rbtree_node_find_offset (tree_view->priv->prelight_tree, + tree_view->priv->prelight_node) + + TREE_VIEW_HEADER_HEIGHT (tree_view); + + y2 = y1 + GTK_RBNODE_GET_HEIGHT (tree_view->priv->prelight_node); + + if (tree_view->priv->prelight_node) + GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); + + /* FIXME queue draw on y1-y2 range */ + + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT) && + !coords_are_over_arrow (tree_view, + tree_view->priv->prelight_tree, + tree_view->priv->prelight_node, + x, + y)) + /* We need to unprelight the old arrow. */ + { + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); + + gtk_tree_view_draw_arrow (tree_view, + tree_view->priv->prelight_tree, + tree_view->priv->prelight_node, + x, + y); + + } + + tree_view->priv->prelight_node = NULL; + tree_view->priv->prelight_tree = NULL; + + /* FIXME */ + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +static void +do_prelight (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node, + /* these are in tree window coords */ + gint x, + gint y) +{ + if (coords_are_over_arrow (tree_view, tree, node, x, y)) + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); + + tree_view->priv->prelight_node = node; + tree_view->priv->prelight_tree = tree; + + GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT); + + /* FIXME */ + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +static gboolean gtk_tree_view_motion (GtkWidget *widget, GdkEventMotion *event) { @@ -925,8 +1273,7 @@ gtk_tree_view_motion (GtkWidget *widget, GtkRBTree *tree; GtkRBNode *node; gint new_y; - gint y_offset; - + g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); @@ -956,39 +1303,20 @@ gtk_tree_view_motion (GtkWidget *widget, /* Sanity check it */ if (event->window != tree_view->priv->bin_window) return FALSE; + if (tree_view->priv->tree == NULL) return FALSE; - - if (tree_view->priv->prelight_node != NULL) - { - if ((((gint) event->y - TREE_VIEW_HEADER_HEIGHT (tree_view) < tree_view->priv->prelight_offset) || - ((gint) event->y - TREE_VIEW_HEADER_HEIGHT (tree_view) >= - (tree_view->priv->prelight_offset + GTK_RBNODE_GET_HEIGHT (tree_view->priv->prelight_node))) || - ((gint) event->x > tree_view->priv->tab_offset))) - /* We need to unprelight the old one. */ - { - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) - { - GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); - gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), - tree_view->priv->prelight_node, - tree_view->priv->prelight_offset, - event->x, - event->y); - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - } - - GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); - tree_view->priv->prelight_node = NULL; - tree_view->priv->prelight_tree = NULL; - tree_view->priv->prelight_offset = 0; - } - } + gtk_tree_view_maybe_begin_dragging_row (tree_view, + event); + + do_unprelight (tree_view, event->x, event->y); + new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y; - y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y - TREE_VIEW_HEADER_HEIGHT (tree_view), - &tree, - &node) + new_y - (gint)event->y; + + _gtk_rbtree_find_offset (tree_view->priv->tree, new_y - TREE_VIEW_HEADER_HEIGHT (tree_view), + &tree, + &node); if (node == NULL) return TRUE; @@ -998,23 +1326,8 @@ gtk_tree_view_motion (GtkWidget *widget, (tree_view->priv->button_pressed_node != node)) return TRUE; - /* Do we want to prelight a tab? */ - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - if (event->x <= tree_view->priv->tab_offset && - event->x >= 0 && - ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)) - { - tree_view->priv->prelight_offset = event->y+y_offset; - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - } - tree_view->priv->prelight_node = node; - tree_view->priv->prelight_tree = tree; - tree_view->priv->prelight_offset = event->y+y_offset; - - GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT); - /* FIXME */ - gtk_widget_queue_draw (widget); + do_prelight (tree_view, tree, node, event->x, new_y); return TRUE; } @@ -1030,7 +1343,6 @@ gtk_tree_view_enter_notify (GtkWidget *widget, GtkRBTree *tree; GtkRBNode *node; gint new_y; - gint y_offset; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); @@ -1040,6 +1352,7 @@ gtk_tree_view_enter_notify (GtkWidget *widget, /* Sanity check it */ if (event->window != tree_view->priv->bin_window) return FALSE; + if (tree_view->priv->tree == NULL) return FALSE; @@ -1049,31 +1362,16 @@ gtk_tree_view_enter_notify (GtkWidget *widget, /* find the node internally */ new_y = ((gint)event->y<TREE_VIEW_HEADER_HEIGHT (tree_view))?TREE_VIEW_HEADER_HEIGHT (tree_view):(gint)event->y; - y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, - new_y - TREE_VIEW_HEADER_HEIGHT (tree_view), - &tree, - &node) + new_y - (gint)event->y; + _gtk_rbtree_find_offset (tree_view->priv->tree, + new_y - TREE_VIEW_HEADER_HEIGHT (tree_view), + &tree, + &node); + if (node == NULL) return FALSE; - /* Do we want to prelight a tab? */ - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - if (event->x <= tree_view->priv->tab_offset && - event->x >= 0 && - ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT)) - { - tree_view->priv->prelight_offset = event->y+y_offset; - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - } - - tree_view->priv->prelight_node = node; - tree_view->priv->prelight_tree = tree; - tree_view->priv->prelight_offset = event->y+y_offset; - - GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PRELIT); - /* FIXME */ - gtk_widget_queue_draw (widget); + do_prelight (tree_view, tree, node, event->x, new_y); return TRUE; } @@ -1089,16 +1387,8 @@ gtk_tree_view_leave_notify (GtkWidget *widget, tree_view = GTK_TREE_VIEW (widget); - if (tree_view->priv->prelight_node != NULL) - { - GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); - tree_view->priv->prelight_node = NULL; - tree_view->priv->prelight_tree = NULL; - tree_view->priv->prelight_offset = 0; - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_ARROW_PRELIT); - /* FIXME */ - gtk_widget_queue_draw (widget); - } + do_unprelight (tree_view, -1000, -1000); /* coords not possibly over an arrow */ + return TRUE; } @@ -1132,8 +1422,10 @@ gtk_tree_view_button_press (GtkWidget *widget, if (!GTK_WIDGET_HAS_FOCUS (widget)) gtk_widget_grab_focus (widget); GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + /* are we in an arrow? */ - if (tree_view->priv->prelight_node != FALSE && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) + if (tree_view->priv->prelight_node && + GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) { if (event->button == 1) { @@ -1141,8 +1433,8 @@ gtk_tree_view_button_press (GtkWidget *widget, tree_view->priv->button_pressed_node = tree_view->priv->prelight_node; tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree; gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), + tree_view->priv->prelight_tree, tree_view->priv->prelight_node, - tree_view->priv->prelight_offset, event->x, event->y); } @@ -1178,7 +1470,8 @@ gtk_tree_view_button_press (GtkWidget *widget, continue; background_area.width = TREE_VIEW_COLUMN_WIDTH (column); - if (i == 0 && TREE_VIEW_DRAW_EXPANDERS(tree_view)) + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS(tree_view)) { cell_area = background_area; cell_area.x += depth*tree_view->priv->tab_offset; @@ -1227,6 +1520,16 @@ gtk_tree_view_button_press (GtkWidget *widget, break; } } + + /* Save press to possibly begin a drag + */ + if (tree_view->priv->pressed_button < 0) + { + tree_view->priv->pressed_button = event->button; + tree_view->priv->press_start_x = event->x; + tree_view->priv->press_start_y = event->y; + } + /* Handle the selection */ if (tree_view->priv->selection == NULL) tree_view->priv->selection = @@ -1287,6 +1590,9 @@ gtk_tree_view_button_release (GtkWidget *widget, tree_view = GTK_TREE_VIEW (widget); + if (tree_view->priv->pressed_button == event->button) + tree_view->priv->pressed_button = -1; + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE)) { gpointer drag_data; @@ -1318,7 +1624,8 @@ gtk_tree_view_button_release (GtkWidget *widget, if (event->button == 1) { gtk_grab_remove (widget); - if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) + if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node && + GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) { GtkTreePath *path; GtkTreeIter iter; @@ -1374,8 +1681,6 @@ static void gtk_tree_view_draw_focus (GtkWidget *widget) { GtkTreeView *tree_view; - GtkRBTree *cursor_tree = NULL; - GtkRBNode *cursor = NULL; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_TREE_VIEW (widget)); @@ -1387,18 +1692,7 @@ gtk_tree_view_draw_focus (GtkWidget *widget) if (tree_view->priv->cursor == NULL) return; - _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor); - if (cursor == NULL) - return; - - /* FIXME need a style function appropriate for this */ - gdk_draw_rectangle (tree_view->priv->bin_window, - widget->style->fg_gc[GTK_STATE_NORMAL], - FALSE, - 0, - _gtk_rbtree_node_find_offset (cursor_tree, cursor) + TREE_VIEW_HEADER_HEIGHT (tree_view), - (gint) MAX (tree_view->priv->width, tree_view->priv->hadjustment->upper), - GTK_RBNODE_GET_HEIGHT (cursor)); + gtk_tree_view_draw_node_focus_rect (widget, tree_view->priv->cursor); } @@ -2191,10 +2485,12 @@ gtk_tree_view_insert_iter_height (GtkTreeView *tree_view, { GtkTreeViewColumn *column; GtkCellRenderer *cell; - gboolean first = TRUE; GList *list; gint max_height = 0; + gint i; + i = 0; + /* do stuff with node */ for (list = tree_view->priv->columns; list; list = list->next) { @@ -2203,11 +2499,12 @@ gtk_tree_view_insert_iter_height (GtkTreeView *tree_view, if (!column->visible) continue; + if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) - { - first = FALSE;; - continue; - } + { + ++i; + continue; + } cell = column->cell; gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); @@ -2215,17 +2512,17 @@ gtk_tree_view_insert_iter_height (GtkTreeView *tree_view, gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, &height); max_height = MAX (max_height, TREE_VIEW_VERTICAL_SEPARATOR + height); - if (first == TRUE && TREE_VIEW_DRAW_EXPANDERS (tree_view)) + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS (tree_view)) gtk_tree_view_column_set_width (column, MAX (column->width, depth * tree_view->priv->tab_offset + width)); else gtk_tree_view_column_set_width (column, MAX (column->width, width)); - first = FALSE; + ++i; } return max_height; - } static void @@ -2313,7 +2610,8 @@ gtk_tree_view_calc_size (GtkTreeView *tree_view, if (column->dirty == FALSE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) continue; - if (i == 0 && TREE_VIEW_DRAW_EXPANDERS (tree_view)) + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS (tree_view)) gtk_tree_view_column_set_width (column, MAX (column->width, depth * tree_view->priv->tab_offset + width)); else @@ -2365,7 +2663,8 @@ gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view, { gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), &width, NULL); } - if (i == 0 && TREE_VIEW_DRAW_EXPANDERS (tree_view)) + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS (tree_view)) { if (depth * tree_view->priv->tab_offset + width > column->width) { @@ -2498,7 +2797,6 @@ gtk_tree_view_create_buttons (GtkTreeView *tree_view) { GtkWidget *alignment; GtkWidget *label; - GtkRequisition requisition; GList *list; GtkTreeViewColumn *column; gint i; @@ -2527,12 +2825,18 @@ gtk_tree_view_create_buttons (GtkTreeView *tree_view) alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); break; } - label = gtk_label_new (column->title); + + if (column->child) + label = column->child; + else + { + label = gtk_label_new (column->title); + gtk_widget_show (label); + } gtk_container_add (GTK_CONTAINER (alignment), label); gtk_container_add (GTK_CONTAINER (column->button), alignment); - - gtk_widget_show (label); + gtk_widget_show (alignment); } @@ -2671,27 +2975,78 @@ _gtk_tree_view_find_node (GtkTreeView *tree_view, while (1); } +static void +gtk_tree_view_get_arrow_range (GtkTreeView *tree_view, + gint *x1, + gint *x2) +{ + gint x_offset = 0; + GList *list; + GtkTreeViewColumn *tmp_column = NULL; + gint total_width; + gint i; + + i = 0; + total_width = 0; + for (list = tree_view->priv->columns; list; list = list->next) + { + tmp_column = list->data; + + if (i == tree_view->priv->expander_column) + { + x_offset = total_width; + break; + } + + if (tmp_column->visible) + total_width += tmp_column->width; + + ++i; + } + + if (x1) + *x1 = x_offset; + + if (tmp_column && tmp_column->visible) + { + /* +1 because x2 isn't included in the range. */ + if (x2) + *x2 = x_offset + tree_view->priv->tab_offset + 1; + } + else + { + /* return an empty range, the expander column is hidden */ + if (x2) + *x2 = x_offset; + } +} + /* x and y are the mouse position */ static void gtk_tree_view_draw_arrow (GtkTreeView *tree_view, + GtkRBTree *tree, GtkRBNode *node, - gint offset, gint x, gint y) { GdkRectangle area; GtkStateType state; - GdkPoint points[3]; GtkWidget *widget; + gint x_offset = 0; + gint y_offset = 0; if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT)) return; widget = GTK_WIDGET (tree_view); + + y_offset = _gtk_rbtree_node_find_offset (tree, node) + TREE_VIEW_HEADER_HEIGHT (tree_view); + + gtk_tree_view_get_arrow_range (tree_view, &x_offset, NULL); - area.x = 0; - area.y = offset + TREE_VIEW_VERTICAL_SEPARATOR; + area.x = x_offset; + area.y = y_offset + TREE_VIEW_VERTICAL_SEPARATOR; area.width = tree_view->priv->tab_offset - 2; area.height = GTK_RBNODE_GET_HEIGHT (node) - TREE_VIEW_VERTICAL_SEPARATOR; @@ -2945,9 +3300,6 @@ void gtk_tree_view_set_model (GtkTreeView *tree_view, GtkTreeModel *model) { - GList *list; - GtkTreeViewColumn *column; - g_return_if_fail (tree_view != NULL); g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); @@ -2987,6 +3339,10 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, g_list_free (tree_view->priv->columns); tree_view->priv->columns = NULL; #endif + + if (tree_view->priv->drag_dest_row) + gtk_tree_path_free (tree_view->priv->drag_dest_row); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP); } @@ -3427,8 +3783,57 @@ gtk_tree_view_get_column (GtkTreeView *tree_view, return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data); } +void +gtk_tree_view_set_expander_column (GtkTreeView *tree_view, + gint col) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (tree_view->priv->expander_column != col) + { + tree_view->priv->expander_column = col; + + /* g_object_notify (G_OBJECT (tree_view), "expander_column"); */ + } +} + +gint +gtk_tree_view_get_expander_column (GtkTreeView *tree_view) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + + return tree_view->priv->expander_column; +} + +/** + * gtk_tree_view_scroll_to_point: + * @tree_view: a #GtkTreeView + * @tree_x: X coordinate of new top-left pixel of visible area + * @tree_y: Y coordinate of new top-left pixel of visible area + * + * Scrolls the tree view such that the top-left corner of the visible + * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified + * in tree window coordinates. + **/ +void +gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, + gint tree_x, + gint tree_y) +{ + GtkAdjustment *hadj; + GtkAdjustment *vadj; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + hadj = tree_view->priv->hadjustment; + vadj = tree_view->priv->vadjustment; + + gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper)); + gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper)); +} + /** - * gtk_tree_view_move_to: + * gtk_tree_view_scroll_to_cell * @tree_view: A #GtkTreeView. * @path: The path of the row to move to. * @column: The #GtkTreeViewColumn to move horizontally to. @@ -3436,53 +3841,71 @@ gtk_tree_view_get_column (GtkTreeView *tree_view, * @col_align: The horizontal alignment of the column specified by @column. * * Moves the alignments of @tree_view to the position specified by - * @column and @path. If @column is NULL, then the first visible - * column is assumed, and the @tree_view is left justified. Likewise, - * if @path is NULL the first row is assumed, and the @tree_view is - * top justified. @row_align determines where the row is placed, and + * @column and @path. If @column is NULL, then no horizontal + * scrolling occurs. Likewise, if @path is NULL no vertical scrolling + * occurs. @row_align determines where the row is placed, and * @col_align determines where @column is placed. Both are expected * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means * right/bottom alignment, 0.5 means center. **/ void -gtk_tree_view_move_to (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gfloat row_align, - gfloat col_align) +gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gfloat row_align, + gfloat col_align) { - GtkRBNode *node = NULL; - GtkRBTree *tree = NULL; - + GdkRectangle cell_rect; + GdkRectangle vis_rect; + gint dest_x, dest_y; + + /* FIXME work on unmapped/unrealized trees? maybe implement when + * we do incremental reflow for trees + */ + g_return_if_fail (tree_view != NULL); g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (row_align >= 0.0); + g_return_if_fail (row_align <= 1.0); + g_return_if_fail (col_align >= 0.0); + g_return_if_fail (col_align <= 1.0); + + row_align = CLAMP (row_align, 0.0, 1.0); + col_align = CLAMP (col_align, 0.0, 1.0); - row_align = CLAMP (row_align, 0, 1); - col_align = CLAMP (col_align, 0, 1); + gtk_tree_view_get_cell_rect (tree_view, path, column, &cell_rect); + gtk_tree_view_get_visible_rect (tree_view, &vis_rect); - if (path != NULL) + dest_x = vis_rect.x; + dest_y = vis_rect.y; + + if (path) { - _gtk_tree_view_find_node (tree_view, path, - &tree, &node); - /* Should we justify it to the bottom? */ - if (node == NULL) - return; + dest_x = cell_rect.x + + cell_rect.width * row_align - + vis_rect.width * row_align; } - if (tree_view->priv->hadjustment && column >= 0) + if (column) { - /* FIXME -- write */ + dest_y = cell_rect.y + + cell_rect.height * col_align - + vis_rect.height * col_align; } + + gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y); } /** * gtk_tree_view_get_path_at_pos: * @tree_view: A #GtkTreeView. - * @window: The #GtkWindow to check against. + * @window: The #GdkWindow to check against. * @x: The x position to be identified. * @y: The y position to be identified. - * @path: A pointer to a #GtkTreePath pointer to be filled in, or NULL - * @column: A pointer to a #GtkTreeViewColumn pointer to be filled in, or NULL + * @path: A pointer to a #GtkTreePath pointer to be filled in, or %NULL + * @column: A pointer to a #GtkTreeViewColumn pointer to be filled in, or %NULL + * @cell_x: A pointer where the X coordinate relative to the cell can be placed, or %NULL + * @cell_y: A pointer where the Y coordinate relative to the cell can be placed, or %NULL * * Finds the path at the point (@x, @y) relative to @window. If @window is * NULL, then the point is found relative to the widget coordinates. This @@ -3490,7 +3913,8 @@ gtk_tree_view_move_to (GtkTreeView *tree_view, * passed in as @window. It is primarily for things like popup menus. If @path * is non-NULL, then it will be filled with the #GtkTreePath at that point. * This path should be freed with #gtk_tree_path_free. If @column is non-NULL, - * then it will be filled with the column at that point. + * then it will be filled with the column at that point. @cell_x and @cell_y return + * the coordinates relative to the cell. * * Return value: TRUE if a row exists at that coordinate. **/ @@ -3500,16 +3924,16 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, gint x, gint y, GtkTreePath **path, - GtkTreeViewColumn **column) - + GtkTreeViewColumn **column, + gint *cell_x, + gint *cell_y) { GtkRBTree *tree; GtkRBNode *node; - + gint y_offset; + g_return_val_if_fail (tree_view != NULL, FALSE); g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); - g_return_val_if_fail (x >= 0, FALSE); - g_return_val_if_fail (y >= 0, FALSE); g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); if (window) @@ -3523,12 +3947,17 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, if (x > tree_view->priv->hadjustment->upper) return FALSE; - if (column) + if (x < 0 || y < 0) + return FALSE; + + if (column || cell_x) { GtkTreeViewColumn *tmp_column; GtkTreeViewColumn *last_column = NULL; GList *list; - + gint remaining_x = x; + gboolean found = FALSE; + for (list = tree_view->priv->columns; list; list = list->next) { tmp_column = list->data; @@ -3537,43 +3966,137 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, continue; last_column = tmp_column; - if (x <= tmp_column->width) + if (remaining_x <= tmp_column->width) { - *column = tmp_column; + found = TRUE; + + if (column) + *column = tmp_column; + + if (cell_x) + *cell_x = remaining_x; + break; } - x -= tmp_column->width; + remaining_x -= tmp_column->width; } - if (*column == NULL) - *column = last_column; + if (!found) + { + if (column) + *column = last_column; + + if (cell_x) + *cell_x = last_column->width + remaining_x; + } } if (window) { - _gtk_rbtree_find_offset (tree_view->priv->tree, - y - TREE_VIEW_HEADER_HEIGHT (tree_view), - &tree, &node); + y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, + y - TREE_VIEW_HEADER_HEIGHT (tree_view), + &tree, &node); } else { if (y < TREE_VIEW_HEADER_HEIGHT (tree_view)) return FALSE; - _gtk_rbtree_find_offset (tree_view->priv->tree, y - TREE_VIEW_HEADER_HEIGHT (tree_view) + - tree_view->priv->vadjustment->value, - &tree, &node); + y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, y - TREE_VIEW_HEADER_HEIGHT (tree_view) + + tree_view->priv->vadjustment->value, + &tree, &node); } - + if (tree == NULL) return FALSE; + if (cell_y) + *cell_y = y_offset; + if (path) *path = _gtk_tree_view_find_path (tree_view, tree, node); return TRUE; } +/** + * gtk_tree_view_get_cell_rect: + * @tree_view: a #GtkTreeView + * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates + * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes + * @rect: rectangle to fill with cell rect + * + * Fills the bounding rectangle in tree window coordinates for the cell + * at the row specified by @path and the column specified by @column. + * If @path is %NULL, the y and height fields of the rectangle will be filled + * with 0. If @column is %NULL, the x and width fields will be filled with 0. + * + **/ +void +gtk_tree_view_get_cell_rect (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect) +{ + GtkTreeViewColumn *tmp_column = NULL; + gint total_width; + GList *list; + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (rect != NULL); + + rect->x = 0; + rect->y = 0; + rect->width = 0; + rect->height = 0; + + if (path) + { + /* Get vertical coords */ + + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + if (tree == NULL) + { + g_warning (G_STRLOC": no row corresponding to path"); + return; + } + + rect->y = _gtk_rbtree_node_find_offset (tree, node) + TREE_VIEW_HEADER_HEIGHT (tree_view); + + rect->height = GTK_RBNODE_GET_HEIGHT (node); + } + + if (column && column->visible) + { + total_width = 0; + for (list = tree_view->priv->columns; list; list = list->next) + { + tmp_column = list->data; + + if (tmp_column == column) + { + rect->x = total_width; + break; + } + + if (tmp_column->visible) + total_width += tmp_column->width; + } + + if (tmp_column != column) + { + g_warning (G_STRLOC": passed-in column isn't in the tree"); + return; + } + + rect->width = column->width; + } +} + static void gtk_tree_view_expand_all_helper (GtkRBTree *tree, GtkRBNode *node, @@ -3715,10 +4238,13 @@ gtk_tree_view_expand_row (GtkTreeView *tree_view, &node)) return FALSE; + if (node->children) + return TRUE; + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter)) return FALSE; - + node->children = _gtk_rbtree_new (); node->children->parent_tree = tree; node->children->parent_node = node; @@ -3781,3 +4307,849 @@ gtk_tree_view_collapse_row (GtkTreeView *tree_view, return TRUE; } +/** + * gtk_tree_view_get_visible_rect: + * @tree_view: a #GtkTreeView + * @visible_rect: rectangle to fill + * + * Fills @visible_rect with the currently-visible region of the + * buffer, in tree coordinates. Convert to widget coordinates with + * gtk_tree_view_tree_to_widget_coords(). Tree coordinates start at + * 0,0 for row 0 of the tree, and cover the entire scrollable area of + * the tree. + **/ +void +gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, + GdkRectangle *visible_rect) +{ + GtkWidget *widget; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + widget = GTK_WIDGET (tree_view); + + if (visible_rect) + { + visible_rect->x = tree_view->priv->hadjustment->value; + visible_rect->y = tree_view->priv->vadjustment->value; + visible_rect->width = widget->allocation.width; + visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); + } +} + +/** + * gtk_tree_view_widget_to_tree_coords: + * @tree_view: a #GtkTreeView + * @wx: widget X coordinate + * @wy: widget Y coordinate + * @tx: return location for tree X coordinate + * @ty: return location for tree Y coordinate + * + * Converts widget coordinates to coordinates for the + * tree window (the full scrollable area of the tree). + * + **/ +void +gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view, + gint wx, + gint wy, + gint *tx, + gint *ty) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (tx) + { + *tx = wx + tree_view->priv->hadjustment->value; + } + + if (ty) + { + *ty = wy - TREE_VIEW_HEADER_HEIGHT (tree_view) + + tree_view->priv->vadjustment->value; + } +} + +/** + * gtk_tree_view_tree_to_widget_coords: + * @tree_view: a #GtkTreeView + * @tx: tree X coordinate + * @ty: tree Y coordinate + * @wx: return location for widget X coordinate + * @wy: return location for widget Y coordinate + * + * Converts tree coordinates (coordinates in full scrollable + * area of the tree) to widget coordinates. + * + **/ +void +gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, + gint tx, + gint ty, + gint *wx, + gint *wy) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (wx) + { + *wx = tx - tree_view->priv->hadjustment->value; + } + + if (wy) + { + *wy = ty + TREE_VIEW_HEADER_HEIGHT (tree_view) - + tree_view->priv->vadjustment->value; + } +} + +/* Drag-and-drop */ + +typedef struct _TreeViewDragInfo TreeViewDragInfo; + +struct _TreeViewDragInfo +{ + GdkModifierType start_button_mask; + GtkTargetList *source_target_list; + GdkDragAction source_actions; + GClosure *row_draggable_closure; + GtkTreePath *source_row; + + GtkTargetList *dest_target_list; + GClosure *location_droppable_closure; + + guint source_set : 1; + guint dest_set : 1; +}; + +static TreeViewDragInfo* +get_info (GtkTreeView *tree_view) +{ + return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); +} + +static void +clear_source_info (TreeViewDragInfo *di) +{ + if (di->source_target_list) + gtk_target_list_unref (di->source_target_list); + + if (di->row_draggable_closure) + g_closure_unref (di->row_draggable_closure); + + if (di->source_row) + gtk_tree_path_free (di->source_row); + + di->source_target_list = NULL; + di->row_draggable_closure = NULL; + di->source_row = NULL; +} + +static void +clear_dest_info (TreeViewDragInfo *di) +{ + if (di->location_droppable_closure) + g_closure_unref (di->location_droppable_closure); + + if (di->dest_target_list) + gtk_target_list_unref (di->dest_target_list); + + di->location_droppable_closure = NULL; + di->dest_target_list = NULL; +} + +static void +destroy_info (TreeViewDragInfo *di) +{ + clear_source_info (di); + clear_dest_info (di); + g_free (di); +} + +static TreeViewDragInfo* +ensure_info (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + di = get_info (tree_view); + + if (di == NULL) + { + di = g_new0 (TreeViewDragInfo, 1); + + g_object_set_data_full (G_OBJECT (tree_view), + "gtk-tree-view-drag-info", + di, + (GDestroyNotify) destroy_info); + } + + return di; +} + +static void +remove_info (GtkTreeView *tree_view) +{ + g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL); +} + +#define SCROLL_EDGE_SIZE 15 + +static gint +drag_scan_timeout (gpointer data) +{ + GtkTreeView *tree_view; + gint x, y; + GdkModifierType state; + GtkTreePath *path = NULL; + GtkTreeViewColumn *column = NULL; + GdkRectangle visible_rect; + + tree_view = GTK_TREE_VIEW (data); + + gdk_window_get_pointer (tree_view->priv->bin_window, + &x, &y, &state); + + gtk_tree_view_get_visible_rect (tree_view, &visible_rect); + + /* See if we are near the edge. */ + if ((x - visible_rect.x) < SCROLL_EDGE_SIZE || + (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE || + (y - visible_rect.y) < SCROLL_EDGE_SIZE || + (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE) + { + gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + x, y, + &path, + &column, + NULL, + NULL); + + if (path != NULL) + { + gtk_tree_view_scroll_to_cell (tree_view, + path, + column, + 0.5, 0.5); + + gtk_tree_path_free (path); + } + } + + return TRUE; +} + + +static void +ensure_scroll_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->scroll_timeout == 0) + tree_view->priv->scroll_timeout = gtk_timeout_add (50, drag_scan_timeout, tree_view); +} + +static void +remove_scroll_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->scroll_timeout != 0) + { + gtk_timeout_remove (tree_view->priv->scroll_timeout); + tree_view->priv->scroll_timeout = 0; + } +} + +#ifdef __GNUC__ +#warning "implement g_closure_sink" +#endif +#define g_closure_sink(c) + +void +gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view, + GdkModifierType start_button_mask, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDraggableFunc row_draggable_func, + gpointer user_data) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = ensure_info (tree_view); + clear_source_info (di); + + di->start_button_mask = start_button_mask; + di->source_target_list = gtk_target_list_new (targets, n_targets); + di->source_actions = actions; + + if (row_draggable_func) + { + di->row_draggable_closure = g_cclosure_new ((GCallback) row_draggable_func, + user_data, NULL); + g_closure_ref (di->row_draggable_closure); + g_closure_sink (di->row_draggable_closure); + } + + di->source_set = TRUE; +} + +void +gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDroppableFunc location_droppable_func, + gpointer user_data) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + gtk_drag_dest_set (GTK_WIDGET (tree_view), + 0, + NULL, + 0, + actions); + + di = ensure_info (tree_view); + clear_dest_info (di); + + if (targets) + di->dest_target_list = gtk_target_list_new (targets, n_targets); + + if (location_droppable_func) + { + di->location_droppable_closure = g_cclosure_new ((GCallback) location_droppable_func, + user_data, NULL); + g_closure_ref (di->location_droppable_closure); + g_closure_sink (di->location_droppable_closure); + } + + di->dest_set = TRUE; +} + +void +gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = get_info (tree_view); + + if (di) + { + if (di->source_set) + { + clear_source_info (di); + di->source_set = FALSE; + } + + if (!di->dest_set && !di->source_set) + remove_info (tree_view); + } +} + +void +gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + di = get_info (tree_view); + + if (di) + { + if (di->dest_set) + { + gtk_drag_dest_unset (GTK_WIDGET (tree_view)); + clear_dest_info (di); + di->dest_set = FALSE; + } + + if (!di->dest_set && !di->source_set) + remove_info (tree_view); + } +} + + +void +gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewDropPosition pos) +{ + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ + + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (tree_view->priv->drag_dest_row) + { + /* FIXME queue undraw on previous location */ + gtk_tree_path_free (tree_view->priv->drag_dest_row); + } + + tree_view->priv->drag_dest_pos = pos; + + if (path) + tree_view->priv->drag_dest_row = gtk_tree_path_copy (path); + else + tree_view->priv->drag_dest_row = NULL; + + /* FIXME this is crap, queue draw only on the newly-highlighted row */ + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} + +gboolean +gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, + gint drag_x, + gint drag_y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos) +{ + gint cell_y; + gdouble offset_into_row; + gdouble quarter; + gint x, y; + GdkRectangle cell; + GtkTreeViewColumn *column = NULL; + GtkTreePath *tmp_path = NULL; + + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ + + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); + g_return_val_if_fail (drag_x >= 0, FALSE); + g_return_val_if_fail (drag_y >= 0, FALSE); + g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); + + if (path) + *path = NULL; + + /* remember that drag_x and drag_y are in widget coords, convert to rbtree */ + + gtk_tree_view_widget_to_tree_coords (tree_view, drag_x, drag_y, + &x, &y); + + /* If in the top quarter of a row, we drop before that row; if + * in the bottom quarter, drop after that row; if in the middle, + * and the row has children, drop into the row. + */ + + if (!gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + x, y, + &tmp_path, + &column, + NULL, + &cell_y)) + return FALSE; + + gtk_tree_view_get_cell_rect (tree_view, tmp_path, column, + &cell); + + offset_into_row = cell_y; + + if (path) + *path = tmp_path; + else + gtk_tree_path_free (tmp_path); + + tmp_path = NULL; + + quarter = cell.height / 4.0; + + if (pos) + { + if (offset_into_row < quarter) + { + *pos = GTK_TREE_VIEW_DROP_BEFORE; + } + else if (offset_into_row < quarter * 2) + { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + } + else if (offset_into_row < quarter * 3) + { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER; + } + else + { + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + } + + return TRUE; +} + +gboolean +gtk_selection_data_set_tree_row (GtkSelectionData *selection_data, + GtkTreeView *tree_view, + GtkTreePath *path) +{ + + +} + +gboolean +gtk_selection_data_get_tree_row (GtkSelectionData *selection_data, + GtkTreeView **tree_view, + GtkTreePath **path) +{ + + +} + + +static gboolean +gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, + GdkEventMotion *event) +{ + GdkDragContext *context; + TreeViewDragInfo *di; + GtkTreePath *path = NULL; + gint button; + gint cell_x, cell_y; + + di = get_info (tree_view); + + if (di == NULL) + return FALSE; + + if (tree_view->priv->pressed_button < 0) + return FALSE; + + if (!gtk_drag_check_threshold (GTK_WIDGET (tree_view), + tree_view->priv->press_start_x, + tree_view->priv->press_start_y, + event->x, event->y)) + return FALSE; + + button = tree_view->priv->pressed_button; + tree_view->priv->pressed_button = -1; + + gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + tree_view->priv->press_start_x, + tree_view->priv->press_start_y, + &path, + NULL, + &cell_x, + &cell_y); + + if (path == NULL) + return FALSE; + + /* FIXME if the path doesn't match the row_draggable predicate, + * return FALSE and free path + */ + + /* FIXME Check whether we're a start button, if not return FALSE and + * free path + */ + + context = gtk_drag_begin (GTK_WIDGET (tree_view), + di->source_target_list, + di->source_actions, + button, + (GdkEvent*)event); + + gtk_drag_set_icon_default (context); + + { + GdkPixmap *row_pix; + + row_pix = gtk_tree_view_create_row_drag_icon (tree_view, + path); + + gtk_drag_set_icon_pixmap (context, + gdk_drawable_get_colormap (row_pix), + row_pix, + NULL, + /* the + 1 is for the black border in the icon */ + tree_view->priv->press_start_x + 1, + cell_y + 1); + + gdk_pixmap_unref (row_pix); + } + + di->source_row = path; + + return TRUE; +} + +/* Default signal implementations for the drag signals */ + +static void +gtk_tree_view_drag_begin (GtkWidget *widget, + GdkDragContext *context) +{ + /* do nothing */ +} + +static void +gtk_tree_view_drag_end (GtkWidget *widget, + GdkDragContext *context) +{ + /* do nothing */ +} + +static void +gtk_tree_view_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (widget); + + if (selection_data->target == gdk_atom_intern ("GTK_TREE_VIEW_ROW", FALSE)) + { + TreeViewDragInfo *di; + + di = get_info (GTK_TREE_VIEW (widget)); + + if (di == NULL) + { + /* There's a race where someone could have unset + * drag source before the data is requested + */ + return; + } + + gtk_selection_data_set_tree_row (selection_data, + GTK_TREE_VIEW (widget), + di->source_row); + } +} + +static void +gtk_tree_view_drag_data_delete (GtkWidget *widget, + GdkDragContext *context) +{ + /* FIXME we need to delete the source_row if we're doing automagical + * reordering stuff. + */ +} + + +static void +remove_open_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->open_dest_timeout != 0) + { + gtk_timeout_remove (tree_view->priv->open_dest_timeout); + tree_view->priv->open_dest_timeout = 0; + } +} + +static void +gtk_tree_view_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time) +{ + TreeViewDragInfo *di; + + di = get_info (GTK_TREE_VIEW (widget)); + + /* unset any highlight row */ + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); +} + +static gint +open_row_timeout (gpointer data) +{ + GtkTreeView *tree_view = data; + + if (tree_view->priv->drag_dest_row && + (tree_view->priv->drag_dest_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + tree_view->priv->drag_dest_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + { + gtk_tree_view_expand_row (tree_view, + tree_view->priv->drag_dest_row, + FALSE); + tree_view->priv->open_dest_timeout = 0; + return FALSE; + } + else + return TRUE; +} + +static gboolean +gtk_tree_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkTreePath *path = NULL; + TreeViewDragInfo *di; + GtkTreeViewDropPosition pos; + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (widget); + + di = get_info (GTK_TREE_VIEW (widget)); + + if (di == NULL) + { + /* someone unset us as a drag dest, note that if + * we return FALSE drag_leave isn't called + */ + + gtk_tree_view_set_drag_dest_row (tree_view, + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); + + return FALSE; /* no longer a drop site */ + } + + ensure_scroll_timeout (tree_view); + + if (!gtk_tree_view_get_dest_row_at_pos (tree_view, + x, y, + &path, + &pos)) + { + /* can't drop here */ + remove_open_timeout (tree_view); + + gdk_drag_status (context, 0, time); + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + /* don't propagate to parent though */ + return TRUE; + } + + g_assert (path); + + /* If we left the current row's "open" zone, unset the timeout for + * opening the row + */ + if (tree_view->priv->drag_dest_row && + (gtk_tree_path_compare (path, tree_view->priv->drag_dest_row) != 0 || + !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))) + remove_open_timeout (tree_view); + + if (TRUE /* FIXME if the location droppable predicate */ && + gtk_drag_dest_find_target (widget, context, di->dest_target_list) != GDK_NONE) + { + GtkWidget *source_widget; + GdkDragAction suggested_action; + + suggested_action = context->suggested_action; + + source_widget = gtk_drag_get_source_widget (context); + + if (source_widget == widget) + { + /* Default to MOVE, unless the user has + * pressed ctrl or alt to affect available actions + */ + if ((context->actions & GDK_ACTION_MOVE) != 0) + suggested_action = GDK_ACTION_MOVE; + } + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + path, pos); + + if (tree_view->priv->open_dest_timeout == 0) + { + tree_view->priv->open_dest_timeout = + gtk_timeout_add (250, open_row_timeout, tree_view); + } + + gdk_drag_status (context, suggested_action, time); + } + else + { + /* can't drop here */ + remove_open_timeout (tree_view); + + gdk_drag_status (context, 0, time); + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + } + + gtk_tree_path_free (path); + + return TRUE; +} + +static gboolean +gtk_tree_view_drag_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GdkAtom target = GDK_NONE; + TreeViewDragInfo *di; + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (widget); + + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); + + di = get_info (tree_view); + + if (di && tree_view->priv->drag_dest_row && di->dest_target_list) + target = gtk_drag_dest_find_target (widget, context, di->dest_target_list); + + if (target != GDK_NONE) + { + gtk_drag_get_data (widget, context, target, time); + return TRUE; + } + else + return FALSE; +} + +static void +gtk_tree_view_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + if (selection_data->length >= 0) + { + /* FIXME respond to contents of selection_data */ + + } + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + gtk_drag_finish (context, + (selection_data->length >= 0), + (context->action == GDK_ACTION_MOVE), + time); +} + + diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h index 58709893ff..d15109eff2 100644 --- a/gtk/gtktreeview.h +++ b/gtk/gtktreeview.h @@ -22,11 +22,24 @@ #include <gtk/gtkcontainer.h> #include <gtk/gtktreemodel.h> #include <gtk/gtktreeviewcolumn.h> +#include <gtk/gtkdnd.h> #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +typedef enum +{ + /* drop before/after this row */ + GTK_TREE_VIEW_DROP_BEFORE, + GTK_TREE_VIEW_DROP_AFTER, + /* drop as a child of this row (with fallback to before or after + * if into is not possible) + */ + GTK_TREE_VIEW_DROP_INTO_OR_BEFORE, + GTK_TREE_VIEW_DROP_INTO_OR_AFTER +} GtkTreeViewDropPosition; + #define GTK_TYPE_TREE_VIEW (gtk_tree_view_get_type ()) #define GTK_TREE_VIEW(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_VIEW, GtkTreeView)) #define GTK_TREE_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_VIEW, GtkTreeViewClass)) @@ -89,8 +102,17 @@ gint gtk_tree_view_insert_column (GtkTreeView *tr GtkTreeViewColumn *gtk_tree_view_get_column (GtkTreeView *tree_view, gint n); +void gtk_tree_view_set_expander_column (GtkTreeView *tree_view, + gint col); + +gint gtk_tree_view_get_expander_column (GtkTreeView *tree_view); + /* Actions */ -void gtk_tree_view_move_to (GtkTreeView *tree_view, +void gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, + gint tree_x, + gint tree_y); + +void gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gfloat row_align, @@ -100,7 +122,14 @@ gboolean gtk_tree_view_get_path_at_pos (GtkTreeView *tr gint x, gint y, GtkTreePath **path, - GtkTreeViewColumn **column); + GtkTreeViewColumn **column, + gint *cell_x, + gint *cell_y); +void gtk_tree_view_get_cell_rect (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect); + void gtk_tree_view_expand_all (GtkTreeView *tree_view); void gtk_tree_view_collapse_all (GtkTreeView *tree_view); gboolean gtk_tree_view_expand_row (GtkTreeView *tree_view, @@ -109,6 +138,77 @@ gboolean gtk_tree_view_expand_row (GtkTreeView *tr gboolean gtk_tree_view_collapse_row (GtkTreeView *tree_view, GtkTreePath *path); +void gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, + GdkRectangle *visible_rect); +void gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view, + gint wx, + gint wy, + gint *tx, + gint *ty); +void gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, + gint tx, + gint ty, + gint *wx, + gint *wy); + + + +/* Drag-and-Drop support */ + +typedef gboolean (* GtkTreeViewDraggableFunc) (GtkTreeView *tree_view, + GdkDragContext *context, + GtkTreePath *path, + gpointer user_data); + +/* this func can change "pos" if it likes, in addition to returning + * true/false for whether a drop is possible + */ +typedef gboolean (* GtkTreeViewDroppableFunc) (GtkTreeView *tree_view, + GdkDragContext *context, + GtkTreePath *path, + GtkTreeViewDropPosition *pos, + gpointer user_data); + +void gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view, + GdkModifierType start_button_mask, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDraggableFunc row_draggable_func, + gpointer user_data); +void gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDroppableFunc location_droppable_func, + gpointer user_data); + +void gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view); +void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view); + +/* These are useful to implement your own custom stuff. */ +void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewDropPosition pos); +gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, + gint drag_x, + gint drag_y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos); +GdkPixmap* gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, + GtkTreePath *path); + +/* The selection data would normally have target type GTK_TREE_VIEW_ROW in this + * case. If the target is wrong these functions return FALSE. + */ +gboolean gtk_selection_data_set_tree_row (GtkSelectionData *selection_data, + GtkTreeView *tree_view, + GtkTreePath *path); +gboolean gtk_selection_data_get_tree_row (GtkSelectionData *selection_data, + GtkTreeView **tree_view, + GtkTreePath **path); + + #ifdef __cplusplus } diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index 9efa9e1d82..140bbb6a7e 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -914,6 +914,48 @@ gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column) gtk_signal_emit (GTK_OBJECT (tree_column), tree_column_signals[CLICKED]); } +static void +update_button_contents (GtkTreeViewColumn *tree_column) +{ + if (tree_column->button) + { + GtkWidget *current_child = GTK_BIN (GTK_BIN (tree_column->button)->child)->child; + + if (tree_column->child) + { + if (current_child != tree_column->child) + { + gtk_container_remove (GTK_CONTAINER (tree_column->button), + current_child); + + gtk_container_add (GTK_CONTAINER (tree_column->button), + tree_column->child); + } + } + else + { + if (current_child == NULL) + { + current_child = gtk_label_new (NULL); + + gtk_widget_show (current_child); + + gtk_container_add (GTK_CONTAINER (tree_column->button), + current_child); + } + + g_return_if_fail (GTK_IS_LABEL (current_child)); + + if (tree_column->title) + gtk_label_set_text (GTK_LABEL (current_child), + tree_column->title); + else + gtk_label_set_text (GTK_LABEL (current_child), + ""); + } + } +} + /** * gtk_tree_view_column_set_title: * @tree_column: A #GtkTreeViewColumn. @@ -924,7 +966,7 @@ gtk_tree_view_column_clicked (GtkTreeViewColumn *tree_column) **/ void gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column, - gchar *title) + gchar *title) { g_return_if_fail (tree_column != NULL); g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); @@ -935,20 +977,7 @@ gtk_tree_view_column_set_title (GtkTreeViewColumn *tree_column, else tree_column->title = NULL; - /* Hmmm. This is a little ugly... */ - if (tree_column->button) - { - if (GTK_BIN (tree_column->button)->child && - GTK_IS_ALIGNMENT (GTK_BIN (tree_column->button)->child)) - { - if (GTK_BIN (GTK_BIN (tree_column->button)->child)->child && - GTK_IS_LABEL (GTK_BIN (GTK_BIN (tree_column->button)->child)->child)) - { - gtk_label_set_text (GTK_LABEL (GTK_BIN (GTK_BIN (tree_column->button)->child)->child), - tree_column->title); - } - } - } + update_button_contents (tree_column); g_object_notify (G_OBJECT (tree_column), "title"); } @@ -1047,45 +1076,21 @@ void gtk_tree_view_column_set_widget (GtkTreeViewColumn *tree_column, GtkWidget *widget) { - /* FIXME: Implement this function. */ -#if 0 - gint new_button = 0; - GtkWidget *old_widget; - - g_return_if_fail (tree_view != NULL); - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (column < 0 || column >= tree_view->priv->columns) - return; - - /* if the column button doesn't currently exist, - * it has to be created first */ - if (!column->button) - { - column_button_create (tree_view, column); - new_button = 1; - } - - column_title_new (clist, column, NULL); - - /* remove and destroy the old widget */ - old_widget = GTK_BIN (clist->column[column].button)->child; - if (old_widget) - gtk_container_remove (GTK_CONTAINER (clist->column[column].button), - old_widget); + g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column)); + g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); - /* add and show the widget */ if (widget) { - gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget); - gtk_widget_show (widget); + gtk_object_ref (GTK_OBJECT (widget)); + gtk_object_sink (GTK_OBJECT (widget)); } - /* if this button didn't previously exist, then the - * column button positions have to be re-computed */ - if (GTK_WIDGET_VISIBLE (clist) && new_button) - size_allocate_title_buttons (clist); -#endif + if (tree_column->child) + gtk_object_unref (GTK_OBJECT (tree_column->child)); + + tree_column->child = widget; + + update_button_contents (tree_column); g_object_notify (G_OBJECT (tree_column), "widget"); } @@ -1106,10 +1111,7 @@ gtk_tree_view_column_get_widget (GtkTreeViewColumn *tree_column) g_return_val_if_fail (tree_column != NULL, NULL); g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (tree_column), NULL); - if (tree_column->button) - return GTK_BUTTON (tree_column->button)->child; - - return NULL; + return tree_column->child; } /** @@ -1134,8 +1136,6 @@ gtk_tree_view_column_set_justification (GtkTreeViewColumn *tree_column, tree_column->justification = justification; - /* change the alignment of the button title if it's not a - * custom widget */ alignment = GTK_BIN (tree_column->button)->child; if (GTK_IS_ALIGNMENT (alignment)) @@ -1165,3 +1165,4 @@ gtk_tree_view_column_set_justification (GtkTreeViewColumn *tree_column, g_object_notify (G_OBJECT (tree_column), "justification"); } + diff --git a/gtk/gtktreeviewcolumn.h b/gtk/gtktreeviewcolumn.h index 7a98ff6dbc..ea13946c4b 100644 --- a/gtk/gtktreeviewcolumn.h +++ b/gtk/gtktreeviewcolumn.h @@ -55,6 +55,7 @@ struct _GtkTreeViewColumn GtkWidget *tree_view; GtkWidget *button; + GtkWidget *child; GdkWindow *window; GtkJustification justification; |