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"
27 // ----------------------------------------------------------------------------
29 // ----------------------------------------------------------------------------
33 wxWindow
* m_currentTargetWindow
;
34 wxDropTarget
* m_currentTarget
;
35 wxDropSource
* m_currentSource
;
39 MacTrackingGlobals gTrackingGlobals
;
41 void wxMacEnsureTrackingHandlersInstalled() ;
43 //----------------------------------------------------------------------------
45 //----------------------------------------------------------------------------
47 wxDropTarget::wxDropTarget( wxDataObject
*data
)
48 : wxDropTargetBase( data
)
50 wxMacEnsureTrackingHandlersInstalled() ;
53 wxDragResult
wxDropTarget::OnDragOver( wxCoord
WXUNUSED(x
),
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( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
71 if (m_dataObject
== NULL
)
74 if (!CurrentDragHasSupportedFormat())
77 return GetData() ? def
: wxDragNone
;
80 bool wxDropTarget::CurrentDragHasSupportedFormat()
82 bool supported
= false ;
84 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
86 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject() ;
90 size_t formatcount
= data
->GetFormatCount() ;
91 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
92 data
->GetAllFormats( array
);
93 for (size_t i
= 0; !supported
&& i
< formatcount
; i
++)
95 wxDataFormat format
= array
[i
] ;
96 if ( m_dataObject
->IsSupported( format
) )
111 ItemReference theItem
;
115 CountDragItems((DragReference
)m_currentDrag
, &items
);
116 for (UInt16 index
= 1; index
<= items
&& !supported
; ++index
)
119 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
120 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
122 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
124 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
125 if ( m_dataObject
->IsSupportedFormat( wxDataFormat( theType
) ) )
137 bool wxDropTarget::GetData()
142 if ( !CurrentDragHasSupportedFormat() )
145 bool transferred
= false ;
146 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
148 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject() ;
152 size_t formatcount
= data
->GetFormatCount() ;
153 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
154 data
->GetAllFormats( array
);
155 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
157 wxDataFormat format
= array
[i
] ;
158 if ( m_dataObject
->IsSupported( format
) )
160 int size
= data
->GetDataSize( format
);
165 m_dataObject
->SetData( format
, 0 , 0 );
169 char *d
= new char[size
];
170 data
->GetDataHere( format
, (void*) d
);
171 m_dataObject
->SetData( format
, size
, d
) ;
185 ItemReference theItem
;
187 FlavorFlags theFlags
;
189 bool firstFileAdded
= false ;
191 CountDragItems( (DragReference
)m_currentDrag
, &items
);
192 for (UInt16 index
= 1; index
<= items
; ++index
)
195 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
196 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
197 bool hasPreferredFormat
= false ;
198 wxDataFormat preferredFormat
= m_dataObject
->GetPreferredFormat( wxDataObject::Set
) ;
200 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
202 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
203 wxDataFormat
format( theType
);
204 if ( preferredFormat
== format
)
206 hasPreferredFormat
= true ;
211 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
213 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
214 wxDataFormat
format(theType
) ;
215 if ( (hasPreferredFormat
&& format
== preferredFormat
)
216 || (!hasPreferredFormat
&& m_dataObject
->IsSupportedFormat( format
)))
218 result
= GetFlavorFlags( (DragReference
)m_currentDrag
, theItem
, theType
, &theFlags
);
224 GetFlavorDataSize( (DragReference
)m_currentDrag
, theItem
, theType
, &dataSize
);
225 if ( theType
== kScrapFlavorTypeText
)
227 // this increment is only valid for allocating, on the next GetFlavorData
228 // call it is reset again to the original value
231 else if ( theType
== kScrapFlavorTypeUnicode
)
233 // this increment is only valid for allocating, on the next GetFlavorData
234 // call it is reset again to the original value
239 theData
= new char[dataSize
];
241 GetFlavorData( (DragReference
)m_currentDrag
, theItem
, theType
, (void*) theData
, &dataSize
, 0L );
242 if ( theType
== kScrapFlavorTypeText
)
244 theData
[dataSize
] = 0 ;
245 m_dataObject
->SetData( wxDataFormat(wxDF_TEXT
), dataSize
, theData
);
248 else if ( theType
== kScrapFlavorTypeUnicode
)
250 theData
[dataSize
+ 0] =
251 theData
[dataSize
+ 1] = 0 ;
252 m_dataObject
->SetData( wxDataFormat(wxDF_UNICODETEXT
), dataSize
, theData
);
255 else if ( theType
== kDragFlavorTypeHFS
)
257 HFSFlavor
* theFile
= (HFSFlavor
*) theData
;
258 wxString name
= wxMacFSSpec2MacFilename( &theFile
->fileSpec
) ;
259 if ( !firstFileAdded
)
262 ((wxFileDataObject
*)m_dataObject
)->SetData( 0 , "" ) ;
263 firstFileAdded
= true ;
266 ((wxFileDataObject
*)m_dataObject
)->AddFile( name
) ;
270 m_dataObject
->SetData( format
, dataSize
, theData
);
284 //-------------------------------------------------------------------------
286 //-------------------------------------------------------------------------
288 //-----------------------------------------------------------------------------
291 wxDropSource::wxDropSource(wxWindow
*win
,
292 const wxCursor
&cursorCopy
,
293 const wxCursor
&cursorMove
,
294 const wxCursor
&cursorStop
)
295 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
297 wxMacEnsureTrackingHandlersInstalled() ;
301 wxDropSource::wxDropSource(wxDataObject
& data
,
303 const wxCursor
&cursorCopy
,
304 const wxCursor
&cursorMove
,
305 const wxCursor
&cursorStop
)
306 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
308 wxMacEnsureTrackingHandlersInstalled() ;
313 wxDropSource::~wxDropSource()
317 wxDragResult
wxDropSource::DoDragDrop(int flags
)
319 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
322 return (wxDragResult
) wxDragNone
;
323 if (m_data
->GetFormatCount() == 0)
324 return (wxDragResult
) wxDragNone
;
327 DragReference theDrag
;
328 RgnHandle dragRegion
;
329 if ((result
= NewDrag(&theDrag
)) != noErr
)
333 size_t formatCount
= m_data
->GetFormatCount() ;
334 wxDataFormat
*formats
= new wxDataFormat
[formatCount
] ;
335 m_data
->GetAllFormats( formats
) ;
336 ItemReference theItem
= 1 ;
338 for ( size_t i
= 0 ; i
< formatCount
; ++i
)
340 size_t dataSize
= m_data
->GetDataSize( formats
[i
] ) ;
341 Ptr dataPtr
= new char[dataSize
] ;
342 m_data
->GetDataHere( formats
[i
] , dataPtr
) ;
343 OSType type
= formats
[i
].GetFormatId() ;
344 if ( type
== 'TEXT' || type
== 'utxt' )
348 dataPtr
[ dataSize
] = 0 ;
349 if ( type
== 'utxt' )
353 dataPtr
[ dataSize
] = 0 ;
356 AddDragItemFlavor(theDrag
, theItem
, type
, dataPtr
, dataSize
, 0);
358 else if (type
== kDragFlavorTypeHFS
)
360 HFSFlavor theFlavor
;
364 wxMacFilename2FSSpec( wxString( dataPtr
, *wxConvCurrent
) , &theFlavor
.fileSpec
) ;
366 memset( &cat
, 0, sizeof(cat
) );
367 cat
.hFileInfo
.ioNamePtr
= theFlavor
.fileSpec
.name
;
368 cat
.hFileInfo
.ioVRefNum
= theFlavor
.fileSpec
.vRefNum
;
369 cat
.hFileInfo
.ioDirID
= theFlavor
.fileSpec
.parID
;
370 cat
.hFileInfo
.ioFDirIndex
= 0;
371 err
= PBGetCatInfoSync(&cat
);
374 theFlavor
.fdFlags
= cat
.hFileInfo
.ioFlFndrInfo
.fdFlags
;
375 if (theFlavor
.fileSpec
.parID
== fsRtParID
)
377 theFlavor
.fileCreator
= 'MACS';
378 theFlavor
.fileType
= 'disk';
380 else if ((cat
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0)
382 theFlavor
.fileCreator
= 'MACS';
383 theFlavor
.fileType
= 'fold';
387 theFlavor
.fileCreator
= cat
.hFileInfo
.ioFlFndrInfo
.fdCreator
;
388 theFlavor
.fileType
= cat
.hFileInfo
.ioFlFndrInfo
.fdType
;
391 AddDragItemFlavor(theDrag
, theItem
, type
, &theFlavor
, sizeof(theFlavor
), 0);
396 AddDragItemFlavor(theDrag
, theItem
, type
, dataPtr
, dataSize
, 0);
404 dragRegion
= NewRgn();
405 RgnHandle tempRgn
= NewRgn() ;
407 EventRecord
* ev
= NULL
;
409 #if !TARGET_CARBON // TODO
410 ev
= (EventRecord
*) wxTheApp
->MacGetCurrentEvent() ;
415 wxMacConvertEventToRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent() , &rec
) ;
419 const short dragRegionOuterBoundary
= 10 ;
420 const short dragRegionInnerBoundary
= 9 ;
423 dragRegion
, ev
->where
.h
- dragRegionOuterBoundary
,
424 ev
->where
.v
- dragRegionOuterBoundary
,
425 ev
->where
.h
+ dragRegionOuterBoundary
,
426 ev
->where
.v
+ dragRegionOuterBoundary
) ;
429 tempRgn
, ev
->where
.h
- dragRegionInnerBoundary
,
430 ev
->where
.v
- dragRegionInnerBoundary
,
431 ev
->where
.h
+ dragRegionInnerBoundary
,
432 ev
->where
.v
+ dragRegionInnerBoundary
) ;
434 DiffRgn( dragRegion
, tempRgn
, dragRegion
) ;
435 DisposeRgn( tempRgn
) ;
437 // TODO:work with promises in order to return data only when drag
438 // was successfully completed
440 gTrackingGlobals
.m_currentSource
= this ;
441 result
= TrackDrag(theDrag
, ev
, dragRegion
);
442 DisposeRgn(dragRegion
);
443 DisposeDrag(theDrag
);
444 gTrackingGlobals
.m_currentSource
= NULL
;
446 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
447 wxDragResult dndresult
= wxDragCopy
;
448 if ( flags
!= wxDrag_CopyOnly
)
449 // on mac the option key is always the indication for copy
450 dndresult
= optionDown
? wxDragCopy
: wxDragMove
;
455 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
457 const wxCursor
& cursor
= GetCursor(effect
);
458 bool result
= cursor
.Ok();
461 cursor
.MacInstall() ;
466 bool gTrackingGlobalsInstalled
= false ;
468 // passing the globals via refcon is not needed by the CFM and later architectures anymore
469 // but I'll leave it in there, just in case...
471 pascal OSErr
wxMacWindowDragTrackingHandler(
472 DragTrackingMessage theMessage
, WindowPtr theWindow
,
473 void *handlerRefCon
, DragReference theDrag
) ;
474 pascal OSErr
wxMacWindowDragReceiveHandler(
475 WindowPtr theWindow
, void *handlerRefCon
,
476 DragReference theDrag
) ;
478 void wxMacEnsureTrackingHandlersInstalled()
480 if ( !gTrackingGlobalsInstalled
)
484 result
= InstallTrackingHandler(NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L,&gTrackingGlobals
);
485 wxASSERT( result
== noErr
) ;
487 result
= InstallReceiveHandler(NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
488 wxASSERT( result
== noErr
) ;
490 gTrackingGlobalsInstalled
= true ;
494 pascal OSErr
wxMacWindowDragTrackingHandler(
495 DragTrackingMessage theMessage
, WindowPtr theWindow
,
496 void *handlerRefCon
, DragReference theDrag
)
498 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
499 Point mouse
, localMouse
;
500 DragAttributes attributes
;
501 GetDragAttributes(theDrag
, &attributes
);
502 wxTopLevelWindowMac
* toplevel
= wxFindWinFromMacWindow( theWindow
) ;
504 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
505 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
509 case kDragTrackingEnterHandler
:
510 case kDragTrackingLeaveHandler
:
513 case kDragTrackingEnterWindow
:
514 trackingGlobals
->m_currentTargetWindow
= NULL
;
515 trackingGlobals
->m_currentTarget
= NULL
;
518 case kDragTrackingInWindow
:
519 if (toplevel
== NULL
)
522 GetDragMouse(theDrag
, &mouse
, 0L);
524 GlobalToLocal(&localMouse
);
527 wxWindow
*win
= NULL
;
528 ControlPartCode controlPart
;
529 ControlRef control
= wxMacFindControlUnderMouse(
530 toplevel
, localMouse
,
531 theWindow
, &controlPart
) ;
533 win
= wxFindControlFromMacControl( control
) ;
537 int localx
, localy
;
538 localx
= localMouse
.h
;
539 localy
= localMouse
.v
;
542 win
->MacRootWindowToWindow( &localx
, &localy
) ;
543 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
545 if ( trackingGlobals
->m_currentTargetWindow
)
547 // this window is left
548 if ( trackingGlobals
->m_currentTarget
)
550 HideDragHilite( theDrag
);
551 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
552 trackingGlobals
->m_currentTarget
->OnLeave();
553 trackingGlobals
->m_currentTarget
= NULL
;
554 trackingGlobals
->m_currentTargetWindow
= NULL
;
560 // this window is entered
561 trackingGlobals
->m_currentTargetWindow
= win
;
562 trackingGlobals
->m_currentTarget
= win
->GetDropTarget() ;
564 if ( trackingGlobals
->m_currentTarget
)
566 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
567 result
= trackingGlobals
->m_currentTarget
->OnEnter( localx
, localy
, result
) ;
570 if ( result
!= wxDragNone
)
575 win
->MacWindowToRootWindow( &x
, &y
) ;
576 RgnHandle hiliteRgn
= NewRgn() ;
577 Rect r
= { y
, x
, y
+ win
->GetSize().y
, x
+ win
->GetSize().x
} ;
578 RectRgn( hiliteRgn
, &r
) ;
579 ShowDragHilite( theDrag
, hiliteRgn
, true );
580 DisposeRgn( hiliteRgn
) ;
587 if ( trackingGlobals
->m_currentTarget
)
589 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
590 trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
) ;
594 // set cursor for OnEnter and OnDragOver
595 if ( !trackingGlobals
->m_currentSource
&& trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
597 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
603 wxCursor
cursor(wxCURSOR_COPY_ARROW
) ;
604 cursor
.MacInstall() ;
610 wxCursor
cursor(wxCURSOR_ARROW
) ;
611 cursor
.MacInstall() ;
617 wxCursor
cursor(wxCURSOR_NO_ENTRY
) ;
618 cursor
.MacInstall() ;
626 // put these here to make gcc happy
634 case kDragTrackingLeaveWindow
:
635 if (trackingGlobals
->m_currentTarget
)
637 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
638 trackingGlobals
->m_currentTarget
->OnLeave();
639 HideDragHilite( theDrag
);
640 trackingGlobals
->m_currentTarget
= NULL
;
642 trackingGlobals
->m_currentTargetWindow
= NULL
;
652 pascal OSErr
wxMacWindowDragReceiveHandler(
655 DragReference theDrag
)
657 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
658 if ( trackingGlobals
->m_currentTarget
)
660 Point mouse
, localMouse
;
663 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
664 GetDragMouse(theDrag
, &mouse
, 0L);
666 GlobalToLocal(&localMouse
);
667 localx
= localMouse
.h
;
668 localy
= localMouse
.v
;
670 // TODO : should we use client coordinates?
671 if ( trackingGlobals
->m_currentTargetWindow
)
672 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
) ;
673 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
675 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
676 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
677 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
) ;