]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/dnd.cpp
7cd8ecc62cbb46ecbd445ef77d9ec225b530c0fe
[wxWidgets.git] / src / mac / carbon / dnd.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: dnd.cpp
3 // Purpose: wxDropTarget, wxDropSource implementations
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Stefan Csomor
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_DRAG_AND_DROP
15
16 #include "wx/dnd.h"
17 #include "wx/window.h"
18 #include "wx/toplevel.h"
19 #include "wx/app.h"
20 #include "wx/gdicmn.h"
21 #include "wx/mac/private.h"
22
23 #ifndef __DARWIN__
24 #include <Scrap.h>
25 #endif
26
27 // ----------------------------------------------------------------------------
28 // globals
29 // ----------------------------------------------------------------------------
30
31 typedef struct
32 {
33 wxWindow* m_currentTargetWindow ;
34 wxDropTarget* m_currentTarget ;
35 wxDropSource* m_currentSource ;
36 }
37 MacTrackingGlobals ;
38
39 MacTrackingGlobals gTrackingGlobals ;
40
41 void wxMacEnsureTrackingHandlersInstalled() ;
42
43 //----------------------------------------------------------------------------
44 // wxDropTarget
45 //----------------------------------------------------------------------------
46
47 wxDropTarget::wxDropTarget( wxDataObject *data )
48 : wxDropTargetBase( data )
49 {
50 wxMacEnsureTrackingHandlersInstalled() ;
51 }
52
53 wxDragResult wxDropTarget::OnDragOver( wxCoord WXUNUSED(x),
54 wxCoord WXUNUSED(y),
55 wxDragResult def )
56 {
57 return CurrentDragHasSupportedFormat() ? def : wxDragNone;
58 }
59
60 bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
61 {
62 if (m_dataObject == NULL)
63 return false;
64
65 return CurrentDragHasSupportedFormat() ;
66 }
67
68 wxDragResult wxDropTarget::OnData( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
69 wxDragResult def )
70 {
71 if (m_dataObject == NULL)
72 return wxDragNone;
73
74 if (!CurrentDragHasSupportedFormat())
75 return wxDragNone;
76
77 return GetData() ? def : wxDragNone;
78 }
79
80 bool wxDropTarget::CurrentDragHasSupportedFormat()
81 {
82 bool supported = false ;
83
84 if ( gTrackingGlobals.m_currentSource != NULL )
85 {
86 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject() ;
87
88 if ( data )
89 {
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++)
94 {
95 wxDataFormat format = array[i] ;
96 if ( m_dataObject->IsSupported( format ) )
97 {
98 supported = true ;
99 break ;
100 }
101 }
102
103 delete [] array ;
104 }
105 }
106
107 if ( !supported )
108 {
109 UInt16 items ;
110 OSErr result;
111 ItemReference theItem;
112 FlavorType theType ;
113 UInt16 flavors = 0 ;
114
115 CountDragItems((DragReference)m_currentDrag, &items);
116 for (UInt16 index = 1; index <= items && !supported; ++index)
117 {
118 flavors = 0 ;
119 GetDragItemReferenceNumber( (DragReference)m_currentDrag, index, &theItem );
120 CountDragItemFlavors( (DragReference)m_currentDrag, theItem, &flavors );
121
122 for ( UInt16 flavor = 1 ; flavor <= flavors ; ++flavor )
123 {
124 result = GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
125 if ( m_dataObject->IsSupportedFormat( wxDataFormat( theType ) ) )
126 {
127 supported = true ;
128 break ;
129 }
130 }
131 }
132 }
133
134 return supported ;
135 }
136
137 bool wxDropTarget::GetData()
138 {
139 if (!m_dataObject)
140 return false;
141
142 if ( !CurrentDragHasSupportedFormat() )
143 return false ;
144
145 bool transferred = false ;
146 if ( gTrackingGlobals.m_currentSource != NULL )
147 {
148 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject() ;
149
150 if ( data )
151 {
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++)
156 {
157 wxDataFormat format = array[i] ;
158 if ( m_dataObject->IsSupported( format ) )
159 {
160 int size = data->GetDataSize( format );
161 transferred = true ;
162
163 if (size == 0)
164 {
165 m_dataObject->SetData( format , 0 , 0 );
166 }
167 else
168 {
169 char *d = new char[size];
170 data->GetDataHere( format , (void*) d );
171 m_dataObject->SetData( format , size , d ) ;
172 delete [] d ;
173 }
174 }
175 }
176
177 delete [] array ;
178 }
179 }
180
181 if ( !transferred )
182 {
183 UInt16 items ;
184 OSErr result;
185 ItemReference theItem;
186 FlavorType theType ;
187 FlavorFlags theFlags;
188 UInt16 flavors ;
189 bool firstFileAdded = false ;
190
191 CountDragItems( (DragReference)m_currentDrag, &items );
192 for (UInt16 index = 1; index <= items; ++index)
193 {
194 flavors = 0 ;
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 ) ;
199
200 for ( UInt16 flavor = 1 ; flavor <= flavors ; ++flavor )
201 {
202 result = GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
203 wxDataFormat format( theType );
204 if ( preferredFormat == format )
205 {
206 hasPreferredFormat = true ;
207 break ;
208 }
209 }
210
211 for ( UInt16 flavor = 1 ; flavor <= flavors ; ++flavor )
212 {
213 result = GetFlavorType((DragReference)m_currentDrag, theItem, flavor , &theType);
214 wxDataFormat format(theType) ;
215 if ( (hasPreferredFormat && format == preferredFormat)
216 || (!hasPreferredFormat && m_dataObject->IsSupportedFormat( format )))
217 {
218 result = GetFlavorFlags( (DragReference)m_currentDrag, theItem, theType, &theFlags );
219 if (result == noErr)
220 {
221 Size dataSize ;
222 Ptr theData ;
223
224 GetFlavorDataSize( (DragReference)m_currentDrag, theItem, theType, &dataSize );
225 if ( theType == kScrapFlavorTypeText )
226 {
227 // this increment is only valid for allocating, on the next GetFlavorData
228 // call it is reset again to the original value
229 dataSize++ ;
230 }
231 else if ( theType == kScrapFlavorTypeUnicode )
232 {
233 // this increment is only valid for allocating, on the next GetFlavorData
234 // call it is reset again to the original value
235 dataSize++ ;
236 dataSize++ ;
237 }
238
239 theData = new char[dataSize];
240
241 GetFlavorData( (DragReference)m_currentDrag, theItem, theType, (void*) theData, &dataSize, 0L );
242 if ( theType == kScrapFlavorTypeText )
243 {
244 theData[dataSize] = 0 ;
245 m_dataObject->SetData( wxDataFormat(wxDF_TEXT), dataSize , theData );
246 }
247 #if wxUSE_UNICODE
248 else if ( theType == kScrapFlavorTypeUnicode )
249 {
250 theData[dataSize + 0] =
251 theData[dataSize + 1] = 0 ;
252 m_dataObject->SetData( wxDataFormat(wxDF_UNICODETEXT), dataSize , theData );
253 }
254 #endif
255 else if ( theType == kDragFlavorTypeHFS )
256 {
257 HFSFlavor* theFile = (HFSFlavor*) theData ;
258 wxString name = wxMacFSSpec2MacFilename( &theFile->fileSpec ) ;
259 if ( !firstFileAdded )
260 {
261 // reset file list
262 ((wxFileDataObject*)m_dataObject)->SetData( 0 , "" ) ;
263 firstFileAdded = true ;
264 }
265
266 ((wxFileDataObject*)m_dataObject)->AddFile( name ) ;
267 }
268 else
269 {
270 m_dataObject->SetData( format, dataSize, theData );
271 }
272
273 delete [] theData;
274 }
275 break ;
276 }
277 }
278 }
279 }
280
281 return true ;
282 }
283
284 //-------------------------------------------------------------------------
285 // wxDropSource
286 //-------------------------------------------------------------------------
287
288 //-----------------------------------------------------------------------------
289 // drag request
290
291 wxDropSource::wxDropSource(wxWindow *win,
292 const wxCursor &cursorCopy,
293 const wxCursor &cursorMove,
294 const wxCursor &cursorStop)
295 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
296 {
297 wxMacEnsureTrackingHandlersInstalled() ;
298 m_window = win;
299 }
300
301 wxDropSource::wxDropSource(wxDataObject& data,
302 wxWindow *win,
303 const wxCursor &cursorCopy,
304 const wxCursor &cursorMove,
305 const wxCursor &cursorStop)
306 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
307 {
308 wxMacEnsureTrackingHandlersInstalled() ;
309 SetData( data );
310 m_window = win;
311 }
312
313 wxDropSource::~wxDropSource()
314 {
315 }
316
317 wxDragResult wxDropSource::DoDragDrop(int flags)
318 {
319 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
320
321 if (!m_data)
322 return (wxDragResult) wxDragNone;
323 if (m_data->GetFormatCount() == 0)
324 return (wxDragResult) wxDragNone;
325
326 OSErr result;
327 DragReference theDrag;
328 RgnHandle dragRegion;
329 if ((result = NewDrag(&theDrag)) != noErr)
330 return wxDragNone ;
331
332 // add data to drag
333 size_t formatCount = m_data->GetFormatCount() ;
334 wxDataFormat *formats = new wxDataFormat[formatCount] ;
335 m_data->GetAllFormats( formats ) ;
336 ItemReference theItem = 1 ;
337
338 for ( size_t i = 0 ; i < formatCount ; ++i )
339 {
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' )
345 {
346 if ( dataSize > 0 )
347 dataSize-- ;
348 dataPtr[ dataSize ] = 0 ;
349 if ( type == 'utxt' )
350 {
351 if ( dataSize > 0 )
352 dataSize-- ;
353 dataPtr[ dataSize ] = 0 ;
354 }
355
356 AddDragItemFlavor(theDrag, theItem, type , dataPtr, dataSize, 0);
357 }
358 else if (type == kDragFlavorTypeHFS )
359 {
360 HFSFlavor theFlavor ;
361 OSErr err = noErr;
362 CInfoPBRec cat;
363
364 wxMacFilename2FSSpec( wxString( dataPtr , *wxConvCurrent ) , &theFlavor.fileSpec ) ;
365
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);
372 if (err == noErr)
373 {
374 theFlavor.fdFlags = cat.hFileInfo.ioFlFndrInfo.fdFlags;
375 if (theFlavor.fileSpec.parID == fsRtParID)
376 {
377 theFlavor.fileCreator = 'MACS';
378 theFlavor.fileType = 'disk';
379 }
380 else if ((cat.hFileInfo.ioFlAttrib & ioDirMask) != 0)
381 {
382 theFlavor.fileCreator = 'MACS';
383 theFlavor.fileType = 'fold';
384 }
385 else
386 {
387 theFlavor.fileCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator;
388 theFlavor.fileType = cat.hFileInfo.ioFlFndrInfo.fdType;
389 }
390
391 AddDragItemFlavor(theDrag, theItem, type , &theFlavor, sizeof(theFlavor), 0);
392 }
393 }
394 else
395 {
396 AddDragItemFlavor(theDrag, theItem, type , dataPtr, dataSize, 0);
397 }
398
399 delete [] dataPtr ;
400 }
401
402 delete [] formats ;
403
404 dragRegion = NewRgn();
405 RgnHandle tempRgn = NewRgn() ;
406
407 EventRecord* ev = NULL ;
408
409 #if !TARGET_CARBON // TODO
410 ev = (EventRecord*) wxTheApp->MacGetCurrentEvent() ;
411 #else
412 {
413 EventRecord rec ;
414 ev = &rec ;
415 wxMacConvertEventToRecord( (EventRef) wxTheApp->MacGetCurrentEvent() , &rec ) ;
416 }
417 #endif
418
419 const short dragRegionOuterBoundary = 10 ;
420 const short dragRegionInnerBoundary = 9 ;
421
422 SetRectRgn(
423 dragRegion , ev->where.h - dragRegionOuterBoundary ,
424 ev->where.v - dragRegionOuterBoundary ,
425 ev->where.h + dragRegionOuterBoundary ,
426 ev->where.v + dragRegionOuterBoundary ) ;
427
428 SetRectRgn(
429 tempRgn , ev->where.h - dragRegionInnerBoundary ,
430 ev->where.v - dragRegionInnerBoundary ,
431 ev->where.h + dragRegionInnerBoundary ,
432 ev->where.v + dragRegionInnerBoundary ) ;
433
434 DiffRgn( dragRegion , tempRgn , dragRegion ) ;
435 DisposeRgn( tempRgn ) ;
436
437 // TODO:work with promises in order to return data only when drag
438 // was successfully completed
439
440 gTrackingGlobals.m_currentSource = this ;
441 result = TrackDrag(theDrag, ev , dragRegion);
442 DisposeRgn(dragRegion);
443 DisposeDrag(theDrag);
444 gTrackingGlobals.m_currentSource = NULL ;
445
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;
451
452 return dndresult;
453 }
454
455 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect)
456 {
457 const wxCursor& cursor = GetCursor(effect);
458 bool result = cursor.Ok();
459
460 if ( result )
461 cursor.MacInstall() ;
462
463 return result;
464 }
465
466 bool gTrackingGlobalsInstalled = false ;
467
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...
470
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) ;
477
478 void wxMacEnsureTrackingHandlersInstalled()
479 {
480 if ( !gTrackingGlobalsInstalled )
481 {
482 OSErr result;
483
484 result = InstallTrackingHandler(NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L,&gTrackingGlobals);
485 wxASSERT( result == noErr ) ;
486
487 result = InstallReceiveHandler(NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals);
488 wxASSERT( result == noErr ) ;
489
490 gTrackingGlobalsInstalled = true ;
491 }
492 }
493
494 pascal OSErr wxMacWindowDragTrackingHandler(
495 DragTrackingMessage theMessage, WindowPtr theWindow,
496 void *handlerRefCon, DragReference theDrag)
497 {
498 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
499 Point mouse, localMouse;
500 DragAttributes attributes;
501 GetDragAttributes(theDrag, &attributes);
502 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( theWindow ) ;
503
504 bool optionDown = GetCurrentKeyModifiers() & optionKey ;
505 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
506
507 switch (theMessage)
508 {
509 case kDragTrackingEnterHandler:
510 case kDragTrackingLeaveHandler:
511 break;
512
513 case kDragTrackingEnterWindow:
514 trackingGlobals->m_currentTargetWindow = NULL ;
515 trackingGlobals->m_currentTarget = NULL ;
516 break;
517
518 case kDragTrackingInWindow:
519 if (toplevel == NULL)
520 break;
521
522 GetDragMouse(theDrag, &mouse, 0L);
523 localMouse = mouse;
524 GlobalToLocal(&localMouse);
525
526 {
527 wxWindow *win = NULL ;
528 ControlPartCode controlPart ;
529 ControlRef control = wxMacFindControlUnderMouse(
530 toplevel , localMouse ,
531 theWindow , &controlPart ) ;
532 if ( control )
533 win = wxFindControlFromMacControl( control ) ;
534 else
535 win = toplevel ;
536
537 int localx , localy ;
538 localx = localMouse.h ;
539 localy = localMouse.v ;
540
541 if ( win )
542 win->MacRootWindowToWindow( &localx , &localy ) ;
543 if ( win != trackingGlobals->m_currentTargetWindow )
544 {
545 if ( trackingGlobals->m_currentTargetWindow )
546 {
547 // this window is left
548 if ( trackingGlobals->m_currentTarget )
549 {
550 HideDragHilite( theDrag );
551 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
552 trackingGlobals->m_currentTarget->OnLeave();
553 trackingGlobals->m_currentTarget = NULL;
554 trackingGlobals->m_currentTargetWindow = NULL;
555 }
556 }
557
558 if ( win )
559 {
560 // this window is entered
561 trackingGlobals->m_currentTargetWindow = win ;
562 trackingGlobals->m_currentTarget = win->GetDropTarget() ;
563 {
564 if ( trackingGlobals->m_currentTarget )
565 {
566 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
567 result = trackingGlobals->m_currentTarget->OnEnter( localx , localy , result ) ;
568 }
569
570 if ( result != wxDragNone )
571 {
572 int x , y ;
573
574 x = y = 0 ;
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 ) ;
581 }
582 }
583 }
584 }
585 else
586 {
587 if ( trackingGlobals->m_currentTarget )
588 {
589 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
590 trackingGlobals->m_currentTarget->OnDragOver( localx , localy , result ) ;
591 }
592 }
593
594 // set cursor for OnEnter and OnDragOver
595 if ( !trackingGlobals->m_currentSource && trackingGlobals->m_currentSource->GiveFeedback( result ) )
596 {
597 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
598 {
599 switch ( result )
600 {
601 case wxDragCopy :
602 {
603 wxCursor cursor(wxCURSOR_COPY_ARROW) ;
604 cursor.MacInstall() ;
605 }
606 break ;
607
608 case wxDragMove :
609 {
610 wxCursor cursor(wxCURSOR_ARROW) ;
611 cursor.MacInstall() ;
612 }
613 break ;
614
615 case wxDragNone :
616 {
617 wxCursor cursor(wxCURSOR_NO_ENTRY) ;
618 cursor.MacInstall() ;
619 }
620 break ;
621
622 case wxDragError:
623 case wxDragLink:
624 case wxDragCancel:
625 default:
626 // put these here to make gcc happy
627 ;
628 }
629 }
630 }
631 }
632 break;
633
634 case kDragTrackingLeaveWindow:
635 if (trackingGlobals->m_currentTarget)
636 {
637 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
638 trackingGlobals->m_currentTarget->OnLeave();
639 HideDragHilite( theDrag );
640 trackingGlobals->m_currentTarget = NULL;
641 }
642 trackingGlobals->m_currentTargetWindow = NULL;
643 break;
644
645 default:
646 break;
647 }
648
649 return noErr;
650 }
651
652 pascal OSErr wxMacWindowDragReceiveHandler(
653 WindowPtr theWindow,
654 void *handlerRefCon,
655 DragReference theDrag)
656 {
657 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
658 if ( trackingGlobals->m_currentTarget )
659 {
660 Point mouse, localMouse ;
661 int localx, localy ;
662
663 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
664 GetDragMouse(theDrag, &mouse, 0L);
665 localMouse = mouse;
666 GlobalToLocal(&localMouse);
667 localx = localMouse.h ;
668 localy = localMouse.v ;
669
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 ) )
674 {
675 bool optionDown = GetCurrentKeyModifiers() & optionKey ;
676 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
677 trackingGlobals->m_currentTarget->OnData( localx , localy , result ) ;
678 }
679 }
680
681 return noErr;
682 }
683
684 #endif