1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxDropTarget, wxDropSource, wxDataObject implementation
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 // ----------------------------------------------------------------------------
31 void wxMacEnsureTrackingHandlersInstalled() ;
35 wxWindow
* m_currentTargetWindow
;
36 wxDropTarget
* m_currentTarget
;
37 wxDropSource
* m_currentSource
;
38 } MacTrackingGlobals
;
40 MacTrackingGlobals gTrackingGlobals
;
42 //----------------------------------------------------------------------------
44 //----------------------------------------------------------------------------
46 wxDropTarget::wxDropTarget( wxDataObject
*data
)
47 : wxDropTargetBase( data
)
49 wxMacEnsureTrackingHandlersInstalled() ;
52 wxDragResult
wxDropTarget::OnDragOver( wxCoord
WXUNUSED(x
),
57 return CurrentDragHasSupportedFormat() ? def
: wxDragNone
;
60 bool wxDropTarget::OnDrop( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
) )
65 return CurrentDragHasSupportedFormat() ;
68 wxDragResult
wxDropTarget::OnData( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
74 if (!CurrentDragHasSupportedFormat())
77 return GetData() ? def
: wxDragNone
;
80 bool wxDropTarget::CurrentDragHasSupportedFormat()
82 bool supported
= false ;
83 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
85 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject() ;
89 size_t formatcount
= data
->GetFormatCount() ;
90 wxDataFormat
*array
= new wxDataFormat
[ formatcount
];
91 data
->GetAllFormats( array
);
92 for (size_t i
= 0; !supported
&& i
< formatcount
; i
++)
94 wxDataFormat format
= array
[i
] ;
95 if ( m_dataObject
->IsSupported( format
) )
108 CountDragItems((DragReference
)m_currentDrag
, &items
);
109 for (UInt16 index
= 1; index
<= items
&& supported
== false ; ++index
)
111 ItemReference theItem
;
114 GetDragItemReferenceNumber((DragReference
)m_currentDrag
, index
, &theItem
);
115 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
) ;
116 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
118 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
119 if ( m_dataObject
->IsSupportedFormat( wxDataFormat( theType
) ) )
130 bool wxDropTarget::GetData()
135 if ( !CurrentDragHasSupportedFormat() )
138 bool transferred
= false ;
139 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
141 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject() ;
145 size_t formatcount
= data
->GetFormatCount() ;
146 wxDataFormat
*array
= new wxDataFormat
[ formatcount
];
147 data
->GetAllFormats( array
);
148 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
150 wxDataFormat format
= array
[i
] ;
151 if ( m_dataObject
->IsSupported( format
) )
153 int size
= data
->GetDataSize( format
);
158 m_dataObject
->SetData(format
, 0 , 0 ) ;
162 char *d
= new char[size
];
163 data
->GetDataHere( format
, (void*) d
);
164 m_dataObject
->SetData( format
, size
, d
) ;
176 bool firstFileAdded
= false ;
177 CountDragItems((DragReference
)m_currentDrag
, &items
);
178 for (UInt16 index
= 1; index
<= items
; ++index
)
180 ItemReference theItem
;
183 GetDragItemReferenceNumber((DragReference
)m_currentDrag
, index
, &theItem
);
184 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
) ;
185 bool hasPreferredFormat
= false ;
186 wxDataFormat preferredFormat
= m_dataObject
->GetPreferredFormat( wxDataObject::Set
) ;
188 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
190 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
191 wxDataFormat
format(theType
) ;
192 if ( preferredFormat
== format
)
194 hasPreferredFormat
= true ;
199 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
201 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
202 wxDataFormat
format(theType
) ;
203 if ( (hasPreferredFormat
&& format
==preferredFormat
) || (!hasPreferredFormat
&& m_dataObject
->IsSupportedFormat( format
)))
205 FlavorFlags theFlags
;
206 result
= GetFlavorFlags((DragReference
)m_currentDrag
, theItem
, theType
, &theFlags
);
211 GetFlavorDataSize((DragReference
)m_currentDrag
, theItem
, theType
, &dataSize
);
212 if ( theType
== kScrapFlavorTypeText
)
214 // this increment is only valid for allocating, on the next GetFlavorData
215 // call it is reset again to the original value
218 else if ( theType
== kScrapFlavorTypeUnicode
)
220 // this increment is only valid for allocating, on the next GetFlavorData
221 // call it is reset again to the original value
225 theData
= new char[dataSize
];
226 GetFlavorData((DragReference
)m_currentDrag
, theItem
, theType
, (void*) theData
, &dataSize
, 0L);
227 if( theType
== kScrapFlavorTypeText
)
229 theData
[dataSize
]=0 ;
230 m_dataObject
->SetData( wxDataFormat(wxDF_TEXT
), dataSize
, theData
);
233 else if ( theType
== kScrapFlavorTypeUnicode
)
235 theData
[dataSize
]=0 ;
236 theData
[dataSize
+1]=0 ;
237 m_dataObject
->SetData( wxDataFormat(wxDF_UNICODETEXT
), dataSize
, theData
);
240 else if ( theType
== kDragFlavorTypeHFS
)
242 HFSFlavor
* theFile
= (HFSFlavor
*) theData
;
243 wxString name
= wxMacFSSpec2MacFilename( &theFile
->fileSpec
) ;
244 if ( !firstFileAdded
)
247 ((wxFileDataObject
*)m_dataObject
)->SetData( 0 , "" ) ;
248 firstFileAdded
= true ;
250 ((wxFileDataObject
*)m_dataObject
)->AddFile( name
) ;
254 m_dataObject
->SetData( format
, dataSize
, theData
);
266 //-------------------------------------------------------------------------
268 //-------------------------------------------------------------------------
270 //-----------------------------------------------------------------------------
273 wxDropSource::wxDropSource(wxWindow
*win
,
274 const wxCursor
&cursorCopy
,
275 const wxCursor
&cursorMove
,
276 const wxCursor
&cursorStop
)
277 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
279 wxMacEnsureTrackingHandlersInstalled() ;
283 wxDropSource::wxDropSource(wxDataObject
& data
,
285 const wxCursor
&cursorCopy
,
286 const wxCursor
&cursorMove
,
287 const wxCursor
&cursorStop
)
288 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
290 wxMacEnsureTrackingHandlersInstalled() ;
295 wxDropSource::~wxDropSource()
300 wxDragResult
wxDropSource::DoDragDrop(int flags
)
302 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
305 return (wxDragResult
) wxDragNone
;
307 if (m_data
->GetFormatCount() == 0)
308 return (wxDragResult
) wxDragNone
;
311 DragReference theDrag
;
312 RgnHandle dragRegion
;
313 if ((result
= NewDrag(&theDrag
)))
318 size_t formatCount
= m_data
->GetFormatCount() ;
319 wxDataFormat
*formats
= new wxDataFormat
[formatCount
] ;
320 m_data
->GetAllFormats( formats
) ;
321 ItemReference theItem
= 1 ;
322 for ( size_t i
= 0 ; i
< formatCount
; ++i
)
324 size_t dataSize
= m_data
->GetDataSize( formats
[i
] ) ;
325 Ptr dataPtr
= new char[dataSize
] ;
326 m_data
->GetDataHere( formats
[i
] , dataPtr
) ;
327 OSType type
= formats
[i
].GetFormatId() ;
328 if ( type
== 'TEXT' || type
== 'utxt' )
332 dataPtr
[ dataSize
] = 0 ;
333 if ( type
== 'utxt' )
337 dataPtr
[ dataSize
] = 0 ;
339 AddDragItemFlavor(theDrag
, theItem
, type
, dataPtr
, dataSize
, 0);
341 else if (type
== kDragFlavorTypeHFS
)
343 HFSFlavor theFlavor
;
347 wxMacFilename2FSSpec( wxString( dataPtr
, *wxConvCurrent
) , &theFlavor
.fileSpec
) ;
349 cat
.hFileInfo
.ioNamePtr
= theFlavor
.fileSpec
.name
;
350 cat
.hFileInfo
.ioVRefNum
= theFlavor
.fileSpec
.vRefNum
;
351 cat
.hFileInfo
.ioDirID
= theFlavor
.fileSpec
.parID
;
352 cat
.hFileInfo
.ioFDirIndex
= 0;
353 err
= PBGetCatInfoSync(&cat
);
356 theFlavor
.fdFlags
= cat
.hFileInfo
.ioFlFndrInfo
.fdFlags
;
357 if (theFlavor
.fileSpec
.parID
== fsRtParID
) {
358 theFlavor
.fileCreator
= 'MACS';
359 theFlavor
.fileType
= 'disk';
360 } else if ((cat
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0) {
361 theFlavor
.fileCreator
= 'MACS';
362 theFlavor
.fileType
= 'fold';
364 theFlavor
.fileCreator
= cat
.hFileInfo
.ioFlFndrInfo
.fdCreator
;
365 theFlavor
.fileType
= cat
.hFileInfo
.ioFlFndrInfo
.fdType
;
367 AddDragItemFlavor(theDrag
, theItem
, type
, &theFlavor
, sizeof(theFlavor
), 0);
372 AddDragItemFlavor(theDrag
, theItem
, type
, dataPtr
, dataSize
, 0);
378 dragRegion
= NewRgn();
379 RgnHandle tempRgn
= NewRgn() ;
381 EventRecord
* ev
= NULL
;
382 #if !TARGET_CARBON // TODO
383 ev
= (EventRecord
*) wxTheApp
->MacGetCurrentEvent() ;
387 wxMacConvertEventToRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent() , &rec
) ;
389 const short dragRegionOuterBoundary
= 10 ;
390 const short dragRegionInnerBoundary
= 9 ;
392 SetRectRgn( dragRegion
, ev
->where
.h
- dragRegionOuterBoundary
,
393 ev
->where
.v
- dragRegionOuterBoundary
,
394 ev
->where
.h
+ dragRegionOuterBoundary
,
395 ev
->where
.v
+ dragRegionOuterBoundary
) ;
397 SetRectRgn( tempRgn
, ev
->where
.h
- dragRegionInnerBoundary
,
398 ev
->where
.v
- dragRegionInnerBoundary
,
399 ev
->where
.h
+ dragRegionInnerBoundary
,
400 ev
->where
.v
+ dragRegionInnerBoundary
) ;
402 DiffRgn( dragRegion
, tempRgn
, dragRegion
) ;
403 DisposeRgn( tempRgn
) ;
405 // TODO:work with promises in order to return data only when drag
406 // was successfully completed
408 gTrackingGlobals
.m_currentSource
= this ;
409 result
= TrackDrag(theDrag
, ev
, dragRegion
);
410 DisposeRgn(dragRegion
);
411 DisposeDrag(theDrag
);
412 gTrackingGlobals
.m_currentSource
= NULL
;
414 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
415 wxDragResult dndresult
= wxDragCopy
;
416 if ( flags
!= wxDrag_CopyOnly
)
418 // on mac the option key is always the indication for copy
419 dndresult
= optionDown
? wxDragCopy
: wxDragMove
;
424 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
426 const wxCursor
& cursor
= GetCursor(effect
);
429 cursor
.MacInstall() ;
439 bool gTrackingGlobalsInstalled
= false ;
441 // passing the globals via refcon is not needed by the CFM and later architectures anymore
442 // but I'll leave it in there, just in case...
444 pascal OSErr
wxMacWindowDragTrackingHandler(DragTrackingMessage theMessage
, WindowPtr theWindow
,
445 void *handlerRefCon
, DragReference theDrag
) ;
446 pascal OSErr
wxMacWindowDragReceiveHandler(WindowPtr theWindow
, void *handlerRefCon
,
447 DragReference theDrag
) ;
449 void wxMacEnsureTrackingHandlersInstalled()
451 if( !gTrackingGlobalsInstalled
)
455 result
= InstallTrackingHandler(NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L,&gTrackingGlobals
);
456 wxASSERT( result
== noErr
) ;
457 result
= InstallReceiveHandler(NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
458 wxASSERT( result
== noErr
) ;
460 gTrackingGlobalsInstalled
= true ;
464 pascal OSErr
wxMacWindowDragTrackingHandler(DragTrackingMessage theMessage
, WindowPtr theWindow
,
465 void *handlerRefCon
, DragReference theDrag
)
467 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
468 Point mouse
, localMouse
;
469 DragAttributes attributes
;
470 GetDragAttributes(theDrag
, &attributes
);
471 wxTopLevelWindowMac
* toplevel
= wxFindWinFromMacWindow( theWindow
) ;
473 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
474 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
478 case kDragTrackingEnterHandler
:
480 case kDragTrackingLeaveHandler
:
482 case kDragTrackingEnterWindow
:
483 trackingGlobals
->m_currentTargetWindow
= NULL
;
484 trackingGlobals
->m_currentTarget
= NULL
;
486 case kDragTrackingInWindow
:
487 if (toplevel
== NULL
)
490 GetDragMouse(theDrag
, &mouse
, 0L);
492 GlobalToLocal(&localMouse
);
497 wxWindow
*win
= NULL
;
498 ControlPartCode controlPart
;
499 ControlRef control
= wxMacFindControlUnderMouse( toplevel
, localMouse
,
500 theWindow
, &controlPart
) ;
502 win
= wxFindControlFromMacControl( control
) ;
506 int localx
, localy
;
507 localx
= localMouse
.h
;
508 localy
= localMouse
.v
;
511 win
->MacRootWindowToWindow( &localx
, &localy
) ;
512 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
514 if ( trackingGlobals
->m_currentTargetWindow
)
516 // this window is left
517 if ( trackingGlobals
->m_currentTarget
)
519 HideDragHilite(theDrag
);
520 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
521 trackingGlobals
->m_currentTarget
->OnLeave() ;
522 trackingGlobals
->m_currentTarget
= NULL
;
523 trackingGlobals
->m_currentTargetWindow
= NULL
;
528 // this window is entered
529 trackingGlobals
->m_currentTargetWindow
= win
;
530 trackingGlobals
->m_currentTarget
= win
->GetDropTarget() ;
533 if ( trackingGlobals
->m_currentTarget
)
535 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
536 result
= trackingGlobals
->m_currentTarget
->OnEnter(
537 localx
, localy
, result
) ;
541 if ( result
!= wxDragNone
)
545 win
->MacWindowToRootWindow( &x
, &y
) ;
546 RgnHandle hiliteRgn
= NewRgn() ;
547 Rect r
= { y
, x
, y
+win
->GetSize().y
, x
+win
->GetSize().x
} ;
548 RectRgn( hiliteRgn
, &r
) ;
549 ShowDragHilite(theDrag
, hiliteRgn
, true);
550 DisposeRgn( hiliteRgn
) ;
557 if( trackingGlobals
->m_currentTarget
)
559 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
560 trackingGlobals
->m_currentTarget
->OnDragOver(
561 localx
, localy
, result
) ;
565 // set cursor for OnEnter and OnDragOver
566 if ( trackingGlobals
->m_currentSource
&& trackingGlobals
->m_currentSource
->GiveFeedback( result
) == FALSE
)
568 if ( trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) == FALSE
)
574 wxCursor
cursor(wxCURSOR_COPY_ARROW
) ;
575 cursor
.MacInstall() ;
580 wxCursor
cursor(wxCURSOR_ARROW
) ;
581 cursor
.MacInstall() ;
586 wxCursor
cursor(wxCURSOR_NO_ENTRY
) ;
587 cursor
.MacInstall() ;
594 // put these here to make gcc happy
602 case kDragTrackingLeaveWindow
:
603 if (trackingGlobals
->m_currentTarget
)
605 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
606 trackingGlobals
->m_currentTarget
->OnLeave() ;
607 HideDragHilite(theDrag
);
608 trackingGlobals
->m_currentTarget
= NULL
;
610 trackingGlobals
->m_currentTargetWindow
= NULL
;
616 pascal OSErr
wxMacWindowDragReceiveHandler(WindowPtr theWindow
,
618 DragReference theDrag
)
620 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
621 if ( trackingGlobals
->m_currentTarget
)
623 Point mouse
,localMouse
;
626 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
627 GetDragMouse(theDrag
, &mouse
, 0L);
629 GlobalToLocal(&localMouse
);
630 localx
= localMouse
.h
;
631 localy
= localMouse
.v
;
632 //TODO : should we use client coordinates
633 if ( trackingGlobals
->m_currentTargetWindow
)
634 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
) ;
635 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
637 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
638 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
639 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
) ;