]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/dnd.cpp
allowing the suppression of SetValue event-triggering (needed for composite controls)
[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 wxString filenamesPassed;
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 if (theData != NULL)
264 {
265 HFSFlavor* theFile = (HFSFlavor*)theData;
266 wxString name = wxMacFSSpec2MacFilename( &theFile->fileSpec );
267
268 if (!name.IsEmpty())
269 filenamesPassed += name + wxT("\n");
270 }
271 break;
272
273 default:
274 m_dataObject->SetData( format, dataSize, theData );
275 break;
276 }
277
278 delete [] theData;
279 }
280 break;
281 }
282 }
283 }
284
285 if (filenamesPassed.Len() > 0)
286 {
287 wxCharBuffer buf = filenamesPassed.fn_str();
288 m_dataObject->SetData( wxDataFormat(wxDF_FILENAME), strlen( buf ), (const char*)buf );
289 }
290 }
291
292 return true;
293 }
294
295 //-------------------------------------------------------------------------
296 // wxDropSource
297 //-------------------------------------------------------------------------
298
299 //-----------------------------------------------------------------------------
300 // drag request
301
302 wxDropSource::wxDropSource(wxWindow *win,
303 const wxCursor &cursorCopy,
304 const wxCursor &cursorMove,
305 const wxCursor &cursorStop)
306 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
307 {
308 wxMacEnsureTrackingHandlersInstalled();
309
310 m_window = win;
311 }
312
313 wxDropSource::wxDropSource(wxDataObject& data,
314 wxWindow *win,
315 const wxCursor &cursorCopy,
316 const wxCursor &cursorMove,
317 const wxCursor &cursorStop)
318 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
319 {
320 wxMacEnsureTrackingHandlersInstalled();
321
322 SetData( data );
323 m_window = win;
324 }
325
326 wxDropSource::~wxDropSource()
327 {
328 }
329
330 wxDragResult wxDropSource::DoDragDrop(int flags)
331 {
332 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
333
334 if ((m_data == NULL) || (m_data->GetFormatCount() == 0))
335 return (wxDragResult)wxDragNone;
336
337 OSStatus result;
338 DragReference theDrag;
339 RgnHandle dragRegion;
340
341 if ((result = NewDrag( &theDrag )) != noErr)
342 return wxDragNone;
343
344 // add data to drag
345 size_t formatCount = m_data->GetFormatCount();
346 wxDataFormat *formats = new wxDataFormat[formatCount];
347 m_data->GetAllFormats( formats );
348 ItemReference theItem = 1;
349
350 for ( size_t i = 0; i < formatCount; ++i )
351 {
352 size_t dataSize = m_data->GetDataSize( formats[i] );
353 Ptr dataPtr = new char[dataSize];
354 m_data->GetDataHere( formats[i], dataPtr );
355 OSType type = formats[i].GetFormatId();
356 if ( type == 'TEXT' || type == 'utxt' )
357 {
358 if ( dataSize > 0 )
359 dataSize--;
360 dataPtr[ dataSize ] = 0;
361 if ( type == 'utxt' )
362 {
363 if ( dataSize > 0 )
364 dataSize--;
365 dataPtr[ dataSize ] = 0;
366 }
367
368 AddDragItemFlavor( theDrag, theItem, type, dataPtr, dataSize, 0 );
369 }
370 else if (type == kDragFlavorTypeHFS )
371 {
372 HFSFlavor theFlavor;
373 OSErr err = noErr;
374 CInfoPBRec cat;
375
376 wxMacFilename2FSSpec( wxString( dataPtr, *wxConvCurrent ), &theFlavor.fileSpec );
377
378 memset( &cat, 0, sizeof(cat) );
379 cat.hFileInfo.ioNamePtr = theFlavor.fileSpec.name;
380 cat.hFileInfo.ioVRefNum = theFlavor.fileSpec.vRefNum;
381 cat.hFileInfo.ioDirID = theFlavor.fileSpec.parID;
382 cat.hFileInfo.ioFDirIndex = 0;
383 err = PBGetCatInfoSync( &cat );
384 if (err == noErr)
385 {
386 theFlavor.fdFlags = cat.hFileInfo.ioFlFndrInfo.fdFlags;
387 if (theFlavor.fileSpec.parID == fsRtParID)
388 {
389 theFlavor.fileCreator = 'MACS';
390 theFlavor.fileType = 'disk';
391 }
392 else if ((cat.hFileInfo.ioFlAttrib & ioDirMask) != 0)
393 {
394 theFlavor.fileCreator = 'MACS';
395 theFlavor.fileType = 'fold';
396 }
397 else
398 {
399 theFlavor.fileCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator;
400 theFlavor.fileType = cat.hFileInfo.ioFlFndrInfo.fdType;
401 }
402
403 AddDragItemFlavor( theDrag, theItem, type, &theFlavor, sizeof(theFlavor), 0 );
404 }
405 }
406 else
407 {
408 AddDragItemFlavor( theDrag, theItem, type, dataPtr, dataSize, 0 );
409 }
410
411 delete [] dataPtr;
412 }
413
414 delete [] formats;
415
416 dragRegion = NewRgn();
417 RgnHandle tempRgn = NewRgn();
418
419 EventRecord* ev = NULL;
420
421 #if !TARGET_CARBON // TODO
422 ev = (EventRecord*) wxTheApp->MacGetCurrentEvent();
423 #else
424 EventRecord rec;
425 ev = &rec;
426 wxMacConvertEventToRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec );
427 #endif
428
429 const short dragRegionOuterBoundary = 10;
430 const short dragRegionInnerBoundary = 9;
431
432 SetRectRgn(
433 dragRegion,
434 ev->where.h - dragRegionOuterBoundary,
435 ev->where.v - dragRegionOuterBoundary,
436 ev->where.h + dragRegionOuterBoundary,
437 ev->where.v + dragRegionOuterBoundary );
438
439 SetRectRgn(
440 tempRgn,
441 ev->where.h - dragRegionInnerBoundary,
442 ev->where.v - dragRegionInnerBoundary,
443 ev->where.h + dragRegionInnerBoundary,
444 ev->where.v + dragRegionInnerBoundary );
445
446 DiffRgn( dragRegion, tempRgn, dragRegion );
447 DisposeRgn( tempRgn );
448
449 // TODO: work with promises in order to return data
450 // only when drag was successfully completed
451
452 gTrackingGlobals.m_currentSource = this;
453 result = TrackDrag( theDrag, ev, dragRegion );
454 DisposeRgn( dragRegion );
455 DisposeDrag( theDrag );
456 gTrackingGlobals.m_currentSource = NULL;
457
458 bool optionDown = GetCurrentKeyModifiers() & optionKey;
459 wxDragResult dndresult = wxDragCopy;
460 if ( flags != wxDrag_CopyOnly )
461 // on mac the option key is always the indication for copy
462 dndresult = optionDown ? wxDragCopy : wxDragMove;
463
464 return dndresult;
465 }
466
467 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect)
468 {
469 const wxCursor& cursor = GetCursor(effect);
470 bool result = cursor.Ok();
471
472 if ( result )
473 cursor.MacInstall();
474
475 return result;
476 }
477
478 bool gTrackingGlobalsInstalled = false;
479
480 // passing the globals via refcon is not needed by the CFM and later architectures anymore
481 // but I'll leave it in there, just in case...
482
483 pascal OSErr wxMacWindowDragTrackingHandler(
484 DragTrackingMessage theMessage, WindowPtr theWindow,
485 void *handlerRefCon, DragReference theDrag );
486 pascal OSErr wxMacWindowDragReceiveHandler(
487 WindowPtr theWindow, void *handlerRefCon,
488 DragReference theDrag );
489
490 void wxMacEnsureTrackingHandlersInstalled()
491 {
492 if ( !gTrackingGlobalsInstalled )
493 {
494 OSStatus err;
495
496 err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals );
497 verify_noerr( err );
498
499 err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals );
500 verify_noerr( err );
501
502 gTrackingGlobalsInstalled = true;
503 }
504 }
505
506 pascal OSErr wxMacWindowDragTrackingHandler(
507 DragTrackingMessage theMessage, WindowPtr theWindow,
508 void *handlerRefCon, DragReference theDrag )
509 {
510 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
511
512 Point mouse, localMouse;
513 DragAttributes attributes;
514
515 GetDragAttributes( theDrag, &attributes );
516
517 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( theWindow );
518
519 bool optionDown = GetCurrentKeyModifiers() & optionKey;
520 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
521
522 switch (theMessage)
523 {
524 case kDragTrackingEnterHandler:
525 case kDragTrackingLeaveHandler:
526 break;
527
528 case kDragTrackingEnterWindow:
529 if (trackingGlobals != NULL)
530 {
531 trackingGlobals->m_currentTargetWindow = NULL;
532 trackingGlobals->m_currentTarget = NULL;
533 }
534 break;
535
536 case kDragTrackingInWindow:
537 if (trackingGlobals == NULL)
538 break;
539 if (toplevel == NULL)
540 break;
541
542 GetDragMouse( theDrag, &mouse, 0L );
543 localMouse = mouse;
544 GlobalToLocal( &localMouse );
545
546 {
547 wxWindow *win = NULL;
548 ControlPartCode controlPart;
549 ControlRef control = wxMacFindControlUnderMouse(
550 toplevel, localMouse, theWindow, &controlPart );
551 if ( control )
552 win = wxFindControlFromMacControl( control );
553 else
554 win = toplevel;
555
556 int localx, localy;
557 localx = localMouse.h;
558 localy = localMouse.v;
559
560 if ( win )
561 win->MacRootWindowToWindow( &localx, &localy );
562 if ( win != trackingGlobals->m_currentTargetWindow )
563 {
564 if ( trackingGlobals->m_currentTargetWindow )
565 {
566 // this window is left
567 if ( trackingGlobals->m_currentTarget )
568 {
569 HideDragHilite( theDrag );
570 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
571 trackingGlobals->m_currentTarget->OnLeave();
572 trackingGlobals->m_currentTarget = NULL;
573 trackingGlobals->m_currentTargetWindow = NULL;
574 }
575 }
576
577 if ( win )
578 {
579 // this window is entered
580 trackingGlobals->m_currentTargetWindow = win;
581 trackingGlobals->m_currentTarget = win->GetDropTarget();
582 {
583 if ( trackingGlobals->m_currentTarget )
584 {
585 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
586 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
587 }
588
589 if ( result != wxDragNone )
590 {
591 int x, y;
592
593 x = y = 0;
594 win->MacWindowToRootWindow( &x, &y );
595 RgnHandle hiliteRgn = NewRgn();
596 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
597 RectRgn( hiliteRgn, &r );
598 ShowDragHilite( theDrag, hiliteRgn, true );
599 DisposeRgn( hiliteRgn );
600 }
601 }
602 }
603 }
604 else
605 {
606 if ( trackingGlobals->m_currentTarget )
607 {
608 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
609 trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
610 }
611 }
612
613 // set cursor for OnEnter and OnDragOver
614 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
615 {
616 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
617 {
618 int cursorID = wxCURSOR_NONE;
619
620 switch (result)
621 {
622 case wxDragCopy:
623 cursorID = wxCURSOR_COPY_ARROW;
624 break;
625
626 case wxDragMove:
627 cursorID = wxCURSOR_ARROW;
628 break;
629
630 case wxDragNone:
631 cursorID = wxCURSOR_NO_ENTRY;
632 break;
633
634 case wxDragError:
635 case wxDragLink:
636 case wxDragCancel:
637 default:
638 // put these here to make gcc happy
639 ;
640 }
641
642 if (cursorID != wxCURSOR_NONE)
643 {
644 wxCursor cursor( cursorID );
645 cursor.MacInstall();
646 }
647 }
648 }
649 }
650 break;
651
652 case kDragTrackingLeaveWindow:
653 if (trackingGlobals == NULL)
654 break;
655
656 if (trackingGlobals->m_currentTarget)
657 {
658 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
659 trackingGlobals->m_currentTarget->OnLeave();
660 HideDragHilite( theDrag );
661 trackingGlobals->m_currentTarget = NULL;
662 }
663 trackingGlobals->m_currentTargetWindow = NULL;
664 break;
665
666 default:
667 break;
668 }
669
670 return noErr;
671 }
672
673 pascal OSErr wxMacWindowDragReceiveHandler(
674 WindowPtr theWindow,
675 void *handlerRefCon,
676 DragReference theDrag)
677 {
678 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
679 if ( trackingGlobals->m_currentTarget )
680 {
681 Point mouse, localMouse;
682 int localx, localy;
683
684 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
685 GetDragMouse( theDrag, &mouse, 0L );
686 localMouse = mouse;
687 GlobalToLocal( &localMouse );
688 localx = localMouse.h;
689 localy = localMouse.v;
690
691 // TODO : should we use client coordinates?
692 if ( trackingGlobals->m_currentTargetWindow )
693 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
694 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
695 {
696 bool optionDown = GetCurrentKeyModifiers() & optionKey;
697 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
698 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
699 }
700 }
701
702 return noErr;
703 }
704
705 #endif