1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_vcl.hxx" 26 27 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> 28 #include <com/sun/star/datatransfer/XTransferable.hpp> 29 #include <com/sun/star/awt/MouseButton.hpp> 30 31 #include "rtl/unload.h" 32 #include "rtl/ustring.hxx" 33 34 #include "comphelper/makesequence.hxx" 35 36 #include "DragSource.hxx" 37 #include "DragSourceContext.hxx" 38 #include "aqua_clipboard.hxx" 39 #include "DragActionConversion.hxx" 40 41 #include "aqua/salframe.h" 42 43 #include <memory> 44 45 46 using namespace rtl; 47 using namespace cppu; 48 using namespace osl; 49 using namespace com::sun::star; 50 using namespace com::sun::star::datatransfer; 51 using namespace com::sun::star::datatransfer::clipboard; 52 using namespace com::sun::star::datatransfer::dnd; 53 using namespace com::sun::star::datatransfer::dnd::DNDConstants; 54 using namespace com::sun::star::uno; 55 using namespace com::sun::star::awt::MouseButton; 56 using namespace com::sun::star::awt; 57 using namespace com::sun::star::lang; 58 using namespace comphelper; 59 using namespace std; 60 61 62 // For OOo internal D&D we provide the Transferable without NSDragPboard 63 // interference as a shortcut 64 uno::Reference<XTransferable> DragSource::g_XTransferable; 65 NSView* DragSource::g_DragSourceView = nil; 66 bool DragSource::g_DropSuccessSet = false; 67 bool DragSource::g_DropSuccess = false; 68 69 70 OUString dragSource_getImplementationName() 71 { 72 return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.datatransfer.dnd.OleDragSource_V1")); 73 } 74 75 Sequence<OUString> dragSource_getSupportedServiceNames() 76 { 77 return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource"))); 78 } 79 80 81 @implementation DragSourceHelper; 82 83 -(DragSourceHelper*)initWithDragSource: (DragSource*) pds 84 { 85 self = [super init]; 86 87 if (self) 88 { 89 mDragSource = pds; 90 } 91 92 return self; 93 } 94 95 96 -(void)mouseDown: (NSEvent*)theEvent 97 { 98 mDragSource->saveMouseEvent(theEvent); 99 } 100 101 102 -(void)mouseDragged: (NSEvent*)theEvent 103 { 104 mDragSource->saveMouseEvent(theEvent); 105 } 106 107 108 -(unsigned int)draggingSourceOperationMaskForLocal: (BOOL)isLocal 109 { 110 return mDragSource->getSupportedDragOperations(isLocal); 111 } 112 113 114 -(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint 115 { 116 (void)anImage; 117 (void)aPoint; 118 DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource), 119 new DragSourceContext(mDragSource), 120 mDragSource, 121 DNDConstants::ACTION_COPY, 122 DNDConstants::ACTION_COPY); 123 124 mDragSource->mXDragSrcListener->dragEnter(dsde); 125 } 126 127 128 -(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation 129 { 130 (void)anImage; 131 (void)aPoint; 132 // an internal drop can accept the drop but fail with dropComplete( false ) 133 // this is different than the Cocoa API 134 bool bDropSuccess = operation != NSDragOperationNone; 135 if( DragSource::g_DropSuccessSet ) 136 bDropSuccess = DragSource::g_DropSuccess; 137 138 DragSourceDropEvent dsde(static_cast<OWeakObject*>(mDragSource), 139 new DragSourceContext(mDragSource), 140 static_cast< XDragSource* >(mDragSource), 141 SystemToOfficeDragActions(operation), 142 bDropSuccess ); 143 144 mDragSource->mXDragSrcListener->dragDropEnd(dsde); 145 mDragSource->mXDragSrcListener = uno::Reference<XDragSourceListener>(); 146 } 147 148 149 -(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint 150 { 151 (void)draggedImage; 152 (void)screenPoint; 153 DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource), 154 new DragSourceContext(mDragSource), 155 mDragSource, 156 DNDConstants::ACTION_COPY, 157 DNDConstants::ACTION_COPY); 158 159 mDragSource->mXDragSrcListener->dragOver(dsde); 160 } 161 162 @end 163 164 165 DragSource::DragSource(): 166 WeakComponentImplHelper3<XDragSource, XInitialization, XServiceInfo>(m_aMutex), 167 mView(NULL), 168 mpFrame(NULL), 169 mLastMouseEventBeforeStartDrag(nil), 170 m_MouseButton(0) 171 { 172 } 173 174 175 DragSource::~DragSource() 176 { 177 if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) 178 [(id <MouseEventListener>)mView unregisterMouseEventListener: mDragSourceHelper]; 179 [mDragSourceHelper release]; 180 } 181 182 183 void SAL_CALL DragSource::initialize(const Sequence< Any >& aArguments) 184 throw(Exception) 185 { 186 if (aArguments.getLength() < 2) 187 { 188 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Not enough parameter.")), 189 static_cast<OWeakObject*>(this)); 190 } 191 192 Any pNSView = aArguments[1]; 193 sal_uInt64 tmp = 0; 194 pNSView >>= tmp; 195 mView = (NSView*)tmp; 196 197 /* All SalFrameView the base class for all VCL system views inherits from 198 NSView in order to get mouse and other events. This is the only way to 199 get these events. In order to start a drag operation we need to provide 200 the mouse event which was the trigger. SalFrameView therefor implements 201 a hook mechanism so that we can get mouse events for our purpose. 202 */ 203 if (![mView respondsToSelector: @selector(registerMouseEventListener:)] || 204 ![mView respondsToSelector: @selector(unregisterMouseEventListener:)]) 205 { 206 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view doesn't support mouse listener")), 207 static_cast<OWeakObject*>(this)); 208 } 209 NSWindow* pWin = [mView window]; 210 if( ! pWin || ![pWin respondsToSelector: @selector(getSalFrame)] ) 211 { 212 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view is not attached to a vcl frame")), 213 static_cast<OWeakObject*>(this)); 214 } 215 mpFrame = (AquaSalFrame*)[pWin performSelector: @selector(getSalFrame)]; 216 217 mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this]; 218 219 if (mDragSourceHelper == nil) 220 { 221 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Cannot initialize DragSource")), 222 static_cast<OWeakObject*>(this)); 223 } 224 225 [(id <MouseEventListener>)mView registerMouseEventListener: mDragSourceHelper]; 226 } 227 228 229 //---------------------------------------------------- 230 // XDragSource 231 //---------------------------------------------------- 232 233 sal_Bool SAL_CALL DragSource::isDragImageSupported( ) 234 throw(RuntimeException) 235 { 236 return true; 237 } 238 239 240 sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ ) 241 throw( IllegalArgumentException, RuntimeException) 242 { 243 return 0; 244 } 245 246 247 void SAL_CALL DragSource::startDrag(const DragGestureEvent& trigger, 248 sal_Int8 sourceActions, 249 sal_Int32 /*cursor*/, 250 sal_Int32 /*image*/, 251 const uno::Reference<XTransferable >& transferable, 252 const uno::Reference<XDragSourceListener >& listener ) 253 throw( RuntimeException) 254 { 255 MutexGuard guard(m_aMutex); 256 257 OSL_ASSERT(listener.is() && "DragSource::startDrag: No XDragSourceListener provided\n"); 258 OSL_ASSERT(transferable.is() && "DragSource::startDrag: No transferable provided\n"); 259 260 trigger.Event >>= mMouseEvent; 261 m_MouseButton= mMouseEvent.Buttons; 262 mXDragSrcListener = listener; 263 mXCurrentContext = static_cast<XDragSourceContext*>(new DragSourceContext(this)); 264 auto_ptr<AquaClipboard> clipb(new AquaClipboard(NULL, false)); 265 g_XTransferable = transferable; 266 clipb->setContents(g_XTransferable, uno::Reference<XClipboardOwner>()); 267 mDragSourceActions = sourceActions; 268 g_DragSourceView = mView; 269 270 NSSize sz; 271 sz.width = 5; 272 sz.height = 5; 273 274 NSImage* dragImage; 275 dragImage = [[NSImage alloc] initWithSize: sz]; 276 277 NSRect bounds; 278 bounds.origin = NSMakePoint(0,0); 279 bounds.size = sz; 280 281 [dragImage lockFocus]; 282 [[NSColor blackColor] set]; 283 [NSBezierPath fillRect: bounds]; 284 [dragImage unlockFocus]; 285 286 NSPoint pInWnd = [mLastMouseEventBeforeStartDrag locationInWindow]; 287 NSPoint p; 288 p = [mView convertPoint: pInWnd fromView: nil]; 289 p.x = p.x - sz.width/2; 290 p.y = p.y - sz.height/2; 291 292 // reset drop success flags 293 g_DropSuccessSet = false; 294 g_DropSuccess = false; 295 296 [mView dragImage: dragImage 297 at: p 298 offset: NSMakeSize(0,0) 299 event: mLastMouseEventBeforeStartDrag 300 pasteboard: clipb->getPasteboard() 301 source: mDragSourceHelper 302 slideBack: 1]; 303 304 [dragImage release]; 305 306 g_XTransferable = uno::Reference<XTransferable>(); 307 g_DragSourceView = nil; 308 309 // reset drop success flags 310 g_DropSuccessSet = false; 311 g_DropSuccess = false; 312 } 313 314 315 // In order to initiate a D&D operation we need to 316 // provide the triggering mouse event which we get 317 // from the SalFrameView that is associated with 318 // this DragSource 319 void DragSource::saveMouseEvent(NSEvent* theEvent) 320 { 321 if (mLastMouseEventBeforeStartDrag != nil) 322 { 323 [mLastMouseEventBeforeStartDrag release]; 324 } 325 326 mLastMouseEventBeforeStartDrag = theEvent; 327 } 328 329 330 /* isLocal indicates whether or not the DnD operation is OOo 331 internal. 332 */ 333 unsigned int DragSource::getSupportedDragOperations(bool isLocal) const 334 { 335 unsigned int srcActions = OfficeToSystemDragActions(mDragSourceActions); 336 337 if (isLocal) 338 { 339 // Support NSDragOperation generic which means we can 340 // decide which D&D operation to choose. We map 341 // NSDragOperationGenric to DNDConstants::ACTION_DEFAULT 342 // in SystemToOfficeDragActions to signal this and 343 // use it in DropTarget::determineDropAction 344 srcActions |= NSDragOperationGeneric; 345 } 346 else 347 { 348 // Mask out link and move operations on external DnD 349 srcActions &= ~(NSDragOperationMove | NSDragOperationLink); 350 } 351 352 return srcActions; 353 } 354 355 356 //################################ 357 // XServiceInfo 358 //################################ 359 360 OUString SAL_CALL DragSource::getImplementationName( ) throw (RuntimeException) 361 { 362 return dragSource_getImplementationName(); 363 } 364 365 366 sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) throw (RuntimeException) 367 { 368 return ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource"))); 369 } 370 371 372 Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames() throw (RuntimeException) 373 { 374 return dragSource_getSupportedServiceNames(); 375 } 376 377 378 379 380