| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file is part of the Collabora Office project. |
| * |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| * |
| * This file incorporates work covered by the following license notice: |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
| */ |
| |
| #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> |
| #include <com/sun/star/datatransfer/XTransferable.hpp> |
| #include <com/sun/star/awt/MouseButton.hpp> |
| |
| #include <rtl/ustring.hxx> |
| |
| #include <cppuhelper/supportsservice.hxx> |
| |
| #include "DragSource.hxx" |
| #include "DragSourceContext.hxx" |
| #include "clipboard.hxx" |
| #include "DragActionConversion.hxx" |
| |
| #include <osx/salframe.h> |
| |
| #include <cassert> |
| |
| using namespace cppu; |
| using namespace osl; |
| using namespace com::sun::star; |
| using namespace com::sun::star::datatransfer; |
| using namespace com::sun::star::datatransfer::clipboard; |
| using namespace com::sun::star::datatransfer::dnd; |
| using namespace com::sun::star::datatransfer::dnd::DNDConstants; |
| using namespace com::sun::star::uno; |
| using namespace com::sun::star::awt::MouseButton; |
| using namespace com::sun::star::awt; |
| using namespace com::sun::star::lang; |
| using namespace comphelper; |
| |
| // For LibreOffice internal D&D we provide the Transferable without NSDragPboard |
| // interference as a shortcut, see tdf#100097 for how dbaccess depends on this |
| uno::Reference<XTransferable> DragSource::g_XTransferable; |
| NSView* DragSource::g_DragSourceView = nil; |
| bool DragSource::g_DropSuccessSet = false; |
| bool DragSource::g_DropSuccess = false; |
| |
| static OUString dragSource_getImplementationName() |
| { |
| return "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"; |
| } |
| |
| static Sequence<OUString> dragSource_getSupportedServiceNames() |
| { |
| return { OUString("com.sun.star.datatransfer.dnd.OleDragSource") }; |
| } |
| |
| @implementation DragSourceHelper; |
| |
| -(DragSourceHelper*)initWithDragSource: (DragSource*) pds |
| { |
| self = [super init]; |
| |
| if (self) |
| { |
| mDragSource = pds; |
| } |
| |
| return self; |
| } |
| |
| -(void)mouseDown: (NSEvent*)theEvent |
| { |
| mDragSource->saveMouseEvent(theEvent); |
| } |
| |
| -(void)mouseDragged: (NSEvent*)theEvent |
| { |
| mDragSource->saveMouseEvent(theEvent); |
| } |
| |
| -(unsigned int)draggingSourceOperationMaskForLocal: (BOOL)isLocal |
| { |
| return mDragSource->getSupportedDragOperations(isLocal); |
| } |
| |
| -(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint |
| { |
| (void)anImage; |
| (void)aPoint; |
| DragSourceDragEvent dsde(mDragSource->getXWeak(), |
| new DragSourceContext, |
| mDragSource, |
| DNDConstants::ACTION_COPY, |
| DNDConstants::ACTION_COPY); |
| |
| mDragSource->mXDragSrcListener->dragEnter(dsde); |
| } |
| |
| -(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation |
| { |
| (void)anImage; |
| (void)aPoint; |
| // an internal drop can accept the drop but fail with dropComplete( false ) |
| // this is different than the Cocoa API |
| bool bDropSuccess = operation != NSDragOperationNone; |
| if( DragSource::g_DropSuccessSet ) |
| bDropSuccess = DragSource::g_DropSuccess; |
| |
| DragSourceDropEvent dsde(mDragSource->getXWeak(), |
| new DragSourceContext, |
| static_cast< XDragSource* >(mDragSource), |
| SystemToOfficeDragActions(operation), |
| bDropSuccess ); |
| |
| mDragSource->mXDragSrcListener->dragDropEnd(dsde); |
| mDragSource->mXDragSrcListener.clear(); |
| } |
| |
| -(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint |
| { |
| (void)draggedImage; |
| (void)screenPoint; |
| DragSourceDragEvent dsde(mDragSource->getXWeak(), |
| new DragSourceContext, |
| mDragSource, |
| DNDConstants::ACTION_COPY, |
| DNDConstants::ACTION_COPY); |
| |
| mDragSource->mXDragSrcListener->dragOver(dsde); |
| } |
| |
| @end |
| |
| DragSource::DragSource(): |
| WeakComponentImplHelper<XDragSource, XInitialization, XServiceInfo>(m_aMutex), |
| mView(nullptr), |
| mpFrame(nullptr), |
| mLastMouseEventBeforeStartDrag(nil), |
| mDragSourceHelper(nil), |
| m_MouseButton(0) |
| { |
| } |
| |
| DragSource::~DragSource() |
| { |
| if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) |
| [static_cast<id <MouseEventListener>>(mView) unregisterMouseEventListener: mDragSourceHelper]; |
| [mDragSourceHelper release]; |
| |
| if( mLastMouseEventBeforeStartDrag ) |
| [mLastMouseEventBeforeStartDrag release]; |
| } |
| |
| void SAL_CALL DragSource::initialize(const Sequence< Any >& aArguments) |
| { |
| if (aArguments.getLength() < 2) |
| { |
| throw Exception("DragSource::initialize: Not enough parameter.", |
| getXWeak()); |
| } |
| |
| Any pNSView = aArguments[1]; |
| sal_uInt64 tmp = 0; |
| pNSView >>= tmp; |
| mView = reinterpret_cast<NSView*>(tmp); |
| |
| /* All SalFrameView the base class for all VCL system views inherits from |
| NSView in order to get mouse and other events. This is the only way to |
| get these events. In order to start a drag operation we need to provide |
| the mouse event which was the trigger. SalFrameView therefore implements |
| a hook mechanism so that we can get mouse events for our purpose. |
| */ |
| if (![mView respondsToSelector: @selector(registerMouseEventListener:)] || |
| ![mView respondsToSelector: @selector(unregisterMouseEventListener:)]) |
| { |
| throw Exception("DragSource::initialize: Provided view doesn't support mouse listener", |
| getXWeak()); |
| } |
| NSWindow* pWin = [mView window]; |
| if( ! pWin || ![pWin respondsToSelector: @selector(getSalFrame)] ) |
| { |
| throw Exception("DragSource::initialize: Provided view is not attached to a vcl frame", |
| getXWeak()); |
| } |
| mpFrame = reinterpret_cast<AquaSalFrame*>([pWin performSelector: @selector(getSalFrame)]); |
| |
| mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this]; |
| |
| if (mDragSourceHelper == nil) |
| { |
| throw Exception("DragSource::initialize: Cannot initialize DragSource", |
| getXWeak()); |
| } |
| |
| [static_cast<id <MouseEventListener>>(mView) registerMouseEventListener: mDragSourceHelper]; |
| } |
| |
| sal_Bool SAL_CALL DragSource::isDragImageSupported( ) |
| { |
| return true; |
| } |
| |
| sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ ) |
| { |
| return 0; |
| } |
| |
| void SAL_CALL DragSource::startDrag(const DragGestureEvent& trigger, |
| sal_Int8 sourceActions, |
| sal_Int32 /*cursor*/, |
| sal_Int32 /*image*/, |
| const uno::Reference<XTransferable >& transferable, |
| const uno::Reference<XDragSourceListener >& listener ) |
| { |
| MutexGuard guard(m_aMutex); |
| |
| assert(listener.is() && "DragSource::startDrag: No XDragSourceListener provided"); |
| assert(transferable.is() && "DragSource::startDrag: No transferable provided"); |
| |
| trigger.Event >>= mMouseEvent; |
| m_MouseButton= mMouseEvent.Buttons; |
| mXDragSrcListener = listener; |
| mXCurrentContext = static_cast<XDragSourceContext*>(new DragSourceContext); |
| rtl::Reference<AquaClipboard> clipb(new AquaClipboard(nullptr, false)); |
| g_XTransferable = transferable; |
| clipb->setContents(g_XTransferable, uno::Reference<XClipboardOwner>()); |
| mDragSourceActions = sourceActions; |
| g_DragSourceView = mView; |
| |
| NSSize sz; |
| sz.width = 5; |
| sz.height = 5; |
| |
| NSImage* dragImage; |
| dragImage = [[NSImage alloc] initWithSize: sz]; |
| |
| NSRect bounds; |
| bounds.origin = NSMakePoint(0,0); |
| bounds.size = sz; |
| |
| [dragImage lockFocus]; |
| [[NSColor blackColor] set]; |
| [NSBezierPath fillRect: bounds]; |
| [dragImage unlockFocus]; |
| |
| NSPoint pInWnd = [mLastMouseEventBeforeStartDrag locationInWindow]; |
| NSPoint p; |
| p = [mView convertPoint: pInWnd fromView: nil]; |
| p.x = p.x - sz.width/2; |
| p.y = p.y - sz.height/2; |
| |
| // reset drop success flags |
| g_DropSuccessSet = false; |
| g_DropSuccess = false; |
| |
| SAL_WNODEPRECATED_DECLARATIONS_PUSH |
| //TODO: 10.7 dragImage:at:offset:event:pasteboard:source:slideBack: |
| [mView dragImage: dragImage |
| at: p |
| offset: NSMakeSize(0,0) |
| event: mLastMouseEventBeforeStartDrag |
| pasteboard: clipb->getPasteboard() |
| source: mDragSourceHelper |
| slideBack: true]; |
| SAL_WNODEPRECATED_DECLARATIONS_POP |
| |
| [dragImage release]; |
| |
| g_XTransferable.clear(); |
| g_DragSourceView = nil; |
| |
| // reset drop success flags |
| g_DropSuccessSet = false; |
| g_DropSuccess = false; |
| } |
| |
| // In order to initiate a D&D operation we need to |
| // provide the triggering mouse event which we get |
| // from the SalFrameView that is associated with |
| // this DragSource |
| void DragSource::saveMouseEvent(NSEvent* theEvent) |
| { |
| if (mLastMouseEventBeforeStartDrag != nil) |
| { |
| [mLastMouseEventBeforeStartDrag release]; |
| } |
| |
| mLastMouseEventBeforeStartDrag = theEvent; |
| |
| if (mLastMouseEventBeforeStartDrag != nil) |
| { |
| [mLastMouseEventBeforeStartDrag retain]; |
| } |
| } |
| |
| /* isLocal indicates whether or not the DnD operation is OOo |
| internal. |
| */ |
| unsigned int DragSource::getSupportedDragOperations(bool isLocal) const |
| { |
| unsigned int srcActions = OfficeToSystemDragActions(mDragSourceActions); |
| |
| if (isLocal) |
| { |
| // Support NSDragOperation generic which means we can |
| // decide which D&D operation to choose. We map |
| // NSDragOperationGeneric to DNDConstants::ACTION_DEFAULT |
| // in SystemToOfficeDragActions to signal this and |
| // use it in DropTarget::determineDropAction |
| srcActions |= NSDragOperationGeneric; |
| } |
| else |
| { |
| // Mask out link and move operations on external DnD |
| srcActions &= ~(NSDragOperationMove | NSDragOperationLink); |
| } |
| |
| return srcActions; |
| } |
| |
| OUString SAL_CALL DragSource::getImplementationName( ) |
| { |
| return dragSource_getImplementationName(); |
| } |
| |
| sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) |
| { |
| return cppu::supportsService(this, ServiceName); |
| } |
| |
| Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames() |
| { |
| return dragSource_getSupportedServiceNames(); |
| } |
| |
| /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |