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