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: dnd.cpp 46434 2007-06-13 04:20:40Z SC $ 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 30typedef 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 39MacTrackingGlobals gTrackingGlobals; 40 41void wxMacEnsureTrackingHandlersInstalled(); 42 43//---------------------------------------------------------------------------- 44// wxDropTarget 45//---------------------------------------------------------------------------- 46 47wxDropTarget::wxDropTarget( wxDataObject *data ) 48 : wxDropTargetBase( data ) 49{ 50 wxMacEnsureTrackingHandlersInstalled(); 51} 52 53wxDragResult wxDropTarget::OnDragOver( 54 wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), 55 wxDragResult def ) 56{ 57 return CurrentDragHasSupportedFormat() ? def : wxDragNone; 58} 59 60bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) ) 61{ 62 if (m_dataObject == NULL) 63 return false; 64 65 return CurrentDragHasSupportedFormat(); 66} 67 68wxDragResult 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 81bool 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 123bool 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 187wxDropSource::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 198wxDropSource::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 211wxDropSource::~wxDropSource() 212{ 213} 214 215OSStatus 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 225wxDragResult 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 310bool 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 321bool 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 326pascal OSErr wxMacWindowDragTrackingHandler( 327 DragTrackingMessage theMessage, WindowPtr theWindow, 328 void *handlerRefCon, DragReference theDrag ); 329pascal OSErr wxMacWindowDragReceiveHandler( 330 WindowPtr theWindow, void *handlerRefCon, 331 DragReference theDrag ); 332 333void 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 349pascal 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 wxWindowMac *win = NULL; 391 ControlPartCode controlPart; 392 ControlRef control = wxMacFindControlUnderMouse( 393 toplevel, localMouse, theWindow, &controlPart ); 394 if ( control ) 395 win = wxFindControlFromMacControl( control ); 396 else 397 win = toplevel; 398 399 int localx, localy; 400 localx = localMouse.h; 401 localy = localMouse.v; 402 403 if ( win ) 404 win->MacRootWindowToWindow( &localx, &localy ); 405 if ( win != trackingGlobals->m_currentTargetWindow ) 406 { 407 if ( trackingGlobals->m_currentTargetWindow ) 408 { 409 // this window is left 410 if ( trackingGlobals->m_currentTarget ) 411 { 412#ifndef __LP64__ 413 HideDragHilite( theDrag ); 414#endif 415 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); 416 trackingGlobals->m_currentTarget->OnLeave(); 417 trackingGlobals->m_currentTarget = NULL; 418 trackingGlobals->m_currentTargetWindow = NULL; 419 } 420 } 421 422 if ( win ) 423 { 424 // this window is entered 425 trackingGlobals->m_currentTargetWindow = (wxWindow*)win; 426 trackingGlobals->m_currentTarget = win->GetDropTarget(); 427 { 428 if ( trackingGlobals->m_currentTarget ) 429 { 430 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); 431 result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result ); 432 } 433 434 if ( result != wxDragNone ) 435 { 436 int x, y; 437 438 x = y = 0; 439 win->MacWindowToRootWindow( &x, &y ); 440 RgnHandle hiliteRgn = NewRgn(); 441 Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x }; 442 RectRgn( hiliteRgn, &r ); 443#ifndef __LP64__ 444 ShowDragHilite( theDrag, hiliteRgn, true ); 445#endif 446 DisposeRgn( hiliteRgn ); 447 } 448 } 449 } 450 } 451 else 452 { 453 if ( trackingGlobals->m_currentTarget ) 454 { 455 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); 456 result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result ); 457 } 458 } 459 460 // set cursor for OnEnter and OnDragOver 461 if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) ) 462 { 463 if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) ) 464 { 465 int cursorID = wxCURSOR_NONE; 466 467 switch (result) 468 { 469 case wxDragCopy: 470 cursorID = wxCURSOR_COPY_ARROW; 471 break; 472 473 case wxDragMove: 474 cursorID = wxCURSOR_ARROW; 475 break; 476 477 case wxDragNone: 478 cursorID = wxCURSOR_NO_ENTRY; 479 break; 480 481 case wxDragError: 482 case wxDragLink: 483 case wxDragCancel: 484 default: 485 // put these here to make gcc happy 486 ; 487 } 488 489 if (cursorID != wxCURSOR_NONE) 490 { 491 wxCursor cursor( cursorID ); 492 cursor.MacInstall(); 493 } 494 } 495 } 496 } 497 break; 498 499 case kDragTrackingLeaveWindow: 500 if (trackingGlobals == NULL) 501 break; 502 503 if (trackingGlobals->m_currentTarget) 504 { 505 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); 506 trackingGlobals->m_currentTarget->OnLeave(); 507#ifndef __LP64__ 508 HideDragHilite( theDrag ); 509#endif 510 trackingGlobals->m_currentTarget = NULL; 511 } 512 trackingGlobals->m_currentTargetWindow = NULL; 513 break; 514 515 default: 516 break; 517 } 518 519 return noErr; 520} 521 522pascal OSErr wxMacWindowDragReceiveHandler( 523 WindowPtr theWindow, 524 void *handlerRefCon, 525 DragReference theDrag) 526{ 527 MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon; 528 if ( trackingGlobals->m_currentTarget ) 529 { 530 Point mouse, localMouse; 531 int localx, localy; 532 533 trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); 534 GetDragMouse( theDrag, &mouse, 0L ); 535 localMouse = mouse; 536 wxMacGlobalToLocal( theWindow, &localMouse ); 537 localx = localMouse.h; 538 localy = localMouse.v; 539 540 // TODO : should we use client coordinates? 541 if ( trackingGlobals->m_currentTargetWindow ) 542 trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy ); 543 if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) ) 544 { 545 // the option key indicates copy in Mac UI, if it's not pressed do 546 // move by default if it's allowed at all 547 wxDragResult 548 result = !(trackingGlobals->m_flags & wxDrag_AllowMove) || 549 (GetCurrentKeyModifiers() & optionKey) 550 ? wxDragCopy 551 : wxDragMove; 552 trackingGlobals->m_result = 553 trackingGlobals->m_currentTarget->OnData( localx, localy, result ); 554 } 555 } 556 557 return noErr; 558} 559 560#endif // wxUSE_DRAG_AND_DROP 561 562