/* -*- 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 <fpicker/fpsofficeResMgr.hxx>
#include <fpicker/strings.hrc>
#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>
 
#include <config_gpgme.h>
#if HAVE_FEATURE_GPGME
# include <com/sun/star/xml/crypto/SEInitializer.hpp>
# include <com/sun/star/xml/crypto/GPGSEInitializer.hpp>
# include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp>
#endif
#include <comphelper/xmlsechelper.hxx>
#include <unotools/useroptions.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 )
{
    TranslateId aHelpId;
 
    // mapping from element id -> help id
    switch ( aEvent.ElementId )
    {
        case ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION :
            aHelpId = STR_FILESAVE_AUTOEXTENSION;
            break;
 
        case ExtendedFilePickerElementIds::CHECKBOX_PASSWORD :
            aHelpId = STR_FILESAVE_SAVEWITHPASSWORD;
            break;
 
        case ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS :
            aHelpId = STR_FILESAVE_CUSTOMIZEFILTER;
            break;
 
        case ExtendedFilePickerElementIds::CHECKBOX_READONLY :
            aHelpId = STR_FILEOPEN_READONLY;
            break;
 
        case ExtendedFilePickerElementIds::CHECKBOX_LINK :
            aHelpId = STR_FILEDLG_LINK_CB;
            break;
 
        case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW :
            aHelpId = STR_FILEDLG_PREVIEW_CB;
            break;
 
        case ExtendedFilePickerElementIds::PUSHBUTTON_PLAY :
            aHelpId = STR_FILESAVE_DOPLAY;
            break;
 
        case ExtendedFilePickerElementIds::LISTBOX_VERSION_LABEL :
        case ExtendedFilePickerElementIds::LISTBOX_VERSION :
            aHelpId = STR_FILEOPEN_VERSION;
            break;
 
        case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE_LABEL :
        case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE :
            aHelpId = STR_FILESAVE_TEMPLATE;
            break;
 
        case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE_LABEL :
        case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE :
            aHelpId = STR_FILEOPEN_IMAGE_TEMPLATE;
            break;
 
        case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR_LABEL :
        case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR :
            aHelpId = STR_FILEOPEN_IMAGE_ANCHOR;
            break;
 
        case ExtendedFilePickerElementIds::CHECKBOX_SELECTION :
            aHelpId = STR_FILESAVE_SELECTION;
            break;
 
        default:
            SAL_WARN( "sfx.dialog", "invalid element id" );
    }
 
    return aHelpId ? FpsResId(aHelpId) : OUString();
}
 
void FileDialogHelper_Impl::handleControlStateChanged( const FilePickerEvent& aEvent )
{
    switch ( aEvent.ElementId )
    {
        case CommonFilePickerElementIds::LISTBOX_FILTER:
            updateFilterOptionsBox();
            enablePasswordBox( false );
            enableGpgEncrBox( false );
            updateSelectionBox();
            updateSignByDefault();
            // 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 : 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::updateSignByDefault()
{
#if HAVE_FEATURE_GPGME
    if (!mbHasSignByDefault)
        return;
 
    OUString aSigningKey = SvtUserOptions{}.GetSigningKey();
    updateExtendedControl(ExtendedFilePickerElementIds::CHECKBOX_GPGSIGN, !aSigningKey.isEmpty());
#endif
}
 
void FileDialogHelper_Impl::enablePasswordBox( bool bInit )
{
    if ( ! mbHasPassword )
        return;
 
    // in case of initialization assume previous state to be false
    bool bWasEnabled = !bInit && mbIsPwdEnabled;
 
    std::shared_ptr<const SfxFilter> pCurrentFilter = getCurrentSfxFilter();
    mbIsPwdEnabled = updateExtendedControl(
        ExtendedFilePickerElementIds::CHECKBOX_PASSWORD,
        pCurrentFilter && ( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::ENCRYPTION )
    );
 
    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::enableGpgEncrBox( bool bInit )
{
    if ( ! mbHasPassword )  // CHECKBOX_GPGENCRYPTION is visible if CHECKBOX_PASSWORD is visible
        return;
 
    // in case of initialization assume previous state to be false
    bool bWasEnabled = !bInit && mbIsGpgEncrEnabled;
 
    std::shared_ptr<const SfxFilter> pCurrentFilter = getCurrentSfxFilter();
    mbIsGpgEncrEnabled = updateExtendedControl(
        ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION,
        pCurrentFilter && ( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::GPGENCRYPTION )
    );
 
    if( !bWasEnabled && mbIsGpgEncrEnabled )
    {
        uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
        if( mbGpgCheckBoxState )
            xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION, 0, Any( true ) );
    }
    else if( bWasEnabled && !mbIsGpgEncrEnabled )
    {
        // remember user settings until checkbox is enabled
        uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
        Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION, 0 );
        bool bGpgEncryption = false;
        mbGpgCheckBoxState = ( aValue >>= bGpgEncryption ) && bGpgEncryption;
        xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION, 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 ) )
    {
        const 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( u"com.sun.star.ui.dialogs.SystemFilePicker"_ustr );
    }
    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;
    mbHasSignByDefault      = false;
    m_bHaveFilterOptions    = false;
    mbIsPwdEnabled          = true;
    mbIsGpgEncrEnabled      = 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;
    mbGpgCheckBoxState      = 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;
 
    // 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;
                mbHasSignByDefault = 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( u"com.sun.star.document.FilterFactory"_ustr ),
                        UNO_QUERY );
                }
 
                mbHasAutoExt = true;
                mbIsSaveDlg = true;
                mbHasSignByDefault = 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( u"com.sun.star.document.FilterFactory"_ustr ),
                        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(
                                    u"TemplateDescription"_ustr,
                                    Any( nTemplateDescription )
                                );
 
            pInitArguments[1] <<= NamedValue(
                                    u"StandardDir"_ustr,
                                    Any( sStandardDir )
                                );
 
            pInitArguments[2] <<= NamedValue(
                                    u"DenyList"_ustr,
                                    Any( rDenyList )
                                );
 
 
            if (xWindow.is())
                pInitArguments[3] <<= NamedValue(u"ParentWindow"_ustr, 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()
{
    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!" );
    }
}
 
void FileDialogHelper_Impl::preExecute()
{
    loadConfig( );
    setDefaultValues( );
    updatePreviewState( false );
 
    implInitializeFileName( );
 
    enablePasswordBox( true );
    enableGpgEncrBox( true );
    updateFilterOptionsBox( );
    updateSelectionBox( );
    updateSignByDefault();
}
 
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);
            // TODO: tdf#158839 problem: Is also true if the file is GPG encrypted. (not with a password)
            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
                    const OUString& aDocName(rpURLList[0]);
                    // TODO: tdf#158839 problem: Also asks for a password if CHECKBOX_GPGENCRYPTION && CHECKBOX_PASSWORD
                    //       are checked. But only encrypts using GPG and discards the password.
                    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(
                                    GetFrameInterface());
                            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) ) );
                    else
                        return ERRCODE_ABORT;
                }
            }
            catch( const IllegalArgumentException& ){}
        }
        if ( pCurrentFilter && xCtrlAccess.is() )
        {
            try
            {
                Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_GPGSIGN, 0 );
                bool bSign = false;
                if ((aValue >>= bSign) && bSign)
                {
                    rpSet->Put(SfxBoolItem(SID_GPGSIGN, bSign));
                }
            }
            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(u"com.sun.star.document.FilterFactory"_ustr),
        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(u"LastPath"_ustr, Any(sDirectory));
    }
    else
    {
        Reference<XPropertySet> el(
            (Reference<lang::XSingleServiceFactory>(set, UNO_QUERY_THROW)->createInstance()),
            UNO_QUERY_THROW);
        el->setPropertyValue(u"LastPath"_ustr, 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(u"LastPath"_ustr).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 u"AcceleratorConfig"_ustr;
        case AutoRedact:
            return u"AutoRedact"_ustr;
        case BaseDataSource:
            return u"BaseDataSource"_ustr;
        case BaseSaveAs:
            return u"BaseSaveAs"_ustr;
        case BasicExportDialog:
            return u"BasicExportDialog"_ustr;
        case BasicExportPackage:
            return u"BasicExportPackage"_ustr;
        case BasicExportSource:
            return u"BasicExportSource"_ustr;
        case BasicImportDialog:
            return u"BasicImportDialog"_ustr;
        case BasicImportSource:
            return u"BasicImportSource"_ustr;
        case BasicInsertLib:
            return u"BasicInsertLib"_ustr;
        case BulletsAddImage:
            return u"BulletsAddImage"_ustr;
        case CalcDataProvider:
            return u"CalcDataProvider"_ustr;
        case CalcDataStream:
            return u"CalcDataStream"_ustr;
        case CalcExport:
            return u"CalcExport"_ustr;
        case CalcSaveAs:
            return u"CalcSaveAs"_ustr;
        case CalcXMLSource:
            return u"CalcXMLSource"_ustr;
        case ExportImage:
            return u"ExportImage"_ustr;
        case ExtensionManager:
            return u"ExtensionManager"_ustr;
        case FormsAddInstance:
            return u"FormsAddInstance"_ustr;
        case FormsInsertImage:
            return u"FormsInsertImage"_ustr;
        case LinkClientOLE:
            return u"LinkClientOLE"_ustr;
        case LinkClientFile:
            return u"LinkClientFile"_ustr;
        case DrawImpressInsertFile:
            return u"DrawImpressInsertFile"_ustr;
        case DrawImpressOpenSound:
            return u"DrawImpressOpenSound"_ustr;
        case DrawExport:
            return u"DrawExport"_ustr;
        case DrawSaveAs:
            return u"DrawSaveAs"_ustr;
        case IconImport:
            return u"IconImport"_ustr;
        case ImpressClickAction:
            return u"ImpressClickAction"_ustr;
        case ImpressExport:
            return u"ImpressExport"_ustr;
        case ImpressPhotoDialog:
            return u"ImpressPhotoDialog"_ustr;
        case ImpressSaveAs:
            return u"ImpressSaveAs"_ustr;
        case ImageMap:
            return u"ImageMap"_ustr;
        case InsertDoc:
            return u"InsertDoc"_ustr;
        case InsertImage:
            return u"InsertImage"_ustr;
        case InsertOLE:
            return u"InsertOLE"_ustr;
        case InsertMedia:
            return u"InsertMedia"_ustr;
        case JavaClassPath:
            return u"JavaClassPath"_ustr;
        case ReportInsertImage:
            return u"ReportInsertImage"_ustr;
        case ScreenshotAnnotation:
            return u"ScreenshotAnnotation"_ustr;
        case SignatureLine:
            return u"SignatureLine"_ustr;
        case TemplateImport:
            return u"TemplateImport"_ustr;
        case WriterCreateAddressList:
            return u"WriterCreateAddressList"_ustr;
        case WriterExport:
            return u"WriterExport"_ustr;
        case WriterImportAutotext:
            return u"WriterImportAutotext"_ustr;
        case WriterInsertImage:
            return u"WriterInsertImage"_ustr;
        case WriterInsertScript:
            return u"WriterInsertScript"_ustr;
        case WriterLoadTemplate:
            return u"WriterLoadTemplate"_ustr;
        case WriterMailMerge:
            return u"WriterMailMerge"_ustr;
        case WriterMailMergeSaveAs:
            return u"WriterMailMergeSaveAs"_ustr;
        case WriterNewHTMLGlobalDoc:
            return u"WriterNewHTMLGlobalDoc"_ustr;
        case WriterRegisterDataSource:
            return u"WriterRegisterDataSource"_ustr;
        case WriterSaveAs:
            return u"WriterSaveAs"_ustr;
        case WriterSaveHTML:
            return u"WriterSaveHTML"_ustr;
        case XMLFilterSettings:
            return u"XMLFilterSettings"_ustr;
        case UnknownContext:
        default:
            return u""_ustr;
    }
}
 
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 = std::move(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[ u"OOXPassword"_ustr ] <<= rPasswordToOpen;
                aHashData[ u"CryptoType"_ustr ] <<= u"Standard"_ustr;
                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[ u"STD97EncryptionKey"_ustr ] <<= aEncryptionKey;
                    aHashData[ u"STD97UniqueID"_ustr ] <<= 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: */

V595 The 'pSet' pointer was utilized before it was verified against nullptr. Check lines: 2975, 2987.

V547 Expression 'bAutoExtChecked' is always false.

V1019 Compound assignment expression is used inside condition.