1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxDropTarget, wxDropSource, wxDataObject implementation
4 // Author: Stefan Csomor
8 // Copyright: (c) 1998 Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "dnd.h"
16 #include "wx/wxprec.h"
18 #if wxUSE_DRAG_AND_DROP
21 #include "wx/window.h"
22 #include "wx/toplevel.h"
24 #include "wx/gdicmn.h"
25 #include "wx/mac/private.h"
31 // ----------------------------------------------------------------------------
33 // ----------------------------------------------------------------------------
35 void wxMacEnsureTrackingHandlersInstalled() ;
39 wxWindow
* m_currentTargetWindow
;
40 wxDropTarget
* m_currentTarget
;
41 wxDropSource
* m_currentSource
;
42 } MacTrackingGlobals
;
44 MacTrackingGlobals gTrackingGlobals
;
46 //----------------------------------------------------------------------------
48 //----------------------------------------------------------------------------
50 wxDropTarget::wxDropTarget( wxDataObject
*data
)
51 : wxDropTargetBase( data
)
53 wxMacEnsureTrackingHandlersInstalled() ;
56 wxDragResult
wxDropTarget::OnDragOver( wxCoord
WXUNUSED(x
),
61 return CurrentDragHasSupportedFormat() ? def
: wxDragNone
;
64 bool wxDropTarget::OnDrop( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
) )
69 return CurrentDragHasSupportedFormat() ;
72 wxDragResult
wxDropTarget::OnData( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
78 if (!CurrentDragHasSupportedFormat())
81 return GetData() ? def
: wxDragNone
;
84 bool wxDropTarget::CurrentDragHasSupportedFormat()
86 bool supported
= false ;
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 CountDragItems((DragReference
)m_currentDrag
, &items
);
113 for (UInt16 index
= 1; index
<= items
&& supported
== false ; ++index
)
115 ItemReference theItem
;
118 GetDragItemReferenceNumber((DragReference
)m_currentDrag
, index
, &theItem
);
119 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
) ;
120 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
122 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
123 if ( m_dataObject
->IsSupportedFormat( wxDataFormat( theType
) ) )
134 bool wxDropTarget::GetData()
139 if ( !CurrentDragHasSupportedFormat() )
142 bool transferred
= false ;
143 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
145 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject() ;
149 size_t formatcount
= data
->GetFormatCount() ;
150 wxDataFormat
*array
= new wxDataFormat
[ formatcount
];
151 data
->GetAllFormats( array
);
152 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
154 wxDataFormat format
= array
[i
] ;
155 if ( m_dataObject
->IsSupported( format
) )
157 int size
= data
->GetDataSize( format
);
162 m_dataObject
->SetData(format
, 0 , 0 ) ;
166 char *d
= new char[size
];
167 data
->GetDataHere( format
, (void*) d
);
168 m_dataObject
->SetData( format
, size
, d
) ;
180 bool firstFileAdded
= false ;
181 CountDragItems((DragReference
)m_currentDrag
, &items
);
182 for (UInt16 index
= 1; index
<= items
; ++index
)
184 ItemReference theItem
;
187 GetDragItemReferenceNumber((DragReference
)m_currentDrag
, index
, &theItem
);
188 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
) ;
189 bool hasPreferredFormat
= false ;
190 wxDataFormat preferredFormat
= m_dataObject
->GetPreferredFormat( wxDataObject::Set
) ;
192 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
194 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
195 wxDataFormat
format(theType
) ;
196 if ( preferredFormat
== format
)
198 hasPreferredFormat
= true ;
203 for ( UInt16 flavor
= 1 ; flavor
<= flavors
; ++flavor
)
205 result
= GetFlavorType((DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
206 wxDataFormat
format(theType
) ;
207 if ( (hasPreferredFormat
&& format
==preferredFormat
) || (!hasPreferredFormat
&& m_dataObject
->IsSupportedFormat( format
)))
209 FlavorFlags theFlags
;
210 result
= GetFlavorFlags((DragReference
)m_currentDrag
, theItem
, theType
, &theFlags
);
215 GetFlavorDataSize((DragReference
)m_currentDrag
, theItem
, theType
, &dataSize
);
216 if ( theType
== kScrapFlavorTypeText
)
218 // this increment is only valid for allocating, on the next GetFlavorData
219 // call it is reset again to the original value
222 else if ( theType
== kScrapFlavorTypeUnicode
)
224 // this increment is only valid for allocating, on the next GetFlavorData
225 // call it is reset again to the original value
229 theData
= new char[dataSize
];
230 GetFlavorData((DragReference
)m_currentDrag
, theItem
, theType
, (void*) theData
, &dataSize
, 0L);
231 if( theType
== kScrapFlavorTypeText
)
233 theData
[dataSize
]=0 ;
234 m_dataObject
->SetData( wxDataFormat(wxDF_TEXT
), dataSize
, theData
);
237 else if ( theType
== kScrapFlavorTypeUnicode
)
239 theData
[dataSize
]=0 ;
240 theData
[dataSize
+1]=0 ;
241 m_dataObject
->SetData( wxDataFormat(wxDF_UNICODETEXT
), dataSize
, theData
);
244 else if ( theType
== kDragFlavorTypeHFS
)
246 HFSFlavor
* theFile
= (HFSFlavor
*) theData
;
247 wxString name
= wxMacFSSpec2MacFilename( &theFile
->fileSpec
) ;
248 if ( !firstFileAdded
)
251 ((wxFileDataObject
*)m_dataObject
)->SetData( 0 , "" ) ;
252 firstFileAdded
= true ;
254 ((wxFileDataObject
*)m_dataObject
)->AddFile( name
) ;
258 m_dataObject
->SetData( format
, dataSize
, theData
);
270 //-------------------------------------------------------------------------
272 //-------------------------------------------------------------------------
274 //-----------------------------------------------------------------------------
277 wxDropSource::wxDropSource(wxWindow
*win
,
278 const wxCursor
&cursorCopy
,
279 const wxCursor
&cursorMove
,
280 const wxCursor
&cursorStop
)
281 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
283 wxMacEnsureTrackingHandlersInstalled() ;
287 wxDropSource::wxDropSource(wxDataObject
& data
,
289 const wxCursor
&cursorCopy
,
290 const wxCursor
&cursorMove
,
291 const wxCursor
&cursorStop
)
292 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
294 wxMacEnsureTrackingHandlersInstalled() ;
299 wxDropSource::~wxDropSource()
304 wxDragResult
wxDropSource::DoDragDrop(int flags
)
306 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
309 return (wxDragResult
) wxDragNone
;
311 if (m_data
->GetFormatCount() == 0)
312 return (wxDragResult
) wxDragNone
;
315 DragReference theDrag
;
316 RgnHandle dragRegion
;
317 if ((result
= NewDrag(&theDrag
)))
322 size_t formatCount
= m_data
->GetFormatCount() ;
323 wxDataFormat
*formats
= new wxDataFormat
[formatCount
] ;
324 m_data
->GetAllFormats( formats
) ;
325 ItemReference theItem
= 1 ;
326 for ( size_t i
= 0 ; i
< formatCount
; ++i
)
328 size_t dataSize
= m_data
->GetDataSize( formats
[i
] ) ;
329 Ptr dataPtr
= new char[dataSize
] ;
330 m_data
->GetDataHere( formats
[i
] , dataPtr
) ;
331 OSType type
= formats
[i
].GetFormatId() ;
332 if ( type
== 'TEXT' || type
== 'utxt' )
336 dataPtr
[ dataSize
] = 0 ;
337 if ( type
== 'utxt' )
341 dataPtr
[ dataSize
] = 0 ;
343 AddDragItemFlavor(theDrag
, theItem
, type
, dataPtr
, dataSize
, 0);
345 else if (type
== kDragFlavorTypeHFS
)
347 HFSFlavor theFlavor
;
351 wxMacFilename2FSSpec( wxString( dataPtr
, *wxConvCurrent
) , &theFlavor
.fileSpec
) ;
353 cat
.hFileInfo
.ioNamePtr
= theFlavor
.fileSpec
.name
;
354 cat
.hFileInfo
.ioVRefNum
= theFlavor
.fileSpec
.vRefNum
;
355 cat
.hFileInfo
.ioDirID
= theFlavor
.fileSpec
.parID
;
356 cat
.hFileInfo
.ioFDirIndex
= 0;
357 err
= PBGetCatInfoSync(&cat
);
360 theFlavor
.fdFlags
= cat
.hFileInfo
.ioFlFndrInfo
.fdFlags
;
361 if (theFlavor
.fileSpec
.parID
== fsRtParID
) {
362 theFlavor
.fileCreator
= 'MACS';
363 theFlavor
.fileType
= 'disk';
364 } else if ((cat
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0) {
365 theFlavor
.fileCreator
= 'MACS';
366 theFlavor
.fileType
= 'fold';
368 theFlavor
.fileCreator
= cat
.hFileInfo
.ioFlFndrInfo
.fdCreator
;
369 theFlavor
.fileType
= cat
.hFileInfo
.ioFlFndrInfo
.fdType
;
371 AddDragItemFlavor(theDrag
, theItem
, type
, &theFlavor
, sizeof(theFlavor
), 0);
376 AddDragItemFlavor(theDrag
, theItem
, type
, dataPtr
, dataSize
, 0);
382 dragRegion
= NewRgn();
383 RgnHandle tempRgn
= NewRgn() ;
385 EventRecord
* ev
= NULL
;
386 #if !TARGET_CARBON // TODO
387 ev
= (EventRecord
*) wxTheApp
->MacGetCurrentEvent() ;
391 wxMacConvertEventToRecord( (EventRef
) wxTheApp
->MacGetCurrentEvent() , &rec
) ;
393 const short dragRegionOuterBoundary
= 10 ;
394 const short dragRegionInnerBoundary
= 9 ;
396 SetRectRgn( dragRegion
, ev
->where
.h
- dragRegionOuterBoundary
,
397 ev
->where
.v
- dragRegionOuterBoundary
,
398 ev
->where
.h
+ dragRegionOuterBoundary
,
399 ev
->where
.v
+ dragRegionOuterBoundary
) ;
401 SetRectRgn( tempRgn
, ev
->where
.h
- dragRegionInnerBoundary
,
402 ev
->where
.v
- dragRegionInnerBoundary
,
403 ev
->where
.h
+ dragRegionInnerBoundary
,
404 ev
->where
.v
+ dragRegionInnerBoundary
) ;
406 DiffRgn( dragRegion
, tempRgn
, dragRegion
) ;
407 DisposeRgn( tempRgn
) ;
409 // TODO:work with promises in order to return data only when drag
410 // was successfully completed
412 gTrackingGlobals
.m_currentSource
= this ;
413 result
= TrackDrag(theDrag
, ev
, dragRegion
);
414 DisposeRgn(dragRegion
);
415 DisposeDrag(theDrag
);
416 gTrackingGlobals
.m_currentSource
= NULL
;
418 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
419 wxDragResult dndresult
= wxDragCopy
;
420 if ( flags
!= wxDrag_CopyOnly
)
422 // on mac the option key is always the indication for copy
423 dndresult
= optionDown
? wxDragCopy
: wxDragMove
;
428 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
430 const wxCursor
& cursor
= GetCursor(effect
);
433 cursor
.MacInstall() ;
443 bool gTrackingGlobalsInstalled
= false ;
445 // passing the globals via refcon is not needed by the CFM and later architectures anymore
446 // but I'll leave it in there, just in case...
448 pascal OSErr
wxMacWindowDragTrackingHandler(DragTrackingMessage theMessage
, WindowPtr theWindow
,
449 void *handlerRefCon
, DragReference theDrag
) ;
450 pascal OSErr
wxMacWindowDragReceiveHandler(WindowPtr theWindow
, void *handlerRefCon
,
451 DragReference theDrag
) ;
453 void wxMacEnsureTrackingHandlersInstalled()
455 if( !gTrackingGlobalsInstalled
)
459 result
= InstallTrackingHandler(NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L,&gTrackingGlobals
);
460 wxASSERT( result
== noErr
) ;
461 result
= InstallReceiveHandler(NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
462 wxASSERT( result
== noErr
) ;
464 gTrackingGlobalsInstalled
= true ;
468 pascal OSErr
wxMacWindowDragTrackingHandler(DragTrackingMessage theMessage
, WindowPtr theWindow
,
469 void *handlerRefCon
, DragReference theDrag
)
471 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
472 Point mouse
, localMouse
;
473 DragAttributes attributes
;
474 GetDragAttributes(theDrag
, &attributes
);
475 wxTopLevelWindowMac
* toplevel
= wxFindWinFromMacWindow( theWindow
) ;
477 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
478 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
482 case kDragTrackingEnterHandler
:
484 case kDragTrackingLeaveHandler
:
486 case kDragTrackingEnterWindow
:
487 trackingGlobals
->m_currentTargetWindow
= NULL
;
488 trackingGlobals
->m_currentTarget
= NULL
;
490 case kDragTrackingInWindow
:
491 if (toplevel
== NULL
)
494 GetDragMouse(theDrag
, &mouse
, 0L);
496 GlobalToLocal(&localMouse
);
501 wxWindow
*win
= NULL
;
502 ControlPartCode controlPart
;
503 ControlRef control
= wxMacFindControlUnderMouse( toplevel
, localMouse
,
504 theWindow
, &controlPart
) ;
506 win
= wxFindControlFromMacControl( control
) ;
510 int localx
, localy
;
511 localx
= localMouse
.h
;
512 localy
= localMouse
.v
;
515 win
->MacRootWindowToWindow( &localx
, &localy
) ;
516 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
518 if ( trackingGlobals
->m_currentTargetWindow
)
520 // this window is left
521 if ( trackingGlobals
->m_currentTarget
)
523 HideDragHilite(theDrag
);
524 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
525 trackingGlobals
->m_currentTarget
->OnLeave() ;
526 trackingGlobals
->m_currentTarget
= NULL
;
527 trackingGlobals
->m_currentTargetWindow
= NULL
;
532 // this window is entered
533 trackingGlobals
->m_currentTargetWindow
= win
;
534 trackingGlobals
->m_currentTarget
= win
->GetDropTarget() ;
537 if ( trackingGlobals
->m_currentTarget
)
539 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
540 result
= trackingGlobals
->m_currentTarget
->OnEnter(
541 localx
, localy
, result
) ;
545 if ( result
!= wxDragNone
)
549 win
->MacWindowToRootWindow( &x
, &y
) ;
550 RgnHandle hiliteRgn
= NewRgn() ;
551 Rect r
= { y
, x
, y
+win
->GetSize().y
, x
+win
->GetSize().x
} ;
552 RectRgn( hiliteRgn
, &r
) ;
553 ShowDragHilite(theDrag
, hiliteRgn
, true);
554 DisposeRgn( hiliteRgn
) ;
561 if( trackingGlobals
->m_currentTarget
)
563 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
564 trackingGlobals
->m_currentTarget
->OnDragOver(
565 localx
, localy
, result
) ;
569 // set cursor for OnEnter and OnDragOver
570 if ( trackingGlobals
->m_currentSource
&& trackingGlobals
->m_currentSource
->GiveFeedback( result
) == FALSE
)
572 if ( trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) == FALSE
)
578 wxCursor
cursor(wxCURSOR_COPY_ARROW
) ;
579 cursor
.MacInstall() ;
584 wxCursor
cursor(wxCURSOR_ARROW
) ;
585 cursor
.MacInstall() ;
590 wxCursor
cursor(wxCURSOR_NO_ENTRY
) ;
591 cursor
.MacInstall() ;
598 // put these here to make gcc happy
606 case kDragTrackingLeaveWindow
:
607 if (trackingGlobals
->m_currentTarget
)
609 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
610 trackingGlobals
->m_currentTarget
->OnLeave() ;
611 HideDragHilite(theDrag
);
612 trackingGlobals
->m_currentTarget
= NULL
;
614 trackingGlobals
->m_currentTargetWindow
= NULL
;
620 pascal OSErr
wxMacWindowDragReceiveHandler(WindowPtr theWindow
,
622 DragReference theDrag
)
624 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
625 if ( trackingGlobals
->m_currentTarget
)
627 Point mouse
,localMouse
;
630 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
) ;
631 GetDragMouse(theDrag
, &mouse
, 0L);
633 GlobalToLocal(&localMouse
);
634 localx
= localMouse
.h
;
635 localy
= localMouse
.v
;
636 //TODO : should we use client coordinates
637 if ( trackingGlobals
->m_currentTargetWindow
)
638 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
) ;
639 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
641 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
642 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
643 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
) ;