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