static gboolean drag_motion_callback (GtkWidget *widget, GdkDragContext *context, int x, int y, guint32 time) { int action; nemo_icon_container_ensure_drag_data (NEMO_ICON_CONTAINER (widget), context, time); nemo_icon_container_position_shadow (NEMO_ICON_CONTAINER (widget), x, y); nemo_icon_dnd_update_drop_target (NEMO_ICON_CONTAINER (widget), context, x, y); set_up_auto_scroll_if_needed (NEMO_ICON_CONTAINER (widget)); /* Find out what the drop actions are based on our drag selection and * the drop target. */ action = 0; nemo_icon_container_get_drop_action (NEMO_ICON_CONTAINER (widget), context, x, y, &action); if (action != 0) { start_dnd_highlight (widget); } gdk_drag_status (context, action, time); return TRUE; }
/* FIXME bugzilla.gnome.org 47445: Needs to become a shared function */ static void get_data_on_first_target_we_support (GtkWidget *widget, GdkDragContext *context, guint32 time, int x, int y) { GtkTargetList *list; GdkAtom target; if (drop_types_list == NULL) { drop_types_list = gtk_target_list_new (drop_types, G_N_ELEMENTS (drop_types) - 1); gtk_target_list_add_text_targets (drop_types_list, NEMO_ICON_DND_TEXT); } if (drop_types_list_root == NULL) { drop_types_list_root = gtk_target_list_new (drop_types, G_N_ELEMENTS (drop_types)); gtk_target_list_add_text_targets (drop_types_list_root, NEMO_ICON_DND_TEXT); } if (nemo_icon_container_get_is_desktop (NEMO_ICON_CONTAINER (widget))) { list = drop_types_list_root; } else { list = drop_types_list; } target = gtk_drag_dest_find_target (widget, context, list); if (target != GDK_NONE) { guint info; NemoDragInfo *drag_info; gboolean found; drag_info = &(NEMO_ICON_CONTAINER (widget)->details->dnd_info->drag_info); found = gtk_target_list_find (list, target, &info); g_assert (found); /* Don't get_data for destructive ops */ if ((info == NEMO_ICON_DND_ROOTWINDOW_DROP || info == NEMO_ICON_DND_XDNDDIRECTSAVE) && !drag_info->drop_occured) { /* We can't call get_data here, because that would make the source execute the rootwin action or the direct save */ drag_info->got_drop_data_type = TRUE; drag_info->data_type = info; } else { if (info == NEMO_ICON_DND_XDNDDIRECTSAVE) { set_direct_save_uri (widget, context, drag_info, x, y); } gtk_drag_get_data (GTK_WIDGET (widget), context, target, time); } } }
static void nemo_icon_container_dropped_icon_feedback (GtkWidget *widget, GtkSelectionData *data, int x, int y) { NemoIconContainer *container; NemoIconDndInfo *dnd_info; container = NEMO_ICON_CONTAINER (widget); dnd_info = container->details->dnd_info; /* Delete old selection list. */ nemo_drag_destroy_selection_list (dnd_info->drag_info.selection_list); dnd_info->drag_info.selection_list = NULL; /* Delete old shadow if any. */ if (dnd_info->shadow != NULL) { /* FIXME bugzilla.gnome.org 42484: * Is a destroy really sufficient here? Who does the unref? */ eel_canvas_item_destroy (dnd_info->shadow); } /* Build the selection list and the shadow. */ dnd_info->drag_info.selection_list = nemo_drag_build_selection_list (data); dnd_info->shadow = create_selection_shadow (container, dnd_info->drag_info.selection_list); nemo_icon_container_position_shadow (container, x, y); }
/* Queue a redraw of the dnd highlight rect */ static void dnd_highlight_queue_redraw (GtkWidget *widget) { NemoIconDndInfo *dnd_info; int width, height; GtkAllocation allocation; dnd_info = NEMO_ICON_CONTAINER (widget)->details->dnd_info; if (!dnd_info->highlighted) { return; } gtk_widget_get_allocation (widget, &allocation); width = allocation.width; height = allocation.height; /* we don't know how wide the shadow is exactly, * so we expose a 10-pixel wide border */ gtk_widget_queue_draw_area (widget, 0, 0, width, 10); gtk_widget_queue_draw_area (widget, 0, 0, 10, height); gtk_widget_queue_draw_area (widget, 0, height - 10, width, 10); gtk_widget_queue_draw_area (widget, width - 10, 0, 10, height); }
static void drag_begin_callback (GtkWidget *widget, GdkDragContext *context, gpointer data) { NemoIconContainer *container; cairo_surface_t *surface; double x1, y1, x2, y2, winx, winy; int x_offset, y_offset; int start_x, start_y; container = NEMO_ICON_CONTAINER (widget); start_x = container->details->dnd_info->drag_info.start_x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container))); start_y = container->details->dnd_info->drag_info.start_y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container))); /* create a pixmap and mask to drag with */ surface = nemo_icon_canvas_item_get_drag_surface (container->details->drag_icon->item); /* compute the image's offset */ eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (container->details->drag_icon->item), &x1, &y1, &x2, &y2); eel_canvas_world_to_window (EEL_CANVAS (container), x1, y1, &winx, &winy); x_offset = start_x - winx; y_offset = start_y - winy; cairo_surface_set_device_offset (surface, -x_offset, -y_offset); gtk_drag_set_icon_surface (context, surface); cairo_surface_destroy (surface); }
static void drag_leave_callback (GtkWidget *widget, GdkDragContext *context, guint32 time, gpointer data) { NemoIconDndInfo *dnd_info; dnd_info = NEMO_ICON_CONTAINER (widget)->details->dnd_info; if (dnd_info->shadow != NULL) eel_canvas_item_hide (dnd_info->shadow); stop_dnd_highlight (widget); set_drop_target (NEMO_ICON_CONTAINER (widget), NULL); stop_auto_scroll (NEMO_ICON_CONTAINER (widget)); nemo_icon_container_free_drag_data(NEMO_ICON_CONTAINER (widget)); }
NemoIconContainer * nemo_icon_view_container_construct (NemoIconViewContainer *icon_container, NemoIconView *view) { AtkObject *atk_obj; g_return_val_if_fail (NEMO_IS_ICON_VIEW (view), NULL); icon_container->view = view; atk_obj = gtk_widget_get_accessible (GTK_WIDGET (icon_container)); atk_object_set_name (atk_obj, _("Icon View")); return NEMO_ICON_CONTAINER (icon_container); }
static void drag_end_callback (GtkWidget *widget, GdkDragContext *context, gpointer data) { NemoIconContainer *container; NemoIconDndInfo *dnd_info; container = NEMO_ICON_CONTAINER (widget); dnd_info = container->details->dnd_info; nemo_drag_destroy_selection_list (dnd_info->drag_info.selection_list); dnd_info->drag_info.selection_list = NULL; }
/* Adaptor function used with nemo_icon_container_each_selected_icon * to help iterate over all selected items, passing uris, x, y, w and h * values to the iteratee */ static void each_icon_get_data_binder (NemoDragEachSelectedItemDataGet iteratee, gpointer iterator_context, gpointer data) { IconGetDataBinderContext context; NemoIconContainer *container; g_assert (NEMO_IS_ICON_CONTAINER (iterator_context)); container = NEMO_ICON_CONTAINER (iterator_context); context.iterator_context = iterator_context; context.iteratee = iteratee; context.iteratee_data = data; nemo_icon_container_each_selected_icon (container, icon_get_data_binder, &context); }
static void stop_dnd_highlight (GtkWidget *widget) { NemoIconDndInfo *dnd_info; dnd_info = NEMO_ICON_CONTAINER (widget)->details->dnd_info; if (dnd_info->highlighted) { g_signal_handlers_disconnect_by_func (widget, drag_highlight_draw, NULL); dnd_highlight_queue_redraw (widget); dnd_info->highlighted = FALSE; } }
static gboolean icon_get_data_binder (NemoIcon *icon, gpointer data) { IconGetDataBinderContext *context; EelDRect world_rect; EelIRect widget_rect; char *uri; NemoIconContainer *container; context = (IconGetDataBinderContext *)data; g_assert (NEMO_IS_ICON_CONTAINER (context->iterator_context)); container = NEMO_ICON_CONTAINER (context->iterator_context); world_rect = nemo_icon_canvas_item_get_icon_rectangle (icon->item); canvas_rect_world_to_widget (EEL_CANVAS (container), &world_rect, &widget_rect); uri = nemo_icon_container_get_icon_uri (container, icon); if (uri == NULL) { g_warning ("no URI for one of the iterated icons"); return TRUE; } widget_rect = eel_irect_offset_by (widget_rect, - container->details->dnd_info->drag_info.start_x, - container->details->dnd_info->drag_info.start_y); widget_rect = eel_irect_scale_by (widget_rect, 1 / EEL_CANVAS (container)->pixels_per_unit); /* pass the uri, mouse-relative x/y and icon width/height */ context->iteratee (uri, (int) widget_rect.x0, (int) widget_rect.y0, widget_rect.x1 - widget_rect.x0, widget_rect.y1 - widget_rect.y0, context->iteratee_data); g_free (uri); return TRUE; }
static void set_direct_save_uri (GtkWidget *widget, GdkDragContext *context, NemoDragInfo *drag_info, int x, int y) { GFile *base, *child; char *filename, *drop_target; gchar *uri; drag_info->got_drop_data_type = TRUE; drag_info->data_type = NEMO_ICON_DND_XDNDDIRECTSAVE; uri = NULL; filename = get_direct_save_filename (context); drop_target = nemo_icon_container_find_drop_target (NEMO_ICON_CONTAINER (widget), context, x, y, NULL, TRUE); if (drop_target && eel_uri_is_trash (drop_target)) { g_free (drop_target); drop_target = NULL; /* Cannot save to trash ...*/ } if (filename != NULL && drop_target != NULL) { /* Resolve relative path */ base = g_file_new_for_uri (drop_target); child = g_file_get_child (base, filename); uri = g_file_get_uri (child); g_object_unref (base); g_object_unref (child); /* Change the uri property */ gdk_property_change (gdk_drag_context_get_source_window (context), gdk_atom_intern (NEMO_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE), gdk_atom_intern ("text/plain", FALSE), 8, GDK_PROP_MODE_REPLACE, (const guchar *) uri, strlen (uri)); drag_info->direct_save_uri = uri; } g_free (filename); g_free (drop_target); }
static void start_dnd_highlight (GtkWidget *widget) { NemoIconDndInfo *dnd_info; GtkWidget *toplevel; dnd_info = NEMO_ICON_CONTAINER (widget)->details->dnd_info; toplevel = gtk_widget_get_toplevel (widget); if (toplevel != NULL && g_object_get_data (G_OBJECT (toplevel), "is_desktop_window")) { return; } if (!dnd_info->highlighted) { dnd_info->highlighted = TRUE; g_signal_connect_after (widget, "draw", G_CALLBACK (drag_highlight_draw), NULL); dnd_highlight_queue_redraw (widget); } }
static gboolean drag_drop_callback (GtkWidget *widget, GdkDragContext *context, int x, int y, guint32 time, gpointer data) { NemoIconDndInfo *dnd_info; dnd_info = NEMO_ICON_CONTAINER (widget)->details->dnd_info; /* tell the drag_data_received callback that the drop occured and that it can actually process the actions. make sure it is going to be called at least once. */ dnd_info->drag_info.drop_occured = TRUE; get_data_on_first_target_we_support (widget, context, time, x, y); return TRUE; }
static int auto_scroll_timeout_callback (gpointer data) { NemoIconContainer *container; GtkWidget *widget; float x_scroll_delta, y_scroll_delta; GdkRectangle exposed_area; GtkAllocation allocation; g_assert (NEMO_IS_ICON_CONTAINER (data)); widget = GTK_WIDGET (data); container = NEMO_ICON_CONTAINER (widget); if (container->details->dnd_info->drag_info.waiting_to_autoscroll && container->details->dnd_info->drag_info.start_auto_scroll_in > eel_get_system_time()) { /* not yet */ return TRUE; } container->details->dnd_info->drag_info.waiting_to_autoscroll = FALSE; nemo_drag_autoscroll_calculate_delta (widget, &x_scroll_delta, &y_scroll_delta); if (x_scroll_delta == 0 && y_scroll_delta == 0) { /* no work */ return TRUE; } /* Clear the old dnd highlight frame */ dnd_highlight_queue_redraw (widget); if (!nemo_icon_container_scroll (container, (int)x_scroll_delta, (int)y_scroll_delta)) { /* the scroll value got pinned to a min or max adjustment value, * we ended up not scrolling */ return TRUE; } /* Make sure the dnd highlight frame is redrawn */ dnd_highlight_queue_redraw (widget); /* update cached drag start offsets */ container->details->dnd_info->drag_info.start_x -= x_scroll_delta; container->details->dnd_info->drag_info.start_y -= y_scroll_delta; /* Due to a glitch in GtkLayout, whe need to do an explicit draw of the exposed * area. * Calculate the size of the area we need to draw */ gtk_widget_get_allocation (widget, &allocation); exposed_area.x = allocation.x; exposed_area.y = allocation.y; exposed_area.width = allocation.width; exposed_area.height = allocation.height; if (x_scroll_delta > 0) { exposed_area.x = exposed_area.width - x_scroll_delta; } else if (x_scroll_delta < 0) { exposed_area.width = -x_scroll_delta; } if (y_scroll_delta > 0) { exposed_area.y = exposed_area.height - y_scroll_delta; } else if (y_scroll_delta < 0) { exposed_area.height = -y_scroll_delta; } /* offset it to 0, 0 */ exposed_area.x -= allocation.x; exposed_area.y -= allocation.y; gtk_widget_queue_draw_area (widget, exposed_area.x, exposed_area.y, exposed_area.width, exposed_area.height); return TRUE; }
static void drag_data_received_callback (GtkWidget *widget, GdkDragContext *context, int x, int y, GtkSelectionData *data, guint info, guint32 time, gpointer user_data) { NemoDragInfo *drag_info; char *tmp; const char *tmp_raw; int length; gboolean success; drag_info = &(NEMO_ICON_CONTAINER (widget)->details->dnd_info->drag_info); drag_info->got_drop_data_type = TRUE; drag_info->data_type = info; switch (info) { case NEMO_ICON_DND_GNOME_ICON_LIST: nemo_icon_container_dropped_icon_feedback (widget, data, x, y); break; case NEMO_ICON_DND_URI_LIST: case NEMO_ICON_DND_TEXT: case NEMO_ICON_DND_XDNDDIRECTSAVE: case NEMO_ICON_DND_RAW: /* Save the data so we can do the actual work on drop. */ if (drag_info->selection_data != NULL) { gtk_selection_data_free (drag_info->selection_data); } drag_info->selection_data = gtk_selection_data_copy (data); break; /* Netscape keeps sending us the data, even though we accept the first drag */ case NEMO_ICON_DND_NETSCAPE_URL: if (drag_info->selection_data != NULL) { gtk_selection_data_free (drag_info->selection_data); drag_info->selection_data = gtk_selection_data_copy (data); } break; case NEMO_ICON_DND_ROOTWINDOW_DROP: /* Do nothing, this won't even happen, since we don't want to call get_data twice */ break; } /* this is the second use case of this callback. * we have to do the actual work for the drop. */ if (drag_info->drop_occured) { success = FALSE; switch (info) { case NEMO_ICON_DND_GNOME_ICON_LIST: nemo_icon_container_receive_dropped_icons (NEMO_ICON_CONTAINER (widget), context, x, y); break; case NEMO_ICON_DND_NETSCAPE_URL: receive_dropped_netscape_url (NEMO_ICON_CONTAINER (widget), (char *) gtk_selection_data_get_data (data), context, x, y); success = TRUE; break; case NEMO_ICON_DND_URI_LIST: receive_dropped_uri_list (NEMO_ICON_CONTAINER (widget), (char *) gtk_selection_data_get_data (data), context, x, y); success = TRUE; break; case NEMO_ICON_DND_TEXT: tmp = gtk_selection_data_get_text (data); receive_dropped_text (NEMO_ICON_CONTAINER (widget), (char *) tmp, context, x, y); success = TRUE; g_free (tmp); break; case NEMO_ICON_DND_RAW: length = gtk_selection_data_get_length (data); tmp_raw = gtk_selection_data_get_data (data); receive_dropped_raw (NEMO_ICON_CONTAINER (widget), tmp_raw, length, drag_info->direct_save_uri, context, x, y); success = TRUE; break; case NEMO_ICON_DND_ROOTWINDOW_DROP: /* Do nothing, everything is done by the sender */ break; case NEMO_ICON_DND_XDNDDIRECTSAVE: { const guchar *selection_data; gint selection_length; gint selection_format; selection_data = gtk_selection_data_get_data (drag_info->selection_data); selection_length = gtk_selection_data_get_length (drag_info->selection_data); selection_format = gtk_selection_data_get_format (drag_info->selection_data); if (selection_format == 8 && selection_length == 1 && selection_data[0] == 'F') { gtk_drag_get_data (widget, context, gdk_atom_intern (NEMO_ICON_DND_RAW_TYPE, FALSE), time); return; } else if (selection_format == 8 && selection_length == 1 && selection_data[0] == 'F' && drag_info->direct_save_uri != NULL) { GdkPoint p; GFile *location; location = g_file_new_for_uri (drag_info->direct_save_uri); nemo_file_changes_queue_file_added (location); p.x = x; p.y = y; nemo_file_changes_queue_schedule_position_set ( location, p, gdk_screen_get_number ( gtk_widget_get_screen (widget))); g_object_unref (location); nemo_file_changes_consume_changes (TRUE); success = TRUE; } break; } /* NEMO_ICON_DND_XDNDDIRECTSAVE */ } gtk_drag_finish (context, success, FALSE, time); nemo_icon_container_free_drag_data (NEMO_ICON_CONTAINER (widget)); set_drop_target (NEMO_ICON_CONTAINER (widget), NULL); /* reinitialise it for the next dnd */ drag_info->drop_occured = FALSE; } }