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