1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/dnd.cpp
3 // Purpose: wxDropTarget, wxDropSource implementations
4 // Author: Stefan Csomor
8 // Copyright: (c) 1998 Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
14 #if wxUSE_DRAG_AND_DROP
20 #include "wx/toplevel.h"
21 #include "wx/gdicmn.h"
24 #include "wx/osx/private.h"
26 // ----------------------------------------------------------------------------
28 // ----------------------------------------------------------------------------
32 wxWindow
*m_currentTargetWindow
;
33 wxDropTarget
*m_currentTarget
;
34 wxDropSource
*m_currentSource
;
35 wxDragResult m_result
;
39 MacTrackingGlobals gTrackingGlobals
;
41 void wxMacEnsureTrackingHandlersInstalled();
43 //----------------------------------------------------------------------------
45 //----------------------------------------------------------------------------
47 wxDropTarget
::wxDropTarget( wxDataObject
*data
)
48 : wxDropTargetBase( data
)
50 wxMacEnsureTrackingHandlersInstalled();
53 wxDragResult wxDropTarget
::OnDragOver(
54 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
57 return CurrentDragHasSupportedFormat() ? def
: wxDragNone
;
60 bool wxDropTarget
::OnDrop( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
) )
62 if (m_dataObject
== NULL
)
65 return CurrentDragHasSupportedFormat();
68 wxDragResult wxDropTarget
::OnData(
69 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
72 if (m_dataObject
== NULL
)
75 if (!CurrentDragHasSupportedFormat())
78 return GetData() ? def
: wxDragNone
;
81 bool wxDropTarget
::CurrentDragHasSupportedFormat()
83 bool supported
= false;
84 if (m_dataObject
== NULL
)
87 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
89 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
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
++)
98 wxDataFormat format
= array
[i
];
99 if ( m_dataObject
->IsSupported( format
) )
112 supported
= m_dataObject
->HasDataInPasteboard( m_currentDragPasteboard
);
118 bool wxDropTarget
::GetData()
120 if (m_dataObject
== NULL
)
123 if ( !CurrentDragHasSupportedFormat() )
126 bool transferred
= false;
127 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
129 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
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
++)
138 wxDataFormat format
= array
[i
];
139 if ( m_dataObject
->IsSupported( format
) )
141 int size
= data
->GetDataSize( format
);
146 m_dataObject
->SetData( format
, 0, 0 );
150 char *d
= new char[size
];
151 data
->GetDataHere( format
, (void*)d
);
152 m_dataObject
->SetData( format
, size
, d
);
164 transferred
= m_dataObject
->GetFromPasteboard( m_currentDragPasteboard
);
170 //-------------------------------------------------------------------------
172 //-------------------------------------------------------------------------
174 //-----------------------------------------------------------------------------
177 wxDropSource
::wxDropSource(wxWindow
*win
,
178 const wxCursor
&cursorCopy
,
179 const wxCursor
&cursorMove
,
180 const wxCursor
&cursorStop
)
181 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
183 wxMacEnsureTrackingHandlersInstalled();
188 wxDropSource
::wxDropSource(wxDataObject
& data
,
190 const wxCursor
&cursorCopy
,
191 const wxCursor
&cursorMove
,
192 const wxCursor
&cursorStop
)
193 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
195 wxMacEnsureTrackingHandlersInstalled();
201 wxDropSource
::~wxDropSource()
205 OSStatus
wxMacPromiseKeeper(PasteboardRef
WXUNUSED(inPasteboard
),
206 PasteboardItemID
WXUNUSED(inItem
),
207 CFStringRef
WXUNUSED(inFlavorType
),
208 void * WXUNUSED(inContext
))
210 OSStatus err
= noErr
;
212 // we might add promises here later, inContext is the wxDropSource*
217 wxDragResult wxDropSource
::DoDragDrop(int flags
)
219 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
221 if ((m_data
== NULL
) || (m_data
->GetFormatCount() == 0))
222 return (wxDragResult
)wxDragNone
;
225 DragReference theDrag
;
226 RgnHandle dragRegion
;
227 OSStatus err
= noErr
;
228 PasteboardRef pasteboard
;
232 err
= PasteboardCreate( kPasteboardUniqueName
, &pasteboard
);
236 // we add a dummy promise keeper because of strange messages when linking against carbon debug
237 err
= PasteboardSetPromiseKeeper( pasteboard
, wxMacPromiseKeeper
, this );
240 CFRelease( pasteboard
);
244 err
= PasteboardClear( pasteboard
);
247 CFRelease( pasteboard
);
250 PasteboardSynchronize( pasteboard
);
252 m_data
->AddToPasteboard( pasteboard
, 1 );
254 if (NewDragWithPasteboard( pasteboard
, &theDrag
) != noErr
)
256 CFRelease( pasteboard
);
260 dragRegion
= NewRgn();
261 RgnHandle tempRgn
= NewRgn();
264 ConvertEventRefToEventRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent(), &rec
);
266 const short dragRegionOuterBoundary
= 10;
267 const short dragRegionInnerBoundary
= 9;
271 rec
.where
.h
- dragRegionOuterBoundary
,
272 rec
.where
.v
- dragRegionOuterBoundary
,
273 rec
.where
.h
+ dragRegionOuterBoundary
,
274 rec
.where
.v
+ dragRegionOuterBoundary
);
278 rec
.where
.h
- dragRegionInnerBoundary
,
279 rec
.where
.v
- dragRegionInnerBoundary
,
280 rec
.where
.h
+ dragRegionInnerBoundary
,
281 rec
.where
.v
+ dragRegionInnerBoundary
);
283 DiffRgn( dragRegion
, tempRgn
, dragRegion
);
284 DisposeRgn( tempRgn
);
286 // TODO: work with promises in order to return data
287 // only when drag was successfully completed
289 gTrackingGlobals
.m_currentSource
= this;
290 gTrackingGlobals
.m_result
= wxDragNone
;
291 gTrackingGlobals
.m_flags
= flags
;
293 err
= TrackDrag( theDrag
, &rec
, dragRegion
);
295 DisposeRgn( dragRegion
);
296 DisposeDrag( theDrag
);
297 CFRelease( pasteboard
);
298 gTrackingGlobals
.m_currentSource
= NULL
;
301 return gTrackingGlobals
.m_result
;
304 bool wxDropSource
::MacInstallDefaultCursor(wxDragResult effect
)
306 const wxCursor
& cursor
= GetCursor(effect
);
307 bool result
= cursor
.Ok();
315 bool gTrackingGlobalsInstalled
= false;
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...
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
);
329 void wxMacEnsureTrackingHandlersInstalled()
332 if ( !gTrackingGlobalsInstalled
)
336 err
= InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L, &gTrackingGlobals
);
339 err
= InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
342 gTrackingGlobalsInstalled
= true;
348 pascal OSErr
wxMacWindowDragTrackingHandler(
349 DragTrackingMessage theMessage
, WindowPtr theWindow
,
350 void *handlerRefCon
, DragReference theDrag
)
352 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
354 Point mouse
, localMouse
;
355 DragAttributes attributes
;
357 GetDragAttributes( theDrag
, &attributes
);
358 PasteboardRef pasteboard
= 0;
359 GetDragPasteboard( theDrag
, &pasteboard
);
360 wxNonOwnedWindow
* toplevel
= wxNonOwnedWindow
::GetFromWXWindow( (WXWindow
) theWindow
);
362 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
363 wxDragResult result
= optionDown ? wxDragCopy
: wxDragMove
;
367 case kDragTrackingEnterHandler
:
368 case kDragTrackingLeaveHandler
:
371 case kDragTrackingEnterWindow
:
372 if (trackingGlobals
!= NULL
)
374 trackingGlobals
->m_currentTargetWindow
= NULL
;
375 trackingGlobals
->m_currentTarget
= NULL
;
379 case kDragTrackingInWindow
:
380 if (trackingGlobals
== NULL
)
382 if (toplevel
== NULL
)
385 GetDragMouse( theDrag
, &mouse
, 0L );
388 toplevel
->GetNonOwnedPeer()->ScreenToWindow( &x
, &y
);
393 wxWindow
*win
= NULL
;
394 ControlPartCode controlPart
;
395 ControlRef control
= FindControlUnderMouse( localMouse
, theWindow
, &controlPart
);
397 win
= wxFindWindowFromWXWidget( (WXWidget
) control
);
402 localx
= localMouse
.h
;
403 localy
= localMouse
.v
;
406 win
->MacRootWindowToWindow( &localx
, &localy
);
407 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
409 if ( trackingGlobals
->m_currentTargetWindow
)
411 // this window is left
412 if ( trackingGlobals
->m_currentTarget
)
415 HideDragHilite( theDrag
);
417 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
418 trackingGlobals
->m_currentTarget
->OnLeave();
419 trackingGlobals
->m_currentTarget
= NULL
;
420 trackingGlobals
->m_currentTargetWindow
= NULL
;
426 // this window is entered
427 trackingGlobals
->m_currentTargetWindow
= win
;
428 trackingGlobals
->m_currentTarget
= win
->GetDropTarget();
430 if ( trackingGlobals
->m_currentTarget
)
432 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
433 result
= trackingGlobals
->m_currentTarget
->OnEnter( localx
, localy
, result
);
436 if ( result
!= wxDragNone
)
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
);
446 ShowDragHilite( theDrag
, hiliteRgn
, true );
448 DisposeRgn( hiliteRgn
);
455 if ( trackingGlobals
->m_currentTarget
)
457 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
458 result
= trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
);
462 // set cursor for OnEnter and OnDragOver
463 if ( trackingGlobals
->m_currentSource
&& !trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
465 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
467 wxStockCursor cursorID
= wxCURSOR_NONE
;
472 cursorID
= wxCURSOR_COPY_ARROW
;
476 cursorID
= wxCURSOR_ARROW
;
480 cursorID
= wxCURSOR_NO_ENTRY
;
487 // put these here to make gcc happy
491 if (cursorID
!= wxCURSOR_NONE
)
493 wxCursor
cursor( cursorID
);
501 case kDragTrackingLeaveWindow
:
502 if (trackingGlobals
== NULL
)
505 if (trackingGlobals
->m_currentTarget
)
507 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
508 trackingGlobals
->m_currentTarget
->OnLeave();
510 HideDragHilite( theDrag
);
512 trackingGlobals
->m_currentTarget
= NULL
;
514 trackingGlobals
->m_currentTargetWindow
= NULL
;
524 pascal OSErr
wxMacWindowDragReceiveHandler(
527 DragReference theDrag
)
529 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
530 if ( trackingGlobals
->m_currentTarget
)
532 Point mouse
, localMouse
;
535 PasteboardRef pasteboard
= 0;
536 GetDragPasteboard( theDrag
, &pasteboard
);
537 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
538 GetDragMouse( theDrag
, &mouse
, 0L );
540 localx
= localMouse
.h
;
541 localy
= localMouse
.v
;
542 wxNonOwnedWindow
* tlw
= wxNonOwnedWindow
::GetFromWXWindow((WXWindow
) theWindow
);
544 tlw
->GetNonOwnedPeer()->ScreenToWindow( &localx
, &localy
);
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
) )
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
554 result
= !(trackingGlobals
->m_flags
& wxDrag_AllowMove
) ||
555 (GetCurrentKeyModifiers() & optionKey
)
558 trackingGlobals
->m_result
=
559 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
);
567 #endif // wxUSE_DRAG_AND_DROP