DragAndDropHandler.cpp [plain text]
#include "config.h"
#include "DragAndDropHandler.h"
#if ENABLE(DRAG_SUPPORT)
#include "WebPageProxy.h"
#include <WebCore/DragData.h>
#include <WebCore/GRefPtrGtk.h>
#include <WebCore/GUniquePtrGtk.h>
#include <WebCore/GtkUtilities.h>
#include <WebCore/PasteboardHelper.h>
#include <wtf/RunLoop.h>
namespace WebKit {
using namespace WebCore;
DragAndDropHandler::DragAndDropHandler(WebPageProxy& page)
: m_page(page)
{
}
DragAndDropHandler::DroppingContext::DroppingContext(GdkDragContext* gdkContext, const IntPoint& position)
: gdkContext(gdkContext)
, lastMotionPosition(position)
, selectionData(SelectionData::create())
{
}
static inline GdkDragAction dragOperationToGdkDragActions(DragOperation coreAction)
{
GdkDragAction gdkAction = static_cast<GdkDragAction>(0);
if (coreAction == DragOperationNone)
return gdkAction;
if (coreAction & DragOperationCopy)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_COPY | gdkAction);
if (coreAction & DragOperationMove)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_MOVE | gdkAction);
if (coreAction & DragOperationLink)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_LINK | gdkAction);
if (coreAction & DragOperationPrivate)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_PRIVATE | gdkAction);
return gdkAction;
}
static inline GdkDragAction dragOperationToSingleGdkDragAction(DragOperation coreAction)
{
if (coreAction == DragOperationEvery || coreAction & DragOperationCopy)
return GDK_ACTION_COPY;
if (coreAction & DragOperationMove)
return GDK_ACTION_MOVE;
if (coreAction & DragOperationLink)
return GDK_ACTION_LINK;
if (coreAction & DragOperationPrivate)
return GDK_ACTION_PRIVATE;
return static_cast<GdkDragAction>(0);
}
static inline DragOperation gdkDragActionToDragOperation(GdkDragAction gdkAction)
{
if (gdkAction & GDK_ACTION_COPY
&& gdkAction & GDK_ACTION_MOVE
&& gdkAction & GDK_ACTION_LINK
&& gdkAction & GDK_ACTION_PRIVATE)
return DragOperationEvery;
unsigned action = DragOperationNone;
if (gdkAction & GDK_ACTION_COPY)
action |= DragOperationCopy;
if (gdkAction & GDK_ACTION_MOVE)
action |= DragOperationMove;
if (gdkAction & GDK_ACTION_LINK)
action |= DragOperationLink;
if (gdkAction & GDK_ACTION_PRIVATE)
action |= DragOperationPrivate;
return static_cast<DragOperation>(action);
}
void DragAndDropHandler::startDrag(Ref<SelectionData>&& selection, DragOperation dragOperation, RefPtr<ShareableBitmap>&& dragImage)
{
#if GTK_CHECK_VERSION(3, 16, 0)
m_draggingSelectionData = WTFMove(selection);
GRefPtr<GtkTargetList> targetList = PasteboardHelper::singleton().targetListForSelectionData(*m_draggingSelectionData);
#else
RefPtr<SelectionData> selectionData = WTFMove(selection);
GRefPtr<GtkTargetList> targetList = PasteboardHelper::singleton().targetListForSelectionData(*selectionData);
#endif
GUniquePtr<GdkEvent> currentEvent(gtk_get_current_event());
GdkDragContext* context = gtk_drag_begin(m_page.viewWidget(), targetList.get(), dragOperationToGdkDragActions(dragOperation),
GDK_BUTTON_PRIMARY, currentEvent.get());
#if GTK_CHECK_VERSION(3, 16, 0)
if (m_dragContext)
gtk_drag_cancel(m_dragContext.get());
m_dragContext = context;
#else
m_draggingSelectionDataMap.set(context, WTFMove(selectionData));
#endif
if (dragImage) {
RefPtr<cairo_surface_t> image(dragImage->createCairoSurface());
cairo_surface_set_device_offset(image.get(), -cairo_image_surface_get_width(image.get()) / 2, -cairo_image_surface_get_height(image.get()) / 2);
gtk_drag_set_icon_surface(context, image.get());
} else
gtk_drag_set_icon_default(context);
}
void DragAndDropHandler::fillDragData(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info)
{
#if GTK_CHECK_VERSION(3, 16, 0)
if (m_dragContext.get() != context)
return;
ASSERT(m_draggingSelectionData);
PasteboardHelper::singleton().fillSelectionData(*m_draggingSelectionData, info, selectionData);
#else
if (auto* selection = m_draggingSelectionDataMap.get(context))
PasteboardHelper::singleton().fillSelectionData(*selection, info, selectionData);
#endif
}
void DragAndDropHandler::finishDrag(GdkDragContext* context)
{
#if GTK_CHECK_VERSION(3, 16, 0)
if (m_dragContext.get() != context)
return;
if (!m_draggingSelectionData)
return;
m_dragContext = nullptr;
m_draggingSelectionData = nullptr;
#else
if (!m_draggingSelectionDataMap.remove(context))
return;
#endif
GdkDevice* device = gdk_drag_context_get_device(context);
int x = 0, y = 0;
gdk_device_get_window_at_position(device, &x, &y);
int xRoot = 0, yRoot = 0;
gdk_device_get_position(device, nullptr, &xRoot, &yRoot);
m_page.dragEnded(IntPoint(x, y), IntPoint(xRoot, yRoot), gdkDragActionToDragOperation(gdk_drag_context_get_selected_action(context)));
}
SelectionData* DragAndDropHandler::dropDataSelection(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info, IntPoint& position)
{
DroppingContext* droppingContext = m_droppingContexts.get(context);
if (!droppingContext)
return nullptr;
droppingContext->pendingDataRequests--;
PasteboardHelper::singleton().fillSelectionData(selectionData, info, droppingContext->selectionData);
if (droppingContext->pendingDataRequests)
return nullptr;
position = droppingContext->lastMotionPosition;
return droppingContext->selectionData.ptr();
}
void DragAndDropHandler::dragEntered(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info, unsigned time)
{
IntPoint position;
auto* selection = dropDataSelection(context, selectionData, info, position);
if (!selection)
return;
DragData dragData(selection, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
m_page.resetCurrentDragInformation();
m_page.dragEntered(dragData);
DragOperation operation = m_page.currentDragOperation();
gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
}
SelectionData* DragAndDropHandler::dragDataSelection(GdkDragContext* context, const IntPoint& position, unsigned time)
{
std::unique_ptr<DroppingContext>& droppingContext = m_droppingContexts.add(context, nullptr).iterator->value;
if (!droppingContext) {
GtkWidget* widget = m_page.viewWidget();
droppingContext = std::make_unique<DroppingContext>(context, position);
Vector<GdkAtom> acceptableTargets(PasteboardHelper::singleton().dropAtomsForContext(widget, droppingContext->gdkContext));
droppingContext->pendingDataRequests = acceptableTargets.size();
for (auto& target : acceptableTargets)
gtk_drag_get_data(widget, droppingContext->gdkContext, target, time);
} else
droppingContext->lastMotionPosition = position;
if (droppingContext->pendingDataRequests > 0)
return nullptr;
return droppingContext->selectionData.ptr();
}
void DragAndDropHandler::dragMotion(GdkDragContext* context, const IntPoint& position, unsigned time)
{
auto* selection = dragDataSelection(context, position, time);
if (!selection)
return;
DragData dragData(selection, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
m_page.dragUpdated(dragData);
DragOperation operation = m_page.currentDragOperation();
gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
}
void DragAndDropHandler::dragLeave(GdkDragContext* context)
{
DroppingContext* droppingContext = m_droppingContexts.get(context);
if (!droppingContext)
return;
RunLoop::main().dispatch([this, context, droppingContext]() {
auto it = m_droppingContexts.find(context);
if (it == m_droppingContexts.end())
return;
if (droppingContext->pendingDataRequests)
return;
if (!droppingContext->dropHappened) {
const IntPoint& position = droppingContext->lastMotionPosition;
DragData dragData(droppingContext->selectionData.ptr(), position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), DragOperationNone);
m_page.dragExited(dragData);
m_page.resetCurrentDragInformation();
}
m_droppingContexts.remove(it);
});
}
bool DragAndDropHandler::drop(GdkDragContext* context, const IntPoint& position, unsigned time)
{
DroppingContext* droppingContext = m_droppingContexts.get(context);
if (!droppingContext)
return false;
droppingContext->dropHappened = true;
uint32_t flags = 0;
if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_COPY)
flags |= WebCore::DragApplicationIsCopyKeyDown;
DragData dragData(droppingContext->selectionData.ptr(), position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)), static_cast<WebCore::DragApplicationFlags>(flags));
m_page.performDragOperation(dragData, String(), { }, { });
gtk_drag_finish(context, TRUE, FALSE, time);
return true;
}
}
#endif // ENABLE(DRAG_SUPPORT)