]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/dnd.cpp
weak link test is not optimal, as CoreText is already having some symbols defined...
[wxWidgets.git] / src / mac / carbon / dnd.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/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/mac/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 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( 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 localMouse = mouse;
389 wxMacGlobalToLocal( theWindow, &localMouse );
390
391 {
392 wxWindow *win = NULL;
393 ControlPartCode controlPart;
394 ControlRef control = FindControlUnderMouse( localMouse, theWindow, &controlPart );
395 if ( control )
396 win = wxFindControlFromMacControl( control );
397 else
398 win = toplevel;
399
400 int localx, localy;
401 localx = localMouse.h;
402 localy = localMouse.v;
403
404 if ( win )
405 win->MacRootWindowToWindow( &localx, &localy );
406 if ( win != trackingGlobals->m_currentTargetWindow )
407 {
408 if ( trackingGlobals->m_currentTargetWindow )
409 {
410 // this window is left
411 if ( trackingGlobals->m_currentTarget )
412 {
413 #ifndef __LP64__
414 HideDragHilite( theDrag );
415 #endif
416 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
417 trackingGlobals->m_currentTarget->OnLeave();
418 trackingGlobals->m_currentTarget = NULL;
419 trackingGlobals->m_currentTargetWindow = NULL;
420 }
421 }
422
423 if ( win )
424 {
425 // this window is entered
426 trackingGlobals->m_currentTargetWindow = win;
427 trackingGlobals->m_currentTarget = win->GetDropTarget();
428 {
429 if ( trackingGlobals->m_currentTarget )
430 {
431 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
432 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
433 }
434
435 if ( result != wxDragNone )
436 {
437 int x, y;
438
439 x = y = 0;
440 win->MacWindowToRootWindow( &x, &y );
441 RgnHandle hiliteRgn = NewRgn();
442 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
443 RectRgn( hiliteRgn, &r );
444 #ifndef __LP64__
445 ShowDragHilite( theDrag, hiliteRgn, true );
446 #endif
447 DisposeRgn( hiliteRgn );
448 }
449 }
450 }
451 }
452 else
453 {
454 if ( trackingGlobals->m_currentTarget )
455 {
456 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
457 result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
458 }
459 }
460
461 // set cursor for OnEnter and OnDragOver
462 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
463 {
464 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
465 {
466 int cursorID = wxCURSOR_NONE;
467
468 switch (result)
469 {
470 case wxDragCopy:
471 cursorID = wxCURSOR_COPY_ARROW;
472 break;
473
474 case wxDragMove:
475 cursorID = wxCURSOR_ARROW;
476 break;
477
478 case wxDragNone:
479 cursorID = wxCURSOR_NO_ENTRY;
480 break;
481
482 case wxDragError:
483 case wxDragLink:
484 case wxDragCancel:
485 default:
486 // put these here to make gcc happy
487 ;
488 }
489
490 if (cursorID != wxCURSOR_NONE)
491 {
492 wxCursor cursor( cursorID );
493 cursor.MacInstall();
494 }
495 }
496 }
497 }
498 break;
499
500 case kDragTrackingLeaveWindow:
501 if (trackingGlobals == NULL)
502 break;
503
504 if (trackingGlobals->m_currentTarget)
505 {
506 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
507 trackingGlobals->m_currentTarget->OnLeave();
508 #ifndef __LP64__
509 HideDragHilite( theDrag );
510 #endif
511 trackingGlobals->m_currentTarget = NULL;
512 }
513 trackingGlobals->m_currentTargetWindow = NULL;
514 break;
515
516 default:
517 break;
518 }
519
520 return noErr;
521 }
522
523 pascal OSErr wxMacWindowDragReceiveHandler(
524 WindowPtr theWindow,
525 void *handlerRefCon,
526 DragReference theDrag)
527 {
528 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
529 if ( trackingGlobals->m_currentTarget )
530 {
531 Point mouse, localMouse;
532 int localx, localy;
533
534 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
535 GetDragMouse( theDrag, &mouse, 0L );
536 localMouse = mouse;
537 wxMacGlobalToLocal( theWindow, &localMouse );
538 localx = localMouse.h;
539 localy = localMouse.v;
540
541 // TODO : should we use client coordinates?
542 if ( trackingGlobals->m_currentTargetWindow )
543 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
544 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
545 {
546 // the option key indicates copy in Mac UI, if it's not pressed do
547 // move by default if it's allowed at all
548 wxDragResult
549 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) ||
550 (GetCurrentKeyModifiers() & optionKey)
551 ? wxDragCopy
552 : wxDragMove;
553 trackingGlobals->m_result =
554 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
555 }
556 }
557
558 return noErr;
559 }
560
561 #endif // wxUSE_DRAG_AND_DROP
562