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