1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: MtaFop.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_fpicker.hxx"
33 #include <osl/diagnose.h>
34 #include <osl/conditn.hxx>
39 #include "..\misc\resourceprovider.hxx"
41 #include <systools/win32/comtools.hxx>
46 const sal_uInt32 MSG_BROWSEFORFOLDER
= WM_USER
+ 1;
47 const sal_uInt32 MSG_SHUTDOWN
= WM_USER
+ 2;
49 const sal_uInt32 MAX_WAITTIME
= 2000; // msec
51 const sal_Bool MANUAL_RESET
= sal_True
;
52 const sal_Bool AUTO_RESET
= sal_False
;
53 const sal_Bool INIT_NONSIGNALED
= sal_False
;
55 typedef sal::systools::COMReference
<IMalloc
> IMallocPtr
;
56 typedef sal::systools::COMReference
<IShellFolder
> IShellFolderPtr
;
60 const char* FOLDERPICKER_SRV_DLL_NAME
= "fop.dll";
61 const char g_szWndClsName
[] = "FopStaReqWnd###";
62 const char* CURRENT_INSTANCE
= "CurrInst";
64 typedef struct _RequestContext
70 inline sal_Bool
InitializeRequestContext( RequestContext
* aRequestContext
)
72 OSL_ASSERT( aRequestContext
);
74 aRequestContext
->hEvent
= CreateEventA(
75 0, AUTO_RESET
, INIT_NONSIGNALED
, NULL
);
77 aRequestContext
->bRet
= sal_False
;
79 return ( 0 != aRequestContext
->hEvent
);
82 inline void DeinitializeRequestContext( RequestContext
* aRequestContext
)
84 OSL_ASSERT( aRequestContext
&& aRequestContext
->hEvent
);
85 CloseHandle( aRequestContext
->hEvent
);
88 //-------------------------------
89 // Determine if current thread is
90 // an MTA or STA thread
91 //-------------------------------
94 HRESULT hr
= CoInitialize(NULL
);
96 if (RPC_E_CHANGED_MODE
== hr
)
106 //----------------------------------------------------------------
107 // static member initialization
108 //----------------------------------------------------------------
110 ATOM
CMtaFolderPicker::s_ClassAtom
= 0;
111 osl::Mutex
CMtaFolderPicker::s_Mutex
;
112 sal_Int32
CMtaFolderPicker::s_StaRequestWndRegisterCount
= 0;
114 //--------------------------------------------------------------------
116 //--------------------------------------------------------------------
118 CMtaFolderPicker::CMtaFolderPicker( sal_uInt32 Flags
) :
119 m_hStaThread( NULL
),
121 m_hEvtThrdReady( NULL
),
122 m_hwndStaRequestWnd( NULL
)
124 m_hInstance
= GetModuleHandleA( FOLDERPICKER_SRV_DLL_NAME
);
125 OSL_ENSURE( m_hInstance
, "The name of the FolderPicker service dll must have changed" );
127 ZeroMemory( &m_bi
, sizeof( m_bi
) );
129 // !!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!
131 // Remember: This HACK prevents you from stepping
132 // through your code in the debugger because if you
133 // set a break point in the ctor here the debugger
134 // may become the owner of the FolderBrowse dialog
135 // and so it seems that the Visual Studio and the
136 // office are hanging
137 m_bi
.hwndOwner
= GetForegroundWindow( );
141 --------------------------------
142 BIF_EDITBOX Version 4.71
143 BIF_NEWDIALOGSTYLE Version 5.0
144 BIF_SHAREABLE Version 5.0
145 BIF_VALIDATE Version 4.71
147 Version 4.71 - Internet Explorer 4.0
148 Version 5.0 - Internet Explorer 5.0
151 m_bi
.ulFlags
= Flags
;
153 m_bi
.lpfn
= CMtaFolderPicker::FolderPickerCallback
;
154 m_bi
.lParam
= reinterpret_cast< LPARAM
>( this );
156 //---------------------------------------
157 // read the default strings for title and
158 // description from a resource file
160 CResourceProvider ResProvider
;
162 m_dialogTitle
= ResProvider
.getResString( 500 );
163 m_Description
= ResProvider
.getResString( 501 );
165 // signals that the thread was successfully set up
166 m_hEvtThrdReady
= CreateEventA(
172 if ( m_hEvtThrdReady
)
174 // setup the sta thread
175 m_hStaThread
= (HANDLE
)_beginthreadex(
178 CMtaFolderPicker::StaThreadProc
,
183 OSL_ASSERT( m_hStaThread
);
186 OSL_ASSERT( m_hEvtThrdReady
);
189 //--------------------------------------------------------------------
191 //--------------------------------------------------------------------
193 CMtaFolderPicker::~CMtaFolderPicker( )
195 // only if the is a valid event handle
196 // there may also be a thread a hidden
197 // target request window and so on
199 if ( m_hEvtThrdReady
)
201 // block calling threads because we
202 // are about to shutdown
203 ResetEvent( m_hEvtThrdReady
);
205 // force the destruction of the sta thread request window
206 // and the end of the thread
207 // remeber: DestroyWindow may only be called from within
208 // the thread that created the window
209 if ( IsWindow( m_hwndStaRequestWnd
) )
211 SendMessageA( m_hwndStaRequestWnd
, MSG_SHUTDOWN
, 0, 0 );
213 // we place unregister class here because
214 // if we have a valid window we must have
215 // sucessfully registered a window class
216 // if the creation of the window itself
217 // failed after registering the window
218 // class we have unregistered it immediately
219 // in createStaRequestWindow below
220 UnregisterStaRequestWindowClass( );
225 // wait for thread shutdown
226 sal_uInt32 dwResult
= WaitForSingleObject( m_hStaThread
, MAX_WAITTIME
);
227 OSL_ENSURE( dwResult
== WAIT_OBJECT_0
, "sta thread could not terminate" );
229 // terminate the thread if it
230 // doesn't shutdown itself
231 if ( WAIT_OBJECT_0
!= dwResult
)
233 m_hStaThread
, sal::static_int_cast
< DWORD
>(-1) );
235 CloseHandle( m_hStaThread
);
238 CloseHandle( m_hEvtThrdReady
);
242 //--------------------------------------------------------------------
244 //--------------------------------------------------------------------
246 sal_Bool
CMtaFolderPicker::browseForFolder( )
248 sal_Bool bRet
= sal_False
;
253 OSL_ASSERT( m_hEvtThrdReady
);
255 if ( WaitForSingleObject( m_hEvtThrdReady
, MAX_WAITTIME
) != WAIT_OBJECT_0
)
257 OSL_ENSURE( sal_False
, "sta thread not ready" );
261 RequestContext aReqCtx
;
263 if ( !InitializeRequestContext( &aReqCtx
) )
265 OSL_ASSERT( sal_False
);
269 // marshall request into the sta thread
274 reinterpret_cast< LPARAM
>( &aReqCtx
) );
276 // waiting for the event to be signaled or
277 // window messages so that we don't block
280 sal_Bool bContinue
= sal_True
;
284 DWORD dwResult
= MsgWaitForMultipleObjects(
285 1, &aReqCtx
.hEvent
, FALSE
, INFINITE
, QS_ALLEVENTS
);
289 // the request context event is signaled
291 bContinue
= sal_False
;
294 // a window message has arrived
295 case WAIT_OBJECT_0
+ 1:
297 // dispatching all messages but we expect to
298 // receive only paint or timer messages that's
299 // why we don't need to call TranslateMessage or
300 // TranslateAccelerator, because keybord or
301 // mouse messages are for the FolderPicker which
302 // is in the foreground and should not arrive here
304 while ( PeekMessageA( &msg
, NULL
, 0, 0, PM_REMOVE
) )
305 DispatchMessageA(&msg
);
311 OSL_ASSERT( sal_False
);
315 /*sal_Bool*/ bRet
= aReqCtx
.bRet
;
316 DeinitializeRequestContext( &aReqCtx
);
320 bRet
= onBrowseForFolder();
326 //--------------------------------------------------------------------
328 //--------------------------------------------------------------------
330 void SAL_CALL
CMtaFolderPicker::setDisplayDirectory( const OUString
& aDirectory
)
332 m_displayDir
= aDirectory
;
335 //--------------------------------------------------------------------
337 //--------------------------------------------------------------------
339 OUString SAL_CALL
CMtaFolderPicker::getDisplayDirectory( )
344 //--------------------------------------------------------------------
346 //--------------------------------------------------------------------
348 OUString SAL_CALL
CMtaFolderPicker::getDirectory( )
350 return m_SelectedDir
;
353 //--------------------------------------------------------------------
355 //--------------------------------------------------------------------
357 void SAL_CALL
CMtaFolderPicker::setDescription( const rtl::OUString
& aDescription
)
359 m_Description
= aDescription
;
362 //--------------------------------------------------------------------
364 //--------------------------------------------------------------------
366 void SAL_CALL
CMtaFolderPicker::setTitle( const OUString
& aTitle
)
368 m_dialogTitle
= aTitle
;
371 //--------------------------------------------------------------------
373 //--------------------------------------------------------------------
375 OUString SAL_CALL
CMtaFolderPicker::getTitle( )
377 return m_dialogTitle
;
380 //-----------------------------------------------------
382 //-----------------------------------------------------
384 void SAL_CALL
CMtaFolderPicker::cancel( )
386 if ( IsWindow( m_hwnd
) )
388 // simulate a mouse click to the
393 MAKEWPARAM( IDCANCEL
, BN_CLICKED
),
394 (LPARAM
)GetDlgItem( m_hwnd
, IDCANCEL
) );
398 //--------------------------------------------------------------------
400 //--------------------------------------------------------------------
402 sal_Bool SAL_CALL
CMtaFolderPicker::onBrowseForFolder( )
407 // pre SHBrowseFroFolder
410 m_bi
.pszDisplayName
= reinterpret_cast<LPWSTR
>(m_pathBuff
.get());
412 if ( m_Description
.getLength( ) )
413 m_bi
.lpszTitle
= reinterpret_cast<LPCWSTR
>(m_Description
.getStr( ));
415 lpiid
= SHBrowseForFolderW( &m_bi
);
416 bRet
= ( NULL
!= lpiid
);
418 // post SHBrowseForFolder
420 m_SelectedDir
= getPathFromItemIdList( lpiid
);
421 releaseItemIdList( lpiid
);
426 //--------------------------------------------------------------------
428 //--------------------------------------------------------------------
430 void SAL_CALL
CMtaFolderPicker::releaseItemIdList( LPITEMIDLIST lpItemIdList
)
433 SHGetMalloc(&pIMalloc
);
436 pIMalloc
->Free(lpItemIdList
);
441 //--------------------------------------------------------------------
443 //--------------------------------------------------------------------
445 LPITEMIDLIST SAL_CALL
CMtaFolderPicker::getItemIdListFromPath( const rtl::OUString
& aDirectory
)
447 // parameter checking
448 if ( !aDirectory
.getLength( ) )
451 LPITEMIDLIST
lpItemIdList(NULL
);
453 IShellFolderPtr pIShellFolder
;
454 SHGetDesktopFolder(&pIShellFolder
);
456 if (pIShellFolder
.is())
458 pIShellFolder
->ParseDisplayName(
461 reinterpret_cast<LPWSTR
>(const_cast< sal_Unicode
* >( aDirectory
.getStr( ) )),
470 //--------------------------------------------------------------------
472 //--------------------------------------------------------------------
474 OUString SAL_CALL
CMtaFolderPicker::getPathFromItemIdList( LPCITEMIDLIST lpItemIdList
)
480 bool bRet
= SHGetPathFromIDListW( lpItemIdList
, reinterpret_cast<LPWSTR
>(m_pathBuff
.get()) );
482 path
= m_pathBuff
.get( );
488 //--------------------------------------------------------------------
490 //--------------------------------------------------------------------
492 void SAL_CALL
CMtaFolderPicker::enableOk( sal_Bool bEnable
)
494 OSL_ASSERT( IsWindow( m_hwnd
) );
499 static_cast< WPARAM
>( 0 ),
500 static_cast< LPARAM
>( bEnable
) );
503 //--------------------------------------------------------------------
505 //--------------------------------------------------------------------
507 void SAL_CALL
CMtaFolderPicker::setSelection( const rtl::OUString
& aDirectory
)
509 OSL_ASSERT( IsWindow( m_hwnd
) );
512 #pragma message( "#######################################" )
513 #pragma message( "SendMessageW wrapper has to be extended" )
514 #pragma message( "#######################################" )
520 static_cast< WPARAM
>( sal_True
),
521 reinterpret_cast< LPARAM
>( aDirectory
.getStr( ) ) );
524 //--------------------------------------------------------------------
526 //--------------------------------------------------------------------
528 void SAL_CALL
CMtaFolderPicker::setStatusText( const rtl::OUString
& aStatusText
)
530 OSL_ASSERT( IsWindow( m_hwnd
) );
535 static_cast< WPARAM
>( 0 ),
536 reinterpret_cast< LPARAM
>( aStatusText
.getStr( ) ) );
539 //--------------------------------------------------------------------
541 //--------------------------------------------------------------------
543 void SAL_CALL
CMtaFolderPicker::onInitialized( )
545 LPITEMIDLIST lpiidDisplayDir
= getItemIdListFromPath( m_displayDir
);
547 if ( lpiidDisplayDir
)
553 (LPARAM
) lpiidDisplayDir
);
555 releaseItemIdList( lpiidDisplayDir
);
559 //--------------------------------------------------------------------
561 //--------------------------------------------------------------------
563 sal_uInt32
CMtaFolderPicker::onValidateFailed()
565 // to be overwritten by subclasses
569 //--------------------------------------------------------------------
571 //--------------------------------------------------------------------
573 int CALLBACK
CMtaFolderPicker::FolderPickerCallback( HWND hwnd
, UINT uMsg
, LPARAM lParam
, LPARAM lpData
)
575 CMtaFolderPicker
* pImpl
= reinterpret_cast< CMtaFolderPicker
* >( lpData
);
582 case BFFM_INITIALIZED
:
583 pImpl
->m_hwnd
= hwnd
;
584 pImpl
->onInitialized( );
585 SetWindowTextW( hwnd
, reinterpret_cast<LPCWSTR
>(pImpl
->m_dialogTitle
.getStr()) );
588 case BFFM_SELCHANGED
:
589 pImpl
->m_hwnd
= hwnd
;
591 pImpl
->getPathFromItemIdList(
592 reinterpret_cast< LPITEMIDLIST
>( lParam
) ) );
595 case BFFM_VALIDATEFAILEDW
:
596 nRC
= pImpl
->onValidateFailed();
600 OSL_ASSERT( sal_False
);
606 //--------------------------------------------------------------------
608 //--------------------------------------------------------------------
610 LRESULT CALLBACK
CMtaFolderPicker::StaWndProc( HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
613 CMtaFolderPicker
* pImpl
= NULL
;
616 we connect to the belonging class instance of this
617 window using SetProp, GetProp etc.
618 this may fail if somehow the class instance destroyed
626 LPCREATESTRUCT lpcs
=
627 reinterpret_cast< LPCREATESTRUCT
>( lParam
);
629 OSL_ASSERT( lpcs
->lpCreateParams
);
631 // connect the instance handle to the window
632 SetPropA( hWnd
, CURRENT_INSTANCE
, lpcs
->lpCreateParams
);
637 // RemoveProp returns the saved value on success
638 pImpl
= reinterpret_cast< CMtaFolderPicker
* >(
639 RemovePropA( hWnd
, CURRENT_INSTANCE
) );
641 OSL_ASSERT( pImpl
&& !IsBadReadPtr( pImpl
, sizeof( CMtaFolderPicker
) ) );
644 case MSG_BROWSEFORFOLDER
:
646 RequestContext
* aReqCtx
= reinterpret_cast< RequestContext
* >( lParam
);
647 OSL_ASSERT( aReqCtx
);
649 pImpl
= reinterpret_cast< CMtaFolderPicker
* >(
650 GetPropA( hWnd
, CURRENT_INSTANCE
) );
652 OSL_ASSERT( pImpl
&& !IsBadReadPtr( pImpl
, sizeof( CMtaFolderPicker
) ) );
654 aReqCtx
->bRet
= pImpl
->onBrowseForFolder( );
655 SetEvent( aReqCtx
->hEvent
);
660 pImpl
= reinterpret_cast< CMtaFolderPicker
* >(
661 GetPropA( hWnd
, CURRENT_INSTANCE
) );
663 OSL_ASSERT( pImpl
&& !IsBadReadPtr( pImpl
, sizeof( CMtaFolderPicker
) ) );
665 DestroyWindow( pImpl
->m_hwndStaRequestWnd
);
669 PostQuitMessage( 0 );
673 lResult
= DefWindowProcA( hWnd
, uMsg
, wParam
, lParam
);
680 //--------------------------------------------------------------------
682 //--------------------------------------------------------------------
684 sal_Bool SAL_CALL
CMtaFolderPicker::createStaRequestWindow( )
688 if ( RegisterStaRequestWindowClass( ) )
690 m_hwndStaRequestWnd
= CreateWindowA(
691 g_szWndClsName
, NULL
,
693 NULL
, NULL
, m_hInstance
,
694 (LPVOID
)this // provide the instance of the class
697 bIsWnd
= IsWindow( m_hwndStaRequestWnd
);
699 // we do immediately unregister the window class
700 // if the creation of the window fails because we
701 // don't want to spoil the register class counter
703 UnregisterStaRequestWindowClass( );
705 OSL_ENSURE( bIsWnd
, "sta request window creation failed" );
711 //--------------------------------------------------------------------
713 //--------------------------------------------------------------------
715 unsigned int CMtaFolderPicker::run( )
717 OSL_ASSERT( m_hEvtThrdReady
);
719 // setup an sta environment
720 HRESULT hr
= CoInitialize( NULL
);
722 // if we can't setup an sta environment
723 // we stop here and return
726 OSL_ENSURE( sal_False
, "CoInitialize failed" );
727 return sal::static_int_cast
< unsigned int >(-1);
732 if ( createStaRequestWindow( ) )
734 SetEvent( m_hEvtThrdReady
);
738 while( GetMessageA( &msg
, NULL
, 0, 0 ) )
739 DispatchMessageA( &msg
);
745 OSL_ENSURE( sal_False
, "failed to create sta thread" );
746 nRet
= sal::static_int_cast
< unsigned int >(-1);
749 // shutdown sta environment
755 //--------------------------------------------------------------------
757 //--------------------------------------------------------------------
759 unsigned int WINAPI
CMtaFolderPicker::StaThreadProc( LPVOID pParam
)
761 CMtaFolderPicker
* pInst
=
762 reinterpret_cast<CMtaFolderPicker
*>( pParam
);
766 HRESULT hr
= OleInitialize( NULL
);
768 unsigned int result
= pInst
->run( );
770 if ( SUCCEEDED( hr
) )
776 //---------------------------------------------------
778 //---------------------------------------------------
780 ATOM SAL_CALL
CMtaFolderPicker::RegisterStaRequestWindowClass( )
782 osl::MutexGuard
aGuard( s_Mutex
);
784 if ( 0 == s_ClassAtom
)
788 ZeroMemory( &wcex
, sizeof( WNDCLASSEXA
) );
790 wcex
.cbSize
= sizeof(WNDCLASSEXA
);
792 wcex
.lpfnWndProc
= static_cast< WNDPROC
>( CMtaFolderPicker::StaWndProc
);
795 wcex
.hInstance
= m_hInstance
;
798 wcex
.hbrBackground
= NULL
;
799 wcex
.lpszMenuName
= NULL
;
800 wcex
.lpszClassName
= g_szWndClsName
;
803 s_ClassAtom
= RegisterClassExA( &wcex
);
804 OSL_ASSERT( s_ClassAtom
);
807 // increment the register class counter
808 // so that we keep track of the number
809 // of class registrations
810 if ( 0 != s_ClassAtom
)
811 s_StaRequestWndRegisterCount
++;
816 //---------------------------------------------------
818 //---------------------------------------------------
820 void SAL_CALL
CMtaFolderPicker::UnregisterStaRequestWindowClass( )
822 osl::MutexGuard
aGuard( s_Mutex
);
824 OSL_ASSERT( 0 != s_ClassAtom
);
826 // update the register class counter
827 // and unregister the window class if
828 // counter drops to zero
829 if ( 0 != s_ClassAtom
)
831 s_StaRequestWndRegisterCount
--;
832 OSL_ASSERT( s_StaRequestWndRegisterCount
>= 0 );
835 if ( 0 == s_StaRequestWndRegisterCount
)
838 (LPCTSTR
)MAKELONG( s_ClassAtom
, 0 ), m_hInstance
);