summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Camp <dave@ximian.com>2003-03-26 16:14:50 +0000
committerDave Camp <campd@src.gnome.org>2003-03-26 16:14:50 +0000
commitf755bfc133c4705734ff6cbc4e48602592f23030 (patch)
tree17d7d5d104e9f79f5b65324a8b7890b206be7f7e
parentb793c9d66be23f096f6ac50e5aff4b52da5b85f9 (diff)
downloadnautilus-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--ChangeLog17
-rw-r--r--src/file-manager/fm-list-view.c394
2 files changed, 358 insertions, 53 deletions
diff --git a/ChangeLog b/ChangeLog
index bb5991875..82c4f7604 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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);