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