xref: /trunk/main/extensions/source/scanner/scanwin.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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