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