]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/dnd.cpp
minor cleanup
[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 != NULL)
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 if (dataSize > 0)
241 theData = new char[dataSize];
242 else
243 theData = NULL;
244
245 GetFlavorData( (DragReference)m_currentDrag, theItem, theType, (void*) theData, &dataSize, 0L );
246 if ( theType == kScrapFlavorTypeText )
247 {
248 theData[dataSize] = 0 ;
249 m_dataObject->SetData( wxDataFormat(wxDF_TEXT), dataSize , theData );
250 }
251 #if wxUSE_UNICODE
252 else if ( theType == kScrapFlavorTypeUnicode )
253 {
254 theData[dataSize + 0] =
255 theData[dataSize + 1] = 0 ;
256 m_dataObject->SetData( wxDataFormat(wxDF_UNICODETEXT), dataSize , theData );
257 }
258 #endif
259 else if ( theType == kDragFlavorTypeHFS )
260 {
261 wxFileDataObject *fdo = dynamic_cast<wxFileDataObject*>(m_dataObject);
262 wxASSERT( fdo != NULL );
263
264 if ((theData != NULL) && (fdo != NULL))
265 {
266 HFSFlavor* theFile = (HFSFlavor*) theData ;
267 wxString name = wxMacFSSpec2MacFilename( &theFile->fileSpec ) ;
268
269 if ( !firstFileAdded )
270 {
271 // reset file list
272 fdo->SetData( 0 , "" ) ;
273 firstFileAdded = true ;
274 }
275
276 if (!name.IsEmpty())
277 fdo->AddFile( name ) ;
278 }
279 }
280 else
281 {
282 m_dataObject->SetData( format, dataSize, theData );
283 }
284
285 delete [] theData;
286 }
287 break ;
288 }
289 }
290 }
291 }
292
293 return true ;
294 }
295
296 //-------------------------------------------------------------------------
297 // wxDropSource
298 //-------------------------------------------------------------------------
299
300 //-----------------------------------------------------------------------------
301 // drag request
302
303 wxDropSource::wxDropSource(wxWindow *win,
304 const wxCursor &cursorCopy,
305 const wxCursor &cursorMove,
306 const wxCursor &cursorStop)
307 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
308 {
309 wxMacEnsureTrackingHandlersInstalled() ;
310
311 m_window = win;
312 }
313
314 wxDropSource::wxDropSource(wxDataObject& data,
315 wxWindow *win,
316 const wxCursor &cursorCopy,
317 const wxCursor &cursorMove,
318 const wxCursor &cursorStop)
319 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
320 {
321 wxMacEnsureTrackingHandlersInstalled() ;
322
323 SetData( data );
324 m_window = win;
325 }
326
327 wxDropSource::~wxDropSource()
328 {
329 }
330
331 wxDragResult wxDropSource::DoDragDrop(int flags)
332 {
333 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
334
335 if (!m_data)
336 return (wxDragResult) wxDragNone;
337 if (m_data->GetFormatCount() == 0)
338 return (wxDragResult) wxDragNone;
339
340 OSStatus result;
341 DragReference theDrag;
342 RgnHandle dragRegion;
343 if ((result = NewDrag(&theDrag)) != noErr)
344 return wxDragNone ;
345
346 // add data to drag
347 size_t formatCount = m_data->GetFormatCount() ;
348 wxDataFormat *formats = new wxDataFormat[formatCount] ;
349 m_data->GetAllFormats( formats ) ;
350 ItemReference theItem = 1 ;
351
352 for ( size_t i = 0 ; i < formatCount ; ++i )
353 {
354 size_t dataSize = m_data->GetDataSize( formats[i] ) ;
355 Ptr dataPtr = new char[dataSize] ;
356 m_data->GetDataHere( formats[i] , dataPtr ) ;
357 OSType type = formats[i].GetFormatId() ;
358 if ( type == 'TEXT' || type == 'utxt' )
359 {
360 if ( dataSize > 0 )
361 dataSize-- ;
362 dataPtr[ dataSize ] = 0 ;
363 if ( type == 'utxt' )
364 {
365 if ( dataSize > 0 )
366 dataSize-- ;
367 dataPtr[ dataSize ] = 0 ;
368 }
369
370 AddDragItemFlavor( theDrag, theItem, type , dataPtr, dataSize, 0 );
371 }
372 else if (type == kDragFlavorTypeHFS )
373 {
374 HFSFlavor theFlavor ;
375 OSErr err = noErr;
376 CInfoPBRec cat;
377
378 wxMacFilename2FSSpec( wxString( dataPtr, *wxConvCurrent ), &theFlavor.fileSpec ) ;
379
380 memset( &cat, 0, sizeof(cat) );
381 cat.hFileInfo.ioNamePtr = theFlavor.fileSpec.name;
382 cat.hFileInfo.ioVRefNum = theFlavor.fileSpec.vRefNum;
383 cat.hFileInfo.ioDirID = theFlavor.fileSpec.parID;
384 cat.hFileInfo.ioFDirIndex = 0;
385 err = PBGetCatInfoSync( &cat );
386 if (err == noErr)
387 {
388 theFlavor.fdFlags = cat.hFileInfo.ioFlFndrInfo.fdFlags;
389 if (theFlavor.fileSpec.parID == fsRtParID)
390 {
391 theFlavor.fileCreator = 'MACS';
392 theFlavor.fileType = 'disk';
393 }
394 else if ((cat.hFileInfo.ioFlAttrib & ioDirMask) != 0)
395 {
396 theFlavor.fileCreator = 'MACS';
397 theFlavor.fileType = 'fold';
398 }
399 else
400 {
401 theFlavor.fileCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator;
402 theFlavor.fileType = cat.hFileInfo.ioFlFndrInfo.fdType;
403 }
404
405 AddDragItemFlavor( theDrag, theItem, type , &theFlavor, sizeof(theFlavor), 0 );
406 }
407 }
408 else
409 {
410 AddDragItemFlavor( theDrag, theItem, type , dataPtr, dataSize, 0 );
411 }
412
413 delete [] dataPtr ;
414 }
415
416 delete [] formats ;
417
418 dragRegion = NewRgn();
419 RgnHandle tempRgn = NewRgn() ;
420
421 EventRecord* ev = NULL ;
422
423 #if !TARGET_CARBON // TODO
424 ev = (EventRecord*) wxTheApp->MacGetCurrentEvent() ;
425 #else
426 {
427 EventRecord rec ;
428 ev = &rec ;
429 wxMacConvertEventToRecord( (EventRef) wxTheApp->MacGetCurrentEvent() , &rec ) ;
430 }
431 #endif
432
433 const short dragRegionOuterBoundary = 10 ;
434 const short dragRegionInnerBoundary = 9 ;
435
436 SetRectRgn(
437 dragRegion , ev->where.h - dragRegionOuterBoundary ,
438 ev->where.v - dragRegionOuterBoundary ,
439 ev->where.h + dragRegionOuterBoundary ,
440 ev->where.v + dragRegionOuterBoundary ) ;
441
442 SetRectRgn(
443 tempRgn , ev->where.h - dragRegionInnerBoundary ,
444 ev->where.v - dragRegionInnerBoundary ,
445 ev->where.h + dragRegionInnerBoundary ,
446 ev->where.v + dragRegionInnerBoundary ) ;
447
448 DiffRgn( dragRegion , tempRgn , dragRegion ) ;
449 DisposeRgn( tempRgn ) ;
450
451 // TODO: work with promises in order to return data
452 // only when drag was successfully completed
453
454 gTrackingGlobals.m_currentSource = this;
455 result = TrackDrag( theDrag, ev, dragRegion );
456 DisposeRgn( dragRegion );
457 DisposeDrag( theDrag );
458 gTrackingGlobals.m_currentSource = NULL;
459
460 bool optionDown = GetCurrentKeyModifiers() & optionKey;
461 wxDragResult dndresult = wxDragCopy;
462 if ( flags != wxDrag_CopyOnly )
463 // on mac the option key is always the indication for copy
464 dndresult = optionDown ? wxDragCopy : wxDragMove;
465
466 return dndresult;
467 }
468
469 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect)
470 {
471 const wxCursor& cursor = GetCursor(effect);
472 bool result = cursor.Ok();
473
474 if ( result )
475 cursor.MacInstall();
476
477 return result;
478 }
479
480 bool gTrackingGlobalsInstalled = false ;
481
482 // passing the globals via refcon is not needed by the CFM and later architectures anymore
483 // but I'll leave it in there, just in case...
484
485 pascal OSErr wxMacWindowDragTrackingHandler(
486 DragTrackingMessage theMessage, WindowPtr theWindow,
487 void *handlerRefCon, DragReference theDrag );
488 pascal OSErr wxMacWindowDragReceiveHandler(
489 WindowPtr theWindow, void *handlerRefCon,
490 DragReference theDrag );
491
492 void wxMacEnsureTrackingHandlersInstalled()
493 {
494 if ( !gTrackingGlobalsInstalled )
495 {
496 OSStatus err;
497
498 err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals );
499 verify_noerr( err );
500
501 err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals );
502 verify_noerr( err );
503
504 gTrackingGlobalsInstalled = true;
505 }
506 }
507
508 pascal OSErr wxMacWindowDragTrackingHandler(
509 DragTrackingMessage theMessage, WindowPtr theWindow,
510 void *handlerRefCon, DragReference theDrag )
511 {
512 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
513
514 Point mouse, localMouse;
515 DragAttributes attributes;
516
517 GetDragAttributes( theDrag, &attributes );
518
519 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( theWindow ) ;
520
521 bool optionDown = GetCurrentKeyModifiers() & optionKey ;
522 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
523
524 switch (theMessage)
525 {
526 case kDragTrackingEnterHandler:
527 case kDragTrackingLeaveHandler:
528 break;
529
530 case kDragTrackingEnterWindow:
531 if (trackingGlobals != NULL)
532 {
533 trackingGlobals->m_currentTargetWindow = NULL ;
534 trackingGlobals->m_currentTarget = NULL ;
535 }
536 break;
537
538 case kDragTrackingInWindow:
539 if (trackingGlobals == NULL)
540 break;
541 if (toplevel == NULL)
542 break;
543
544 GetDragMouse( theDrag, &mouse, 0L );
545 localMouse = mouse;
546 GlobalToLocal( &localMouse );
547
548 {
549 wxWindow *win = NULL ;
550 ControlPartCode controlPart ;
551 ControlRef control = wxMacFindControlUnderMouse(
552 toplevel , localMouse , theWindow , &controlPart ) ;
553 if ( control )
554 win = wxFindControlFromMacControl( control ) ;
555 else
556 win = toplevel ;
557
558 int localx , localy ;
559 localx = localMouse.h ;
560 localy = localMouse.v ;
561
562 if ( win )
563 win->MacRootWindowToWindow( &localx , &localy ) ;
564 if ( win != trackingGlobals->m_currentTargetWindow )
565 {
566 if ( trackingGlobals->m_currentTargetWindow )
567 {
568 // this window is left
569 if ( trackingGlobals->m_currentTarget )
570 {
571 HideDragHilite( theDrag );
572 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
573 trackingGlobals->m_currentTarget->OnLeave();
574 trackingGlobals->m_currentTarget = NULL;
575 trackingGlobals->m_currentTargetWindow = NULL;
576 }
577 }
578
579 if ( win )
580 {
581 // this window is entered
582 trackingGlobals->m_currentTargetWindow = win ;
583 trackingGlobals->m_currentTarget = win->GetDropTarget() ;
584 {
585 if ( trackingGlobals->m_currentTarget )
586 {
587 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
588 result = trackingGlobals->m_currentTarget->OnEnter( localx , localy , result ) ;
589 }
590
591 if ( result != wxDragNone )
592 {
593 int x , y ;
594
595 x = y = 0 ;
596 win->MacWindowToRootWindow( &x , &y ) ;
597 RgnHandle hiliteRgn = NewRgn() ;
598 Rect r = { y , x , y + win->GetSize().y , x + win->GetSize().x } ;
599 RectRgn( hiliteRgn , &r ) ;
600 ShowDragHilite( theDrag, hiliteRgn, true );
601 DisposeRgn( hiliteRgn ) ;
602 }
603 }
604 }
605 }
606 else
607 {
608 if ( trackingGlobals->m_currentTarget )
609 {
610 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
611 trackingGlobals->m_currentTarget->OnDragOver( localx , localy , result ) ;
612 }
613 }
614
615 // set cursor for OnEnter and OnDragOver
616 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
617 {
618 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
619 {
620 switch ( result )
621 {
622 case wxDragCopy:
623 {
624 wxCursor cursor(wxCURSOR_COPY_ARROW) ;
625 cursor.MacInstall() ;
626 }
627 break ;
628
629 case wxDragMove:
630 {
631 wxCursor cursor(wxCURSOR_ARROW) ;
632 cursor.MacInstall() ;
633 }
634 break ;
635
636 case wxDragNone:
637 {
638 wxCursor cursor(wxCURSOR_NO_ENTRY) ;
639 cursor.MacInstall() ;
640 }
641 break ;
642
643 case wxDragError:
644 case wxDragLink:
645 case wxDragCancel:
646 default:
647 // put these here to make gcc happy
648 ;
649 }
650 }
651 }
652 }
653 break;
654
655 case kDragTrackingLeaveWindow:
656 if (trackingGlobals == NULL)
657 break;
658
659 if (trackingGlobals->m_currentTarget)
660 {
661 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
662 trackingGlobals->m_currentTarget->OnLeave();
663 HideDragHilite( theDrag );
664 trackingGlobals->m_currentTarget = NULL;
665 }
666 trackingGlobals->m_currentTargetWindow = NULL;
667 break;
668
669 default:
670 break;
671 }
672
673 return noErr;
674 }
675
676 pascal OSErr wxMacWindowDragReceiveHandler(
677 WindowPtr theWindow,
678 void *handlerRefCon,
679 DragReference theDrag)
680 {
681 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
682 if ( trackingGlobals->m_currentTarget )
683 {
684 Point mouse, localMouse ;
685 int localx, localy ;
686
687 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
688 GetDragMouse(theDrag, &mouse, 0L);
689 localMouse = mouse;
690 GlobalToLocal(&localMouse);
691 localx = localMouse.h ;
692 localy = localMouse.v ;
693
694 // TODO : should we use client coordinates?
695 if ( trackingGlobals->m_currentTargetWindow )
696 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx , &localy ) ;
697 if ( trackingGlobals->m_currentTarget->OnDrop( localx , localy ) )
698 {
699 bool optionDown = GetCurrentKeyModifiers() & optionKey ;
700 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
701 trackingGlobals->m_currentTarget->OnData( localx , localy , result ) ;
702 }
703 }
704
705 return noErr;
706 }
707
708 #endif