osx new layout
[wxWidgets.git] / src / osx / carbon / dnd.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/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/osx/private.h"
25
26 // ----------------------------------------------------------------------------
27 // globals
28 // ----------------------------------------------------------------------------
29
30 typedef struct
31 {
32 wxWindow *m_currentTargetWindow;
33 wxDropTarget *m_currentTarget;
34 wxDropSource *m_currentSource;
35 wxDragResult m_result;
36 int m_flags;
37 } MacTrackingGlobals;
38
39 MacTrackingGlobals gTrackingGlobals;
40
41 void wxMacEnsureTrackingHandlersInstalled();
42
43 //----------------------------------------------------------------------------
44 // wxDropTarget
45 //----------------------------------------------------------------------------
46
47 wxDropTarget::wxDropTarget( wxDataObject *data )
48 : wxDropTargetBase( data )
49 {
50 wxMacEnsureTrackingHandlersInstalled();
51 }
52
53 wxDragResult wxDropTarget::OnDragOver(
54 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
55 wxDragResult def )
56 {
57 return CurrentDragHasSupportedFormat() ? def : wxDragNone;
58 }
59
60 bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
61 {
62 if (m_dataObject == NULL)
63 return false;
64
65 return CurrentDragHasSupportedFormat();
66 }
67
68 wxDragResult wxDropTarget::OnData(
69 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
70 wxDragResult def )
71 {
72 if (m_dataObject == NULL)
73 return wxDragNone;
74
75 if (!CurrentDragHasSupportedFormat())
76 return wxDragNone;
77
78 return GetData() ? def : wxDragNone;
79 }
80
81 bool wxDropTarget::CurrentDragHasSupportedFormat()
82 {
83 bool supported = false;
84 if (m_dataObject == NULL)
85 return false;
86
87 if ( gTrackingGlobals.m_currentSource != NULL )
88 {
89 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject();
90
91 if ( data )
92 {
93 size_t formatcount = data->GetFormatCount();
94 wxDataFormat *array = new wxDataFormat[formatcount];
95 data->GetAllFormats( array );
96 for (size_t i = 0; !supported && i < formatcount; i++)
97 {
98 wxDataFormat format = array[i];
99 if ( m_dataObject->IsSupported( format ) )
100 {
101 supported = true;
102 break;
103 }
104 }
105
106 delete [] array;
107 }
108 }
109
110 if ( !supported )
111 {
112 PasteboardRef pasteboard;
113
114 if ( GetDragPasteboard( (DragReference)m_currentDrag, &pasteboard ) == noErr )
115 {
116 supported = m_dataObject->HasDataInPasteboard( pasteboard );
117 }
118 }
119
120 return supported;
121 }
122
123 bool wxDropTarget::GetData()
124 {
125 if (m_dataObject == NULL)
126 return false;
127
128 if ( !CurrentDragHasSupportedFormat() )
129 return false;
130
131 bool transferred = false;
132 if ( gTrackingGlobals.m_currentSource != NULL )
133 {
134 wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject();
135
136 if (data != NULL)
137 {
138 size_t formatcount = data->GetFormatCount();
139 wxDataFormat *array = new wxDataFormat[formatcount];
140 data->GetAllFormats( array );
141 for (size_t i = 0; !transferred && i < formatcount; i++)
142 {
143 wxDataFormat format = array[i];
144 if ( m_dataObject->IsSupported( format ) )
145 {
146 int size = data->GetDataSize( format );
147 transferred = true;
148
149 if (size == 0)
150 {
151 m_dataObject->SetData( format, 0, 0 );
152 }
153 else
154 {
155 char *d = new char[size];
156 data->GetDataHere( format, (void*)d );
157 m_dataObject->SetData( format, size, d );
158 delete [] d;
159 }
160 }
161 }
162
163 delete [] array;
164 }
165 }
166
167 if ( !transferred )
168 {
169 PasteboardRef pasteboard;
170
171 if ( GetDragPasteboard( (DragReference)m_currentDrag, &pasteboard ) == noErr )
172 {
173 transferred = m_dataObject->GetFromPasteboard( pasteboard );
174 }
175 }
176
177 return transferred;
178 }
179
180 //-------------------------------------------------------------------------
181 // wxDropSource
182 //-------------------------------------------------------------------------
183
184 //-----------------------------------------------------------------------------
185 // drag request
186
187 wxDropSource::wxDropSource(wxWindow *win,
188 const wxCursor &cursorCopy,
189 const wxCursor &cursorMove,
190 const wxCursor &cursorStop)
191 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
192 {
193 wxMacEnsureTrackingHandlersInstalled();
194
195 m_window = win;
196 }
197
198 wxDropSource::wxDropSource(wxDataObject& data,
199 wxWindow *win,
200 const wxCursor &cursorCopy,
201 const wxCursor &cursorMove,
202 const wxCursor &cursorStop)
203 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
204 {
205 wxMacEnsureTrackingHandlersInstalled();
206
207 SetData( data );
208 m_window = win;
209 }
210
211 wxDropSource::~wxDropSource()
212 {
213 }
214
215 OSStatus wxMacPromiseKeeper(PasteboardRef WXUNUSED(inPasteboard),
216 PasteboardItemID WXUNUSED(inItem),
217 CFStringRef WXUNUSED(inFlavorType),
218 void * WXUNUSED(inContext))
219 {
220 OSStatus err = noErr;
221
222 // we might add promises here later, inContext is the wxDropSource*
223
224 return err;
225 }
226
227 wxDragResult wxDropSource::DoDragDrop(int flags)
228 {
229 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
230
231 if ((m_data == NULL) || (m_data->GetFormatCount() == 0))
232 return (wxDragResult)wxDragNone;
233
234 DragReference theDrag;
235 RgnHandle dragRegion;
236 OSStatus err = noErr;
237 PasteboardRef pasteboard;
238
239 // add data to drag
240
241 err = PasteboardCreate( kPasteboardUniqueName, &pasteboard );
242 if ( err != noErr )
243 return wxDragNone;
244
245 // we add a dummy promise keeper because of strange messages when linking against carbon debug
246 err = PasteboardSetPromiseKeeper( pasteboard, wxMacPromiseKeeper, this );
247 if ( err != noErr )
248 {
249 CFRelease( pasteboard );
250 return wxDragNone;
251 }
252
253 err = PasteboardClear( pasteboard );
254 if ( err != noErr )
255 {
256 CFRelease( pasteboard );
257 return wxDragNone;
258 }
259 PasteboardSynchronize( pasteboard );
260
261 m_data->AddToPasteboard( pasteboard, 1 );
262
263 if (NewDragWithPasteboard( pasteboard , &theDrag) != noErr)
264 {
265 CFRelease( pasteboard );
266 return wxDragNone;
267 }
268
269 dragRegion = NewRgn();
270 RgnHandle tempRgn = NewRgn();
271
272 EventRecord rec;
273 ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec );
274
275 const short dragRegionOuterBoundary = 10;
276 const short dragRegionInnerBoundary = 9;
277
278 SetRectRgn(
279 dragRegion,
280 rec.where.h - dragRegionOuterBoundary,
281 rec.where.v - dragRegionOuterBoundary,
282 rec.where.h + dragRegionOuterBoundary,
283 rec.where.v + dragRegionOuterBoundary );
284
285 SetRectRgn(
286 tempRgn,
287 rec.where.h - dragRegionInnerBoundary,
288 rec.where.v - dragRegionInnerBoundary,
289 rec.where.h + dragRegionInnerBoundary,
290 rec.where.v + dragRegionInnerBoundary );
291
292 DiffRgn( dragRegion, tempRgn, dragRegion );
293 DisposeRgn( tempRgn );
294
295 // TODO: work with promises in order to return data
296 // only when drag was successfully completed
297
298 gTrackingGlobals.m_currentSource = this;
299 gTrackingGlobals.m_result = wxDragNone;
300 gTrackingGlobals.m_flags = flags;
301
302 err = TrackDrag( theDrag, &rec, dragRegion );
303
304 DisposeRgn( dragRegion );
305 DisposeDrag( theDrag );
306 CFRelease( pasteboard );
307 gTrackingGlobals.m_currentSource = NULL;
308
309 return gTrackingGlobals.m_result;
310 }
311
312 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect)
313 {
314 const wxCursor& cursor = GetCursor(effect);
315 bool result = cursor.Ok();
316
317 if ( result )
318 cursor.MacInstall();
319
320 return result;
321 }
322
323 bool gTrackingGlobalsInstalled = false;
324
325 // passing the globals via refcon is not needed by the CFM and later architectures anymore
326 // but I'll leave it in there, just in case...
327
328 pascal OSErr wxMacWindowDragTrackingHandler(
329 DragTrackingMessage theMessage, WindowPtr theWindow,
330 void *handlerRefCon, DragReference theDrag );
331 pascal OSErr wxMacWindowDragReceiveHandler(
332 WindowPtr theWindow, void *handlerRefCon,
333 DragReference theDrag );
334
335 void wxMacEnsureTrackingHandlersInstalled()
336 {
337 if ( !gTrackingGlobalsInstalled )
338 {
339 OSStatus err;
340
341 err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals );
342 verify_noerr( err );
343
344 err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals );
345 verify_noerr( err );
346
347 gTrackingGlobalsInstalled = true;
348 }
349 }
350
351 pascal OSErr wxMacWindowDragTrackingHandler(
352 DragTrackingMessage theMessage, WindowPtr theWindow,
353 void *handlerRefCon, DragReference theDrag )
354 {
355 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
356
357 Point mouse, localMouse;
358 DragAttributes attributes;
359
360 GetDragAttributes( theDrag, &attributes );
361
362 wxNonOwnedWindow* toplevel = wxNonOwnedWindow::GetFromWXWindow( (WXWindow) theWindow );
363
364 bool optionDown = GetCurrentKeyModifiers() & optionKey;
365 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
366
367 switch (theMessage)
368 {
369 case kDragTrackingEnterHandler:
370 case kDragTrackingLeaveHandler:
371 break;
372
373 case kDragTrackingEnterWindow:
374 if (trackingGlobals != NULL)
375 {
376 trackingGlobals->m_currentTargetWindow = NULL;
377 trackingGlobals->m_currentTarget = NULL;
378 }
379 break;
380
381 case kDragTrackingInWindow:
382 if (trackingGlobals == NULL)
383 break;
384 if (toplevel == NULL)
385 break;
386
387 GetDragMouse( theDrag, &mouse, 0L );
388 int x = mouse.h ;
389 int y = mouse.v ;
390 toplevel->GetNonOwnedPeer()->ScreenToWindow( &x, &y );
391 localMouse.h = x;
392 localMouse.v = y;
393
394 {
395 wxWindow *win = NULL;
396 ControlPartCode controlPart;
397 ControlRef control = FindControlUnderMouse( localMouse, theWindow, &controlPart );
398 if ( control )
399 win = wxFindWindowFromWXWidget( (WXWidget) control );
400 else
401 win = toplevel;
402
403 int localx, localy;
404 localx = localMouse.h;
405 localy = localMouse.v;
406
407 if ( win )
408 win->MacRootWindowToWindow( &localx, &localy );
409 if ( win != trackingGlobals->m_currentTargetWindow )
410 {
411 if ( trackingGlobals->m_currentTargetWindow )
412 {
413 // this window is left
414 if ( trackingGlobals->m_currentTarget )
415 {
416 #ifndef __LP64__
417 HideDragHilite( theDrag );
418 #endif
419 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
420 trackingGlobals->m_currentTarget->OnLeave();
421 trackingGlobals->m_currentTarget = NULL;
422 trackingGlobals->m_currentTargetWindow = NULL;
423 }
424 }
425
426 if ( win )
427 {
428 // this window is entered
429 trackingGlobals->m_currentTargetWindow = win;
430 trackingGlobals->m_currentTarget = win->GetDropTarget();
431 {
432 if ( trackingGlobals->m_currentTarget )
433 {
434 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
435 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
436 }
437
438 if ( result != wxDragNone )
439 {
440 int x, y;
441
442 x = y = 0;
443 win->MacWindowToRootWindow( &x, &y );
444 RgnHandle hiliteRgn = NewRgn();
445 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
446 RectRgn( hiliteRgn, &r );
447 #ifndef __LP64__
448 ShowDragHilite( theDrag, hiliteRgn, true );
449 #endif
450 DisposeRgn( hiliteRgn );
451 }
452 }
453 }
454 }
455 else
456 {
457 if ( trackingGlobals->m_currentTarget )
458 {
459 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
460 result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
461 }
462 }
463
464 // set cursor for OnEnter and OnDragOver
465 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
466 {
467 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
468 {
469 int cursorID = wxCURSOR_NONE;
470
471 switch (result)
472 {
473 case wxDragCopy:
474 cursorID = wxCURSOR_COPY_ARROW;
475 break;
476
477 case wxDragMove:
478 cursorID = wxCURSOR_ARROW;
479 break;
480
481 case wxDragNone:
482 cursorID = wxCURSOR_NO_ENTRY;
483 break;
484
485 case wxDragError:
486 case wxDragLink:
487 case wxDragCancel:
488 default:
489 // put these here to make gcc happy
490 ;
491 }
492
493 if (cursorID != wxCURSOR_NONE)
494 {
495 wxCursor cursor( cursorID );
496 cursor.MacInstall();
497 }
498 }
499 }
500 }
501 break;
502
503 case kDragTrackingLeaveWindow:
504 if (trackingGlobals == NULL)
505 break;
506
507 if (trackingGlobals->m_currentTarget)
508 {
509 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
510 trackingGlobals->m_currentTarget->OnLeave();
511 #ifndef __LP64__
512 HideDragHilite( theDrag );
513 #endif
514 trackingGlobals->m_currentTarget = NULL;
515 }
516 trackingGlobals->m_currentTargetWindow = NULL;
517 break;
518
519 default:
520 break;
521 }
522
523 return noErr;
524 }
525
526 pascal OSErr wxMacWindowDragReceiveHandler(
527 WindowPtr theWindow,
528 void *handlerRefCon,
529 DragReference theDrag)
530 {
531 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
532 if ( trackingGlobals->m_currentTarget )
533 {
534 Point mouse, localMouse;
535 int localx, localy;
536
537 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
538 GetDragMouse( theDrag, &mouse, 0L );
539 localMouse = mouse;
540 localx = localMouse.h;
541 localy = localMouse.v;
542 wxNonOwnedWindow* tlw = wxNonOwnedWindow::GetFromWXWindow((WXWindow) theWindow);
543 if ( tlw )
544 tlw->GetNonOwnedPeer()->ScreenToWindow( &localx, &localy );
545
546 // TODO : should we use client coordinates?
547 if ( trackingGlobals->m_currentTargetWindow )
548 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
549 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
550 {
551 // the option key indicates copy in Mac UI, if it's not pressed do
552 // move by default if it's allowed at all
553 wxDragResult
554 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) ||
555 (GetCurrentKeyModifiers() & optionKey)
556 ? wxDragCopy
557 : wxDragMove;
558 trackingGlobals->m_result =
559 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
560 }
561 }
562
563 return noErr;
564 }
565
566 #endif // wxUSE_DRAG_AND_DROP
567