1 ///////////////////////////////////////////////////////////////////////////////
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
17 #include "wx/window.h"
18 #include "wx/toplevel.h"
20 #include "wx/gdicmn.h"
21 #include "wx/mac/private.h"
28 // ----------------------------------------------------------------------------
30 // ----------------------------------------------------------------------------
34 wxWindow
*m_currentTargetWindow
;
35 wxDropTarget
*m_currentTarget
;
36 wxDropSource
*m_currentSource
;
40 MacTrackingGlobals gTrackingGlobals
;
42 void wxMacEnsureTrackingHandlersInstalled();
44 //----------------------------------------------------------------------------
46 //----------------------------------------------------------------------------
48 wxDropTarget::wxDropTarget( wxDataObject
*data
)
49 : wxDropTargetBase( data
)
51 wxMacEnsureTrackingHandlersInstalled();
54 wxDragResult
wxDropTarget::OnDragOver(
55 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
58 return CurrentDragHasSupportedFormat() ? def
: wxDragNone
;
61 bool wxDropTarget::OnDrop( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
) )
63 if (m_dataObject
== NULL
)
66 return CurrentDragHasSupportedFormat();
69 wxDragResult
wxDropTarget::OnData(
70 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
73 if (m_dataObject
== NULL
)
76 if (!CurrentDragHasSupportedFormat())
79 return GetData() ? def
: wxDragNone
;
82 bool wxDropTarget::CurrentDragHasSupportedFormat()
84 bool supported
= false;
86 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
88 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
92 size_t formatcount
= data
->GetFormatCount();
93 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
94 data
->GetAllFormats( array
);
95 for (size_t i
= 0; !supported
&& i
< formatcount
; i
++)
97 wxDataFormat format
= array
[i
];
98 if ( m_dataObject
->IsSupported( format
) )
113 ItemReference theItem
;
117 CountDragItems( (DragReference
)m_currentDrag
, &items
);
118 for (UInt16 index
= 1; index
<= items
&& !supported
; ++index
)
121 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
122 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
124 for ( UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
126 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
127 if ( m_dataObject
->IsSupportedFormat( wxDataFormat( theType
) ) )
139 bool wxDropTarget::GetData()
141 if (m_dataObject
== NULL
)
144 if ( !CurrentDragHasSupportedFormat() )
147 bool transferred
= false;
148 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
150 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
154 size_t formatcount
= data
->GetFormatCount();
155 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
156 data
->GetAllFormats( array
);
157 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
159 wxDataFormat format
= array
[i
];
160 if ( m_dataObject
->IsSupported( format
) )
162 int size
= data
->GetDataSize( format
);
167 m_dataObject
->SetData( format
, 0, 0 );
171 char *d
= new char[size
];
172 data
->GetDataHere( format
, (void*)d
);
173 m_dataObject
->SetData( format
, size
, d
);
187 ItemReference theItem
;
189 FlavorFlags theFlags
;
191 bool firstFileAdded
= false;
193 CountDragItems( (DragReference
)m_currentDrag
, &items
);
194 for (UInt16 index
= 1; index
<= items
; ++index
)
197 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
198 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
199 wxDataFormat preferredFormat
= m_dataObject
->GetPreferredFormat( wxDataObject::Set
);
200 bool hasPreferredFormat
= false;
202 for ( UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
204 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
205 wxDataFormat
format( theType
);
206 if ( preferredFormat
== format
)
208 hasPreferredFormat
= true;
213 for ( UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
215 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
216 wxDataFormat
format( theType
);
217 if ( (hasPreferredFormat
&& format
== preferredFormat
)
218 || (!hasPreferredFormat
&& m_dataObject
->IsSupportedFormat( format
)))
220 result
= GetFlavorFlags( (DragReference
)m_currentDrag
, theItem
, theType
, &theFlags
);
226 GetFlavorDataSize( (DragReference
)m_currentDrag
, theItem
, theType
, &dataSize
);
227 if ( theType
== kScrapFlavorTypeText
)
229 // this increment is only valid for allocating:
230 // on the next GetFlavorData call it is reset again to the original value
233 else if ( theType
== kScrapFlavorTypeUnicode
)
235 // this increment is only valid for allocating:
236 // on the next GetFlavorData call it is reset again to the original value
242 theData
= new char[dataSize
];
246 GetFlavorData( (DragReference
)m_currentDrag
, theItem
, theType
, (void*) theData
, &dataSize
, 0L );
249 case kScrapFlavorTypeText
:
250 theData
[dataSize
] = 0;
251 m_dataObject
->SetData( wxDataFormat(wxDF_TEXT
), dataSize
, theData
);
255 case kScrapFlavorTypeUnicode
:
256 theData
[dataSize
+ 0] =
257 theData
[dataSize
+ 1] = 0;
258 m_dataObject
->SetData( wxDataFormat(wxDF_UNICODETEXT
), dataSize
, theData
);
262 case kDragFlavorTypeHFS
:
264 wxFileDataObject
*fdo
= dynamic_cast<wxFileDataObject
*>(m_dataObject
);
265 wxASSERT( fdo
!= NULL
);
267 if ((theData
!= NULL
) && (fdo
!= NULL
))
269 HFSFlavor
* theFile
= (HFSFlavor
*) theData
;
270 wxString name
= wxMacFSSpec2MacFilename( &theFile
->fileSpec
);
272 if ( !firstFileAdded
)
275 fdo
->SetData( 0, "" );
276 firstFileAdded
= true;
280 fdo
->AddFile( name
);
286 m_dataObject
->SetData( format
, dataSize
, theData
);
301 //-------------------------------------------------------------------------
303 //-------------------------------------------------------------------------
305 //-----------------------------------------------------------------------------
308 wxDropSource::wxDropSource(wxWindow
*win
,
309 const wxCursor
&cursorCopy
,
310 const wxCursor
&cursorMove
,
311 const wxCursor
&cursorStop
)
312 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
314 wxMacEnsureTrackingHandlersInstalled();
319 wxDropSource::wxDropSource(wxDataObject
& data
,
321 const wxCursor
&cursorCopy
,
322 const wxCursor
&cursorMove
,
323 const wxCursor
&cursorStop
)
324 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
326 wxMacEnsureTrackingHandlersInstalled();
332 wxDropSource::~wxDropSource()
336 wxDragResult
wxDropSource::DoDragDrop(int flags
)
338 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
341 return (wxDragResult
) wxDragNone
;
342 if (m_data
->GetFormatCount() == 0)
343 return (wxDragResult
) wxDragNone
;
346 DragReference theDrag
;
347 RgnHandle dragRegion
;
348 if ((result
= NewDrag(&theDrag
)) != noErr
)
352 size_t formatCount
= m_data
->GetFormatCount();
353 wxDataFormat
*formats
= new wxDataFormat
[formatCount
];
354 m_data
->GetAllFormats( formats
);
355 ItemReference theItem
= 1;
357 for ( size_t i
= 0; i
< formatCount
; ++i
)
359 size_t dataSize
= m_data
->GetDataSize( formats
[i
] );
360 Ptr dataPtr
= new char[dataSize
];
361 m_data
->GetDataHere( formats
[i
], dataPtr
);
362 OSType type
= formats
[i
].GetFormatId();
363 if ( type
== 'TEXT' || type
== 'utxt' )
367 dataPtr
[ dataSize
] = 0;
368 if ( type
== 'utxt' )
372 dataPtr
[ dataSize
] = 0;
375 AddDragItemFlavor( theDrag
, theItem
, type
, dataPtr
, dataSize
, 0 );
377 else if (type
== kDragFlavorTypeHFS
)
383 wxMacFilename2FSSpec( wxString( dataPtr
, *wxConvCurrent
), &theFlavor
.fileSpec
);
385 memset( &cat
, 0, sizeof(cat
) );
386 cat
.hFileInfo
.ioNamePtr
= theFlavor
.fileSpec
.name
;
387 cat
.hFileInfo
.ioVRefNum
= theFlavor
.fileSpec
.vRefNum
;
388 cat
.hFileInfo
.ioDirID
= theFlavor
.fileSpec
.parID
;
389 cat
.hFileInfo
.ioFDirIndex
= 0;
390 err
= PBGetCatInfoSync( &cat
);
393 theFlavor
.fdFlags
= cat
.hFileInfo
.ioFlFndrInfo
.fdFlags
;
394 if (theFlavor
.fileSpec
.parID
== fsRtParID
)
396 theFlavor
.fileCreator
= 'MACS';
397 theFlavor
.fileType
= 'disk';
399 else if ((cat
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0)
401 theFlavor
.fileCreator
= 'MACS';
402 theFlavor
.fileType
= 'fold';
406 theFlavor
.fileCreator
= cat
.hFileInfo
.ioFlFndrInfo
.fdCreator
;
407 theFlavor
.fileType
= cat
.hFileInfo
.ioFlFndrInfo
.fdType
;
410 AddDragItemFlavor( theDrag
, theItem
, type
, &theFlavor
, sizeof(theFlavor
), 0 );
415 AddDragItemFlavor( theDrag
, theItem
, type
, dataPtr
, dataSize
, 0 );
423 dragRegion
= NewRgn();
424 RgnHandle tempRgn
= NewRgn();
426 EventRecord
* ev
= NULL
;
428 #if !TARGET_CARBON // TODO
429 ev
= (EventRecord
*) wxTheApp
->MacGetCurrentEvent();
433 wxMacConvertEventToRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent(), &rec
);
436 const short dragRegionOuterBoundary
= 10;
437 const short dragRegionInnerBoundary
= 9;
441 ev
->where
.h
- dragRegionOuterBoundary
,
442 ev
->where
.v
- dragRegionOuterBoundary
,
443 ev
->where
.h
+ dragRegionOuterBoundary
,
444 ev
->where
.v
+ dragRegionOuterBoundary
);
448 ev
->where
.h
- dragRegionInnerBoundary
,
449 ev
->where
.v
- dragRegionInnerBoundary
,
450 ev
->where
.h
+ dragRegionInnerBoundary
,
451 ev
->where
.v
+ dragRegionInnerBoundary
);
453 DiffRgn( dragRegion
, tempRgn
, dragRegion
);
454 DisposeRgn( tempRgn
);
456 // TODO: work with promises in order to return data
457 // only when drag was successfully completed
459 gTrackingGlobals
.m_currentSource
= this;
460 result
= TrackDrag( theDrag
, ev
, dragRegion
);
461 DisposeRgn( dragRegion
);
462 DisposeDrag( theDrag
);
463 gTrackingGlobals
.m_currentSource
= NULL
;
465 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
466 wxDragResult dndresult
= wxDragCopy
;
467 if ( flags
!= wxDrag_CopyOnly
)
468 // on mac the option key is always the indication for copy
469 dndresult
= optionDown
? wxDragCopy
: wxDragMove
;
474 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
476 const wxCursor
& cursor
= GetCursor(effect
);
477 bool result
= cursor
.Ok();
485 bool gTrackingGlobalsInstalled
= false;
487 // passing the globals via refcon is not needed by the CFM and later architectures anymore
488 // but I'll leave it in there, just in case...
490 pascal OSErr
wxMacWindowDragTrackingHandler(
491 DragTrackingMessage theMessage
, WindowPtr theWindow
,
492 void *handlerRefCon
, DragReference theDrag
);
493 pascal OSErr
wxMacWindowDragReceiveHandler(
494 WindowPtr theWindow
, void *handlerRefCon
,
495 DragReference theDrag
);
497 void wxMacEnsureTrackingHandlersInstalled()
499 if ( !gTrackingGlobalsInstalled
)
503 err
= InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L, &gTrackingGlobals
);
506 err
= InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
509 gTrackingGlobalsInstalled
= true;
513 pascal OSErr
wxMacWindowDragTrackingHandler(
514 DragTrackingMessage theMessage
, WindowPtr theWindow
,
515 void *handlerRefCon
, DragReference theDrag
)
517 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
519 Point mouse
, localMouse
;
520 DragAttributes attributes
;
522 GetDragAttributes( theDrag
, &attributes
);
524 wxTopLevelWindowMac
* toplevel
= wxFindWinFromMacWindow( theWindow
);
526 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
527 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
531 case kDragTrackingEnterHandler
:
532 case kDragTrackingLeaveHandler
:
535 case kDragTrackingEnterWindow
:
536 if (trackingGlobals
!= NULL
)
538 trackingGlobals
->m_currentTargetWindow
= NULL
;
539 trackingGlobals
->m_currentTarget
= NULL
;
543 case kDragTrackingInWindow
:
544 if (trackingGlobals
== NULL
)
546 if (toplevel
== NULL
)
549 GetDragMouse( theDrag
, &mouse
, 0L );
551 GlobalToLocal( &localMouse
);
554 wxWindow
*win
= NULL
;
555 ControlPartCode controlPart
;
556 ControlRef control
= wxMacFindControlUnderMouse(
557 toplevel
, localMouse
, theWindow
, &controlPart
);
559 win
= wxFindControlFromMacControl( control
);
564 localx
= localMouse
.h
;
565 localy
= localMouse
.v
;
568 win
->MacRootWindowToWindow( &localx
, &localy
);
569 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
571 if ( trackingGlobals
->m_currentTargetWindow
)
573 // this window is left
574 if ( trackingGlobals
->m_currentTarget
)
576 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
);
605 ShowDragHilite( theDrag
, hiliteRgn
, true );
606 DisposeRgn( hiliteRgn
);
613 if ( trackingGlobals
->m_currentTarget
)
615 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
616 trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
);
620 // set cursor for OnEnter and OnDragOver
621 if ( trackingGlobals
->m_currentSource
&& !trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
623 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
629 wxCursor
cursor(wxCURSOR_COPY_ARROW
);
636 wxCursor
cursor(wxCURSOR_ARROW
);
643 wxCursor
cursor(wxCURSOR_NO_ENTRY
);
652 // put these here to make gcc happy
660 case kDragTrackingLeaveWindow
:
661 if (trackingGlobals
== NULL
)
664 if (trackingGlobals
->m_currentTarget
)
666 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
667 trackingGlobals
->m_currentTarget
->OnLeave();
668 HideDragHilite( theDrag
);
669 trackingGlobals
->m_currentTarget
= NULL
;
671 trackingGlobals
->m_currentTargetWindow
= NULL
;
681 pascal OSErr
wxMacWindowDragReceiveHandler(
684 DragReference theDrag
)
686 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
687 if ( trackingGlobals
->m_currentTarget
)
689 Point mouse
, localMouse
;
692 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
693 GetDragMouse( theDrag
, &mouse
, 0L );
695 GlobalToLocal( &localMouse
);
696 localx
= localMouse
.h
;
697 localy
= localMouse
.v
;
699 // TODO : should we use client coordinates?
700 if ( trackingGlobals
->m_currentTargetWindow
)
701 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
);
702 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
704 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
705 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
706 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
);