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 //------------------------------------------------------------------------
25 // includes
26 //------------------------------------------------------------------------
27 
28 #include "Os2Clipboard.hxx"
29 
30 //------------------------------------------------------------------------
31 // namespace directives
32 //------------------------------------------------------------------------
33 
34 using namespace com::sun::star::datatransfer;
35 using namespace com::sun::star::datatransfer::clipboard;
36 using namespace com::sun::star::datatransfer::clipboard::RenderingCapabilities;
37 using namespace com::sun::star::lang;
38 using namespace com::sun::star::uno;
39 using namespace cppu;
40 using namespace osl;
41 using namespace rtl;
42 using namespace os2;
43 
44 const Type CPPUTYPE_SEQINT8	 = getCppuType( ( Sequence< sal_Int8 >* )0 );
45 const Type CPPUTYPE_OUSTRING = getCppuType( (OUString*)0 );
46 
47 #define DTRANS_OBJ_CLASSNAME "DTRANSOBJWND"
48 
49 // -----------------------------------------------------------------------
50 
51 inline void SetWindowPtr( HWND hWnd, Os2Clipboard* pThis )
52 {
53 	WinSetWindowULong( hWnd, QWL_USER, (ULONG)pThis );
54 }
55 
56 inline Os2Clipboard* GetWindowPtr( HWND hWnd )
57 {
58 	return (Os2Clipboard*)WinQueryWindowULong( hWnd, QWL_USER );
59 }
60 
61 // -----------------------------------------------------------------------
62 
63 MRESULT EXPENTRY DtransObjWndProc( HWND hWnd, ULONG nMsg, MPARAM nMP1, MPARAM nMP2 )
64 {
65 
66 	switch ( nMsg )
67 	{
68 	case WM_DRAWCLIPBOARD:	// clipboard content has changed
69 		{
70 			Os2Clipboard* os2Clipboard = GetWindowPtr( hWnd);
71 			if (os2Clipboard)
72 			{
73 				//MutexGuard aGuard(os2Clipboard->m_aMutex);
74 				debug_printf("WM_DRAWCLIPBOARD os2Clipboard %08x\n", os2Clipboard);
75 				if (os2Clipboard->m_bInSetClipboardData)
76 				{
77 					debug_printf("WM_DRAWCLIPBOARD our change\n");
78 				}
79 				else
80 				{
81 					// notify listener for clipboard change
82 					debug_printf("WM_DRAWCLIPBOARD notify change\n");
83 					os2Clipboard->notifyAllClipboardListener();
84 				}
85 			}
86 		}
87 		break;
88 	}
89 
90 	return WinDefWindowProc( hWnd, nMsg, nMP1, nMP2 );
91 }
92 
93 // -----------------------------------------------------------------------
94 
95 Os2Clipboard::Os2Clipboard() :
96 	m_aMutex(),
97 	WeakComponentImplHelper4< XClipboardEx, XClipboardNotifier, XServiceInfo, XInitialization > (m_aMutex),
98 	m_bInitialized(sal_False),
99 	m_bInSetClipboardData(sal_False)
100 {
101 	MutexGuard aGuard(m_aMutex);
102 
103 	debug_printf("Os2Clipboard::Os2Clipboard\n");
104 	hAB = WinQueryAnchorBlock( HWND_DESKTOP );
105 	hText = 0;
106 	hBitmap = 0;
107 
108 #if 0
109 	// register object class
110 	if ( WinRegisterClass( hAB, (PSZ)DTRANS_OBJ_CLASSNAME,
111 							(PFNWP)DtransObjWndProc, 0, sizeof(ULONG) ))
112 	{
113 		APIRET	rc;
114 		// create object window to get clip viewer messages
115 		hObjWnd = WinCreateWindow( HWND_OBJECT, (PCSZ)DTRANS_OBJ_CLASSNAME,
116 										(PCSZ)"", 0, 0, 0, 0, 0,
117 										HWND_OBJECT, HWND_TOP,
118 										222, NULL, NULL);
119 		// store pointer
120 		SetWindowPtr( hObjWnd, this);
121 		// register the viewer window
122 		rc = WinOpenClipbrd(hAB);
123 		rc = WinSetClipbrdViewer(hAB, hObjWnd);
124 		rc = WinCloseClipbrd(hAB);
125 	}
126 #endif
127 
128 }
129 
130 Os2Clipboard::~Os2Clipboard()
131 {
132 	debug_printf("Os2Clipboard::~Os2Clipboard\n");
133 }
134 
135 void SAL_CALL Os2Clipboard::initialize( const Sequence< Any >& aArguments )
136 	throw(Exception, RuntimeException)
137 {
138 	if (!m_bInitialized)
139 	{
140 		for (sal_Int32 n = 0, nmax = aArguments.getLength(); n < nmax; n++)
141 			if (aArguments[n].getValueType() == getCppuType((OUString *) 0))
142 			{
143 				aArguments[0] >>= m_aName;
144 				break;
145 			}
146 	}
147 }
148 
149 OUString SAL_CALL Os2Clipboard::getImplementationName() throw( RuntimeException )
150 {
151 	debug_printf("Os2Clipboard::getImplementationName\n");
152 	return OUString::createFromAscii( OS2_CLIPBOARD_IMPL_NAME );
153 }
154 
155 sal_Bool SAL_CALL Os2Clipboard::supportsService( const OUString& ServiceName ) throw( RuntimeException )
156 {
157 	debug_printf("Os2Clipboard::supportsService\n");
158 	Sequence < OUString > SupportedServicesNames = Os2Clipboard_getSupportedServiceNames();
159 
160 	for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; )
161 		if (SupportedServicesNames[n].compareTo(ServiceName) == 0)
162 			return sal_True;
163 
164 	return sal_False;
165 }
166 
167 Sequence< OUString > SAL_CALL Os2Clipboard::getSupportedServiceNames() throw( RuntimeException )
168 {
169 	debug_printf("Os2Clipboard::getSupportedServiceNames\n");
170 	return Os2Clipboard_getSupportedServiceNames();
171 }
172 
173 Reference< XTransferable > SAL_CALL Os2Clipboard::getContents() throw( RuntimeException )
174 {
175 	debug_printf("Os2Clipboard::getContents\n");
176 	MutexGuard aGuard(m_aMutex);
177 
178 	// os2 can have only one viewer at time, and we don't get a notification
179 	// when the viewer changes. So we need to check handles of clipboard
180 	// data and compare with previous handles
181 	if (UWinOpenClipbrd(hAB)) {
182 		sal_Bool	fireChanged = sal_False;
183 		ULONG handle = UWinQueryClipbrdData( hAB, UCLIP_CF_UNICODETEXT);
184 		if (handle) {
185 			if (handle != hText) {
186 				hText = handle;
187 				fireChanged = sal_True;
188 			}
189 		}
190 		handle = UWinQueryClipbrdData( hAB, UCLIP_CF_BITMAP);
191 		if (handle) {
192 			if (handle != hBitmap) {
193 				hBitmap = handle;
194 				fireChanged = sal_True;
195 			}
196 		}
197 		UWinCloseClipbrd( hAB);
198 		if (fireChanged)
199 		{
200 			// notify listener for clipboard change
201 			debug_printf("Os2Clipboard::getContents notify change\n");
202 			notifyAllClipboardListener();
203 		}
204 	}
205 
206 	if( ! m_aContents.is() )
207 		m_aContents = new Os2Transferable( static_cast< OWeakObject* >(this) );
208 
209 	return m_aContents;
210 }
211 
212 void SAL_CALL Os2Clipboard::setContents( const Reference< XTransferable >& xTrans, const Reference< XClipboardOwner >& xClipboardOwner ) throw( RuntimeException )
213 {
214 	debug_printf("Os2Clipboard::setContents\n");
215 	// remember old values for callbacks before setting the new ones.
216 	ClearableMutexGuard aGuard(m_aMutex);
217 
218 	Reference< XClipboardOwner > oldOwner(m_aOwner);
219 	m_aOwner = xClipboardOwner;
220 
221 	Reference< XTransferable > oldContents(m_aContents);
222 	m_aContents = xTrans;
223 
224 	aGuard.clear();
225 
226 	// notify old owner on loss of ownership
227 	if( oldOwner.is() )
228 		oldOwner->lostOwnership(static_cast < XClipboard * > (this), oldContents);
229 
230 	// notify all listeners on content changes
231 	OInterfaceContainerHelper *pContainer =
232 		rBHelper.aLC.getContainer(getCppuType( (Reference < XClipboardListener > *) 0));
233 	if (pContainer)
234 	{
235 		ClipboardEvent aEvent(static_cast < XClipboard * > (this), m_aContents);
236 		OInterfaceIteratorHelper aIterator(*pContainer);
237 
238 		while (aIterator.hasMoreElements())
239 		{
240 			Reference < XClipboardListener > xListener(aIterator.next(), UNO_QUERY);
241 			if (xListener.is())
242 				xListener->changedContents(aEvent);
243 		}
244 	}
245 
246 #if OSL_DEBUG_LEVEL>0
247 	// dump list of available mimetypes
248 	Sequence< DataFlavor > aFlavors( m_aContents->getTransferDataFlavors() );
249 	for( int i = 0; i < aFlavors.getLength(); i++ )
250 		debug_printf("Os2Clipboard::setContents available mimetype: %d %s\n",
251 			i, CHAR_POINTER(aFlavors.getConstArray()[i].MimeType));
252 #endif
253 
254 	// we can only export text or bitmap
255 	DataFlavor nFlavorText( OUString::createFromAscii( "text/plain;charset=utf-16" ),
256 						OUString::createFromAscii( "Unicode-Text" ), CPPUTYPE_OUSTRING);
257 	DataFlavor nFlavorBitmap( OUString::createFromAscii( "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"" ),
258 						OUString::createFromAscii( "Bitmap" ), CPPUTYPE_DEFAULT);
259 
260 	// try text transfer data (if any)
261 	PSZ pSharedText = NULL;
262 	HBITMAP hbm = NULL;
263 	try
264 	{
265 		Any aAny = m_aContents->getTransferData( nFlavorText );
266 		if (aAny.hasValue())
267 		{
268 			APIRET rc;
269 			// copy unicode text to clipboard
270 			OUString aString;
271 			aAny >>= aString;
272 			// share text
273 			rc = DosAllocSharedMem( (PPVOID) &pSharedText, NULL,
274 				aString.getLength() * 2 + 2,
275 				PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE | OBJ_ANY);
276 			if (!rc)
277 				memcpy( pSharedText, aString.getStr(), aString.getLength() * 2 + 2 );
278 			else
279 				pSharedText = NULL;
280 			debug_printf("Os2Clipboard::setContents SetClipbrdData text done\n");
281 		}
282 	} catch ( UnsupportedFlavorException&) {
283 		debug_printf("Os2Clipboard::setContents UnsupportedFlavorException (no text)\n");
284 	}
285 
286 	// try bitmap transfer data (if any)
287 	try
288 	{
289 		Any aAnyB = m_aContents->getTransferData( nFlavorBitmap );
290 		if (aAnyB.hasValue())
291 		{
292 			hbm = OOoBmpToOS2Handle( aAnyB);
293 			debug_printf("Os2Clipboard::setContents SetClipbrdData bitmap done\n");
294 		}
295 	} catch ( UnsupportedFlavorException&) {
296 		debug_printf("Os2Clipboard::setContents UnsupportedFlavorException (no bitmap)\n");
297 	}
298 
299 	// copy to clipboard
300 	if ( UWinOpenClipbrd( hAB) && (pSharedText || hbm))
301 	{
302 		// set the flag, so we will ignore the next WM_DRAWCLIPBOARD
303 		// since we generate it with following code.
304 		m_bInSetClipboardData = sal_True;
305 		UWinEmptyClipbrd( hAB);
306 		// give pointer to clipboard (it will become owner of pSharedText!)
307 		if (pSharedText) {
308 			UWinSetClipbrdData( hAB, (ULONG) pSharedText, UCLIP_CF_UNICODETEXT, CFI_POINTER);
309 			// update internal handle to avoid detection of this text as new data
310 			hText = (ULONG)pSharedText;
311 		}
312 		// give bitmap to clipboard
313 		if (hbm) {
314 			UWinSetClipbrdData( hAB, (ULONG) hbm, UCLIP_CF_BITMAP, CFI_HANDLE);
315 			// update internal handle to avoid detection of this bitmap as new data
316 			hBitmap = hbm;
317 		}
318 		// reset the flag, so we will not ignore next WM_DRAWCLIPBOARD
319 		m_bInSetClipboardData = sal_False;
320 		UWinCloseClipbrd( hAB);
321 	}
322 
323 }
324 
325 OUString SAL_CALL Os2Clipboard::getName() throw( RuntimeException )
326 {
327 	debug_printf("Os2Clipboard::getName\n");
328 	return m_aName;
329 }
330 
331 sal_Int8 SAL_CALL Os2Clipboard::getRenderingCapabilities() throw( RuntimeException )
332 {
333 	debug_printf("Os2Clipboard::getRenderingCapabilities\n");
334 	return Delayed;
335 }
336 
337 //========================================================================
338 // XClipboardNotifier
339 //========================================================================
340 
341 void SAL_CALL Os2Clipboard::addClipboardListener( const Reference< XClipboardListener >& listener ) throw( RuntimeException )
342 {
343 	debug_printf("Os2Clipboard::addClipboardListener\n");
344 	MutexGuard aGuard( rBHelper.rMutex );
345 	OSL_ENSURE( !rBHelper.bInDispose, "do not add listeners in the dispose call" );
346 	OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" );
347 	if (!rBHelper.bInDispose && !rBHelper.bDisposed)
348 		rBHelper.aLC.addInterface( getCppuType( (const ::com::sun::star::uno::Reference< XClipboardListener > *) 0), listener );
349 }
350 
351 void SAL_CALL Os2Clipboard::removeClipboardListener( const Reference< XClipboardListener >& listener ) throw( RuntimeException )
352 {
353 	debug_printf("Os2Clipboard::removeClipboardListener\n");
354 	MutexGuard aGuard( rBHelper.rMutex );
355 	OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" );
356 	if (!rBHelper.bInDispose && !rBHelper.bDisposed)
357 		rBHelper.aLC.removeInterface( getCppuType( (const Reference< XClipboardListener > *) 0 ), listener ); \
358 }
359 
360 // ------------------------------------------------------------------------
361 
362 void SAL_CALL Os2Clipboard::notifyAllClipboardListener( )
363 {
364 	if ( !rBHelper.bDisposed )
365 	{
366 		ClearableMutexGuard aGuard( rBHelper.rMutex );
367 		if ( !rBHelper.bDisposed )
368 		{
369 			aGuard.clear( );
370 
371 			ClearableMutexGuard aGuard(m_aMutex);
372 			// copy member references on stack so they can be called
373 			// without having the mutex
374 			Reference< XClipboardOwner > xOwner( m_aOwner );
375 			Reference< XTransferable > xTrans( m_aContents );
376 			// clear members
377 			m_aOwner.clear();
378 			m_aContents.clear();
379 			// release the mutex
380 			aGuard.clear();
381 
382 			// inform previous owner of lost ownership
383 			if ( xOwner.is() )
384 				xOwner->lostOwnership(static_cast < XClipboard * > (this), m_aContents);
385 
386 			OInterfaceContainerHelper* pICHelper = rBHelper.aLC.getContainer(
387 				getCppuType( ( Reference< XClipboardListener > * ) 0 ) );
388 
389 			if ( pICHelper )
390 			{
391 				try
392 				{
393 					OInterfaceIteratorHelper iter(*pICHelper);
394 					m_aContents = 0;
395 					m_aContents = new Os2Transferable( static_cast< OWeakObject* >(this) );
396 					ClipboardEvent aClipbEvent(static_cast<XClipboard*>(this), m_aContents);
397 
398 					while(iter.hasMoreElements())
399 					{
400 						try
401 						{
402 							Reference<XClipboardListener> xCBListener(iter.next(), UNO_QUERY);
403 							if (xCBListener.is())
404 								xCBListener->changedContents(aClipbEvent);
405 						}
406 						catch(RuntimeException&)
407 						{
408 							OSL_ENSURE( false, "RuntimeException caught" );
409 							debug_printf( "RuntimeException caught" );
410 						}
411 					}
412 				}
413 				catch(const ::com::sun::star::lang::DisposedException&)
414 				{
415 					OSL_ENSURE(false, "Service Manager disposed");
416 					debug_printf( "Service Manager disposed");
417 
418 					// no further clipboard changed notifications
419 					//m_pImpl->unregisterClipboardViewer();
420 				}
421 
422 			} // end if
423 		} // end if
424 	} // end if
425 }
426 
427 // ------------------------------------------------------------------------
428 
429 Sequence< OUString > SAL_CALL Os2Clipboard_getSupportedServiceNames()
430 {
431 	Sequence< OUString > aRet(1);
432 	aRet[0] = OUString::createFromAscii( OS2_CLIPBOARD_SERVICE_NAME );
433 	return aRet;
434 }
435 
436 // ------------------------------------------------------------------------
437 
438 Reference< XInterface > SAL_CALL Os2Clipboard_createInstance(
439 	const Reference< XMultiServiceFactory > & xMultiServiceFactory)
440 {
441 	return Reference < XInterface >( ( OWeakObject * ) new Os2Clipboard());
442 }
443 
444