2
* Copyright (C) 2011, Igalia S.L.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this library; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
#include "GtkDragAndDropHelper.h"
22
#include "ClipboardUtilitiesGtk.h"
24
#include "GtkUtilities.h"
25
#include "GtkVersioning.h"
26
#include "PasteboardHelper.h"
28
#include <wtf/PassOwnPtr.h>
32
struct DroppingContext {
33
DroppingContext(GdkDragContext* gdkContext, const IntPoint& position)
34
: gdkContext(gdkContext)
35
, dataObject(DataObjectGtk::create())
36
, lastMotionPosition(position)
42
GdkDragContext* gdkContext;
43
RefPtr<WebCore::DataObjectGtk> dataObject;
44
WebCore::IntPoint lastMotionPosition;
45
int pendingDataRequests;
47
DragExitedCallback exitedCallback;
50
typedef HashMap<GdkDragContext*, DroppingContext*> DroppingContextMap;
51
typedef HashMap<GdkDragContext*, RefPtr<DataObjectGtk> > DraggingDataObjectsMap;
53
GtkDragAndDropHelper::~GtkDragAndDropHelper()
55
deleteAllValues(m_droppingContexts);
58
bool GtkDragAndDropHelper::handleDragEnd(GdkDragContext* dragContext)
60
DraggingDataObjectsMap::iterator iterator = m_draggingDataObjects.find(dragContext);
61
if (iterator == m_draggingDataObjects.end())
64
m_draggingDataObjects.remove(iterator);
68
void GtkDragAndDropHelper::handleGetDragData(GdkDragContext* context, GtkSelectionData* selectionData, guint info)
70
DraggingDataObjectsMap::iterator iterator = m_draggingDataObjects.find(context);
71
if (iterator == m_draggingDataObjects.end())
73
PasteboardHelper::defaultPasteboardHelper()->fillSelectionData(selectionData, info, iterator->value.get());
76
struct HandleDragLaterData {
77
DroppingContext* context;
78
GtkDragAndDropHelper* glue;
81
static gboolean handleDragLeaveLaterCallback(HandleDragLaterData* data)
83
data->glue->handleDragLeaveLater(data->context);
88
void GtkDragAndDropHelper::handleDragLeaveLater(DroppingContext* context)
90
DroppingContextMap::iterator iterator = m_droppingContexts.find(context->gdkContext);
91
if (iterator == m_droppingContexts.end())
94
// If the view doesn't know about the drag yet (there are still pending data)
95
// requests, don't update it with information about the drag.
96
if (context->pendingDataRequests)
99
const IntPoint& position = context->lastMotionPosition;
100
DragData dragData(context->dataObject.get(), position,
101
convertWidgetPointToScreenPoint(m_widget, position),
103
context->exitedCallback(m_widget, &dragData, context->dropHappened);
105
m_droppingContexts.remove(iterator);
109
void GtkDragAndDropHelper::handleDragLeave(GdkDragContext* gdkContext, DragExitedCallback exitedCallback)
111
DroppingContextMap::iterator iterator = m_droppingContexts.find(gdkContext);
112
if (iterator == m_droppingContexts.end())
115
// During a drop GTK+ will fire a drag-leave signal right before firing
116
// the drag-drop signal. We want the actions for drag-leave to happen after
117
// those for drag-drop, so schedule them to happen asynchronously here.
118
HandleDragLaterData* data = new HandleDragLaterData;
119
data->context = iterator->value;
120
data->context->exitedCallback = exitedCallback;
122
g_timeout_add(0, reinterpret_cast<GSourceFunc>(handleDragLeaveLaterCallback), data);
125
static void queryNewDropContextData(DroppingContext* dropContext, GtkWidget* widget, guint time)
127
GdkDragContext* gdkContext = dropContext->gdkContext;
128
Vector<GdkAtom> acceptableTargets(PasteboardHelper::defaultPasteboardHelper()->dropAtomsForContext(widget, gdkContext));
129
dropContext->pendingDataRequests = acceptableTargets.size();
130
for (size_t i = 0; i < acceptableTargets.size(); i++)
131
gtk_drag_get_data(widget, gdkContext, acceptableTargets.at(i), time);
134
PassOwnPtr<DragData> GtkDragAndDropHelper::handleDragMotion(GdkDragContext* context, const IntPoint& position, unsigned time)
136
DroppingContext* droppingContext = 0;
137
DroppingContextMap::iterator iterator = m_droppingContexts.find(context);
138
if (iterator == m_droppingContexts.end()) {
139
droppingContext = new DroppingContext(context, position);
140
m_droppingContexts.set(context, droppingContext);
141
queryNewDropContextData(droppingContext, m_widget, time);
143
droppingContext = iterator->value;
144
droppingContext->lastMotionPosition = position;
147
// Don't send any drag information to WebCore until we've retrieved all
148
// the data for this drag operation. Otherwise we'd have to block to wait
149
// for the drag's data.
150
ASSERT(droppingContext);
151
if (droppingContext->pendingDataRequests > 0)
152
return adoptPtr(static_cast<DragData*>(0));
154
return adoptPtr(new DragData(droppingContext->dataObject.get(), position,
155
convertWidgetPointToScreenPoint(m_widget, position),
156
gdkDragActionToDragOperation(gdk_drag_context_get_actions(context))));
159
PassOwnPtr<DragData> GtkDragAndDropHelper::handleDragDataReceived(GdkDragContext* context, GtkSelectionData* selectionData, guint info)
161
DroppingContextMap::iterator iterator = m_droppingContexts.find(context);
162
if (iterator == m_droppingContexts.end())
163
return adoptPtr(static_cast<DragData*>(0));
165
DroppingContext* droppingContext = iterator->value;
166
droppingContext->pendingDataRequests--;
167
PasteboardHelper::defaultPasteboardHelper()->fillDataObjectFromDropData(selectionData, info, droppingContext->dataObject.get());
169
if (droppingContext->pendingDataRequests)
170
return adoptPtr(static_cast<DragData*>(0));
172
// The coordinates passed to drag-data-received signal are sometimes
173
// inaccurate in DRT, so use the coordinates of the last motion event.
174
const IntPoint& position = droppingContext->lastMotionPosition;
176
// If there are no more pending requests, start sending dragging data to WebCore.
177
return adoptPtr(new DragData(droppingContext->dataObject.get(), position,
178
convertWidgetPointToScreenPoint(m_widget, position),
179
gdkDragActionToDragOperation(gdk_drag_context_get_actions(context))));
182
PassOwnPtr<DragData> GtkDragAndDropHelper::handleDragDrop(GdkDragContext* context, const IntPoint& position)
184
DroppingContextMap::iterator iterator = m_droppingContexts.find(context);
185
if (iterator == m_droppingContexts.end())
186
return adoptPtr(static_cast<DragData*>(0));
188
DroppingContext* droppingContext = iterator->value;
189
droppingContext->dropHappened = true;
191
return adoptPtr(new DragData(droppingContext->dataObject.get(), position,
192
convertWidgetPointToScreenPoint(m_widget, position),
193
gdkDragActionToDragOperation(gdk_drag_context_get_actions(context))));
196
void GtkDragAndDropHelper::startedDrag(GdkDragContext* context, DataObjectGtk* dataObject)
198
m_draggingDataObjects.set(context, dataObject);
201
} // namespace WebCore