1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/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 PasteboardRef pasteboard
;
114 if ( GetDragPasteboard( (DragReference
)m_currentDrag
, &pasteboard
) == noErr
)
116 supported
= m_dataObject
->HasDataInPasteboard( pasteboard
);
123 bool wxDropTarget::GetData()
125 if (m_dataObject
== NULL
)
128 if ( !CurrentDragHasSupportedFormat() )
131 bool transferred
= false;
132 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
134 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
138 size_t formatcount
= data
->GetFormatCount();
139 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
140 data
->GetAllFormats( array
);
141 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
143 wxDataFormat format
= array
[i
];
144 if ( m_dataObject
->IsSupported( format
) )
146 int size
= data
->GetDataSize( format
);
151 m_dataObject
->SetData( format
, 0, 0 );
155 char *d
= new char[size
];
156 data
->GetDataHere( format
, (void*)d
);
157 m_dataObject
->SetData( format
, size
, d
);
169 PasteboardRef pasteboard
;
171 if ( GetDragPasteboard( (DragReference
)m_currentDrag
, &pasteboard
) == noErr
)
173 transferred
= m_dataObject
->GetFromPasteboard( pasteboard
);
180 //-------------------------------------------------------------------------
182 //-------------------------------------------------------------------------
184 //-----------------------------------------------------------------------------
187 wxDropSource::wxDropSource(wxWindow
*win
,
188 const wxCursor
&cursorCopy
,
189 const wxCursor
&cursorMove
,
190 const wxCursor
&cursorStop
)
191 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
193 wxMacEnsureTrackingHandlersInstalled();
198 wxDropSource::wxDropSource(wxDataObject
& data
,
200 const wxCursor
&cursorCopy
,
201 const wxCursor
&cursorMove
,
202 const wxCursor
&cursorStop
)
203 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
205 wxMacEnsureTrackingHandlersInstalled();
211 wxDropSource::~wxDropSource()
215 OSStatus
wxMacPromiseKeeper(PasteboardRef
WXUNUSED(inPasteboard
),
216 PasteboardItemID
WXUNUSED(inItem
),
217 CFStringRef
WXUNUSED(inFlavorType
),
218 void * WXUNUSED(inContext
))
220 OSStatus err
= noErr
;
222 // we might add promises here later, inContext is the wxDropSource*
227 wxDragResult
wxDropSource::DoDragDrop(int flags
)
229 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
231 if ((m_data
== NULL
) || (m_data
->GetFormatCount() == 0))
232 return (wxDragResult
)wxDragNone
;
234 DragReference theDrag
;
235 RgnHandle dragRegion
;
236 OSStatus err
= noErr
;
237 PasteboardRef pasteboard
;
241 err
= PasteboardCreate( kPasteboardUniqueName
, &pasteboard
);
245 // we add a dummy promise keeper because of strange messages when linking against carbon debug
246 err
= PasteboardSetPromiseKeeper( pasteboard
, wxMacPromiseKeeper
, this );
249 CFRelease( pasteboard
);
253 err
= PasteboardClear( pasteboard
);
256 CFRelease( pasteboard
);
259 PasteboardSynchronize( pasteboard
);
261 m_data
->AddToPasteboard( pasteboard
, 1 );
263 if (NewDragWithPasteboard( pasteboard
, &theDrag
) != noErr
)
265 CFRelease( pasteboard
);
269 dragRegion
= NewRgn();
270 RgnHandle tempRgn
= NewRgn();
273 ConvertEventRefToEventRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent(), &rec
);
275 const short dragRegionOuterBoundary
= 10;
276 const short dragRegionInnerBoundary
= 9;
280 rec
.where
.h
- dragRegionOuterBoundary
,
281 rec
.where
.v
- dragRegionOuterBoundary
,
282 rec
.where
.h
+ dragRegionOuterBoundary
,
283 rec
.where
.v
+ dragRegionOuterBoundary
);
287 rec
.where
.h
- dragRegionInnerBoundary
,
288 rec
.where
.v
- dragRegionInnerBoundary
,
289 rec
.where
.h
+ dragRegionInnerBoundary
,
290 rec
.where
.v
+ dragRegionInnerBoundary
);
292 DiffRgn( dragRegion
, tempRgn
, dragRegion
);
293 DisposeRgn( tempRgn
);
295 // TODO: work with promises in order to return data
296 // only when drag was successfully completed
298 gTrackingGlobals
.m_currentSource
= this;
299 gTrackingGlobals
.m_result
= wxDragNone
;
300 gTrackingGlobals
.m_flags
= flags
;
302 err
= TrackDrag( theDrag
, &rec
, dragRegion
);
304 DisposeRgn( dragRegion
);
305 DisposeDrag( theDrag
);
306 CFRelease( pasteboard
);
307 gTrackingGlobals
.m_currentSource
= NULL
;
309 return gTrackingGlobals
.m_result
;
312 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
314 const wxCursor
& cursor
= GetCursor(effect
);
315 bool result
= cursor
.Ok();
323 bool gTrackingGlobalsInstalled
= false;
325 // passing the globals via refcon is not needed by the CFM and later architectures anymore
326 // but I'll leave it in there, just in case...
328 pascal OSErr
wxMacWindowDragTrackingHandler(
329 DragTrackingMessage theMessage
, WindowPtr theWindow
,
330 void *handlerRefCon
, DragReference theDrag
);
331 pascal OSErr
wxMacWindowDragReceiveHandler(
332 WindowPtr theWindow
, void *handlerRefCon
,
333 DragReference theDrag
);
335 void wxMacEnsureTrackingHandlersInstalled()
337 if ( !gTrackingGlobalsInstalled
)
341 err
= InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L, &gTrackingGlobals
);
344 err
= InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
347 gTrackingGlobalsInstalled
= true;
351 pascal OSErr
wxMacWindowDragTrackingHandler(
352 DragTrackingMessage theMessage
, WindowPtr theWindow
,
353 void *handlerRefCon
, DragReference theDrag
)
355 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
357 Point mouse
, localMouse
;
358 DragAttributes attributes
;
360 GetDragAttributes( theDrag
, &attributes
);
362 wxNonOwnedWindow
* toplevel
= wxFindWinFromMacWindow( theWindow
);
364 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
365 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
369 case kDragTrackingEnterHandler
:
370 case kDragTrackingLeaveHandler
:
373 case kDragTrackingEnterWindow
:
374 if (trackingGlobals
!= NULL
)
376 trackingGlobals
->m_currentTargetWindow
= NULL
;
377 trackingGlobals
->m_currentTarget
= NULL
;
381 case kDragTrackingInWindow
:
382 if (trackingGlobals
== NULL
)
384 if (toplevel
== NULL
)
387 GetDragMouse( theDrag
, &mouse
, 0L );
389 wxMacGlobalToLocal( theWindow
, &localMouse
);
392 wxWindow
*win
= NULL
;
393 ControlPartCode controlPart
;
394 ControlRef control
= FindControlUnderMouse( localMouse
, theWindow
, &controlPart
);
396 win
= wxFindControlFromMacControl( control
);
401 localx
= localMouse
.h
;
402 localy
= localMouse
.v
;
405 win
->MacRootWindowToWindow( &localx
, &localy
);
406 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
408 if ( trackingGlobals
->m_currentTargetWindow
)
410 // this window is left
411 if ( trackingGlobals
->m_currentTarget
)
414 HideDragHilite( theDrag
);
416 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
417 trackingGlobals
->m_currentTarget
->OnLeave();
418 trackingGlobals
->m_currentTarget
= NULL
;
419 trackingGlobals
->m_currentTargetWindow
= NULL
;
425 // this window is entered
426 trackingGlobals
->m_currentTargetWindow
= win
;
427 trackingGlobals
->m_currentTarget
= win
->GetDropTarget();
429 if ( trackingGlobals
->m_currentTarget
)
431 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
432 result
= trackingGlobals
->m_currentTarget
->OnEnter( localx
, localy
, result
);
435 if ( result
!= wxDragNone
)
440 win
->MacWindowToRootWindow( &x
, &y
);
441 RgnHandle hiliteRgn
= NewRgn();
442 Rect r
= { y
, x
, y
+ win
->GetSize().y
, x
+ win
->GetSize().x
};
443 RectRgn( hiliteRgn
, &r
);
445 ShowDragHilite( theDrag
, hiliteRgn
, true );
447 DisposeRgn( hiliteRgn
);
454 if ( trackingGlobals
->m_currentTarget
)
456 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
457 result
= trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
);
461 // set cursor for OnEnter and OnDragOver
462 if ( trackingGlobals
->m_currentSource
&& !trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
464 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
466 int cursorID
= wxCURSOR_NONE
;
471 cursorID
= wxCURSOR_COPY_ARROW
;
475 cursorID
= wxCURSOR_ARROW
;
479 cursorID
= wxCURSOR_NO_ENTRY
;
486 // put these here to make gcc happy
490 if (cursorID
!= wxCURSOR_NONE
)
492 wxCursor
cursor( cursorID
);
500 case kDragTrackingLeaveWindow
:
501 if (trackingGlobals
== NULL
)
504 if (trackingGlobals
->m_currentTarget
)
506 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
507 trackingGlobals
->m_currentTarget
->OnLeave();
509 HideDragHilite( theDrag
);
511 trackingGlobals
->m_currentTarget
= NULL
;
513 trackingGlobals
->m_currentTargetWindow
= NULL
;
523 pascal OSErr
wxMacWindowDragReceiveHandler(
526 DragReference theDrag
)
528 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
529 if ( trackingGlobals
->m_currentTarget
)
531 Point mouse
, localMouse
;
534 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
535 GetDragMouse( theDrag
, &mouse
, 0L );
537 wxMacGlobalToLocal( theWindow
, &localMouse
);
538 localx
= localMouse
.h
;
539 localy
= localMouse
.v
;
541 // TODO : should we use client coordinates?
542 if ( trackingGlobals
->m_currentTargetWindow
)
543 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
);
544 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
546 // the option key indicates copy in Mac UI, if it's not pressed do
547 // move by default if it's allowed at all
549 result
= !(trackingGlobals
->m_flags
& wxDrag_AllowMove
) ||
550 (GetCurrentKeyModifiers() & optionKey
)
553 trackingGlobals
->m_result
=
554 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
);
561 #endif // wxUSE_DRAG_AND_DROP