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
;
40 wxDragResult m_result
;
44 MacTrackingGlobals gTrackingGlobals
;
46 void wxMacEnsureTrackingHandlersInstalled();
48 //----------------------------------------------------------------------------
50 //----------------------------------------------------------------------------
52 wxDropTarget::wxDropTarget( wxDataObject
*data
)
53 : wxDropTargetBase( data
)
55 wxMacEnsureTrackingHandlersInstalled();
58 wxDragResult
wxDropTarget::OnDragOver(
59 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
62 return CurrentDragHasSupportedFormat() ? def
: wxDragNone
;
65 bool wxDropTarget::OnDrop( wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
) )
67 if (m_dataObject
== NULL
)
70 return CurrentDragHasSupportedFormat();
73 wxDragResult
wxDropTarget::OnData(
74 wxCoord
WXUNUSED(x
), wxCoord
WXUNUSED(y
),
77 if (m_dataObject
== NULL
)
80 if (!CurrentDragHasSupportedFormat())
83 return GetData() ? def
: wxDragNone
;
86 bool wxDropTarget::CurrentDragHasSupportedFormat()
88 bool supported
= false;
90 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
92 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
96 size_t formatcount
= data
->GetFormatCount();
97 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
98 data
->GetAllFormats( array
);
99 for (size_t i
= 0; !supported
&& i
< formatcount
; i
++)
101 wxDataFormat format
= array
[i
];
102 if ( m_dataObject
->IsSupported( format
) )
116 ItemReference theItem
;
120 CountDragItems( (DragReference
)m_currentDrag
, &items
);
121 for (UInt16 index
= 1; index
<= items
&& !supported
; ++index
)
124 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
125 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
127 for ( UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
129 GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
130 if ( m_dataObject
->IsSupportedFormat( wxDataFormat( theType
) ) )
142 bool wxDropTarget::GetData()
144 if (m_dataObject
== NULL
)
147 if ( !CurrentDragHasSupportedFormat() )
150 bool transferred
= false;
151 if ( gTrackingGlobals
.m_currentSource
!= NULL
)
153 wxDataObject
* data
= gTrackingGlobals
.m_currentSource
->GetDataObject();
157 size_t formatcount
= data
->GetFormatCount();
158 wxDataFormat
*array
= new wxDataFormat
[formatcount
];
159 data
->GetAllFormats( array
);
160 for (size_t i
= 0; !transferred
&& i
< formatcount
; i
++)
162 wxDataFormat format
= array
[i
];
163 if ( m_dataObject
->IsSupported( format
) )
165 int size
= data
->GetDataSize( format
);
170 m_dataObject
->SetData( format
, 0, 0 );
174 char *d
= new char[size
];
175 data
->GetDataHere( format
, (void*)d
);
176 m_dataObject
->SetData( format
, size
, d
);
190 ItemReference theItem
;
192 FlavorFlags theFlags
;
194 wxString filenamesPassed
;
196 CountDragItems( (DragReference
)m_currentDrag
, &items
);
197 for (UInt16 index
= 1; index
<= items
; ++index
)
200 GetDragItemReferenceNumber( (DragReference
)m_currentDrag
, index
, &theItem
);
201 CountDragItemFlavors( (DragReference
)m_currentDrag
, theItem
, &flavors
);
202 wxDataFormat preferredFormat
= m_dataObject
->GetPreferredFormat( wxDataObject::Set
);
203 bool hasPreferredFormat
= false;
205 for (UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
207 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
208 wxDataFormat
format( theType
);
209 if (preferredFormat
== format
)
211 hasPreferredFormat
= true;
216 for (UInt16 flavor
= 1; flavor
<= flavors
; ++flavor
)
218 result
= GetFlavorType( (DragReference
)m_currentDrag
, theItem
, flavor
, &theType
);
219 wxDataFormat
format( theType
);
220 if ((hasPreferredFormat
&& format
== preferredFormat
)
221 || (!hasPreferredFormat
&& m_dataObject
->IsSupportedFormat( format
)))
223 result
= GetFlavorFlags( (DragReference
)m_currentDrag
, theItem
, theType
, &theFlags
);
229 GetFlavorDataSize( (DragReference
)m_currentDrag
, theItem
, theType
, &dataSize
);
230 if (theType
== kScrapFlavorTypeText
)
232 // this increment is only valid for allocating:
233 // on the next GetFlavorData call it is reset again to the original value
236 else if (theType
== kScrapFlavorTypeUnicode
)
238 // this increment is only valid for allocating:
239 // on the next GetFlavorData call it is reset again to the original value
245 theData
= new char[dataSize
];
249 GetFlavorData( (DragReference
)m_currentDrag
, theItem
, theType
, (void*)theData
, &dataSize
, 0L );
252 case kScrapFlavorTypeText
:
253 theData
[dataSize
] = 0;
254 m_dataObject
->SetData( wxDataFormat(wxDF_TEXT
), dataSize
, theData
);
258 case kScrapFlavorTypeUnicode
:
259 theData
[dataSize
+ 0] =
260 theData
[dataSize
+ 1] = 0;
261 m_dataObject
->SetData( wxDataFormat(wxDF_UNICODETEXT
), dataSize
, theData
);
265 case kDragFlavorTypeHFS
:
268 HFSFlavor
* theFile
= (HFSFlavor
*)theData
;
270 wxString name
= wxMacFSSpec2MacFilename( &theFile
->fileSpec
);
273 filenamesPassed
+= name
+ wxT("\n");
279 m_dataObject
->SetData( format
, dataSize
, theData
);
290 if (filenamesPassed
.length() > 0)
292 wxCharBuffer buf
= filenamesPassed
.fn_str();
293 m_dataObject
->SetData( wxDataFormat(wxDF_FILENAME
), strlen( buf
), (const char*)buf
);
300 //-------------------------------------------------------------------------
302 //-------------------------------------------------------------------------
304 //-----------------------------------------------------------------------------
307 wxDropSource::wxDropSource(wxWindow
*win
,
308 const wxCursor
&cursorCopy
,
309 const wxCursor
&cursorMove
,
310 const wxCursor
&cursorStop
)
311 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
313 wxMacEnsureTrackingHandlersInstalled();
318 wxDropSource::wxDropSource(wxDataObject
& data
,
320 const wxCursor
&cursorCopy
,
321 const wxCursor
&cursorMove
,
322 const wxCursor
&cursorStop
)
323 : wxDropSourceBase(cursorCopy
, cursorMove
, cursorStop
)
325 wxMacEnsureTrackingHandlersInstalled();
331 wxDropSource::~wxDropSource()
335 wxDragResult
wxDropSource::DoDragDrop(int flags
)
337 wxASSERT_MSG( m_data
, wxT("Drop source: no data") );
339 if ((m_data
== NULL
) || (m_data
->GetFormatCount() == 0))
340 return (wxDragResult
)wxDragNone
;
342 DragReference theDrag
;
343 RgnHandle dragRegion
;
345 if (NewDrag( &theDrag
) != noErr
)
349 size_t formatCount
= m_data
->GetFormatCount();
350 wxDataFormat
*formats
= new wxDataFormat
[formatCount
];
351 m_data
->GetAllFormats( formats
);
352 ItemReference theItem
= (ItemReference
) 1;
354 for ( size_t i
= 0; i
< formatCount
; ++i
)
356 size_t dataSize
= m_data
->GetDataSize( formats
[i
] );
357 Ptr dataPtr
= new char[dataSize
];
358 m_data
->GetDataHere( formats
[i
], dataPtr
);
359 OSType type
= formats
[i
].GetFormatId();
360 if ( type
== 'TEXT' || type
== 'utxt' )
364 dataPtr
[ dataSize
] = 0;
365 if ( type
== 'utxt' )
369 dataPtr
[ dataSize
] = 0;
372 AddDragItemFlavor( theDrag
, theItem
, type
, dataPtr
, dataSize
, 0 );
374 else if (type
== kDragFlavorTypeHFS
)
381 wxMacFilename2FSSpec( wxString( dataPtr
, *wxConvCurrent
), &theFlavor
.fileSpec
);
383 memset( &cat
, 0, sizeof(cat
) );
384 cat
.hFileInfo
.ioNamePtr
= theFlavor
.fileSpec
.name
;
385 cat
.hFileInfo
.ioVRefNum
= theFlavor
.fileSpec
.vRefNum
;
386 cat
.hFileInfo
.ioDirID
= theFlavor
.fileSpec
.parID
;
387 cat
.hFileInfo
.ioFDirIndex
= 0;
388 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 gTrackingGlobals
.m_result
= wxDragNone
;
461 gTrackingGlobals
.m_flags
= flags
;
463 TrackDrag( theDrag
, ev
, dragRegion
);
464 DisposeRgn( dragRegion
);
465 DisposeDrag( theDrag
);
466 gTrackingGlobals
.m_currentSource
= NULL
;
468 return gTrackingGlobals
.m_result
;
471 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect
)
473 const wxCursor
& cursor
= GetCursor(effect
);
474 bool result
= cursor
.Ok();
482 bool gTrackingGlobalsInstalled
= false;
484 // passing the globals via refcon is not needed by the CFM and later architectures anymore
485 // but I'll leave it in there, just in case...
487 pascal OSErr
wxMacWindowDragTrackingHandler(
488 DragTrackingMessage theMessage
, WindowPtr theWindow
,
489 void *handlerRefCon
, DragReference theDrag
);
490 pascal OSErr
wxMacWindowDragReceiveHandler(
491 WindowPtr theWindow
, void *handlerRefCon
,
492 DragReference theDrag
);
494 void wxMacEnsureTrackingHandlersInstalled()
496 if ( !gTrackingGlobalsInstalled
)
500 err
= InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler
), 0L, &gTrackingGlobals
);
503 err
= InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler
), 0L, &gTrackingGlobals
);
506 gTrackingGlobalsInstalled
= true;
510 pascal OSErr
wxMacWindowDragTrackingHandler(
511 DragTrackingMessage theMessage
, WindowPtr theWindow
,
512 void *handlerRefCon
, DragReference theDrag
)
514 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*) handlerRefCon
;
516 Point mouse
, localMouse
;
517 DragAttributes attributes
;
519 GetDragAttributes( theDrag
, &attributes
);
521 wxTopLevelWindowMac
* toplevel
= wxFindWinFromMacWindow( theWindow
);
523 bool optionDown
= GetCurrentKeyModifiers() & optionKey
;
524 wxDragResult result
= optionDown
? wxDragCopy
: wxDragMove
;
528 case kDragTrackingEnterHandler
:
529 case kDragTrackingLeaveHandler
:
532 case kDragTrackingEnterWindow
:
533 if (trackingGlobals
!= NULL
)
535 trackingGlobals
->m_currentTargetWindow
= NULL
;
536 trackingGlobals
->m_currentTarget
= NULL
;
540 case kDragTrackingInWindow
:
541 if (trackingGlobals
== NULL
)
543 if (toplevel
== NULL
)
546 GetDragMouse( theDrag
, &mouse
, 0L );
548 wxMacGlobalToLocal( theWindow
, &localMouse
);
551 wxWindow
*win
= NULL
;
552 ControlPartCode controlPart
;
553 ControlRef control
= wxMacFindControlUnderMouse(
554 toplevel
, localMouse
, theWindow
, &controlPart
);
556 win
= wxFindControlFromMacControl( control
);
561 localx
= localMouse
.h
;
562 localy
= localMouse
.v
;
565 win
->MacRootWindowToWindow( &localx
, &localy
);
566 if ( win
!= trackingGlobals
->m_currentTargetWindow
)
568 if ( trackingGlobals
->m_currentTargetWindow
)
570 // this window is left
571 if ( trackingGlobals
->m_currentTarget
)
574 HideDragHilite( theDrag
);
576 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
577 trackingGlobals
->m_currentTarget
->OnLeave();
578 trackingGlobals
->m_currentTarget
= NULL
;
579 trackingGlobals
->m_currentTargetWindow
= NULL
;
585 // this window is entered
586 trackingGlobals
->m_currentTargetWindow
= win
;
587 trackingGlobals
->m_currentTarget
= win
->GetDropTarget();
589 if ( trackingGlobals
->m_currentTarget
)
591 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
592 result
= trackingGlobals
->m_currentTarget
->OnEnter( localx
, localy
, result
);
595 if ( result
!= wxDragNone
)
600 win
->MacWindowToRootWindow( &x
, &y
);
601 RgnHandle hiliteRgn
= NewRgn();
602 Rect r
= { y
, x
, y
+ win
->GetSize().y
, x
+ win
->GetSize().x
};
603 RectRgn( hiliteRgn
, &r
);
605 ShowDragHilite( theDrag
, hiliteRgn
, true );
607 DisposeRgn( hiliteRgn
);
614 if ( trackingGlobals
->m_currentTarget
)
616 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
617 trackingGlobals
->m_currentTarget
->OnDragOver( localx
, localy
, result
);
621 // set cursor for OnEnter and OnDragOver
622 if ( trackingGlobals
->m_currentSource
&& !trackingGlobals
->m_currentSource
->GiveFeedback( result
) )
624 if ( !trackingGlobals
->m_currentSource
->MacInstallDefaultCursor( result
) )
626 int cursorID
= wxCURSOR_NONE
;
631 cursorID
= wxCURSOR_COPY_ARROW
;
635 cursorID
= wxCURSOR_ARROW
;
639 cursorID
= wxCURSOR_NO_ENTRY
;
646 // put these here to make gcc happy
650 if (cursorID
!= wxCURSOR_NONE
)
652 wxCursor
cursor( cursorID
);
660 case kDragTrackingLeaveWindow
:
661 if (trackingGlobals
== NULL
)
664 if (trackingGlobals
->m_currentTarget
)
666 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
667 trackingGlobals
->m_currentTarget
->OnLeave();
669 HideDragHilite( theDrag
);
671 trackingGlobals
->m_currentTarget
= NULL
;
673 trackingGlobals
->m_currentTargetWindow
= NULL
;
683 pascal OSErr
wxMacWindowDragReceiveHandler(
686 DragReference theDrag
)
688 MacTrackingGlobals
* trackingGlobals
= (MacTrackingGlobals
*)handlerRefCon
;
689 if ( trackingGlobals
->m_currentTarget
)
691 Point mouse
, localMouse
;
694 trackingGlobals
->m_currentTarget
->SetCurrentDrag( theDrag
);
695 GetDragMouse( theDrag
, &mouse
, 0L );
697 wxMacGlobalToLocal( theWindow
, &localMouse
);
698 localx
= localMouse
.h
;
699 localy
= localMouse
.v
;
701 // TODO : should we use client coordinates?
702 if ( trackingGlobals
->m_currentTargetWindow
)
703 trackingGlobals
->m_currentTargetWindow
->MacRootWindowToWindow( &localx
, &localy
);
704 if ( trackingGlobals
->m_currentTarget
->OnDrop( localx
, localy
) )
706 // the option key indicates copy in Mac UI, if it's not pressed do
707 // move by default if it's allowed at all
709 result
= !(trackingGlobals
->m_flags
& wxDrag_AllowMove
) ||
710 (GetCurrentKeyModifiers() & optionKey
)
713 trackingGlobals
->m_result
=
714 trackingGlobals
->m_currentTarget
->OnData( localx
, localy
, result
);
721 #endif // wxUSE_DRAG_AND_DROP