diff --git a/gnome/ChangeLog b/gnome/ChangeLog index db3194721..8e488b007 100644 --- a/gnome/ChangeLog +++ b/gnome/ChangeLog @@ -1,3 +1,21 @@ +1999-01-26 Federico Mena Quintero + + * gscreen.c (panel_setup_drag_scroll): Renamed from + panel_setup_drag_motion(). + (panel_clist_drag_motion): Use gdnd_validate_action(). + (panel_clist_motion): Return immediately if the event window is + not the clist_window. Otherwise, forward the event to + panel_widget_motion(). + + * gdnd.c (gdnd_find_panel_by_drag_context): New public function to + find a panel based on a drag context. This is basically the old + find_panel_owning_window() made public. + (gdnd_drop_on_directory): Test for the source widget being a tree, + and if so, do not use the default behavior for panels. + (gdnd_validate_action): New function to compute the final drag + action given some conditions. This is now used to make dragging + behavior consistent across the desktop and the file panels. + 1999-01-26 Jonathan Blandford * gnome-file-property-dialog.c: Many Many Many changes. Now it's @@ -11,6 +29,15 @@ 1999-01-25 Federico Mena Quintero + * gscreen.c (panel_create_icon_display): Only use + GTK_DEST_DEFAULT_DROP since we want to do exotic stuff in the + drag_motion handler to validate drops. + (panel_icon_list_drag_motion): Use gdnd_validate_action(). + + * gdnd.c (gdnd_drop_on_directory): Removed hack that prevented the + user from dropping files on the same panel as the source one. + This should not be done here. + * gdnd.c (get_action): Sensitize the menu items based on the allowed actions in the drag context. (actions): Add some underlined accelerators for the action menu. diff --git a/gnome/gdesktop.c b/gnome/gdesktop.c index f96a1229b..561c02779 100644 --- a/gnome/gdesktop.c +++ b/gnome/gdesktop.c @@ -1645,6 +1645,12 @@ desktop_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, /* If it comes from ourselves, make move the default unless the * user is explicitly asking for ASK. */ + printf ("%s\t%s\t%s\t%s\n", + (context->actions & GDK_ACTION_COPY) ? "copy" : "", + (context->actions & GDK_ACTION_MOVE) ? "move" : "", + (context->actions & GDK_ACTION_LINK) ? "link" : "", + (context->actions & GDK_ACTION_ASK) ? "ask" : ""); + if (source_widget && context->suggested_action != GDK_ACTION_ASK && (context->actions & GDK_ACTION_MOVE)) diff --git a/gnome/gdnd.c b/gnome/gdnd.c index 88609ef39..dc6ea47e1 100644 --- a/gnome/gdnd.c +++ b/gnome/gdnd.c @@ -96,56 +96,6 @@ get_action (GdkDragContext *context) return action; } -/* - * Looks for a panel that has the specified window for its list - * display. It is used to figure out if we are receiving a drop from - * a panel on this MC process. If no panel is found, it returns NULL. - */ -static WPanel * -find_panel_owning_window (GdkDragContext *context) -{ - GList *list; - WPanel *panel; - GtkWidget *source_widget, *toplevel_widget; - - source_widget = gtk_drag_get_source_widget (context); - if (!source_widget) - return NULL; - - /* - * We will scan the list of existing WPanels. We - * uniformize the thing by pulling the toplevel - * widget for each WPanel and compare this to the - * toplevel source_widget - */ - toplevel_widget = gtk_widget_get_toplevel (source_widget); - - for (list = containers; list; list = list->next) { - GtkWidget *panel_toplevel_widget; - - panel = ((PanelContainer *) list->data)->panel; - - panel_toplevel_widget = panel->xwindow; - - if (panel->xwindow == toplevel_widget){ - - /* - * Now a WPanel actually contains a number of - * drag sources. If the drag source is the - * Tree, we must report that it was not the - * contents of the WPanel - */ - - if (source_widget == panel->tree) - return NULL; - - return panel; - } - } - - return NULL; -} - /* * Performs a drop action on the specified panel. Only supports copy * and move operations. The files are moved or copied to the @@ -292,6 +242,7 @@ gdnd_drop_on_directory (GdkDragContext *context, GtkSelectionData *selection_dat { GdkDragAction action; WPanel *source_panel; + GtkWidget *source_widget; GList *names; if (context->action == GDK_ACTION_ASK) { @@ -303,14 +254,12 @@ gdnd_drop_on_directory (GdkDragContext *context, GtkSelectionData *selection_dat } else action = context->action; - /* If we are dragging from a file panel, we can display a nicer status display */ - source_panel = find_panel_owning_window (context); - - /* Check if the user did not drag the information to the same directory */ - if (source_panel) { - if (strcmp (source_panel->cwd, destdir) == 0) - return FALSE; - } + /* If we are dragging from a file panel, we can display a nicer status + * display. But if the drag was from the tree, we cannot do this. + */ + source_panel = gdnd_find_panel_by_drag_context (context, &source_widget); + if (source_widget == source_panel->tree) + source_panel = NULL; /* Symlinks do not use file.c */ @@ -355,3 +304,98 @@ gdnd_drag_context_has_target (GdkDragContext *context, TargetType type) return FALSE; } +/** + * gdnd_find_panel_by_drag_context: + * @context: The context by which to find a panel. + * @source_widget: The source widget is returned here. + * + * Looks in the list of panels for the one that corresponds to the specified + * drag context. + * + * Return value: The sought panel, or NULL if no panel corresponds to the + * context. + **/ +WPanel * +gdnd_find_panel_by_drag_context (GdkDragContext *context, GtkWidget **source_widget) +{ + GtkWidget *source; + GtkWidget *toplevel; + GList *l; + WPanel *panel; + + g_return_val_if_fail (context != NULL, NULL); + + source = gtk_drag_get_source_widget (context); + + if (source_widget) + *source_widget = source; + + if (!source) + return NULL; /* different process */ + + toplevel = gtk_widget_get_toplevel (source); + + for (l = containers; l; l = l->next) { + panel = ((PanelContainer *) l->data)->panel; + + if (panel->xwindow == toplevel) + return panel; + } + + return NULL; +} + +/** + * gdnd_validate_action: + * @context: The drag context for this drag operation. + * @same_process: Whether the drag comes from the same process or not. + * @same_source: If same_process, then whether the source and dest widgets are the same. + * @dest: The destination file entry, or NULL if dropping on empty space. + * @dest_selected: If dest is non-NULL, whether it is selected or not. + * + * Computes the final drag action based on the suggested action of the specified + * context and conditions. + * + * Return value: The computed action, meant to be passed to gdk_drag_action(). + **/ +GdkDragAction +gdnd_validate_action (GdkDragContext *context, int same_process, int same_source, + file_entry *dest_fe, int dest_selected) +{ + int on_directory; + int on_exe; + + if (dest_fe) { + on_directory = dest_fe->f.link_to_dir || S_ISDIR (dest_fe->buf.st_mode); + on_exe = is_exe (dest_fe->buf.st_mode) && if_link_is_exe (dest_fe); + } + + if (dest_fe) { + if (same_source && dest_selected) + return 0; + + if (on_directory) { + if ((same_source || same_process) && (context->actions & GDK_ACTION_MOVE)) + return GDK_ACTION_MOVE; + else + return context->suggested_action; + } else if (on_exe) { + if (context->actions & GDK_ACTION_COPY) + return GDK_ACTION_COPY; + } else if (same_source) + return 0; + else if (same_process && (context->actions & GDK_ACTION_MOVE)) + return GDK_ACTION_MOVE; + else + return context->suggested_action; + } else { + if (same_source) + return 0; + else if (same_process && (context->actions & GDK_ACTION_MOVE)) + return GDK_ACTION_MOVE; + else + return context->suggested_action; + } + + return 0; +} diff --git a/gnome/gdnd.h b/gnome/gdnd.h index b3f47abcb..95c2cd3f0 100644 --- a/gnome/gdnd.h +++ b/gnome/gdnd.h @@ -10,6 +10,7 @@ #define GDND_H #include +#include "panel.h" /* Standard DnD types */ @@ -40,4 +41,14 @@ int gdnd_drop_on_directory (GdkDragContext *context, GtkSelectionData *selection /* Test whether the specified context has a certain target type */ int gdnd_drag_context_has_target (GdkDragContext *context, TargetType type); +/* Look for a panel that corresponds to the specified drag context */ +WPanel *gdnd_find_panel_by_drag_context (GdkDragContext *context, GtkWidget **source_widget); + +/* Computes the final drag action based on the suggested actions and the + * specified conditions. + */ +GdkDragAction gdnd_validate_action (GdkDragContext *context, int same_process, int same_source, + file_entry *dest_fe, int dest_selected); + + #endif diff --git a/gnome/gscreen.c b/gnome/gscreen.c index 85cbe1a09..14dbc371b 100644 --- a/gnome/gscreen.c +++ b/gnome/gscreen.c @@ -601,7 +601,7 @@ typedef gboolean (*desirable_fn)(WPanel *p, int x, int y); typedef gboolean (*scroll_fn)(gpointer data); static gboolean -panel_setup_drag_motion (WPanel *panel, int x, int y, desirable_fn desirable, scroll_fn scroll) +panel_setup_drag_scroll (WPanel *panel, int x, int y, desirable_fn desirable, scroll_fn scroll) { if (panel->timer_id != -1){ gtk_timeout_remove (panel->timer_id); @@ -911,7 +911,7 @@ panel_widget_motion (GtkWidget *widget, GdkEventMotion *event, WPanel *panel) { GtkTargetList *list; GdkDragContext *context; - + if (!panel->maybe_start_drag) return FALSE; @@ -952,6 +952,18 @@ panel_drag_end (GtkWidget *widget, GdkDragContext *context, WPanel *panel) panel->dragging = 0; } +/* Wrapper for the motion_notify callback; it ignores motion events from the + * clist if they do not come from the clist_window. + */ +static int +panel_clist_motion (GtkWidget *widget, GdkEventMotion *event, gpointer data) +{ + if (event->window != GTK_CLIST (widget)->clist_window) + return FALSE; + + return panel_widget_motion (widget, event, data); +} + /** * panel_clist_scrolling_is_desirable: * @@ -1011,25 +1023,50 @@ panel_clist_scroll (gpointer data) return TRUE; } -/** - * panel_clist_drag_motion: - * - * Invoked when an application dragging over us has the the cursor moved. - * If we are close to the top or bottom, we scroll the window +/* Callback used for drag motion events over the clist. We set up + * auto-scrolling and validate the drop to present the user with the correct + * feedback. */ static gboolean -panel_clist_drag_motion (GtkWidget *widget, GdkDragContext *ctx, int x, int y, guint time, void *data) +panel_clist_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, + gpointer data) { - WPanel *panel = data; + WPanel *panel; + GdkDragAction action; + GtkWidget *source_widget; + gint idx; + file_entry *fe; - if (ctx->dest_window != GTK_CLIST (widget)->clist_window) - gdk_drag_status (ctx, 0, time); - else { - panel_setup_drag_motion (panel, x, y, - panel_clist_scrolling_is_desirable, panel_clist_scroll); - gdk_drag_status (ctx, ctx->suggested_action, time); + panel = data; + + if (context->dest_window != GTK_CLIST (widget)->clist_window) { + printf ("squick\n"); + gdk_drag_status (context, 0, time); + return TRUE; } + /* Set up auto-scrolling */ + + panel_setup_drag_scroll (panel, x, y, + panel_clist_scrolling_is_desirable, + panel_clist_scroll); + + /* Validate the drop */ + + gdnd_find_panel_by_drag_context (context, &source_widget); + + if (!gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &idx, NULL)) + fe = NULL; + else + fe = &panel->dir.list[idx]; + + action = gdnd_validate_action (context, + source_widget != NULL, + source_widget == widget, + fe, + fe ? fe->f.marked : FALSE); + + gdk_drag_status (context, action, time); return TRUE; } @@ -1108,18 +1145,42 @@ panel_icon_list_scroll (gpointer data) return TRUE; } -/** - * panel_icon_list_drag_motion: - * - * Invoked when an application dragging over us has the the cursor moved. - * If we are close to the top or bottom, we scroll the window +/* Callback used for drag motion events in the icon list. We need to set up + * auto-scrolling and validate the drop to present the user with the correct + * feedback. */ static gboolean -panel_icon_list_drag_motion (GtkWidget *widget, GdkDragContext *ctx, int x, int y, guint time, void *data) +panel_icon_list_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, + gpointer data) { - WPanel *panel = data; - - panel_setup_drag_motion (panel, x, y, panel_icon_list_scrolling_is_desirable, panel_icon_list_scroll); + WPanel *panel; + GdkDragAction action; + GtkWidget *source_widget; + int idx; + file_entry *fe; + + panel = data; + + /* Set up auto-scrolling */ + + panel_setup_drag_scroll (panel, x, y, + panel_icon_list_scrolling_is_desirable, + panel_icon_list_scroll); + + /* Validate the drop */ + + gdnd_find_panel_by_drag_context (context, &source_widget); + + idx = gnome_icon_list_get_icon_at (GNOME_ICON_LIST (widget), x, y); + fe = (idx == -1) ? NULL : &panel->dir.list[idx]; + + action = gdnd_validate_action (context, + source_widget != NULL, + source_widget == widget, + fe, + fe ? fe->f.marked : FALSE); + + gdk_drag_status (context, action, time); return TRUE; } @@ -1189,15 +1250,16 @@ panel_create_file_list (WPanel *panel) load_dnd_icons (); - gtk_drag_dest_set (GTK_WIDGET (file_list), GTK_DEST_DEFAULT_DROP, + gtk_drag_dest_set (GTK_WIDGET (file_list), + GTK_DEST_DEFAULT_DROP, drop_types, ELEMENTS (drop_types), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK); - +#if 0 /* Make directories draggable */ gtk_drag_source_set (GTK_WIDGET (file_list), GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, drag_types, ELEMENTS (drag_types), GDK_ACTION_LINK | GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_ASK); - +#endif gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_get", GTK_SIGNAL_FUNC (panel_drag_data_get), panel); gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_delete", @@ -1226,7 +1288,7 @@ panel_create_file_list (WPanel *panel) GTK_SIGNAL_FUNC (panel_clist_button_release), panel); gtk_signal_connect (GTK_OBJECT (file_list), "motion_notify_event", - GTK_SIGNAL_FUNC (panel_widget_motion), panel); + GTK_SIGNAL_FUNC (panel_clist_motion), panel); gtk_signal_connect (GTK_OBJECT (file_list), "drag_begin", GTK_SIGNAL_FUNC (panel_drag_begin), panel); gtk_signal_connect (GTK_OBJECT (file_list), "drag_end", @@ -1385,7 +1447,8 @@ panel_create_icon_display (WPanel *panel) load_imlib_icons (); load_dnd_icons (); - gtk_drag_dest_set (GTK_WIDGET (ilist), GTK_DEST_DEFAULT_ALL, + gtk_drag_dest_set (GTK_WIDGET (ilist), + GTK_DEST_DEFAULT_DROP, drop_types, ELEMENTS (drop_types), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK); @@ -1918,7 +1981,7 @@ panel_tree_drag_motion (GtkWidget *widget, GdkDragContext *ctx, int x, int y, gu WPanel *panel = data; int r, row, col; - if (panel_setup_drag_motion (panel, x, y, panel_tree_scrolling_is_desirable, panel_tree_scroll)) + if (panel_setup_drag_scroll (panel, x, y, panel_tree_scrolling_is_desirable, panel_tree_scroll)) return TRUE; r = gtk_clist_get_selection_info (