/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "VistaFilePickerImpl.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "WinImplHelper.hxx" #include #include static bool is_current_process_window(HWND hwnd) { DWORD pid; GetWindowThreadProcessId(hwnd, &pid); return (pid == GetCurrentProcessId()); } static HWND choose_parent_window() { HWND hwnd_parent = GetForegroundWindow(); if (!is_current_process_window(hwnd_parent)) hwnd_parent = GetDesktopWindow(); return hwnd_parent; } namespace { bool createFolderItem(OUString const & url, ComPtr & folder) { OUString path; if (osl::FileBase::getSystemPathFromFileURL(url, path) != osl::FileBase::E_None) { return false; } HRESULT res = SHCreateItemFromParsingName( o3tl::toW(path.getStr()), nullptr, IID_PPV_ARGS(&folder)); return SUCCEEDED(res); } } namespace fpicker{ namespace win32{ namespace vista{ // types, const etcpp. const ::sal_Int16 INVALID_CONTROL_ID = -1; const ::sal_Int16 INVALID_CONTROL_ACTION = -1; // Guids used for IFileDialog::SetClientGuid const GUID CLIENTID_FILEDIALOG_SIMPLE = {0xB8628FD3, 0xA3F5, 0x4845, 0x9B, 0x62, 0xD5, 0x1E, 0xDF, 0x97, 0xC4, 0x83}; const GUID CLIENTID_FILEDIALOG_OPTIONS = {0x93ED486F, 0x0D04, 0x4807, 0x8C, 0x44, 0xAC, 0x26, 0xCB, 0x6C, 0x5D, 0x36}; const GUID CLIENTID_FILESAVE_PASSWORD = {0xC12D4F4C, 0x4D41, 0x4D4F, 0x97, 0xEF, 0x87, 0xF9, 0x8D, 0xB6, 0x1E, 0xA6}; const GUID CLIENTID_FILESAVE_SELECTION = {0x5B2482B3, 0x0358, 0x4E09, 0xAA, 0x64, 0x2B, 0x76, 0xB2, 0xA0, 0xDD, 0xFE}; const GUID CLIENTID_FILESAVE_TEMPLATE = {0x9996D877, 0x20D5, 0x424B, 0x9C, 0x2E, 0xD3, 0xB6, 0x31, 0xEC, 0xF7, 0xCE}; const GUID CLIENTID_FILEOPEN_LINK_TEMPLATE = {0x32237796, 0x1509, 0x49D1, 0xBB, 0x7E, 0x63, 0xAD, 0x36, 0xAE, 0x86, 0x8C}; const GUID CLIENTID_FILEOPEN_LINK_ANCHOR = {0xBE3188CB, 0x399A, 0x45AE, 0x8F, 0x78, 0x75, 0x17, 0xAF, 0x26, 0x81, 0xEA}; const GUID CLIENTID_FILEOPEN_PLAY = {0x32CFB147, 0xF5AE, 0x4F90, 0xA1, 0xF1, 0x81, 0x20, 0x72, 0xBB, 0x2F, 0xC5}; const GUID CLIENTID_FILEOPEN_LINK = {0x39AC4BAE, 0x7D2D, 0x46BC, 0xBE, 0x2E, 0xF8, 0x8C, 0xB5, 0x65, 0x5E, 0x6A}; static OUString lcl_getURLFromShellItem (IShellItem* pItem) { LPWSTR pStr = nullptr; OUString sURL; HRESULT hr; hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pStr ); if (SUCCEEDED(hr)) { ::osl::FileBase::getFileURLFromSystemPath( o3tl::toU(pStr), sURL ); goto cleanup; } hr = pItem->GetDisplayName ( SIGDN_URL, &pStr ); if (SUCCEEDED(hr)) { sURL = o3tl::toU(pStr); goto cleanup; } hr = pItem->GetDisplayName ( SIGDN_PARENTRELATIVEPARSING, &pStr ); if (SUCCEEDED(hr)) { GUID known_folder_id; std::wstring aStr = pStr; CoTaskMemFree (pStr); if (0 == aStr.compare(0, 3, L"::{")) aStr = aStr.substr(2); hr = IIDFromString(aStr.c_str(), &known_folder_id); if (SUCCEEDED(hr)) { hr = SHGetKnownFolderPath(known_folder_id, 0, nullptr, &pStr); if (SUCCEEDED(hr)) { ::osl::FileBase::getFileURLFromSystemPath(o3tl::toU(pStr), sURL); goto cleanup; } } } // Default fallback hr = SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &pStr); if (SUCCEEDED(hr)) ::osl::FileBase::getFileURLFromSystemPath(o3tl::toU(pStr), sURL); else // shouldn't happen... goto bailout; cleanup: CoTaskMemFree (pStr); bailout: return sURL; } // Vista file picker shows the filter mask next to filter name in the list; so we need to remove the // mask from the filter name to avoid duplicating masks static OUString lcl_AdjustFilterName(const OUString& sName) { const sal_Int32 idx = sName.indexOf("(."); return (idx > 0) ? sName.copy(0, idx).trim() : sName; } // rvStrings holds the OUStrings, pointers to which data are stored in returned COMDLG_FILTERSPEC static ::std::vector lcl_buildFilterList(CFilterContainer& rContainer, std::vector& rvStrings) { ::std::vector< COMDLG_FILTERSPEC > lList ; CFilterContainer::FILTER_ENTRY_T aFilter; rContainer.beginEnumFilter( ); while( rContainer.getNextFilter(aFilter) ) { COMDLG_FILTERSPEC aSpec; rvStrings.push_back(lcl_AdjustFilterName(aFilter.first)); // to avoid dangling pointer aSpec.pszName = o3tl::toW(rvStrings.back().getStr()); aSpec.pszSpec = o3tl::toW(aFilter.second.getStr()); lList.push_back(aSpec); } return lList; } VistaFilePickerImpl::VistaFilePickerImpl() : m_iDialogOpen () , m_iDialogSave () , m_iFolderPicker() , m_hLastResult () , m_lFilters () , m_iEventHandler(new VistaFilePickerEventHandler(this)) , m_bInExecute (false) , m_bWasExecuted (false) , m_hParentWindow(choose_parent_window()) , m_sDirectory () , m_sFilename () , mnNbCallCoInitializeExForReinit(0) { } VistaFilePickerImpl::~VistaFilePickerImpl() { } void VistaFilePickerImpl::before() { // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); // TRICKY .-) // osl::Thread class initializes COm already in MTA mode because it's needed // by VCL and UNO so. There is no way to change that from outside... // but we need a STA environment... m_hLastResult = o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, mnNbCallCoInitializeExForReinit); } void VistaFilePickerImpl::doRequest(const RequestRef& rRequest) { try { switch(rRequest->getRequest()) { case E_ADD_PICKER_LISTENER : impl_sta_addFilePickerListener(rRequest); break; case E_REMOVE_PICKER_LISTENER : impl_sta_removeFilePickerListener(rRequest); break; case E_APPEND_FILTER : impl_sta_appendFilter(rRequest); break; case E_APPEND_FILTERGROUP : impl_sta_appendFilterGroup(rRequest); break; case E_SET_CURRENT_FILTER : impl_sta_setCurrentFilter(rRequest); break; case E_GET_CURRENT_FILTER : impl_sta_getCurrentFilter(rRequest); break; case E_CREATE_OPEN_DIALOG : impl_sta_CreateOpenDialog(rRequest); break; case E_CREATE_SAVE_DIALOG : impl_sta_CreateSaveDialog(rRequest); break; case E_CREATE_FOLDER_PICKER: impl_sta_CreateFolderPicker(rRequest); break; case E_SET_MULTISELECTION_MODE : impl_sta_SetMultiSelectionMode(rRequest); break; case E_SET_TITLE : impl_sta_SetTitle(rRequest); break; case E_SET_FILENAME: impl_sta_SetFileName(rRequest); break; case E_SET_DIRECTORY : impl_sta_SetDirectory(rRequest); break; case E_GET_DIRECTORY : impl_sta_GetDirectory(rRequest); break; case E_SET_DEFAULT_NAME : impl_sta_SetDefaultName(rRequest); break; case E_GET_SELECTED_FILES : impl_sta_getSelectedFiles(rRequest); break; case E_SHOW_DIALOG_MODAL : impl_sta_ShowDialogModal(rRequest); break; case E_SET_CONTROL_VALUE : impl_sta_SetControlValue(rRequest); break; case E_GET_CONTROL_VALUE : impl_sta_GetControlValue(rRequest); break; case E_SET_CONTROL_LABEL : impl_sta_SetControlLabel(rRequest); break; case E_GET_CONTROL_LABEL : impl_sta_GetControlLabel(rRequest); break; case E_ENABLE_CONTROL : impl_sta_EnableControl(rRequest); break; // no default: let the compiler detect changes on enum ERequest ! } } catch(...) {} } void VistaFilePickerImpl::after() { o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, mnNbCallCoInitializeExForReinit); } void VistaFilePickerImpl::impl_sta_addFilePickerListener(const RequestRef& rRequest) { // SYNCHRONIZED outside ! const css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener = rRequest->getArgumentOrDefault(PROP_PICKER_LISTENER, css::uno::Reference< css::ui::dialogs::XFilePickerListener >()); if ( ! xListener.is()) return; // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialogEvents iHandler = m_iEventHandler; aLock.clear(); // <- SYNCHRONIZED VistaFilePickerEventHandler* pHandlerImpl = static_cast(iHandler.get()); if (pHandlerImpl) pHandlerImpl->addFilePickerListener(xListener); } void VistaFilePickerImpl::impl_sta_removeFilePickerListener(const RequestRef& rRequest) { // SYNCHRONIZED outside ! const css::uno::Reference< css::ui::dialogs::XFilePickerListener > xListener = rRequest->getArgumentOrDefault(PROP_PICKER_LISTENER, css::uno::Reference< css::ui::dialogs::XFilePickerListener >()); if ( ! xListener.is()) return; // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialogEvents iHandler = m_iEventHandler; aLock.clear(); // <- SYNCHRONIZED VistaFilePickerEventHandler* pHandlerImpl = static_cast(iHandler.get()); if (pHandlerImpl) pHandlerImpl->removeFilePickerListener(xListener); } void VistaFilePickerImpl::impl_sta_appendFilter(const RequestRef& rRequest) { const OUString sTitle = rRequest->getArgumentOrDefault(PROP_FILTER_TITLE, OUString()); const OUString sFilter = rRequest->getArgumentOrDefault(PROP_FILTER_VALUE, OUString()); // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); m_lFilters.addFilter(sTitle, sFilter); } void VistaFilePickerImpl::impl_sta_appendFilterGroup(const RequestRef& rRequest) { const css::uno::Sequence< css::beans::StringPair > aFilterGroup = rRequest->getArgumentOrDefault(PROP_FILTER_GROUP, css::uno::Sequence< css::beans::StringPair >()); // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); if ( m_lFilters.numFilter() > 0 && aFilterGroup.getLength() > 0 ) m_lFilters.addFilter( STRING_SEPARATOR, "", true ); ::sal_Int32 c = aFilterGroup.getLength(); ::sal_Int32 i = 0; for (i=0; igetArgumentOrDefault(PROP_FILTER_TITLE, OUString()); // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); m_lFilters.setCurrentFilter(sTitle); } void VistaFilePickerImpl::impl_sta_getCurrentFilter(const RequestRef& rRequest) { TFileDialog iDialog = impl_getBaseDialogInterface(); UINT nIndex = UINT_MAX; HRESULT hResult = iDialog->GetFileTypeIndex(&nIndex); if ( ( FAILED(hResult) ) || ( nIndex == UINT_MAX ) // COM dialog sometimes return S_OK for empty filter lists .-( ) return; // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); OUString sTitle; ::sal_Int32 nRealIndex = nIndex-1; // COM dialog base on 1 ... filter container on 0 .-) if ( (nRealIndex >= 0 ) && (m_lFilters.getFilterNameByIndex(nRealIndex, sTitle)) ) rRequest->setArgument(PROP_FILTER_TITLE, sTitle); else if ( nRealIndex == -1 ) // Dialog not visible yet { sTitle = m_lFilters.getCurrentFilter(); rRequest->setArgument(PROP_FILTER_TITLE, sTitle); } // <- SYNCHRONIZED } void VistaFilePickerImpl::impl_sta_CreateDialog(const RequestRef& rRequest, PickerDialog eType, DWORD nOrFlags) { // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialog iDialog; switch (eType) { case PickerDialog::FileOpen: m_hLastResult = m_iDialogOpen.create(); if (FAILED(m_hLastResult)) return; m_iDialogOpen.query(&iDialog); break; case PickerDialog::FileSave: m_hLastResult = m_iDialogSave.create(); if (FAILED(m_hLastResult)) return; m_iDialogSave.query(&iDialog); break; case PickerDialog::Folder: m_hLastResult = m_iFolderPicker.create(); if (FAILED(m_hLastResult)) return; m_iFolderPicker.query(&iDialog); break; } TFileDialogEvents iHandler = m_iEventHandler; aLock.clear(); // <- SYNCHRONIZED DWORD nFlags = 0; iDialog->GetOptions ( &nFlags ); nFlags &= ~FOS_FORCESHOWHIDDEN; nFlags |= FOS_PATHMUSTEXIST; nFlags |= FOS_DONTADDTORECENT; nFlags |= nOrFlags; iDialog->SetOptions ( nFlags ); css::uno::Reference xWindow = rRequest->getArgumentOrDefault(PROP_PARENT_WINDOW, css::uno::Reference()); if(xWindow.is()) { css::uno::Reference xSysDepWin(xWindow,css::uno::UNO_QUERY); if(xSysDepWin.is()) { css::uno::Sequence aProcessIdent(16); rtl_getGlobalProcessId(reinterpret_cast(aProcessIdent.getArray())); css::uno::Any aAny = xSysDepWin->getWindowHandle(aProcessIdent,css::lang::SystemDependent::SYSTEM_WIN32); sal_Int64 tmp = 0; aAny >>= tmp; if(tmp != 0) m_hParentWindow = reinterpret_cast(tmp); } } ::sal_Int32 nFeatures = rRequest->getArgumentOrDefault(PROP_FEATURES, ::sal_Int32(0)); ::sal_Int32 nTemplate = rRequest->getArgumentOrDefault(PROP_TEMPLATE_DESCR, ::sal_Int32(0)); impl_sta_enableFeatures(nFeatures, nTemplate); VistaFilePickerEventHandler* pHandlerImpl = static_cast(iHandler.get()); if (pHandlerImpl) pHandlerImpl->startListening(iDialog); } void VistaFilePickerImpl::impl_sta_CreateOpenDialog(const RequestRef& rRequest) { DWORD nFlags = 0; nFlags |= FOS_FILEMUSTEXIST; nFlags |= FOS_OVERWRITEPROMPT; impl_sta_CreateDialog(rRequest, PickerDialog::FileOpen, nFlags); } void VistaFilePickerImpl::impl_sta_CreateSaveDialog(const RequestRef& rRequest) { DWORD nFlags = 0; nFlags |= FOS_FILEMUSTEXIST; nFlags |= FOS_OVERWRITEPROMPT; impl_sta_CreateDialog(rRequest, PickerDialog::FileSave, nFlags); } void VistaFilePickerImpl::impl_sta_CreateFolderPicker(const RequestRef& rRequest) { DWORD nFlags = 0; nFlags |= FOS_PICKFOLDERS; impl_sta_CreateDialog(rRequest, PickerDialog::Folder, nFlags); } const ::sal_Int32 GROUP_VERSION = 1; const ::sal_Int32 GROUP_TEMPLATE = 2; const ::sal_Int32 GROUP_IMAGETEMPLATE = 3; const ::sal_Int32 GROUP_CHECKBOXES = 4; const ::sal_Int32 GROUP_IMAGEANCHOR = 5; static void setLabelToControl(TFileDialogCustomize iCustom, sal_uInt16 nControlId) { OUString aLabel = CResourceProvider::getResString(nControlId); aLabel = SOfficeToWindowsLabel(aLabel); iCustom->SetControlLabel(nControlId, o3tl::toW(aLabel.getStr()) ); } void VistaFilePickerImpl::impl_sta_enableFeatures(::sal_Int32 nFeatures, ::sal_Int32 nTemplate) { GUID aGUID = {}; switch (nTemplate) { case css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE : case css::ui::dialogs::TemplateDescription::FILEOPEN_PREVIEW : case css::ui::dialogs::TemplateDescription::FILESAVE_SIMPLE : aGUID = CLIENTID_FILEDIALOG_SIMPLE; break; case css::ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION : case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS : aGUID = CLIENTID_FILEDIALOG_OPTIONS; break; case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD : aGUID = CLIENTID_FILESAVE_PASSWORD; break; case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION : case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION : aGUID = CLIENTID_FILESAVE_SELECTION; break; case css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE : aGUID = CLIENTID_FILESAVE_TEMPLATE; break; case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE : aGUID = CLIENTID_FILEOPEN_LINK_TEMPLATE; break; case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR : aGUID = CLIENTID_FILEOPEN_LINK_ANCHOR; break; case css::ui::dialogs::TemplateDescription::FILEOPEN_PLAY : case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PLAY : aGUID = CLIENTID_FILEOPEN_PLAY; break; case css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW : aGUID = CLIENTID_FILEOPEN_LINK; break; } TFileDialog iDialog = impl_getBaseDialogInterface(); iDialog->SetClientGuid ( aGUID ); TFileDialogCustomize iCustom = impl_getCustomizeInterface(); if ((nFeatures & FEATURE_VERSION) == FEATURE_VERSION) { iCustom->StartVisualGroup (GROUP_VERSION, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_VERSION).replaceFirst("~","").getStr())); iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION); iCustom->EndVisualGroup (); iCustom->MakeProminent (GROUP_VERSION); } if ((nFeatures & FEATURE_TEMPLATE) == FEATURE_TEMPLATE) { iCustom->StartVisualGroup (GROUP_TEMPLATE, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_TEMPLATES).replaceFirst("~","").getStr())); iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE); iCustom->EndVisualGroup (); iCustom->MakeProminent (GROUP_TEMPLATE); } if ((nFeatures & FEATURE_IMAGETEMPLATE) == FEATURE_IMAGETEMPLATE) { iCustom->StartVisualGroup (GROUP_IMAGETEMPLATE, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_IMAGE_TEMPLATE).replaceFirst("~","").getStr())); iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE); iCustom->EndVisualGroup (); iCustom->MakeProminent (GROUP_IMAGETEMPLATE); } if ((nFeatures & FEATURE_IMAGEANCHOR) == FEATURE_IMAGEANCHOR) { iCustom->StartVisualGroup (GROUP_IMAGEANCHOR, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_IMAGE_ANCHOR).replaceFirst("~","").getStr())); iCustom->AddComboBox (css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR); iCustom->EndVisualGroup (); iCustom->MakeProminent (GROUP_IMAGEANCHOR); } iCustom->StartVisualGroup (GROUP_CHECKBOXES, L""); sal_uInt16 nControlId(0); if ((nFeatures & FEATURE_AUTOEXTENSION) == FEATURE_AUTOEXTENSION) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION; iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_AUTO_EXTENSION).replaceFirst("~","").getStr()), true); setLabelToControl(iCustom, nControlId); } if ((nFeatures & FEATURE_PASSWORD) == FEATURE_PASSWORD) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD; iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PASSWORD).replaceFirst("~","").getStr()), false); setLabelToControl(iCustom, nControlId); } if ((nFeatures & FEATURE_GPGPASSWORD) == FEATURE_GPGPASSWORD) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION; iCustom->AddCheckButton (nControlId, L"GpgPassword", false); setLabelToControl(iCustom, nControlId); } if ((nFeatures & FEATURE_READONLY) == FEATURE_READONLY) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY; iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_READONLY).replaceFirst("~","").getStr()), false); setLabelToControl(iCustom, nControlId); } if ((nFeatures & FEATURE_FILTEROPTIONS) == FEATURE_FILTEROPTIONS) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS; iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_FILTER_OPTIONS).replaceFirst("~","").getStr()), false); setLabelToControl(iCustom, nControlId); } if ((nFeatures & FEATURE_LINK) == FEATURE_LINK) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK; iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_INSERT_AS_LINK).replaceFirst("~","").getStr()), false); setLabelToControl(iCustom, nControlId); } if ((nFeatures & FEATURE_SELECTION) == FEATURE_SELECTION) { nControlId = css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION; iCustom->AddCheckButton (nControlId, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_SELECTION).replaceFirst("~","").getStr()), false); setLabelToControl(iCustom, nControlId); } /* can be ignored ... new COM dialog supports preview native now ! if ((nFeatures & FEATURE_PREVIEW) == FEATURE_PREVIEW) iCustom->AddCheckButton (css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, L"Preview", false); */ iCustom->EndVisualGroup(); if ((nFeatures & FEATURE_PLAY) == FEATURE_PLAY) iCustom->AddPushButton (css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, o3tl::toW(FpsResId(STR_SVT_FILEPICKER_PLAY).replaceFirst("~","").getStr())); } void VistaFilePickerImpl::impl_sta_SetMultiSelectionMode(const RequestRef& rRequest) { const bool bMultiSelection = rRequest->getArgumentOrDefault(PROP_MULTISELECTION_MODE, true); // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialog iDialog = impl_getBaseDialogInterface(); aLock.clear(); // <- SYNCHRONIZED DWORD nFlags = 0; m_hLastResult = iDialog->GetOptions ( &nFlags ); if (bMultiSelection) nFlags |= FOS_ALLOWMULTISELECT; else nFlags &= ~FOS_ALLOWMULTISELECT; iDialog->SetOptions ( nFlags ); } void VistaFilePickerImpl::impl_sta_SetTitle(const RequestRef& rRequest) { OUString sTitle = rRequest->getArgumentOrDefault(PROP_TITLE, OUString()); // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialog iDialog = impl_getBaseDialogInterface(); aLock.clear(); // <- SYNCHRONIZED iDialog->SetTitle(o3tl::toW(sTitle.getStr())); } void VistaFilePickerImpl::impl_sta_SetFileName(const RequestRef& rRequest) { OUString sFileName = rRequest->getArgumentOrDefault(PROP_FILENAME, OUString()); // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialog iDialog = impl_getBaseDialogInterface(); aLock.clear(); // <- SYNCHRONIZED iDialog->SetFileName(o3tl::toW(sFileName.getStr())); } void VistaFilePickerImpl::impl_sta_SetDirectory(const RequestRef& rRequest) { OUString sDirectory = rRequest->getArgumentOrDefault(PROP_DIRECTORY, OUString()); if( !m_bInExecute) { // Vista stores last used folders for file dialogs // so we don't want the application to change the folder // in most cases. // Store the requested folder in the meantime and decide later // what to do m_sDirectory = sDirectory; } // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileDialog iDialog = impl_getBaseDialogInterface(); aLock.clear(); // <- SYNCHRONIZED ComPtr< IShellItem > pFolder; if ( !createFolderItem(sDirectory, pFolder) ) return; iDialog->SetFolder(pFolder); } OUString VistaFilePickerImpl::GetDirectory() { TFileDialog iDialog = impl_getBaseDialogInterface(); ComPtr< IShellItem > pFolder; HRESULT hResult = iDialog->GetFolder( &pFolder ); if ( FAILED(hResult) ) return OUString(); return lcl_getURLFromShellItem(pFolder); } void VistaFilePickerImpl::impl_sta_GetDirectory(const RequestRef& rRequest) { const OUString sFolder = m_sDirectory.isEmpty() ? GetDirectory() : m_sDirectory; if (!sFolder.isEmpty()) rRequest->setArgument(PROP_DIRECTORY, sFolder); } void VistaFilePickerImpl::impl_sta_SetDefaultName(const RequestRef& rRequest) { OUString sFilename = rRequest->getArgumentOrDefault(PROP_FILENAME, OUString()); TFileDialog iDialog = impl_getBaseDialogInterface(); TFileDialogCustomize iCustom = impl_getCustomizeInterface(); if ( ! iCustom.is()) return; // if we have the autoextension check box set, remove (or change ???) the extension of the filename // so that the autoextension mechanism can do its job BOOL bValue = FALSE; HRESULT hResult = iCustom->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, &bValue); if ( FAILED(hResult) ) return; if ( bValue ) { sal_Int32 nSepPos = sFilename.lastIndexOf( '.' ); if ( -1 != nSepPos ) sFilename = sFilename.copy(0, nSepPos); } iDialog->SetFileName (o3tl::toW(sFilename.getStr())); m_sFilename = sFilename; } void VistaFilePickerImpl::impl_sta_setFiltersOnDialog() { // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); std::vector vStrings; // to hold the adjusted filter names, pointers to which will be // stored in lFilters ::std::vector< COMDLG_FILTERSPEC > lFilters = lcl_buildFilterList(m_lFilters, vStrings); OUString sCurrentFilter = m_lFilters.getCurrentFilter(); sal_Int32 nCurrentFilter = m_lFilters.getFilterPos(sCurrentFilter); TFileDialog iDialog = impl_getBaseDialogInterface(); TFileDialogCustomize iCustomize = impl_getCustomizeInterface(); aLock.clear(); // <- SYNCHRONIZED if (lFilters.empty()) return; COMDLG_FILTERSPEC *pFilt = lFilters.data(); iDialog->SetFileTypes(lFilters.size(), pFilt/*&lFilters[0]*/); iDialog->SetFileTypeIndex(nCurrentFilter + 1); BOOL bValue = FALSE; HRESULT hResult = iCustomize->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, &bValue); if ( FAILED(hResult) ) return; if ( bValue ) { PCWSTR lpFilterExt = lFilters[0].pszSpec; lpFilterExt = wcsrchr( lpFilterExt, '.' ); if ( lpFilterExt ) lpFilterExt++; iDialog->SetDefaultExtension( lpFilterExt ); } } void VistaFilePickerImpl::impl_sta_getSelectedFiles(const RequestRef& rRequest) { // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); TFileOpenDialog iOpen = m_iDialogOpen; TFileSaveDialog iSave = m_iDialogSave; TFolderPickerDialog iPick = m_iFolderPicker; bool bInExecute = m_bInExecute; aLock.clear(); // <- SYNCHRONIZED // ask dialog for results // Note : we must differ between single/multi selection ! // Note further: we must react different if dialog is in execute or not .-( ComPtr< IShellItem > iItem; ComPtr< IShellItemArray > iItems; HRESULT hResult = E_FAIL; if (iOpen.is()) { if (bInExecute) hResult = iOpen->GetSelectedItems(&iItems); else { hResult = iOpen->GetResults(&iItems); if (FAILED(hResult)) hResult = iOpen->GetResult(&iItem); } } else if (iSave.is()) { if (bInExecute) hResult = iSave->GetCurrentSelection(&iItem); else hResult = iSave->GetResult(&iItem); } else if (iPick.is()) { if (bInExecute) hResult = iPick->GetCurrentSelection(&iItem); else { hResult = iPick->GetResult(&iItem); } } if (FAILED(hResult)) return; // convert and pack results std::vector< OUString > lFiles; if (iItem.is()) { const OUString sURL = lcl_getURLFromShellItem(iItem); if (sURL.getLength() > 0) lFiles.push_back(sURL); } if (iItems.is()) { DWORD nCount; hResult = iItems->GetCount(&nCount); if ( SUCCEEDED(hResult) ) { for (DWORD i=0; iGetItemAt(i, &iItem); if ( SUCCEEDED(hResult) ) { const OUString sURL = lcl_getURLFromShellItem(iItem); if (sURL.getLength() > 0) lFiles.push_back(sURL); } } } } rRequest->setArgument(PROP_SELECTED_FILES, comphelper::containerToSequence(lFiles)); } void VistaFilePickerImpl::impl_sta_ShowDialogModal(const RequestRef& rRequest) { impl_sta_setFiltersOnDialog(); // SYNCHRONIZED-> ::osl::ResettableMutexGuard aLock(m_aMutex); TFileDialog iDialog = impl_getBaseDialogInterface(); TFileOpenDialog iOpen = m_iDialogOpen; TFileSaveDialog iSave = m_iDialogSave; TFolderPickerDialog iPick = m_iFolderPicker; // it's important to know if we are showing the dialog. // Some dialog interface methods can't be called then or some // tasks must be done differently .-) (e.g. see impl_sta_getSelectedFiles()) m_bInExecute = true; m_bWasExecuted = true; aLock.clear(); // <- SYNCHRONIZED // we set the directory only if we have a save dialog and a filename // for the other cases, the file dialog remembers its last location // according to its client guid. if( m_sDirectory.getLength()) { ComPtr< IShellItem > pFolder; if ( createFolderItem(m_sDirectory, pFolder) ) { if (m_sFilename.getLength()) { OUString aFileURL(m_sDirectory); sal_Int32 nIndex = aFileURL.lastIndexOf('/'); if (nIndex != aFileURL.getLength()-1) aFileURL += "/"; aFileURL += m_sFilename; TFileDialogCustomize iCustom = impl_getCustomizeInterface(); BOOL bValue = FALSE; HRESULT hResult = iCustom->GetCheckButtonState( css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, &bValue); if ( bValue ) { UINT nFileType; hResult = iDialog->GetFileTypeIndex(&nFileType); if ( SUCCEEDED(hResult) && nFileType > 0 ) { // COM dialog base on 1 ... filter container on 0 .-) ::size_t nRealIndex = nFileType-1; OUString sFilter; if (m_lFilters.getFilterByIndex(nRealIndex, sFilter)) { const sal_Int32 idx = sFilter.indexOf('.'); if (idx >= 0) aFileURL += sFilter.copy(idx); } } } // Check existence of file. Set folder only for this special case OUString aSystemPath; osl_getSystemPathFromFileURL( aFileURL.pData, &aSystemPath.pData ); WIN32_FIND_DATAW aFindFileData; HANDLE hFind = FindFirstFileW( o3tl::toW(aSystemPath.getStr()), &aFindFileData ); if (hFind != INVALID_HANDLE_VALUE) iDialog->SetFolder(pFolder); else hResult = iDialog->AddPlace(pFolder, FDAP_TOP); FindClose( hFind ); } else iDialog->AddPlace(pFolder, FDAP_TOP); } } HRESULT hResult = E_FAIL; try { // show dialog and wait for user decision if (iOpen.is()) hResult = iOpen->Show( m_hParentWindow ); // parent window needed else if (iSave.is()) hResult = iSave->Show( m_hParentWindow ); // parent window needed else if (iPick.is()) hResult = iPick->Show( m_hParentWindow ); // parent window needed } catch(...) {} // SYNCHRONIZED-> aLock.reset(); m_bInExecute = false; aLock.clear(); // <- SYNCHRONIZED if ( FAILED(hResult) ) return; impl_sta_getSelectedFiles(rRequest); rRequest->setArgument(PROP_DIALOG_SHOW_RESULT, true); } TFileDialog VistaFilePickerImpl::impl_getBaseDialogInterface() { TFileDialog iDialog; // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); if (m_iDialogOpen.is()) m_iDialogOpen.query(&iDialog); if (m_iDialogSave.is()) m_iDialogSave.query(&iDialog); if (m_iFolderPicker.is()) m_iFolderPicker.query(&iDialog); return iDialog; } TFileDialogCustomize VistaFilePickerImpl::impl_getCustomizeInterface() { TFileDialogCustomize iCustom; // SYNCHRONIZED-> osl::MutexGuard aLock(m_aMutex); if (m_iDialogOpen.is()) m_iDialogOpen.query(&iCustom); else if (m_iDialogSave.is()) m_iDialogSave.query(&iCustom); else if (m_iFolderPicker.is()) m_iFolderPicker.query(&iCustom); return iCustom; } static void lcl_removeControlItemsWorkaround(const TFileDialogCustomize& iCustom , ::sal_Int16 nControlId) { (void)iCustom->SetSelectedControlItem(nControlId, 1000); // Don't care if this fails (useless?) DWORD i = 0; HRESULT hResult = S_OK; while ( SUCCEEDED(hResult) ) hResult = iCustom->RemoveControlItem(nControlId, i++); } void VistaFilePickerImpl::impl_sta_SetControlValue(const RequestRef& rRequest) { ::sal_Int16 nId = rRequest->getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID ); ::sal_Int16 nAction = rRequest->getArgumentOrDefault(PROP_CONTROL_ACTION, INVALID_CONTROL_ACTION); css::uno::Any aValue = rRequest->getValue(PROP_CONTROL_VALUE); // don't check for right values here ... // most parameters are optional ! TFileDialogCustomize iCustom = impl_getCustomizeInterface(); if ( ! iCustom.is()) return; switch (nId) { case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK : //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now ! case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION : { bool bValue = false; aValue >>= bValue; iCustom->SetCheckButtonState(nId, bValue); } break; case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION : case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE : case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE : case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR : { HRESULT hResult; switch (nAction) { case css::ui::dialogs::ControlActions::DELETE_ITEMS : { hResult = iCustom->RemoveAllControlItems(nId); if ( FAILED(hResult) ) lcl_removeControlItemsWorkaround(iCustom, nId); } break; case css::ui::dialogs::ControlActions::ADD_ITEMS : { aValue >>= m_lItems; for (::sal_Int32 i=0; iAddControlItem(nId, i, o3tl::toW(sItem.getStr())); } } break; case css::ui::dialogs::ControlActions::SET_SELECT_ITEM : { ::sal_Int32 nItem = 0; aValue >>= nItem; hResult = iCustom->SetSelectedControlItem(nId, nItem); } break; } } break; case css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY : { } break; } } void VistaFilePickerImpl::impl_sta_GetControlValue(const RequestRef& rRequest) { ::sal_Int16 nId = rRequest->getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID ); // don't check for right values here ... // most parameters are optional ! TFileDialogCustomize iCustom = impl_getCustomizeInterface(); if ( ! iCustom.is()) return; css::uno::Any aValue; if( m_bWasExecuted ) switch (nId) { case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PASSWORD : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_READONLY : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS : case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK : //case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : // can be ignored ... preview is supported native now ! case css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_SELECTION : { BOOL bValue = FALSE; HRESULT hResult = iCustom->GetCheckButtonState(nId, &bValue); if ( SUCCEEDED(hResult) ) aValue <<= bool(bValue); } break; case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_VERSION: case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_TEMPLATE: case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE: case css::ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR: { DWORD bValue = 0; HRESULT hResult = iCustom->GetSelectedControlItem(nId, &bValue); if ( SUCCEEDED(hResult) ) { const OUString& sItem = m_lItems[bValue]; aValue <<= OUString(sItem.getStr()); } } break; } if (aValue.hasValue()) rRequest->setArgument(PROP_CONTROL_VALUE, aValue); } void VistaFilePickerImpl::impl_sta_SetControlLabel(const RequestRef& rRequest) { ::sal_Int16 nId = rRequest->getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID ); OUString sLabel = rRequest->getArgumentOrDefault(PROP_CONTROL_LABEL, OUString() ); // don't check for right values here ... // most parameters are optional ! TFileDialogCustomize iCustom = impl_getCustomizeInterface(); if ( ! iCustom.is()) return; iCustom->SetControlLabel (nId, o3tl::toW(sLabel.getStr())); } void VistaFilePickerImpl::impl_sta_GetControlLabel(const RequestRef& /*rRequest*/) { } void VistaFilePickerImpl::impl_sta_EnableControl(const RequestRef& rRequest) { ::sal_Int16 nId = rRequest->getArgumentOrDefault(PROP_CONTROL_ID , INVALID_CONTROL_ID ); bool bEnabled = rRequest->getArgumentOrDefault(PROP_CONTROL_ENABLE, true); // don't check for right values here ... // most parameters are optional ! TFileDialogCustomize iCustom = impl_getCustomizeInterface(); if ( ! iCustom.is()) return; CDCONTROLSTATEF eState = CDCS_VISIBLE; if (bEnabled) eState |= CDCS_ENABLED; else eState |= CDCS_INACTIVE; iCustom->SetControlState(nId, eState); } void VistaFilePickerImpl::impl_SetDefaultExtension( const OUString& currentFilter ) { TFileDialog iDialog = impl_getBaseDialogInterface(); if (currentFilter.getLength()) { OUString FilterExt; m_lFilters.getFilterByName(currentFilter, FilterExt); sal_Int32 posOfPoint = FilterExt.indexOf(L'.'); const sal_Unicode* pFirstExtStart = FilterExt.getStr() + posOfPoint + 1; sal_Int32 posOfSemiColon = FilterExt.indexOf(L';') - 1; if (posOfSemiColon < 0) posOfSemiColon = FilterExt.getLength() - 1; FilterExt = OUString(pFirstExtStart, posOfSemiColon - posOfPoint); iDialog->SetDefaultExtension ( o3tl::toW(FilterExt.getStr()) ); } } void VistaFilePickerImpl::onAutoExtensionChanged (bool bChecked) { // SYNCHRONIZED-> osl::ClearableMutexGuard aLock(m_aMutex); const OUString sFilter = m_lFilters.getCurrentFilter (); OUString sExt ; if (!m_lFilters.getFilterByName(sFilter, sExt)) return; TFileDialog iDialog = impl_getBaseDialogInterface(); aLock.clear(); // <- SYNCHRONIZED PCWSTR pExt = nullptr; if ( bChecked ) { pExt = o3tl::toW(sExt.getStr()); pExt = wcsrchr( pExt, '.' ); if ( pExt ) pExt++; } iDialog->SetDefaultExtension( pExt ); } bool VistaFilePickerImpl::onFileTypeChanged( UINT /*nTypeIndex*/ ) { return true; } void VistaFilePickerImpl::onDirectoryChanged() { m_sDirectory = GetDirectory(); } } // namespace vista } // namespace win32 } // namespace fpicker /* vim:set shiftwidth=4 softtabstop=4 expandtab: */