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