]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/dnd.cpp
compare colours using their operator==() instead of comparing individual RGB componen...
[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 inPasteboard, PasteboardItemID inItem, CFStringRef inFlavorType,
216 void *inContext )
217 {
218 OSStatus err = noErr;
219
220 // we might add promises here later, inContext is the wxDropSource*
221
222 return err;
223 }
224
225 wxDragResult wxDropSource::DoDragDrop(int flags)
226 {
227 wxASSERT_MSG( m_data, wxT("Drop source: no data") );
228
229 if ((m_data == NULL) || (m_data->GetFormatCount() == 0))
230 return (wxDragResult)wxDragNone;
231
232 DragReference theDrag;
233 RgnHandle dragRegion;
234 OSStatus err = noErr;
235 PasteboardRef pasteboard;
236
237 // add data to drag
238
239 err = PasteboardCreate( kPasteboardUniqueName, &pasteboard );
240 if ( err != noErr )
241 return wxDragNone;
242
243 // we add a dummy promise keeper because of strange messages when linking against carbon debug
244 err = PasteboardSetPromiseKeeper( pasteboard, wxMacPromiseKeeper, this );
245 if ( err != noErr )
246 {
247 CFRelease( pasteboard );
248 return wxDragNone;
249 }
250
251 err = PasteboardClear( pasteboard );
252 if ( err != noErr )
253 {
254 CFRelease( pasteboard );
255 return wxDragNone;
256 }
257 PasteboardSynchronize( pasteboard );
258
259 m_data->AddToPasteboard( pasteboard, 1 );
260
261 if (NewDragWithPasteboard( pasteboard , &theDrag) != noErr)
262 {
263 CFRelease( pasteboard );
264 return wxDragNone;
265 }
266
267 dragRegion = NewRgn();
268 RgnHandle tempRgn = NewRgn();
269
270 EventRecord rec;
271 ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec );
272
273 const short dragRegionOuterBoundary = 10;
274 const short dragRegionInnerBoundary = 9;
275
276 SetRectRgn(
277 dragRegion,
278 rec.where.h - dragRegionOuterBoundary,
279 rec.where.v - dragRegionOuterBoundary,
280 rec.where.h + dragRegionOuterBoundary,
281 rec.where.v + dragRegionOuterBoundary );
282
283 SetRectRgn(
284 tempRgn,
285 rec.where.h - dragRegionInnerBoundary,
286 rec.where.v - dragRegionInnerBoundary,
287 rec.where.h + dragRegionInnerBoundary,
288 rec.where.v + dragRegionInnerBoundary );
289
290 DiffRgn( dragRegion, tempRgn, dragRegion );
291 DisposeRgn( tempRgn );
292
293 // TODO: work with promises in order to return data
294 // only when drag was successfully completed
295
296 gTrackingGlobals.m_currentSource = this;
297 gTrackingGlobals.m_result = wxDragNone;
298 gTrackingGlobals.m_flags = flags;
299
300 err = TrackDrag( theDrag, &rec, dragRegion );
301
302 DisposeRgn( dragRegion );
303 DisposeDrag( theDrag );
304 CFRelease( pasteboard );
305 gTrackingGlobals.m_currentSource = NULL;
306
307 return gTrackingGlobals.m_result;
308 }
309
310 bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect)
311 {
312 const wxCursor& cursor = GetCursor(effect);
313 bool result = cursor.Ok();
314
315 if ( result )
316 cursor.MacInstall();
317
318 return result;
319 }
320
321 bool gTrackingGlobalsInstalled = false;
322
323 // passing the globals via refcon is not needed by the CFM and later architectures anymore
324 // but I'll leave it in there, just in case...
325
326 pascal OSErr wxMacWindowDragTrackingHandler(
327 DragTrackingMessage theMessage, WindowPtr theWindow,
328 void *handlerRefCon, DragReference theDrag );
329 pascal OSErr wxMacWindowDragReceiveHandler(
330 WindowPtr theWindow, void *handlerRefCon,
331 DragReference theDrag );
332
333 void wxMacEnsureTrackingHandlersInstalled()
334 {
335 if ( !gTrackingGlobalsInstalled )
336 {
337 OSStatus err;
338
339 err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals );
340 verify_noerr( err );
341
342 err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals );
343 verify_noerr( err );
344
345 gTrackingGlobalsInstalled = true;
346 }
347 }
348
349 pascal OSErr wxMacWindowDragTrackingHandler(
350 DragTrackingMessage theMessage, WindowPtr theWindow,
351 void *handlerRefCon, DragReference theDrag )
352 {
353 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon;
354
355 Point mouse, localMouse;
356 DragAttributes attributes;
357
358 GetDragAttributes( theDrag, &attributes );
359
360 wxTopLevelWindowMac* toplevel = wxFindWinFromMacWindow( theWindow );
361
362 bool optionDown = GetCurrentKeyModifiers() & optionKey;
363 wxDragResult result = optionDown ? wxDragCopy : wxDragMove;
364
365 switch (theMessage)
366 {
367 case kDragTrackingEnterHandler:
368 case kDragTrackingLeaveHandler:
369 break;
370
371 case kDragTrackingEnterWindow:
372 if (trackingGlobals != NULL)
373 {
374 trackingGlobals->m_currentTargetWindow = NULL;
375 trackingGlobals->m_currentTarget = NULL;
376 }
377 break;
378
379 case kDragTrackingInWindow:
380 if (trackingGlobals == NULL)
381 break;
382 if (toplevel == NULL)
383 break;
384
385 GetDragMouse( theDrag, &mouse, 0L );
386 localMouse = mouse;
387 wxMacGlobalToLocal( theWindow, &localMouse );
388
389 {
390 wxWindow *win = NULL;
391 ControlPartCode controlPart;
392 ControlRef control = FindControlUnderMouse( localMouse, theWindow, &controlPart );
393 if ( control )
394 win = wxFindControlFromMacControl( control );
395 else
396 win = toplevel;
397
398 int localx, localy;
399 localx = localMouse.h;
400 localy = localMouse.v;
401
402 if ( win )
403 win->MacRootWindowToWindow( &localx, &localy );
404 if ( win != trackingGlobals->m_currentTargetWindow )
405 {
406 if ( trackingGlobals->m_currentTargetWindow )
407 {
408 // this window is left
409 if ( trackingGlobals->m_currentTarget )
410 {
411 #ifndef __LP64__
412 HideDragHilite( theDrag );
413 #endif
414 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
415 trackingGlobals->m_currentTarget->OnLeave();
416 trackingGlobals->m_currentTarget = NULL;
417 trackingGlobals->m_currentTargetWindow = NULL;
418 }
419 }
420
421 if ( win )
422 {
423 // this window is entered
424 trackingGlobals->m_currentTargetWindow = win;
425 trackingGlobals->m_currentTarget = win->GetDropTarget();
426 {
427 if ( trackingGlobals->m_currentTarget )
428 {
429 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
430 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result );
431 }
432
433 if ( result != wxDragNone )
434 {
435 int x, y;
436
437 x = y = 0;
438 win->MacWindowToRootWindow( &x, &y );
439 RgnHandle hiliteRgn = NewRgn();
440 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x };
441 RectRgn( hiliteRgn, &r );
442 #ifndef __LP64__
443 ShowDragHilite( theDrag, hiliteRgn, true );
444 #endif
445 DisposeRgn( hiliteRgn );
446 }
447 }
448 }
449 }
450 else
451 {
452 if ( trackingGlobals->m_currentTarget )
453 {
454 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
455 result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result );
456 }
457 }
458
459 // set cursor for OnEnter and OnDragOver
460 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) )
461 {
462 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) )
463 {
464 int cursorID = wxCURSOR_NONE;
465
466 switch (result)
467 {
468 case wxDragCopy:
469 cursorID = wxCURSOR_COPY_ARROW;
470 break;
471
472 case wxDragMove:
473 cursorID = wxCURSOR_ARROW;
474 break;
475
476 case wxDragNone:
477 cursorID = wxCURSOR_NO_ENTRY;
478 break;
479
480 case wxDragError:
481 case wxDragLink:
482 case wxDragCancel:
483 default:
484 // put these here to make gcc happy
485 ;
486 }
487
488 if (cursorID != wxCURSOR_NONE)
489 {
490 wxCursor cursor( cursorID );
491 cursor.MacInstall();
492 }
493 }
494 }
495 }
496 break;
497
498 case kDragTrackingLeaveWindow:
499 if (trackingGlobals == NULL)
500 break;
501
502 if (trackingGlobals->m_currentTarget)
503 {
504 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
505 trackingGlobals->m_currentTarget->OnLeave();
506 #ifndef __LP64__
507 HideDragHilite( theDrag );
508 #endif
509 trackingGlobals->m_currentTarget = NULL;
510 }
511 trackingGlobals->m_currentTargetWindow = NULL;
512 break;
513
514 default:
515 break;
516 }
517
518 return noErr;
519 }
520
521 pascal OSErr wxMacWindowDragReceiveHandler(
522 WindowPtr theWindow,
523 void *handlerRefCon,
524 DragReference theDrag)
525 {
526 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon;
527 if ( trackingGlobals->m_currentTarget )
528 {
529 Point mouse, localMouse;
530 int localx, localy;
531
532 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag );
533 GetDragMouse( theDrag, &mouse, 0L );
534 localMouse = mouse;
535 wxMacGlobalToLocal( theWindow, &localMouse );
536 localx = localMouse.h;
537 localy = localMouse.v;
538
539 // TODO : should we use client coordinates?
540 if ( trackingGlobals->m_currentTargetWindow )
541 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy );
542 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) )
543 {
544 // the option key indicates copy in Mac UI, if it's not pressed do
545 // move by default if it's allowed at all
546 wxDragResult
547 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) ||
548 (GetCurrentKeyModifiers() & optionKey)
549 ? wxDragCopy
550 : wxDragMove;
551 trackingGlobals->m_result =
552 trackingGlobals->m_currentTarget->OnData( localx, localy, result );
553 }
554 }
555
556 return noErr;
557 }
558
559 #endif // wxUSE_DRAG_AND_DROP
560