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