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