source: trunk/src/gui/kernel/qdnd_mac.mm@ 452

Last change on this file since 452 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 25.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qapplication.h"
43#ifndef QT_NO_DRAGANDDROP
44#include "qbitmap.h"
45#include "qcursor.h"
46#include "qevent.h"
47#include "qpainter.h"
48#include "qurl.h"
49#include "qwidget.h"
50#include "qfile.h"
51#include "qfileinfo.h"
52#include <stdlib.h>
53#include <string.h>
54#ifndef QT_NO_ACCESSIBILITY
55# include "qaccessible.h"
56#endif
57
58#include <private/qapplication_p.h>
59#include <private/qwidget_p.h>
60#include <private/qdnd_p.h>
61#include <private/qt_mac_p.h>
62
63QT_BEGIN_NAMESPACE
64
65QT_USE_NAMESPACE
66
67QMacDndAnswerRecord qt_mac_dnd_answer_rec;
68
69/*****************************************************************************
70 QDnD debug facilities
71 *****************************************************************************/
72//#define DEBUG_DRAG_EVENTS
73//#define DEBUG_DRAG_PROMISES
74
75/*****************************************************************************
76 QDnD globals
77 *****************************************************************************/
78bool qt_mac_in_drag = false;
79#ifndef QT_MAC_USE_COCOA
80static DragReference qt_mac_current_dragRef = 0;
81#endif
82
83/*****************************************************************************
84 Externals
85 *****************************************************************************/
86extern void qt_mac_send_modifiers_changed(quint32, QObject *); //qapplication_mac.cpp
87extern uint qGlobalPostedEventsCount(); //qapplication.cpp
88extern RgnHandle qt_mac_get_rgn(); // qregion_mac.cpp
89extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp
90/*****************************************************************************
91 QDnD utility functions
92 *****************************************************************************/
93
94//default pixmap
95static const int default_pm_hotx = -2;
96static const int default_pm_hoty = -16;
97#ifndef QT_MAC_USE_COCOA
98static const char * const default_pm[] = {
99 "13 9 3 1",
100 ". c None",
101 " c #000000",
102 "X c #FFFFFF",
103 "X X X X X X X",
104 " X X X X X X ",
105 "X ......... X",
106 " X.........X ",
107 "X ......... X",
108 " X.........X ",
109 "X ......... X",
110 " X X X X X X ",
111 "X X X X X X X",
112};
113#endif
114
115//action management
116#ifdef DEBUG_DRAG_EVENTS
117# define MAP_MAC_ENUM(x) x, #x
118#else
119# define MAP_MAC_ENUM(x) x
120#endif
121struct mac_enum_mapper
122{
123 int mac_code;
124 int qt_code;
125#ifdef DEBUG_DRAG_EVENTS
126 char *qt_desc;
127#endif
128};
129#ifndef QT_MAC_USE_COCOA
130static mac_enum_mapper dnd_action_symbols[] = {
131 { kDragActionAlias, MAP_MAC_ENUM(Qt::LinkAction) },
132 { kDragActionMove, MAP_MAC_ENUM(Qt::MoveAction) },
133 { kDragActionGeneric, MAP_MAC_ENUM(Qt::CopyAction) },
134 { kDragActionCopy, MAP_MAC_ENUM(Qt::CopyAction) },
135 { 0, MAP_MAC_ENUM(0) }
136};
137static DragActions qt_mac_dnd_map_qt_actions(Qt::DropActions qActions)
138{
139 DragActions ret = kDragActionNothing;
140 for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
141 if(qActions & dnd_action_symbols[i].qt_code)
142 ret |= dnd_action_symbols[i].mac_code;
143 }
144 return ret;
145}
146static Qt::DropActions qt_mac_dnd_map_mac_actions(DragActions macActions)
147{
148#ifdef DEBUG_DRAG_EVENTS
149 qDebug("Converting DND ActionList: 0x%lx", macActions);
150#endif
151 Qt::DropActions ret = Qt::IgnoreAction;
152 for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
153#ifdef DEBUG_DRAG_EVENTS
154 qDebug(" %d) [%s] : %s", i, dnd_action_symbols[i].qt_desc,
155 (macActions & dnd_action_symbols[i].mac_code) ? "true" : "false");
156#endif
157 if(macActions & dnd_action_symbols[i].mac_code)
158 ret |= Qt::DropAction(dnd_action_symbols[i].qt_code);
159 }
160 return ret;
161}
162static Qt::DropAction qt_mac_dnd_map_mac_default_action(DragActions macActions)
163{
164 static Qt::DropAction preferred_actions[] = { Qt::CopyAction, Qt::LinkAction, //in order
165 Qt::MoveAction, Qt::IgnoreAction };
166 Qt::DropAction ret = Qt::IgnoreAction;
167 const Qt::DropActions qtActions = qt_mac_dnd_map_mac_actions(macActions);
168 for(int i = 0; preferred_actions[i] != Qt::IgnoreAction; ++i) {
169 if(qtActions & preferred_actions[i]) {
170 ret = preferred_actions[i];
171 break;
172 }
173 }
174#ifdef DEBUG_DRAG_EVENTS
175 for(int i = 0; dnd_action_symbols[i].qt_code; ++i) {
176 if(dnd_action_symbols[i].qt_code == ret) {
177 qDebug("Got default action: %s [0x%lx]", dnd_action_symbols[i].qt_desc, macActions);
178 break;
179 }
180 }
181#endif
182 return ret;
183}
184static void qt_mac_dnd_update_action(DragReference dragRef) {
185 SInt16 modifiers;
186 GetDragModifiers(dragRef, &modifiers, 0, 0);
187 qt_mac_send_modifiers_changed(modifiers, qApp);
188
189 Qt::DropAction qtAction = Qt::IgnoreAction;
190 {
191 DragActions macAllowed = kDragActionNothing;
192 GetDragDropAction(dragRef, &macAllowed);
193 Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(macAllowed);
194 qtAction = QDragManager::self()->defaultAction(qtAllowed, QApplication::keyboardModifiers());
195#if 1
196 if(!(qtAllowed & qtAction))
197 qtAction = qt_mac_dnd_map_mac_default_action(macAllowed);
198#endif
199 }
200 DragActions macAction = qt_mac_dnd_map_qt_actions(qtAction);
201
202 DragActions currentActions;
203 GetDragDropAction(dragRef, &currentActions);
204 if(currentActions != macAction) {
205 SetDragDropAction(dragRef, macAction);
206 QDragManager::self()->emitActionChanged(qtAction);
207 }
208}
209#endif // !QT_MAC_USE_COCOA
210
211/*****************************************************************************
212 DnD functions
213 *****************************************************************************/
214bool QDropData::hasFormat_sys(const QString &mime) const
215{
216#ifndef QT_MAC_USE_COCOA
217 OSPasteboardRef board;
218 if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
219 qDebug("DnD: Cannot get PasteBoard!");
220 return false;
221 }
222 return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mime);
223#else
224 Q_UNUSED(mime);
225 return false;
226#endif // !QT_MAC_USE_COCOA
227}
228
229QVariant QDropData::retrieveData_sys(const QString &mime, QVariant::Type type) const
230{
231#ifndef QT_MAC_USE_COCOA
232 OSPasteboardRef board;
233 if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
234 qDebug("DnD: Cannot get PasteBoard!");
235 return QVariant();
236 }
237 return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mime, type);
238#else
239 Q_UNUSED(mime);
240 Q_UNUSED(type);
241 return QVariant();
242#endif // !QT_MAC_USE_COCOA
243}
244
245QStringList QDropData::formats_sys() const
246{
247#ifndef QT_MAC_USE_COCOA
248 OSPasteboardRef board;
249 if(GetDragPasteboard(qt_mac_current_dragRef, &board) != noErr) {
250 qDebug("DnD: Cannot get PasteBoard!");
251 return QStringList();
252 }
253 return QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats();
254#else
255 return QStringList();
256#endif
257}
258
259void QDragManager::timerEvent(QTimerEvent*)
260{
261}
262
263bool QDragManager::eventFilter(QObject *, QEvent *)
264{
265 return false;
266}
267
268void QDragManager::updateCursor()
269{
270}
271
272void QDragManager::cancel(bool)
273{
274 if(object) {
275 beingCancelled = true;
276 object = 0;
277 }
278#ifndef QT_NO_ACCESSIBILITY
279 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd);
280#endif
281}
282
283void QDragManager::move(const QPoint &)
284{
285}
286
287void QDragManager::drop()
288{
289}
290
291/**
292 If a drop action is already set on the carbon event
293 (from e.g. an earlier enter event), we insert the same
294 action on the new Qt event that has yet to be sendt.
295*/
296static inline bool qt_mac_set_existing_drop_action(const DragRef &dragRef, QDropEvent &event)
297{
298#ifndef QT_MAC_USE_COCOA
299 DragActions currentAction = kDragActionNothing;
300 OSStatus err = GetDragDropAction(dragRef, &currentAction);
301 if (err == noErr && currentAction != kDragActionNothing) {
302 // This looks a bit evil, but we only ever set one action, so it's OK.
303 event.setDropAction(Qt::DropAction(int(qt_mac_dnd_map_mac_actions(currentAction))));
304 return true;
305 }
306#else
307 Q_UNUSED(dragRef);
308 Q_UNUSED(event);
309#endif
310 return false;
311}
312
313/**
314 If an answer rect has been set on the event (after being sent
315 to the global event processor), we store that rect so we can
316 check if the mouse is in the same area upon next drag move event.
317*/
318void qt_mac_copy_answer_rect(const QDragMoveEvent &event)
319{
320 if (!event.answerRect().isEmpty()) {
321 qt_mac_dnd_answer_rec.rect = event.answerRect();
322 qt_mac_dnd_answer_rec.buttons = event.mouseButtons();
323 qt_mac_dnd_answer_rec.modifiers = event.keyboardModifiers();
324 qt_mac_dnd_answer_rec.lastAction = event.dropAction();
325 }
326}
327
328bool qt_mac_mouse_inside_answer_rect(QPoint mouse)
329{
330 if (!qt_mac_dnd_answer_rec.rect.isEmpty()
331 && qt_mac_dnd_answer_rec.rect.contains(mouse)
332 && QApplication::mouseButtons() == qt_mac_dnd_answer_rec.buttons
333 && QApplication::keyboardModifiers() == qt_mac_dnd_answer_rec.modifiers)
334 return true;
335 else
336 return false;
337}
338
339bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef)
340{
341#ifndef QT_MAC_USE_COCOA
342 Q_Q(QWidget);
343 qt_mac_current_dragRef = dragRef;
344 if (kind != kEventControlDragLeave)
345 qt_mac_dnd_update_action(dragRef);
346
347 Point mouse;
348 GetDragMouse(dragRef, &mouse, 0L);
349 if(!mouse.h && !mouse.v)
350 GetGlobalMouse(&mouse);
351
352 if(QApplicationPrivate::modalState()) {
353 for(QWidget *modal = q; modal; modal = modal->parentWidget()) {
354 if(modal->isWindow()) {
355 if(modal != QApplication::activeModalWidget())
356 return noErr;
357 break;
358 }
359 }
360 }
361
362 //lookup the possible actions
363 DragActions allowed = kDragActionNothing;
364 GetDragAllowableActions(dragRef, &allowed);
365 Qt::DropActions qtAllowed = qt_mac_dnd_map_mac_actions(allowed);
366
367 //lookup the source dragAccepted
368 QMimeData *dropdata = QDragManager::self()->dropData;
369 if(QDragManager::self()->source())
370 dropdata = QDragManager::self()->dragPrivate()->data;
371
372 // 'interrestedInDrag' should end up being 'true' if a later drop
373 // will be accepted by the widget for the current mouse position
374 bool interrestedInDrag = true;
375
376 //Dispatch events
377 if (kind == kEventControlDragWithin) {
378 if (qt_mac_mouse_inside_answer_rect(q->mapFromGlobal(QPoint(mouse.h, mouse.v))))
379 return qt_mac_dnd_answer_rec.lastAction == Qt::IgnoreAction;
380 else
381 qt_mac_dnd_answer_rec.clear();
382
383 QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
384 QApplication::mouseButtons(), QApplication::keyboardModifiers());
385
386 // Accept the event by default if a
387 // drag action exists on the carbon event
388 if (qt_mac_set_existing_drop_action(dragRef, qDMEvent))
389 qDMEvent.accept();
390
391 QApplication::sendEvent(q, &qDMEvent);
392 if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
393 interrestedInDrag = false;
394
395 qt_mac_copy_answer_rect(qDMEvent);
396 SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
397
398 } else if (kind == kEventControlDragEnter) {
399 qt_mac_dnd_answer_rec.clear();
400
401 QDragEnterEvent qDEEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
402 QApplication::mouseButtons(), QApplication::keyboardModifiers());
403 qt_mac_set_existing_drop_action(dragRef, qDEEvent);
404 QApplication::sendEvent(q, &qDEEvent);
405 SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDEEvent.dropAction()));
406
407 if (!qDEEvent.isAccepted())
408 // The widget is simply not interrested in this
409 // drag. So tell carbon this by returning 'false'. We will then
410 // not receive any further move, drop or leave events for this widget.
411 return false;
412 else {
413 // Documentation states that a drag move event is sendt immidiatly after
414 // a drag enter event. So we do that. This will honor widgets overriding
415 // 'dragMoveEvent' only, and not 'dragEnterEvent'
416 QDragMoveEvent qDMEvent(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
417 QApplication::mouseButtons(), QApplication::keyboardModifiers());
418 qDMEvent.accept(); // accept by default, since enter event was accepted.
419 qDMEvent.setDropAction(qDEEvent.dropAction());
420 QApplication::sendEvent(q, &qDMEvent);
421 if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction)
422 interrestedInDrag = false;
423
424 qt_mac_copy_answer_rect(qDMEvent);
425 SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(qDMEvent.dropAction()));
426 }
427
428 } else if(kind == kEventControlDragLeave) {
429 QDragLeaveEvent de;
430 QApplication::sendEvent(q, &de);
431 } else if(kind == kEventControlDragReceive) {
432 QDropEvent de(q->mapFromGlobal(QPoint(mouse.h, mouse.v)), qtAllowed, dropdata,
433 QApplication::mouseButtons(), QApplication::keyboardModifiers());
434 if(QDragManager::self()->object)
435 QDragManager::self()->dragPrivate()->target = q;
436 QApplication::sendEvent(q, &de);
437 if(!de.isAccepted()) {
438 interrestedInDrag = false;
439 SetDragDropAction(dragRef, kDragActionNothing);
440 } else {
441 if(QDragManager::self()->object)
442 QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
443 SetDragDropAction(dragRef, qt_mac_dnd_map_qt_actions(de.dropAction()));
444 }
445 }
446
447#ifdef DEBUG_DRAG_EVENTS
448 {
449 const char *desc = 0;
450 switch(kind) {
451 case kEventControlDragWithin: desc = "DragMove"; break;
452 case kEventControlDragEnter: desc = "DragEnter"; break;
453 case kEventControlDragLeave: desc = "DragLeave"; break;
454 case kEventControlDragReceive: desc = "DragDrop"; break;
455 }
456 if(desc) {
457 QPoint pos(q->mapFromGlobal(QPoint(mouse.h, mouse.v)));
458 qDebug("Sending <%s>(%d, %d) event to %p(%s::%s) [%d] (%p)",
459 desc, pos.x(), pos.y(), q, q->metaObject()->className(),
460 q->objectName().toLocal8Bit().constData(), ret, dragRef);
461 }
462 }
463#endif
464
465 //set the cursor
466 bool found_cursor = false;