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_extensions.hxx"
30 #include <com/sun/star/uno/Any.hxx>
31 #include <com/sun/star/uno/Reference.hxx>
32 #include <com/sun/star/util/XCloseable.hpp>
33 #include <com/sun/star/util/XCloseBroadcaster.hpp>
34 #include <com/sun/star/util/XCloseListener.hpp>
35 #include <com/sun/star/frame/XFrame.hpp>
36 #include <com/sun/star/frame/XDesktop.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <cppuhelper/implbase1.hxx>
39 #include <comphelper/processfactory.hxx>
40 
41 #include <math.h>
42 #include <tools/svwin.h>
43 #include <tools/stream.hxx>
44 #include <vos/mutex.hxx>
45 #include <vos/module.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/wrkwin.hxx>
48 #include <vcl/sysdata.hxx>
49 #include <vcl/salbtype.hxx>
50 #include "scanner.hxx"
51 
52 #pragma warning (push,1)
53 #pragma warning (disable:4668)
54 #include "twain/twain.h"
55 #pragma warning (pop)
56 
57 using namespace ::com::sun::star;
58 
59 // -----------
60 // - Defines -
61 // -----------
62 
63 #define TWAIN_SELECT			0x00000001UL
64 #define TWAIN_ACQUIRE			0x00000002UL
65 #define TWAIN_TERMINATE			0xFFFFFFFFUL
66 
67 #define TWAIN_EVENT_NONE		0x00000000UL
68 #define TWAIN_EVENT_QUIT		0x00000001UL
69 #define TWAIN_EVENT_SCANNING	0x00000002UL
70 #define TWAIN_EVENT_XFER		0x00000004UL
71 
72 #define PFUNC					(*pDSM)
73 #define PTWAINMSG				MSG*
74 #define FIXTODOUBLE( nFix ) 	((double)nFix.Whole+(double)nFix.Frac/65536.)
75 #define FIXTOLONG( nFix )		((long)floor(FIXTODOUBLE(nFix)+0.5))
76 
77 #if defined WNT
78 #define TWAIN_LIBNAME			"TWAIN_32.DLL"
79 #define TWAIN_FUNCNAME			"DSM_Entry"
80 #endif
81 
82 // --------------
83 // - TwainState -
84 // --------------
85 
86 enum TwainState
87 {
88 	TWAIN_STATE_NONE = 0,
89 	TWAIN_STATE_SCANNING = 1,
90 	TWAIN_STATE_DONE = 2,
91 	TWAIN_STATE_CANCELED = 3
92 };
93 
94 // ------------
95 // - ImpTwain -
96 // ------------
97 
98 class ImpTwain : public ::cppu::WeakImplHelper1< util::XCloseListener >
99 {
100     friend LRESULT CALLBACK TwainMsgProc( int nCode, WPARAM wParam, LPARAM lParam );
101 
102     uno::Reference< uno::XInterface >			mxSelfRef;
103 	uno::Reference< scanner::XScannerManager >	mxMgr;
104 	ScannerManager&								mrMgr;
105 	TW_IDENTITY									aAppIdent;
106 	TW_IDENTITY									aSrcIdent;
107 	Link										aNotifyLink;
108 	DSMENTRYPROC								pDSM;
109 	vos:: OModule *					pMod;
110 	ULONG										nCurState;
111 	HWND						                hTwainWnd;
112 	HHOOK						                hTwainHook;
113     bool										mbCloseFrameOnExit;
114 
115 	bool						                ImplHandleMsg( void* pMsg );
116 	void										ImplCreate();
117 	void										ImplOpenSourceManager();
118 	void										ImplOpenSource();
119 	bool										ImplEnableSource();
120 	void										ImplXfer();
121 	void										ImplFallback( ULONG nEvent );
122     void 										ImplSendCloseEvent();
123     void 										ImplDeregisterCloseListener();
124     void 										ImplRegisterCloseListener();
125     uno::Reference< frame::XFrame > 			ImplGetActiveFrame();
126     uno::Reference< util::XCloseBroadcaster > 	ImplGetActiveFrameCloseBroadcaster();
127 
128 												DECL_LINK( ImplFallbackHdl, void* );
129 												DECL_LINK( ImplDestroyHdl, void* );
130 
131     // from util::XCloseListener
132     virtual void SAL_CALL queryClosing( const lang::EventObject& Source, sal_Bool GetsOwnership ) throw (util::CloseVetoException, uno::RuntimeException);
133     virtual void SAL_CALL notifyClosing( const lang::EventObject& Source ) throw (uno::RuntimeException);
134 
135     // from lang::XEventListener
136     virtual void SAL_CALL disposing( const lang::EventObject& Source ) throw (uno::RuntimeException);
137 
138 public:
139 
140 								                ImpTwain( ScannerManager& rMgr, const Link& rNotifyLink );
141 								                ~ImpTwain();
142 
143 	void						                Destroy();
144 
145 	bool						                SelectSource();
146 	bool						                InitXfer();
147 };
148 
149 // ---------
150 // - Procs -
151 // ---------
152 
153 static ImpTwain* pImpTwainInstance = NULL;
154 
155 // -------------------------------------------------------------------------
156 
157 LRESULT CALLBACK TwainWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 )
158 {
159 	return DefWindowProc( hWnd, nMsg, nPar1, nPar2 );
160 }
161 
162 // -------------------------------------------------------------------------
163 
164 LRESULT CALLBACK TwainMsgProc( int nCode, WPARAM wParam, LPARAM lParam )
165 {
166 	MSG* pMsg = (MSG*) lParam;
167 
168 	if( ( nCode < 0 ) || ( pImpTwainInstance->hTwainWnd != pMsg->hwnd ) || !pImpTwainInstance->ImplHandleMsg( (void*) lParam ) )
169 	{
170 		return CallNextHookEx( pImpTwainInstance->hTwainHook, nCode, wParam, lParam );
171 	}
172 	else
173 	{
174 		pMsg->message = WM_USER;
175 		pMsg->lParam = 0;
176 
177 		return 0;
178 	}
179 }
180 
181 // -----------------------------------------------------------------------------
182 
183 // #107835# hold reference to ScannerManager, to prevent premature death
184 ImpTwain::ImpTwain( ScannerManager& rMgr, const Link& rNotifyLink ) :
185 			mrMgr( rMgr ),
186 		    mxMgr( uno::Reference< scanner::XScannerManager >( static_cast< OWeakObject* >( &rMgr ), uno::UNO_QUERY) ),
187 			aNotifyLink( rNotifyLink ),
188 			pDSM( NULL ),
189 			pMod( NULL ),
190 			hTwainWnd( 0 ),
191 			hTwainHook( 0 ),
192 			nCurState( 1 ),
193             mbCloseFrameOnExit( false )
194 {
195     // setup TWAIN window
196 	pImpTwainInstance = this;
197 
198 	aAppIdent.Id = 0;
199 	aAppIdent.Version.MajorNum = 1;
200 	aAppIdent.Version.MinorNum = 0;
201 	aAppIdent.Version.Language = TWLG_USA;
202 	aAppIdent.Version.Country = TWCY_USA;
203 	aAppIdent.ProtocolMajor = TWON_PROTOCOLMAJOR;
204 	aAppIdent.ProtocolMinor = TWON_PROTOCOLMINOR;
205 	aAppIdent.SupportedGroups =	DG_IMAGE | DG_CONTROL;
206 	strncpy( aAppIdent.Version.Info, "8.0", 32 );
207     aAppIdent.Version.Info[32] = aAppIdent.Version.Info[33] = 0;
208 	strncpy( aAppIdent.Manufacturer, "Sun Microsystems", 32 );
209     aAppIdent.Manufacturer[32] = aAppIdent.Manufacturer[33] = 0;
210 	strncpy( aAppIdent.ProductFamily,"Office", 32 );
211     aAppIdent.ProductFamily[32] = aAppIdent.ProductFamily[33] = 0;
212 	strncpy( aAppIdent.ProductName, "Office", 32 );
213     aAppIdent.ProductName[32] = aAppIdent.ProductName[33] = 0;
214 
215 	WNDCLASS aWc = { 0, &TwainWndProc, 0, sizeof( WNDCLASS ), GetModuleHandle( NULL ), NULL, NULL, NULL, NULL, "TwainClass" };
216 	RegisterClass( &aWc );
217 
218 	hTwainWnd = CreateWindowEx( WS_EX_TOPMOST, aWc.lpszClassName, "TWAIN", 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, aWc.hInstance, 0 );
219 	hTwainHook = SetWindowsHookEx( WH_GETMESSAGE, &TwainMsgProc, NULL, GetCurrentThreadId() );
220 
221     // #107835# block destruction until ImplDestroyHdl is called
222     mxSelfRef = static_cast< ::cppu::OWeakObject* >( this );
223 }
224 
225 // -----------------------------------------------------------------------------
226 
227 ImpTwain::~ImpTwain()
228 {
229     // are we responsible for application shutdown?
230     if( mbCloseFrameOnExit )
231         ImplSendCloseEvent();
232 }
233 
234 // -----------------------------------------------------------------------------
235 
236 void ImpTwain::Destroy()
237 {
238 	ImplFallback( TWAIN_EVENT_NONE );
239 	Application::PostUserEvent( LINK( this, ImpTwain, ImplDestroyHdl ), NULL );
240 }
241 
242 // -----------------------------------------------------------------------------
243 
244 bool ImpTwain::SelectSource()
245 {
246 	TW_UINT16 nRet = TWRC_FAILURE;
247 
248 	ImplOpenSourceManager();
249 
250 	if( 3 == nCurState )
251 	{
252 		TW_IDENTITY aIdent;
253 
254 		aIdent.Id = 0, aIdent.ProductName[ 0 ] = '\0';
255 		aNotifyLink.Call( (void*) TWAIN_EVENT_SCANNING );
256 		nRet = PFUNC( &aAppIdent, NULL, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &aIdent );
257 	}
258 
259 	ImplFallback( TWAIN_EVENT_QUIT );
260 
261 	return( TWRC_SUCCESS == nRet );
262 }
263 
264 // -----------------------------------------------------------------------------
265 
266 bool ImpTwain::InitXfer()
267 {
268 	bool bRet = false;
269 
270 	ImplOpenSourceManager();
271 
272 	if( 3 == nCurState )
273 	{
274 		ImplOpenSource();
275 
276 		if( 4 == nCurState )
277 			bRet = ImplEnableSource();
278 	}
279 
280 	if( !bRet )
281 		ImplFallback( TWAIN_EVENT_QUIT );
282 
283 	return bRet;
284 }
285 
286 // -----------------------------------------------------------------------------
287 
288 void ImpTwain::ImplOpenSourceManager()
289 {
290 	if( 1 == nCurState )
291 	{
292 		pMod = new ::vos::OModule( ::rtl::OUString() );
293 
294 		if( pMod->load( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( TWAIN_LIBNAME ) ) ) )
295 		{
296 			nCurState = 2;
297 
298 			if( ( ( pDSM = (DSMENTRYPROC) pMod->getSymbol( String( RTL_CONSTASCII_USTRINGPARAM( TWAIN_FUNCNAME ) ) ) ) != NULL ) &&
299 				( PFUNC( &aAppIdent, NULL, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, &hTwainWnd ) == TWRC_SUCCESS ) )
300 			{
301 				nCurState = 3;
302 			}
303 		}
304 		else
305 		{
306 			delete pMod;
307 			pMod = NULL;
308 		}
309 	}
310 }
311 
312 // -----------------------------------------------------------------------------
313 
314 void ImpTwain::ImplOpenSource()
315 {
316 	if( 3 == nCurState )
317 	{
318 		if( ( PFUNC( &aAppIdent, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &aSrcIdent ) == TWRC_SUCCESS ) &&
319 			( PFUNC( &aAppIdent, NULL, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &aSrcIdent ) == TWRC_SUCCESS ) )
320 		{
321 			TW_CAPABILITY	aCap = { CAP_XFERCOUNT, TWON_ONEVALUE, GlobalAlloc( GHND, sizeof( TW_ONEVALUE ) ) };
322 			TW_ONEVALUE*	pVal = (TW_ONEVALUE*) GlobalLock( aCap.hContainer );
323 
324 			pVal->ItemType = TWTY_INT16, pVal->Item = 1;
325 			GlobalUnlock( aCap.hContainer );
326 			PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &aCap );
327 			GlobalFree( aCap.hContainer );
328 			nCurState = 4;
329 		}
330 	}
331 }
332 
333 // -----------------------------------------------------------------------------
334 
335 bool ImpTwain::ImplEnableSource()
336 {
337 	bool bRet = false;
338 
339 	if( 4 == nCurState )
340 	{
341 		TW_USERINTERFACE aUI = { true, true, hTwainWnd };
342 
343 		aNotifyLink.Call( (void*) TWAIN_EVENT_SCANNING );
344 		nCurState = 5;
345 
346         // #107835# register as vetoable close listener, to prevent application to die under us
347         ImplRegisterCloseListener();
348 
349 		if( PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, &aUI ) == TWRC_SUCCESS )
350         {
351 			bRet = true;
352         }
353 		else
354         {
355 			nCurState = 4;
356 
357             // #107835# deregister as vetoable close listener, dialog failed
358             ImplDeregisterCloseListener();
359         }
360 	}
361 
362 	return bRet;
363 }
364 
365 // -----------------------------------------------------------------------------
366 
367 bool ImpTwain::ImplHandleMsg( void* pMsg )
368 {
369 	TW_UINT16	nRet;
370 	PTWAINMSG	pMess = (PTWAINMSG) pMsg;
371 	TW_EVENT	aEvt = { pMess, MSG_NULL };
372 
373 	nRet = PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, &aEvt );
374 
375 	if( aEvt.TWMessage != MSG_NULL )
376 	{
377 		switch( aEvt.TWMessage )
378 		{
379 			case MSG_XFERREADY:
380 			{
381 				ULONG nEvent = TWAIN_EVENT_QUIT;
382 
383 				if( 5 == nCurState )
384 				{
385 					nCurState = 6;
386 					ImplXfer();
387 
388 					if( mrMgr.GetData() )
389 						nEvent = TWAIN_EVENT_XFER;
390 				}
391 
392 				ImplFallback( nEvent );
393 			}
394 			break;
395 
396 			case MSG_CLOSEDSREQ:
397 				ImplFallback( TWAIN_EVENT_QUIT );
398 			break;
399 
400 			default:
401 			break;
402 		}
403 	}
404 	else
405 		nRet = TWRC_NOTDSEVENT;
406 
407 	return( TWRC_DSEVENT == nRet );
408 }
409 
410 // -----------------------------------------------------------------------------
411 
412 void ImpTwain::ImplXfer()
413 {
414 	if( nCurState == 6 )
415 	{
416 		TW_IMAGEINFO	aInfo;
417 		TW_UINT32		hDIB = 0;
418 		long			nWidth, nHeight, nXRes, nYRes;
419 
420 		if( PFUNC( &aAppIdent, &aSrcIdent, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, &aInfo ) == TWRC_SUCCESS )
421 		{
422 			nWidth = aInfo.ImageWidth;
423 			nHeight = aInfo.ImageLength;
424 			nXRes = FIXTOLONG( aInfo.XResolution );
425 			nYRes = FIXTOLONG( aInfo.YResolution );
426 		}
427 		else
428 			nWidth = nHeight = nXRes = nYRes = -1L;
429 
430 		switch( PFUNC( &aAppIdent, &aSrcIdent, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &hDIB ) )
431 		{
432 			case( TWRC_CANCEL ):
433 				nCurState = 7;
434 			break;
435 
436 			case( TWRC_XFERDONE ):
437 			{
438 				if( hDIB )
439 				{
440 					if( ( nXRes != -1 ) && ( nYRes != - 1 ) && ( nWidth != - 1 ) && ( nHeight != - 1 ) )
441 					{
442 						// set resolution of bitmap
443 						BITMAPINFOHEADER*	pBIH = (BITMAPINFOHEADER*) GlobalLock( (HGLOBAL) hDIB );
444 						static const double	fFactor = 100.0 / 2.54;
445 
446 						pBIH->biXPelsPerMeter = FRound( fFactor * nXRes );
447 						pBIH->biYPelsPerMeter = FRound( fFactor * nYRes );
448 
449 						GlobalUnlock( (HGLOBAL) hDIB );
450 					}
451 
452 					mrMgr.SetData( (void*)(long) hDIB );
453 				}
454 				else
455 					GlobalFree( (HGLOBAL) hDIB );
456 
457 				nCurState = 7;
458 			}
459 			break;
460 
461 			default:
462 			break;
463 		}
464 	}
465 }
466 
467 // -----------------------------------------------------------------------------
468 
469 void ImpTwain::ImplFallback( ULONG nEvent )
470 {
471 	Application::PostUserEvent( LINK( this, ImpTwain, ImplFallbackHdl ), (void*) nEvent );
472 }
473 
474 // -----------------------------------------------------------------------------
475 
476 IMPL_LINK( ImpTwain, ImplFallbackHdl, void*, pData )
477 {
478 	const ULONG	nEvent = (ULONG) pData;
479 	bool		bFallback = true;
480 
481 	switch( nCurState )
482 	{
483 		case( 7 ):
484 		case( 6 ):
485 		{
486 			TW_PENDINGXFERS aXfers;
487 
488 			if( PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, &aXfers ) == TWRC_SUCCESS )
489 			{
490 				if( aXfers.Count != 0 )
491 					PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, &aXfers );
492 			}
493 
494 			nCurState = 5;
495 		}
496 		break;
497 
498 		case( 5 ):
499 		{
500 			TW_USERINTERFACE aUI = { true, true, hTwainWnd };
501 
502 			PFUNC( &aAppIdent, &aSrcIdent, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, &aUI );
503 			nCurState = 4;
504 
505             // #107835# deregister as vetoable close listener
506             ImplDeregisterCloseListener();
507 		}
508 		break;
509 
510 		case( 4 ):
511 		{
512 			PFUNC( &aAppIdent, NULL, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &aSrcIdent );
513 			nCurState = 3;
514 		}
515 		break;
516 
517 		case( 3 ):
518 		{
519 			PFUNC( &aAppIdent, NULL, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, &hTwainWnd );
520 			nCurState = 2;
521 		}
522 		break;
523 
524 		case( 2 ):
525 		{
526 			delete pMod;
527 			pMod = NULL;
528 			nCurState = 1;
529 		}
530 		break;
531 
532 		default:
533 		{
534 			if( nEvent != TWAIN_EVENT_NONE )
535 				aNotifyLink.Call( (void*) nEvent );
536 
537 			bFallback = false;
538 		}
539 		break;
540 	}
541 
542 	if( bFallback )
543 		ImplFallback( nEvent );
544 
545 	return 0L;
546 }
547 
548 // -----------------------------------------------------------------------------
549 
550 IMPL_LINK( ImpTwain, ImplDestroyHdl, void*, /*p*/ )
551 {
552 	if( hTwainWnd )
553 		DestroyWindow( hTwainWnd );
554 
555 	if( hTwainHook )
556 		UnhookWindowsHookEx( hTwainHook );
557 
558     // #107835# permit destruction of ourselves (normally, refcount
559     // should drop to zero exactly here)
560 	mxSelfRef = NULL;
561 	pImpTwainInstance = NULL;
562 
563 	return 0L;
564 }
565 
566 // -----------------------------------------------------------------------------
567 
568 uno::Reference< frame::XFrame > ImpTwain::ImplGetActiveFrame()
569 {
570     try
571     {
572         uno::Reference< lang::XMultiServiceFactory >  xMgr( ::comphelper::getProcessServiceFactory() );
573 
574         if( xMgr.is() )
575         {
576             // query desktop instance
577             uno::Reference< frame::XDesktop > xDesktop( xMgr->createInstance(
578                                                             OUString::createFromAscii( "com.sun.star.frame.Desktop" ) ), uno::UNO_QUERY );
579 
580             if( xDesktop.is() )
581             {
582                 // query property set from desktop, which contains the currently active frame
583                 uno::Reference< beans::XPropertySet > xDesktopProps( xDesktop, uno::UNO_QUERY );
584 
585                 if( xDesktopProps.is() )
586                 {
587                     uno::Any aActiveFrame;
588 
589                     try
590                     {
591                         aActiveFrame = xDesktopProps->getPropertyValue(
592                             OUString::createFromAscii( "ActiveFrame" ) );
593                     }
594                     catch( const beans::UnknownPropertyException& )
595                     {
596                         // property unknown.
597                         DBG_ERROR("ImpTwain::ImplGetActiveFrame: ActiveFrame property unknown, cannot determine active frame!");
598                         return uno::Reference< frame::XFrame >();
599                     }
600 
601                     uno::Reference< frame::XFrame > xActiveFrame;
602 
603                     if( (aActiveFrame >>= xActiveFrame) &&
604                         xActiveFrame.is() )
605                     {
606                         return xActiveFrame;
607                     }
608                 }
609             }
610         }
611     }
612     catch( const uno::Exception& )
613     {
614     }
615 
616     DBG_ERROR("ImpTwain::ImplGetActiveFrame: Could not determine active frame!");
617     return uno::Reference< frame::XFrame >();
618 }
619 
620 // -----------------------------------------------------------------------------
621 
622 uno::Reference< util::XCloseBroadcaster > ImpTwain::ImplGetActiveFrameCloseBroadcaster()
623 {
624     try
625     {
626         return uno::Reference< util::XCloseBroadcaster >( ImplGetActiveFrame(), uno::UNO_QUERY );
627     }
628     catch( const uno::Exception& )
629     {
630     }
631 
632     DBG_ERROR("ImpTwain::ImplGetActiveFrameCloseBroadcaster: Could determine close broadcaster on active frame!");
633     return uno::Reference< util::XCloseBroadcaster >();
634 }
635 
636 // -----------------------------------------------------------------------------
637 
638 void ImpTwain::ImplRegisterCloseListener()
639 {
640     try
641     {
642         uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( ImplGetActiveFrameCloseBroadcaster() );
643 
644         if( xCloseBroadcaster.is() )
645         {
646             xCloseBroadcaster->addCloseListener(this);
647             return; // successfully registered as a close listener
648         }
649         else
650         {
651             // interface unknown. don't register, then
652             DBG_ERROR("ImpTwain::ImplRegisterCloseListener: XFrame has no XCloseBroadcaster!");
653             return;
654         }
655     }
656     catch( const uno::Exception& )
657     {
658     }
659 
660     DBG_ERROR("ImpTwain::ImplRegisterCloseListener: Could not register as close listener!");
661 }
662 
663 // -----------------------------------------------------------------------------
664 
665 void ImpTwain::ImplDeregisterCloseListener()
666 {
667     try
668     {
669         uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
670             ImplGetActiveFrameCloseBroadcaster() );
671 
672         if( xCloseBroadcaster.is() )
673         {
674             xCloseBroadcaster->removeCloseListener(this);
675             return; // successfully deregistered as a close listener
676         }
677         else
678         {
679             // interface unknown. don't deregister, then
680             DBG_ERROR("ImpTwain::ImplDeregisterCloseListener: XFrame has no XCloseBroadcaster!");
681             return;
682         }
683     }
684     catch( const uno::Exception& )
685     {
686     }
687 
688     DBG_ERROR("ImpTwain::ImplDeregisterCloseListener: Could not deregister as close listener!");
689 }
690 
691 // -----------------------------------------------------------------------------
692 
693 void SAL_CALL ImpTwain::queryClosing( const lang::EventObject& /*Source*/, sal_Bool GetsOwnership ) throw (util::CloseVetoException, uno::RuntimeException)
694 {
695     // shall we re-send the close query later on?
696     mbCloseFrameOnExit = GetsOwnership;
697 
698     // the sole purpose of this listener is to forbid closing of the listened-at frame
699     throw util::CloseVetoException();
700 }
701 
702 // -----------------------------------------------------------------------------
703 
704 void SAL_CALL ImpTwain::notifyClosing( const lang::EventObject& /*Source*/ ) throw (uno::RuntimeException)
705 {
706     // should not happen
707     DBG_ERROR("ImpTwain::notifyClosing called, but we vetoed the closing before!");
708 }
709 
710 // -----------------------------------------------------------------------------
711 
712 void SAL_CALL ImpTwain::disposing( const lang::EventObject& /*Source*/ ) throw (uno::RuntimeException)
713 {
714     // we're not holding any references to the frame, thus noop
715 }
716 
717 // -----------------------------------------------------------------------------
718 
719 void ImpTwain::ImplSendCloseEvent()
720 {
721     try
722     {
723         uno::Reference< util::XCloseable > xCloseable( ImplGetActiveFrame(), uno::UNO_QUERY );
724 
725         if( xCloseable.is() )
726             xCloseable->close( true );
727     }
728     catch( const uno::Exception& )
729     {
730     }
731 
732     DBG_ERROR("ImpTwain::ImplSendCloseEvent: Could not send required close broadcast!");
733 }
734 
735 
736 // ---------
737 // - Twain -
738 // ---------
739 
740 class Twain
741 {
742 	uno::Reference< lang::XEventListener >	    mxListener;
743 	uno::Reference< scanner::XScannerManager >	mxMgr;
744 	const ScannerManager*			            mpCurMgr;
745 	ImpTwain* 						            mpImpTwain;
746 	TwainState						            meState;
747 
748 									            DECL_LINK( ImpNotifyHdl, ImpTwain* );
749 
750 public:
751 
752 									Twain();
753 									~Twain();
754 
755 	bool							SelectSource( ScannerManager& rMgr );
756 	bool							PerformTransfer( ScannerManager& rMgr, const uno::Reference< lang::XEventListener >& rxListener );
757 
758 	TwainState						GetState() const { return meState; }
759 };
760 
761 // ------------------------------------------------------------------------
762 
763 Twain::Twain() :
764 		mpCurMgr( NULL ),
765 		mpImpTwain( NULL ),
766 		meState( TWAIN_STATE_NONE )
767 {
768 }
769 
770 // ------------------------------------------------------------------------
771 
772 Twain::~Twain()
773 {
774 	if( mpImpTwain )
775 		mpImpTwain->Destroy();
776 }
777 
778 // ------------------------------------------------------------------------
779 
780 bool Twain::SelectSource( ScannerManager& rMgr )
781 {
782 	bool bRet;
783 
784 	if( !mpImpTwain )
785 	{
786         // #107835# hold reference to ScannerManager, to prevent premature death
787         mxMgr = uno::Reference< scanner::XScannerManager >( static_cast< OWeakObject* >( const_cast< ScannerManager* >( mpCurMgr = &rMgr ) ),
788                                                             uno::UNO_QUERY ),
789 
790 		meState = TWAIN_STATE_NONE;
791 		mpImpTwain = new ImpTwain( rMgr, LINK( this, Twain, ImpNotifyHdl ) );
792 		bRet = mpImpTwain->SelectSource();
793 	}
794 	else
795 		bRet = false;
796 
797 	return bRet;
798 }
799 
800 // ------------------------------------------------------------------------
801 
802 bool Twain::PerformTransfer( ScannerManager& rMgr, const uno::Reference< lang::XEventListener >& rxListener )
803 {
804 	bool bRet;
805 
806 	if( !mpImpTwain )
807 	{
808         // #107835# hold reference to ScannerManager, to prevent premature death
809         mxMgr = uno::Reference< scanner::XScannerManager >( static_cast< OWeakObject* >( const_cast< ScannerManager* >( mpCurMgr = &rMgr ) ),
810                                                             uno::UNO_QUERY ),
811 
812 		mxListener = rxListener;
813 		meState = TWAIN_STATE_NONE;
814 		mpImpTwain = new ImpTwain( rMgr, LINK( this, Twain, ImpNotifyHdl ) );
815 		bRet = mpImpTwain->InitXfer();
816 	}
817 	else
818 		bRet = false;
819 
820 	return bRet;
821 }
822 
823 // ------------------------------------------------------------------------
824 
825 IMPL_LINK( Twain, ImpNotifyHdl, ImpTwain*, nEvent )
826 {
827 	switch( (ULONG)(void*) nEvent )
828 	{
829 		case( TWAIN_EVENT_SCANNING ):
830 			meState = TWAIN_STATE_SCANNING;
831 		break;
832 
833 		case( TWAIN_EVENT_QUIT ):
834 		{
835 			if( meState != TWAIN_STATE_DONE )
836 				meState = TWAIN_STATE_CANCELED;
837 
838 			if( mpImpTwain )
839 			{
840 			    mpImpTwain->Destroy();
841 			    mpImpTwain = NULL;
842                 mpCurMgr = NULL;
843 			}
844 
845 			if( mxListener.is() )
846 				mxListener->disposing( lang::EventObject( mxMgr ) );
847 
848 			mxListener = NULL;
849 		}
850 		break;
851 
852 		case( TWAIN_EVENT_XFER ):
853 		{
854 			if( mpImpTwain )
855 			{
856 				meState = ( mpCurMgr->GetData() ? TWAIN_STATE_DONE : TWAIN_STATE_CANCELED );
857 
858 				mpImpTwain->Destroy();
859 				mpImpTwain = NULL;
860                 mpCurMgr = NULL;
861 
862 				if( mxListener.is() )
863 					mxListener->disposing( lang::EventObject( mxMgr ) );
864 			}
865 
866 			mxListener = NULL;
867 		}
868 		break;
869 
870 		default:
871 		break;
872 	}
873 
874 	return 0L;
875 }
876 
877 // -----------
878 // - statics -
879 // -----------
880 
881 static Twain aTwain;
882 
883 // ------------------
884 // - ScannerManager -
885 // ------------------
886 
887 void ScannerManager::AcquireData()
888 {
889 }
890 
891 void ScannerManager::ReleaseData()
892 {
893 	if( mpData )
894 	{
895 		GlobalFree( (HGLOBAL)(long) mpData );
896 		mpData = NULL;
897 	}
898 }
899 
900 // -----------------------------------------------------------------------------
901 
902 AWT::Size ScannerManager::getSize() throw()
903 {
904 	AWT::Size	aRet;
905 	HGLOBAL		hDIB = (HGLOBAL)(long) mpData;
906 
907 	if( hDIB )
908 	{
909 		BITMAPINFOHEADER* pBIH = (BITMAPINFOHEADER*) GlobalLock( hDIB );
910 
911 		if( pBIH )
912 		{
913 			aRet.Width = pBIH->biWidth;
914 			aRet.Height = pBIH->biHeight;
915 		}
916 		else
917 			aRet.Width = aRet.Height = 0;
918 
919 		GlobalUnlock( hDIB );
920 	}
921 	else
922 		aRet.Width = aRet.Height = 0;
923 
924 	return aRet;
925 }
926 
927 // -----------------------------------------------------------------------------
928 
929 SEQ( sal_Int8 ) ScannerManager::getDIB() throw()
930 {
931 	SEQ( sal_Int8 ) aRet;
932 
933 	if( mpData )
934 	{
935 		HGLOBAL				hDIB = (HGLOBAL)(long) mpData;
936 		const sal_uInt32	nDIBSize = GlobalSize( hDIB );
937 		BITMAPINFOHEADER*	pBIH = (BITMAPINFOHEADER*) GlobalLock( hDIB );
938 
939 		if( pBIH )
940 		{
941 			sal_uInt32	nColEntries;
942 
943 			switch( pBIH->biBitCount )
944 			{
945 				case( 1 ):
946 				case( 4 ):
947 				case( 8 ):
948 					nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : ( 1 << pBIH->biBitCount );
949 				break;
950 
951 				case( 24 ):
952 					nColEntries = pBIH->biClrUsed ? pBIH->biClrUsed : 0;
953 				break;
954 
955 				case( 16 ):
956 				case( 32 ):
957 				{
958 					nColEntries = pBIH->biClrUsed;
959 
960 					if( pBIH->biCompression == BI_BITFIELDS )
961 						nColEntries += 3;
962 				}
963 				break;
964 
965 				default:
966 					nColEntries = 0;
967 				break;
968 			}
969 
970 			aRet = SEQ( sal_Int8 )( sizeof( BITMAPFILEHEADER ) + nDIBSize );
971 
972 			sal_Int8*		pBuf = aRet.getArray();
973 			SvMemoryStream* pMemStm = new SvMemoryStream( (char*) pBuf, sizeof( BITMAPFILEHEADER ), STREAM_WRITE );
974 
975 			*pMemStm << 'B' << 'M' << (sal_uInt32) 0 << (sal_uInt32) 0;
976 			*pMemStm << (sal_uInt32) ( sizeof( BITMAPFILEHEADER ) + pBIH->biSize + ( nColEntries * sizeof( RGBQUAD ) ) );
977 
978 			delete pMemStm;
979 			memcpy( pBuf + sizeof( BITMAPFILEHEADER ), pBIH, nDIBSize );
980 		}
981 
982 		GlobalUnlock( hDIB );
983 		ReleaseData();
984 	}
985 
986 	return aRet;
987 }
988 
989 // -----------------------------------------------------------------------------
990 
991 SEQ( ScannerContext ) SAL_CALL ScannerManager::getAvailableScanners() throw()
992 {
993 	vos::OGuard				aGuard( maProtector );
994 	SEQ( ScannerContext )	aRet( 1 );
995 
996 	aRet.getArray()[0].ScannerName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TWAIN" ) );
997 	aRet.getArray()[0].InternalData = 0;
998 
999 	return aRet;
1000 }
1001 
1002 // -----------------------------------------------------------------------------
1003 
1004 sal_Bool SAL_CALL ScannerManager::configureScanner( ScannerContext& rContext )
1005     throw( ScannerException )
1006 {
1007 	vos::OGuard				            aGuard( maProtector );
1008 	uno::Reference< XScannerManager >	xThis( this );
1009 
1010 	if( rContext.InternalData != 0 || rContext.ScannerName != ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TWAIN" ) ) )
1011 		throw ScannerException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Scanner does not exist" ) ), xThis, ScanError_InvalidContext );
1012 
1013 	ReleaseData();
1014 
1015 	return aTwain.SelectSource( *this );
1016 }
1017 
1018 // -----------------------------------------------------------------------------
1019 
1020 void SAL_CALL ScannerManager::startScan( const ScannerContext& rContext, const uno::Reference< lang::XEventListener >& rxListener )
1021     throw( ScannerException )
1022 {
1023 	vos::OGuard				            aGuard( maProtector );
1024 	uno::Reference< XScannerManager >	xThis( this );
1025 
1026 	if( rContext.InternalData != 0 || rContext.ScannerName != ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TWAIN" ) ) )
1027 		throw ScannerException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Scanner does not exist" ) ), xThis, ScanError_InvalidContext );
1028 
1029 	ReleaseData();
1030 	aTwain.PerformTransfer( *this, rxListener );
1031 }
1032 
1033 // -----------------------------------------------------------------------------
1034 
1035 ScanError SAL_CALL ScannerManager::getError( const ScannerContext& rContext )
1036     throw( ScannerException )
1037 {
1038 	vos::OGuard				            aGuard( maProtector );
1039 	uno::Reference< XScannerManager >	xThis( this );
1040 
1041 	if( rContext.InternalData != 0 || rContext.ScannerName != ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TWAIN" ) ) )
1042 		throw ScannerException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Scanner does not exist" ) ), xThis, ScanError_InvalidContext );
1043 
1044 	return( ( aTwain.GetState() == TWAIN_STATE_CANCELED ) ? ScanError_ScanCanceled : ScanError_ScanErrorNone );
1045 }
1046 
1047 // -----------------------------------------------------------------------------
1048 
1049 uno::Reference< awt::XBitmap > SAL_CALL ScannerManager::getBitmap( const ScannerContext& /*rContext*/ )
1050     throw( ScannerException )
1051 {
1052 	vos::OGuard	aGuard( maProtector );
1053 	return uno::Reference< awt::XBitmap >( this );
1054 }
1055