| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| #include "aqua_clipboard.hxx" |
| |
| #include "DataFlavorMapping.hxx" |
| #include "OSXTransferable.hxx" |
| |
| #include "vcl/unohelp.hxx" |
| |
| #include "comphelper/makesequence.hxx" |
| |
| #include <boost/assert.hpp> |
| |
| using namespace com::sun::star::datatransfer; |
| using namespace com::sun::star::datatransfer::clipboard; |
| using namespace com::sun::star::lang; |
| using namespace com::sun::star::uno; |
| using namespace cppu; |
| using namespace osl; |
| using namespace rtl; |
| using namespace std; |
| using namespace comphelper; |
| |
| |
| @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 = NULL; |
| } |
| |
| @end |
| |
| |
| OUString clipboard_getImplementationName() |
| { |
| return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.AquaClipboard")); |
| } |
| |
| Sequence<OUString> clipboard_getSupportedServiceNames() |
| { |
| return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.SystemClipboard"))); |
| } |
| |
| |
| AquaClipboard::AquaClipboard(NSPasteboard* pasteboard, bool bUseSystemPasteboard) : |
| WeakComponentImplHelper4<XClipboardEx, XClipboardNotifier, XFlushableClipboard, XServiceInfo>(m_aMutex), |
| mIsSystemPasteboard(bUseSystemPasteboard) |
| { |
| Reference<XMultiServiceFactory> mrServiceMgr = vcl::unohelper::GetMultiServiceFactory(); |
| |
| mrXMimeCntFactory = Reference<XMimeContentTypeFactory>(mrServiceMgr->createInstance( |
| OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.MimeContentTypeFactory"))), UNO_QUERY); |
| |
| if (!mrXMimeCntFactory.is()) |
| { |
| throw RuntimeException(OUString( |
| RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create com.sun.star.datatransfer.MimeContentTypeFactory")), |
| static_cast<XClipboardEx*>(this)); |
| } |
| |
| mpDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper()); |
| |
| if (pasteboard != NULL) |
| { |
| mPasteboard = pasteboard; |
| mIsSystemPasteboard = false; |
| } |
| else |
| { |
| mPasteboard = bUseSystemPasteboard ? [NSPasteboard generalPasteboard] : |
| [NSPasteboard pasteboardWithName: NSDragPboard]; |
| |
| if (mPasteboard == nil) |
| { |
| throw RuntimeException(OUString( |
| RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create Cocoa pasteboard")), |
| static_cast<XClipboardEx*>(this)); |
| } |
| } |
| |
| [mPasteboard retain]; |
| |
| mEventListener = [[EventListener alloc] initWithAquaClipboard: this]; |
| |
| if (mEventListener == nil) |
| { |
| [mPasteboard release]; |
| |
| throw RuntimeException( |
| OUString(RTL_CONSTASCII_USTRINGPARAM("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]; |
| } |
| |
| |
| Reference<XTransferable> SAL_CALL AquaClipboard::getContents() throw(RuntimeException) |
| { |
| MutexGuard aGuard(m_aMutex); |
| |
| // 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 Reference<XTransferable>(new OSXTransferable(mrXMimeCntFactory, |
| mpDataFlavorMapper, |
| mPasteboard)); |
| } |
| |
| |
| void SAL_CALL AquaClipboard::setContents(const Reference<XTransferable>& xTransferable, |
| const Reference<XClipboardOwner>& xClipboardOwner) |
| throw( RuntimeException ) |
| { |
| NSArray* types = xTransferable.is() ? |
| mpDataFlavorMapper->flavorSequenceToTypesArray(xTransferable->getTransferDataFlavors()) : |
| [NSArray array]; |
| |
| ClearableMutexGuard aGuard(m_aMutex); |
| |
| Reference<XClipboardOwner> oldOwner(mXClipboardOwner); |
| mXClipboardOwner = xClipboardOwner; |
| |
| Reference<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() throw( RuntimeException ) |
| { |
| return OUString(); |
| } |
| |
| |
| sal_Int8 SAL_CALL AquaClipboard::getRenderingCapabilities() throw( RuntimeException ) |
| { |
| return 0; |
| } |
| |
| |
| void SAL_CALL AquaClipboard::addClipboardListener(const Reference< XClipboardListener >& listener) |
| throw( RuntimeException ) |
| { |
| MutexGuard aGuard(m_aMutex); |
| |
| if (!listener.is()) |
| throw IllegalArgumentException(OUString(RTL_CONSTASCII_USTRINGPARAM("empty reference")), |
| static_cast<XClipboardEx*>(this), 1); |
| |
| mClipboardListeners.push_back(listener); |
| } |
| |
| |
| void SAL_CALL AquaClipboard::removeClipboardListener(const Reference< XClipboardListener >& listener) |
| throw( RuntimeException ) |
| { |
| MutexGuard aGuard(m_aMutex); |
| |
| if (!listener.is()) |
| throw IllegalArgumentException(OUString(RTL_CONSTASCII_USTRINGPARAM("empty reference")), |
| static_cast<XClipboardEx*>(this), 1); |
| |
| mClipboardListeners.remove(listener); |
| } |
| |
| |
| void AquaClipboard::applicationDidBecomeActive(NSNotification*) |
| { |
| 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 |
| Reference<XClipboardOwner> oldOwner(mXClipboardOwner); |
| mXClipboardOwner = Reference<XClipboardOwner>(); |
| |
| Reference<XTransferable> oldContent(mXClipboardContent); |
| mXClipboardContent = Reference<XTransferable>(); |
| |
| aGuard.clear(); |
| |
| if (oldOwner.is()) |
| { |
| fireLostClipboardOwnershipEvent(oldOwner, oldContent); |
| } |
| |
| fireClipboardChangedEvent(); |
| } |
| } |
| |
| |
| void AquaClipboard::fireClipboardChangedEvent() |
| { |
| ClearableMutexGuard aGuard(m_aMutex); |
| |
| list<Reference< XClipboardListener > > listeners(mClipboardListeners); |
| ClipboardEvent aEvent; |
| |
| if (listeners.begin() != listeners.end()) |
| { |
| aEvent = ClipboardEvent(static_cast<OWeakObject*>(this), getContents()); |
| } |
| |
| aGuard.clear(); |
| |
| while (listeners.begin() != listeners.end()) |
| { |
| if (listeners.front().is()) |
| { |
| try { listeners.front()->changedContents(aEvent); } |
| catch (RuntimeException&) { } |
| } |
| listeners.pop_front(); |
| } |
| } |
| |
| |
| void AquaClipboard::fireLostClipboardOwnershipEvent(Reference<XClipboardOwner> oldOwner, Reference<XTransferable> oldContent) |
| { |
| BOOST_ASSERT(oldOwner.is()); |
| |
| try { oldOwner->lostOwnership(static_cast<XClipboardEx*>(this), oldContent); } |
| catch(RuntimeException&) { } |
| } |
| |
| |
| void AquaClipboard::provideDataForType(NSPasteboard* sender, const NSString* type) |
| { |
| if( mXClipboardContent.is() ) |
| { |
| DataProviderPtr_t dp = mpDataFlavorMapper->getDataProvider(type, mXClipboardContent); |
| NSData* pBoardData = NULL; |
| |
| if (dp.get() != NULL) |
| { |
| pBoardData = (NSData*)dp->getSystemData(); |
| [sender setData: pBoardData forType:const_cast<NSString*>(type)]; |
| } |
| } |
| } |
| |
| |
| //------------------------------------------------ |
| // XFlushableClipboard |
| //------------------------------------------------ |
| |
| void SAL_CALL AquaClipboard::flushClipboard() |
| throw(RuntimeException) |
| { |
| if (mXClipboardContent.is()) |
| { |
| Sequence<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 != NULL) |
| { |
| provideDataForType(mPasteboard, sysType); |
| } |
| } |
| mXClipboardContent.clear(); |
| } |
| } |
| |
| |
| NSPasteboard* AquaClipboard::getPasteboard() const |
| { |
| return mPasteboard; |
| } |
| |
| |
| //------------------------------------------------- |
| // XServiceInfo |
| //------------------------------------------------- |
| |
| OUString SAL_CALL AquaClipboard::getImplementationName() throw( RuntimeException ) |
| { |
| return clipboard_getImplementationName(); |
| } |
| |
| |
| sal_Bool SAL_CALL AquaClipboard::supportsService( const OUString& /*ServiceName*/ ) throw( RuntimeException ) |
| { |
| return sal_False; |
| } |
| |
| |
| Sequence< OUString > SAL_CALL AquaClipboard::getSupportedServiceNames() throw( RuntimeException ) |
| { |
| return clipboard_getSupportedServiceNames(); |
| } |
| |