]> git.saurik.com Git - wxWidgets.git/blob - src/mac/dnd.cpp
don't crash in UnselectAll() if the tree has no root
[wxWidgets.git] / src / mac / dnd.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: dnd.cpp
3 // Purpose: wxDropTarget, wxDropSource, wxDataObject implementation
4 // Author: AUTHOR
5 // Modified by:
6 // Created: ??/??/98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 AUTHOR
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "dnd.h"
14 #endif
15
16 #include "wx/defs.h"
17
18 #if wxUSE_DRAG_AND_DROP
19
20 #include "wx/dnd.h"
21 #include "wx/window.h"
22 #include "wx/toplevel.h"
23 #include "wx/app.h"
24 #include "wx/gdicmn.h"
25
26 // ----------------------------------------------------------------------------
27 // global
28 // ----------------------------------------------------------------------------
29
30 void wxMacEnsureTrackingHandlersInstalled() ;
31
32 typedef struct
33 {
34 wxWindow* m_currentTargetWindow ;
35 wxDropTarget* m_currentTarget ;
36 wxDropSource* m_currentSource ;
37 } MacTrackingGlobals ;
38
39 MacTrackingGlobals gTrackingGlobals ;
40
41 //----------------------------------------------------------------------------
42 // wxDropTarget
43 //----------------------------------------------------------------------------
44
45 wxDropTarget::wxDropTarget( wxDataObject *data )
46 : wxDropTargetBase( data )
47 {
48 wxMacEnsureTrackingHandlersInstalled() ;
49 }
50
51 wxDragResult wxDropTarget::OnDragOver( wxCoord WXUNUSED(x),
52 wxCoord WXUNUSED(y),
53 wxDragResult def )
54 {
55
56 return CurrentDragHasSupportedFormat() ? def : wxDragNone;
57 }
58
59 bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
60 {
61 if (!m_dataObject)
62 return FALSE;
63
64 return CurrentDragHasSupportedFormat() ;
65 }
66
67 wxDragResult wxDropTarget::OnData( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
68 wxDragResult def )
69 {
70 if (!m_dataObject)
71 return wxDragNone;
72
73 if (!CurrentDragHasSupportedFormat())
74 return wxDragNone;
75
76 return GetData() ? def : wxDragNone;
77 }
78
79 bool wxDropTarget::CurrentDragHasSupportedFormat()
80 {
81 bool supported = false ;
82 if ( gTrackingGlobals.m_currentSource != NULL )
83 {
84 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject() ;
85
86 if ( data )
87 {
88 int formatcount = data->GetFormatCount() ;
89 wxDataFormat *array = new wxDataFormat[ formatcount ];
90 data->GetAllFormats( array );
91 for (size_t i = 0; !supported && i < formatcount ; i++)
92 {
93 wxDataFormat format = array[i] ;
94 if ( m_dataObject->IsSupported( format ) )
95 {
96 supported = true ;
97 break ;
98 }
99 }
100 delete[] array ;
101 }
102 }
103 if ( !supported )
104 {
105 UInt16 items ;
106 OSErr result;
107 CountDragItems(m_currentDrag, &items);
108 for (UInt16 index = 1; index <= items && supported == false ; ++index)
109 {
110 ItemReference theItem;
111 FlavorType theType ;
112 UInt16 flavors = 0 ;
113 GetDragItemReferenceNumber(m_currentDrag, index, &theItem);
114 CountDragItemFlavors( m_currentDrag, theItem , &flavors ) ;
115 for ( UInt16 flavor = 1 ; flavor <= flavors ; ++flavor )
116 {
117 result = GetFlavorType(m_currentDrag, theItem, flavor , &theType);
118 if ( m_dataObject->IsSupportedFormat( wxDataFormat( theType ) ) )
119 {
120 supported = true ;
121 break ;
122 }
123 }
124 }
125 }
126 return supported ;
127 }
128
129 bool wxDropTarget::GetData()
130 {
131 if (!m_dataObject)
132 return FALSE;
133
134 if ( !CurrentDragHasSupportedFormat() )
135 return FALSE ;
136
137 bool transferred = false ;
138 if ( gTrackingGlobals.m_currentSource != NULL )
139 {
140 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject() ;
141
142 if ( data )
143 {
144 int formatcount = data->GetFormatCount() ;
145 wxDataFormat *array = new wxDataFormat[ formatcount ];
146 data->GetAllFormats( array );
147 for (size_t i = 0; !transferred && i < formatcount ; i++)
148 {
149 wxDataFormat format = array[i] ;
150 if ( m_dataObject->IsSupported( format ) )
151 {
152 int size = data->GetDataSize( format );
153 transferred = true ;
154
155 if (size == 0)
156 {
157 m_dataObject->SetData(format , 0 , 0 ) ;
158 }
159 else
160 {
161 char *d = new char[size];
162 data->GetDataHere( format , (void*) d );
163 m_dataObject->SetData( format , size , d ) ;
164 delete[] d ;
165 }
166 }
167 }
168 delete[] array ;
169 }
170 }
171 if ( !transferred )
172 {
173 UInt16 items ;
174 OSErr result;
175 CountDragItems(m_currentDrag, &items);
176 for (UInt16 index = 1; index <= items; ++index)
177 {
178 ItemReference theItem;
179 FlavorType theType ;
180 UInt16 flavors = 0 ;
181 GetDragItemReferenceNumber(m_currentDrag, index, &theItem);
182 CountDragItemFlavors( m_currentDrag, theItem , &flavors ) ;
183 for ( UInt16 flavor = 1 ; flavor <= flavors ; ++flavor )
184 {
185 result = GetFlavorType(m_currentDrag, theItem, flavor , &theType);
186 wxDataFormat format(theType) ;
187 if ( m_dataObject->IsSupportedFormat( format ) )
188 {
189 FlavorFlags theFlags;
190 result = GetFlavorFlags(m_currentDrag, theItem, theType, &theFlags);
191 if (result == noErr)
192 {
193 Size dataSize ;
194 Ptr theData ;
195 GetFlavorDataSize(m_currentDrag, theItem, theType, &dataSize);
196 if ( theType == 'TEXT' )
197 dataSize++ ;
198 theData = new char[dataSize];
199 GetFlavorData(m_currentDrag, theItem, theType, (void*) theData, &dataSize, 0L);
200 if( theType == 'TEXT' )
201 {
202 theData[dataSize]=0 ;
203 if ( wxApp::s_macDefaultEncodingIsPC )
204 {
205 wxMacConvertToPC((char*)theData) ;
206 }
207 m_dataObject->SetData( format, dataSize, theData );
208 }
209 else if ( theType == kDragFlavorTypeHFS )
210 {
211 HFSFlavor* theFile = (HFSFlavor*) theData ;
212 wxString name = wxMacFSSpec2MacFilename( &theFile->fileSpec ) ;
213 m_dataObject->SetData( format , name.Length() + 1, name ) ;
214 }
215 else
216 {
217 m_dataObject->SetData( format, dataSize, theData );
218 }
219 delete[] theData;
220 }
221 break ;
222 }
223 }
224 }
225 }
226 return TRUE ;
227 }
228
229 //-------------------------------------------------------------------------
230 // wxDropSource
231 //-------------------------------------------------------------------------
232
233 //-----------------------------------------------------------------------------
234 // drag request
235
236 wxDropSource::wxDropSource(wxWindow *win,
237 const wxIcon &iconCopy,
238 const wxIcon &iconMove,
239 const wxIcon &iconNone)
240 {
241 wxMacEnsureTrackingHandlersInstalled() ;
242 m_window = win;
243 }
244
245 wxDropSource::wxDropSource(wxDataObject& data,
246 wxWindow *win,
247 const wxIcon &iconCopy,
248 const wxIcon &iconMove,
249 const wxIcon &iconNone)
250 {
251 wxMacEnsureTrackingHandlersInstalled() ;
252 SetData( data );
253 m_window = win;
254 }
255
256 wxDropSource::~wxDropSource()
257 {
258 }
259
260
261 wxDragResult wxDropSource::DoDragDrop( bool allowMove )
262 {
263 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
264
265 if (!m_data)
266 return (wxDragResult) wxDragNone;
267
268 if (m_data->GetFormatCount() == 0)
269 return (wxDragResult) wxDragNone;
270
271 OSErr result;
272 DragReference theDrag;
273 RgnHandle dragRegion;
274 if (result = NewDrag(&theDrag))
275 {
276 return wxDragNone ;
277 }
278 // add data to drag
279 size_t formatCount = m_data->GetFormatCount() ;
280 wxDataFormat *formats = new wxDataFormat[formatCount] ;
281 m_data->GetAllFormats( formats ) ;
282 ItemReference theItem = 1 ;
283 for ( int i = 0 ; i < formatCount ; ++i )
284 {
285 size_t dataSize = m_data->GetDataSize( formats[i] ) ;
286 Ptr dataPtr = new char[dataSize] ;
287 m_data->GetDataHere( formats[i] , dataPtr ) ;
288 OSType type = formats[i].GetFormatId() ;
289 if ( type == 'TEXT' )
290 {
291 dataSize-- ;
292 if ( wxApp::s_macDefaultEncodingIsPC )
293 {
294 wxMacConvertFromPC((char*)dataPtr) ;
295 }
296 AddDragItemFlavor(theDrag, theItem, type , dataPtr, dataSize, 0);
297 }
298 else if (type == kDragFlavorTypeHFS )
299 {
300 HFSFlavor theFlavor ;
301 OSErr err = noErr;
302 CInfoPBRec cat;
303
304 wxMacFilename2FSSpec( dataPtr , &theFlavor.fileSpec ) ;
305
306 cat.hFileInfo.ioNamePtr = theFlavor.fileSpec.name;
307 cat.hFileInfo.ioVRefNum = theFlavor.fileSpec.vRefNum;
308 cat.hFileInfo.ioDirID = theFlavor.fileSpec.parID;
309 cat.hFileInfo.ioFDirIndex = 0;
310 err = PBGetCatInfoSync(&cat);
311 if (err == noErr )
312 {
313 theFlavor.fdFlags = cat.hFileInfo.ioFlFndrInfo.fdFlags;
314 if (theFlavor.fileSpec.parID == fsRtParID) {
315 theFlavor.fileCreator = 'MACS';
316 theFlavor.fileType = 'disk';
317 } else if ((cat.hFileInfo.ioFlAttrib & ioDirMask) != 0) {
318 theFlavor.fileCreator = 'MACS';
319 theFlavor.fileType = 'fold';
320 } else {
321 theFlavor.fileCreator = cat.hFileInfo.ioFlFndrInfo.fdCreator;
322 theFlavor.fileType = cat.hFileInfo.ioFlFndrInfo.fdType;
323 }
324 AddDragItemFlavor(theDrag, theItem, type , &theFlavor, sizeof(theFlavor), 0);
325 }
326 }
327 else
328 {
329 AddDragItemFlavor(theDrag, theItem, type , dataPtr, dataSize, 0);
330 }
331 delete[] dataPtr ;
332 }
333 delete[] formats ;
334
335 dragRegion = NewRgn();
336 RgnHandle tempRgn = NewRgn() ;
337
338 EventRecord* ev = wxTheApp->MacGetCurrentEvent() ;
339 const short dragRegionOuterBoundary = 10 ;
340 const short dragRegionInnerBoundary = 9 ;
341
342 SetRectRgn( dragRegion , ev->where.h - dragRegionOuterBoundary ,
343 ev->where.v - dragRegionOuterBoundary ,
344 ev->where.h + dragRegionOuterBoundary ,
345 ev->where.v + dragRegionOuterBoundary ) ;
346
347 SetRectRgn( tempRgn , ev->where.h - dragRegionInnerBoundary ,
348 ev->where.v - dragRegionInnerBoundary ,
349 ev->where.h + dragRegionInnerBoundary ,
350 ev->where.v + dragRegionInnerBoundary ) ;
351
352 DiffRgn( dragRegion , tempRgn , dragRegion ) ;
353 DisposeRgn( tempRgn ) ;
354
355 // TODO:work with promises in order to return data only when drag
356 // was successfully completed
357
358 gTrackingGlobals.m_currentSource = this ;
359 result = TrackDrag(theDrag, ev , dragRegion);
360 DisposeRgn(dragRegion);
361 DisposeDrag(theDrag);
362 gTrackingGlobals.m_currentSource = NULL ;
363
364 return wxDragCopy ;
365 }
366
367 bool gTrackingGlobalsInstalled = false ;
368
369 // passing the globals via refcon is not needed by the CFM and later architectures anymore
370 // but I'll leave it in there, just in case...
371
372 pascal OSErr wxMacWindowDragTrackingHandler(DragTrackingMessage theMessage, WindowPtr theWindow,
373 void *handlerRefCon, DragReference theDrag) ;
374 pascal OSErr wxMacWindowDragReceiveHandler(WindowPtr theWindow, void *handlerRefCon,
375 DragReference theDrag) ;
376
377 void wxMacEnsureTrackingHandlersInstalled()
378 {
379 if( !gTrackingGlobalsInstalled )
380 {
381 OSErr result;
382
383 result = InstallTrackingHandler(NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L,&gTrackingGlobals);
384 wxASSERT( result == noErr ) ;
385 result = InstallReceiveHandler(NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals);
386 wxASSERT( result == noErr ) ;
387
388 gTrackingGlobalsInstalled = true ;
389 }
390 }
391
392 pascal OSErr wxMacWindowDragTrackingHandler(DragTrackingMessage theMessage, WindowPtr theWindow,
393 void *handlerRefCon, DragReference theDrag)
394 {
395 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
396 Point mouse, localMouse;
397 DragAttributes attributes;
398 RgnHandle hiliteRgn;
399 GetDragAttributes(theDrag, &attributes);
400 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( theWindow ) ;
401 switch(theMessage)
402 {
403 case kDragTrackingEnterHandler:
404 break;
405 case kDragTrackingLeaveHandler:
406 break;
407 case kDragTrackingEnterWindow:
408 trackingGlobals->m_currentTargetWindow = NULL ;
409 trackingGlobals->m_currentTarget = NULL ;
410 break;
411 case kDragTrackingInWindow:
412 if (toplevel == NULL)
413 break;
414
415 GetDragMouse(theDrag, &mouse, 0L);
416 localMouse = mouse;
417 GlobalToLocal(&localMouse);
418
419 // if (attributes & kDragHasLeftSenderWindow)
420 {
421 wxPoint point(localMouse.h , localMouse.v) ;
422 wxWindow *win = NULL ;
423 toplevel->MacGetWindowFromPointSub( point , &win ) ;
424 int localx , localy ;
425 localx = localMouse.h ;
426 localy = localMouse.v ;
427 //TODO : should we use client coordinates
428 if ( win )
429 win->MacRootWindowToWindow( &localx , &localy ) ;
430 if ( win != trackingGlobals->m_currentTargetWindow )
431 {
432 if ( trackingGlobals->m_currentTargetWindow )
433 {
434 // this window is left
435 if ( trackingGlobals->m_currentTarget )
436 {
437 HideDragHilite(theDrag);
438 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
439 trackingGlobals->m_currentTarget->OnLeave() ;
440 trackingGlobals->m_currentTarget = NULL;
441 trackingGlobals->m_currentTargetWindow = NULL ;
442 }
443 }
444 if ( win )
445 {
446 // this window is entered
447 trackingGlobals->m_currentTargetWindow = win ;
448 trackingGlobals->m_currentTarget = win->GetDropTarget() ;
449 if ( trackingGlobals->m_currentTarget )
450 {
451 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
452 if ( trackingGlobals->m_currentTarget->OnEnter(
453 localx , localy , wxDragCopy ) != wxDragNone )
454 {
455 int x , y ;
456 x = y = 0 ;
457 win->MacWindowToRootWindow( &x , &y ) ;
458 RgnHandle hiliteRgn = NewRgn() ;
459 SetRectRgn( hiliteRgn , x , y , x+win->GetSize().x ,y+win->GetSize().y) ;
460 ShowDragHilite(theDrag, hiliteRgn, true);
461 DisposeRgn( hiliteRgn ) ;
462 }
463 }
464 }
465 }
466 else
467 {
468 if( trackingGlobals->m_currentTarget )
469 {
470 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
471 trackingGlobals->m_currentTarget->OnDragOver(
472 localx , localy , wxDragCopy ) ;
473 }
474 }
475
476 }
477 // MyTrackItemUnderMouse(localMouse, theWindow);
478 break;
479 case kDragTrackingLeaveWindow:
480 if (trackingGlobals->m_currentTarget)
481 {
482 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
483 trackingGlobals->m_currentTarget->OnLeave() ;
484 HideDragHilite(theDrag);
485 trackingGlobals->m_currentTarget = NULL ;
486 }
487 trackingGlobals->m_currentTargetWindow = NULL ;
488 break;
489 }
490 return(noErr);
491 }
492
493 pascal OSErr wxMacWindowDragReceiveHandler(WindowPtr theWindow, void *handlerRefCon,
494 DragReference theDrag)
495 {
496 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
497 if ( trackingGlobals->m_currentTarget )
498 {
499 Point mouse,localMouse ;
500 int localx,localy ;
501
502 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ) ;
503 GetDragMouse(theDrag, &mouse, 0L);
504 localMouse = mouse;
505 GlobalToLocal(&localMouse);
506 localx = localMouse.h ;
507 localy = localMouse.v ;
508 //TODO : should we use client coordinates
509 if ( trackingGlobals->m_currentTargetWindow )
510 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx , &localy ) ;
511 if ( trackingGlobals->m_currentTarget->OnDrop( localx , localy ) )
512 {
513 trackingGlobals->m_currentTarget->OnData( localx , localy , wxDragCopy ) ;
514 }
515 }
516 return(noErr);
517 }
518 #endif