update emoji autocorrect entries from po-files
[LibreOffice.git] / fpicker / source / win32 / folderpicker / MtaFop.cxx
blobd832ddf0aa3ee6bda7973ecc6e39a5f42d0dcae8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <osl/thread.h>
22 #include <osl/conditn.hxx>
24 #include "MtaFop.hxx"
25 #include <wchar.h>
26 #include <process.h>
27 #include "../misc/resourceprovider.hxx"
29 #include <systools/win32/comtools.hxx>
31 using osl::Condition;
33 const sal_uInt32 MSG_BROWSEFORFOLDER = WM_USER + 1;
34 const sal_uInt32 MSG_SHUTDOWN = WM_USER + 2;
36 const sal_uInt32 MAX_WAITTIME = 2000; // msec
38 const sal_Bool MANUAL_RESET = sal_True;
39 const sal_Bool AUTO_RESET = sal_False;
40 const sal_Bool INIT_NONSIGNALED = sal_False;
42 typedef sal::systools::COMReference<IMalloc> IMallocPtr;
43 typedef sal::systools::COMReference<IShellFolder> IShellFolderPtr;
45 namespace
47 const char* FOLDERPICKER_SRV_DLL_NAME = "fps.dll";
48 const char g_szWndClsName[] = "FopStaReqWnd###";
49 const char* CURRENT_INSTANCE = "CurrInst";
51 typedef struct _RequestContext
53 HANDLE hEvent;
54 sal_Bool bRet;
55 } RequestContext;
57 inline sal_Bool InitializeRequestContext( RequestContext* aRequestContext )
59 OSL_ASSERT( aRequestContext );
61 aRequestContext->hEvent = CreateEventA(
62 0, AUTO_RESET, INIT_NONSIGNALED, NULL );
64 aRequestContext->bRet = sal_False;
66 return ( 0 != aRequestContext->hEvent );
69 inline void DeinitializeRequestContext( RequestContext* aRequestContext )
71 OSL_ASSERT( aRequestContext && aRequestContext->hEvent );
72 CloseHandle( aRequestContext->hEvent );
76 // Determine if current thread is
77 // an MTA or STA thread
79 bool IsMTA()
81 HRESULT hr = CoInitialize(NULL);
83 if (RPC_E_CHANGED_MODE == hr)
84 return true;
86 if(SUCCEEDED(hr))
87 CoUninitialize();
89 return false;
94 // static member initialization
97 ATOM CMtaFolderPicker::s_ClassAtom = 0;
98 osl::Mutex CMtaFolderPicker::s_Mutex;
99 sal_Int32 CMtaFolderPicker::s_StaRequestWndRegisterCount = 0;
102 // ctor
105 CMtaFolderPicker::CMtaFolderPicker( sal_uInt32 Flags ) :
106 m_hStaThread( NULL ),
107 m_uStaThreadId( 0 ),
108 m_hEvtThrdReady( NULL ),
109 m_hwndStaRequestWnd( NULL )
111 m_hInstance = GetModuleHandleA( FOLDERPICKER_SRV_DLL_NAME );
112 OSL_ENSURE( m_hInstance, "The name of the FolderPicker service dll must have changed" );
114 ZeroMemory( &m_bi, sizeof( m_bi ) );
116 // !!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!
118 // Remember: This HACK prevents you from stepping
119 // through your code in the debugger because if you
120 // set a break point in the ctor here the debugger
121 // may become the owner of the FolderBrowse dialog
122 // and so it seems that the Visual Studio and the
123 // office are hanging
124 m_bi.hwndOwner = GetForegroundWindow( );
127 Flag Available
128 --------------------------------
129 BIF_EDITBOX Version 4.71
130 BIF_NEWDIALOGSTYLE Version 5.0
131 BIF_SHAREABLE Version 5.0
132 BIF_VALIDATE Version 4.71
134 Version 4.71 - Internet Explorer 4.0
135 Version 5.0 - Internet Explorer 5.0
136 Windows 2000
138 m_bi.ulFlags = Flags;
140 m_bi.lpfn = CMtaFolderPicker::FolderPickerCallback;
141 m_bi.lParam = reinterpret_cast< LPARAM >( this );
144 // read the default strings for title and
145 // description from a resource file
147 CResourceProvider ResProvider;
149 m_dialogTitle = ResProvider.getResString( 500 );
150 m_Description = ResProvider.getResString( 501 );
152 // signals that the thread was successfully set up
153 m_hEvtThrdReady = CreateEventA(
155 MANUAL_RESET,
156 INIT_NONSIGNALED,
157 NULL );
159 if ( m_hEvtThrdReady )
161 // setup the sta thread
162 m_hStaThread = (HANDLE)_beginthreadex(
163 NULL,
165 CMtaFolderPicker::StaThreadProc,
166 this,
168 &m_uStaThreadId );
170 OSL_ASSERT( m_hStaThread );
173 OSL_ASSERT( m_hEvtThrdReady );
177 // dtor
180 CMtaFolderPicker::~CMtaFolderPicker( )
182 // only if the is a valid event handle
183 // there may also be a thread a hidden
184 // target request window and so on
185 // see ctor
186 if ( m_hEvtThrdReady )
188 // block calling threads because we
189 // are about to shutdown
190 ResetEvent( m_hEvtThrdReady );
192 // force the destruction of the sta thread request window
193 // and the end of the thread
194 // remember: DestroyWindow may only be called from within
195 // the thread that created the window
196 if ( IsWindow( m_hwndStaRequestWnd ) )
198 SendMessageA( m_hwndStaRequestWnd, MSG_SHUTDOWN, 0, 0 );
200 // we place unregister class here because
201 // if we have a valid window we must have
202 // successfully registered a window class
203 // if the creation of the window itself
204 // failed after registering the window
205 // class we have unregistered it immediately
206 // in createStaRequestWindow below
207 UnregisterStaRequestWindowClass( );
210 if ( m_hStaThread )
212 // wait for thread shutdown
213 sal_uInt32 dwResult = WaitForSingleObject( m_hStaThread, MAX_WAITTIME );
214 OSL_ENSURE( dwResult == WAIT_OBJECT_0, "sta thread could not terminate" );
216 // terminate the thread if it
217 // doesn't shutdown itself
218 if ( WAIT_OBJECT_0 != dwResult )
219 TerminateThread(
220 m_hStaThread, sal::static_int_cast< DWORD >(-1) );
222 CloseHandle( m_hStaThread );
225 CloseHandle( m_hEvtThrdReady );
233 sal_Bool CMtaFolderPicker::browseForFolder( )
235 sal_Bool bRet = sal_False;
237 if (IsMTA())
240 OSL_ASSERT( m_hEvtThrdReady );
242 if ( WaitForSingleObject( m_hEvtThrdReady, MAX_WAITTIME ) != WAIT_OBJECT_0 )
244 OSL_FAIL( "sta thread not ready" );
245 return sal_False;
248 RequestContext aReqCtx;
250 if ( !InitializeRequestContext( &aReqCtx ) )
252 OSL_ASSERT( sal_False );
253 return sal_False;
256 // marshall request into the sta thread
257 PostMessageA(
258 m_hwndStaRequestWnd,
259 MSG_BROWSEFORFOLDER,
261 reinterpret_cast< LPARAM >( &aReqCtx ) );
263 // waiting for the event to be signaled or
264 // window messages so that we don't block
265 // our parent window
267 sal_Bool bContinue = sal_True;
269 while ( bContinue )
271 DWORD dwResult = MsgWaitForMultipleObjects(
272 1, &aReqCtx.hEvent, sal_False, INFINITE, QS_ALLEVENTS );
274 switch ( dwResult )
276 // the request context event is signaled
277 case WAIT_OBJECT_0:
278 bContinue = sal_False;
279 break;
281 // a window message has arrived
282 case WAIT_OBJECT_0 + 1:
284 // dispatching all messages but we expect to
285 // receive only paint or timer messages that's
286 // why we don't need to call TranslateMessage or
287 // TranslateAccelerator, because keybord or
288 // mouse messages are for the FolderPicker which
289 // is in the foreground and should not arrive here
290 MSG msg;
291 while ( PeekMessageA( &msg, NULL, 0, 0, PM_REMOVE ) )
292 DispatchMessageA(&msg);
294 break;
296 // should not happen
297 default:
298 OSL_ASSERT( sal_False );
302 /*sal_Bool*/ bRet = aReqCtx.bRet;
303 DeinitializeRequestContext( &aReqCtx );
305 else
307 bRet = onBrowseForFolder();
310 return bRet;
317 void SAL_CALL CMtaFolderPicker::setDisplayDirectory( const OUString& aDirectory )
319 m_displayDir = aDirectory;
326 OUString SAL_CALL CMtaFolderPicker::getDisplayDirectory( )
328 return m_displayDir;
335 OUString SAL_CALL CMtaFolderPicker::getDirectory( )
337 return m_SelectedDir;
344 void SAL_CALL CMtaFolderPicker::setDescription( const OUString& aDescription )
346 m_Description = aDescription;
353 void SAL_CALL CMtaFolderPicker::setTitle( const OUString& aTitle )
355 m_dialogTitle = aTitle;
362 OUString SAL_CALL CMtaFolderPicker::getTitle( )
364 return m_dialogTitle;
368 // XCancellable
371 void SAL_CALL CMtaFolderPicker::cancel( )
373 if ( IsWindow( m_hwnd ) )
375 // simulate a mouse click to the
376 // cancel button
377 PostMessageA(
378 m_hwnd,
379 WM_COMMAND,
380 MAKEWPARAM( IDCANCEL, BN_CLICKED ),
381 (LPARAM)GetDlgItem( m_hwnd, IDCANCEL ) );
389 sal_Bool SAL_CALL CMtaFolderPicker::onBrowseForFolder( )
391 sal_Bool bRet;
392 LPITEMIDLIST lpiid;
394 // pre SHBrowseFroFolder
396 m_bi.pidlRoot = 0;
397 m_bi.pszDisplayName = reinterpret_cast<LPWSTR>(m_pathBuff.get());
399 if ( m_Description.getLength( ) )
400 m_bi.lpszTitle = reinterpret_cast<LPCWSTR>(m_Description.getStr( ));
402 lpiid = SHBrowseForFolderW( &m_bi );
403 bRet = ( NULL != lpiid );
405 // post SHBrowseForFolder
407 m_SelectedDir = getPathFromItemIdList( lpiid );
408 releaseItemIdList( lpiid );
410 return bRet;
417 void SAL_CALL CMtaFolderPicker::releaseItemIdList( LPITEMIDLIST lpItemIdList )
419 IMallocPtr pIMalloc;
420 SHGetMalloc(&pIMalloc);
421 if (pIMalloc.is())
423 pIMalloc->Free(lpItemIdList);
424 lpItemIdList = NULL;
432 LPITEMIDLIST SAL_CALL CMtaFolderPicker::getItemIdListFromPath( const OUString& aDirectory )
434 // parameter checking
435 if ( !aDirectory.getLength( ) )
436 return NULL;
438 LPITEMIDLIST lpItemIdList(NULL);
440 IShellFolderPtr pIShellFolder;
441 SHGetDesktopFolder(&pIShellFolder);
443 if (pIShellFolder.is())
445 pIShellFolder->ParseDisplayName(
446 NULL,
447 NULL,
448 reinterpret_cast<LPWSTR>(const_cast< sal_Unicode* >( aDirectory.getStr( ) )),
449 NULL,
450 &lpItemIdList,
451 NULL );
454 return lpItemIdList;
461 OUString SAL_CALL CMtaFolderPicker::getPathFromItemIdList( LPCITEMIDLIST lpItemIdList )
463 OUString path;
465 if ( lpItemIdList )
467 bool bRet = SHGetPathFromIDListW( lpItemIdList, reinterpret_cast<LPWSTR>(m_pathBuff.get()) );
468 if ( bRet )
469 path = m_pathBuff.get( );
472 return path;
479 void SAL_CALL CMtaFolderPicker::enableOk( sal_Bool bEnable )
481 OSL_ASSERT( IsWindow( m_hwnd ) );
483 SendMessageW(
484 m_hwnd,
485 BFFM_ENABLEOK,
486 static_cast< WPARAM >( 0 ),
487 static_cast< LPARAM >( bEnable ) );
494 void SAL_CALL CMtaFolderPicker::setSelection( const OUString& aDirectory )
496 OSL_ASSERT( IsWindow( m_hwnd ) );
498 SendMessageW(
499 m_hwnd,
500 BFFM_SETSELECTIONW,
501 static_cast< WPARAM >( sal_True ),
502 reinterpret_cast< LPARAM >( aDirectory.getStr( ) ) );
509 void SAL_CALL CMtaFolderPicker::setStatusText( const OUString& aStatusText )
511 OSL_ASSERT( IsWindow( m_hwnd ) );
513 SendMessageW(
514 m_hwnd,
515 BFFM_SETSTATUSTEXTW,
516 static_cast< WPARAM >( 0 ),
517 reinterpret_cast< LPARAM >( aStatusText.getStr( ) ) );
524 void SAL_CALL CMtaFolderPicker::onInitialized( )
526 LPITEMIDLIST lpiidDisplayDir = getItemIdListFromPath( m_displayDir );
528 if ( lpiidDisplayDir )
530 SendMessageA(
531 m_hwnd,
532 BFFM_SETSELECTION,
533 (WPARAM)sal_False,
534 (LPARAM) lpiidDisplayDir );
536 releaseItemIdList( lpiidDisplayDir );
544 sal_uInt32 CMtaFolderPicker::onValidateFailed()
546 // to be overwritten by subclasses
547 return 1;
554 int CALLBACK CMtaFolderPicker::FolderPickerCallback( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData )
556 CMtaFolderPicker* pImpl = reinterpret_cast< CMtaFolderPicker* >( lpData );
557 OSL_ASSERT( pImpl );
559 int nRC = 0;
561 switch( uMsg )
563 case BFFM_INITIALIZED:
564 pImpl->m_hwnd = hwnd;
565 pImpl->onInitialized( );
566 SetWindowTextW( hwnd, reinterpret_cast<LPCWSTR>(pImpl->m_dialogTitle.getStr()) );
567 break;
569 case BFFM_SELCHANGED:
570 pImpl->m_hwnd = hwnd;
571 pImpl->onSelChanged(
572 pImpl->getPathFromItemIdList(
573 reinterpret_cast< LPITEMIDLIST >( lParam ) ) );
574 break;
576 case BFFM_VALIDATEFAILEDW:
577 nRC = pImpl->onValidateFailed();
578 break;
580 default:
581 OSL_ASSERT( sal_False );
584 return nRC;
588 // the window proc
591 LRESULT CALLBACK CMtaFolderPicker::StaWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
593 LRESULT lResult = 0;
594 CMtaFolderPicker* pImpl = NULL;
597 we connect to the belonging class instance of this
598 window using SetProp, GetProp etc.
599 this may fail if somehow the class instance destroyed
600 before the window
603 switch( uMsg )
605 case WM_CREATE:
607 LPCREATESTRUCT lpcs =
608 reinterpret_cast< LPCREATESTRUCT >( lParam );
610 OSL_ASSERT( lpcs->lpCreateParams );
612 // connect the instance handle to the window
613 SetPropA( hWnd, CURRENT_INSTANCE, lpcs->lpCreateParams );
615 break;
617 case WM_NCDESTROY:
618 // RemoveProp returns the saved value on success
619 pImpl = reinterpret_cast< CMtaFolderPicker* >(
620 RemovePropA( hWnd, CURRENT_INSTANCE ) );
622 OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) );
623 break;
625 case MSG_BROWSEFORFOLDER:
627 RequestContext* aReqCtx = reinterpret_cast< RequestContext* >( lParam );
628 OSL_ASSERT( aReqCtx );
630 pImpl = reinterpret_cast< CMtaFolderPicker* >(
631 GetPropA( hWnd, CURRENT_INSTANCE ) );
633 OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) );
635 aReqCtx->bRet = pImpl->onBrowseForFolder( );
636 SetEvent( aReqCtx->hEvent );
638 break;
640 case MSG_SHUTDOWN:
641 pImpl = reinterpret_cast< CMtaFolderPicker* >(
642 GetPropA( hWnd, CURRENT_INSTANCE ) );
644 OSL_ASSERT( pImpl && !IsBadReadPtr( pImpl, sizeof( CMtaFolderPicker ) ) );
646 DestroyWindow( pImpl->m_hwndStaRequestWnd );
647 break;
649 case WM_DESTROY:
650 PostQuitMessage( 0 );
651 break;
653 default:
654 lResult = DefWindowProcA( hWnd, uMsg, wParam, lParam );
655 break;
658 return lResult;
665 sal_Bool SAL_CALL CMtaFolderPicker::createStaRequestWindow( )
667 bool bIsWnd = false;
669 if ( RegisterStaRequestWindowClass( ) )
671 m_hwndStaRequestWnd = CreateWindowA(
672 g_szWndClsName, NULL,
673 0, 0, 0, 0, 0,
674 NULL, NULL, m_hInstance,
675 (LPVOID)this // provide the instance of the class
678 bIsWnd = IsWindow( m_hwndStaRequestWnd );
680 // we do immediately unregister the window class
681 // if the creation of the window fails because we
682 // don't want to spoil the register class counter
683 if ( !bIsWnd )
684 UnregisterStaRequestWindowClass( );
686 OSL_ENSURE( bIsWnd, "sta request window creation failed" );
689 return bIsWnd;
696 unsigned int CMtaFolderPicker::run( )
698 OSL_ASSERT( m_hEvtThrdReady );
700 // setup an sta environment
701 HRESULT hr = CoInitialize( NULL );
703 // if we can't setup an sta environment
704 // we stop here and return
705 if ( FAILED( hr ) )
707 OSL_FAIL( "CoInitialize failed" );
708 return sal::static_int_cast< unsigned int >(-1);
711 unsigned int nRet;
713 if ( createStaRequestWindow( ) )
715 SetEvent( m_hEvtThrdReady );
717 // pumping messages
718 MSG msg;
719 while( GetMessageA( &msg, NULL, 0, 0 ) )
720 DispatchMessageA( &msg );
722 nRet = 0;
724 else
726 OSL_FAIL( "failed to create sta thread" );
727 nRet = sal::static_int_cast< unsigned int >(-1);
730 // shutdown sta environment
731 CoUninitialize( );
733 return nRet;
740 unsigned int WINAPI CMtaFolderPicker::StaThreadProc( LPVOID pParam )
742 osl_setThreadName("fpicker CMtaFolderPicker::run()");
744 CMtaFolderPicker* pInst =
745 reinterpret_cast<CMtaFolderPicker*>( pParam );
747 OSL_ASSERT( pInst );
749 HRESULT hr = OleInitialize( NULL );
751 unsigned int result = pInst->run( );
753 if ( SUCCEEDED( hr ) )
754 OleUninitialize();
756 return result;
763 ATOM SAL_CALL CMtaFolderPicker::RegisterStaRequestWindowClass( )
765 osl::MutexGuard aGuard( s_Mutex );
767 if ( 0 == s_ClassAtom )
769 WNDCLASSEXA wcex;
771 ZeroMemory( &wcex, sizeof( WNDCLASSEXA ) );
773 wcex.cbSize = sizeof(WNDCLASSEXA);
774 wcex.style = 0;
775 wcex.lpfnWndProc = static_cast< WNDPROC >( CMtaFolderPicker::StaWndProc );
776 wcex.cbClsExtra = 0;
777 wcex.cbWndExtra = 0;
778 wcex.hInstance = m_hInstance;
779 wcex.hIcon = NULL;
780 wcex.hCursor = NULL;
781 wcex.hbrBackground = NULL;
782 wcex.lpszMenuName = NULL;
783 wcex.lpszClassName = g_szWndClsName;
784 wcex.hIconSm = NULL;
786 s_ClassAtom = RegisterClassExA( &wcex );
787 OSL_ASSERT( s_ClassAtom );
790 // increment the register class counter
791 // so that we keep track of the number
792 // of class registrations
793 if ( 0 != s_ClassAtom )
794 s_StaRequestWndRegisterCount++;
796 return s_ClassAtom;
803 void SAL_CALL CMtaFolderPicker::UnregisterStaRequestWindowClass( )
805 osl::MutexGuard aGuard( s_Mutex );
807 OSL_ASSERT( 0 != s_ClassAtom );
809 // update the register class counter
810 // and unregister the window class if
811 // counter drops to zero
812 if ( 0 != s_ClassAtom )
814 s_StaRequestWndRegisterCount--;
815 OSL_ASSERT( s_StaRequestWndRegisterCount >= 0 );
818 if ( 0 == s_StaRequestWndRegisterCount )
820 UnregisterClass(
821 (LPCTSTR)(DWORD_PTR)MAKELONG( s_ClassAtom, 0 ), m_hInstance );
823 s_ClassAtom = 0;
827 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */