]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/dnd.cpp
filedata implementation streamlined
[wxWidgets.git] / src / mac / carbon / dnd.cpp
... / ...
CommitLineData
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
32typedef struct
33{
34 wxWindow *m_currentTargetWindow;
35 wxDropTarget *m_currentTarget;
36 wxDropSource *m_currentSource;
37}
38MacTrackingGlobals;
39
40MacTrackingGlobals gTrackingGlobals;
41
42void wxMacEnsureTrackingHandlersInstalled();
43
44//----------------------------------------------------------------------------
45// wxDropTarget
46//----------------------------------------------------------------------------
47
48wxDropTarget::wxDropTarget( wxDataObject *data )
49 : wxDropTargetBase( data )
50{
51 wxMacEnsureTrackingHandlersInstalled();
52}
53
54wxDragResult wxDropTarget::OnDragOver(
55 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
56 wxDragResult def )
57{
58 return CurrentDragHasSupportedFormat() ? def : wxDragNone;
59}
60
61bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
62{
63 if (m_dataObject == NULL)
64 return false;
65
66 return CurrentDragHasSupportedFormat();
67}
68
69wxDragResult 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
82bool 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
139bool 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 wxString filenamesPassed ;
194
195 CountDragItems( (DragReference)m_currentDrag, &items );
196 for (UInt16 index = 1; index <= items; ++index)
197 {
198 flavors = 0;
199 GetDragItemReferenceNumber( (DragReference)m_currentDrag, index, &theItem );
200 CountDragItemFlavors( (DragReference)m_currentDrag, theItem, &flavors );
201 wxDataFormat preferredFormat = m_dataObject->GetPreferredFormat( wxDataObject::Set );
202 bool hasPreferredFormat = false;
203
204 for ( UInt16 flavor = 1; flavor <= flavors; ++flavor )
205 {
206 result = GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
207 wxDataFormat format( theType );
208 if ( preferredFormat == format )
209 {
210 hasPreferredFormat = true;
211 break;
212 }
213 }
214
215 for ( UInt16 flavor = 1; flavor <= flavors; ++flavor )
216 {
217 result = GetFlavorType( (DragReference)m_currentDrag, theItem, flavor, &theType );
218 wxDataFormat format( theType );
219 if ( (hasPreferredFormat && format == preferredFormat)
220 || (!hasPreferredFormat && m_dataObject->IsSupportedFormat( format )))
221 {
222 result = GetFlavorFlags( (DragReference)m_currentDrag, theItem, theType, &theFlags );
223 if (result == noErr)
224 {
225 Size dataSize;
226 Ptr theData;
227
228 GetFlavorDataSize( (DragReference)m_currentDrag, theItem, theType, &dataSize );
229 if ( theType == kScrapFlavorTypeText )
230 {
231 // this increment is only valid for allocating:
232 // on the next GetFlavorData call it is reset again to the original value
233 dataSize++;
234 }
235 else if ( theType == kScrapFlavorTypeUnicode )
236 {
237 // this increment is only valid for allocating:
238 // on the next GetFlavorData call it is reset again to the original value
239 dataSize++;
240 dataSize++;
241 }
242
243 if (dataSize > 0)
244 theData = new char[dataSize];
245 else
246 theData = NULL;
247
248 GetFlavorData( (DragReference)m_currentDrag, theItem, theType, (void*) theData, &dataSize, 0L );
249 switch (theType)
250 {
251 case kScrapFlavorTypeText:
252 theData[dataSize] = 0;
253 m_dataObject->SetData( wxDataFormat(wxDF_TEXT), dataSize, theData );
254 break;
255
256#if wxUSE_UNICODE
257 case kScrapFlavorTypeUnicode:
258 theData[dataSize + 0] =
259 theData[dataSize + 1] = 0;
260 m_dataObject->SetData( wxDataFormat(wxDF_UNICODETEXT), dataSize, theData );
261 break;
262#endif
263
264 case kDragFlavorTypeHFS:
265 {
266 if (theData != NULL)
267 {
268 HFSFlavor* theFile = (HFSFlavor*) theData;
269 wxString name = wxMacFSSpec2MacFilename( &theFile->fileSpec );
270
271 if (!name.IsEmpty())
272 filenamesPassed += name + wxT("\n");
273 }
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 if ( filenamesPassed.Len() > 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
305wxDropSource::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
316wxDropSource::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
329wxDropSource::~wxDropSource()
330{
331}
332
333wxDragResult wxDropSource::DoDragDrop(int flags)
334{
335 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
336
337 if (!m_data)
338 return (wxDragResult) wxDragNone;
339 if (m_data->GetFormatCount() == 0)
340 return (wxDragResult) wxDragNone;
341
342 OSStatus result;
343 DragReference theDrag;
344 RgnHandle dragRegion;
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
471bool 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
482bool 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
487pascal OSErr wxMacWindowDragTrackingHandler(
488 DragTrackingMessage theMessage, WindowPtr theWindow,
489 void *handlerRefCon, DragReference theDrag );
490pascal OSErr wxMacWindowDragReceiveHandler(
491 WindowPtr theWindow, void *handlerRefCon,
492 DragReference theDrag );
493
494void 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
510pascal 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 switch ( result )
623 {
624 case wxDragCopy:
625 {
626 wxCursor cursor(wxCURSOR_COPY_ARROW);
627 cursor.MacInstall();
628 }
629 break;
630
631 case wxDragMove:
632 {
633 wxCursor cursor(wxCURSOR_ARROW);
634 cursor.MacInstall();
635 }
636 break;
637
638 case wxDragNone:
639 {
640 wxCursor cursor(wxCURSOR_NO_ENTRY);
641 cursor.MacInstall();
642 }
643 break;
644
645 case wxDragError:
646 case wxDragLink:
647 case wxDragCancel:
648 default:
649 // put these here to make gcc happy
650 ;
651 }
652 }
653 }
654 }
655 break;
656
657 case kDragTrackingLeaveWindow:
658 if (trackingGlobals == NULL)
659 break;
660
661 if (trackingGlobals->m_currentTarget)
662 {
663 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
664 trackingGlobals->m_currentTarget->OnLeave();
665 HideDragHilite( theDrag );
666 trackingGlobals->m_currentTarget = NULL;
667 }
668 trackingGlobals->m_currentTargetWindow = NULL;
669 break;
670
671 default:
672 break;
673 }
674
675 return noErr;
676}
677
678pascal OSErr wxMacWindowDragReceiveHandler(
679 WindowPtr theWindow,
680 void *handlerRefCon,
681 DragReference theDrag)
682{
683 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
684 if ( trackingGlobals->m_currentTarget )
685 {
686 Point mouse, localMouse;
687 int localx, localy;
688
689 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
690 GetDragMouse( theDrag, &mouse, 0L );
691 localMouse = mouse;
692 GlobalToLocal( &localMouse );
693 localx = localMouse.h;
694 localy = localMouse.v;
695
696 // TODO : should we use client coordinates?
697 if ( trackingGlobals->m_currentTargetWindow )
698 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
699 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
700 {
701 bool optionDown = GetCurrentKeyModifiers() & optionKey;
702 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
703 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
704 }
705 }
706
707 return noErr;
708}
709
710#endif