/* -*- 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 #include #include #include #include "clipboard.hxx" #include "DropTarget.hxx" #include "DragActionConversion.hxx" #include "DragSource.hxx" #include #include #include #include #include #include #include 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 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 )sender { return mDropTarget->draggingEntered(sender); } -(NSDragOperation)draggingUpdated:(id )sender { return mDropTarget->draggingUpdated(sender); } -(void)draggingExited:(id )sender { mDropTarget->draggingExited(sender); } -(BOOL)prepareForDragOperation:(id )sender { (void) sender; return DropTarget::prepareForDragOperation(); } -(BOOL)performDragOperation:(id )sender { (void) sender; return mDropTarget->performDragOperation(); } -(void)concludeDragOperation:(id )sender { mDropTarget->concludeDragOperation(sender); } @end DropTarget::DropTarget() : WeakComponentImplHelper(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(); } DropTarget::~DropTarget() { if( AquaSalFrame::isAlive( mpFrame ) ) [static_cast>(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(dragLocation.x); sal_Int32 posY = static_cast(dragLocation.y); NSPasteboard* dragPboard = [sender draggingPasteboard]; mXCurrentDragClipboard = new AquaClipboard(dragPboard, false); uno::Reference xTransferable = DragSource::g_XTransferable.is() ? DragSource::g_XTransferable : mXCurrentDragClipboard->getContents(); DropTargetDragEnterEvent dtdee(static_cast(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(dragLocation.x); sal_Int32 posY = static_cast(dragLocation.y); DropTargetDragEvent dtde(static_cast(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(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 = 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(dragLocation.x); sal_Int32 posY = static_cast(dragLocation.y); DropTargetDropEvent dtde(static_cast(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(this)); } Any pNSView = aArguments[0]; sal_uInt64 tmp = 0; pNSView >>= tmp; mView = reinterpret_cast(tmp); mpFrame = [static_cast(mView) getSalFrame]; mDropTargetHelper = [[DropTargetHelper alloc] initWithDropTarget: this]; [static_cast>(mView) registerDraggingDestinationHandler:mDropTargetHelper]; [mView registerForDraggedTypes: DataFlavorMapper::getAllSupportedPboardTypes()]; id wnd = [mView window]; NSWindow* parentWnd = [wnd parentWindow]; unsigned int topWndStyle = (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable); 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& dtl) { rBHelper.addListener(cppu::UnoType::get(), dtl); } void SAL_CALL DropTarget::removeDropTargetListener(const uno::Reference& dtl) { rBHelper.removeListener(cppu::UnoType::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::get()); if( pContainer) { OInterfaceIteratorHelper iter( *pContainer); while( iter.hasMoreElements()) { uno::Reference listener( static_cast( iter.next())); try { listener->drop( dte); } catch(RuntimeException&) {} } } } void DropTarget::fire_dragEnter(const DropTargetDragEnterEvent& e) { OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType::get()); if( pContainer) { OInterfaceIteratorHelper iter( *pContainer); while( iter.hasMoreElements()) { uno::Reference listener( static_cast( iter.next())); try { listener->dragEnter( e); } catch (RuntimeException&) {} } } } void DropTarget::fire_dragExit(const DropTargetEvent& dte) { OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType::get()); if( pContainer) { OInterfaceIteratorHelper iter( *pContainer); while( iter.hasMoreElements()) { uno::Reference listener( static_cast( iter.next())); try { listener->dragExit( dte); } catch (RuntimeException&) {} } } } void DropTarget::fire_dragOver(const DropTargetDragEvent& dtde) { OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType::get()); if( pContainer) { OInterfaceIteratorHelper iter( *pContainer ); while( iter.hasMoreElements()) { uno::Reference listener( static_cast( iter.next())); try { listener->dragOver( dtde); } catch (RuntimeException&) {} } } } void DropTarget::fire_dropActionChanged(const DropTargetDragEvent& dtde) { OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType::get()); if( pContainer) { OInterfaceIteratorHelper iter( *pContainer); while( iter.hasMoreElements()) { uno::Reference listener( static_cast( 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: */