1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_vcl.hxx" 26 #include "aqua_clipboard.hxx" 27 28 #include "DataFlavorMapping.hxx" 29 #include "OSXTransferable.hxx" 30 31 #include "vcl/unohelp.hxx" 32 33 #include "comphelper/makesequence.hxx" 34 35 #include <boost/assert.hpp> 36 37 using namespace com::sun::star::datatransfer; 38 using namespace com::sun::star::datatransfer::clipboard; 39 using namespace com::sun::star::lang; 40 using namespace com::sun::star::uno; 41 using namespace cppu; 42 using namespace osl; 43 using namespace rtl; 44 using namespace std; 45 using namespace comphelper; 46 47 48 @implementation EventListener; 49 50 -(EventListener*)initWithAquaClipboard: (AquaClipboard*) pcb 51 { 52 self = [super init]; 53 54 if (self) 55 pAquaClipboard = pcb; 56 57 return self; 58 } 59 60 -(void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type 61 { 62 if( pAquaClipboard ) 63 pAquaClipboard->provideDataForType(sender, type); 64 } 65 66 -(void)applicationDidBecomeActive:(NSNotification*)aNotification 67 { 68 if( pAquaClipboard ) 69 pAquaClipboard->applicationDidBecomeActive(aNotification); 70 } 71 72 -(void)disposing 73 { 74 pAquaClipboard = NULL; 75 } 76 77 @end 78 79 80 OUString clipboard_getImplementationName() 81 { 82 return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.AquaClipboard")); 83 } 84 85 Sequence<OUString> clipboard_getSupportedServiceNames() 86 { 87 return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.SystemClipboard"))); 88 } 89 90 91 AquaClipboard::AquaClipboard(NSPasteboard* pasteboard, bool bUseSystemPasteboard) : 92 WeakComponentImplHelper4<XClipboardEx, XClipboardNotifier, XFlushableClipboard, XServiceInfo>(m_aMutex), 93 mIsSystemPasteboard(bUseSystemPasteboard) 94 { 95 Reference<XMultiServiceFactory> mrServiceMgr = vcl::unohelper::GetMultiServiceFactory(); 96 97 mrXMimeCntFactory = Reference<XMimeContentTypeFactory>(mrServiceMgr->createInstance( 98 OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.MimeContentTypeFactory"))), UNO_QUERY); 99 100 if (!mrXMimeCntFactory.is()) 101 { 102 throw RuntimeException(OUString( 103 RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create com.sun.star.datatransfer.MimeContentTypeFactory")), 104 static_cast<XClipboardEx*>(this)); 105 } 106 107 mpDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper()); 108 109 if (pasteboard != NULL) 110 { 111 mPasteboard = pasteboard; 112 mIsSystemPasteboard = false; 113 } 114 else 115 { 116 mPasteboard = bUseSystemPasteboard ? [NSPasteboard generalPasteboard] : 117 [NSPasteboard pasteboardWithName: NSDragPboard]; 118 119 if (mPasteboard == nil) 120 { 121 throw RuntimeException(OUString( 122 RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create Cocoa pasteboard")), 123 static_cast<XClipboardEx*>(this)); 124 } 125 } 126 127 [mPasteboard retain]; 128 129 mEventListener = [[EventListener alloc] initWithAquaClipboard: this]; 130 131 if (mEventListener == nil) 132 { 133 [mPasteboard release]; 134 135 throw RuntimeException( 136 OUString(RTL_CONSTASCII_USTRINGPARAM("AquaClipboard: Cannot create pasteboard change listener")), 137 static_cast<XClipboardEx*>(this)); 138 } 139 140 if (mIsSystemPasteboard) 141 { 142 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; 143 144 [notificationCenter addObserver: mEventListener 145 selector: @selector(applicationDidBecomeActive:) 146 name: @"NSApplicationDidBecomeActiveNotification" 147 object: [NSApplication sharedApplication]]; 148 } 149 150 mPasteboardChangeCount = [mPasteboard changeCount]; 151 } 152 153 154 AquaClipboard::~AquaClipboard() 155 { 156 if (mIsSystemPasteboard) 157 { 158 [[NSNotificationCenter defaultCenter] removeObserver: mEventListener]; 159 } 160 161 [mEventListener disposing]; 162 [mEventListener release]; 163 [mPasteboard release]; 164 } 165 166 167 Reference<XTransferable> SAL_CALL AquaClipboard::getContents() throw(RuntimeException) 168 { 169 MutexGuard aGuard(m_aMutex); 170 171 // Shortcut: If we are clipboard owner already we don't need 172 // to drag the data through the system clipboard 173 if (mXClipboardContent.is()) 174 { 175 return mXClipboardContent; 176 } 177 178 return Reference<XTransferable>(new OSXTransferable(mrXMimeCntFactory, 179 mpDataFlavorMapper, 180 mPasteboard)); 181 } 182 183 184 void SAL_CALL AquaClipboard::setContents(const Reference<XTransferable>& xTransferable, 185 const Reference<XClipboardOwner>& xClipboardOwner) 186 throw( RuntimeException ) 187 { 188 NSArray* types = xTransferable.is() ? 189 mpDataFlavorMapper->flavorSequenceToTypesArray(xTransferable->getTransferDataFlavors()) : 190 [NSArray array]; 191 192 ClearableMutexGuard aGuard(m_aMutex); 193 194 Reference<XClipboardOwner> oldOwner(mXClipboardOwner); 195 mXClipboardOwner = xClipboardOwner; 196 197 Reference<XTransferable> oldContent(mXClipboardContent); 198 mXClipboardContent = xTransferable; 199 200 mPasteboardChangeCount = [mPasteboard declareTypes: types owner: mEventListener]; 201 202 aGuard.clear(); 203 204 // if we are already the owner of the clipboard 205 // then fire lost ownership event 206 if (oldOwner.is()) 207 { 208 fireLostClipboardOwnershipEvent(oldOwner, oldContent); 209 } 210 211 fireClipboardChangedEvent(); 212 } 213 214 215 OUString SAL_CALL AquaClipboard::getName() throw( RuntimeException ) 216 { 217 return OUString(); 218 } 219 220 221 sal_Int8 SAL_CALL AquaClipboard::getRenderingCapabilities() throw( RuntimeException ) 222 { 223 return 0; 224 } 225 226 227 void SAL_CALL AquaClipboard::addClipboardListener(const Reference< XClipboardListener >& listener) 228 throw( RuntimeException ) 229 { 230 MutexGuard aGuard(m_aMutex); 231 232 if (!listener.is()) 233 throw IllegalArgumentException(OUString(RTL_CONSTASCII_USTRINGPARAM("empty reference")), 234 static_cast<XClipboardEx*>(this), 1); 235 236 mClipboardListeners.push_back(listener); 237 } 238 239 240 void SAL_CALL AquaClipboard::removeClipboardListener(const Reference< XClipboardListener >& listener) 241 throw( RuntimeException ) 242 { 243 MutexGuard aGuard(m_aMutex); 244 245 if (!listener.is()) 246 throw IllegalArgumentException(OUString(RTL_CONSTASCII_USTRINGPARAM("empty reference")), 247 static_cast<XClipboardEx*>(this), 1); 248 249 mClipboardListeners.remove(listener); 250 } 251 252 253 void AquaClipboard::applicationDidBecomeActive(NSNotification*) 254 { 255 ClearableMutexGuard aGuard(m_aMutex); 256 257 int currentPboardChgCount = [mPasteboard changeCount]; 258 259 if (currentPboardChgCount != mPasteboardChangeCount) 260 { 261 mPasteboardChangeCount = currentPboardChgCount; 262 263 // Clear clipboard content and owner and send lostOwnership 264 // notification to the old clipboard owner as well as 265 // ClipboardChanged notification to any clipboard listener 266 Reference<XClipboardOwner> oldOwner(mXClipboardOwner); 267 mXClipboardOwner = Reference<XClipboardOwner>(); 268 269 Reference<XTransferable> oldContent(mXClipboardContent); 270 mXClipboardContent = Reference<XTransferable>(); 271 272 aGuard.clear(); 273 274 if (oldOwner.is()) 275 { 276 fireLostClipboardOwnershipEvent(oldOwner, oldContent); 277 } 278 279 fireClipboardChangedEvent(); 280 } 281 } 282 283 284 void AquaClipboard::fireClipboardChangedEvent() 285 { 286 ClearableMutexGuard aGuard(m_aMutex); 287 288 list<Reference< XClipboardListener > > listeners(mClipboardListeners); 289 ClipboardEvent aEvent; 290 291 if (listeners.begin() != listeners.end()) 292 { 293 aEvent = ClipboardEvent(static_cast<OWeakObject*>(this), getContents()); 294 } 295 296 aGuard.clear(); 297 298 while (listeners.begin() != listeners.end()) 299 { 300 if (listeners.front().is()) 301 { 302 try { listeners.front()->changedContents(aEvent); } 303 catch (RuntimeException&) { } 304 } 305 listeners.pop_front(); 306 } 307 } 308 309 310 void AquaClipboard::fireLostClipboardOwnershipEvent(Reference<XClipboardOwner> oldOwner, Reference<XTransferable> oldContent) 311 { 312 BOOST_ASSERT(oldOwner.is()); 313 314 try { oldOwner->lostOwnership(static_cast<XClipboardEx*>(this), oldContent); } 315 catch(RuntimeException&) { } 316 } 317 318 319 void AquaClipboard::provideDataForType(NSPasteboard* sender, NSString* type) 320 { 321 if( mXClipboardContent.is() ) 322 { 323 DataProviderPtr_t dp = mpDataFlavorMapper->getDataProvider(type, mXClipboardContent); 324 NSData* pBoardData = NULL; 325 326 if (dp.get() != NULL) 327 { 328 pBoardData = (NSData*)dp->getSystemData(); 329 [sender setData: pBoardData forType: type]; 330 } 331 } 332 } 333 334 335 //------------------------------------------------ 336 // XFlushableClipboard 337 //------------------------------------------------ 338 339 void SAL_CALL AquaClipboard::flushClipboard() 340 throw(RuntimeException) 341 { 342 if (mXClipboardContent.is()) 343 { 344 Sequence<DataFlavor> flavorList = mXClipboardContent->getTransferDataFlavors(); 345 sal_uInt32 nFlavors = flavorList.getLength(); 346 347 for (sal_uInt32 i = 0; i < nFlavors; i++) 348 { 349 NSString* sysType = mpDataFlavorMapper->openOfficeToSystemFlavor(flavorList[i]); 350 351 if (sysType != NULL) 352 { 353 provideDataForType(mPasteboard, sysType); 354 } 355 } 356 mXClipboardContent.clear(); 357 } 358 } 359 360 361 NSPasteboard* AquaClipboard::getPasteboard() const 362 { 363 return mPasteboard; 364 } 365 366 367 //------------------------------------------------- 368 // XServiceInfo 369 //------------------------------------------------- 370 371 OUString SAL_CALL AquaClipboard::getImplementationName() throw( RuntimeException ) 372 { 373 return clipboard_getImplementationName(); 374 } 375 376 377 sal_Bool SAL_CALL AquaClipboard::supportsService( const OUString& /*ServiceName*/ ) throw( RuntimeException ) 378 { 379 return sal_False; 380 } 381 382 383 Sequence< OUString > SAL_CALL AquaClipboard::getSupportedServiceNames() throw( RuntimeException ) 384 { 385 return clipboard_getSupportedServiceNames(); 386 } 387 388