diff options
Diffstat (limited to 'vcl/osx/DropTarget.cxx')
-rw-r--r-- | vcl/osx/DropTarget.cxx | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/vcl/osx/DropTarget.cxx b/vcl/osx/DropTarget.cxx new file mode 100644 index 000000000..26398cb90 --- /dev/null +++ b/vcl/osx/DropTarget.cxx @@ -0,0 +1,548 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice 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/datatransfer/dnd/DropTargetDragEnterEvent.hpp> +#include <cppuhelper/interfacecontainer.hxx> +#include "clipboard.hxx" +#include "DropTarget.hxx" +#include "DragActionConversion.hxx" +#include "DragSource.hxx" +#include <rtl/ustring.h> +#include <premac.h> +#include <Carbon/Carbon.h> +#include <postmac.h> +#include <osx/salframe.h> +#include <osx/salframeview.h> +#include <cppuhelper/supportsservice.hxx> + +using namespace cppu; +using namespace osl; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::datatransfer::dnd::DNDConstants; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star; +using namespace comphelper; + +static OUString dropTarget_getImplementationName() +{ + return "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"; +} + +static Sequence<OUString> dropTarget_getSupportedServiceNames() +{ + return { OUString("com.sun.star.datatransfer.dnd.OleDropTarget") }; +} + +namespace /* private */ +{ + // Cocoa's coordinate system has its origin lower-left, VCL's + // coordinate system upper-left hence we need to transform + // coordinates + + void CocoaToVCL(NSPoint& rPoint, const NSRect& bounds) + { + rPoint.y = bounds.size.height - rPoint.y; + } +} + +@implementation DropTargetHelper + +-(DropTargetHelper*)initWithDropTarget:(DropTarget*)pdt +{ + self = [super init]; + + if (self) + { + mDropTarget = pdt; + } + + return self; +} + +-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + return mDropTarget->draggingEntered(sender); +} + +-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + return mDropTarget->draggingUpdated(sender); +} + +-(void)draggingExited:(id <NSDraggingInfo>)sender +{ + mDropTarget->draggingExited(sender); +} + +-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender +{ + (void) sender; + return DropTarget::prepareForDragOperation(); +} + +-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + (void) sender; + return mDropTarget->performDragOperation(); +} + +-(void)concludeDragOperation:(id <NSDraggingInfo>)sender +{ + mDropTarget->concludeDragOperation(sender); +} + +@end + +DropTarget::DropTarget() : + WeakComponentImplHelper<XInitialization, XDropTarget, XDropTargetDragContext, XDropTargetDropContext, XServiceInfo>(m_aMutex), + mView(nil), + mpFrame(nullptr), + mDropTargetHelper(nil), + mbActive(false), + mDragSourceSupportedActions(DNDConstants::ACTION_NONE), + mSelectedDropAction(DNDConstants::ACTION_NONE), + mDefaultActions(DNDConstants::ACTION_COPY_OR_MOVE | DNDConstants::ACTION_LINK | DNDConstants::ACTION_DEFAULT) +{ + mDataFlavorMapper = std::make_shared<DataFlavorMapper>(); +} + +DropTarget::~DropTarget() +{ + if( AquaSalFrame::isAlive( mpFrame ) ) + [static_cast<id <DraggingDestinationHandler>>(mView) unregisterDraggingDestinationHandler:mDropTargetHelper]; + [mDropTargetHelper release]; +} + +sal_Int8 DropTarget::determineDropAction(sal_Int8 dropActions, id sender) const +{ + sal_Int8 dropAct = dropActions; + bool srcAndDestEqual = false; + + if ([sender draggingSource] != nil) + { + // Internal DnD + NSView* destView = [[sender draggingDestinationWindow] contentView]; + srcAndDestEqual = (DragSource::g_DragSourceView == destView); + } + + // If ACTION_DEFAULT is set this means NSDragOperationGeneric + // has been set and we map this to ACTION_MOVE or ACTION_COPY + // depending on whether or not source and dest are equal, + // this hopefully satisfies all parties + if( (dropActions == DNDConstants::ACTION_DEFAULT) + || ((dropActions == mDragSourceSupportedActions) + && !(~mDragSourceSupportedActions & DNDConstants::ACTION_COPY_OR_MOVE ) ) ) + { + dropAct = srcAndDestEqual ? DNDConstants::ACTION_MOVE : + DNDConstants::ACTION_COPY; + } + // if more than one drop actions have been specified + // set ACTION_DEFAULT in order to let the drop target + // decide which one to use + else if (dropActions != DNDConstants::ACTION_NONE && + dropActions != DNDConstants::ACTION_MOVE && + dropActions != DNDConstants::ACTION_COPY && + dropActions != DNDConstants::ACTION_LINK) + { + if (srcAndDestEqual) + { + dropAct = dropActions; + } + else // source and destination are different + { + if (dropActions & DNDConstants::ACTION_COPY) + dropAct = DNDConstants::ACTION_COPY; + else if (dropActions & DNDConstants::ACTION_MOVE) + dropAct = DNDConstants::ACTION_MOVE; + else if (dropActions & DNDConstants::ACTION_LINK) + dropAct = DNDConstants::ACTION_LINK; + } + + dropAct |= DNDConstants::ACTION_DEFAULT; + } + + return dropAct; +} + +NSDragOperation DropTarget::draggingEntered(id sender) +{ + // Initially when DnD will be started no modifier key can be pressed yet + // thus we are getting all actions that the drag source supports, we save + // this value because later the system masks the drag source actions if + // a modifier key will be pressed + mDragSourceSupportedActions = SystemToOfficeDragActions([sender draggingSourceOperationMask]); + + // Only if the drop target is really interested in the drag actions + // supported by the source + if (mDragSourceSupportedActions & mDefaultActions) + { + sal_Int8 currentAction = determineDropAction(mDragSourceSupportedActions, sender); + + NSRect bounds = [mView bounds]; + NSPoint mouseLoc = [NSEvent mouseLocation]; + + id wnd = [mView window]; + NSPoint dragLocation = [mView convertPoint:[wnd convertRectFromScreen:NSMakeRect(mouseLoc.x, mouseLoc.y, 1, 1)].origin fromView:nil]; + + CocoaToVCL(dragLocation, bounds); + + sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x); + sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y); + + NSPasteboard* dragPboard = [sender draggingPasteboard]; + mXCurrentDragClipboard = new AquaClipboard(dragPboard, false); + + uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable.is() ? + DragSource::g_XTransferable : mXCurrentDragClipboard->getContents(); + + DropTargetDragEnterEvent dtdee(static_cast<OWeakObject*>(this), + 0, + this, + currentAction, + posX, + posY, + mDragSourceSupportedActions, + xTransferable->getTransferDataFlavors()); + + fire_dragEnter(dtdee); + } + + return OfficeToSystemDragActions(mSelectedDropAction); +} + +NSDragOperation DropTarget::draggingUpdated(id sender) +{ + sal_Int8 currentDragSourceActions = + SystemToOfficeDragActions([sender draggingSourceOperationMask]); + NSDragOperation dragOp = NSDragOperationNone; + + if (currentDragSourceActions & mDefaultActions) + { + sal_Int8 currentAction = determineDropAction(currentDragSourceActions, sender); + NSRect bounds = [mView bounds]; + NSPoint mouseLoc = [NSEvent mouseLocation]; + + id wnd = [mView window]; + NSPoint dragLocation = [mView convertPoint:[wnd convertRectFromScreen:NSMakeRect(mouseLoc.x, mouseLoc.y, 1, 1)].origin fromView:nil]; + + CocoaToVCL(dragLocation, bounds); + + sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x); + sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y); + + DropTargetDragEvent dtde(static_cast<OWeakObject*>(this), + 0, + this, + currentAction, + posX, + posY, + mDragSourceSupportedActions); + + fire_dragOver(dtde); + + // drag over callbacks likely have rendered something + [mView setNeedsDisplay: true]; + + dragOp = OfficeToSystemDragActions(mSelectedDropAction); + + //NSLog(@"Drag update: Source actions: %x proposed action %x selected action %x", mDragSourceSupportedActions, currentAction, mSelectedDropAction); + } + + if (dragOp == NSDragOperationNone) + [[NSCursor operationNotAllowedCursor] set]; + else if (dragOp == NSDragOperationCopy) + [[NSCursor dragCopyCursor] set]; + else + [[NSCursor arrowCursor] set]; + + return dragOp; +} + +void DropTarget::draggingExited(id /*sender*/) +{ + DropTargetEvent dte(static_cast<OWeakObject*>(this), 0); + fire_dragExit(dte); + mDragSourceSupportedActions = DNDConstants::ACTION_NONE; + mSelectedDropAction = DNDConstants::ACTION_NONE; + [[NSCursor arrowCursor] set]; +} + +BOOL DropTarget::prepareForDragOperation() +{ + return true; +} + +BOOL DropTarget::performDragOperation() +{ + bool bSuccess = false; + + if (mSelectedDropAction != DNDConstants::ACTION_NONE) + { + uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable; + + if (!DragSource::g_XTransferable.is()) + { + xTransferable = mXCurrentDragClipboard->getContents(); + } + + NSRect bounds = [mView bounds]; + NSPoint mouseLoc = [NSEvent mouseLocation]; + + id wnd = [mView window]; + NSPoint dragLocation = [mView convertPoint:[wnd convertRectFromScreen:NSMakeRect(mouseLoc.x, mouseLoc.y, 1, 1)].origin fromView:nil]; + + CocoaToVCL(dragLocation, bounds); + + sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x); + sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y); + + DropTargetDropEvent dtde(static_cast<OWeakObject*>(this), + 0, + this, + mSelectedDropAction, + posX, + posY, + mDragSourceSupportedActions, + xTransferable); + + fire_drop(dtde); + + bSuccess = true; + } + + return bSuccess; +} + +void DropTarget::concludeDragOperation(id /*sender*/) +{ + mDragSourceSupportedActions = DNDConstants::ACTION_NONE; + mSelectedDropAction = DNDConstants::ACTION_NONE; + mXCurrentDragClipboard.clear(); + [[NSCursor arrowCursor] set]; +} + +// called from WeakComponentImplHelperX::dispose +// WeakComponentImplHelper calls disposing before it destroys +// itself. +void SAL_CALL DropTarget::disposing() +{ +} + +void SAL_CALL DropTarget::initialize(const Sequence< Any >& aArguments) +{ + if (aArguments.getLength() < 2) + { + throw RuntimeException("DropTarget::initialize: Cannot install window event handler", + static_cast<OWeakObject*>(this)); + } + + Any pNSView = aArguments[0]; + sal_uInt64 tmp = 0; + pNSView >>= tmp; + mView = reinterpret_cast<id>(tmp); + mpFrame = [static_cast<SalFrameView*>(mView) getSalFrame]; + + mDropTargetHelper = [[DropTargetHelper alloc] initWithDropTarget: this]; + + [static_cast<id <DraggingDestinationHandler>>(mView) registerDraggingDestinationHandler:mDropTargetHelper]; + [mView registerForDraggedTypes: DataFlavorMapper::getAllSupportedPboardTypes()]; + + id wnd = [mView window]; + NSWindow* parentWnd = [wnd parentWindow]; +SAL_WNODEPRECATED_DECLARATIONS_PUSH + // 'NSClosableWindowMask' is deprecated: first deprecated in macOS 10.12 + // 'NSResizableWindowMask' is deprecated: first deprecated in macOS 10.12 + // 'NSTitleWindowMask' is deprecated: first deprecated in macOS 10.12 + unsigned int topWndStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask); +SAL_WNODEPRECATED_DECLARATIONS_POP + unsigned int wndStyles = [wnd styleMask] & topWndStyle; + + if (parentWnd == nil && (wndStyles == topWndStyle)) + { + [wnd registerDraggingDestinationHandler:mDropTargetHelper]; +SAL_WNODEPRECATED_DECLARATIONS_PUSH + // "'NSFilenamesPboardType' is deprecated: first deprecated in macOS 10.14 - Create + // multiple pasteboard items with NSPasteboardTypeFileURL or kUTTypeFileURL instead" + [wnd registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, nil]]; +SAL_WNODEPRECATED_DECLARATIONS_POP + } +} + +void SAL_CALL DropTarget::addDropTargetListener(const uno::Reference<XDropTargetListener>& dtl) +{ + rBHelper.addListener(cppu::UnoType<decltype(dtl)>::get(), dtl); +} + +void SAL_CALL DropTarget::removeDropTargetListener(const uno::Reference<XDropTargetListener>& dtl) +{ + rBHelper.removeListener(cppu::UnoType<decltype(dtl)>::get(), dtl); +} + +sal_Bool SAL_CALL DropTarget::isActive( ) +{ + return mbActive; +} + +void SAL_CALL DropTarget::setActive(sal_Bool active) +{ + mbActive = active; +} + +sal_Int8 SAL_CALL DropTarget::getDefaultActions() +{ + return mDefaultActions; +} + +void SAL_CALL DropTarget::setDefaultActions(sal_Int8 actions) +{ + OSL_ENSURE( actions < 8, "No valid default actions"); + mDefaultActions= actions; +} + +void SAL_CALL DropTarget::acceptDrag(sal_Int8 dragOperation) +{ + mSelectedDropAction = dragOperation; +} + +void SAL_CALL DropTarget::rejectDrag() +{ + mSelectedDropAction = DNDConstants::ACTION_NONE; +} + +void SAL_CALL DropTarget::acceptDrop(sal_Int8 dropOperation) +{ + mSelectedDropAction = dropOperation; +} + +void SAL_CALL DropTarget::rejectDrop() +{ + mSelectedDropAction = DNDConstants::ACTION_NONE; +} + +void SAL_CALL DropTarget::dropComplete(sal_Bool success) +{ + // Reset the internal transferable used as shortcut in case this is + // an internal D&D operation + DragSource::g_XTransferable.clear(); + DragSource::g_DropSuccessSet = true; + DragSource::g_DropSuccess = success; +} + +void DropTarget::fire_drop( const DropTargetDropEvent& dte) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->drop( dte); } + catch(RuntimeException&) {} + } + } +} + +void DropTarget::fire_dragEnter(const DropTargetDragEnterEvent& e) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dragEnter( e); } + catch (RuntimeException&) {} + } + } +} + +void DropTarget::fire_dragExit(const DropTargetEvent& dte) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dragExit( dte); } + catch (RuntimeException&) {} + } + } +} + +void DropTarget::fire_dragOver(const DropTargetDragEvent& dtde) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer ); + while( iter.hasMoreElements()) + { + uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dragOver( dtde); } + catch (RuntimeException&) {} + } + } +} + +void DropTarget::fire_dropActionChanged(const DropTargetDragEvent& dtde) +{ + OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get()); + if( pContainer) + { + OInterfaceIteratorHelper iter( *pContainer); + while( iter.hasMoreElements()) + { + uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next())); + + try { listener->dropActionChanged( dtde); } + catch (RuntimeException&) {} + } + } +} + +OUString SAL_CALL DropTarget::getImplementationName() +{ + return dropTarget_getImplementationName(); +} + +sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( ) +{ + return dropTarget_getSupportedServiceNames(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |