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