/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "filtergrouping.hxx" #include "filedlgimpl.hxx" #include #include #include #include #include #include #ifdef UNX #include #include #endif using namespace ::com::sun::star; using namespace ::com::sun::star::container; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::ui::dialogs; using namespace ::com::sun::star::ui::dialogs::TemplateDescription; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::beans; using namespace ::cppu; constexpr OUString IODLG_CONFIGNAME = u"FilePicker_Save"_ustr; constexpr OUString IMPGRF_CONFIGNAME = u"FilePicker_Graph"_ustr; constexpr OUString USERITEM_NAME = u"UserItem"_ustr; namespace sfx2 { namespace { bool lclSupportsOOXMLEncryption(std::u16string_view aFilterName) { return aFilterName == u"Calc MS Excel 2007 XML" || aFilterName == u"MS Word 2007 XML" || aFilterName == u"Impress MS PowerPoint 2007 XML" || aFilterName == u"Impress MS PowerPoint 2007 XML AutoPlay" || aFilterName == u"Calc Office Open XML" || aFilterName == u"Impress Office Open XML" || aFilterName == u"Impress Office Open XML AutoPlay" || aFilterName == u"Office Open XML Text"; } } static std::optional GetLastFilterConfigId( FileDialogHelper::Context _eContext ) { static constexpr OUStringLiteral aSD_EXPORT_IDENTIFIER(u"SdExportLastFilter"); static constexpr OUStringLiteral aSI_EXPORT_IDENTIFIER(u"SiExportLastFilter"); static constexpr OUStringLiteral aSW_EXPORT_IDENTIFIER(u"SwExportLastFilter"); switch( _eContext ) { case FileDialogHelper::DrawExport: return aSD_EXPORT_IDENTIFIER; case FileDialogHelper::ImpressExport: return aSI_EXPORT_IDENTIFIER; case FileDialogHelper::WriterExport: return aSW_EXPORT_IDENTIFIER; default: break; } return {}; } static OUString EncodeSpaces_Impl( const OUString& rSource ); static OUString DecodeSpaces_Impl( const OUString& rSource ); // FileDialogHelper_Impl // XFilePickerListener Methods void SAL_CALL FileDialogHelper_Impl::fileSelectionChanged( const FilePickerEvent& ) { SolarMutexGuard aGuard; mpAntiImpl->FileSelectionChanged(); } void SAL_CALL FileDialogHelper_Impl::directoryChanged( const FilePickerEvent& ) { SolarMutexGuard aGuard; mpAntiImpl->DirectoryChanged(); } OUString SAL_CALL FileDialogHelper_Impl::helpRequested( const FilePickerEvent& aEvent ) { SolarMutexGuard aGuard; return sfx2::FileDialogHelper::HelpRequested( aEvent ); } void SAL_CALL FileDialogHelper_Impl::controlStateChanged( const FilePickerEvent& aEvent ) { SolarMutexGuard aGuard; mpAntiImpl->ControlStateChanged( aEvent ); } void SAL_CALL FileDialogHelper_Impl::dialogSizeChanged() { SolarMutexGuard aGuard; mpAntiImpl->DialogSizeChanged(); } // XDialogClosedListener Methods void SAL_CALL FileDialogHelper_Impl::dialogClosed( const DialogClosedEvent& _rEvent ) { SolarMutexGuard aGuard; mpAntiImpl->DialogClosed( _rEvent ); postExecute( _rEvent.DialogResult ); } // handle XFilePickerListener events void FileDialogHelper_Impl::handleFileSelectionChanged() { if ( mbHasVersions ) updateVersions(); if ( mbShowPreview ) maPreviewIdle.Start(); } void FileDialogHelper_Impl::handleDirectoryChanged() { if ( mbShowPreview ) TimeOutHdl_Impl( nullptr ); } OUString FileDialogHelper_Impl::handleHelpRequested( const FilePickerEvent& aEvent ) { //!!! todo: cache the help strings (here or TRA) OUString sHelpId; // mapping from element id -> help id switch ( aEvent.ElementId ) { case ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION : sHelpId = HID_FILESAVE_AUTOEXTENSION; break; case ExtendedFilePickerElementIds::CHECKBOX_PASSWORD : sHelpId = HID_FILESAVE_SAVEWITHPASSWORD; break; case ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS : sHelpId = HID_FILESAVE_CUSTOMIZEFILTER; break; case ExtendedFilePickerElementIds::CHECKBOX_READONLY : sHelpId = HID_FILEOPEN_READONLY; break; case ExtendedFilePickerElementIds::CHECKBOX_LINK : sHelpId = HID_FILEDLG_LINK_CB; break; case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW : sHelpId = HID_FILEDLG_PREVIEW_CB; break; case ExtendedFilePickerElementIds::PUSHBUTTON_PLAY : sHelpId = HID_FILESAVE_DOPLAY; break; case ExtendedFilePickerElementIds::LISTBOX_VERSION_LABEL : case ExtendedFilePickerElementIds::LISTBOX_VERSION : sHelpId = HID_FILEOPEN_VERSION; break; case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE_LABEL : case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE : sHelpId = HID_FILESAVE_TEMPLATE; break; case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE_LABEL : case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE : sHelpId = HID_FILEOPEN_IMAGE_TEMPLATE; break; case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR_LABEL : case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR : sHelpId = HID_FILEOPEN_IMAGE_ANCHOR; break; case ExtendedFilePickerElementIds::CHECKBOX_SELECTION : sHelpId = HID_FILESAVE_SELECTION; break; default: SAL_WARN( "sfx.dialog", "invalid element id" ); } OUString aHelpText; Help* pHelp = Application::GetHelp(); if ( pHelp ) aHelpText = pHelp->GetHelpText(sHelpId, static_cast(nullptr)); return aHelpText; } void FileDialogHelper_Impl::handleControlStateChanged( const FilePickerEvent& aEvent ) { switch ( aEvent.ElementId ) { case CommonFilePickerElementIds::LISTBOX_FILTER: updateFilterOptionsBox(); enablePasswordBox( false ); updateSelectionBox(); // only use it for export and with our own dialog if ( mbExport && !mbSystemPicker ) updateExportButton(); break; case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW: updatePreviewState(true); break; } } void FileDialogHelper_Impl::handleDialogSizeChanged() { if ( mbShowPreview ) TimeOutHdl_Impl( nullptr ); } // XEventListener Methods void SAL_CALL FileDialogHelper_Impl::disposing( const EventObject& ) { SolarMutexGuard aGuard; dispose(); } void FileDialogHelper_Impl::dispose() { if ( mxFileDlg.is() ) { // remove the event listener mxFileDlg->removeFilePickerListener( this ); ::comphelper::disposeComponent( mxFileDlg ); mxFileDlg.clear(); } } OUString FileDialogHelper_Impl::getCurrentFilterUIName() const { OUString aFilterName; if( mxFileDlg.is() ) { aFilterName = mxFileDlg->getCurrentFilter(); if ( !aFilterName.isEmpty() && isShowFilterExtensionEnabled() ) aFilterName = getFilterName( aFilterName ); } return aFilterName; } void FileDialogHelper_Impl::LoadLastUsedFilter( const OUString& _rContextIdentifier ) { SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME ); if( aDlgOpt.Exists() ) { OUString aLastFilter; if( aDlgOpt.GetUserItem( _rContextIdentifier ) >>= aLastFilter ) setFilter( aLastFilter ); } } void FileDialogHelper_Impl::SaveLastUsedFilter() { std::optional pConfigId = GetLastFilterConfigId( meContext ); if( pConfigId ) SvtViewOptions( EViewType::Dialog, IODLG_CONFIGNAME ).SetUserItem( *pConfigId, Any( getFilterWithExtension( getFilter() ) ) ); } std::shared_ptr FileDialogHelper_Impl::getCurrentSfxFilter() { OUString aFilterName = getCurrentFilterUIName(); if ( mpMatcher && !aFilterName.isEmpty() ) return mpMatcher->GetFilter4UIName( aFilterName, m_nMustFlags, m_nDontFlags ); return nullptr; } bool FileDialogHelper_Impl::updateExtendedControl( sal_Int16 _nExtendedControlId, bool _bEnable ) { bool bIsEnabled = false; uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); if ( xCtrlAccess.is() ) { try { xCtrlAccess->enableControl( _nExtendedControlId, _bEnable ); bIsEnabled = _bEnable; } catch( const IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::updateExtendedControl" ); } } return bIsEnabled; } bool FileDialogHelper_Impl::CheckFilterOptionsCapability( const std::shared_ptr& _pFilter ) { bool bResult = false; if( mxFilterCFG.is() && _pFilter ) { try { Sequence < PropertyValue > aProps; Any aAny = mxFilterCFG->getByName( _pFilter->GetName() ); if ( aAny >>= aProps ) { OUString aServiceName; for (const auto& rProp : aProps) { if( rProp.Name == "UIComponent" ) { rProp.Value >>= aServiceName; if( !aServiceName.isEmpty() ) bResult = true; } } } } catch( const Exception& ) { } } return bResult; } bool FileDialogHelper_Impl::isInOpenMode() const { bool bRet = false; switch ( m_nDialogType ) { case FILEOPEN_SIMPLE: case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE: case FILEOPEN_PLAY: case FILEOPEN_LINK_PLAY: case FILEOPEN_READONLY_VERSION: case FILEOPEN_LINK_PREVIEW: case FILEOPEN_PREVIEW: bRet = true; } return bRet; } void FileDialogHelper_Impl::updateFilterOptionsBox() { if ( !m_bHaveFilterOptions ) return; updateExtendedControl( ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS, CheckFilterOptionsCapability( getCurrentSfxFilter() ) ); } void FileDialogHelper_Impl::updateExportButton() { uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); if ( !xCtrlAccess.is() ) return; OUString sOldLabel( xCtrlAccess->getLabel( CommonFilePickerElementIds::PUSHBUTTON_OK ) ); // initialize button label; we need the label with the mnemonic char if ( maButtonLabel.isEmpty() || maButtonLabel.indexOf( MNEMONIC_CHAR ) == -1 ) { // cut the ellipses, if necessary sal_Int32 nIndex = sOldLabel.indexOf( "..." ); if ( -1 == nIndex ) nIndex = sOldLabel.getLength(); maButtonLabel = sOldLabel.copy( 0, nIndex ); } OUString sLabel = maButtonLabel; // filter with options -> append ellipses on export button label if ( CheckFilterOptionsCapability( getCurrentSfxFilter() ) ) sLabel += "..."; if ( sOldLabel != sLabel ) { try { xCtrlAccess->setLabel( CommonFilePickerElementIds::PUSHBUTTON_OK, sLabel ); } catch( const IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::updateExportButton" ); } } } void FileDialogHelper_Impl::updateSelectionBox() { if ( !mbHasSelectionBox ) return; // Does the selection box exist? bool bSelectionBoxFound = false; uno::Reference< XControlInformation > xCtrlInfo( mxFileDlg, UNO_QUERY ); if ( xCtrlInfo.is() ) { Sequence< OUString > aCtrlList = xCtrlInfo->getSupportedControls(); bSelectionBoxFound = comphelper::findValue(aCtrlList, "SelectionBox") != -1; } if ( bSelectionBoxFound ) { std::shared_ptr pFilter = getCurrentSfxFilter(); mbSelectionFltrEnabled = updateExtendedControl( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, ( mbSelectionEnabled && pFilter && ( pFilter->GetFilterFlags() & SfxFilterFlags::SUPPORTSSELECTION ) ) ); uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0, Any( mbSelection ) ); } } void FileDialogHelper_Impl::enablePasswordBox( bool bInit ) { if ( ! mbHasPassword ) return; bool bWasEnabled = mbIsPwdEnabled; std::shared_ptr pCurrentFilter = getCurrentSfxFilter(); mbIsPwdEnabled = updateExtendedControl( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, pCurrentFilter && ( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::ENCRYPTION ) ); if( bInit ) { // in case of initialization previous state is not interesting if( mbIsPwdEnabled ) { uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); if( mbPwdCheckBoxState ) xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, Any( true ) ); } } else if( !bWasEnabled && mbIsPwdEnabled ) { uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); if( mbPwdCheckBoxState ) xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, Any( true ) ); } else if( bWasEnabled && !mbIsPwdEnabled ) { // remember user settings until checkbox is enabled uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 ); bool bPassWord = false; mbPwdCheckBoxState = ( aValue >>= bPassWord ) && bPassWord; xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, Any( false ) ); } } void FileDialogHelper_Impl::updatePreviewState( bool _bUpdatePreviewWindow ) { if ( !mbHasPreview ) return; uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); // check, whether or not we have to display a preview if ( !xCtrlAccess.is() ) return; try { Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0 ); bool bShowPreview = false; if ( aValue >>= bShowPreview ) { mbShowPreview = bShowPreview; // setShowState has currently no effect for the // OpenOffice FilePicker (see svtools/source/filepicker/iodlg.cxx) uno::Reference< XFilePreview > xFilePreview( mxFileDlg, UNO_QUERY ); if ( xFilePreview.is() ) xFilePreview->setShowState( mbShowPreview ); if ( _bUpdatePreviewWindow ) TimeOutHdl_Impl( nullptr ); } } catch( const Exception& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::updatePreviewState" ); } } void FileDialogHelper_Impl::updateVersions() { Sequence < OUString > aEntries; Sequence < OUString > aPathSeq = mxFileDlg->getFiles(); if ( aPathSeq.getLength() == 1 ) { INetURLObject aObj( aPathSeq[0] ); if ( ( aObj.GetProtocol() == INetProtocol::File ) && ( utl::UCBContentHelper::IsDocument( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) ) { try { uno::Reference< embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), embed::ElementModes::READ ); DBG_ASSERT( xStorage.is(), "The method must return the storage or throw exception!" ); if ( !xStorage.is() ) throw uno::RuntimeException(); const uno::Sequence < util::RevisionTag > xVersions = SfxMedium::GetVersionList( xStorage ); aEntries.realloc( xVersions.getLength() + 1 ); aEntries.getArray()[0] = SfxResId( STR_SFX_FILEDLG_ACTUALVERSION ); std::transform(xVersions.begin(), xVersions.end(), std::next(aEntries.getArray()), [](const util::RevisionTag& rVersion) -> OUString { return rVersion.Identifier; }); } catch( const uno::Exception& ) { } } } uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY ); Any aValue; try { xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION, ControlActions::DELETE_ITEMS, aValue ); } catch( const IllegalArgumentException& ){} if ( !aEntries.hasElements() ) return; try { aValue <<= aEntries; xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION, ControlActions::ADD_ITEMS, aValue ); Any aPos; aPos <<= sal_Int32(0); xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION, ControlActions::SET_SELECT_ITEM, aPos ); } catch( const IllegalArgumentException& ){} } IMPL_LINK_NOARG(FileDialogHelper_Impl, TimeOutHdl_Impl, Timer *, void) { if ( !mbHasPreview ) return; maGraphic.Clear(); Any aAny; uno::Reference < XFilePreview > xFilePicker( mxFileDlg, UNO_QUERY ); if ( ! xFilePicker.is() ) return; Sequence < OUString > aPathSeq = mxFileDlg->getFiles(); if ( mbShowPreview && ( aPathSeq.getLength() == 1 ) ) { OUString aURL = aPathSeq[0]; if ( ERRCODE_NONE == getGraphic( aURL, maGraphic ) ) { // changed the code slightly; // before: the bitmap was scaled and // surrounded a white frame // now: the bitmap will only be scaled // and the filepicker implementation // is responsible for placing it at its // proper position and painting a frame BitmapEx aBmp = maGraphic.GetBitmapEx(); if ( !aBmp.IsEmpty() ) { // scale the bitmap to the correct size sal_Int32 nOutWidth = xFilePicker->getAvailableWidth(); sal_Int32 nOutHeight = xFilePicker->getAvailableHeight(); sal_Int32 nBmpWidth = aBmp.GetSizePixel().Width(); sal_Int32 nBmpHeight = aBmp.GetSizePixel().Height(); double nXRatio = static_cast(nOutWidth) / nBmpWidth; double nYRatio = static_cast(nOutHeight) / nBmpHeight; if ( nXRatio < nYRatio ) aBmp.Scale( nXRatio, nXRatio ); else aBmp.Scale( nYRatio, nYRatio ); // Convert to true color, to allow CopyPixel aBmp.Convert( BmpConversion::N24Bit ); // and copy it into the Any SvMemoryStream aData; WriteDIB(aBmp, aData, false); const Sequence < sal_Int8 > aBuffer( static_cast< const sal_Int8* >(aData.GetData()), aData.GetEndOfData() ); aAny <<= aBuffer; } } } try { SolarMutexReleaser aReleaseForCallback; // clear the preview window xFilePicker->setImage( FilePreviewImageFormats::BITMAP, aAny ); } catch( const IllegalArgumentException& ) { } } ErrCode FileDialogHelper_Impl::getGraphic( const OUString& rURL, Graphic& rGraphic ) const { if ( utl::UCBContentHelper::IsFolder( rURL ) ) return ERRCODE_IO_NOTAFILE; if ( !mpGraphicFilter ) return ERRCODE_IO_NOTSUPPORTED; // select graphic filter from dialog filter selection OUString aCurFilter( getFilter() ); sal_uInt16 nFilter = !aCurFilter.isEmpty() && mpGraphicFilter->GetImportFormatCount() ? mpGraphicFilter->GetImportFormatNumber( aCurFilter ) : GRFILTER_FORMAT_DONTKNOW; INetURLObject aURLObj( rURL ); if ( aURLObj.HasError() || INetProtocol::NotValid == aURLObj.GetProtocol() ) { aURLObj.SetSmartProtocol( INetProtocol::File ); aURLObj.SetSmartURL( rURL ); } ErrCode nRet = ERRCODE_NONE; GraphicFilterImportFlags nFilterImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg; // non-local? if ( INetProtocol::File != aURLObj.GetProtocol() ) { std::unique_ptr pStream = ::utl::UcbStreamHelper::CreateStream( rURL, StreamMode::READ ); if( pStream ) nRet = mpGraphicFilter->ImportGraphic( rGraphic, rURL, *pStream, nFilter, nullptr, nFilterImportFlags ); else nRet = mpGraphicFilter->ImportGraphic( rGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags ); } else { nRet = mpGraphicFilter->ImportGraphic( rGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags ); } return nRet; } ErrCode FileDialogHelper_Impl::getGraphic( Graphic& rGraphic ) const { ErrCode nRet = ERRCODE_NONE; // rhbz#1079672 do not return maGraphic, it needs not to be the selected file OUString aPath; Sequence aPathSeq = mxFileDlg->getFiles(); if (aPathSeq.getLength() == 1) { aPath = aPathSeq[0]; } if (!aPath.isEmpty()) nRet = getGraphic(aPath, rGraphic); else nRet = ERRCODE_IO_GENERAL; return nRet; } static bool lcl_isSystemFilePicker( const uno::Reference< XExecutableDialog >& _rxFP ) { try { uno::Reference< XServiceInfo > xSI( _rxFP, UNO_QUERY ); if ( !xSI.is() ) return true; return xSI->supportsService( "com.sun.star.ui.dialogs.SystemFilePicker" ); } catch( const Exception& ) { } return false; } namespace { bool lcl_isAsyncFilePicker( const uno::Reference< XExecutableDialog >& _rxFP ) { try { uno::Reference xSI(_rxFP, UNO_QUERY); return xSI.is(); } catch( const Exception& ) { } return false; } enum open_or_save_t {OPEN, SAVE, UNDEFINED}; } static open_or_save_t lcl_OpenOrSave(sal_Int16 const nDialogType) { switch (nDialogType) { case FILEOPEN_SIMPLE: case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE: case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR: case FILEOPEN_PLAY: case FILEOPEN_LINK_PLAY: case FILEOPEN_READONLY_VERSION: case FILEOPEN_LINK_PREVIEW: case FILEOPEN_PREVIEW: return OPEN; case FILESAVE_SIMPLE: case FILESAVE_AUTOEXTENSION_PASSWORD: case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS: case FILESAVE_AUTOEXTENSION_SELECTION: case FILESAVE_AUTOEXTENSION_TEMPLATE: case FILESAVE_AUTOEXTENSION: return SAVE; default: assert(false); // invalid dialog type } return UNDEFINED; } // FileDialogHelper_Impl css::uno::Reference FileDialogHelper_Impl::GetFrameInterface() { if (mpFrameWeld) return mpFrameWeld->GetXWindow(); return css::uno::Reference(); } FileDialogHelper_Impl::FileDialogHelper_Impl( FileDialogHelper* _pAntiImpl, sal_Int16 nDialogType, FileDialogFlags nFlags, sal_Int16 nDialog, weld::Window* pFrameWeld, const OUString& sStandardDir, const css::uno::Sequence< OUString >& rDenyList ) :maPreviewIdle("sfx2 FileDialogHelper_Impl maPreviewIdle") ,m_nDialogType ( nDialogType ) ,meContext ( FileDialogHelper::UnknownContext ) { const char* pServiceName=nullptr; switch (nDialog) { case SFX2_IMPL_DIALOG_SYSTEM: case SFX2_IMPL_DIALOG_OOO: pServiceName = "com.sun.star.ui.dialogs.OfficeFilePicker"; break; case SFX2_IMPL_DIALOG_REMOTE: pServiceName = "com.sun.star.ui.dialogs.RemoteFilePicker"; break; default: pServiceName = "com.sun.star.ui.dialogs.FilePicker"; break; } OUString aService = OUString::createFromAscii( pServiceName ); uno::Reference< XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() ); // create the file open dialog // the flags can be SFXWB_INSERT or SFXWB_MULTISELECTION mpFrameWeld = pFrameWeld; mpAntiImpl = _pAntiImpl; mbHasAutoExt = false; mbHasPassword = false; m_bHaveFilterOptions = false; mbIsPwdEnabled = true; mbHasVersions = false; mbHasPreview = false; mbShowPreview = false; mbDeleteMatcher = false; mbInsert = bool(nFlags & (FileDialogFlags::Insert| FileDialogFlags::InsertCompare| FileDialogFlags::InsertMerge)); mbExport = bool(nFlags & FileDialogFlags::Export); mbIsSaveDlg = false; mbPwdCheckBoxState = false; mbSelection = false; mbSelectionEnabled = true; mbHasSelectionBox = false; mbSelectionFltrEnabled = false; // default settings m_nDontFlags = SFX_FILTER_NOTINSTALLED | SfxFilterFlags::INTERNAL | SfxFilterFlags::NOTINFILEDLG; if (OPEN == lcl_OpenOrSave(m_nDialogType)) m_nMustFlags = SfxFilterFlags::IMPORT; else m_nMustFlags = SfxFilterFlags::EXPORT; mpMatcher = nullptr; mpGraphicFilter = nullptr; mnPostUserEventId = nullptr; // create the picker component mxFileDlg.set(xFactory->createInstance( aService ), css::uno::UNO_QUERY); mbSystemPicker = lcl_isSystemFilePicker( mxFileDlg ); mbAsyncPicker = lcl_isAsyncFilePicker(mxFileDlg); uno::Reference< XInitialization > xInit( mxFileDlg, UNO_QUERY ); if ( ! mxFileDlg.is() ) { return; } if ( xInit.is() ) { sal_Int16 nTemplateDescription = TemplateDescription::FILEOPEN_SIMPLE; switch ( m_nDialogType ) { case FILEOPEN_SIMPLE: nTemplateDescription = TemplateDescription::FILEOPEN_SIMPLE; break; case FILESAVE_SIMPLE: nTemplateDescription = TemplateDescription::FILESAVE_SIMPLE; mbIsSaveDlg = true; break; case FILESAVE_AUTOEXTENSION_PASSWORD: nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD; mbHasPassword = true; mbHasAutoExt = true; mbIsSaveDlg = true; break; case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS: nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS; mbHasPassword = true; m_bHaveFilterOptions = true; if( xFactory.is() ) { mxFilterCFG.set( xFactory->createInstance( "com.sun.star.document.FilterFactory" ), UNO_QUERY ); } mbHasAutoExt = true; mbIsSaveDlg = true; break; case FILESAVE_AUTOEXTENSION_SELECTION: nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION; mbHasAutoExt = true; mbIsSaveDlg = true; mbHasSelectionBox = true; if ( mbExport && !mxFilterCFG.is() && xFactory.is() ) { mxFilterCFG.set( xFactory->createInstance( "com.sun.star.document.FilterFactory" ), UNO_QUERY ); } break; case FILESAVE_AUTOEXTENSION_TEMPLATE: nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE; mbHasAutoExt = true; mbIsSaveDlg = true; break; case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE: nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE; mbHasPreview = true; break; case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR: nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR; mbHasPreview = true; break; case FILEOPEN_PLAY: nTemplateDescription = TemplateDescription::FILEOPEN_PLAY; break; case FILEOPEN_LINK_PLAY: nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PLAY; break; case FILEOPEN_READONLY_VERSION: nTemplateDescription = TemplateDescription::FILEOPEN_READONLY_VERSION; mbHasVersions = true; break; case FILEOPEN_LINK_PREVIEW: nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW; mbHasPreview = true; break; case FILESAVE_AUTOEXTENSION: nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION; mbHasAutoExt = true; mbIsSaveDlg = true; break; case FILEOPEN_PREVIEW: nTemplateDescription = TemplateDescription::FILEOPEN_PREVIEW; mbHasPreview = true; break; default: SAL_WARN( "sfx.dialog", "FileDialogHelper::ctor with unknown type" ); break; } if (mbHasPreview) { maPreviewIdle.SetPriority( TaskPriority::LOWEST ); maPreviewIdle.SetInvokeHandler( LINK( this, FileDialogHelper_Impl, TimeOutHdl_Impl ) ); } auto xWindow = GetFrameInterface(); Sequence < Any > aInitArguments(!xWindow.is() ? 3 : 4); auto pInitArguments = aInitArguments.getArray(); // This is a hack. We currently know that the internal file picker implementation // supports the extended arguments as specified below. // TODO: // a) adjust the service description so that it includes the TemplateDescription and ParentWindow args // b) adjust the implementation of the system file picker to that it recognizes it if ( mbSystemPicker ) { pInitArguments[0] <<= nTemplateDescription; if (xWindow.is()) pInitArguments[1] <<= xWindow; } else { pInitArguments[0] <<= NamedValue( "TemplateDescription", Any( nTemplateDescription ) ); pInitArguments[1] <<= NamedValue( "StandardDir", Any( sStandardDir ) ); pInitArguments[2] <<= NamedValue( "DenyList", Any( rDenyList ) ); if (xWindow.is()) pInitArguments[3] <<= NamedValue("ParentWindow", Any(xWindow)); } try { xInit->initialize( aInitArguments ); } catch( const Exception& ) { OSL_FAIL( "FileDialogHelper_Impl::FileDialogHelper_Impl: could not initialize the picker!" ); } } // set multiselection mode if ( nFlags & FileDialogFlags::MultiSelection ) mxFileDlg->setMultiSelectionMode( true ); if ( nFlags & FileDialogFlags::Graphic ) // generate graphic filter only on demand { addGraphicFilter(); } // Export dialog if ( mbExport ) { mxFileDlg->setTitle( SfxResId( STR_SFX_EXPLORERFILE_EXPORT ) ); try { css::uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY_THROW ); xCtrlAccess->enableControl( ExtendedFilePickerElementIds::LISTBOX_FILTER_SELECTOR, true ); } catch( const Exception & ) { } } // Save a copy dialog if ( nFlags & FileDialogFlags::SaveACopy ) { mxFileDlg->setTitle( SfxResId( STR_PB_SAVEACOPY ) ); } // the "insert file" dialog needs another title if ( mbInsert ) { if ( nFlags & FileDialogFlags::InsertCompare ) { mxFileDlg->setTitle( SfxResId( STR_PB_COMPAREDOC ) ); } else if ( nFlags & FileDialogFlags::InsertMerge ) { mxFileDlg->setTitle( SfxResId( STR_PB_MERGEDOC ) ); } else { mxFileDlg->setTitle( SfxResId( STR_SFX_EXPLORERFILE_INSERT ) ); } uno::Reference < XFilePickerControlAccess > xExtDlg( mxFileDlg, UNO_QUERY ); if ( xExtDlg.is() ) { try { xExtDlg->setLabel( CommonFilePickerElementIds::PUSHBUTTON_OK, SfxResId( STR_SFX_EXPLORERFILE_BUTTONINSERT ) ); } catch( const IllegalArgumentException& ){} } } // add the event listener mxFileDlg->addFilePickerListener( this ); } css::uno::Reference createFolderPicker(const css::uno::Reference& rContext, weld::Window* pPreferredParent) { auto xRet = css::ui::dialogs::FolderPicker::create(rContext); // see FileDialogHelper_Impl::FileDialogHelper_Impl (above) for args to FilePicker // reuse the same arguments for FolderPicker if (pPreferredParent && lcl_isSystemFilePicker(xRet)) { uno::Reference< XInitialization > xInit(xRet, UNO_QUERY); if (xInit.is()) { Sequence aInitArguments{ Any(sal_Int32(0)), Any(pPreferredParent->GetXWindow()) }; try { xInit->initialize(aInitArguments); } catch (const Exception&) { OSL_FAIL( "createFolderPicker: could not initialize the picker!" ); } } } return xRet; } FileDialogHelper_Impl::~FileDialogHelper_Impl() { // Remove user event if we haven't received it yet if ( mnPostUserEventId ) Application::RemoveUserEvent( mnPostUserEventId ); mnPostUserEventId = nullptr; mpGraphicFilter.reset(); if ( mbDeleteMatcher ) delete mpMatcher; maPreviewIdle.ClearInvokeHandler(); ::comphelper::disposeComponent( mxFileDlg ); } void FileDialogHelper_Impl::setControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId ) { DBG_ASSERT( _pControlId && _pHelpId, "FileDialogHelper_Impl::setControlHelpIds: invalid array pointers!" ); if ( !_pControlId || !_pHelpId ) return; // forward these ids to the file picker try { const OUString sHelpIdPrefix( INET_HID_SCHEME ); // the ids for the single controls uno::Reference< XFilePickerControlAccess > xControlAccess( mxFileDlg, UNO_QUERY ); if ( xControlAccess.is() ) { while ( *_pControlId ) { DBG_ASSERT( INetURLObject( OStringToOUString( *_pHelpId, RTL_TEXTENCODING_UTF8 ) ).GetProtocol() == INetProtocol::NotValid, "Wrong HelpId!" ); OUString sId = sHelpIdPrefix + OUString( *_pHelpId, strlen( *_pHelpId ), RTL_TEXTENCODING_UTF8 ); xControlAccess->setValue( *_pControlId, ControlActions::SET_HELP_URL, Any( sId ) ); ++_pControlId; ++_pHelpId; } } } catch( const Exception& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::setControlHelpIds: caught an exception while setting the help ids!" ); } } IMPL_LINK_NOARG( FileDialogHelper_Impl, InitControls, void*, void ) { mnPostUserEventId = nullptr; enablePasswordBox( true ); updateFilterOptionsBox( ); updateSelectionBox( ); } void FileDialogHelper_Impl::preExecute() { loadConfig( ); setDefaultValues( ); updatePreviewState( false ); implInitializeFileName( ); #if !(defined(MACOSX) && defined(MACOSX)) && !defined(_WIN32) // allow for dialog implementations which need to be executed before they return valid values for // current filter and such // On Vista (at least SP1) it's the same as on MacOSX, the modal dialog won't let message pass // through before it returns from execution mnPostUserEventId = Application::PostUserEvent( LINK( this, FileDialogHelper_Impl, InitControls ) ); #else // However, the macOS implementation's pickers run modally in execute and so the event doesn't // get through in time... so we call the methods directly enablePasswordBox( true ); updateFilterOptionsBox( ); updateSelectionBox( ); #endif } void FileDialogHelper_Impl::postExecute( sal_Int16 _nResult ) { if ( ExecutableDialogResults::CANCEL != _nResult ) saveConfig(); } void FileDialogHelper_Impl::implInitializeFileName( ) { if ( maFileName.isEmpty() ) return; INetURLObject aObj( maPath ); aObj.Append( maFileName ); // in case we're operating as save dialog, and "auto extension" is checked, // cut the extension from the name if ( !(mbIsSaveDlg && mbHasAutoExt) ) return; try { bool bAutoExtChecked = false; uno::Reference < XFilePickerControlAccess > xControlAccess( mxFileDlg, UNO_QUERY ); if ( xControlAccess.is() && ( xControlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0 ) >>= bAutoExtChecked ) ) { if ( bAutoExtChecked ) { // cut the extension aObj.removeExtension( ); mxFileDlg->setDefaultName( aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset)); } } } catch( const Exception& ) { OSL_FAIL( "FileDialogHelper_Impl::implInitializeFileName: could not ask for the auto-extension current-value!" ); } } sal_Int16 FileDialogHelper_Impl::implDoExecute() { preExecute(); sal_Int16 nRet = ExecutableDialogResults::CANCEL; //On MacOSX the native file picker has to run in the primordial thread because of drawing issues //On Linux the native gtk file picker, when backed by gnome-vfs2, needs to be run in the same //primordial thread as the ucb gnome-vfs2 provider was initialized in. { try { #ifdef _WIN32 if ( mbSystemPicker ) { SolarMutexReleaser aSolarMutex; nRet = mxFileDlg->execute(); } else #endif nRet = mxFileDlg->execute(); } catch( const Exception& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::implDoExecute" ); } } postExecute( nRet ); return nRet; } void FileDialogHelper_Impl::implStartExecute() { DBG_ASSERT( mxFileDlg.is(), "invalid file dialog" ); assert(mbAsyncPicker); preExecute(); try { uno::Reference< XAsynchronousExecutableDialog > xAsyncDlg( mxFileDlg, UNO_QUERY ); if ( xAsyncDlg.is() ) xAsyncDlg->startExecuteModal( this ); } catch( const Exception& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::implDoExecute" ); } } void FileDialogHelper_Impl::implGetAndCacheFiles(const uno::Reference< XInterface >& xPicker, std::vector& rpURLList) { rpURLList.clear(); // a) the new way (optional!) uno::Reference< XFilePicker3 > xPickNew(xPicker, UNO_QUERY); if (xPickNew.is()) { Sequence< OUString > lFiles = xPickNew->getSelectedFiles(); comphelper::sequenceToContainer(rpURLList, lFiles); } // b) the olde way ... non optional. else { uno::Reference< XFilePicker3 > xPickOld(xPicker, UNO_QUERY_THROW); Sequence< OUString > lFiles = xPickOld->getFiles(); ::sal_Int32 nFiles = lFiles.getLength(); if ( nFiles == 1 ) { rpURLList.push_back(lFiles[0]); } else if ( nFiles > 1 ) { INetURLObject aPath( lFiles[0] ); aPath.setFinalSlash(); for (::sal_Int32 i = 1; i < nFiles; i++) { if (i == 1) aPath.Append( lFiles[i] ); else aPath.setName( lFiles[i] ); rpURLList.push_back(aPath.GetMainURL(INetURLObject::DecodeMechanism::NONE)); } } } mlLastURLs = rpURLList; } ErrCode FileDialogHelper_Impl::execute( std::vector& rpURLList, std::optional& rpSet, OUString& rFilter ) { // rFilter is a pure output parameter, it shouldn't be used for anything else // changing this would surely break code // rpSet is in/out parameter, usually just a media-descriptor that can be changed by dialog uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY ); // retrieves parameters from rpSet // for now only Password is used if ( rpSet ) { // check password checkbox if the document had password before if( mbHasPassword ) { const SfxBoolItem* pPassItem = SfxItemSet::GetItem(&*rpSet, SID_PASSWORDINTERACTION, false); mbPwdCheckBoxState = ( pPassItem != nullptr && pPassItem->GetValue() ); // in case the document has password to modify, the dialog should be shown const SfxUnoAnyItem* pPassToModifyItem = SfxItemSet::GetItem(&*rpSet, SID_MODIFYPASSWORDINFO, false); mbPwdCheckBoxState |= ( pPassToModifyItem && pPassToModifyItem->GetValue().hasValue() ); } const SfxBoolItem* pSelectItem = SfxItemSet::GetItem(&*rpSet, SID_SELECTION, false); if ( pSelectItem ) mbSelection = pSelectItem->GetValue(); else mbSelectionEnabled = false; // the password will be set in case user decide so rpSet->ClearItem( SID_PASSWORDINTERACTION ); if (rpSet->HasItem( SID_PASSWORD )) { // As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both. // Note: Do not remove SID_ENCRYPTIONDATA without SID_PASSWORD rpSet->ClearItem( SID_PASSWORD ); rpSet->ClearItem( SID_ENCRYPTIONDATA ); } rpSet->ClearItem( SID_RECOMMENDREADONLY ); rpSet->ClearItem( SID_MODIFYPASSWORDINFO ); } if ( mbHasPassword && !mbPwdCheckBoxState ) { mbPwdCheckBoxState = ( SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::DocWarnRecommendPassword ) ); } rpURLList.clear(); if ( ! mxFileDlg.is() ) return ERRCODE_ABORT; if ( ExecutableDialogResults::CANCEL != implDoExecute() ) { // create an itemset if there is no if( !rpSet ) rpSet.emplace( SfxGetpApp()->GetPool() ); // the item should remain only if it was set by the dialog rpSet->ClearItem( SID_SELECTION ); if( mbExport && mbHasSelectionBox ) { try { Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 ); bool bSelection = false; if ( aValue >>= bSelection ) rpSet->Put( SfxBoolItem( SID_SELECTION, bSelection ) ); } catch( const IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" ); } } // set the read-only flag. When inserting a file, this flag is always set if ( mbInsert ) rpSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); else { if ( ( FILEOPEN_READONLY_VERSION == m_nDialogType ) && xCtrlAccess.is() ) { try { Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_READONLY, 0 ); bool bReadOnly = false; if ( ( aValue >>= bReadOnly ) && bReadOnly ) rpSet->Put( SfxBoolItem( SID_DOC_READONLY, bReadOnly ) ); } catch( const IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" ); } } } if ( mbHasVersions && xCtrlAccess.is() ) { try { Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::LISTBOX_VERSION, ControlActions::GET_SELECTED_ITEM_INDEX ); sal_Int32 nVersion = 0; if ( ( aValue >>= nVersion ) && nVersion > 0 ) // open a special version; 0 == current version rpSet->Put( SfxInt16Item( SID_VERSION, static_cast(nVersion) ) ); } catch( const IllegalArgumentException& ){} } // set the filter getRealFilter( rFilter ); std::shared_ptr pCurrentFilter = getCurrentSfxFilter(); // fill the rpURLList implGetAndCacheFiles( mxFileDlg, rpURLList ); if ( rpURLList.empty() ) return ERRCODE_ABORT; // check, whether or not we have to display a password box if ( pCurrentFilter && mbHasPassword && mbIsPwdEnabled && xCtrlAccess.is() ) { try { Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 ); bool bPassWord = false; if ( ( aValue >>= bPassWord ) && bPassWord ) { // ask for a password OUString aDocName(rpURLList[0]); ErrCode errCode = RequestPassword(pCurrentFilter, aDocName, &*rpSet, GetFrameInterface()); if (errCode != ERRCODE_NONE) return errCode; } } catch( const IllegalArgumentException& ){} } // check, whether or not we have to display a key selection box if ( pCurrentFilter && mbHasPassword && xCtrlAccess.is() ) { try { Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION, 0 ); bool bGpg = false; if ( ( aValue >>= bGpg ) && bGpg ) { uno::Sequence< beans::NamedValue > aEncryptionData; while(true) { try { // ask for keys aEncryptionData = ::comphelper::OStorageHelper::CreateGpgPackageEncryptionData(); break; // user cancelled or we've some keys now } catch( const IllegalArgumentException& ) { std::unique_ptr xBox(Application::CreateMessageDialog(mpFrameWeld, VclMessageType::Warning, VclButtonsType::Ok, SfxResId(RID_SVXSTR_GPG_ENCRYPT_FAILURE))); xBox->run(); } } if ( aEncryptionData.hasElements() ) rpSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData) ) ); else return ERRCODE_ABORT; } } catch( const IllegalArgumentException& ){} } SaveLastUsedFilter(); return ERRCODE_NONE; } else return ERRCODE_ABORT; } ErrCode FileDialogHelper_Impl::execute() { if ( ! mxFileDlg.is() ) return ERRCODE_ABORT; sal_Int16 nRet = implDoExecute(); maPath = mxFileDlg->getDisplayDirectory(); if ( ExecutableDialogResults::CANCEL == nRet ) return ERRCODE_ABORT; else { return ERRCODE_NONE; } } OUString FileDialogHelper_Impl::getPath() const { OUString aPath; if ( mxFileDlg.is() ) aPath = mxFileDlg->getDisplayDirectory(); if ( aPath.isEmpty() ) aPath = maPath; return aPath; } OUString FileDialogHelper_Impl::getFilter() const { OUString aFilter = getCurrentFilterUIName(); if( aFilter.isEmpty() ) aFilter = maCurFilter; return aFilter; } void FileDialogHelper_Impl::getRealFilter( OUString& _rFilter ) const { _rFilter = getCurrentFilterUIName(); if ( _rFilter.isEmpty() ) _rFilter = maCurFilter; if ( !_rFilter.isEmpty() && mpMatcher ) { std::shared_ptr pFilter = mpMatcher->GetFilter4UIName( _rFilter, m_nMustFlags, m_nDontFlags ); _rFilter = pFilter ? pFilter->GetFilterName() : OUString(); } } void FileDialogHelper_Impl::verifyPath() { #ifdef UNX // lp#905355, fdo#43895 // Check that the file has read only permission and is in /tmp -- this is // the case if we have opened the file from the web with firefox only. if (maFileName.isEmpty()) { return; } INetURLObject url(maPath); if (url.GetProtocol() != INetProtocol::File || url.getName(0, true, INetURLObject::DecodeMechanism::WithCharset) != "tmp") { return; } if (maFileName.indexOf('/') != -1) { SAL_WARN("sfx.dialog", maFileName << " contains /"); return; } url.insertName( maFileName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All); OUString sysPathU; osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL( url.GetMainURL(INetURLObject::DecodeMechanism::NONE), sysPathU); if (e != osl::FileBase::E_None) { SAL_WARN( "sfx.dialog", "getSystemPathFromFileURL(" << url.GetMainURL(INetURLObject::DecodeMechanism::NONE) << ") failed with " << +e); return; } OString sysPathC; if (!sysPathU.convertToString( &sysPathC, osl_getThreadTextEncoding(), (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) { SAL_WARN( "sfx.dialog", "convertToString(" << sysPathU << ") failed for encoding " << +osl_getThreadTextEncoding()); return; } struct stat aFileStat; if (stat(sysPathC.getStr(), &aFileStat) == -1) { SAL_WARN( "sfx.dialog", "stat(" << sysPathC << ") failed with errno " << errno); return; } if ((aFileStat.st_mode & (S_IRWXO | S_IRWXG | S_IRWXU)) == S_IRUSR) { maPath = SvtPathOptions().GetWorkPath(); mxFileDlg->setDisplayDirectory( maPath ); } #else (void) this; #endif } void FileDialogHelper_Impl::displayFolder( const OUString& _rPath ) { if ( _rPath.isEmpty() ) // nothing to do return; maPath = _rPath; if ( mxFileDlg.is() ) { try { mxFileDlg->setDisplayDirectory( maPath ); verifyPath(); } catch( const IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::displayFolder" ); } } } void FileDialogHelper_Impl::setFileName( const OUString& _rFile ) { maFileName = _rFile; if ( mxFileDlg.is() ) { try { mxFileDlg->setDefaultName( maFileName ); verifyPath(); } catch( const IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::setFileName" ); } } } void FileDialogHelper_Impl::setFilter( const OUString& rFilter ) { DBG_ASSERT( rFilter.indexOf(':') == -1, "Old filter name used!"); maCurFilter = rFilter; if ( !rFilter.isEmpty() && mpMatcher ) { std::shared_ptr pFilter = mpMatcher->GetFilter4FilterName( rFilter, m_nMustFlags, m_nDontFlags ); if ( pFilter ) maCurFilter = pFilter->GetUIName(); } if ( !maCurFilter.isEmpty() && mxFileDlg.is() ) { try { mxFileDlg->setCurrentFilter( maCurFilter ); } catch( const IllegalArgumentException& ){} } } void FileDialogHelper_Impl::createMatcher( const OUString& rFactory ) { if (mbDeleteMatcher) delete mpMatcher; mpMatcher = new SfxFilterMatcher( SfxObjectShell::GetServiceNameFromFactory(rFactory) ); mbDeleteMatcher = true; } void FileDialogHelper_Impl::addFilters( const OUString& rFactory, SfxFilterFlags nMust, SfxFilterFlags nDont ) { if ( ! mxFileDlg.is() ) return; if (mbDeleteMatcher) delete mpMatcher; // we still need a matcher to convert UI names to filter names if ( rFactory.isEmpty() ) { SfxApplication *pSfxApp = SfxGetpApp(); mpMatcher = &pSfxApp->GetFilterMatcher(); mbDeleteMatcher = false; } else { mpMatcher = new SfxFilterMatcher( rFactory ); mbDeleteMatcher = true; } uno::Reference< XMultiServiceFactory > xSMGR = ::comphelper::getProcessServiceFactory(); uno::Reference< XContainerQuery > xFilterCont( xSMGR->createInstance("com.sun.star.document.FilterFactory"), UNO_QUERY); if ( ! xFilterCont.is() ) return; m_nMustFlags |= nMust; m_nDontFlags |= nDont; // create the list of filters OUString sQuery = "getSortedFilterList()" ":module=" + rFactory + // use long name here ! ":iflags=" + OUString::number(static_cast(m_nMustFlags)) + ":eflags=" + OUString::number(static_cast(m_nDontFlags)); uno::Reference< XEnumeration > xResult; try { xResult = xFilterCont->createSubSetEnumerationByQuery(sQuery); } catch( const uno::Exception& ) { SAL_WARN( "sfx.dialog", "Could not get filters from the configuration!" ); } TSortedFilterList aIter (xResult); // append the filters OUString sFirstFilter; if (OPEN == lcl_OpenOrSave(m_nDialogType)) ::sfx2::appendFiltersForOpen( aIter, mxFileDlg, sFirstFilter, *this ); else if ( mbExport ) ::sfx2::appendExportFilters( aIter, mxFileDlg, sFirstFilter, *this ); else ::sfx2::appendFiltersForSave( aIter, mxFileDlg, sFirstFilter, *this, rFactory ); // set our initial selected filter (if we do not already have one) if ( maSelectFilter.isEmpty() ) maSelectFilter = sFirstFilter; } void FileDialogHelper_Impl::addFilter( const OUString& rFilterName, const OUString& rExtension ) { if ( ! mxFileDlg.is() ) return; try { mxFileDlg->appendFilter( rFilterName, rExtension ); if ( maSelectFilter.isEmpty() ) maSelectFilter = rFilterName; } catch( const IllegalArgumentException& ) { SAL_WARN( "sfx.dialog", "Could not append Filter" << rFilterName ); } } void FileDialogHelper_Impl::addGraphicFilter() { if ( ! mxFileDlg.is() ) return; // create the list of filters mpGraphicFilter.reset( new GraphicFilter ); sal_uInt16 i, j, nCount = mpGraphicFilter->GetImportFormatCount(); // compute the extension string for all known import filters OUString aExtensions; for ( i = 0; i < nCount; i++ ) { j = 0; while( true ) { OUString sWildcard = mpGraphicFilter->GetImportWildcard( i, j++ ); if ( sWildcard.isEmpty() ) break; if ( aExtensions.indexOf( sWildcard ) == -1 ) { if ( !aExtensions.isEmpty() ) aExtensions += ";"; aExtensions += sWildcard; } } } #if defined(_WIN32) if ( aExtensions.getLength() > 240 ) aExtensions = FILEDIALOG_FILTER_ALL; #endif bool bIsInOpenMode = isInOpenMode(); try { // if the extension is not "All files", insert "All images" if (aExtensions != FILEDIALOG_FILTER_ALL) { OUString aAllFilterName = SfxResId(STR_SFX_IMPORT_ALL_IMAGES); aAllFilterName = ::sfx2::addExtension( aAllFilterName, aExtensions, bIsInOpenMode, *this ); mxFileDlg->appendFilter( aAllFilterName, aExtensions ); maSelectFilter = aAllFilterName; // and make it the default } // rhbz#1715109 always include All files *.* or * OUString aAllFilesName = SfxResId( STR_SFX_FILTERNAME_ALL ); aAllFilesName = ::sfx2::addExtension( aAllFilesName, FILEDIALOG_FILTER_ALL, bIsInOpenMode, *this ); mxFileDlg->appendFilter( aAllFilesName, FILEDIALOG_FILTER_ALL ); // if the extension is "All files", make that the default if (aExtensions == FILEDIALOG_FILTER_ALL) maSelectFilter = aAllFilesName; } catch( const IllegalArgumentException& ) { SAL_WARN( "sfx.dialog", "Could not append Filter" ); } // Now add the filter for ( i = 0; i < nCount; i++ ) { OUString aName = mpGraphicFilter->GetImportFormatName( i ); OUString aExt; j = 0; while( true ) { OUString sWildcard = mpGraphicFilter->GetImportWildcard( i, j++ ); if ( sWildcard.isEmpty() ) break; if ( aExt.indexOf( sWildcard ) == -1 ) { if ( !aExt.isEmpty() ) aExt += ";"; aExt += sWildcard; } } aName = ::sfx2::addExtension( aName, aExt, bIsInOpenMode, *this ); try { mxFileDlg->appendFilter( aName, aExt ); } catch( const IllegalArgumentException& ) { SAL_WARN( "sfx.dialog", "Could not append Filter" ); } } } constexpr OUStringLiteral GRF_CONFIG_STR = u" "; constexpr OUString STD_CONFIG_STR = u"1 "_ustr; static void SetToken( OUString& rOrigStr, sal_Int32 nToken, sal_Unicode cTok, std::u16string_view rStr) { const sal_Unicode* pStr = rOrigStr.getStr(); sal_Int32 nLen = rOrigStr.getLength(); sal_Int32 nTok = 0; sal_Int32 nFirstChar = 0; sal_Int32 i = nFirstChar; // Determine token position and length pStr += i; while ( i < nLen ) { // Increase token count if match if ( *pStr == cTok ) { ++nTok; if ( nTok == nToken ) nFirstChar = i+1; else { if ( nTok > nToken ) break; } } ++pStr; ++i; } if ( nTok >= nToken ) rOrigStr = rOrigStr.replaceAt( nFirstChar, i-nFirstChar, rStr ); } namespace { void SaveLastDirectory(OUString const& sContext, OUString const& sDirectory) { if (sContext.isEmpty()) return; std::shared_ptr batch( comphelper::ConfigurationChanges::create()); Reference set( officecfg::Office::Common::Misc::FilePickerLastDirectory::get(batch)); bool found; Any v; try { v = set->getByName(sContext); found = true; } catch (container::NoSuchElementException&) { found = false; } if (found) { Reference el(v.get>(), UNO_SET_THROW); el->setPropertyValue("LastPath", Any(sDirectory)); } else { Reference el( (Reference(set, UNO_QUERY_THROW)->createInstance()), UNO_QUERY_THROW); el->setPropertyValue("LastPath", Any(sDirectory)); Any v2(el); set->insertByName(sContext, v2); } batch->commit(); } } void FileDialogHelper_Impl::saveConfig() { uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY ); Any aValue; if ( ! xDlg.is() ) return; if ( mbHasPreview ) { SvtViewOptions aDlgOpt( EViewType::Dialog, IMPGRF_CONFIGNAME ); try { aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0 ); bool bValue = false; aValue >>= bValue; OUString aUserData(GRF_CONFIG_STR); SetToken( aUserData, 1, ' ', OUString::number( static_cast(bValue) ) ); INetURLObject aObj( getPath() ); if ( aObj.GetProtocol() == INetProtocol::File ) SetToken( aUserData, 2, ' ', aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); OUString aFilter = getFilter(); aFilter = EncodeSpaces_Impl( aFilter ); SetToken( aUserData, 3, ' ', aFilter ); aDlgOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) ); } catch( const IllegalArgumentException& ){} } else { bool bWriteConfig = false; SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME ); OUString aUserData(STD_CONFIG_STR); if ( aDlgOpt.Exists() ) { Any aUserItem = aDlgOpt.GetUserItem( USERITEM_NAME ); OUString aTemp; if ( aUserItem >>= aTemp ) aUserData = aTemp; } if ( mbHasAutoExt ) { try { aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0 ); bool bAutoExt = true; aValue >>= bAutoExt; SetToken( aUserData, 0, ' ', OUString::number( static_cast(bAutoExt) ) ); bWriteConfig = true; } catch( const IllegalArgumentException& ){} } if ( ! mbIsSaveDlg ) { OUString aPath = getPath(); if ( comphelper::isFileUrl( aPath ) ) { SetToken( aUserData, 1, ' ', aPath ); bWriteConfig = true; } } if( mbHasSelectionBox && mbSelectionFltrEnabled ) { try { aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 ); bool bSelection = true; aValue >>= bSelection; if ( comphelper::string::getTokenCount(aUserData, ' ') < 3 ) aUserData += " "; SetToken( aUserData, 2, ' ', OUString::number( static_cast(bSelection) ) ); bWriteConfig = true; } catch( const IllegalArgumentException& ){} } if ( bWriteConfig ) aDlgOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) ); } // Store to config, if explicit context is set. Otherwise store in (global) runtime var. if (meContext != FileDialogHelper::UnknownContext) { SaveLastDirectory(FileDialogHelper::contextToString(meContext), getPath()); } else { SfxApplication *pSfxApp = SfxGetpApp(); pSfxApp->SetLastDir_Impl( getPath() ); } } OUString FileDialogHelper_Impl::getInitPath(std::u16string_view _rFallback, const sal_Int32 _nFallbackToken) { OUString sPath; // Load from config, if explicit context is set. Otherwise load from (global) runtime var. if (meContext != FileDialogHelper::UnknownContext) { OUString sContext = FileDialogHelper::contextToString(meContext); Reference set(officecfg::Office::Common::Misc::FilePickerLastDirectory::get()); Any v; try { v = set->getByName(sContext); Reference el(v.get>(), UNO_SET_THROW); sPath = el->getPropertyValue("LastPath").get(); } catch (NoSuchElementException&) { } } else { SfxApplication *pSfxApp = SfxGetpApp(); sPath = pSfxApp->GetLastDir_Impl(); } if ( sPath.isEmpty() ) sPath = o3tl::getToken(_rFallback, _nFallbackToken, ' ' ); // check if the path points to a valid (accessible) directory bool bValid = false; if ( !sPath.isEmpty() ) { OUString sPathCheck( sPath ); if ( sPathCheck[ sPathCheck.getLength() - 1 ] != '/' ) sPathCheck += "/"; sPathCheck += "."; try { ::ucbhelper::Content aContent( sPathCheck, utl::UCBContentHelper::getDefaultCommandEnvironment(), comphelper::getProcessComponentContext() ); bValid = aContent.isFolder(); } catch( const Exception& ) {} } if ( !bValid ) sPath.clear(); return sPath; } void FileDialogHelper_Impl::loadConfig() { uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY ); Any aValue; if ( ! xDlg.is() ) return; if ( mbHasPreview ) { SvtViewOptions aViewOpt( EViewType::Dialog, IMPGRF_CONFIGNAME ); OUString aUserData; if ( aViewOpt.Exists() ) { Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME ); OUString aTemp; if ( aUserItem >>= aTemp ) aUserData = aTemp; } if ( !aUserData.isEmpty() ) { try { // respect the last "insert as link" state bool bLink = o3tl::toInt32(o3tl::getToken(aUserData, 0, ' ' )); aValue <<= bLink; xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, aValue ); // respect the last "show preview" state bool bShowPreview = o3tl::toInt32(o3tl::getToken(aUserData, 1, ' ' )); aValue <<= bShowPreview; xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, aValue ); if ( maPath.isEmpty() ) displayFolder( getInitPath( aUserData, 2 ) ); if ( maCurFilter.isEmpty() ) { OUString aFilter = aUserData.getToken( 3, ' ' ); aFilter = DecodeSpaces_Impl( aFilter ); setFilter( aFilter ); } // set the member so we know that we have to show the preview mbShowPreview = bShowPreview; } catch( const IllegalArgumentException& ){} } if ( maPath.isEmpty() ) displayFolder( SvtPathOptions().GetWorkPath() ); } else { SvtViewOptions aViewOpt( EViewType::Dialog, IODLG_CONFIGNAME ); OUString aUserData; if ( aViewOpt.Exists() ) { Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME ); OUString aTemp; if ( aUserItem >>= aTemp ) aUserData = aTemp; } if ( aUserData.isEmpty() ) aUserData = STD_CONFIG_STR; if ( maPath.isEmpty() ) displayFolder( getInitPath( aUserData, 1 ) ); if ( mbHasAutoExt ) { sal_Int32 nFlag = o3tl::toInt32(o3tl::getToken(aUserData, 0, ' ' )); aValue <<= static_cast(nFlag); try { xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, aValue ); } catch( const IllegalArgumentException& ){} } if( mbHasSelectionBox ) { sal_Int32 nFlag = o3tl::toInt32(o3tl::getToken(aUserData, 2, ' ' )); aValue <<= static_cast(nFlag); try { xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0, aValue ); } catch( const IllegalArgumentException& ){} } if ( maPath.isEmpty() ) displayFolder( SvtPathOptions().GetWorkPath() ); } } void FileDialogHelper_Impl::setDefaultValues() { // when no filter is set, we set the currentFilter to if ( maCurFilter.isEmpty() && !maSelectFilter.isEmpty() ) { try { mxFileDlg->setCurrentFilter( maSelectFilter ); } catch( const IllegalArgumentException& ) {} } // when no path is set, we use the standard 'work' folder if ( maPath.isEmpty() ) { OUString aWorkFolder = SvtPathOptions().GetWorkPath(); try { mxFileDlg->setDisplayDirectory( aWorkFolder ); } catch( const Exception& ) { TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::setDefaultValues: caught an exception while setting the display directory!" ); } } } bool FileDialogHelper_Impl::isShowFilterExtensionEnabled() const { return !maFilters.empty(); } void FileDialogHelper_Impl::addFilterPair( const OUString& rFilter, const OUString& rFilterWithExtension ) { maFilters.emplace_back( rFilter, rFilterWithExtension ); } OUString FileDialogHelper_Impl::getFilterName( std::u16string_view rFilterWithExtension ) const { OUString sRet; for (auto const& filter : maFilters) { if (filter.Second == rFilterWithExtension) { sRet = filter.First; break; } } return sRet; } OUString FileDialogHelper_Impl::getFilterWithExtension( std::u16string_view rFilter ) const { OUString sRet; for (auto const& filter : maFilters) { if ( filter.First == rFilter ) { sRet = filter.Second; break; } } return sRet; } void FileDialogHelper_Impl::SetContext( FileDialogHelper::Context _eNewContext ) { meContext = _eNewContext; std::optional pConfigId = GetLastFilterConfigId( _eNewContext ); if( pConfigId ) LoadLastUsedFilter( *pConfigId ); } // FileDialogHelper FileDialogHelper::FileDialogHelper( sal_Int16 nDialogType, FileDialogFlags nFlags, const OUString& rFact, SfxFilterFlags nMust, SfxFilterFlags nDont, weld::Window* pPreferredParent) : m_nError(0), mpImpl(new FileDialogHelper_Impl(this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent)) { // create the list of filters mpImpl->addFilters( SfxObjectShell::GetServiceNameFromFactory(rFact), nMust, nDont ); } FileDialogHelper::FileDialogHelper( sal_Int16 nDialogType, FileDialogFlags nFlags, const OUString& rFact, sal_Int16 nDialog, SfxFilterFlags nMust, SfxFilterFlags nDont, const OUString& rStandardDir, const css::uno::Sequence< OUString >& rDenyList, weld::Window* pPreferredParent) : m_nError(0), mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, nDialog, pPreferredParent, rStandardDir, rDenyList ) ) { // create the list of filters mpImpl->addFilters( SfxObjectShell::GetServiceNameFromFactory(rFact), nMust, nDont ); } FileDialogHelper::FileDialogHelper(sal_Int16 nDialogType, FileDialogFlags nFlags, weld::Window* pPreferredParent) : m_nError(0), mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent ) ) { } FileDialogHelper::FileDialogHelper( sal_Int16 nDialogType, FileDialogFlags nFlags, const OUString& aFilterUIName, std::u16string_view aExtName, const OUString& rStandardDir, const css::uno::Sequence< OUString >& rDenyList, weld::Window* pPreferredParent ) : m_nError(0), mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent, rStandardDir, rDenyList ) ) { // the wildcard here is expected in form "*.extension" OUString aWildcard; if ( aExtName.find( '*' ) != 0 ) { if ( !aExtName.empty() && aExtName.find( '.' ) != 0 ) aWildcard = "*."; else aWildcard = "*"; } aWildcard += aExtName; OUString const aUIString = ::sfx2::addExtension( aFilterUIName, aWildcard, (OPEN == lcl_OpenOrSave(mpImpl->m_nDialogType)), *mpImpl); AddFilter( aUIString, aWildcard ); } FileDialogHelper::~FileDialogHelper() { mpImpl->dispose(); } void FileDialogHelper::CreateMatcher( const OUString& rFactory ) { mpImpl->createMatcher( SfxObjectShell::GetServiceNameFromFactory(rFactory) ); } void FileDialogHelper::SetControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId ) { mpImpl->setControlHelpIds( _pControlId, _pHelpId ); } void FileDialogHelper::SetContext( Context _eNewContext ) { mpImpl->SetContext( _eNewContext ); } OUString FileDialogHelper::contextToString(Context context) { // These strings are used in the configuration, to store the last used directory for each context. // Please don't change them. switch(context) { case AcceleratorConfig: return "AcceleratorConfig"; case AutoRedact: return "AutoRedact"; case BaseDataSource: return "BaseDataSource"; case BaseSaveAs: return "BaseSaveAs"; case BasicExportDialog: return "BasicExportDialog"; case BasicExportPackage: return "BasicExportPackage"; case BasicExportSource: return "BasicExportSource"; case BasicImportDialog: return "BasicImportDialog"; case BasicImportSource: return "BasicImportSource"; case BasicInsertLib: return "BasicInsertLib"; case BulletsAddImage: return "BulletsAddImage"; case CalcDataProvider: return "CalcDataProvider"; case CalcDataStream: return "CalcDataStream"; case CalcExport: return "CalcExport"; case CalcSaveAs: return "CalcSaveAs"; case CalcXMLSource: return "CalcXMLSource"; case ExportImage: return "ExportImage"; case ExtensionManager: return "ExtensionManager"; case FormsAddInstance: return "FormsAddInstance"; case FormsInsertImage: return "FormsInsertImage"; case LinkClientOLE: return "LinkClientOLE"; case LinkClientFile: return "LinkClientFile"; case DrawImpressInsertFile: return "DrawImpressInsertFile"; case DrawImpressOpenSound: return "DrawImpressOpenSound"; case DrawExport: return "DrawExport"; case DrawSaveAs: return "DrawSaveAs"; case IconImport: return "IconImport"; case ImpressClickAction: return "ImpressClickAction"; case ImpressExport: return "ImpressExport"; case ImpressPhotoDialog: return "ImpressPhotoDialog"; case ImpressSaveAs: return "ImpressSaveAs"; case ImageMap: return "ImageMap"; case InsertDoc: return "InsertDoc"; case InsertImage: return "InsertImage"; case InsertOLE: return "InsertOLE"; case InsertMedia: return "InsertMedia"; case JavaClassPath: return "JavaClassPath"; case ReportInsertImage: return "ReportInsertImage"; case ScreenshotAnnotation: return "ScreenshotAnnotation"; case SignatureLine: return "SignatureLine"; case TemplateImport: return "TemplateImport"; case WriterCreateAddressList: return "WriterCreateAddressList"; case WriterExport: return "WriterExport"; case WriterImportAutotext: return "WriterImportAutotext"; case WriterInsertHyperlink: return "WriterInsertHyperlink"; case WriterInsertImage: return "WriterInsertImage"; case WriterInsertScript: return "WriterInsertScript"; case WriterLoadTemplate: return "WriterLoadTemplate"; case WriterMailMerge: return "WriterMailMerge"; case WriterMailMergeSaveAs: return "WriterMailMergeSaveAs"; case WriterNewHTMLGlobalDoc: return "WriterNewHTMLGlobalDoc"; case WriterRegisterDataSource: return "WriterRegisterDataSource"; case WriterSaveAs: return "WriterSaveAs"; case WriterSaveHTML: return "WriterSaveHTML"; case XMLFilterSettings: return "XMLFilterSettings"; case UnknownContext: default: return ""; } } IMPL_LINK_NOARG(FileDialogHelper, ExecuteSystemFilePicker, void*, void) { m_nError = mpImpl->execute(); m_aDialogClosedLink.Call( this ); } // rDirPath has to be a directory ErrCode FileDialogHelper::Execute( std::vector& rpURLList, std::optional& rpSet, OUString& rFilter, const OUString& rDirPath ) { SetDisplayFolder( rDirPath ); return mpImpl->execute( rpURLList, rpSet, rFilter ); } ErrCode FileDialogHelper::Execute() { return mpImpl->execute(); } ErrCode FileDialogHelper::Execute( std::optional& rpSet, OUString& rFilter ) { ErrCode nRet; std::vector rURLList; nRet = mpImpl->execute(rURLList, rpSet, rFilter); return nRet; } void FileDialogHelper::StartExecuteModal( const Link& rEndDialogHdl ) { m_aDialogClosedLink = rEndDialogHdl; m_nError = ERRCODE_NONE; if (!mpImpl->isAsyncFilePicker()) Application::PostUserEvent( LINK( this, FileDialogHelper, ExecuteSystemFilePicker ) ); else mpImpl->implStartExecute(); } sal_Int16 FileDialogHelper::GetDialogType() const { return mpImpl ? mpImpl->m_nDialogType : 0; } bool FileDialogHelper::IsPasswordEnabled() const { return mpImpl && mpImpl->isPasswordEnabled(); } OUString FileDialogHelper::GetRealFilter() const { OUString sFilter; if (mpImpl) mpImpl->getRealFilter( sFilter ); return sFilter; } void FileDialogHelper::SetTitle( const OUString& rNewTitle ) { if ( mpImpl->mxFileDlg.is() ) mpImpl->mxFileDlg->setTitle( rNewTitle ); } OUString FileDialogHelper::GetPath() const { OUString aPath; if ( !mpImpl->mlLastURLs.empty()) return mpImpl->mlLastURLs[0]; if ( mpImpl->mxFileDlg.is() ) { Sequence < OUString > aPathSeq = mpImpl->mxFileDlg->getFiles(); if ( aPathSeq.getLength() == 1 ) { aPath = aPathSeq[0]; } } return aPath; } Sequence < OUString > FileDialogHelper::GetMPath() const { if ( !mpImpl->mlLastURLs.empty()) return comphelper::containerToSequence(mpImpl->mlLastURLs); if ( mpImpl->mxFileDlg.is() ) return mpImpl->mxFileDlg->getFiles(); else { Sequence < OUString > aEmpty; return aEmpty; } } Sequence< OUString > FileDialogHelper::GetSelectedFiles() const { // a) the new way (optional!) uno::Sequence< OUString > aResultSeq; if (mpImpl->mxFileDlg.is()) { aResultSeq = mpImpl->mxFileDlg->getSelectedFiles(); } // b) the olde way ... non optional. else { uno::Reference< XFilePicker > xPickOld(mpImpl->mxFileDlg, UNO_QUERY_THROW); Sequence< OUString > lFiles = xPickOld->getFiles(); ::sal_Int32 nFiles = lFiles.getLength(); if ( nFiles > 1 ) { aResultSeq = Sequence< OUString >( nFiles-1 ); auto pResultSeq = aResultSeq.getArray(); INetURLObject aPath( lFiles[0] ); aPath.setFinalSlash(); for (::sal_Int32 i = 1; i < nFiles; i++) { if (i == 1) aPath.Append( lFiles[i] ); else aPath.setName( lFiles[i] ); pResultSeq[i-1] = aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ); } } else aResultSeq = lFiles; } return aResultSeq; } OUString FileDialogHelper::GetDisplayDirectory() const { return mpImpl->getPath(); } OUString FileDialogHelper::GetCurrentFilter() const { return mpImpl->getFilter(); } ErrCode FileDialogHelper::GetGraphic( Graphic& rGraphic ) const { return mpImpl->getGraphic( rGraphic ); } static int impl_isFolder( const OUString& rPath ) { try { ::ucbhelper::Content aContent( rPath, uno::Reference< ucb::XCommandEnvironment > (), comphelper::getProcessComponentContext() ); if ( aContent.isFolder() ) return 1; return 0; } catch ( const Exception & ) { } return -1; } void FileDialogHelper::SetDisplayDirectory( const OUString& _rPath ) { if ( _rPath.isEmpty() ) return; // if the given path isn't a folder, we cut off the last part // and take it as filename and the rest of the path should be // the folder INetURLObject aObj( _rPath ); OUString sFileName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset); aObj.removeSegment(); OUString sPath = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); int nIsFolder = impl_isFolder( _rPath ); if ( nIsFolder == 0 || ( nIsFolder == -1 && impl_isFolder( sPath ) == 1 ) ) { mpImpl->setFileName( sFileName ); mpImpl->displayFolder( sPath ); } else { INetURLObject aObjPathName( _rPath ); OUString sFolder( aObjPathName.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); if ( sFolder.isEmpty() ) { // _rPath is not a valid path -> fallback to home directory osl::Security aSecurity; aSecurity.getHomeDir( sFolder ); } mpImpl->displayFolder( sFolder ); } } void FileDialogHelper::SetDisplayFolder( const OUString& _rURL ) { mpImpl->displayFolder( _rURL ); } void FileDialogHelper::SetFileName( const OUString& _rFileName ) { mpImpl->setFileName( _rFileName ); } void FileDialogHelper::AddFilter( const OUString& rFilterName, const OUString& rExtension ) { mpImpl->addFilter( rFilterName, rExtension ); } void FileDialogHelper::SetCurrentFilter( const OUString& rFilter ) { OUString sFilter( rFilter ); if ( mpImpl->isShowFilterExtensionEnabled() ) sFilter = mpImpl->getFilterWithExtension( rFilter ); mpImpl->setFilter( sFilter ); } const uno::Reference < XFilePicker3 >& FileDialogHelper::GetFilePicker() const { return mpImpl->mxFileDlg; } // XFilePickerListener Methods void FileDialogHelper::FileSelectionChanged() { mpImpl->handleFileSelectionChanged(); } void FileDialogHelper::DirectoryChanged() { mpImpl->handleDirectoryChanged(); } OUString FileDialogHelper::HelpRequested( const FilePickerEvent& aEvent ) { return sfx2::FileDialogHelper_Impl::handleHelpRequested( aEvent ); } void FileDialogHelper::ControlStateChanged( const FilePickerEvent& aEvent ) { mpImpl->handleControlStateChanged( aEvent ); } void FileDialogHelper::DialogSizeChanged() { mpImpl->handleDialogSizeChanged(); } void FileDialogHelper::DialogClosed( const DialogClosedEvent& _rEvent ) { m_nError = ( RET_OK == _rEvent.DialogResult ) ? ERRCODE_NONE : ERRCODE_ABORT; m_aDialogClosedLink.Call( this ); } ErrCode FileOpenDialog_Impl( weld::Window* pParent, sal_Int16 nDialogType, FileDialogFlags nFlags, std::vector& rpURLList, OUString& rFilter, std::optional& rpSet, const OUString* pPath, sal_Int16 nDialog, const OUString& rStandardDir, const css::uno::Sequence< OUString >& rDenyList ) { ErrCode nRet; std::unique_ptr pDialog; // Sign existing PDF: only works with PDF files and they are opened // read-only to discourage editing (which would invalidate existing // signatures). if (nFlags & FileDialogFlags::SignPDF) pDialog.reset(new FileDialogHelper(nDialogType, nFlags, SfxResId(STR_SFX_FILTERNAME_PDF), u"pdf", rStandardDir, rDenyList, pParent)); else pDialog.reset(new FileDialogHelper(nDialogType, nFlags, OUString(), nDialog, SfxFilterFlags::NONE, SfxFilterFlags::NONE, rStandardDir, rDenyList, pParent)); OUString aPath; if ( pPath ) aPath = *pPath; nRet = pDialog->Execute(rpURLList, rpSet, rFilter, aPath); DBG_ASSERT( rFilter.indexOf(": ") == -1, "Old filter name used!"); if (rpSet && nFlags & FileDialogFlags::SignPDF) rpSet->Put(SfxBoolItem(SID_DOC_READONLY, true)); return nRet; } bool IsMSType(const std::shared_ptr& pCurrentFilter) { // TODO: need a save way to distinguish MS filters from other filters // for now MS-filters are the only alien filters that support encryption return !pCurrentFilter->IsOwnFormat(); } bool IsOOXML(const std::shared_ptr& pCurrentFilter) { // For OOXML we can use the standard password ("unlimited" characters) return IsMSType(pCurrentFilter) && lclSupportsOOXMLEncryption( pCurrentFilter->GetFilterName()); } ErrCode SetPassword(const std::shared_ptr& pCurrentFilter, SfxItemSet* pSet, const OUString& rPasswordToOpen, std::u16string_view rPasswordToModify, bool bAllowPasswordReset) { const bool bMSType = IsMSType(pCurrentFilter); const bool bOOXML = IsOOXML(pCurrentFilter); if ( rPasswordToOpen.getLength() ) { css::uno::Sequence< css::beans::NamedValue > aEncryptionData; if ( bMSType ) { if (bOOXML) { ::comphelper::SequenceAsHashMap aHashData; aHashData[ OUString( "OOXPassword" ) ] <<= rPasswordToOpen; aHashData[ OUString( "CryptoType" ) ] <<= OUString( "Standard" ); aEncryptionData = aHashData.getAsConstNamedValueList(); } else { uno::Sequence< sal_Int8 > aUniqueID = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 ); uno::Sequence< sal_Int8 > aEncryptionKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( rPasswordToOpen, aUniqueID ); if ( aEncryptionKey.hasElements() ) { ::comphelper::SequenceAsHashMap aHashData; aHashData[ OUString( "STD97EncryptionKey" ) ] <<= aEncryptionKey; aHashData[ OUString( "STD97UniqueID" ) ] <<= aUniqueID; aEncryptionData = aHashData.getAsConstNamedValueList(); } else { return ERRCODE_IO_NOTSUPPORTED; } } } // tdf#118639: We need ODF encryption data for autorecovery where password will already // be unavailable, even for non-ODF documents, so append it here unconditionally pSet->Put(SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any(comphelper::concatSequences( aEncryptionData, comphelper::OStorageHelper::CreatePackageEncryptionData( rPasswordToOpen))))); } else if (bAllowPasswordReset) { // Remove password if (pSet->HasItem(SID_ENCRYPTIONDATA)) pSet->ClearItem(SID_MODIFYPASSWORDINFO); if (pSet->HasItem(SID_ENCRYPTIONDATA)) pSet->ClearItem(SID_ENCRYPTIONDATA); return ERRCODE_NONE; } if ( bMSType ) { if (bOOXML) { uno::Sequence aModifyPasswordInfo = ::comphelper::DocPasswordHelper::GenerateNewModifyPasswordInfoOOXML( rPasswordToModify); if (aModifyPasswordInfo.hasElements() && pSet) pSet->Put( SfxUnoAnyItem(SID_MODIFYPASSWORDINFO, uno::Any(aModifyPasswordInfo))); } else { // the empty password has 0 as Hash sal_Int32 nHash = SfxMedium::CreatePasswordToModifyHash( rPasswordToModify, pCurrentFilter->GetServiceName() == "com.sun.star.text.TextDocument"); if (nHash && pSet) pSet->Put(SfxUnoAnyItem(SID_MODIFYPASSWORDINFO, uno::Any(nHash))); } } else { uno::Sequence< beans::PropertyValue > aModifyPasswordInfo = ::comphelper::DocPasswordHelper::GenerateNewModifyPasswordInfo( rPasswordToModify ); if ( aModifyPasswordInfo.hasElements() && pSet) pSet->Put( SfxUnoAnyItem( SID_MODIFYPASSWORDINFO, uno::Any( aModifyPasswordInfo ) ) ); } return ERRCODE_NONE; } ErrCode RequestPassword(const std::shared_ptr& pCurrentFilter, OUString const & aURL, SfxItemSet* pSet, const css::uno::Reference& rParent) { uno::Reference xInteractionHandler = task::InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), rParent); const auto eType = IsMSType(pCurrentFilter) && !IsOOXML(pCurrentFilter) ? ::comphelper::DocPasswordRequestType::MS : ::comphelper::DocPasswordRequestType::Standard; ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest( new ::comphelper::DocPasswordRequest( eType, css::task::PasswordRequestMode_PASSWORD_CREATE, aURL, bool( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY ) ) ); const bool bMSType = IsMSType(pCurrentFilter); uno::Reference< css::task::XInteractionRequest > rRequest( pPasswordRequest ); do { xInteractionHandler->handle( rRequest ); if (!pPasswordRequest->isPassword() || bMSType) { break; } OString const utf8Pwd(OUStringToOString(pPasswordRequest->getPassword(), RTL_TEXTENCODING_UTF8)); OString const utf8Ptm(OUStringToOString(pPasswordRequest->getPasswordToModify(), RTL_TEXTENCODING_UTF8)); if (!(52 <= utf8Pwd.getLength() && utf8Pwd.getLength() <= 55 && GetODFSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012) && (52 > utf8Ptm.getLength() || utf8Ptm.getLength() > 55)) { break; } std::unique_ptr xBox(Application::CreateMessageDialog(Application::GetFrameWeld(rParent), VclMessageType::Warning, VclButtonsType::Ok, SfxResId(STR_PASSWORD_LEN))); xBox->set_secondary_text(SfxResId(STR_PASSWORD_WARNING)); xBox->run(); } while (true); if ( !pPasswordRequest->isPassword() ) return ERRCODE_ABORT; const auto result = SetPassword(pCurrentFilter, pSet, pPasswordRequest->getPassword(), pPasswordRequest->getPasswordToModify()); if ( result != ERRCODE_IO_NOTSUPPORTED && pPasswordRequest->getRecommendReadOnly() ) pSet->Put( SfxBoolItem( SID_RECOMMENDREADONLY, true ) ); return result; } OUString EncodeSpaces_Impl( const OUString& rSource ) { OUString sRet = rSource.replaceAll( " ", "%20" ); return sRet; } OUString DecodeSpaces_Impl( const OUString& rSource ) { OUString sRet = rSource.replaceAll( "%20", " " ); return sRet; } } // end of namespace sfx2 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */