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