Update ooo320-m1
[ooovba.git] / fpicker / source / win32 / folderpicker / MtaFop.cxx
blobd63ca14d12d2b66a299794d155d3fb04bad3815b
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: MtaFop.cxx,v $
10 * $Revision: 1.17 $
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>
36 #include "MtaFop.hxx"
37 #include <wchar.h>
38 #include <process.h>
39 #include "..\misc\resourceprovider.hxx"
41 #include <systools/win32/comtools.hxx>
43 using rtl::OUString;
44 using osl::Condition;
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;
58 namespace
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
66 HANDLE hEvent;
67 sal_Bool bRet;
68 } 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 //-------------------------------
92 bool IsMTA()
94 HRESULT hr = CoInitialize(NULL);
96 if (RPC_E_CHANGED_MODE == hr)
97 return true;
99 if(SUCCEEDED(hr))
100 CoUninitialize();
102 return false;
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 //--------------------------------------------------------------------
115 // ctor
116 //--------------------------------------------------------------------
118 CMtaFolderPicker::CMtaFolderPicker( sal_uInt32 Flags ) :
119 m_hStaThread( NULL ),
120 m_uStaThreadId( 0 ),
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( );
140 Flag Available
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
149 Windows 2000
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(
168 MANUAL_RESET,
169 INIT_NONSIGNALED,
170 NULL );
172 if ( m_hEvtThrdReady )
174 // setup the sta thread
175 m_hStaThread = (HANDLE)_beginthreadex(
176 NULL,
178 CMtaFolderPicker::StaThreadProc,
179 this,
181 &m_uStaThreadId );
183 OSL_ASSERT( m_hStaThread );
186 OSL_ASSERT( m_hEvtThrdReady );
189 //--------------------------------------------------------------------
190 // dtor
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
198 // see ctor
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( );
223 if ( m_hStaThread )
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 )
232 TerminateThread(
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;
250 if (IsMTA())
253 OSL_ASSERT( m_hEvtThrdReady );
255 if ( WaitForSingleObject( m_hEvtThrdReady, MAX_WAITTIME ) != WAIT_OBJECT_0 )
257 OSL_ENSURE( sal_False, "sta thread not ready" );
258 return sal_False;
261 RequestContext aReqCtx;
263 if ( !InitializeRequestContext( &aReqCtx ) )
265 OSL_ASSERT( sal_False );
266 return sal_False;
269 // marshall request into the sta thread
270 PostMessageA(
271 m_hwndStaRequestWnd,
272 MSG_BROWSEFORFOLDER,
274 reinterpret_cast< LPARAM >( &aReqCtx ) );
276 // waiting for the event to be signaled or
277 // window messages so that we don't block
278 // our parent window
280 sal_Bool bContinue = sal_True;
282 while ( bContinue )
284 DWORD dwResult = MsgWaitForMultipleObjects(
285 1, &aReqCtx.hEvent, FALSE, INFINITE, QS_ALLEVENTS );
287 switch ( dwResult )
289 // the request context event is signaled
290 case WAIT_OBJECT_0:
291 bContinue = sal_False;
292 break;
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
303 MSG msg;
304 while ( PeekMessageA( &msg, NULL, 0, 0, PM_REMOVE ) )
305 DispatchMessageA(&msg);
307 break;
309 // should not happen
310 default:
311 OSL_ASSERT( sal_False );
315 /*sal_Bool*/ bRet = aReqCtx.bRet;
316 DeinitializeRequestContext( &aReqCtx );
318 else
320 bRet = onBrowseForFolder();
323 return bRet;
326 //--------------------------------------------------------------------
328 //--------------------------------------------------------------------
330 void SAL_CALL CMtaFolderPicker::setDisplayDirectory( const OUString& aDirectory )
332 m_displayDir = aDirectory;
335 //--------------------------------------------------------------------
337 //--------------------------------------------------------------------
339 OUString SAL_CALL CMtaFolderPicker::getDisplayDirectory( )
341 return m_displayDir;
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 //-----------------------------------------------------
381 // XCancellable
382 //-----------------------------------------------------
384 void SAL_CALL CMtaFolderPicker::cancel( )
386 if ( IsWindow( m_hwnd ) )
388 // simulate a mouse click to the
389 // cancel button
390 PostMessageA(
391 m_hwnd,
392 WM_COMMAND,
393 MAKEWPARAM( IDCANCEL, BN_CLICKED ),
394 (LPARAM)GetDlgItem( m_hwnd, IDCANCEL ) );
398 //--------------------------------------------------------------------
400 //--------------------------------------------------------------------
402 sal_Bool SAL_CALL CMtaFolderPicker::onBrowseForFolder( )
404 sal_Bool bRet;
405 LPITEMIDLIST lpiid;
407 // pre SHBrowseFroFolder
409 m_bi.pidlRoot = 0;
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 );
423 return bRet;
426 //--------------------------------------------------------------------
428 //--------------------------------------------------------------------
430 void SAL_CALL CMtaFolderPicker::releaseItemIdList( LPITEMIDLIST lpItemIdList )
432 IMallocPtr pIMalloc;
433 SHGetMalloc(&pIMalloc);
434 if (pIMalloc.is())
436 pIMalloc->Free(lpItemIdList);
437 lpItemIdList = NULL;
441 //--------------------------------------------------------------------
443 //--------------------------------------------------------------------
445 LPITEMIDLIST SAL_CALL CMtaFolderPicker::getItemIdListFromPath( const rtl::OUString& aDirectory )
447 // parameter checking
448 if ( !aDirectory.getLength( ) )
449 return NULL;
451 LPITEMIDLIST lpItemIdList(NULL);
453 IShellFolderPtr pIShellFolder;
454 SHGetDesktopFolder(&pIShellFolder);
456 if (pIShellFolder.is())
458 pIShellFolder->ParseDisplayName(
459 NULL,
460 NULL,
461 reinterpret_cast<LPWSTR>(const_cast< sal_Unicode* >( aDirectory.getStr( ) )),
462 NULL,
463 &lpItemIdList,
464 NULL );
467 return lpItemIdList;
470 //--------------------------------------------------------------------
472 //--------------------------------------------------------------------
474 OUString SAL_CALL CMtaFolderPicker::getPathFromItemIdList( LPCITEMIDLIST lpItemIdList )
476 OUString path;
478 if ( lpItemIdList )
480 bool bRet = SHGetPathFromIDListW( lpItemIdList, reinterpret_cast<LPWSTR>(m_pathBuff.get()) );
481 if ( bRet )
482 path = m_pathBuff.get( );
485 return path;
488 //--------------------------------------------------------------------
490 //--------------------------------------------------------------------
492 void SAL_CALL CMtaFolderPicker::enableOk( sal_Bool bEnable )
494 OSL_ASSERT( IsWindow( m_hwnd ) );
496 SendMessageW(
497 m_hwnd,
498 BFFM_ENABLEOK,
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 ) );
511 #ifdef _MSC_VER
512 #pragma message( "#######################################" )
513 #pragma message( "SendMessageW wrapper has to be extended" )
514 #pragma message( "#######################################" )
515 #endif
517 SendMessageW(
518 m_hwnd,
519 BFFM_SETSELECTIONW,
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 ) );
532 SendMessageW(
533 m_hwnd,
534 BFFM_SETSTATUSTEXTW,
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 )
549 SendMessageA(
550 m_hwnd,
551 BFFM_SETSELECTION,
552 (WPARAM)FALSE,
553 (LPARAM) lpiidDisplayDir );
555 releaseItemIdList( lpiidDisplayDir );
559 //--------------------------------------------------------------------
561 //--------------------------------------------------------------------
563 sal_uInt32 CMtaFolderPicker::onValidateFailed()
565 // to be overwritten by subclasses
566 return 1;
569 //--------------------------------------------------------------------
571 //--------------------------------------------------------------------
573 int CALLBACK CMtaFolderPicker::FolderPickerCallback( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData )
575 CMtaFolderPicker* pImpl = reinterpret_cast< CMtaFolderPicker* >( lpData );
576 OSL_ASSERT( pImpl );
578 int nRC = 0;
580 switch( uMsg )
582 case BFFM_INITIALIZED:
583 pImpl->m_hwnd = hwnd;
584 pImpl->onInitialized( );
585 SetWindowTextW( hwnd, reinterpret_cast<LPCWSTR>(pImpl->m_dialogTitle.getStr()) );
586 break;
588 case BFFM_SELCHANGED:
589 pImpl->m_hwnd = hwnd;
590 pImpl->onSelChanged(
591 pImpl->getPathFromItemIdList(
592 reinterpret_cast< LPITEMIDLIST >( lParam ) ) );
593 break;
595 case BFFM_VALIDATEFAILEDW:
596 nRC = pImpl->onValidateFailed();
597 break;
599 default:
600 OSL_ASSERT( sal_False );
603 return nRC;
606 //--------------------------------------------------------------------
607 // the window proc
608 //--------------------------------------------------------------------
610 LRESULT CALLBACK CMtaFolderPicker::StaWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
612 LRESULT lResult = 0;
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
619 before the window
622 switch( uMsg )
624 case WM_CREATE:
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 );
634 break;
636 case WM_NCDESTROY:
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 ) ) );
642 break;
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 );
657 break;
659 case MSG_SHUTDOWN:
660 pImpl = reinterpret_cast< CMtaFolderPicker* >(
661 GetPropA( hWnd, CURRENT_INSTANCE ) );
663 OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) );
665 DestroyWindow( pImpl->m_hwndStaRequestWnd );
666 break;
668 case WM_DESTROY:
669 PostQuitMessage( 0 );
670 break;
672 default:
673 lResult = DefWindowProcA( hWnd, uMsg, wParam, lParam );
674 break;
677 return lResult;
680 //--------------------------------------------------------------------
682 //--------------------------------------------------------------------
684 sal_Bool SAL_CALL CMtaFolderPicker::createStaRequestWindow( )
686 bool bIsWnd = false;
688 if ( RegisterStaRequestWindowClass( ) )
690 m_hwndStaRequestWnd = CreateWindowA(
691 g_szWndClsName, NULL,
692 0, 0, 0, 0, 0,
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
702 if ( !bIsWnd )
703 UnregisterStaRequestWindowClass( );
705 OSL_ENSURE( bIsWnd, "sta request window creation failed" );
708 return bIsWnd;
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
724 if ( FAILED( hr ) )
726 OSL_ENSURE( sal_False, "CoInitialize failed" );
727 return sal::static_int_cast< unsigned int >(-1);
730 unsigned int nRet;
732 if ( createStaRequestWindow( ) )
734 SetEvent( m_hEvtThrdReady );
736 // pumping messages
737 MSG msg;
738 while( GetMessageA( &msg, NULL, 0, 0 ) )
739 DispatchMessageA( &msg );
741 nRet = 0;
743 else
745 OSL_ENSURE( sal_False, "failed to create sta thread" );
746 nRet = sal::static_int_cast< unsigned int >(-1);
749 // shutdown sta environment
750 CoUninitialize( );
752 return nRet;
755 //--------------------------------------------------------------------
757 //--------------------------------------------------------------------
759 unsigned int WINAPI CMtaFolderPicker::StaThreadProc( LPVOID pParam )
761 CMtaFolderPicker* pInst =
762 reinterpret_cast<CMtaFolderPicker*>( pParam );
764 OSL_ASSERT( pInst );
766 HRESULT hr = OleInitialize( NULL );
768 unsigned int result = pInst->run( );
770 if ( SUCCEEDED( hr ) )
771 OleUninitialize();
773 return result;
776 //---------------------------------------------------
778 //---------------------------------------------------
780 ATOM SAL_CALL CMtaFolderPicker::RegisterStaRequestWindowClass( )
782 osl::MutexGuard aGuard( s_Mutex );
784 if ( 0 == s_ClassAtom )
786 WNDCLASSEXA wcex;
788 ZeroMemory( &wcex, sizeof( WNDCLASSEXA ) );
790 wcex.cbSize = sizeof(WNDCLASSEXA);
791 wcex.style = 0;
792 wcex.lpfnWndProc = static_cast< WNDPROC >( CMtaFolderPicker::StaWndProc );
793 wcex.cbClsExtra = 0;
794 wcex.cbWndExtra = 0;
795 wcex.hInstance = m_hInstance;
796 wcex.hIcon = NULL;
797 wcex.hCursor = NULL;
798 wcex.hbrBackground = NULL;
799 wcex.lpszMenuName = NULL;
800 wcex.lpszClassName = g_szWndClsName;
801 wcex.hIconSm = NULL;
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++;
813 return s_ClassAtom;
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 )
837 UnregisterClass(
838 (LPCTSTR)MAKELONG( s_ClassAtom, 0 ), m_hInstance );
840 s_ClassAtom = 0;