diff options
Diffstat (limited to 'sfx2/source/dialog/filedlghelper.cxx')
-rw-r--r-- | sfx2/source/dialog/filedlghelper.cxx | 3029 |
1 files changed, 3029 insertions, 0 deletions
diff --git a/sfx2/source/dialog/filedlghelper.cxx b/sfx2/source/dialog/filedlghelper.cxx new file mode 100644 index 0000000000..96955837c8 --- /dev/null +++ b/sfx2/source/dialog/filedlghelper.cxx @@ -0,0 +1,3029 @@ +/* -*- 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 <memory> +#include <optional> +#include <string_view> + +#include <sfx2/filedlghelper.hxx> +#include <sal/types.h> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/ControlActions.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp> +#include <com/sun/star/ui/dialogs/FolderPicker.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XControlInformation.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/XFilePreview.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/container/XContainerQuery.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/util/RevisionTag.hpp> +#include <comphelper/fileurl.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/string.hxx> +#include <comphelper/types.hxx> +#include <tools/urlobj.hxx> +#include <vcl/help.hxx> +#include <vcl/weld.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/ucbhelper.hxx> +#include <osl/file.hxx> +#include <osl/security.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/svapp.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/saveopt.hxx> +#include <unotools/securityoptions.hxx> +#include <svl/itemset.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <vcl/dibtools.hxx> +#include <vcl/graphicfilter.hxx> +#include <unotools/viewoptions.hxx> +#include <svtools/helpids.h> +#include <comphelper/docpasswordrequest.hxx> +#include <comphelper/docpasswordhelper.hxx> +#include <ucbhelper/content.hxx> +#include <comphelper/storagehelper.hxx> +#include <sfx2/app.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/sfxresid.hxx> +#include <sfx2/sfxsids.hrc> +#include "filtergrouping.hxx" +#include "filedlgimpl.hxx" +#include <sfx2/strings.hrc> +#include <sal/log.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <o3tl/string_view.hxx> +#include <officecfg/Office/Common.hxx> + +#ifdef UNX +#include <errno.h> +#include <sys/stat.h> +#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<OUString> 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<weld::Widget*>(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<OUString> pConfigId = GetLastFilterConfigId( meContext ); + if( pConfigId ) + SvtViewOptions( EViewType::Dialog, IODLG_CONFIGNAME ).SetUserItem( *pConfigId, + Any( getFilterWithExtension( getFilter() ) ) ); +} + +std::shared_ptr<const SfxFilter> 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<const SfxFilter>& _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 : std::as_const(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<const SfxFilter> 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<const SfxFilter> 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<double>(nOutWidth) / nBmpWidth; + double nYRatio = static_cast<double>(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<SvStream> 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<OUString> 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<XAsynchronousExecutableDialog> 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<css::awt::XWindow> FileDialogHelper_Impl::GetFrameInterface() +{ + if (mpFrameWeld) + return mpFrameWeld->GetXWindow(); + return css::uno::Reference<css::awt::XWindow>(); +} + +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<css::ui::dialogs::XFolderPicker2> createFolderPicker(const css::uno::Reference<css::uno::XComponentContext>& 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<Any> 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<OUString>& 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<OUString>& rpURLList, + std::optional<SfxAllItemSet>& 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<SfxBoolItem>(&*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<SfxUnoAnyItem>(&*rpSet, SID_MODIFYPASSWORDINFO, false); + mbPwdCheckBoxState |= ( pPassToModifyItem && pPassToModifyItem->GetValue().hasValue() ); + } + + const SfxBoolItem* pSelectItem = SfxItemSet::GetItem<SfxBoolItem>(&*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<short>(nVersion) ) ); + } + catch( const IllegalArgumentException& ){} + } + + // set the filter + getRealFilter( rFilter ); + + std::shared_ptr<const SfxFilter> 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<weld::MessageDialog> 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) ) ); + } + } + 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<const SfxFilter> 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<const SfxFilter> 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<sal_Int32>(m_nMustFlags)) + + ":eflags=" + + OUString::number(static_cast<sal_Int32>(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<comphelper::ConfigurationChanges> batch( + comphelper::ConfigurationChanges::create()); + Reference<container::XNameContainer> 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<XPropertySet> el(v.get<Reference<XPropertySet>>(), UNO_SET_THROW); + el->setPropertyValue("LastPath", Any(sDirectory)); + } + else + { + Reference<XPropertySet> el( + (Reference<lang::XSingleServiceFactory>(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<sal_Int32>(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<sal_Int32>(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<sal_Int32>(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<XNameAccess> set(officecfg::Office::Common::Misc::FilePickerLastDirectory::get()); + Any v; + try + { + v = set->getByName(sContext); + Reference<XPropertySet> el(v.get<Reference<XPropertySet>>(), UNO_SET_THROW); + sPath = el->getPropertyValue("LastPath").get<OUString>(); + } + 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<bool>(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<bool>(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 <all> + 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<OUString> 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<OUString>& rpURLList, + std::optional<SfxAllItemSet>& 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<SfxAllItemSet>& rpSet, + OUString& rFilter ) +{ + ErrCode nRet; + std::vector<OUString> rURLList; + nRet = mpImpl->execute(rURLList, rpSet, rFilter); + return nRet; +} + +void FileDialogHelper::StartExecuteModal( const Link<FileDialogHelper*,void>& 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<OUString>& rpURLList, + OUString& rFilter, + std::optional<SfxAllItemSet>& rpSet, + const OUString* pPath, + sal_Int16 nDialog, + const OUString& rStandardDir, + const css::uno::Sequence< OUString >& rDenyList ) +{ + ErrCode nRet; + std::unique_ptr<FileDialogHelper> 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<const SfxFilter>& 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<const SfxFilter>& pCurrentFilter) +{ + // For OOXML we can use the standard password ("unlimited" characters) + return IsMSType(pCurrentFilter) && lclSupportsOOXMLEncryption( pCurrentFilter->GetFilterName()); +} + +ErrCode SetPassword(const std::shared_ptr<const SfxFilter>& 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<beans::PropertyValue> 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<const SfxFilter>& pCurrentFilter, OUString const & aURL, SfxItemSet* pSet, const css::uno::Reference<css::awt::XWindow>& rParent) +{ + uno::Reference<task::XInteractionHandler2> 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<weld::MessageDialog> 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: */ |