diff options
Diffstat (limited to 'vcl/osx/clipboard.cxx')
-rw-r--r-- | vcl/osx/clipboard.cxx | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/vcl/osx/clipboard.cxx b/vcl/osx/clipboard.cxx new file mode 100644 index 0000000000..f5be6f86c3 --- /dev/null +++ b/vcl/osx/clipboard.cxx @@ -0,0 +1,353 @@ +/* -*- 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 "clipboard.hxx" + +#include "DataFlavorMapping.hxx" +#include "OSXTransferable.hxx" +#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace css; + +@implementation EventListener; + +-(EventListener*)initWithAquaClipboard: (AquaClipboard*) pcb +{ + self = [super init]; + + if (self) + pAquaClipboard = pcb; + + return self; +} + +-(void)pasteboard:(NSPasteboard*)sender provideDataForType:(const NSString*)type +{ + if( pAquaClipboard ) + pAquaClipboard->provideDataForType(sender, type); +} + +-(void)applicationDidBecomeActive:(NSNotification*)aNotification +{ + if( pAquaClipboard ) + pAquaClipboard->applicationDidBecomeActive(aNotification); +} + +-(void)disposing +{ + pAquaClipboard = nullptr; +} + +@end + +static OUString clipboard_getImplementationName() +{ + return "com.sun.star.datatransfer.clipboard.AquaClipboard"; +} + +static uno::Sequence<OUString> clipboard_getSupportedServiceNames() +{ + return { OUString("com.sun.star.datatransfer.clipboard.SystemClipboard") }; +} + +AquaClipboard::AquaClipboard(NSPasteboard* pasteboard, bool bUseSystemPasteboard) + : WeakComponentImplHelper<XSystemClipboard, XFlushableClipboard, XServiceInfo>(m_aMutex) + , mIsSystemPasteboard(bUseSystemPasteboard) +{ + uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext(); + + mrXMimeCntFactory = datatransfer::MimeContentTypeFactory::create(xContext); + + mpDataFlavorMapper = std::make_shared<DataFlavorMapper>(); + + if (pasteboard != nullptr) + { + mPasteboard = pasteboard; + mIsSystemPasteboard = false; + } + else + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.13 NSDragPboard + mPasteboard = bUseSystemPasteboard ? [NSPasteboard generalPasteboard] : + [NSPasteboard pasteboardWithName: NSDragPboard]; + SAL_WNODEPRECATED_DECLARATIONS_POP + + if (mPasteboard == nil) + { + throw uno::RuntimeException("AquaClipboard: Cannot create Cocoa pasteboard", + static_cast<XClipboardEx*>(this)); + } + } + + [mPasteboard retain]; + + mEventListener = [[EventListener alloc] initWithAquaClipboard: this]; + + if (mEventListener == nil) + { + [mPasteboard release]; + + throw uno::RuntimeException( + "AquaClipboard: Cannot create pasteboard change listener", + static_cast<XClipboardEx*>(this)); + } + + if (mIsSystemPasteboard) + { + NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver: mEventListener + selector: @selector(applicationDidBecomeActive:) + name: @"NSApplicationDidBecomeActiveNotification" + object: [NSApplication sharedApplication]]; + } + + mPasteboardChangeCount = [mPasteboard changeCount]; +} + +AquaClipboard::~AquaClipboard() +{ + if (mIsSystemPasteboard) + { + [[NSNotificationCenter defaultCenter] removeObserver: mEventListener]; + } + + [mEventListener disposing]; + [mEventListener release]; + [mPasteboard release]; +} + +uno::Reference<datatransfer::XTransferable> SAL_CALL AquaClipboard::getContents() +{ + osl::MutexGuard aGuard(m_aMutex); + + // tdf#144124 Detect if ownership has been lost + // The shortcut assumes that lost ownership notifications from the + // system clipboard will happen elsewhere. They do under normal + // conditions, but do not when some clipboard managers are running. + // So, explicitly check ownership to catch such cases. + if (mIsSystemPasteboard) + applicationDidBecomeActive(nullptr); + + // Shortcut: If we are clipboard owner already we don't need + // to drag the data through the system clipboard + if (mXClipboardContent.is()) + { + return mXClipboardContent; + } + + return uno::Reference<datatransfer::XTransferable>( + new OSXTransferable(mrXMimeCntFactory, + mpDataFlavorMapper, + mPasteboard)); +} + +void SAL_CALL AquaClipboard::setContents( + uno::Reference<datatransfer::XTransferable> const & xTransferable, + uno::Reference<datatransfer::clipboard::XClipboardOwner> const & xClipboardOwner) +{ + NSArray* types = xTransferable.is() ? + mpDataFlavorMapper->flavorSequenceToTypesArray(xTransferable->getTransferDataFlavors(), true) : + [NSArray array]; + + osl::ClearableMutexGuard aGuard(m_aMutex); + + uno::Reference<datatransfer::clipboard::XClipboardOwner> oldOwner(mXClipboardOwner); + mXClipboardOwner = xClipboardOwner; + + uno::Reference<datatransfer::XTransferable> oldContent(mXClipboardContent); + mXClipboardContent = xTransferable; + + mPasteboardChangeCount = [mPasteboard declareTypes: types owner: mEventListener]; + + aGuard.clear(); + + // if we are already the owner of the clipboard + // then fire lost ownership event + if (oldOwner.is()) + { + fireLostClipboardOwnershipEvent(oldOwner, oldContent); + } + + fireClipboardChangedEvent(); +} + +OUString SAL_CALL AquaClipboard::getName() +{ + return OUString(); +} + +sal_Int8 SAL_CALL AquaClipboard::getRenderingCapabilities() +{ + return 0; +} + +void SAL_CALL AquaClipboard::addClipboardListener(uno::Reference<datatransfer::clipboard::XClipboardListener> const & listener) +{ + osl::MutexGuard aGuard(m_aMutex); + + if (!listener.is()) + throw lang::IllegalArgumentException("empty reference", + static_cast<XClipboardEx*>(this), 1); + + mClipboardListeners.push_back(listener); +} + +void SAL_CALL AquaClipboard::removeClipboardListener(uno::Reference<datatransfer::clipboard::XClipboardListener> const & listener) +{ + osl::MutexGuard aGuard(m_aMutex); + + if (!listener.is()) + throw lang::IllegalArgumentException("empty reference", + static_cast<XClipboardEx*>(this), 1); + + mClipboardListeners.remove(listener); +} + +void AquaClipboard::applicationDidBecomeActive(NSNotification*) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + int currentPboardChgCount = [mPasteboard changeCount]; + + if (currentPboardChgCount != mPasteboardChangeCount) + { + mPasteboardChangeCount = currentPboardChgCount; + + // Clear clipboard content and owner and send lostOwnership + // notification to the old clipboard owner as well as + // ClipboardChanged notification to any clipboard listener + uno::Reference<datatransfer::clipboard::XClipboardOwner> oldOwner(mXClipboardOwner); + mXClipboardOwner.clear(); + + uno::Reference<datatransfer::XTransferable> oldContent(mXClipboardContent); + mXClipboardContent.clear(); + + aGuard.clear(); + + if (oldOwner.is()) + { + fireLostClipboardOwnershipEvent(oldOwner, oldContent); + } + + fireClipboardChangedEvent(); + } +} + +void AquaClipboard::fireClipboardChangedEvent() +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + datatransfer::clipboard::ClipboardEvent aEvent; + + if (!mClipboardListeners.empty()) + { + aEvent = datatransfer::clipboard::ClipboardEvent(getXWeak(), getContents()); + } + + aGuard.clear(); + + for (auto const& rListener : mClipboardListeners) + { + if (rListener.is()) + { + try + { + rListener->changedContents(aEvent); + } + catch (uno::RuntimeException& ) + {} + } + } +} + +void AquaClipboard::fireLostClipboardOwnershipEvent( + uno::Reference<datatransfer::clipboard::XClipboardOwner> const & rOldOwner, + uno::Reference<datatransfer::XTransferable> const & rOldContent) +{ + assert(rOldOwner.is()); + + try + { + rOldOwner->lostOwnership(static_cast<XClipboardEx*>(this), rOldContent); + } + catch(uno::RuntimeException&) + {} +} + +void AquaClipboard::provideDataForType(NSPasteboard* sender, const NSString* type) +{ + if( mXClipboardContent.is() ) + { + DataProviderPtr_t dp = mpDataFlavorMapper->getDataProvider(type, mXClipboardContent); + NSData* pBoardData = nullptr; + + if (dp) + { + pBoardData = dp->getSystemData(); + [sender setData: pBoardData forType:const_cast<NSString*>(type)]; + } + } +} + +void SAL_CALL AquaClipboard::flushClipboard() +{ + if (mXClipboardContent.is()) + { + uno::Sequence<datatransfer::DataFlavor> flavorList = mXClipboardContent->getTransferDataFlavors(); + sal_uInt32 nFlavors = flavorList.getLength(); + bool bInternal(false); + + for (sal_uInt32 i = 0; i < nFlavors; i++) + { + const NSString* sysType = mpDataFlavorMapper->openOfficeToSystemFlavor(flavorList[i], bInternal); + + if (sysType != nullptr) + { + provideDataForType(mPasteboard, sysType); + } + } + mXClipboardContent.clear(); + } +} + +NSPasteboard* AquaClipboard::getPasteboard() const +{ + return mPasteboard; +} + +OUString SAL_CALL AquaClipboard::getImplementationName() +{ + return clipboard_getImplementationName(); +} + +sal_Bool SAL_CALL AquaClipboard::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SAL_CALL AquaClipboard::getSupportedServiceNames() +{ + return clipboard_getSupportedServiceNames(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |