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