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/mac/private.h"
31 // ----------------------------------------------------------------------------
33 // ----------------------------------------------------------------------------
37 wxWindow
*m_currentTargetWindow
;
38 wxDropTarget
*m_currentTarget
;
39 wxDropSource
*m_currentSource
;
42 MacTrackingGlobals gTrackingGlobals
;
44 void wxMacEnsureTrackingHandlersInstalled();
46 //----------------------------------------------------------------------------
48 //----------------------------------------------------------------------------
50 wxDropTarget::wxDropTarget( wxDataObject
*data
)
51 : wxDropTargetBase( data
)
53 wxMacEnsureTrackingHandlersInstalled();
56 wxDragResult
wxDropTarget::OnDragOver(
57 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
60 return CurrentDragHasSupportedFormat() ? def
: wxDragNone
;
63 bool wxDropTarget::OnDrop( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
) )
65 if (m_dataObject
== NULL
)
68 return CurrentDragHasSupportedFormat();
71 wxDragResult
wxDropTarget::OnData(
72 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
75 if (m_dataObject
== NULL
)
78 if (!CurrentDragHasSupportedFormat())
81 return GetData() ? def
: wxDragNone
;
84 bool wxDropTarget::CurrentDragHasSupportedFormat()
86 bool supported
= false;
88 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
90 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
94 size_t formatcount
= data
->GetFormatCount();
95 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
96 data
->GetAllFormats( array
);
97 for (size_t i
= 0; !supported
&& i
< formatcount
; i
++)
99 wxDataFormat format
= array
[i
];
100 if ( m_dataObject
->IsSupported( format
) )
114 ItemReference theItem
;
118 CountDragItems( (DragReference
)m_currentDrag
, &items
);
119 for (UInt16 index
= 1; index
<= items
&& !supported
; ++index
)
122 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
123 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
125 for ( UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
127 GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
128 if ( m_dataObject
->IsSupportedFormat( wxDataFormat( theType
) ) )
140 bool wxDropTarget::GetData()
142 if (m_dataObject
== NULL
)
145 if ( !CurrentDragHasSupportedFormat() )
148 bool transferred
= false;
149 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
151 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
155 size_t formatcount
= data
->GetFormatCount();
156 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
157 data
->GetAllFormats( array
);
158 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
160 wxDataFormat format
= array
[i
];
161 if ( m_dataObject
->IsSupported( format
) )
163 int size
= data
->GetDataSize( format
);
168 m_dataObject
->SetData( format
, 0, 0 );
172 char *d
= new char[size
];
173 data
->GetDataHere( format
, (void*)d
);
174 m_dataObject
->SetData( format
, size
, d
);
188 ItemReference theItem
;
190 FlavorFlags theFlags
;
192 wxString filenamesPassed
;
194 CountDragItems( (DragReference
)m_currentDrag
, &items
);
195 for (UInt16 index
= 1; index
<= items
; ++index
)
198 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
199 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
200 wxDataFormat preferredFormat
= m_dataObject
->GetPreferredFormat( wxDataObject::Set
);
201 bool hasPreferredFormat
= false;
203 for (UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
205 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
206 wxDataFormat
format( theType
);
207 if (preferredFormat
== format
)
209 hasPreferredFormat
= true;
214 for (UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
216 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
217 wxDataFormat
format( theType
);
218 if ((hasPreferredFormat
&& format
== preferredFormat
)
219 || (!hasPreferredFormat
&& m_dataObject
->IsSupportedFormat( format
)))
221 result
= GetFlavorFlags( (DragReference
)m_currentDrag
, theItem
, theType
, &theFlags
);
227 GetFlavorDataSize( (DragReference
)m_currentDrag
, theItem
, theType
, &dataSize
);
228 if (theType
== kScrapFlavorTypeText
)
230 // this increment is only valid for allocating:
231 // on the next GetFlavorData call it is reset again to the original value
234 else if (theType
== kScrapFlavorTypeUnicode
)
236 // this increment is only valid for allocating:
237 // on the next GetFlavorData call it is reset again to the original value
243 theData
= new char[dataSize
];
247 GetFlavorData( (DragReference
)m_currentDrag
, theItem
, theType
, (void*)theData
, &dataSize
, 0L );
250 case kScrapFlavorTypeText
:
251 theData
[dataSize
] = 0;
252 m_dataObject
->SetData( wxDataFormat(wxDF_TEXT
), dataSize
, theData
);
256 case kScrapFlavorTypeUnicode
:
257 theData
[dataSize
+ 0] =
258 theData
[dataSize
+ 1] = 0;
259 m_dataObject
->SetData( wxDataFormat(wxDF_UNICODETEXT
), dataSize
, theData
);
263 case kDragFlavorTypeHFS
:
266 HFSFlavor
* theFile
= (HFSFlavor
*)theData
;
268 wxString name
= wxMacFSSpec2MacFilename( &theFile
->fileSpec
);
271 filenamesPassed
+= name
+ wxT("\n");
277 m_dataObject
->SetData( format
, dataSize
, theData
);
288 if (filenamesPassed
.length() > 0)
290 wxCharBuffer buf
= filenamesPassed
.fn_str();
291 m_dataObject
->SetData( wxDataFormat(wxDF_FILENAME
), strlen( buf
), (const char*)buf
);
298 //-------------------------------------------------------------------------
300 //-------------------------------------------------------------------------
302 //-----------------------------------------------------------------------------
305 wxDropSource::wxDropSource(wxWindow
*win
,
306 const wxCursor
&cursorCopy
,
307 const wxCursor
&cursorMove
,
308 const wxCursor
&cursorStop
)
309 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
311 wxMacEnsureTrackingHandlersInstalled();
316 wxDropSource::wxDropSource(wxDataObject
& data
,
318 const wxCursor
&cursorCopy
,
319 const wxCursor
&cursorMove
,
320 const wxCursor
&cursorStop
)
321 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
323 wxMacEnsureTrackingHandlersInstalled();
329 wxDropSource::~wxDropSource()
333 wxDragResult
wxDropSource::DoDragDrop(int flags
)
335 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
337 if ((m_data
== NULL
) || (m_data
->GetFormatCount() == 0))
338 return (wxDragResult
)wxDragNone
;
340 DragReference theDrag
;
341 RgnHandle dragRegion
;
343 if (NewDrag( &theDrag
) != noErr
)
347 size_t formatCount
= m_data
->GetFormatCount();
348 wxDataFormat
*formats
= new wxDataFormat
[formatCount
];
349 m_data
->GetAllFormats( formats
);
350 ItemReference theItem
= (ItemReference
) 1;
352 for ( size_t i
= 0; i
< formatCount
; ++i
)
354 size_t dataSize
= m_data
->GetDataSize( formats
[i
] );
355 Ptr dataPtr
= new char[dataSize
];
356 m_data
->GetDataHere( formats
[i
], dataPtr
);
357 OSType type
= formats
[i
].GetFormatId();
358 if ( type
== 'TEXT' || type
== 'utxt' )
362 dataPtr
[ dataSize
] = 0;
363 if ( type
== 'utxt' )
367 dataPtr
[ dataSize
] = 0;
370 AddDragItemFlavor( theDrag
, theItem
, type
, dataPtr
, dataSize
, 0 );
372 else if (type
== kDragFlavorTypeHFS
)
379 wxMacFilename2FSSpec( wxString( dataPtr
, *wxConvCurrent
), &theFlavor
.fileSpec
);
381 memset( &cat
, 0, sizeof(cat
) );
382 cat
.hFileInfo
.ioNamePtr
= theFlavor
.fileSpec
.name
;
383 cat
.hFileInfo
.ioVRefNum
= theFlavor
.fileSpec
.vRefNum
;
384 cat
.hFileInfo
.ioDirID
= theFlavor
.fileSpec
.parID
;
385 cat
.hFileInfo
.ioFDirIndex
= 0;
386 err
= PBGetCatInfoSync( &cat
);
391 theFlavor
.fdFlags
= cat
.hFileInfo
.ioFlFndrInfo
.fdFlags
;
392 if (theFlavor
.fileSpec
.parID
== fsRtParID
)
394 theFlavor
.fileCreator
= 'MACS';
395 theFlavor
.fileType
= 'disk';
397 else if ((cat
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0)
399 theFlavor
.fileCreator
= 'MACS';
400 theFlavor
.fileType
= 'fold';
404 theFlavor
.fileCreator
= cat
.hFileInfo
.ioFlFndrInfo
.fdCreator
;
405 theFlavor
.fileType
= cat
.hFileInfo
.ioFlFndrInfo
.fdType
;
408 AddDragItemFlavor( theDrag
, theItem
, type
, &theFlavor
, sizeof(theFlavor
), 0 );
413 AddDragItemFlavor( theDrag
, theItem
, type
, dataPtr
, dataSize
, 0 );
421 dragRegion
= NewRgn();
422 RgnHandle tempRgn
= NewRgn();
424 EventRecord
* ev
= NULL
;
426 #if !TARGET_CARBON // TODO
427 ev
= (EventRecord
*) wxTheApp
->MacGetCurrentEvent();
431 wxMacConvertEventToRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent(), &rec
);
434 const short dragRegionOuterBoundary
= 10;
435 const short dragRegionInnerBoundary
= 9;
439 ev
->where
.h
- dragRegionOuterBoundary
,
440 ev
->where
.v
- dragRegionOuterBoundary
,
441 ev
->where
.h
+ dragRegionOuterBoundary
,
442 ev
->where
.v
+ dragRegionOuterBoundary
);
446 ev
->where
.h
- dragRegionInnerBoundary
,
447 ev
->where
.v
- dragRegionInnerBoundary
,
448 ev
->where
.h
+ dragRegionInnerBoundary
,
449 ev
->where
.v
+ dragRegionInnerBoundary
);
451 DiffRgn( dragRegion
, tempRgn
, dragRegion
);
452 DisposeRgn( tempRgn
);
454 // TODO: work with promises in order to return data
455 // only when drag was successfully completed
457 gTrackingGlobals
.m_currentSource
= this;
458 TrackDrag( theDrag
, ev
, dragRegion
);
459 DisposeRgn( dragRegion
);
460 DisposeDrag( theDrag
);
461 gTrackingGlobals
.m_currentSource
= NULL
;
463 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
464 wxDragResult dndresult
= wxDragCopy
;
465 if ( flags
!= wxDrag_CopyOnly
)
466 // on mac the option key is always the indication for copy
467 dndresult
= optionDown
? wxDragCopy
: wxDragMove
;
472 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
474 const wxCursor
& cursor
= GetCursor(effect
);
475 bool result
= cursor
.Ok();
483 bool gTrackingGlobalsInstalled
= false;
485 // passing the globals via refcon is not needed by the CFM and later architectures anymore
486 // but I'll leave it in there, just in case...
488 pascal OSErr
wxMacWindowDragTrackingHandler(
489 DragTrackingMessage theMessage
, WindowPtr theWindow
,
490 void *handlerRefCon
, DragReference theDrag
);
491 pascal OSErr
wxMacWindowDragReceiveHandler(
492 WindowPtr theWindow
, void *handlerRefCon
,
493 DragReference theDrag
);
495 void wxMacEnsureTrackingHandlersInstalled()
497 if ( !gTrackingGlobalsInstalled
)
501 err
= InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L, &gTrackingGlobals
);
504 err
= InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
507 gTrackingGlobalsInstalled
= true;
511 pascal OSErr
wxMacWindowDragTrackingHandler(
512 DragTrackingMessage theMessage
, WindowPtr theWindow
,
513 void *handlerRefCon
, DragReference theDrag
)
515 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
517 Point mouse
, localMouse
;
518 DragAttributes attributes
;
520 GetDragAttributes( theDrag
, &attributes
);
522 wxTopLevelWindowMac
* toplevel
= wxFindWinFromMacWindow( theWindow
);
524 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
525 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
529 case kDragTrackingEnterHandler
:
530 case kDragTrackingLeaveHandler
:
533 case kDragTrackingEnterWindow
:
534 if (trackingGlobals
!= NULL
)
536 trackingGlobals
->m_currentTargetWindow
= NULL
;
537 trackingGlobals
->m_currentTarget
= NULL
;
541 case kDragTrackingInWindow
:
542 if (trackingGlobals
== NULL
)
544 if (toplevel
== NULL
)
547 GetDragMouse( theDrag
, &mouse
, 0L );
549 wxMacGlobalToLocal( theWindow
, &localMouse
);
552 wxWindow
*win
= NULL
;
553 ControlPartCode controlPart
;
554 ControlRef control
= wxMacFindControlUnderMouse(
555 toplevel
, localMouse
, theWindow
, &controlPart
);
557 win
= wxFindControlFromMacControl( control
);
562 localx
= localMouse
.h
;
563 localy
= localMouse
.v
;
566 win
->MacRootWindowToWindow( &localx
, &localy
);
567 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
569 if ( trackingGlobals
->m_currentTargetWindow
)
571 // this window is left
572 if ( trackingGlobals
->m_currentTarget
)
575 HideDragHilite( theDrag
);
577 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
578 trackingGlobals
->m_currentTarget
->OnLeave();
579 trackingGlobals
->m_currentTarget
= NULL
;
580 trackingGlobals
->m_currentTargetWindow
= NULL
;
586 // this window is entered
587 trackingGlobals
->m_currentTargetWindow
= win
;
588 trackingGlobals
->m_currentTarget
= win
->GetDropTarget();
590 if ( trackingGlobals
->m_currentTarget
)
592 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
593 result
= trackingGlobals
->m_currentTarget
->OnEnter( localx
, localy
, result
);
596 if ( result
!= wxDragNone
)
601 win
->MacWindowToRootWindow( &x
, &y
);
602 RgnHandle hiliteRgn
= NewRgn();
603 Rect r
= { y
, x
, y
+ win
->GetSize().y
, x
+ win
->GetSize().x
};
604 RectRgn( hiliteRgn
, &r
);
606 ShowDragHilite( theDrag
, hiliteRgn
, true );
608 DisposeRgn( hiliteRgn
);
615 if ( trackingGlobals
->m_currentTarget
)
617 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
618 trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
);
622 // set cursor for OnEnter and OnDragOver
623 if ( trackingGlobals
->m_currentSource
&& !trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
625 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
627 int cursorID
= wxCURSOR_NONE
;
632 cursorID
= wxCURSOR_COPY_ARROW
;
636 cursorID
= wxCURSOR_ARROW
;
640 cursorID
= wxCURSOR_NO_ENTRY
;
647 // put these here to make gcc happy
651 if (cursorID
!= wxCURSOR_NONE
)
653 wxCursor
cursor( cursorID
);
661 case kDragTrackingLeaveWindow
:
662 if (trackingGlobals
== NULL
)
665 if (trackingGlobals
->m_currentTarget
)
667 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
668 trackingGlobals
->m_currentTarget
->OnLeave();
670 HideDragHilite( theDrag
);
672 trackingGlobals
->m_currentTarget
= NULL
;
674 trackingGlobals
->m_currentTargetWindow
= NULL
;
684 pascal OSErr
wxMacWindowDragReceiveHandler(
687 DragReference theDrag
)
689 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
690 if ( trackingGlobals
->m_currentTarget
)
692 Point mouse
, localMouse
;
695 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
696 GetDragMouse( theDrag
, &mouse
, 0L );
698 wxMacGlobalToLocal( theWindow
, &localMouse
);
699 localx
= localMouse
.h
;
700 localy
= localMouse
.v
;
702 // TODO : should we use client coordinates?
703 if ( trackingGlobals
->m_currentTargetWindow
)
704 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
);
705 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
707 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
708 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
709 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
);