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 OSStatus
wxMacPromiseKeeper(PasteboardRef
WXUNUSED(inPasteboard
),
44 PasteboardItemID
WXUNUSED(inItem
),
45 CFStringRef
WXUNUSED(inFlavorType
),
46 void * WXUNUSED(inContext
))
50 // we might add promises here later, inContext is the wxDropSource*
55 wxDropTarget::wxDropTarget( wxDataObject
*data
)
56 : wxDropTargetBase( data
)
58 wxMacEnsureTrackingHandlersInstalled();
61 bool wxDropTarget::CurrentDragHasSupportedFormat()
63 bool supported
= false;
64 if (m_dataObject
== NULL
)
67 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
69 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
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
++)
78 wxDataFormat format
= array
[i
];
79 if ( m_dataObject
->IsSupported( format
) )
92 supported
= m_dataObject
->HasDataInPasteboard( m_currentDragPasteboard
);
98 bool wxDropTarget::GetData()
100 if (m_dataObject
== NULL
)
103 if ( !CurrentDragHasSupportedFormat() )
106 bool transferred
= false;
107 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
109 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
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
++)
118 wxDataFormat format
= array
[i
];
119 if ( m_dataObject
->IsSupported( format
) )
121 int size
= data
->GetDataSize( format
);
126 m_dataObject
->SetData( format
, 0, 0 );
130 char *d
= new char[size
];
131 data
->GetDataHere( format
, (void*)d
);
132 m_dataObject
->SetData( format
, size
, d
);
144 transferred
= m_dataObject
->GetFromPasteboard( m_currentDragPasteboard
);
150 //-------------------------------------------------------------------------
152 //-------------------------------------------------------------------------
154 wxDropSource::wxDropSource(wxWindow
*win
,
155 const wxCursor
&cursorCopy
,
156 const wxCursor
&cursorMove
,
157 const wxCursor
&cursorStop
)
158 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
160 wxMacEnsureTrackingHandlersInstalled();
165 wxDropSource
* wxDropSource::GetCurrentDropSource()
167 return gTrackingGlobals
.m_currentSource
;
170 wxDropSource::wxDropSource(wxDataObject
& data
,
172 const wxCursor
&cursorCopy
,
173 const wxCursor
&cursorMove
,
174 const wxCursor
&cursorStop
)
175 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
177 wxMacEnsureTrackingHandlersInstalled();
183 wxDragResult
wxDropSource::DoDragDrop(int flags
)
185 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
187 if ((m_data
== NULL
) || (m_data
->GetFormatCount() == 0))
188 return (wxDragResult
)wxDragNone
;
190 DragReference theDrag
;
191 RgnHandle dragRegion
;
192 OSStatus err
= noErr
;
193 PasteboardRef pasteboard
;
197 err
= PasteboardCreate( kPasteboardUniqueName
, &pasteboard
);
201 // we add a dummy promise keeper because of strange messages when linking against carbon debug
202 err
= PasteboardSetPromiseKeeper( pasteboard
, wxMacPromiseKeeper
, this );
205 CFRelease( pasteboard
);
209 err
= PasteboardClear( pasteboard
);
212 CFRelease( pasteboard
);
215 PasteboardSynchronize( pasteboard
);
217 m_data
->AddToPasteboard( pasteboard
, 1 );
219 if (NewDragWithPasteboard( pasteboard
, &theDrag
) != noErr
)
221 CFRelease( pasteboard
);
225 dragRegion
= NewRgn();
226 RgnHandle tempRgn
= NewRgn();
229 ConvertEventRefToEventRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent(), &rec
);
231 const short dragRegionOuterBoundary
= 10;
232 const short dragRegionInnerBoundary
= 9;
236 rec
.where
.h
- dragRegionOuterBoundary
,
237 rec
.where
.v
- dragRegionOuterBoundary
,
238 rec
.where
.h
+ dragRegionOuterBoundary
,
239 rec
.where
.v
+ dragRegionOuterBoundary
);
243 rec
.where
.h
- dragRegionInnerBoundary
,
244 rec
.where
.v
- dragRegionInnerBoundary
,
245 rec
.where
.h
+ dragRegionInnerBoundary
,
246 rec
.where
.v
+ dragRegionInnerBoundary
);
248 DiffRgn( dragRegion
, tempRgn
, dragRegion
);
249 DisposeRgn( tempRgn
);
251 // TODO: work with promises in order to return data
252 // only when drag was successfully completed
254 gTrackingGlobals
.m_currentSource
= this;
255 gTrackingGlobals
.m_result
= wxDragNone
;
256 gTrackingGlobals
.m_flags
= flags
;
258 err
= TrackDrag( theDrag
, &rec
, dragRegion
);
260 DisposeRgn( dragRegion
);
261 DisposeDrag( theDrag
);
262 CFRelease( pasteboard
);
263 gTrackingGlobals
.m_currentSource
= NULL
;
265 return gTrackingGlobals
.m_result
;
268 bool gTrackingGlobalsInstalled
= false;
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...
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
);
280 void wxMacEnsureTrackingHandlersInstalled()
282 if ( !gTrackingGlobalsInstalled
)
286 err
= InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L, &gTrackingGlobals
);
289 err
= InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
292 gTrackingGlobalsInstalled
= true;
296 pascal OSErr
wxMacWindowDragTrackingHandler(
297 DragTrackingMessage theMessage
, WindowPtr theWindow
,
298 void *handlerRefCon
, DragReference theDrag
)
300 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
302 Point mouse
, localMouse
;
303 DragAttributes attributes
;
305 GetDragAttributes( theDrag
, &attributes
);
306 PasteboardRef pasteboard
= 0;
307 GetDragPasteboard( theDrag
, &pasteboard
);
308 wxNonOwnedWindow
* toplevel
= wxNonOwnedWindow::GetFromWXWindow( (WXWindow
) theWindow
);
310 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
311 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
315 case kDragTrackingEnterHandler
:
316 case kDragTrackingLeaveHandler
:
319 case kDragTrackingEnterWindow
:
320 if (trackingGlobals
!= NULL
)
322 trackingGlobals
->m_currentTargetWindow
= NULL
;
323 trackingGlobals
->m_currentTarget
= NULL
;
327 case kDragTrackingInWindow
:
328 if (trackingGlobals
== NULL
)
330 if (toplevel
== NULL
)
333 GetDragMouse( theDrag
, &mouse
, 0L );
337 toplevel
->GetNonOwnedPeer()->ScreenToWindow( &x
, &y
);
342 wxWindow
*win
= NULL
;
343 ControlPartCode controlPart
;
344 ControlRef control
= FindControlUnderMouse( localMouse
, theWindow
, &controlPart
);
346 win
= wxFindWindowFromWXWidget( (WXWidget
) control
);
351 localx
= localMouse
.h
;
352 localy
= localMouse
.v
;
355 win
->MacRootWindowToWindow( &localx
, &localy
);
356 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
358 if ( trackingGlobals
->m_currentTargetWindow
)
360 // this window is left
361 if ( trackingGlobals
->m_currentTarget
)
363 HideDragHilite( theDrag
);
364 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
365 trackingGlobals
->m_currentTarget
->OnLeave();
366 trackingGlobals
->m_currentTarget
= NULL
;
367 trackingGlobals
->m_currentTargetWindow
= NULL
;
373 // this window is entered
374 trackingGlobals
->m_currentTargetWindow
= win
;
375 trackingGlobals
->m_currentTarget
= win
->GetDropTarget();
377 if ( trackingGlobals
->m_currentTarget
)
379 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
380 result
= trackingGlobals
->m_currentTarget
->OnEnter( localx
, localy
, result
);
383 if ( result
!= wxDragNone
)
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
);
400 if ( trackingGlobals
->m_currentTarget
)
402 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
403 result
= trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
);
407 // set cursor for OnEnter and OnDragOver
408 if ( trackingGlobals
->m_currentSource
&& !trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
410 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
412 wxStockCursor cursorID
= wxCURSOR_NONE
;
417 cursorID
= wxCURSOR_COPY_ARROW
;
421 cursorID
= wxCURSOR_ARROW
;
425 cursorID
= wxCURSOR_NO_ENTRY
;
432 // put these here to make gcc happy
436 if (cursorID
!= wxCURSOR_NONE
)
438 wxCursor
cursor( cursorID
);
447 case kDragTrackingLeaveWindow
:
448 if (trackingGlobals
== NULL
)
451 if (trackingGlobals
->m_currentTarget
)
453 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
454 trackingGlobals
->m_currentTarget
->OnLeave();
455 HideDragHilite( theDrag
);
456 trackingGlobals
->m_currentTarget
= NULL
;
458 trackingGlobals
->m_currentTargetWindow
= NULL
;
468 pascal OSErr
wxMacWindowDragReceiveHandler(
471 DragReference theDrag
)
473 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
474 if ( trackingGlobals
->m_currentTarget
)
476 Point mouse
, localMouse
;
479 PasteboardRef pasteboard
= 0;
480 GetDragPasteboard( theDrag
, &pasteboard
);
481 trackingGlobals
->m_currentTarget
->SetCurrentDragPasteboard( pasteboard
);
482 GetDragMouse( theDrag
, &mouse
, 0L );
484 localx
= localMouse
.h
;
485 localy
= localMouse
.v
;
486 wxNonOwnedWindow
* tlw
= wxNonOwnedWindow::GetFromWXWindow((WXWindow
) theWindow
);
488 tlw
->GetNonOwnedPeer()->ScreenToWindow( &localx
, &localy
);
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
) )
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
498 result
= !(trackingGlobals
->m_flags
& wxDrag_AllowMove
) ||
499 (GetCurrentKeyModifiers() & optionKey
)
502 trackingGlobals
->m_result
=
503 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
);
510 #endif // wxUSE_DRAG_AND_DROP