diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/osx/DragSource.cxx | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/vcl/osx/DragSource.cxx b/vcl/osx/DragSource.cxx new file mode 100644 index 000000000..bfe990d73 --- /dev/null +++ b/vcl/osx/DragSource.cxx @@ -0,0 +1,338 @@ +/* -*- 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/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(static_cast<OWeakObject*>(mDragSource), + 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(static_cast<OWeakObject*>(mDragSource), + 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(static_cast<OWeakObject*>(mDragSource), + 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]; +} + +void SAL_CALL DragSource::initialize(const Sequence< Any >& aArguments) +{ + if (aArguments.getLength() < 2) + { + throw Exception("DragSource::initialize: Not enough parameter.", + static_cast<OWeakObject*>(this)); + } + + 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", + static_cast<OWeakObject*>(this)); + } + NSWindow* pWin = [mView window]; + if( ! pWin || ![pWin respondsToSelector: @selector(getSalFrame)] ) + { + throw Exception("DragSource::initialize: Provided view is not attached to a vcl frame", + static_cast<OWeakObject*>(this)); + } + mpFrame = reinterpret_cast<AquaSalFrame*>([pWin performSelector: @selector(getSalFrame)]); + + mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this]; + + if (mDragSourceHelper == nil) + { + throw Exception("DragSource::initialize: Cannot initialize DragSource", + static_cast<OWeakObject*>(this)); + } + + [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; +} + +/* 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 + // NSDragOperationGenric 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: */ |