diff options
Diffstat (limited to 'fpicker/source/win32/folderpicker/MtaFop.cxx')
-rw-r--r-- | fpicker/source/win32/folderpicker/MtaFop.cxx | 839 |
1 files changed, 839 insertions, 0 deletions
diff --git a/fpicker/source/win32/folderpicker/MtaFop.cxx b/fpicker/source/win32/folderpicker/MtaFop.cxx new file mode 100644 index 000000000000..254887a2a0f1 --- /dev/null +++ b/fpicker/source/win32/folderpicker/MtaFop.cxx @@ -0,0 +1,839 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_fpicker.hxx" +#include <osl/diagnose.h> +#include <osl/conditn.hxx> + +#include "MtaFop.hxx" +#include <wchar.h> +#include <process.h> +#include "..\misc\resourceprovider.hxx" + +#include <systools/win32/comtools.hxx> + +using rtl::OUString; +using osl::Condition; + +const sal_uInt32 MSG_BROWSEFORFOLDER = WM_USER + 1; +const sal_uInt32 MSG_SHUTDOWN = WM_USER + 2; + +const sal_uInt32 MAX_WAITTIME = 2000; // msec + +const sal_Bool MANUAL_RESET = sal_True; +const sal_Bool AUTO_RESET = sal_False; +const sal_Bool INIT_NONSIGNALED = sal_False; + +typedef sal::systools::COMReference<IMalloc> IMallocPtr; +typedef sal::systools::COMReference<IShellFolder> IShellFolderPtr; + +namespace +{ + const char* FOLDERPICKER_SRV_DLL_NAME = "fop.dll"; + const char g_szWndClsName[] = "FopStaReqWnd###"; + const char* CURRENT_INSTANCE = "CurrInst"; + + typedef struct _RequestContext + { + HANDLE hEvent; + sal_Bool bRet; + } RequestContext; + + inline sal_Bool InitializeRequestContext( RequestContext* aRequestContext ) + { + OSL_ASSERT( aRequestContext ); + + aRequestContext->hEvent = CreateEventA( + 0, AUTO_RESET, INIT_NONSIGNALED, NULL ); + + aRequestContext->bRet = sal_False; + + return ( 0 != aRequestContext->hEvent ); + } + + inline void DeinitializeRequestContext( RequestContext* aRequestContext ) + { + OSL_ASSERT( aRequestContext && aRequestContext->hEvent ); + CloseHandle( aRequestContext->hEvent ); + } + + //------------------------------- + // Determine if current thread is + // an MTA or STA thread + //------------------------------- + bool IsMTA() + { + HRESULT hr = CoInitialize(NULL); + + if (RPC_E_CHANGED_MODE == hr) + return true; + + if(SUCCEEDED(hr)) + CoUninitialize(); + + return false; + } +} + +//---------------------------------------------------------------- +// static member initialization +//---------------------------------------------------------------- + +ATOM CMtaFolderPicker::s_ClassAtom = 0; +osl::Mutex CMtaFolderPicker::s_Mutex; +sal_Int32 CMtaFolderPicker::s_StaRequestWndRegisterCount = 0; + +//-------------------------------------------------------------------- +// ctor +//-------------------------------------------------------------------- + +CMtaFolderPicker::CMtaFolderPicker( sal_uInt32 Flags ) : + m_hStaThread( NULL ), + m_uStaThreadId( 0 ), + m_hEvtThrdReady( NULL ), + m_hwndStaRequestWnd( NULL ) +{ + m_hInstance = GetModuleHandleA( FOLDERPICKER_SRV_DLL_NAME ); + OSL_ENSURE( m_hInstance, "The name of the FolderPicker service dll must have changed" ); + + ZeroMemory( &m_bi, sizeof( m_bi ) ); + + // !!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!! + // + // Remember: This HACK prevents you from stepping + // through your code in the debugger because if you + // set a break point in the ctor here the debugger + // may become the owner of the FolderBrowse dialog + // and so it seems that the Visual Studio and the + // office are hanging + m_bi.hwndOwner = GetForegroundWindow( ); + + /* + Flag Available + -------------------------------- + BIF_EDITBOX Version 4.71 + BIF_NEWDIALOGSTYLE Version 5.0 + BIF_SHAREABLE Version 5.0 + BIF_VALIDATE Version 4.71 + + Version 4.71 - Internet Explorer 4.0 + Version 5.0 - Internet Explorer 5.0 + Windows 2000 + */ + m_bi.ulFlags = Flags; + + m_bi.lpfn = CMtaFolderPicker::FolderPickerCallback; + m_bi.lParam = reinterpret_cast< LPARAM >( this ); + + //--------------------------------------- + // read the default strings for title and + // description from a resource file + + CResourceProvider ResProvider; + + m_dialogTitle = ResProvider.getResString( 500 ); + m_Description = ResProvider.getResString( 501 ); + + // signals that the thread was successfully set up + m_hEvtThrdReady = CreateEventA( + 0, + MANUAL_RESET, + INIT_NONSIGNALED, + NULL ); + + if ( m_hEvtThrdReady ) + { + // setup the sta thread + m_hStaThread = (HANDLE)_beginthreadex( + NULL, + 0, + CMtaFolderPicker::StaThreadProc, + this, + 0, + &m_uStaThreadId ); + + OSL_ASSERT( m_hStaThread ); + } + + OSL_ASSERT( m_hEvtThrdReady ); +} + +//-------------------------------------------------------------------- +// dtor +//-------------------------------------------------------------------- + +CMtaFolderPicker::~CMtaFolderPicker( ) +{ + // only if the is a valid event handle + // there may also be a thread a hidden + // target request window and so on + // see ctor + if ( m_hEvtThrdReady ) + { + // block calling threads because we + // are about to shutdown + ResetEvent( m_hEvtThrdReady ); + + // force the destruction of the sta thread request window + // and the end of the thread + // remeber: DestroyWindow may only be called from within + // the thread that created the window + if ( IsWindow( m_hwndStaRequestWnd ) ) + { + SendMessageA( m_hwndStaRequestWnd, MSG_SHUTDOWN, 0, 0 ); + + // we place unregister class here because + // if we have a valid window we must have + // sucessfully registered a window class + // if the creation of the window itself + // failed after registering the window + // class we have unregistered it immediately + // in createStaRequestWindow below + UnregisterStaRequestWindowClass( ); + } + + if ( m_hStaThread ) + { + // wait for thread shutdown + sal_uInt32 dwResult = WaitForSingleObject( m_hStaThread, MAX_WAITTIME ); + OSL_ENSURE( dwResult == WAIT_OBJECT_0, "sta thread could not terminate" ); + + // terminate the thread if it + // doesn't shutdown itself + if ( WAIT_OBJECT_0 != dwResult ) + TerminateThread( + m_hStaThread, sal::static_int_cast< DWORD >(-1) ); + + CloseHandle( m_hStaThread ); + } + + CloseHandle( m_hEvtThrdReady ); + } +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +sal_Bool CMtaFolderPicker::browseForFolder( ) +{ + sal_Bool bRet = sal_False; + + if (IsMTA()) + { + + OSL_ASSERT( m_hEvtThrdReady ); + + if ( WaitForSingleObject( m_hEvtThrdReady, MAX_WAITTIME ) != WAIT_OBJECT_0 ) + { + OSL_ENSURE( sal_False, "sta thread not ready" ); + return sal_False; + } + + RequestContext aReqCtx; + + if ( !InitializeRequestContext( &aReqCtx ) ) + { + OSL_ASSERT( sal_False ); + return sal_False; + } + + // marshall request into the sta thread + PostMessageA( + m_hwndStaRequestWnd, + MSG_BROWSEFORFOLDER, + 0, + reinterpret_cast< LPARAM >( &aReqCtx ) ); + + // waiting for the event to be signaled or + // window messages so that we don't block + // our parent window + + sal_Bool bContinue = sal_True; + + while ( bContinue ) + { + DWORD dwResult = MsgWaitForMultipleObjects( + 1, &aReqCtx.hEvent, FALSE, INFINITE, QS_ALLEVENTS ); + + switch ( dwResult ) + { + // the request context event is signaled + case WAIT_OBJECT_0: + bContinue = sal_False; + break; + + // a window message has arrived + case WAIT_OBJECT_0 + 1: + { + // dispatching all messages but we expect to + // receive only paint or timer messages that's + // why we don't need to call TranslateMessage or + // TranslateAccelerator, because keybord or + // mouse messages are for the FolderPicker which + // is in the foreground and should not arrive here + MSG msg; + while ( PeekMessageA( &msg, NULL, 0, 0, PM_REMOVE ) ) + DispatchMessageA(&msg); + } + break; + + // should not happen + default: + OSL_ASSERT( sal_False ); + } + } + + /*sal_Bool*/ bRet = aReqCtx.bRet; + DeinitializeRequestContext( &aReqCtx ); + } + else + { + bRet = onBrowseForFolder(); + } + + return bRet; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::setDisplayDirectory( const OUString& aDirectory ) +{ + m_displayDir = aDirectory; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +OUString SAL_CALL CMtaFolderPicker::getDisplayDirectory( ) +{ + return m_displayDir; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +OUString SAL_CALL CMtaFolderPicker::getDirectory( ) +{ + return m_SelectedDir; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::setDescription( const rtl::OUString& aDescription ) +{ + m_Description = aDescription; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::setTitle( const OUString& aTitle ) +{ + m_dialogTitle = aTitle; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +OUString SAL_CALL CMtaFolderPicker::getTitle( ) +{ + return m_dialogTitle; +} + +//----------------------------------------------------- +// XCancellable +//----------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::cancel( ) +{ + if ( IsWindow( m_hwnd ) ) + { + // simulate a mouse click to the + // cancel button + PostMessageA( + m_hwnd, + WM_COMMAND, + MAKEWPARAM( IDCANCEL, BN_CLICKED ), + (LPARAM)GetDlgItem( m_hwnd, IDCANCEL ) ); + } +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +sal_Bool SAL_CALL CMtaFolderPicker::onBrowseForFolder( ) +{ + sal_Bool bRet; + LPITEMIDLIST lpiid; + + // pre SHBrowseFroFolder + + m_bi.pidlRoot = 0; + m_bi.pszDisplayName = reinterpret_cast<LPWSTR>(m_pathBuff.get()); + + if ( m_Description.getLength( ) ) + m_bi.lpszTitle = reinterpret_cast<LPCWSTR>(m_Description.getStr( )); + + lpiid = SHBrowseForFolderW( &m_bi ); + bRet = ( NULL != lpiid ); + + // post SHBrowseForFolder + + m_SelectedDir = getPathFromItemIdList( lpiid ); + releaseItemIdList( lpiid ); + + return bRet; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::releaseItemIdList( LPITEMIDLIST lpItemIdList ) +{ + IMallocPtr pIMalloc; + SHGetMalloc(&pIMalloc); + if (pIMalloc.is()) + { + pIMalloc->Free(lpItemIdList); + lpItemIdList = NULL; + } +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +LPITEMIDLIST SAL_CALL CMtaFolderPicker::getItemIdListFromPath( const rtl::OUString& aDirectory ) +{ + // parameter checking + if ( !aDirectory.getLength( ) ) + return NULL; + + LPITEMIDLIST lpItemIdList(NULL); + + IShellFolderPtr pIShellFolder; + SHGetDesktopFolder(&pIShellFolder); + + if (pIShellFolder.is()) + { + pIShellFolder->ParseDisplayName( + NULL, + NULL, + reinterpret_cast<LPWSTR>(const_cast< sal_Unicode* >( aDirectory.getStr( ) )), + NULL, + &lpItemIdList, + NULL ); + } + + return lpItemIdList; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +OUString SAL_CALL CMtaFolderPicker::getPathFromItemIdList( LPCITEMIDLIST lpItemIdList ) +{ + OUString path; + + if ( lpItemIdList ) + { + bool bRet = SHGetPathFromIDListW( lpItemIdList, reinterpret_cast<LPWSTR>(m_pathBuff.get()) ); + if ( bRet ) + path = m_pathBuff.get( ); + } + + return path; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::enableOk( sal_Bool bEnable ) +{ + OSL_ASSERT( IsWindow( m_hwnd ) ); + + SendMessageW( + m_hwnd, + BFFM_ENABLEOK, + static_cast< WPARAM >( 0 ), + static_cast< LPARAM >( bEnable ) ); +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::setSelection( const rtl::OUString& aDirectory ) +{ + OSL_ASSERT( IsWindow( m_hwnd ) ); + +#ifdef _MSC_VER +#pragma message( "#######################################" ) +#pragma message( "SendMessageW wrapper has to be extended" ) +#pragma message( "#######################################" ) +#endif + + SendMessageW( + m_hwnd, + BFFM_SETSELECTIONW, + static_cast< WPARAM >( sal_True ), + reinterpret_cast< LPARAM >( aDirectory.getStr( ) ) ); +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::setStatusText( const rtl::OUString& aStatusText ) +{ + OSL_ASSERT( IsWindow( m_hwnd ) ); + + SendMessageW( + m_hwnd, + BFFM_SETSTATUSTEXTW, + static_cast< WPARAM >( 0 ), + reinterpret_cast< LPARAM >( aStatusText.getStr( ) ) ); +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::onInitialized( ) +{ + LPITEMIDLIST lpiidDisplayDir = getItemIdListFromPath( m_displayDir ); + + if ( lpiidDisplayDir ) + { + SendMessageA( + m_hwnd, + BFFM_SETSELECTION, + (WPARAM)FALSE, + (LPARAM) lpiidDisplayDir ); + + releaseItemIdList( lpiidDisplayDir ); + } +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +sal_uInt32 CMtaFolderPicker::onValidateFailed() +{ + // to be overwritten by subclasses + return 1; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +int CALLBACK CMtaFolderPicker::FolderPickerCallback( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData ) +{ + CMtaFolderPicker* pImpl = reinterpret_cast< CMtaFolderPicker* >( lpData ); + OSL_ASSERT( pImpl ); + + int nRC = 0; + + switch( uMsg ) + { + case BFFM_INITIALIZED: + pImpl->m_hwnd = hwnd; + pImpl->onInitialized( ); + SetWindowTextW( hwnd, reinterpret_cast<LPCWSTR>(pImpl->m_dialogTitle.getStr()) ); + break; + + case BFFM_SELCHANGED: + pImpl->m_hwnd = hwnd; + pImpl->onSelChanged( + pImpl->getPathFromItemIdList( + reinterpret_cast< LPITEMIDLIST >( lParam ) ) ); + break; + + case BFFM_VALIDATEFAILEDW: + nRC = pImpl->onValidateFailed(); + break; + + default: + OSL_ASSERT( sal_False ); + } + + return nRC; +} + +//-------------------------------------------------------------------- +// the window proc +//-------------------------------------------------------------------- + +LRESULT CALLBACK CMtaFolderPicker::StaWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + LRESULT lResult = 0; + CMtaFolderPicker* pImpl = NULL; + + /* + we connect to the belonging class instance of this + window using SetProp, GetProp etc. + this may fail if somehow the class instance destroyed + before the window + */ + + switch( uMsg ) + { + case WM_CREATE: + { + LPCREATESTRUCT lpcs = + reinterpret_cast< LPCREATESTRUCT >( lParam ); + + OSL_ASSERT( lpcs->lpCreateParams ); + + // connect the instance handle to the window + SetPropA( hWnd, CURRENT_INSTANCE, lpcs->lpCreateParams ); + } + break; + + case WM_NCDESTROY: + // RemoveProp returns the saved value on success + pImpl = reinterpret_cast< CMtaFolderPicker* >( + RemovePropA( hWnd, CURRENT_INSTANCE ) ); + + OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) ); + break; + + case MSG_BROWSEFORFOLDER: + { + RequestContext* aReqCtx = reinterpret_cast< RequestContext* >( lParam ); + OSL_ASSERT( aReqCtx ); + + pImpl = reinterpret_cast< CMtaFolderPicker* >( + GetPropA( hWnd, CURRENT_INSTANCE ) ); + + OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) ); + + aReqCtx->bRet = pImpl->onBrowseForFolder( ); + SetEvent( aReqCtx->hEvent ); + } + break; + + case MSG_SHUTDOWN: + pImpl = reinterpret_cast< CMtaFolderPicker* >( + GetPropA( hWnd, CURRENT_INSTANCE ) ); + + OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) ); + + DestroyWindow( pImpl->m_hwndStaRequestWnd ); + break; + + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + + default: + lResult = DefWindowProcA( hWnd, uMsg, wParam, lParam ); + break; + } + + return lResult; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +sal_Bool SAL_CALL CMtaFolderPicker::createStaRequestWindow( ) +{ + bool bIsWnd = false; + + if ( RegisterStaRequestWindowClass( ) ) + { + m_hwndStaRequestWnd = CreateWindowA( + g_szWndClsName, NULL, + 0, 0, 0, 0, 0, + NULL, NULL, m_hInstance, + (LPVOID)this // provide the instance of the class + ); + + bIsWnd = IsWindow( m_hwndStaRequestWnd ); + + // we do immediately unregister the window class + // if the creation of the window fails because we + // don't want to spoil the register class counter + if ( !bIsWnd ) + UnregisterStaRequestWindowClass( ); + + OSL_ENSURE( bIsWnd, "sta request window creation failed" ); + } + + return bIsWnd; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +unsigned int CMtaFolderPicker::run( ) +{ + OSL_ASSERT( m_hEvtThrdReady ); + + // setup an sta environment + HRESULT hr = CoInitialize( NULL ); + + // if we can't setup an sta environment + // we stop here and return + if ( FAILED( hr ) ) + { + OSL_ENSURE( sal_False, "CoInitialize failed" ); + return sal::static_int_cast< unsigned int >(-1); + } + + unsigned int nRet; + + if ( createStaRequestWindow( ) ) + { + SetEvent( m_hEvtThrdReady ); + + // pumping messages + MSG msg; + while( GetMessageA( &msg, NULL, 0, 0 ) ) + DispatchMessageA( &msg ); + + nRet = 0; + } + else + { + OSL_ENSURE( sal_False, "failed to create sta thread" ); + nRet = sal::static_int_cast< unsigned int >(-1); + } + + // shutdown sta environment + CoUninitialize( ); + + return nRet; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + +unsigned int WINAPI CMtaFolderPicker::StaThreadProc( LPVOID pParam ) +{ + CMtaFolderPicker* pInst = + reinterpret_cast<CMtaFolderPicker*>( pParam ); + + OSL_ASSERT( pInst ); + + HRESULT hr = OleInitialize( NULL ); + + unsigned int result = pInst->run( ); + + if ( SUCCEEDED( hr ) ) + OleUninitialize(); + + return result; +} + +//--------------------------------------------------- +// +//--------------------------------------------------- + +ATOM SAL_CALL CMtaFolderPicker::RegisterStaRequestWindowClass( ) +{ + osl::MutexGuard aGuard( s_Mutex ); + + if ( 0 == s_ClassAtom ) + { + WNDCLASSEXA wcex; + + ZeroMemory( &wcex, sizeof( WNDCLASSEXA ) ); + + wcex.cbSize = sizeof(WNDCLASSEXA); + wcex.style = 0; + wcex.lpfnWndProc = static_cast< WNDPROC >( CMtaFolderPicker::StaWndProc ); + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = m_hInstance; + wcex.hIcon = NULL; + wcex.hCursor = NULL; + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = g_szWndClsName; + wcex.hIconSm = NULL; + + s_ClassAtom = RegisterClassExA( &wcex ); + OSL_ASSERT( s_ClassAtom ); + } + + // increment the register class counter + // so that we keep track of the number + // of class registrations + if ( 0 != s_ClassAtom ) + s_StaRequestWndRegisterCount++; + + return s_ClassAtom; +} + +//--------------------------------------------------- +// +//--------------------------------------------------- + +void SAL_CALL CMtaFolderPicker::UnregisterStaRequestWindowClass( ) +{ + osl::MutexGuard aGuard( s_Mutex ); + + OSL_ASSERT( 0 != s_ClassAtom ); + + // update the register class counter + // and unregister the window class if + // counter drops to zero + if ( 0 != s_ClassAtom ) + { + s_StaRequestWndRegisterCount--; + OSL_ASSERT( s_StaRequestWndRegisterCount >= 0 ); + } + + if ( 0 == s_StaRequestWndRegisterCount ) + { + UnregisterClass( + (LPCTSTR)MAKELONG( s_ClassAtom, 0 ), m_hInstance ); + + s_ClassAtom = 0; + } +} |