|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
63 QT_BEGIN_NAMESPACE |
|
64 |
|
65 QT_USE_NAMESPACE |
|
66 |
|
67 QMacDndAnswerRecord 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 *****************************************************************************/ |
|
78 bool qt_mac_in_drag = false; |
|
79 #ifndef QT_MAC_USE_COCOA |
|
80 static DragReference qt_mac_current_dragRef = 0; |
|
81 #endif |
|
82 |
|
83 /***************************************************************************** |
|
84 Externals |
|
85 *****************************************************************************/ |
|
86 extern void qt_mac_send_modifiers_changed(quint32, QObject *); //qapplication_mac.cpp |
|
87 extern uint qGlobalPostedEventsCount(); //qapplication.cpp |
|
88 extern RgnHandle qt_mac_get_rgn(); // qregion_mac.cpp |
|
89 extern void qt_mac_dispose_rgn(RgnHandle); // qregion_mac.cpp |
|
90 /***************************************************************************** |
|
91 QDnD utility functions |
|
92 *****************************************************************************/ |
|
93 |
|
94 //default pixmap |
|
95 static const int default_pm_hotx = -2; |
|
96 static const int default_pm_hoty = -16; |
|
97 #ifndef QT_MAC_USE_COCOA |
|
98 static 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 |
|
121 struct 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 |
|
130 static 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 }; |
|
137 static 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 } |
|
146 static 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 } |
|
162 static 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 } |
|
184 static 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, ¤tActions); |
|
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 *****************************************************************************/ |
|
214 bool 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 |
|
229 QVariant 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 |
|
245 QStringList 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 |
|
259 void QDragManager::timerEvent(QTimerEvent*) |
|
260 { |
|
261 } |
|
262 |
|
263 bool QDragManager::eventFilter(QObject *, QEvent *) |
|
264 { |
|
265 return false; |
|
266 } |
|
267 |
|
268 void QDragManager::updateCursor() |
|
269 { |
|
270 } |
|
271 |
|
272 void 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 |
|
283 void QDragManager::move(const QPoint &) |
|
284 { |
|
285 } |
|
286 |
|
287 void 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 */ |
|
296 static 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, ¤tAction); |
|
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 */ |
|
318 void 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 |
|
328 bool 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 |
|
339 bool 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 interested 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 sent immediately 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; |
|
467 if(kind == kEventControlDragWithin || kind == kEventControlDragEnter) { |
|
468 ThemeCursor cursor = kThemeNotAllowedCursor; |
|
469 found_cursor = true; |
|
470 if (interrestedInDrag) { |
|
471 DragActions action = kDragActionNothing; |
|
472 GetDragDropAction(dragRef, &action); |
|
473 switch(qt_mac_dnd_map_mac_default_action(action)) { |
|
474 case Qt::IgnoreAction: |
|
475 cursor = kThemeNotAllowedCursor; |
|
476 break; |
|
477 case Qt::MoveAction: |
|
478 cursor = kThemeArrowCursor; |
|
479 break; |
|
480 case Qt::CopyAction: |
|
481 cursor = kThemeCopyArrowCursor; |
|
482 break; |
|
483 case Qt::LinkAction: |
|
484 cursor = kThemeAliasArrowCursor; |
|
485 break; |
|
486 default: |
|
487 cursor = kThemeNotAllowedCursor; |
|
488 break; |
|
489 } |
|
490 } |
|
491 SetThemeCursor(cursor); |
|
492 } |
|
493 if(found_cursor) { |
|
494 qt_mac_set_cursor(0, QPoint()); //just use our's |
|
495 } else { |
|
496 QCursor cursor(Qt::ArrowCursor); |
|
497 if(qApp && qApp->overrideCursor()) { |
|
498 cursor = *qApp->overrideCursor(); |
|
499 } else if(q) { |
|
500 for(QWidget *p = q; p; p = p->parentWidget()) { |
|
501 if(p->isEnabled() && p->testAttribute(Qt::WA_SetCursor)) { |
|
502 cursor = p->cursor(); |
|
503 break; |
|
504 } |
|
505 } |
|
506 } |
|
507 qt_mac_set_cursor(&cursor, QPoint(mouse.h, mouse.v)); |
|
508 } |
|
509 |
|
510 //idle things |
|
511 if(qGlobalPostedEventsCount()) { |
|
512 QApplication::sendPostedEvents(); |
|
513 QApplication::flush(); |
|
514 } |
|
515 |
|
516 // If this was not a drop, tell carbon that we will be interresed in receiving more |
|
517 // events for the current drag. We do that by returning true. |
|
518 if (kind == kEventControlDragReceive) |
|
519 return interrestedInDrag; |
|
520 else |
|
521 return true; |
|
522 #else |
|
523 Q_UNUSED(kind); |
|
524 Q_UNUSED(dragRef); |
|
525 return false; |
|
526 #endif // !QT_MAC_USE_COCOA |
|
527 } |
|
528 |
|
529 #ifndef QT_MAC_USE_COCOA |
|
530 Qt::DropAction QDragManager::drag(QDrag *o) |
|
531 { |
|
532 |
|
533 if(qt_mac_in_drag) { //just make sure.. |
|
534 qWarning("Qt: Internal error: WH0A, unexpected condition reached"); |
|
535 return Qt::IgnoreAction; |
|
536 } |
|
537 if(object == o) |
|
538 return Qt::IgnoreAction; |
|
539 /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button |
|
540 so we just bail early to prevent it */ |
|
541 if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary)) |
|
542 return Qt::IgnoreAction; |
|
543 |
|
544 if(object) { |
|
545 dragPrivate()->source->removeEventFilter(this); |
|
546 cancel(); |
|
547 beingCancelled = false; |
|
548 } |
|
549 |
|
550 object = o; |
|
551 dragPrivate()->target = 0; |
|
552 |
|
553 #ifndef QT_NO_ACCESSIBILITY |
|
554 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart); |
|
555 #endif |
|
556 |
|
557 //setup the data |
|
558 QMacPasteboard dragBoard(QMacPasteboardMime::MIME_DND); |
|
559 dragBoard.setMimeData(dragPrivate()->data); |
|
560 |
|
561 //create the drag |
|
562 OSErr result; |
|
563 DragRef dragRef; |
|
564 if((result = NewDragWithPasteboard(dragBoard.pasteBoard(), &dragRef))) |
|
565 return Qt::IgnoreAction; |
|
566 //setup the actions |
|
567 DragActions possibleActions = qt_mac_dnd_map_qt_actions(dragPrivate()->possible_actions); |
|
568 SetDragAllowableActions(dragRef, //local |
|
569 possibleActions, |
|
570 true); |
|
571 SetDragAllowableActions(dragRef, //remote (same as local) |
|
572 possibleActions, |
|
573 false); |
|
574 |
|
575 |
|
576 QPoint hotspot; |
|
577 QPixmap pix = dragPrivate()->pixmap; |
|
578 if(pix.isNull()) { |
|
579 if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) { |
|
580 //get the string |
|
581 QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text() |
|
582 : dragPrivate()->data->urls().first().toString(); |
|
583 if(s.length() > 26) |
|
584 s = s.left(23) + QChar(0x2026); |
|
585 if(!s.isEmpty()) { |
|
586 //draw it |
|
587 QFont f(qApp->font()); |
|
588 f.setPointSize(12); |
|
589 QFontMetrics fm(f); |
|
590 const int width = fm.width(s); |
|
591 const int height = fm.height(); |
|
592 if(width > 0 && height > 0) { |
|
593 QPixmap tmp(width, height); |
|
594 QPainter p(&tmp); |
|
595 p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0); |
|
596 p.setPen(Qt::color1); |
|
597 p.setFont(f); |
|
598 p.drawText(0, fm.ascent(), s); |
|
599 p.end(); |
|
600 //save it |
|
601 pix = tmp; |
|
602 hotspot = QPoint(tmp.width() / 2, tmp.height() / 2); |
|
603 } |
|
604 } |
|
605 } else { |
|
606 pix = QPixmap(default_pm); |
|
607 hotspot = QPoint(default_pm_hotx, default_pm_hoty); |
|
608 } |
|
609 } else { |
|
610 hotspot = dragPrivate()->hotspot; |
|
611 } |
|
612 |
|
613 //so we must fake an event |
|
614 EventRecord fakeEvent; |
|
615 GetGlobalMouse(&(fakeEvent.where)); |
|
616 fakeEvent.message = 0; |
|
617 fakeEvent.what = mouseDown; |
|
618 fakeEvent.when = EventTimeToTicks(GetCurrentEventTime()); |
|
619 fakeEvent.modifiers = GetCurrentKeyModifiers(); |
|
620 if(GetCurrentEventButtonState() & 2) |
|
621 fakeEvent.modifiers |= controlKey; |
|
622 |
|
623 //find the hotspot in relation to the pixmap |
|
624 Point boundsPoint; |
|
625 boundsPoint.h = fakeEvent.where.h - hotspot.x(); |
|
626 boundsPoint.v = fakeEvent.where.v - hotspot.y(); |
|
627 Rect boundsRect; |
|
628 SetRect(&boundsRect, boundsPoint.h, boundsPoint.v, boundsPoint.h + pix.width(), boundsPoint.v + pix.height()); |
|
629 |
|
630 //set the drag image |
|
631 QRegion dragRegion(boundsPoint.h, boundsPoint.v, pix.width(), pix.height()), pixRegion; |
|
632 if(!pix.isNull()) { |
|
633 HIPoint hipoint = { -hotspot.x(), -hotspot.y() }; |
|
634 CGImageRef img = (CGImageRef)pix.macCGHandle(); |
|
635 SetDragImageWithCGImage(dragRef, img, &hipoint, 0); |
|
636 CGImageRelease(img); |
|
637 } |
|
638 |
|
639 SetDragItemBounds(dragRef, (ItemReference)1 , &boundsRect); |
|
640 { //do the drag |
|
641 qt_mac_in_drag = true; |
|
642 qt_mac_dnd_update_action(dragRef); |
|
643 result = TrackDrag(dragRef, &fakeEvent, QMacSmartQuickDrawRegion(dragRegion.toQDRgn())); |
|
644 qt_mac_in_drag = false; |
|
645 } |
|
646 object = 0; |
|
647 if(result == noErr) { |
|
648 // Check if the receiver points us to |
|
649 // a file location. If so, we need to do |
|
650 // the file copy/move ourselves: |
|
651 QCFType<CFURLRef> pasteLocation = 0; |
|
652 PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation); |
|
653 if (pasteLocation){ |
|
654 Qt::DropAction action = o->d_func()->defaultDropAction; |
|
655 if (action == Qt::IgnoreAction){ |
|
656 if (o->d_func()->possible_actions & Qt::MoveAction) |
|
657 action = Qt::MoveAction; |
|
658 else if (o->d_func()->possible_actions & Qt::CopyAction) |
|
659 action = Qt::CopyAction; |
|
660 |
|
661 } |
|
662 bool atleastOne = false; |
|
663 QList<QUrl> urls = o->mimeData()->urls(); |
|
664 for (int i = 0; i < urls.size(); ++i){ |
|
665 QUrl fromUrl = urls.at(i); |
|
666 QString filename = QFileInfo(fromUrl.path()).fileName(); |
|
667 QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename); |
|
668 if (action == Qt::MoveAction){ |
|
669 if (QFile::rename(fromUrl.path(), toUrl.path())) |
|
670 atleastOne = true; |
|
671 } else if (action == Qt::CopyAction){ |
|
672 if (QFile::copy(fromUrl.path(), toUrl.path())) |
|
673 atleastOne = true; |
|
674 } |
|
675 } |
|
676 if (atleastOne){ |
|
677 DisposeDrag(dragRef); |
|
678 return action; |
|
679 } |
|
680 } |
|
681 |
|
682 DragActions ret = kDragActionNothing; |
|
683 GetDragDropAction(dragRef, &ret); |
|
684 DisposeDrag(dragRef); //cleanup |
|
685 return qt_mac_dnd_map_mac_default_action(ret); |
|
686 } |
|
687 DisposeDrag(dragRef); //cleanup |
|
688 return Qt::IgnoreAction; |
|
689 } |
|
690 #endif |
|
691 |
|
692 void QDragManager::updatePixmap() |
|
693 { |
|
694 } |
|
695 |
|
696 QCocoaDropData::QCocoaDropData(CFStringRef pasteboard) |
|
697 : QInternalMimeData() |
|
698 { |
|
699 NSString* pasteboardName = (NSString*)pasteboard; |
|
700 [pasteboardName retain]; |
|
701 dropPasteboard = pasteboard; |
|
702 } |
|
703 |
|
704 QCocoaDropData::~QCocoaDropData() |
|
705 { |
|
706 NSString* pasteboardName = (NSString*)dropPasteboard; |
|
707 [pasteboardName release]; |
|
708 } |
|
709 |
|
710 QStringList QCocoaDropData::formats_sys() const |
|
711 { |
|
712 QStringList formats; |
|
713 OSPasteboardRef board; |
|
714 if (PasteboardCreate(dropPasteboard, &board) != noErr) { |
|
715 qDebug("DnD: Cannot get PasteBoard!"); |
|
716 return formats; |
|
717 } |
|
718 formats = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats(); |
|
719 return formats; |
|
720 } |
|
721 |
|
722 QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const |
|
723 { |
|
724 QVariant data; |
|
725 OSPasteboardRef board; |
|
726 if (PasteboardCreate(dropPasteboard, &board) != noErr) { |
|
727 qDebug("DnD: Cannot get PasteBoard!"); |
|
728 return data; |
|
729 } |
|
730 data = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mimeType, type); |
|
731 CFRelease(board); |
|
732 return data; |
|
733 } |
|
734 |
|
735 bool QCocoaDropData::hasFormat_sys(const QString &mimeType) const |
|
736 { |
|
737 bool has = false; |
|
738 OSPasteboardRef board; |
|
739 if (PasteboardCreate(dropPasteboard, &board) != noErr) { |
|
740 qDebug("DnD: Cannot get PasteBoard!"); |
|
741 return has; |
|
742 } |
|
743 has = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mimeType); |
|
744 CFRelease(board); |
|
745 return has; |
|
746 } |
|
747 |
|
748 #endif // QT_NO_DRAGANDDROP |
|
749 QT_END_NAMESPACE |