diff options
author | Dave Camp <dave@ximian.com> | 2003-03-26 16:14:50 +0000 |
---|---|---|
committer | Dave Camp <campd@src.gnome.org> | 2003-03-26 16:14:50 +0000 |
commit | f755bfc133c4705734ff6cbc4e48602592f23030 (patch) | |
tree | 17d7d5d104e9f79f5b65324a8b7890b206be7f7e | |
parent | b793c9d66be23f096f6ac50e5aff4b52da5b85f9 (diff) | |
download | nautilus-f755bfc133c4705734ff6cbc4e48602592f23030.tar.gz |
Implement the widget side drag and drop here instead of eggtreemultidnd.c.
2003-03-26 Dave Camp <dave@ximian.com>
* src/file-manager/fm-list-view.c: (activate_selected_items),
(fm_list_view_did_not_drag), (drag_data_get_callback),
(selection_foreach), (get_selection_refs), (ref_list_free),
(stop_drag_check), (get_drag_pixbuf), (motion_notify_callback),
(button_event_modifies_selection), (button_press_callback),
(button_release_callback), (key_press_callback),
(create_and_set_up_tree_view), (fm_list_view_finalize): Implement
the widget side drag and drop here instead of eggtreemultidnd.c.
Clean up mouse event handling to be more similar to the icon view.
Fixes bug #48051, #89980, #90437, #105593, #108946, among others.
Thanks to marten ter borgh <marten@terborgh.demon.nl> for help
with this patch.
-rw-r--r-- | ChangeLog | 17 | ||||
-rw-r--r-- | src/file-manager/fm-list-view.c | 394 |
2 files changed, 358 insertions, 53 deletions
@@ -1,3 +1,20 @@ +2003-03-26 Dave Camp <dave@ximian.com> + + * src/file-manager/fm-list-view.c: (activate_selected_items), + (fm_list_view_did_not_drag), (drag_data_get_callback), + (selection_foreach), (get_selection_refs), (ref_list_free), + (stop_drag_check), (get_drag_pixbuf), (motion_notify_callback), + (button_event_modifies_selection), (button_press_callback), + (button_release_callback), (key_press_callback), + (create_and_set_up_tree_view), (fm_list_view_finalize): Implement + the widget side drag and drop here instead of eggtreemultidnd.c. + Clean up mouse event handling to be more similar to the icon view. + + Fixes bug #48051, #89980, #90437, #105593, #108946, among others. + + Thanks to marten ter borgh <marten@terborgh.demon.nl> for help + with this patch. + 2003-03-26 Alexander Larsson <alexl@redhat.com> * libnautilus-private/nautilus-icon-private.h: diff --git a/src/file-manager/fm-list-view.c b/src/file-manager/fm-list-view.c index 78e324328..e40185125 100644 --- a/src/file-manager/fm-list-view.c +++ b/src/file-manager/fm-list-view.c @@ -31,6 +31,7 @@ #include "fm-error-reporting.h" #include "fm-list-model.h" #include <eel/eel-cell-renderer-pixbuf-list.h> +#include <eel/eel-glib-extensions.h> #include <gdk/gdkkeysyms.h> #include <gtk/gtkcellrendererpixbuf.h> #include <gtk/gtkcellrenderertext.h> @@ -67,6 +68,16 @@ struct FMListViewDetails { NautilusScrollPositionable *positionable; NautilusTreeViewDragDest *drag_dest; + + GtkTargetList *source_target_list; + + GtkTreePath *double_click_path[2]; /* Both clicks in a double click need to be on the same row */ + + guint drag_button; + int drag_x; + int drag_y; + + gboolean drag_started; }; /* @@ -131,6 +142,218 @@ tree_view_has_selection (GtkTreeView *view) return tree_selection_not_empty (gtk_tree_view_get_selection (view)); } +static void +activate_selected_items (FMListView *view) +{ + GList *file_list; + + file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view)); + fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view), + file_list); + nautilus_file_list_free (file_list); + +} + +static gboolean +button_event_modifies_selection (GdkEventButton *event) +{ + return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0; +} + +static void +fm_list_view_did_not_drag (FMListView *view, + GdkEventButton *event) +{ + GtkTreeView *tree_view; + GtkTreeSelection *selection; + GtkTreePath *path; + + tree_view = view->details->tree_view; + selection = gtk_tree_view_get_selection (tree_view); + + if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, + &path, NULL, NULL, NULL)) { + if(event->button == 1 + && (click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE) + && gtk_tree_selection_path_is_selected (selection, path) + && !button_event_modifies_selection (event)) { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, path); + } + + if ((event->button == 1) + && (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) + && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) { + activate_selected_items (view); + } + gtk_tree_path_free (path); + } + +} + +static void +drag_data_get_callback (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkTreeView *tree_view; + GtkTreeModel *model; + GList *ref_list; + + tree_view = GTK_TREE_VIEW (widget); + + model = gtk_tree_view_get_model (tree_view); + + if (model == NULL) { + return; + } + + ref_list = g_object_get_data (G_OBJECT (context), "drag-info"); + + if (ref_list == NULL) { + return; + } + + if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model)) + { + egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model), + ref_list, + selection_data); + } +} + +static void +selection_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list; + + list = (GList**)data; + + *list = g_list_prepend (*list, + gtk_tree_row_reference_new (model, path)); + +} + +static GList * +get_selection_refs (GtkTreeView *tree_view) +{ + GtkTreeSelection *selection; + GList *ref_list; + + ref_list = NULL; + + selection = gtk_tree_view_get_selection (tree_view); + gtk_tree_selection_selected_foreach (selection, + selection_foreach, + &ref_list); + ref_list = g_list_reverse (ref_list); + + return ref_list; +} + +static void +ref_list_free (GList *ref_list) +{ + g_list_foreach (ref_list, (GFunc) gtk_tree_row_reference_free, NULL); + g_list_free (ref_list); +} + +static void +stop_drag_check (FMListView *view) +{ + view->details->drag_button = 0; +} + +static GdkPixbuf * +get_drag_pixbuf (FMListView *view, int *offset_x, int *offset_y) +{ + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + GdkPixbuf *ret; + GdkRectangle cell_area; + + ret = NULL; + + if (gtk_tree_view_get_path_at_pos (view->details->tree_view, + view->details->drag_x, + view->details->drag_y, + &path, NULL, NULL, NULL)) { + model = gtk_tree_view_get_model (view->details->tree_view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + fm_list_model_get_column_id_from_zoom_level (view->details->zoom_level), + &ret, + -1); + + gtk_tree_view_get_cell_area (view->details->tree_view, + path, + view->details->file_name_column, + &cell_area); + *offset_x = view->details->drag_x - cell_area.x; + *offset_y = view->details->drag_y - cell_area.y; + + gtk_tree_path-free (path); + } + + return ret; +} + +static gboolean +motion_notify_callback (GtkWidget *widget, + GdkEventMotion *event, + gpointer callback_data) +{ + FMListView *view; + GdkDragContext *context; + GList *ref_list; + GdkPixbuf *pixbuf; + int offset_x, offset_y; + + view = FM_LIST_VIEW (callback_data); + + if (view->details->drag_button != 0) { + if (gtk_drag_check_threshold (widget, + view->details->drag_x, + view->details->drag_y, + event->x, + event->y)) { + context = gtk_drag_begin + (widget, + view->details->source_target_list, + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK, + view->details->drag_button, + (GdkEvent*)event); + + stop_drag_check (view); + view->details->drag_started = TRUE; + + ref_list = get_selection_refs (GTK_TREE_VIEW (widget)); + g_object_set_data_full (G_OBJECT (context), + "drag-info", + ref_list, + (GDestroyNotify)ref_list_free); + + pixbuf = get_drag_pixbuf (view, &offset_x, &offset_y); + if (pixbuf) { + gtk_drag_set_icon_pixbuf (context, + pixbuf, + offset_x, + offset_y); + g_object_unref (pixbuf); + } else { + gtk_drag_set_icon_default (context); + } + } + } + return TRUE; +} + static gboolean button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callback_data) { @@ -138,12 +361,18 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba GtkTreeView *tree_view; GtkTreePath *path; gboolean call_parent; + gboolean allow_drag; + GtkTreeSelection *selection; GtkWidgetClass *tree_view_class; - GList *file_list; + gint64 current_time; + static gint64 last_click_time = 0; + static int click_count = 0; + int double_click_time; view = FM_LIST_VIEW (callback_data); tree_view = GTK_TREE_VIEW (widget); tree_view_class = GTK_WIDGET_GET_CLASS (tree_view); + selection = gtk_tree_view_get_selection(tree_view); if (event->window != gtk_tree_view_get_bin_window (tree_view)) { return FALSE; @@ -153,56 +382,89 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba (FM_LIST_MODEL (gtk_tree_view_get_model (tree_view)), tree_view, event->x, event->y); + + g_object_get (G_OBJECT (gtk_widget_get_settings (widget)), + "gtk-double-click-time", &double_click_time, + NULL); + + /* Determine click count */ + current_time = eel_get_system_time (); + if (current_time - last_click_time < double_click_time * 1000) { + click_count++; + } else { + click_count = 0; + } + + /* Stash time for next compare */ + last_click_time = current_time; + + /* Ignore double click if we are in single click mode */ + if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE && click_count >= 2) { + return TRUE; + } call_parent = TRUE; + allow_drag = FALSE; if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, &path, NULL, NULL, NULL)) { - if ((event->button == 3 || - (event->button == 1 && click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE)) - && gtk_tree_selection_path_is_selected (gtk_tree_view_get_selection (tree_view), path)) { - /* Don't let the default code run because if - multiple rows are selected it will unselect - all but one row; but we- want the right - click menu or single click to apply to - everything that's currently selected. */ + if (event->button == 1 && + event->type == GDK_BUTTON_PRESS) { + if (view->details->double_click_path[1]) { + gtk_tree_path_free (view->details->double_click_path[1]); + } + view->details->double_click_path[1] = view->details->double_click_path[0]; + view->details->double_click_path[0] = gtk_tree_path_copy (path); + } + + if (event->type == GDK_2BUTTON_PRESS && + (event->button == 1 || event->button == 3)) { + if (view->details->double_click_path[1] && + gtk_tree_path_compare (view->details->double_click_path[0], view->details->double_click_path[1]) == 0 + && !button_event_modifies_selection (event)) { + activate_selected_items (view); + + } + } + + /* We're going to filter out some situations where + * we can't let the default code run because all + * but one row would be would be deselected. We don't + * want that; we want the right click menu or single + * click to apply to everything that's currently selected. */ + + if (event->button == 3 && gtk_tree_selection_path_is_selected (selection, path)) { + call_parent = FALSE; + } + + if(!button_event_modifies_selection (event) && event->button == 1 && gtk_tree_selection_path_is_selected (selection, path)) { call_parent = FALSE; } - + + if (call_parent) { + tree_view_class->button_press_event (widget, event); + } + + if (event->button == 1 || event->button == 2) { + view->details->drag_started = FALSE; + view->details->drag_button = event->button; + view->details->drag_x = event->x; + view->details->drag_y = event->y; + } + + if (event->button == 3) { + if (tree_view_has_selection (tree_view)) { + fm_directory_view_pop_up_selection_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event); + } else { + fm_directory_view_pop_up_background_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event); + } + } + gtk_tree_path_free (path); } else { /* Deselect if people click outside any row. It's OK to let default code run; it won't reselect anything. */ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view)); } - - /* Instead of doing this, this list view should probably be a - * derived widget. I'm doing this quick hack because I'm hoping - * that gtktreeview will have the input modes thing in 2.4, - * getting rid of this altogether. - * If we still need this in 2.4, we should rewrite this widget - * as a derived class. */ - if (call_parent) { - tree_view_class->button_press_event (widget, event); - } - - if (event->button == 3) { - if (tree_view_has_selection (GTK_TREE_VIEW (widget))) { - fm_directory_view_pop_up_selection_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event); - } else { - fm_directory_view_pop_up_background_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event); - } - } else if (event->button == 1) { - if ((event->type == GDK_BUTTON_PRESS - && click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE - && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) - || (event->type == GDK_2BUTTON_PRESS - && click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE)) { - file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view)); - fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view), - file_list); - nautilus_file_list_free (file_list); - } - } /* We chained to the default handler in this method, so never * let the default handler run */ @@ -210,10 +472,28 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba } static gboolean +button_release_callback (GtkWidget *widget, + GdkEventButton *event, + gpointer callback_data) +{ + FMListView *view; + + view = FM_LIST_VIEW (callback_data); + + if (event->button == view->details->drag_button) { + stop_drag_check (view); + if (!view->details->drag_started) { + fm_list_view_did_not_drag (view, event); + return TRUE; + } + } + return FALSE; +} + +static gboolean key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_data) { FMDirectoryView *view; - GList *file_list; view = FM_DIRECTORY_VIEW (callback_data); @@ -221,9 +501,7 @@ key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_dat case GDK_space: case GDK_Return: case GDK_KP_Enter: - file_list = fm_list_view_get_selection (view); - fm_directory_view_activate_files (view, file_list); - nautilus_file_list_free (file_list); + activate_selected_items (FM_LIST_VIEW (view)); return TRUE; default: @@ -359,16 +637,7 @@ create_and_set_up_tree_view (FMListView *view) view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); fm_list_model_get_drag_types (&drag_types, &num_drag_types); - - egg_tree_multi_drag_add_drag_support (view->details->tree_view); - - gtk_tree_view_enable_model_drag_source - (view->details->tree_view, - GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, - drag_types, - num_drag_types, - GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK); - + view->details->drag_dest = nautilus_tree_view_drag_dest_new (view->details->tree_view); @@ -389,8 +658,14 @@ create_and_set_up_tree_view (FMListView *view) "changed", G_CALLBACK (list_selection_changed_callback), view, 0); + g_signal_connect_object (view->details->tree_view, "drag_data_get", + G_CALLBACK (drag_data_get_callback), view, 0); + g_signal_connect_object (view->details->tree_view, "motion_notify_event", + G_CALLBACK (motion_notify_callback), view, 0); g_signal_connect_object (view->details->tree_view, "button_press_event", G_CALLBACK (button_press_callback), view, 0); + g_signal_connect_object (view->details->tree_view, "button_release_event", + G_CALLBACK (button_release_callback), view, 0); g_signal_connect_object (view->details->tree_view, "key_press_event", G_CALLBACK (key_press_callback), view, 0); @@ -400,6 +675,10 @@ create_and_set_up_tree_view (FMListView *view) g_signal_connect_object (view->details->model, "sort_column_changed", G_CALLBACK (sort_column_changed_callback), view, 0); + view->details->source_target_list = + gtk_target_list_new (drag_types, num_drag_types); + + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), GTK_SELECTION_MULTIPLE); gtk_tree_view_set_rules_hint (view->details->tree_view, TRUE); @@ -942,6 +1221,15 @@ fm_list_view_finalize (GObject *object) list_view = FM_LIST_VIEW (object); + if (list_view->details->double_click_path[0]) { + gtk_tree_path_free (list_view->details->double_click_path[0]); + } + if (list_view->details->double_click_path[1]) { + gtk_tree_path_free (list_view->details->double_click_path[1]); + } + + gtk_target_list_unref (list_view->details->source_target_list); + g_free (list_view->details); G_OBJECT_CLASS (parent_class)->finalize (object); |