]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/dnd.cpp
weak link test is not optimal, as CoreText is already having some symbols defined...
[wxWidgets.git] / src / mac / carbon / dnd.cpp
CommitLineData
e9576ca5 1///////////////////////////////////////////////////////////////////////////////
670f9935 2// Name: src/mac/carbon/dnd.cpp
6eae1f7d 3// Purpose: wxDropTarget, wxDropSource implementations
a31a5f85 4// Author: Stefan Csomor
e9576ca5 5// Modified by:
a31a5f85 6// Created: 1998-01-01
e9576ca5 7// RCS-ID: $Id$
a31a5f85 8// Copyright: (c) 1998 Stefan Csomor
65571936 9// Licence: wxWindows licence
e9576ca5
SC
10///////////////////////////////////////////////////////////////////////////////
11
3d1a4878 12#include "wx/wxprec.h"
ff143598
GD
13
14#if wxUSE_DRAG_AND_DROP
15
e9576ca5 16#include "wx/dnd.h"
670f9935
WS
17
18#ifndef WX_PRECOMP
19 #include "wx/app.h"
1832043f 20 #include "wx/toplevel.h"
dd05139a 21 #include "wx/gdicmn.h"
670f9935
WS
22#endif // WX_PRECOMP
23
76a5e5d2 24#include "wx/mac/private.h"
e9576ca5
SC
25
26// ----------------------------------------------------------------------------
6eae1f7d 27// globals
e9576ca5
SC
28// ----------------------------------------------------------------------------
29
6eae1f7d 30typedef struct
e9576ca5 31{
51d4293d
DS
32 wxWindow *m_currentTargetWindow;
33 wxDropTarget *m_currentTarget;
34 wxDropSource *m_currentSource;
56d152b2
VZ
35 wxDragResult m_result;
36 int m_flags;
716d0327 37} MacTrackingGlobals;
e9576ca5 38
e22ab33a 39MacTrackingGlobals gTrackingGlobals;
6eae1f7d 40
51d4293d 41void wxMacEnsureTrackingHandlersInstalled();
e9576ca5 42
a07c1212
SC
43//----------------------------------------------------------------------------
44// wxDropTarget
45//----------------------------------------------------------------------------
e9576ca5 46
a07c1212
SC
47wxDropTarget::wxDropTarget( wxDataObject *data )
48 : wxDropTargetBase( data )
e9576ca5 49{
51d4293d 50 wxMacEnsureTrackingHandlersInstalled();
e9576ca5
SC
51}
52
51d4293d
DS
53wxDragResult wxDropTarget::OnDragOver(
54 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
55 wxDragResult def )
e9576ca5 56{
a07c1212
SC
57 return CurrentDragHasSupportedFormat() ? def : wxDragNone;
58}
e9576ca5 59
a07c1212 60bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
e9576ca5 61{
876b960a 62 if (m_dataObject == NULL)
6eae1f7d 63 return false;
a07c1212 64
51d4293d 65 return CurrentDragHasSupportedFormat();
e9576ca5
SC
66}
67
51d4293d
DS
68wxDragResult wxDropTarget::OnData(
69 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
70 wxDragResult def )
e9576ca5 71{
876b960a 72 if (m_dataObject == NULL)
a07c1212
SC
73 return wxDragNone;
74
75 if (!CurrentDragHasSupportedFormat())
76 return wxDragNone;
77
78 return GetData() ? def : wxDragNone;
e9576ca5
SC
79}
80
6eae1f7d 81bool wxDropTarget::CurrentDragHasSupportedFormat()
e9576ca5 82{
51d4293d 83 bool supported = false;
6239ee05
SC
84 if (m_dataObject == NULL)
85 return false;
876b960a 86
a07c1212
SC
87 if ( gTrackingGlobals.m_currentSource != NULL )
88 {
51d4293d 89 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject();
6eae1f7d 90
e40298d5 91 if ( data )
a07c1212 92 {
51d4293d 93 size_t formatcount = data->GetFormatCount();
876b960a 94 wxDataFormat *array = new wxDataFormat[formatcount];
e40298d5 95 data->GetAllFormats( array );
51d4293d 96 for (size_t i = 0; !supported && i < formatcount; i++)
a07c1212 97 {
51d4293d 98 wxDataFormat format = array[i];
6eae1f7d 99 if ( m_dataObject->IsSupported( format ) )
e40298d5 100 {
51d4293d
DS
101 supported = true;
102 break;
e40298d5 103 }
a07c1212 104 }
6eae1f7d 105
51d4293d 106 delete [] array;
a07c1212 107 }
a07c1212 108 }
6eae1f7d 109
a07c1212
SC
110 if ( !supported )
111 {
6239ee05 112 PasteboardRef pasteboard;
6eae1f7d 113
6239ee05 114 if ( GetDragPasteboard( (DragReference)m_currentDrag, &pasteboard ) == noErr )
e40298d5 115 {
6239ee05 116 supported = m_dataObject->HasDataInPasteboard( pasteboard );
e40298d5 117 }
a07c1212 118 }
6eae1f7d 119
51d4293d 120 return supported;
e9576ca5
SC
121}
122
a07c1212 123bool wxDropTarget::GetData()
e9576ca5 124{
e22ab33a 125 if (m_dataObject == NULL)
6eae1f7d
DS
126 return false;
127
a07c1212 128 if ( !CurrentDragHasSupportedFormat() )
2e91d506 129 return false;
6eae1f7d 130
2e91d506 131 bool transferred = false;
a07c1212
SC
132 if ( gTrackingGlobals.m_currentSource != NULL )
133 {
2e91d506 134 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject();
6eae1f7d 135
2e91d506 136 if (data != NULL)
a07c1212 137 {
2e91d506 138 size_t formatcount = data->GetFormatCount();
6eae1f7d 139 wxDataFormat *array = new wxDataFormat[formatcount];
e40298d5 140 data->GetAllFormats( array );
2e91d506 141 for (size_t i = 0; !transferred && i < formatcount; i++)
a07c1212 142 {
2e91d506 143 wxDataFormat format = array[i];
6eae1f7d 144 if ( m_dataObject->IsSupported( format ) )
e40298d5
JS
145 {
146 int size = data->GetDataSize( format );
2e91d506 147 transferred = true;
6eae1f7d
DS
148
149 if (size == 0)
e40298d5 150 {
2e91d506 151 m_dataObject->SetData( format, 0, 0 );
e40298d5
JS
152 }
153 else
154 {
155 char *d = new char[size];
2e91d506
DS
156 data->GetDataHere( format, (void*)d );
157 m_dataObject->SetData( format, size, d );
158 delete [] d;
e40298d5
JS
159 }
160 }
a07c1212 161 }
6eae1f7d 162
2e91d506 163 delete [] array;
e40298d5 164 }
a07c1212 165 }
6eae1f7d 166
a07c1212
SC
167 if ( !transferred )
168 {
6239ee05 169 PasteboardRef pasteboard;
de5e599c 170
6239ee05 171 if ( GetDragPasteboard( (DragReference)m_currentDrag, &pasteboard ) == noErr )
55f5548f 172 {
6239ee05 173 transferred = m_dataObject->GetFromPasteboard( pasteboard );
55f5548f 174 }
a07c1212 175 }
6eae1f7d 176
6239ee05 177 return transferred;
e9576ca5
SC
178}
179
180//-------------------------------------------------------------------------
181// wxDropSource
182//-------------------------------------------------------------------------
183
184//-----------------------------------------------------------------------------
185// drag request
186
a07c1212 187wxDropSource::wxDropSource(wxWindow *win,
da804130
SC
188 const wxCursor &cursorCopy,
189 const wxCursor &cursorMove,
190 const wxCursor &cursorStop)
191 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
e9576ca5 192{
51d4293d 193 wxMacEnsureTrackingHandlersInstalled();
e22ab33a 194
a07c1212
SC
195 m_window = win;
196}
e9576ca5 197
a07c1212
SC
198wxDropSource::wxDropSource(wxDataObject& data,
199 wxWindow *win,
da804130
SC
200 const wxCursor &cursorCopy,
201 const wxCursor &cursorMove,
202 const wxCursor &cursorStop)
203 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
a07c1212 204{
51d4293d 205 wxMacEnsureTrackingHandlersInstalled();
e22ab33a 206
a07c1212
SC
207 SetData( data );
208 m_window = win;
209}
e9576ca5 210
a07c1212 211wxDropSource::~wxDropSource()
e9576ca5 212{
a07c1212 213}
e9576ca5 214
31c1cbc7
VZ
215OSStatus wxMacPromiseKeeper(PasteboardRef WXUNUSED(inPasteboard),
216 PasteboardItemID WXUNUSED(inItem),
217 CFStringRef WXUNUSED(inFlavorType),
218 void * WXUNUSED(inContext))
6239ee05
SC
219{
220 OSStatus err = noErr;
31c1cbc7 221
6239ee05
SC
222 // we might add promises here later, inContext is the wxDropSource*
223
224 return err;
225}
226
525ae3fe 227wxDragResult wxDropSource::DoDragDrop(int flags)
e9576ca5 228{
a07c1212 229 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
6eae1f7d 230
de5e599c
DS
231 if ((m_data == NULL) || (m_data->GetFormatCount() == 0))
232 return (wxDragResult)wxDragNone;
6eae1f7d 233
a07c1212
SC
234 DragReference theDrag;
235 RgnHandle dragRegion;
6239ee05
SC
236 OSStatus err = noErr;
237 PasteboardRef pasteboard;
31c1cbc7 238
a07c1212 239 // add data to drag
31c1cbc7 240
6239ee05
SC
241 err = PasteboardCreate( kPasteboardUniqueName, &pasteboard );
242 if ( err != noErr )
243 return wxDragNone;
31c1cbc7 244
6239ee05 245 // we add a dummy promise keeper because of strange messages when linking against carbon debug
31c1cbc7 246 err = PasteboardSetPromiseKeeper( pasteboard, wxMacPromiseKeeper, this );
6239ee05 247 if ( err != noErr )
a07c1212 248 {
6239ee05
SC
249 CFRelease( pasteboard );
250 return wxDragNone;
a07c1212 251 }
31c1cbc7
VZ
252
253 err = PasteboardClear( pasteboard );
6239ee05
SC
254 if ( err != noErr )
255 {
256 CFRelease( pasteboard );
257 return wxDragNone;
258 }
31c1cbc7
VZ
259 PasteboardSynchronize( pasteboard );
260
6239ee05 261 m_data->AddToPasteboard( pasteboard, 1 );
31c1cbc7 262
6239ee05
SC
263 if (NewDragWithPasteboard( pasteboard , &theDrag) != noErr)
264 {
265 CFRelease( pasteboard );
266 return wxDragNone;
267 }
31c1cbc7 268
a07c1212 269 dragRegion = NewRgn();
51d4293d 270 RgnHandle tempRgn = NewRgn();
6eae1f7d 271
db28a493 272 EventRecord rec;
6239ee05 273 ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec );
6eae1f7d 274
51d4293d
DS
275 const short dragRegionOuterBoundary = 10;
276 const short dragRegionInnerBoundary = 9;
6eae1f7d
DS
277
278 SetRectRgn(
51d4293d 279 dragRegion,
6239ee05
SC
280 rec.where.h - dragRegionOuterBoundary,
281 rec.where.v - dragRegionOuterBoundary,
282 rec.where.h + dragRegionOuterBoundary,
283 rec.where.v + dragRegionOuterBoundary );
6eae1f7d
DS
284
285 SetRectRgn(
51d4293d 286 tempRgn,
6239ee05
SC
287 rec.where.h - dragRegionInnerBoundary,
288 rec.where.v - dragRegionInnerBoundary,
289 rec.where.h + dragRegionInnerBoundary,
290 rec.where.v + dragRegionInnerBoundary );
6eae1f7d 291
51d4293d
DS
292 DiffRgn( dragRegion, tempRgn, dragRegion );
293 DisposeRgn( tempRgn );
6eae1f7d 294
e22ab33a
DS
295 // TODO: work with promises in order to return data
296 // only when drag was successfully completed
6eae1f7d 297
e22ab33a 298 gTrackingGlobals.m_currentSource = this;
56d152b2
VZ
299 gTrackingGlobals.m_result = wxDragNone;
300 gTrackingGlobals.m_flags = flags;
301
6239ee05 302 err = TrackDrag( theDrag, &rec, dragRegion );
31c1cbc7 303
e22ab33a
DS
304 DisposeRgn( dragRegion );
305 DisposeDrag( theDrag );
6239ee05 306 CFRelease( pasteboard );
e22ab33a 307 gTrackingGlobals.m_currentSource = NULL;
6eae1f7d 308
56d152b2 309 return gTrackingGlobals.m_result;
a07c1212
SC
310}
311
da804130
SC
312bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect)
313{
314 const wxCursor& cursor = GetCursor(effect);
6eae1f7d
DS
315 bool result = cursor.Ok();
316
317 if ( result )
e22ab33a 318 cursor.MacInstall();
da804130 319
6eae1f7d 320 return result;
da804130
SC
321}
322
51d4293d 323bool gTrackingGlobalsInstalled = false;
a07c1212
SC
324
325// passing the globals via refcon is not needed by the CFM and later architectures anymore
326// but I'll leave it in there, just in case...
327
6eae1f7d
DS
328pascal OSErr wxMacWindowDragTrackingHandler(
329 DragTrackingMessage theMessage, WindowPtr theWindow,
e22ab33a 330 void *handlerRefCon, DragReference theDrag );
6eae1f7d
DS
331pascal OSErr wxMacWindowDragReceiveHandler(
332 WindowPtr theWindow, void *handlerRefCon,
e22ab33a 333 DragReference theDrag );
a07c1212
SC
334
335void wxMacEnsureTrackingHandlersInstalled()
e9576ca5 336{
6eae1f7d 337 if ( !gTrackingGlobalsInstalled )
a07c1212 338 {
2e91d506 339 OSStatus err;
a07c1212 340
2e91d506
DS
341 err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals );
342 verify_noerr( err );
6eae1f7d 343
2e91d506
DS
344 err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals );
345 verify_noerr( err );
e9576ca5 346
e22ab33a 347 gTrackingGlobalsInstalled = true;
a07c1212
SC
348 }
349}
350
6eae1f7d
DS
351pascal OSErr wxMacWindowDragTrackingHandler(
352 DragTrackingMessage theMessage, WindowPtr theWindow,
e22ab33a 353 void *handlerRefCon, DragReference theDrag )
6eae1f7d 354{
a07c1212 355 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
e22ab33a 356
a07c1212
SC
357 Point mouse, localMouse;
358 DragAttributes attributes;
e22ab33a
DS
359
360 GetDragAttributes( theDrag, &attributes );
361
51d4293d 362 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( theWindow );
f43084de 363
51d4293d 364 bool optionDown = GetCurrentKeyModifiers() & optionKey;
f43084de
JS
365 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
366
6eae1f7d 367 switch (theMessage)
a07c1212
SC
368 {
369 case kDragTrackingEnterHandler:
a07c1212
SC
370 case kDragTrackingLeaveHandler:
371 break;
6eae1f7d 372
a07c1212 373 case kDragTrackingEnterWindow:
e22ab33a
DS
374 if (trackingGlobals != NULL)
375 {
51d4293d
DS
376 trackingGlobals->m_currentTargetWindow = NULL;
377 trackingGlobals->m_currentTarget = NULL;
e22ab33a 378 }
a07c1212 379 break;
6eae1f7d 380
a07c1212 381 case kDragTrackingInWindow:
e22ab33a
DS
382 if (trackingGlobals == NULL)
383 break;
a07c1212
SC
384 if (toplevel == NULL)
385 break;
386
e22ab33a 387 GetDragMouse( theDrag, &mouse, 0L );
a07c1212 388 localMouse = mouse;
4913272f 389 wxMacGlobalToLocal( theWindow, &localMouse );
f43084de 390
a07c1212 391 {
51d4293d
DS
392 wxWindow *win = NULL;
393 ControlPartCode controlPart;
9d463591 394 ControlRef control = FindControlUnderMouse( localMouse, theWindow, &controlPart );
2877659b 395 if ( control )
51d4293d 396 win = wxFindControlFromMacControl( control );
7adabb9a 397 else
51d4293d 398 win = toplevel;
6eae1f7d 399
51d4293d
DS
400 int localx, localy;
401 localx = localMouse.h;
402 localy = localMouse.v;
7adabb9a 403
a07c1212 404 if ( win )
51d4293d 405 win->MacRootWindowToWindow( &localx, &localy );
a07c1212
SC
406 if ( win != trackingGlobals->m_currentTargetWindow )
407 {
408 if ( trackingGlobals->m_currentTargetWindow )
409 {
410 // this window is left
411 if ( trackingGlobals->m_currentTarget )
412 {
4913272f 413#ifndef __LP64__
6eae1f7d 414 HideDragHilite( theDrag );
4913272f 415#endif
876b960a
DS
416 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
417 trackingGlobals->m_currentTarget->OnLeave();
a07c1212 418 trackingGlobals->m_currentTarget = NULL;
876b960a 419 trackingGlobals->m_currentTargetWindow = NULL;
a07c1212
SC
420 }
421 }
6eae1f7d 422
a07c1212
SC
423 if ( win )
424 {
425 // this window is entered
51d4293d
DS
426 trackingGlobals->m_currentTargetWindow = win;
427 trackingGlobals->m_currentTarget = win->GetDropTarget();
a07c1212 428 {
6eae1f7d
DS
429 if ( trackingGlobals->m_currentTarget )
430 {
51d4293d
DS
431 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
432 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
da804130 433 }
6eae1f7d 434
da804130 435 if ( result != wxDragNone )
a07c1212 436 {
51d4293d 437 int x, y;
6eae1f7d 438
51d4293d
DS
439 x = y = 0;
440 win->MacWindowToRootWindow( &x, &y );
441 RgnHandle hiliteRgn = NewRgn();
442 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
443 RectRgn( hiliteRgn, &r );
4913272f 444#ifndef __LP64__
876b960a 445 ShowDragHilite( theDrag, hiliteRgn, true );
4913272f 446#endif
51d4293d 447 DisposeRgn( hiliteRgn );
a07c1212
SC
448 }
449 }
450 }
451 }
452 else
453 {
6eae1f7d 454 if ( trackingGlobals->m_currentTarget )
a07c1212 455 {
51d4293d 456 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
6239ee05 457 result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
f43084de
JS
458 }
459 }
460
461 // set cursor for OnEnter and OnDragOver
bf473da7 462 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
f43084de 463 {
6eae1f7d 464 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
f43084de 465 {
de5e599c
DS
466 int cursorID = wxCURSOR_NONE;
467
468 switch (result)
f43084de 469 {
2e91d506 470 case wxDragCopy:
de5e599c 471 cursorID = wxCURSOR_COPY_ARROW;
51d4293d 472 break;
6eae1f7d 473
2e91d506 474 case wxDragMove:
de5e599c 475 cursorID = wxCURSOR_ARROW;
51d4293d 476 break;
6eae1f7d 477
2e91d506 478 case wxDragNone:
de5e599c 479 cursorID = wxCURSOR_NO_ENTRY;
51d4293d 480 break;
eb7f8ac5
VZ
481
482 case wxDragError:
483 case wxDragLink:
484 case wxDragCancel:
6eae1f7d 485 default:
eb7f8ac5
VZ
486 // put these here to make gcc happy
487 ;
f43084de 488 }
de5e599c
DS
489
490 if (cursorID != wxCURSOR_NONE)
491 {
492 wxCursor cursor( cursorID );
493 cursor.MacInstall();
494 }
6eae1f7d 495 }
a07c1212 496 }
7adabb9a 497 }
a07c1212 498 break;
6eae1f7d 499
a07c1212 500 case kDragTrackingLeaveWindow:
e22ab33a
DS
501 if (trackingGlobals == NULL)
502 break;
503
6eae1f7d 504 if (trackingGlobals->m_currentTarget)
a07c1212 505 {
876b960a
DS
506 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
507 trackingGlobals->m_currentTarget->OnLeave();
4913272f 508#ifndef __LP64__
876b960a 509 HideDragHilite( theDrag );
4913272f 510#endif
876b960a 511 trackingGlobals->m_currentTarget = NULL;
a07c1212 512 }
876b960a 513 trackingGlobals->m_currentTargetWindow = NULL;
a07c1212 514 break;
6eae1f7d
DS
515
516 default:
517 break;
a07c1212 518 }
6eae1f7d
DS
519
520 return noErr;
a07c1212
SC
521}
522
6eae1f7d
DS
523pascal OSErr wxMacWindowDragReceiveHandler(
524 WindowPtr theWindow,
525 void *handlerRefCon,
526 DragReference theDrag)
527{
528 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
a07c1212
SC
529 if ( trackingGlobals->m_currentTarget )
530 {
51d4293d
DS
531 Point mouse, localMouse;
532 int localx, localy;
6eae1f7d 533
51d4293d
DS
534 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
535 GetDragMouse( theDrag, &mouse, 0L );
a07c1212 536 localMouse = mouse;
4913272f 537 wxMacGlobalToLocal( theWindow, &localMouse );
51d4293d
DS
538 localx = localMouse.h;
539 localy = localMouse.v;
6eae1f7d
DS
540
541 // TODO : should we use client coordinates?
a07c1212 542 if ( trackingGlobals->m_currentTargetWindow )
51d4293d
DS
543 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
544 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
a07c1212 545 {
56d152b2
VZ
546 // the option key indicates copy in Mac UI, if it's not pressed do
547 // move by default if it's allowed at all
548 wxDragResult
549 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) ||
550 (GetCurrentKeyModifiers() & optionKey)
551 ? wxDragCopy
552 : wxDragMove;
553 trackingGlobals->m_result =
554 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
a07c1212
SC
555 }
556 }
6eae1f7d
DS
557
558 return noErr;
a07c1212 559}
6eae1f7d 560
56d152b2
VZ
561#endif // wxUSE_DRAG_AND_DROP
562