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