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