]> git.saurik.com Git - wxWidgets.git/blob - src/osx/carbon/dnd.cpp
notify the event loop that synthesized events are on the queue, wait for them to...
[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 OSStatus wxMacPromiseKeeper(PasteboardRef WXUNUSED(inPasteboard),
44 PasteboardItemID WXUNUSED(inItem),
45 CFStringRef WXUNUSED(inFlavorType),
46 void * WXUNUSED(inContext))
47 {
48 OSStatus err = noErr;
49
50 // we might add promises here later, inContext is the wxDropSource*
51
52 return err;
53 }
54
55 wxDropTarget::wxDropTarget( wxDataObject *data )
56 : wxDropTargetBase( data )
57 {
58 wxMacEnsureTrackingHandlersInstalled();
59 }
60
61
62 //-------------------------------------------------------------------------
63 // wxDropSource
64 //-------------------------------------------------------------------------
65
66 wxDropSource::wxDropSource(wxWindow *win,
67 const wxCursor &cursorCopy,
68 const wxCursor &cursorMove,
69 const wxCursor &cursorStop)
70 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
71 {
72 wxMacEnsureTrackingHandlersInstalled();
73
74 m_window = win;
75 }
76
77 wxDropSource* wxDropSource::GetCurrentDropSource()
78 {
79 return gTrackingGlobals.m_currentSource;
80 }
81
82 wxDropSource::wxDropSource(wxDataObject& data,
83 wxWindow *win,
84 const wxCursor &cursorCopy,
85 const wxCursor &cursorMove,
86 const wxCursor &cursorStop)
87 : wxDropSourceBase(cursorCopy, cursorMove, cursorStop)
88 {
89 wxMacEnsureTrackingHandlersInstalled();
90
91 SetData( data );
92 m_window = win;
93 }
94
95 wxDragResult wxDropSource::DoDragDrop(int flags)
96 {
97 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
98
99 if ((m_data == NULL) || (m_data->GetFormatCount() == 0))
100 return (wxDragResult)wxDragNone;
101
102 DragReference theDrag;
103 RgnHandle dragRegion;
104 OSStatus err = noErr;
105 PasteboardRef pasteboard;
106
107 // add data to drag
108
109 err = PasteboardCreate( kPasteboardUniqueName, &pasteboard );
110 if ( err != noErr )
111 return wxDragNone;
112
113 // we add a dummy promise keeper because of strange messages when linking against carbon debug
114 err = PasteboardSetPromiseKeeper( pasteboard, wxMacPromiseKeeper, this );
115 if ( err != noErr )
116 {
117 CFRelease( pasteboard );
118 return wxDragNone;
119 }
120
121 err = PasteboardClear( pasteboard );
122 if ( err != noErr )
123 {
124 CFRelease( pasteboard );
125 return wxDragNone;
126 }
127 PasteboardSynchronize( pasteboard );
128
129 m_data->AddToPasteboard( pasteboard, 1 );
130
131 if (NewDragWithPasteboard( pasteboard , &theDrag) != noErr)
132 {
133 CFRelease( pasteboard );
134 return wxDragNone;
135 }
136
137 dragRegion = NewRgn();
138 RgnHandle tempRgn = NewRgn();
139
140 EventRecord rec;
141 ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec );
142
143 const short dragRegionOuterBoundary = 10;
144 const short dragRegionInnerBoundary = 9;
145
146 SetRectRgn(
147 dragRegion,
148 rec.where.h - dragRegionOuterBoundary,
149 rec.where.v - dragRegionOuterBoundary,
150 rec.where.h + dragRegionOuterBoundary,
151 rec.where.v + dragRegionOuterBoundary );
152
153 SetRectRgn(
154 tempRgn,
155 rec.where.h - dragRegionInnerBoundary,
156 rec.where.v - dragRegionInnerBoundary,
157 rec.where.h + dragRegionInnerBoundary,
158 rec.where.v + dragRegionInnerBoundary );
159
160 DiffRgn( dragRegion, tempRgn, dragRegion );
161 DisposeRgn( tempRgn );
162
163 // TODO: work with promises in order to return data
164 // only when drag was successfully completed
165
166 gTrackingGlobals.m_currentSource = this;
167 gTrackingGlobals.m_result = wxDragNone;
168 gTrackingGlobals.m_flags = flags;
169
170 err = TrackDrag( theDrag, &rec, dragRegion );
171
172 DisposeRgn( dragRegion );
173 DisposeDrag( theDrag );
174 CFRelease( pasteboard );
175 gTrackingGlobals.m_currentSource = NULL;
176
177 return gTrackingGlobals.m_result;
178 }
179
180 bool gTrackingGlobalsInstalled = false;
181
182 // passing the globals via refcon is not needed by the CFM and later architectures anymore
183 // but I'll leave it in there, just in case...
184
185 pascal OSErr wxMacWindowDragTrackingHandler(
186 DragTrackingMessage theMessage, WindowPtr theWindow,
187 void *handlerRefCon, DragReference theDrag );
188 pascal OSErr wxMacWindowDragReceiveHandler(
189 WindowPtr theWindow, void *handlerRefCon,
190 DragReference theDrag );
191
192 void wxMacEnsureTrackingHandlersInstalled()
193 {
194 if ( !gTrackingGlobalsInstalled )
195 {
196 OSStatus err;
197
198 err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals );
199 verify_noerr( err );
200
201 err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals );
202 verify_noerr( err );
203
204 gTrackingGlobalsInstalled = true;
205 }
206 }
207
208 pascal OSErr wxMacWindowDragTrackingHandler(
209 DragTrackingMessage theMessage, WindowPtr theWindow,
210 void *handlerRefCon, DragReference theDrag )
211 {
212 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
213
214 Point mouse, localMouse;
215 DragAttributes attributes;
216
217 GetDragAttributes( theDrag, &attributes );
218 PasteboardRef pasteboard = 0;
219 GetDragPasteboard( theDrag, &pasteboard );
220 wxNonOwnedWindow* toplevel = wxNonOwnedWindow::GetFromWXWindow( (WXWindow) theWindow );
221
222 bool optionDown = GetCurrentKeyModifiers() & optionKey;
223 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
224
225 switch (theMessage)
226 {
227 case kDragTrackingEnterHandler:
228 case kDragTrackingLeaveHandler:
229 break;
230
231 case kDragTrackingEnterWindow:
232 if (trackingGlobals != NULL)
233 {
234 trackingGlobals->m_currentTargetWindow = NULL;
235 trackingGlobals->m_currentTarget = NULL;
236 }
237 break;
238
239 case kDragTrackingInWindow:
240 if (trackingGlobals == NULL)
241 break;
242 if (toplevel == NULL)
243 break;
244
245 GetDragMouse( theDrag, &mouse, 0L );
246 {
247 int x = mouse.h ;
248 int y = mouse.v ;
249 toplevel->GetNonOwnedPeer()->ScreenToWindow( &x, &y );
250 localMouse.h = x;
251 localMouse.v = y;
252
253 {
254 wxWindow *win = NULL;
255 ControlPartCode controlPart;
256 ControlRef control = FindControlUnderMouse( localMouse, theWindow, &controlPart );
257 if ( control )
258 win = wxFindWindowFromWXWidget( (WXWidget) control );
259 else
260 win = toplevel;
261
262 int localx, localy;
263 localx = localMouse.h;
264 localy = localMouse.v;
265
266 if ( win )
267 win->MacRootWindowToWindow( &localx, &localy );
268 if ( win != trackingGlobals->m_currentTargetWindow )
269 {
270 if ( trackingGlobals->m_currentTargetWindow )
271 {
272 // this window is left
273 if ( trackingGlobals->m_currentTarget )
274 {
275 HideDragHilite( theDrag );
276 trackingGlobals->m_currentTarget->SetCurrentDragPasteboard( pasteboard );
277 trackingGlobals->m_currentTarget->OnLeave();
278 trackingGlobals->m_currentTarget = NULL;
279 trackingGlobals->m_currentTargetWindow = NULL;
280 }
281 }
282
283 if ( win )
284 {
285 // this window is entered
286 trackingGlobals->m_currentTargetWindow = win;
287 trackingGlobals->m_currentTarget = win->GetDropTarget();
288 {
289 if ( trackingGlobals->m_currentTarget )
290 {
291 trackingGlobals->m_currentTarget->SetCurrentDragPasteboard( pasteboard );
292 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
293 }
294
295 if ( result != wxDragNone )
296 {
297 int x, y;
298
299 x = y = 0;
300 win->MacWindowToRootWindow( &x, &y );
301 RgnHandle hiliteRgn = NewRgn();
302 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
303 RectRgn( hiliteRgn, &r );
304 ShowDragHilite( theDrag, hiliteRgn, true );
305 DisposeRgn( hiliteRgn );
306 }
307 }
308 }
309 }
310 else
311 {
312 if ( trackingGlobals->m_currentTarget )
313 {
314 trackingGlobals->m_currentTarget->SetCurrentDragPasteboard( pasteboard );
315 result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
316 }
317 }
318
319 // set cursor for OnEnter and OnDragOver
320 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
321 {
322 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
323 {
324 wxStockCursor cursorID = wxCURSOR_NONE;
325
326 switch (result)
327 {
328 case wxDragCopy:
329 cursorID = wxCURSOR_COPY_ARROW;
330 break;
331
332 case wxDragMove:
333 cursorID = wxCURSOR_ARROW;
334 break;
335
336 case wxDragNone:
337 cursorID = wxCURSOR_NO_ENTRY;
338 break;
339
340 case wxDragError:
341 case wxDragLink:
342 case wxDragCancel:
343 default:
344 // put these here to make gcc happy
345 ;
346 }
347
348 if (cursorID != wxCURSOR_NONE)
349 {
350 wxCursor cursor( cursorID );
351 cursor.MacInstall();
352 }
353 }
354 }
355 }
356 }
357 break;
358
359 case kDragTrackingLeaveWindow:
360 if (trackingGlobals == NULL)
361 break;
362
363 if (trackingGlobals->m_currentTarget)
364 {
365 trackingGlobals->m_currentTarget->SetCurrentDragPasteboard( pasteboard );
366 trackingGlobals->m_currentTarget->OnLeave();
367 HideDragHilite( theDrag );
368 trackingGlobals->m_currentTarget = NULL;
369 }
370 trackingGlobals->m_currentTargetWindow = NULL;
371 break;
372
373 default:
374 break;
375 }
376
377 return noErr;
378 }
379
380 pascal OSErr wxMacWindowDragReceiveHandler(
381 WindowPtr theWindow,
382 void *handlerRefCon,
383 DragReference theDrag)
384 {
385 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
386 if ( trackingGlobals->m_currentTarget )
387 {
388 Point mouse, localMouse;
389 int localx, localy;
390
391 PasteboardRef pasteboard = 0;
392 GetDragPasteboard( theDrag, &pasteboard );
393 trackingGlobals->m_currentTarget->SetCurrentDragPasteboard( pasteboard );
394 GetDragMouse( theDrag, &mouse, 0L );
395 localMouse = mouse;
396 localx = localMouse.h;
397 localy = localMouse.v;
398 wxNonOwnedWindow* tlw = wxNonOwnedWindow::GetFromWXWindow((WXWindow) theWindow);
399 if ( tlw )
400 tlw->GetNonOwnedPeer()->ScreenToWindow( &localx, &localy );
401
402 // TODO : should we use client coordinates?
403 if ( trackingGlobals->m_currentTargetWindow )
404 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
405 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
406 {
407 // the option key indicates copy in Mac UI, if it's not pressed do
408 // move by default if it's allowed at all
409 wxDragResult
410 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) ||
411 (GetCurrentKeyModifiers() & optionKey)
412 ? wxDragCopy
413 : wxDragMove;
414 trackingGlobals->m_result =
415 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
416 }
417 }
418
419 return noErr;
420 }
421
422 #endif // wxUSE_DRAG_AND_DROP
423