]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/dnd.cpp
Patch from Hartwig for wxMac implementation
[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/toplevel.h"
21 #include "wx/gdicmn.h"
22 #endif // WX_PRECOMP
23
24 #include "wx/mac/private.h"
25
26 #ifndef __DARWIN__
27 #include <Scrap.h>
28 #endif
29
30
31 // ----------------------------------------------------------------------------
32 // globals
33 // ----------------------------------------------------------------------------
34
35 typedef struct
36 {
37 wxWindow *m_currentTargetWindow;
38 wxDropTarget *m_currentTarget;
39 wxDropSource *m_currentSource;
40 wxDragResult m_result;
41 int m_flags;
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 ItemReference theItem;
117 FlavorType theType;
118 UInt16 flavors = 0;
119
120 CountDragItems( (DragReference)m_currentDrag, &items );
121 for (UInt16 index = 1; index <= items && !supported; ++index)
122 {
123 flavors = 0;
124 GetDragItemReferenceNumber( (DragReference)m_currentDrag, index, &theItem );
125 CountDragItemFlavors( (DragReference)m_currentDrag, theItem, &flavors );
126
127 for ( UInt16 flavor = 1; flavor <= flavors; ++flavor )
128 {
129 GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
130 if ( m_dataObject->IsSupportedFormat( wxDataFormat( theType ) ) )
131 {
132 supported = true;
133 break;
134 }
135 }
136 }
137 }
138
139 return supported;
140 }
141
142 bool wxDropTarget::GetData()
143 {
144 if (m_dataObject == NULL)
145 return false;
146
147 if ( !CurrentDragHasSupportedFormat() )
148 return false;
149
150 bool transferred = false;
151 if ( gTrackingGlobals.m_currentSource != NULL )
152 {
153 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject();
154
155 if (data != NULL)
156 {
157 size_t formatcount = data->GetFormatCount();
158 wxDataFormat *array = new wxDataFormat[formatcount];
159 data->GetAllFormats( array );
160 for (size_t i = 0; !transferred && i < formatcount; i++)
161 {
162 wxDataFormat format = array[i];
163 if ( m_dataObject->IsSupported( format ) )
164 {
165 int size = data->GetDataSize( format );
166 transferred = true;
167
168 if (size == 0)
169 {
170 m_dataObject->SetData( format, 0, 0 );
171 }
172 else
173 {
174 char *d = new char[size];
175 data->GetDataHere( format, (void*)d );
176 m_dataObject->SetData( format, size, d );
177 delete [] d;
178 }
179 }
180 }
181
182 delete [] array;
183 }
184 }
185
186 if ( !transferred )
187 {
188 UInt16 items;
189 OSErr result;
190 ItemReference theItem;
191 FlavorType theType;
192 FlavorFlags theFlags;
193 UInt16 flavors;
194 wxString filenamesPassed;
195
196 CountDragItems( (DragReference)m_currentDrag, &items );
197 for (UInt16 index = 1; index <= items; ++index)
198 {
199 flavors = 0;
200 GetDragItemReferenceNumber( (DragReference)m_currentDrag, index, &theItem );
201 CountDragItemFlavors( (DragReference)m_currentDrag, theItem, &flavors );
202 wxDataFormat preferredFormat = m_dataObject->GetPreferredFormat( wxDataObject::Set );
203 bool hasPreferredFormat = false;
204
205 for (UInt16 flavor = 1; flavor <= flavors; ++flavor)
206 {
207 result = GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
208 wxDataFormat format( theType );
209 if (preferredFormat == format)
210 {
211 hasPreferredFormat = true;
212 break;
213 }
214 }
215
216 for (UInt16 flavor = 1; flavor <= flavors; ++flavor)
217 {
218 result = GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
219 wxDataFormat format( theType );
220 if ((hasPreferredFormat && format == preferredFormat)
221 || (!hasPreferredFormat && m_dataObject->IsSupportedFormat( format )))
222 {
223 result = GetFlavorFlags( (DragReference)m_currentDrag, theItem, theType, &theFlags );
224 if (result == noErr)
225 {
226 Size dataSize;
227 Ptr theData;
228
229 GetFlavorDataSize( (DragReference)m_currentDrag, theItem, theType, &dataSize );
230 if (theType == kScrapFlavorTypeText)
231 {
232 // this increment is only valid for allocating:
233 // on the next GetFlavorData call it is reset again to the original value
234 dataSize++;
235 }
236 else if (theType == kScrapFlavorTypeUnicode)
237 {
238 // this increment is only valid for allocating:
239 // on the next GetFlavorData call it is reset again to the original value
240 dataSize++;
241 dataSize++;
242 }
243
244 if (dataSize > 0)
245 theData = new char[dataSize];
246 else
247 theData = NULL;
248
249 GetFlavorData( (DragReference)m_currentDrag, theItem, theType, (void*)theData, &dataSize, 0L );
250 switch (theType)
251 {
252 case kScrapFlavorTypeText:
253 theData[dataSize] = 0;
254 m_dataObject->SetData( wxDataFormat(wxDF_TEXT), dataSize, theData );
255 break;
256
257 #if wxUSE_UNICODE
258 case kScrapFlavorTypeUnicode:
259 theData[dataSize + 0] =
260 theData[dataSize + 1] = 0;
261 m_dataObject->SetData( wxDataFormat(wxDF_UNICODETEXT), dataSize, theData );
262 break;
263 #endif
264
265 case kDragFlavorTypeHFS:
266 if (theData != NULL)
267 {
268 HFSFlavor* theFile = (HFSFlavor*)theData;
269 #ifndef __LP64__
270 wxString name = wxMacFSSpec2MacFilename( &theFile->fileSpec );
271
272 if (!name.empty())
273 filenamesPassed += name + wxT("\n");
274 #endif
275 }
276 break;
277
278 default:
279 m_dataObject->SetData( format, dataSize, theData );
280 break;
281 }
282
283 delete [] theData;
284 }
285 break;
286 }
287 }
288 }
289
290 if (filenamesPassed.length() > 0)
291 {
292 wxCharBuffer buf = filenamesPassed.fn_str();
293 m_dataObject->SetData( wxDataFormat(wxDF_FILENAME), strlen( buf ), (const char*)buf );
294 }
295 }
296
297 return true;
298 }
299
300 //-------------------------------------------------------------------------
301 // wxDropSource
302 //-------------------------------------------------------------------------
303
304 //-----------------------------------------------------------------------------
305 // drag request
306
307 wxDropSource::wxDropSource(wxWindow *win,
308 const wxCursor &cursorCopy,
309 const wxCursor &cursorMove,
310 const wxCursor &cursorStop)
311 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
312 {
313 wxMacEnsureTrackingHandlersInstalled();
314
315 m_window = win;
316 }
317
318 wxDropSource::wxDropSource(wxDataObject& data,
319 wxWindow *win,
320 const wxCursor &cursorCopy,
321 const wxCursor &cursorMove,
322 const wxCursor &cursorStop)
323 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
324 {
325 wxMacEnsureTrackingHandlersInstalled();
326
327 SetData( data );
328 m_window = win;
329 }
330
331 wxDropSource::~wxDropSource()
332 {
333 }
334
335 wxDragResult wxDropSource::DoDragDrop(int flags)
336 {
337 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
338
339 if ((m_data == NULL) || (m_data->GetFormatCount() == 0))
340 return (wxDragResult)wxDragNone;
341
342 DragReference theDrag;
343 RgnHandle dragRegion;
344
345 if (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 = (ItemReference) 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 #ifndef __LP64__
379 CInfoPBRec cat;
380
381 wxMacFilename2FSSpec( wxString( dataPtr, *wxConvCurrent ), &theFlavor.fileSpec );
382
383 memset( &cat, 0, sizeof(cat) );
384 cat.hFileInfo.ioNamePtr = theFlavor.fileSpec.name;
385 cat.hFileInfo.ioVRefNum = theFlavor.fileSpec.vRefNum;
386 cat.hFileInfo.ioDirID = theFlavor.fileSpec.parID;
387 cat.hFileInfo.ioFDirIndex = 0;
388 err = PBGetCatInfoSync( &cat );
389 #endif
390 if (err == noErr)
391 {
392 #ifndef __LP64__
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 #endif
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 EventRecord rec;
432 ev = &rec;
433 wxMacConvertEventToRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec );
434 #endif
435
436 const short dragRegionOuterBoundary = 10;
437 const short dragRegionInnerBoundary = 9;
438
439 SetRectRgn(
440 dragRegion,
441 ev->where.h - dragRegionOuterBoundary,
442 ev->where.v - dragRegionOuterBoundary,
443 ev->where.h + dragRegionOuterBoundary,
444 ev->where.v + dragRegionOuterBoundary );
445
446 SetRectRgn(
447 tempRgn,
448 ev->where.h - dragRegionInnerBoundary,
449 ev->where.v - dragRegionInnerBoundary,
450 ev->where.h + dragRegionInnerBoundary,
451 ev->where.v + dragRegionInnerBoundary );
452
453 DiffRgn( dragRegion, tempRgn, dragRegion );
454 DisposeRgn( tempRgn );
455
456 // TODO: work with promises in order to return data
457 // only when drag was successfully completed
458
459 gTrackingGlobals.m_currentSource = this;
460 gTrackingGlobals.m_result = wxDragNone;
461 gTrackingGlobals.m_flags = flags;
462
463 TrackDrag( theDrag, ev, dragRegion );
464 DisposeRgn( dragRegion );
465 DisposeDrag( theDrag );
466 gTrackingGlobals.m_currentSource = NULL;
467
468 return gTrackingGlobals.m_result;
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 wxMacGlobalToLocal( theWindow, &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 #ifndef __LP64__
574 HideDragHilite( theDrag );
575 #endif
576 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
577 trackingGlobals->m_currentTarget->OnLeave();
578 trackingGlobals->m_currentTarget = NULL;
579 trackingGlobals->m_currentTargetWindow = NULL;
580 }
581 }
582
583 if ( win )
584 {
585 // this window is entered
586 trackingGlobals->m_currentTargetWindow = win;
587 trackingGlobals->m_currentTarget = win->GetDropTarget();
588 {
589 if ( trackingGlobals->m_currentTarget )
590 {
591 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
592 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
593 }
594
595 if ( result != wxDragNone )
596 {
597 int x, y;
598
599 x = y = 0;
600 win->MacWindowToRootWindow( &x, &y );
601 RgnHandle hiliteRgn = NewRgn();
602 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
603 RectRgn( hiliteRgn, &r );
604 #ifndef __LP64__
605 ShowDragHilite( theDrag, hiliteRgn, true );
606 #endif
607 DisposeRgn( hiliteRgn );
608 }
609 }
610 }
611 }
612 else
613 {
614 if ( trackingGlobals->m_currentTarget )
615 {
616 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
617 trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
618 }
619 }
620
621 // set cursor for OnEnter and OnDragOver
622 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
623 {
624 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
625 {
626 int cursorID = wxCURSOR_NONE;
627
628 switch (result)
629 {
630 case wxDragCopy:
631 cursorID = wxCURSOR_COPY_ARROW;
632 break;
633
634 case wxDragMove:
635 cursorID = wxCURSOR_ARROW;
636 break;
637
638 case wxDragNone:
639 cursorID = wxCURSOR_NO_ENTRY;
640 break;
641
642 case wxDragError:
643 case wxDragLink:
644 case wxDragCancel:
645 default:
646 // put these here to make gcc happy
647 ;
648 }
649
650 if (cursorID != wxCURSOR_NONE)
651 {
652 wxCursor cursor( cursorID );
653 cursor.MacInstall();
654 }
655 }
656 }
657 }
658 break;
659
660 case kDragTrackingLeaveWindow:
661 if (trackingGlobals == NULL)
662 break;
663
664 if (trackingGlobals->m_currentTarget)
665 {
666 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
667 trackingGlobals->m_currentTarget->OnLeave();
668 #ifndef __LP64__
669 HideDragHilite( theDrag );
670 #endif
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 wxMacGlobalToLocal( theWindow, &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 // the option key indicates copy in Mac UI, if it's not pressed do
707 // move by default if it's allowed at all
708 wxDragResult
709 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) ||
710 (GetCurrentKeyModifiers() & optionKey)
711 ? wxDragCopy
712 : wxDragMove;
713 trackingGlobals->m_result =
714 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
715 }
716 }
717
718 return noErr;
719 }
720
721 #endif // wxUSE_DRAG_AND_DROP
722