LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / doc / guisaveas.cxx
blob7a831f07a8573f897d00ea18a257f1b292d107c6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
21 #include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
22 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
23 #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
24 #include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
25 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
26 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
27 #include <com/sun/star/view/XSelectionSupplier.hpp>
28 #include <com/sun/star/beans/PropertyExistException.hpp>
29 #include <com/sun/star/beans/XPropertyAccess.hpp>
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <com/sun/star/beans/XPropertyContainer.hpp>
32 #include <com/sun/star/beans/PropertyAttribute.hpp>
33 #include <com/sun/star/document/XExporter.hpp>
34 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
35 #include <com/sun/star/document/XDocumentProperties.hpp>
36 #include <com/sun/star/task/ErrorCodeIOException.hpp>
37 #include <com/sun/star/task/InteractionHandler.hpp>
38 #include <com/sun/star/util/URLTransformer.hpp>
39 #include <com/sun/star/util/XURLTransformer.hpp>
40 #include <com/sun/star/frame/ModuleManager.hpp>
41 #include <com/sun/star/frame/XStorable.hpp>
42 #include <com/sun/star/frame/XStorable2.hpp>
43 #include <com/sun/star/frame/XDispatchProvider.hpp>
44 #include <com/sun/star/frame/XDispatch.hpp>
45 #include <com/sun/star/frame/XTitle.hpp>
46 #include <com/sun/star/util/XModifiable.hpp>
47 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
49 #include <com/sun/star/util/XCloneable.hpp>
51 #include <guisaveas.hxx>
53 #include <sal/log.hxx>
54 #include <unotools/pathoptions.hxx>
55 #include <svl/itemset.hxx>
56 #include <svl/eitem.hxx>
57 #include <unotools/saveopt.hxx>
58 #include <tools/debug.hxx>
59 #include <tools/diagnose_ex.h>
60 #include <tools/urlobj.hxx>
61 #include <tools/json_writer.hxx>
62 #include <tools/urlobj.hxx>
63 #include <comphelper/processfactory.hxx>
64 #include <comphelper/propertysequence.hxx>
65 #include <comphelper/propertyvalue.hxx>
66 #include <comphelper/sequenceashashmap.hxx>
67 #include <comphelper/mimeconfighelper.hxx>
68 #include <comphelper/lok.hxx>
69 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
70 #include <vcl/svapp.hxx>
71 #include <vcl/weld.hxx>
72 #include <o3tl/char16_t2wchar_t.hxx>
73 #include <unotools/tempfile.hxx>
75 #include <sfx2/sfxsids.hrc>
76 #include <sfx2/strings.hrc>
77 #include <sfx2/sfxresid.hxx>
78 #include <sfx2/filedlghelper.hxx>
79 #include <sfx2/app.hxx>
80 #include <sfx2/sfxuno.hxx>
81 #include <sfx2/viewsh.hxx>
82 #include <sfx2/bindings.hxx>
83 #include <alienwarn.hxx>
85 #include <memory>
86 #include <string_view>
88 #include <officecfg/Office/Common.hxx>
90 #include <vcl/FilterConfigItem.hxx>
91 #include <com/sun/star/system/SystemShellExecute.hpp>
92 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
94 #include <osl/file.hxx>
96 #ifdef _WIN32
97 #include <Shlobj.h>
98 #ifdef GetTempPath
99 #undef GetTempPath
100 #endif
101 #endif
103 // flags that specify requested operation
104 #define EXPORT_REQUESTED 1
105 #define PDFEXPORT_REQUESTED 2
106 #define PDFDIRECTEXPORT_REQUESTED 4
107 #define WIDEEXPORT_REQUESTED 8
108 #define SAVE_REQUESTED 16
109 #define SAVEAS_REQUESTED 32
110 #define SAVEACOPY_REQUESTED 64
111 #define EPUBEXPORT_REQUESTED 128
112 #define EPUBDIRECTEXPORT_REQUESTED 256
113 #define SAVEASREMOTE_REQUESTED -1
115 // possible statuses of save operation
116 #define STATUS_NO_ACTION 0
117 #define STATUS_SAVE 1
118 #define STATUS_SAVEAS 2
119 #define STATUS_SAVEAS_STANDARDNAME 3
121 constexpr OUStringLiteral aFilterNameString = u"FilterName";
122 constexpr OUStringLiteral aFilterOptionsString = u"FilterOptions";
123 constexpr OUStringLiteral aFilterDataString = u"FilterData";
125 using namespace ::com::sun::star;
126 using namespace css::system;
128 namespace {
130 sal_uInt16 getSlotIDFromMode( sal_Int16 nStoreMode )
132 // This is a temporary hardcoded solution must be removed when
133 // dialogs do not need parameters in SidSet representation any more
135 sal_uInt16 nResult = 0;
136 if ( nStoreMode == EXPORT_REQUESTED || nStoreMode == ( EXPORT_REQUESTED | SAVEACOPY_REQUESTED | WIDEEXPORT_REQUESTED ) )
137 nResult = SID_EXPORTDOC;
138 else if ( nStoreMode == ( EXPORT_REQUESTED | PDFEXPORT_REQUESTED ) )
139 nResult = SID_EXPORTDOCASPDF;
140 else if ( nStoreMode == ( EXPORT_REQUESTED | EPUBEXPORT_REQUESTED ) )
141 nResult = SID_EXPORTDOCASEPUB;
142 else if ( nStoreMode == ( EXPORT_REQUESTED | PDFEXPORT_REQUESTED | PDFDIRECTEXPORT_REQUESTED ) )
143 nResult = SID_DIRECTEXPORTDOCASPDF;
144 else if ( nStoreMode == ( EXPORT_REQUESTED | EPUBEXPORT_REQUESTED | EPUBDIRECTEXPORT_REQUESTED ) )
145 nResult = SID_DIRECTEXPORTDOCASEPUB;
146 else if ( nStoreMode == SAVEAS_REQUESTED || nStoreMode == ( EXPORT_REQUESTED | WIDEEXPORT_REQUESTED ) )
147 nResult = SID_SAVEASDOC;
148 else if ( nStoreMode == SAVEASREMOTE_REQUESTED )
149 nResult = SID_SAVEASREMOTE;
150 else {
151 SAL_WARN( "sfx.doc", "Unacceptable slot name is provided!" );
154 return nResult;
158 sal_Int16 getStoreModeFromSlotName( std::u16string_view aSlotName )
160 sal_Int16 nResult = 0;
161 if ( aSlotName == u"ExportTo" )
162 nResult = EXPORT_REQUESTED;
163 else if ( aSlotName == u"ExportToPDF" )
164 nResult = EXPORT_REQUESTED | PDFEXPORT_REQUESTED;
165 else if ( aSlotName == u"ExportDirectToPDF" )
166 nResult = EXPORT_REQUESTED | PDFEXPORT_REQUESTED | PDFDIRECTEXPORT_REQUESTED;
167 else if ( aSlotName == u"ExportToEPUB" )
168 nResult = EXPORT_REQUESTED | EPUBEXPORT_REQUESTED;
169 else if ( aSlotName == u"ExportDirectToEPUB" )
170 nResult = EXPORT_REQUESTED | EPUBEXPORT_REQUESTED | EPUBDIRECTEXPORT_REQUESTED;
171 else if ( aSlotName == u"Save" )
172 nResult = SAVE_REQUESTED;
173 else if ( aSlotName == u"SaveAs" )
174 nResult = SAVEAS_REQUESTED;
175 else if ( aSlotName == u"SaveAsRemote" )
176 nResult = SAVEASREMOTE_REQUESTED;
177 else
178 throw task::ErrorCodeIOException(
179 (OUString::Concat("getStoreModeFromSlotName(\"") + aSlotName
180 + "): ERRCODE_IO_INVALIDPARAMETER"),
181 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER) );
183 return nResult;
187 SfxFilterFlags getMustFlags( sal_Int16 nStoreMode )
189 return ( SfxFilterFlags::EXPORT
190 | ( ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) ) ? SfxFilterFlags::NONE : SfxFilterFlags::IMPORT ) );
194 SfxFilterFlags getDontFlags( sal_Int16 nStoreMode )
196 return ( SfxFilterFlags::INTERNAL
197 | SfxFilterFlags::NOTINFILEDLG
198 | ( ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) ) ? SfxFilterFlags::IMPORT : SfxFilterFlags::NONE ) );
204 class DocumentSettingsGuard
206 uno::Reference< beans::XPropertySet > m_xDocumentSettings;
207 bool m_bPreserveReadOnly;
208 bool m_bReadOnlySupported;
210 bool m_bRestoreSettings;
211 public:
212 DocumentSettingsGuard( const uno::Reference< frame::XModel >& xModel, bool bReadOnly, bool bRestore )
213 : m_bPreserveReadOnly( false )
214 , m_bReadOnlySupported( false )
215 , m_bRestoreSettings( bRestore )
219 uno::Reference< lang::XMultiServiceFactory > xDocSettingsSupplier( xModel, uno::UNO_QUERY_THROW );
220 m_xDocumentSettings.set(
221 xDocSettingsSupplier->createInstance( "com.sun.star.document.Settings" ),
222 uno::UNO_QUERY_THROW );
226 OUString aLoadReadonlyString( "LoadReadonly" );
227 m_xDocumentSettings->getPropertyValue( aLoadReadonlyString ) >>= m_bPreserveReadOnly;
228 m_xDocumentSettings->setPropertyValue( aLoadReadonlyString, uno::makeAny( bReadOnly ) );
229 m_bReadOnlySupported = true;
231 catch( const uno::Exception& )
234 catch( const uno::Exception& )
237 if ( bReadOnly && !m_bReadOnlySupported )
238 throw uno::RuntimeException(); // the user could provide the data, so it must be stored
241 ~DocumentSettingsGuard()
243 if ( m_bRestoreSettings )
247 if ( m_bReadOnlySupported )
248 m_xDocumentSettings->setPropertyValue( "LoadReadonly", uno::makeAny( m_bPreserveReadOnly ) );
250 catch( const uno::Exception& )
252 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
257 } // anonymous namespace
261 class ModelData_Impl
263 SfxStoringHelper* m_pOwner;
264 uno::Reference< frame::XModel > m_xModel;
265 uno::Reference< frame::XStorable > m_xStorable;
266 uno::Reference< frame::XStorable2 > m_xStorable2;
268 OUString m_aModuleName;
269 std::unique_ptr<::comphelper::SequenceAsHashMap> m_pDocumentPropsHM;
270 std::unique_ptr<::comphelper::SequenceAsHashMap> m_pModulePropsHM;
272 uno::Reference<beans::XPropertyAccess> m_xFilterProperties;
273 uno::Reference<ui::dialogs::XAsynchronousExecutableDialog> m_xFilterDialog;
275 ::comphelper::SequenceAsHashMap m_aMediaDescrHM;
277 bool m_bRecommendReadOnly;
279 DECL_LINK(OptionsDialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, void);
281 public:
282 ModelData_Impl( SfxStoringHelper& aOwner,
283 const uno::Reference< frame::XModel >& xModel,
284 const uno::Sequence< beans::PropertyValue >& aMediaDescr );
286 ~ModelData_Impl();
288 void FreeDocumentProps();
290 uno::Reference< frame::XModel > const & GetModel() const;
291 uno::Reference< frame::XStorable > const & GetStorable();
292 uno::Reference< frame::XStorable2 > const & GetStorable2();
294 ::comphelper::SequenceAsHashMap& GetMediaDescr() { return m_aMediaDescrHM; }
296 bool IsRecommendReadOnly() const { return m_bRecommendReadOnly; }
298 const ::comphelper::SequenceAsHashMap& GetDocProps();
300 OUString const & GetModuleName();
301 const ::comphelper::SequenceAsHashMap& GetModuleProps();
303 void CheckInteractionHandler();
306 OUString GetDocServiceName();
307 uno::Sequence< beans::PropertyValue > GetDocServiceDefaultFilterCheckFlags( SfxFilterFlags nMust, SfxFilterFlags nDont );
308 uno::Sequence< beans::PropertyValue > GetDocServiceAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont );
309 uno::Sequence< beans::PropertyValue > GetPreselectedFilter_Impl( sal_Int16 nStoreMode );
310 uno::Sequence< beans::PropertyValue > GetDocServiceDefaultFilter();
312 bool ExecuteFilterDialog_Impl( const OUString& aFilterName, bool bAsync );
314 sal_Int8 CheckSaveAcceptable( sal_Int8 nCurStatus );
315 sal_Int8 CheckStateForSave();
317 sal_Int8 CheckFilter( const OUString& );
319 bool CheckFilterOptionsDialogExistence();
321 bool OutputFileDialog( sal_Int16 nStoreMode,
322 const ::comphelper::SequenceAsHashMap& aPreselectedFilterPropsHM,
323 bool bSetStandardName,
324 OUString& aSuggestedName,
325 bool bPreselectPassword,
326 OUString& aSuggestedDir,
327 sal_Int16 nDialog,
328 const OUString& rStandardDir,
329 const css::uno::Sequence< OUString >& rDenyList
332 bool ShowDocumentInfoDialog();
334 static OUString GetRecommendedExtension( const OUString& aTypeName );
335 OUString GetRecommendedDir( const OUString& aSuggestedDir );
336 OUString GetRecommendedName( const OUString& aSuggestedName,
337 const OUString& aTypeName );
341 ModelData_Impl::ModelData_Impl( SfxStoringHelper& aOwner,
342 const uno::Reference< frame::XModel >& xModel,
343 const uno::Sequence< beans::PropertyValue >& aMediaDescr )
344 : m_pOwner( &aOwner )
345 , m_xModel( xModel )
346 , m_aMediaDescrHM( aMediaDescr )
347 , m_bRecommendReadOnly( false )
349 CheckInteractionHandler();
353 ModelData_Impl::~ModelData_Impl()
355 FreeDocumentProps();
356 m_pDocumentPropsHM.reset();
357 m_pModulePropsHM.reset();
358 if (m_xFilterProperties)
359 m_xFilterProperties.clear();
363 void ModelData_Impl::FreeDocumentProps()
365 m_pDocumentPropsHM.reset();
369 uno::Reference< frame::XModel > const & ModelData_Impl::GetModel() const
371 if ( !m_xModel.is() )
372 throw uno::RuntimeException();
374 return m_xModel;
378 uno::Reference< frame::XStorable > const & ModelData_Impl::GetStorable()
380 if ( !m_xStorable.is() )
382 m_xStorable.set( m_xModel, uno::UNO_QUERY_THROW );
385 return m_xStorable;
389 uno::Reference< frame::XStorable2 > const & ModelData_Impl::GetStorable2()
391 if ( !m_xStorable2.is() )
393 m_xStorable2.set( m_xModel, uno::UNO_QUERY_THROW );
396 return m_xStorable2;
400 const ::comphelper::SequenceAsHashMap& ModelData_Impl::GetDocProps()
402 if ( !m_pDocumentPropsHM )
403 m_pDocumentPropsHM.reset( new ::comphelper::SequenceAsHashMap( GetModel()->getArgs() ) );
405 return *m_pDocumentPropsHM;
409 OUString const & ModelData_Impl::GetModuleName()
411 if ( m_aModuleName.isEmpty() )
413 m_aModuleName = m_pOwner->GetModuleManager()->identify(
414 uno::Reference< uno::XInterface >( m_xModel, uno::UNO_QUERY ) );
415 if ( m_aModuleName.isEmpty() )
416 throw uno::RuntimeException(); // TODO:
418 return m_aModuleName;
422 const ::comphelper::SequenceAsHashMap& ModelData_Impl::GetModuleProps()
424 if ( !m_pModulePropsHM )
426 uno::Sequence< beans::PropertyValue > aModuleProps;
427 m_pOwner->GetModuleManager()->getByName( GetModuleName() ) >>= aModuleProps;
428 if ( !aModuleProps.hasElements() )
429 throw uno::RuntimeException(); // TODO;
430 m_pModulePropsHM.reset( new ::comphelper::SequenceAsHashMap( aModuleProps ) );
433 return *m_pModulePropsHM;
437 OUString ModelData_Impl::GetDocServiceName()
439 return GetModuleProps().getUnpackedValueOrDefault("ooSetupFactoryDocumentService", OUString());
443 void ModelData_Impl::CheckInteractionHandler()
445 const OUString sInteractionHandler {"InteractionHandler"};
446 ::comphelper::SequenceAsHashMap::const_iterator aInteractIter =
447 m_aMediaDescrHM.find( sInteractionHandler );
449 if ( aInteractIter == m_aMediaDescrHM.end() )
451 try {
452 m_aMediaDescrHM[ sInteractionHandler ]
453 <<= task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr);
455 catch( const uno::Exception& )
459 else
461 uno::Reference< task::XInteractionHandler > xInteract;
462 DBG_ASSERT( ( aInteractIter->second >>= xInteract ) && xInteract.is(), "Broken interaction handler is provided!\n" );
467 uno::Sequence< beans::PropertyValue > ModelData_Impl::GetDocServiceDefaultFilter()
469 uno::Sequence< beans::PropertyValue > aProps;
471 const OUString aFilterName = GetModuleProps().getUnpackedValueOrDefault( "ooSetupFactoryDefaultFilter", OUString() );
473 m_pOwner->GetFilterConfiguration()->getByName( aFilterName ) >>= aProps;
475 return aProps;
479 uno::Sequence< beans::PropertyValue > ModelData_Impl::GetDocServiceDefaultFilterCheckFlags( SfxFilterFlags nMust,
480 SfxFilterFlags nDont )
482 uno::Sequence< beans::PropertyValue > aFilterProps;
483 uno::Sequence< beans::PropertyValue > aProps = GetDocServiceDefaultFilter();
484 if ( aProps.hasElements() )
486 ::comphelper::SequenceAsHashMap aFiltHM( aProps );
487 SfxFilterFlags nFlags = static_cast<SfxFilterFlags>(aFiltHM.getUnpackedValueOrDefault("Flags",
488 sal_Int32(0) ));
489 if ( ( ( nFlags & nMust ) == nMust ) && !( nFlags & nDont ) )
490 aFilterProps = aProps;
493 return aFilterProps;
497 uno::Sequence< beans::PropertyValue > ModelData_Impl::GetDocServiceAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont )
499 uno::Sequence< beans::NamedValue > aSearchRequest { { "DocumentService", css::uno::makeAny(GetDocServiceName()) } };
501 return ::comphelper::MimeConfigurationHelper::SearchForFilter( m_pOwner->GetFilterQuery(), aSearchRequest, nMust, nDont );
505 uno::Sequence< beans::PropertyValue > ModelData_Impl::GetPreselectedFilter_Impl( sal_Int16 nStoreMode )
507 if ( nStoreMode == SAVEASREMOTE_REQUESTED )
508 nStoreMode = SAVEAS_REQUESTED;
510 uno::Sequence< beans::PropertyValue > aFilterProps;
512 SfxFilterFlags nMust = getMustFlags( nStoreMode );
513 SfxFilterFlags nDont = getDontFlags( nStoreMode );
515 if ( ( nStoreMode != SAVEASREMOTE_REQUESTED ) && ( nStoreMode & PDFEXPORT_REQUESTED ) )
517 // Preselect PDF-Filter for EXPORT
518 uno::Sequence< beans::NamedValue > aSearchRequest
520 { "Type", css::uno::makeAny(OUString("pdf_Portable_Document_Format")) },
521 { "DocumentService", css::uno::makeAny(GetDocServiceName()) }
524 aFilterProps = ::comphelper::MimeConfigurationHelper::SearchForFilter( m_pOwner->GetFilterQuery(), aSearchRequest, nMust, nDont );
526 else if ( ( nStoreMode != SAVEASREMOTE_REQUESTED ) && ( nStoreMode & EPUBEXPORT_REQUESTED ) )
528 // Preselect EPUB filter for export.
529 uno::Sequence<beans::NamedValue> aSearchRequest
531 { "Type", css::uno::makeAny(OUString("writer_EPUB_Document")) },
532 { "DocumentService", css::uno::makeAny(GetDocServiceName()) }
535 aFilterProps = ::comphelper::MimeConfigurationHelper::SearchForFilter( m_pOwner->GetFilterQuery(), aSearchRequest, nMust, nDont );
537 else
539 aFilterProps = GetDocServiceDefaultFilterCheckFlags( nMust, nDont );
541 if ( !aFilterProps.hasElements() )
543 // the default filter was not found, use just the first acceptable one
544 aFilterProps = GetDocServiceAnyFilter( nMust, nDont );
548 return aFilterProps;
552 bool ModelData_Impl::ExecuteFilterDialog_Impl( const OUString& aFilterName, bool bIsAsync )
554 bool bDialogUsed = false;
556 try {
557 uno::Sequence < beans::PropertyValue > aProps;
558 uno::Any aAny = m_pOwner->GetFilterConfiguration()->getByName( aFilterName );
559 if ( aAny >>= aProps )
561 auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps),
562 [](const beans::PropertyValue& rProp) { return rProp.Name == "UIComponent"; });
563 if (pProp != std::cend(aProps))
565 OUString aServiceName;
566 pProp->Value >>= aServiceName;
567 if( !aServiceName.isEmpty() )
569 uno::Sequence<uno::Any> aDialogArgs(comphelper::InitAnyPropertySequence(
571 {"ParentWindow", uno::Any(SfxStoringHelper::GetModelXWindow(m_xModel))},
572 }));
574 uno::Reference< beans::XPropertyAccess > xFilterProperties;
575 uno::Reference< ui::dialogs::XExecutableDialog > xFilterDialog;
576 uno::Reference< ui::dialogs::XAsynchronousExecutableDialog > xAsyncFilterDialog;
577 uno::Reference< document::XExporter > xExporter;
579 if ( bIsAsync )
581 xAsyncFilterDialog = uno::Reference< ui::dialogs::XAsynchronousExecutableDialog >(
582 comphelper::getProcessServiceFactory()->createInstanceWithArguments( aServiceName, aDialogArgs ), uno::UNO_QUERY );
583 OSL_ENSURE(xAsyncFilterDialog.is(), "ModelData_Impl::ExecuteFilterDialog_Impl: Dialog is not async!");
584 xFilterProperties = uno::Reference< beans::XPropertyAccess >( xAsyncFilterDialog, uno::UNO_QUERY );
585 xExporter = uno::Reference< document::XExporter >( xAsyncFilterDialog, uno::UNO_QUERY );
587 else
589 xFilterDialog = uno::Reference< ui::dialogs::XExecutableDialog >(
590 comphelper::getProcessServiceFactory()->createInstanceWithArguments( aServiceName, aDialogArgs ), uno::UNO_QUERY );
591 xFilterProperties = uno::Reference< beans::XPropertyAccess >( xFilterDialog, uno::UNO_QUERY );
592 xExporter = uno::Reference< document::XExporter >( xFilterDialog, uno::UNO_QUERY );
595 if ( xFilterProperties.is() && ( xFilterDialog.is() || xAsyncFilterDialog.is() ) )
597 bDialogUsed = true;
599 if( xExporter.is() )
600 xExporter->setSourceDocument( GetModel() );
602 uno::Sequence< beans::PropertyValue > aPropsForDialog;
603 GetMediaDescr() >> aPropsForDialog;
604 xFilterProperties->setPropertyValues( aPropsForDialog );
606 if ( bIsAsync )
608 m_xFilterProperties = xFilterProperties;
609 m_xFilterDialog = xAsyncFilterDialog;
611 auto aDialogClosedListener = rtl::Reference(new svt::DialogClosedListener());
612 aDialogClosedListener->SetDialogClosedLink( LINK( this, ModelData_Impl, OptionsDialogClosedHdl ) );
614 m_xFilterDialog->startExecuteModal( aDialogClosedListener );
616 else
618 if( !xFilterDialog->execute() )
620 throw task::ErrorCodeIOException(
621 ("ModelData_Impl::ExecuteFilterDialog_Impl:"
622 " ERRCODE_IO_ABORT"),
623 uno::Reference< uno::XInterface >(),
624 sal_uInt32(ERRCODE_IO_ABORT));
627 const uno::Sequence< beans::PropertyValue > aPropsFromDialog =
628 xFilterProperties->getPropertyValues();
629 for ( const auto& rProp : aPropsFromDialog )
630 GetMediaDescr()[rProp.Name] = rProp.Value;
637 catch( const container::NoSuchElementException& e )
639 // the filter name is unknown
640 throw task::ErrorCodeIOException(
641 ("ModelData_Impl::ExecuteFilterDialog_Impl: NoSuchElementException"
642 " \"" + e.Message + "\": ERRCODE_IO_ABORT"),
643 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
645 catch( const task::ErrorCodeIOException& )
647 throw;
649 catch( const uno::Exception& )
651 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
654 return bDialogUsed;
657 void SfxStoringHelper::CallFinishGUIStoreModel()
659 ::comphelper::SequenceAsHashMap::const_iterator aFileNameIter = m_xModelData->GetMediaDescr().find( OUString("URL") );
660 uno::Sequence< beans::PropertyValue > aFilterProps = m_xModelData->GetPreselectedFilter_Impl( m_nStoreMode );
661 const OUString aFilterFromMediaDescr = m_xModelData->GetMediaDescr().getUnpackedValueOrDefault( aFilterNameString, OUString() );
662 const OUString aOldFilterName = m_xModelData->GetDocProps().getUnpackedValueOrDefault( aFilterNameString, OUString() );
663 ::comphelper::SequenceAsHashMap aFilterPropsHM( aFilterProps );
664 OUString aFilterName = aFilterPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
666 SfxStoringHelper::FinishGUIStoreModel(aFileNameIter, *m_xModelData, m_bRemote, m_nStoreMode, aFilterProps,
667 m_bSetStandardName, m_bPreselectPassword, m_bDialogUsed,
668 aFilterFromMediaDescr, aOldFilterName, m_aArgsSequence, aFilterName);
670 if (SfxViewShell::Current())
671 SfxViewShell::Current()->SetStoringHelper(nullptr);
674 IMPL_LINK( ModelData_Impl, OptionsDialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, pEvt, void )
676 if (pEvt->DialogResult == RET_OK && m_xFilterProperties)
678 const uno::Sequence< beans::PropertyValue > aPropsFromDialog = m_xFilterProperties->getPropertyValues();
679 for ( const auto& rProp : aPropsFromDialog )
680 GetMediaDescr()[rProp.Name] = rProp.Value;
682 m_pOwner->CallFinishGUIStoreModel();
686 sal_Int8 ModelData_Impl::CheckSaveAcceptable( sal_Int8 nCurStatus )
688 sal_Int8 nResult = nCurStatus;
690 if ( nResult != STATUS_NO_ACTION && GetStorable()->hasLocation() )
692 // the saving is acceptable
693 // in case the configuration entry is not set or set to false
694 // or in case of version creation
695 if ( officecfg::Office::Common::Save::Document::AlwaysSaveAs::get()
696 && GetMediaDescr().find( OUString("VersionComment") ) == GetMediaDescr().end() )
698 // notify the user that SaveAs is going to be done
699 std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(SfxStoringHelper::GetModelWindow(m_xModel),
700 VclMessageType::Question, VclButtonsType::OkCancel, SfxResId(STR_NEW_FILENAME_SAVE)));
701 if (xMessageBox->run() == RET_OK)
702 nResult = STATUS_SAVEAS;
703 else
704 nResult = STATUS_NO_ACTION;
708 return nResult;
712 sal_Int8 ModelData_Impl::CheckStateForSave()
714 // if the document is readonly or a new one a SaveAs operation must be used
715 if ( !GetStorable()->hasLocation() || GetStorable()->isReadonly() )
716 return STATUS_SAVEAS;
718 // check acceptable entries for media descriptor
719 ::comphelper::SequenceAsHashMap aAcceptedArgs;
721 static const OUStringLiteral aVersionCommentString(u"VersionComment");
722 static const OUStringLiteral aAuthorString(u"Author");
723 static const OUStringLiteral aDontTerminateEdit(u"DontTerminateEdit");
724 static const OUStringLiteral aInteractionHandlerString(u"InteractionHandler");
725 static const OUStringLiteral aStatusIndicatorString(u"StatusIndicator");
726 static const OUStringLiteral aFailOnWarningString(u"FailOnWarning");
727 static const OUStringLiteral aNoFileSync(u"NoFileSync");
729 if ( GetMediaDescr().find( aVersionCommentString ) != GetMediaDescr().end() )
730 aAcceptedArgs[ aVersionCommentString ] = GetMediaDescr()[ aVersionCommentString ];
731 if ( GetMediaDescr().find( aAuthorString ) != GetMediaDescr().end() )
732 aAcceptedArgs[ aAuthorString ] = GetMediaDescr()[ aAuthorString ];
733 if ( GetMediaDescr().find( aDontTerminateEdit ) != GetMediaDescr().end() )
734 aAcceptedArgs[ aDontTerminateEdit ] = GetMediaDescr()[ aDontTerminateEdit ];
735 if ( GetMediaDescr().find( aInteractionHandlerString ) != GetMediaDescr().end() )
736 aAcceptedArgs[ aInteractionHandlerString ] = GetMediaDescr()[ aInteractionHandlerString ];
737 if ( GetMediaDescr().find( aStatusIndicatorString ) != GetMediaDescr().end() )
738 aAcceptedArgs[ aStatusIndicatorString ] = GetMediaDescr()[ aStatusIndicatorString ];
739 if ( GetMediaDescr().find( aFailOnWarningString ) != GetMediaDescr().end() )
740 aAcceptedArgs[ aFailOnWarningString ] = GetMediaDescr()[ aFailOnWarningString ];
741 if (GetMediaDescr().find(aNoFileSync) != GetMediaDescr().end())
742 aAcceptedArgs[aNoFileSync] = GetMediaDescr()[aNoFileSync];
744 // remove unacceptable entry if there is any
745 DBG_ASSERT( GetMediaDescr().size() == aAcceptedArgs.size(),
746 "Unacceptable parameters are provided in Save request!\n" );
747 if ( GetMediaDescr().size() != aAcceptedArgs.size() )
748 GetMediaDescr() = aAcceptedArgs;
750 // check that the old filter is acceptable
751 return CheckFilter( GetDocProps().getUnpackedValueOrDefault(aFilterNameString, OUString()) );
754 sal_Int8 ModelData_Impl::CheckFilter( const OUString& aFilterName )
756 ::comphelper::SequenceAsHashMap aFiltPropsHM;
757 SfxFilterFlags nFiltFlags = SfxFilterFlags::NONE;
758 if ( !aFilterName.isEmpty() )
760 // get properties of filter
761 uno::Sequence< beans::PropertyValue > aFilterProps;
762 m_pOwner->GetFilterConfiguration()->getByName( aFilterName ) >>= aFilterProps;
764 aFiltPropsHM = ::comphelper::SequenceAsHashMap( aFilterProps );
765 nFiltFlags = static_cast<SfxFilterFlags>(aFiltPropsHM.getUnpackedValueOrDefault("Flags", sal_Int32(0) ));
768 // only a temporary solution until default filter retrieving feature is implemented
769 // then GetDocServiceDefaultFilter() must be used
770 ::comphelper::SequenceAsHashMap aDefFiltPropsHM = GetDocServiceDefaultFilterCheckFlags( SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT, SfxFilterFlags::NONE );
771 SfxFilterFlags nDefFiltFlags = static_cast<SfxFilterFlags>(aDefFiltPropsHM.getUnpackedValueOrDefault("Flags", sal_Int32(0) ));
773 bool bAsk = false;
775 // if the old filter is not acceptable
776 // and there is no default filter or it is not acceptable for requested parameters then proceed with saveAs
777 if ( ( aFiltPropsHM.empty() || !( nFiltFlags & SfxFilterFlags::EXPORT ) )
778 && ( aDefFiltPropsHM.empty() || !( nDefFiltFlags & SfxFilterFlags::EXPORT ) || nDefFiltFlags & SfxFilterFlags::INTERNAL ) )
779 return STATUS_SAVEAS;
781 // so at this point there is either an acceptable old filter or default one
782 if ( aFiltPropsHM.empty() || !( nFiltFlags & SfxFilterFlags::EXPORT ) )
784 // so the default filter must be acceptable
785 return STATUS_SAVEAS_STANDARDNAME;
787 else if ( ( !( nFiltFlags & SfxFilterFlags::OWN ) || ( nFiltFlags & SfxFilterFlags::ALIEN ) )
788 && !aDefFiltPropsHM.empty()
789 && ( nDefFiltFlags & SfxFilterFlags::EXPORT ) && !( nDefFiltFlags & SfxFilterFlags::INTERNAL ))
791 bAsk = true;
794 // check if EncryptionData supports this output format
796 OUString aSupportedFilters;
797 const ::comphelper::SequenceAsHashMap& rDocumentProperties = GetDocProps();
798 const css::uno::Sequence<css::beans::NamedValue> aEncryptionData = rDocumentProperties.getUnpackedValueOrDefault("EncryptionData", css::uno::Sequence<css::beans::NamedValue>());
799 if (aEncryptionData != css::uno::Sequence<css::beans::NamedValue>())
801 for (const css::beans::NamedValue& aNamedValue : aEncryptionData)
803 if (aNamedValue.Name == "SupportedFilters")
805 aNamedValue.Value >>= aSupportedFilters;
810 // if 'SupportedFilters' is empty assume that all filters are supported.
811 if (!aSupportedFilters.isEmpty())
813 const OUString aSelectedFilter = aFiltPropsHM.getUnpackedValueOrDefault("UIName", OUString());
815 aSupportedFilters = ";" + aSupportedFilters + ";";
816 const OUString aSearchToken = ";" + aSelectedFilter + ";";
817 bAsk = (aSupportedFilters.indexOf(aSearchToken) < 0);
821 if (bAsk)
823 // the default filter is acceptable and the old filter is alien one
824 // so ask to make a saveAs operation
825 const OUString aUIName = aFiltPropsHM.getUnpackedValueOrDefault("UIName", OUString() );
826 const OUString aDefUIName = aDefFiltPropsHM.getUnpackedValueOrDefault("UIName", OUString() );
827 const OUString aPreusedFilterName = GetDocProps().getUnpackedValueOrDefault("PreusedFilterName", OUString() );
828 const OUString aDefType = aDefFiltPropsHM.getUnpackedValueOrDefault( "Type", OUString() );
829 const OUString aDefExtension = GetRecommendedExtension( aDefType );
831 if ( aPreusedFilterName != aFilterName && aUIName != aDefUIName )
833 if ( !SfxStoringHelper::WarnUnacceptableFormat( GetModel(), aUIName, aDefExtension,
834 static_cast<bool>( nDefFiltFlags & SfxFilterFlags::ALIEN ) ) )
835 return STATUS_SAVEAS_STANDARDNAME;
839 return STATUS_SAVE;
843 bool ModelData_Impl::CheckFilterOptionsDialogExistence()
845 uno::Sequence< beans::NamedValue > aSearchRequest { { "DocumentService", css::uno::makeAny(GetDocServiceName()) } };
847 uno::Reference< container::XEnumeration > xFilterEnum =
848 m_pOwner->GetFilterQuery()->createSubSetEnumerationByProperties( aSearchRequest );
850 while ( xFilterEnum->hasMoreElements() )
852 uno::Sequence< beans::PropertyValue > aProps;
853 if ( xFilterEnum->nextElement() >>= aProps )
855 ::comphelper::SequenceAsHashMap aPropsHM( aProps );
856 if ( !aPropsHM.getUnpackedValueOrDefault("UIComponent", OUString()).isEmpty() )
857 return true;
861 return false;
865 bool ModelData_Impl::OutputFileDialog( sal_Int16 nStoreMode,
866 const ::comphelper::SequenceAsHashMap& aPreselectedFilterPropsHM,
867 bool bSetStandardName,
868 OUString& aSuggestedName,
869 bool bPreselectPassword,
870 OUString& aSuggestedDir,
871 sal_Int16 nDialog,
872 const OUString& rStandardDir,
873 const css::uno::Sequence< OUString >& rDenyList)
875 if ( nStoreMode == SAVEASREMOTE_REQUESTED )
876 nStoreMode = SAVEAS_REQUESTED;
878 bool bUseFilterOptions = false;
880 ::comphelper::SequenceAsHashMap::const_iterator aOverwriteIter =
881 GetMediaDescr().find( OUString("Overwrite") );
883 // the file name must be specified if overwrite option is set
884 if ( aOverwriteIter != GetMediaDescr().end() )
885 throw task::ErrorCodeIOException(
886 "ModelData_Impl::OutputFileDialog: ERRCODE_IO_INVALIDPARAMETER",
887 uno::Reference< uno::XInterface >(),
888 sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
890 // no target file name is specified
891 // we need to show the file dialog
893 // check if we have a filter which allows for filter options, so we need a corresponding checkbox in the dialog
894 bool bAllowOptions = false;
896 // in case of Export, filter options dialog is used if available
897 if( !( nStoreMode & EXPORT_REQUESTED ) || ( nStoreMode & WIDEEXPORT_REQUESTED ) )
898 bAllowOptions = CheckFilterOptionsDialogExistence();
900 // get the filename by dialog ...
901 // create the file dialog
902 sal_Int16 aDialogMode = bAllowOptions
903 ? css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS
904 : css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD;
905 FileDialogFlags aDialogFlags = FileDialogFlags::NONE;
907 if( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) )
909 if ( (nStoreMode & PDFEXPORT_REQUESTED) || (nStoreMode & EPUBEXPORT_REQUESTED) )
910 aDialogMode = css::ui::dialogs::TemplateDescription::
911 FILESAVE_AUTOEXTENSION;
912 else
913 aDialogMode = css::ui::dialogs::TemplateDescription::
914 FILESAVE_AUTOEXTENSION_SELECTION;
915 aDialogFlags = FileDialogFlags::Export;
918 if( ( nStoreMode & EXPORT_REQUESTED ) && ( nStoreMode & SAVEACOPY_REQUESTED ) && ( nStoreMode & WIDEEXPORT_REQUESTED ) )
920 aDialogFlags = FileDialogFlags::SaveACopy;
923 std::unique_ptr<sfx2::FileDialogHelper> pFileDlg;
925 const OUString aDocServiceName {GetDocServiceName()};
926 DBG_ASSERT( !aDocServiceName.isEmpty(), "No document service for this module set!" );
928 SfxFilterFlags nMust = getMustFlags( nStoreMode );
929 SfxFilterFlags nDont = getDontFlags( nStoreMode );
930 weld::Window* pFrameWin = SfxStoringHelper::GetModelWindow(m_xModel);
931 if ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) )
933 if ( ( nStoreMode & PDFEXPORT_REQUESTED ) && !aPreselectedFilterPropsHM.empty() )
935 // this is a PDF export
936 // the filter options has been shown already
937 const OUString aFilterUIName = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() );
938 pFileDlg.reset(new sfx2::FileDialogHelper( aDialogMode, aDialogFlags, aFilterUIName, "pdf", rStandardDir, rDenyList, pFrameWin ));
939 pFileDlg->SetCurrentFilter( aFilterUIName );
941 else if ((nStoreMode & EPUBEXPORT_REQUESTED) && !aPreselectedFilterPropsHM.empty())
943 // This is an EPUB export, the filter options has been shown already.
944 const OUString aFilterUIName = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() );
945 pFileDlg.reset(new sfx2::FileDialogHelper(aDialogMode, aDialogFlags, aFilterUIName, "epub", rStandardDir, rDenyList, pFrameWin));
946 pFileDlg->SetCurrentFilter(aFilterUIName);
948 else
950 // This is the normal dialog
951 pFileDlg.reset(new sfx2::FileDialogHelper( aDialogMode, aDialogFlags, aDocServiceName, nDialog, nMust, nDont, rStandardDir, rDenyList, pFrameWin ));
954 sfx2::FileDialogHelper::Context eCtxt = sfx2::FileDialogHelper::UnknownContext;
955 if ( aDocServiceName == "com.sun.star.drawing.DrawingDocument" )
956 eCtxt = sfx2::FileDialogHelper::DrawExport;
957 else if ( aDocServiceName == "com.sun.star.presentation.PresentationDocument" )
958 eCtxt = sfx2::FileDialogHelper::ImpressExport;
959 else if ( aDocServiceName == "com.sun.star.text.TextDocument" )
960 eCtxt = sfx2::FileDialogHelper::WriterExport;
961 else if ( aDocServiceName == "com.sun.star.sheet.SpreadsheetDocument" )
962 eCtxt = sfx2::FileDialogHelper::CalcExport;
964 if ( eCtxt != sfx2::FileDialogHelper::UnknownContext )
965 pFileDlg->SetContext( eCtxt );
967 pFileDlg->CreateMatcher( aDocServiceName );
969 uno::Reference< ui::dialogs::XFilePicker3 > xFilePicker = pFileDlg->GetFilePicker();
970 uno::Reference< ui::dialogs::XFilePickerControlAccess > xControlAccess( xFilePicker, uno::UNO_QUERY );
972 if ( xControlAccess.is() )
974 xControlAccess->setLabel( ui::dialogs::CommonFilePickerElementIds::PUSHBUTTON_OK, SfxResId(STR_EXPORTBUTTON) );
975 xControlAccess->setLabel( ui::dialogs::CommonFilePickerElementIds::LISTBOX_FILTER_LABEL, SfxResId(STR_LABEL_FILEFORMAT) );
978 else
980 // This is the normal save as dialog
981 pFileDlg.reset(new sfx2::FileDialogHelper( aDialogMode, aDialogFlags, aDocServiceName, nDialog,
982 nMust, nDont, rStandardDir, rDenyList, pFrameWin ));
983 pFileDlg->CreateMatcher( aDocServiceName );
985 sfx2::FileDialogHelper::Context eCtxt = sfx2::FileDialogHelper::UnknownContext;
986 if ( aDocServiceName == "com.sun.star.drawing.DrawingDocument" )
987 eCtxt = sfx2::FileDialogHelper::DrawSaveAs;
988 else if ( aDocServiceName == "com.sun.star.presentation.PresentationDocument" )
989 eCtxt = sfx2::FileDialogHelper::ImpressSaveAs;
990 else if ( aDocServiceName == "com.sun.star.text.TextDocument" )
991 eCtxt = sfx2::FileDialogHelper::WriterSaveAs;
992 else if ( aDocServiceName == "com.sun.star.sheet.SpreadsheetDocument" )
993 eCtxt = sfx2::FileDialogHelper::CalcSaveAs;
995 if ( eCtxt != sfx2::FileDialogHelper::UnknownContext )
996 pFileDlg->SetContext( eCtxt );
999 OUString aAdjustToType;
1001 const OUString sFilterNameString(aFilterNameString);
1003 if ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) )
1005 // it is export, set the preselected filter
1006 pFileDlg->SetCurrentFilter( aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() ) );
1007 aAdjustToType = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "Type", OUString() );
1009 // it is no export, bSetStandardName == true means that user agreed to store document in the default (default default ;-)) format
1010 else if ( bSetStandardName || GetStorable()->hasLocation() )
1012 uno::Sequence< beans::PropertyValue > aOldFilterProps;
1013 const OUString aOldFilterName = GetDocProps().getUnpackedValueOrDefault( sFilterNameString, OUString() );
1015 if ( !aOldFilterName.isEmpty() )
1016 m_pOwner->GetFilterConfiguration()->getByName( aOldFilterName ) >>= aOldFilterProps;
1018 ::comphelper::SequenceAsHashMap aOldFiltPropsHM( aOldFilterProps );
1019 SfxFilterFlags nOldFiltFlags = static_cast<SfxFilterFlags>(aOldFiltPropsHM.getUnpackedValueOrDefault("Flags", sal_Int32(0) ));
1021 if ( bSetStandardName || ( nOldFiltFlags & nMust ) != nMust || bool(nOldFiltFlags & nDont) )
1023 // the suggested type will be changed, the extension should be adjusted
1024 aAdjustToType = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "Type", OUString() );
1025 pFileDlg->SetCurrentFilter( aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "UIName", OUString() ) );
1027 else
1029 pFileDlg->SetCurrentFilter( aOldFiltPropsHM.getUnpackedValueOrDefault(
1030 "UIName",
1031 OUString() ) );
1035 const OUString aRecommendedDir {GetRecommendedDir( aSuggestedDir )};
1036 if ( !aRecommendedDir.isEmpty() )
1037 pFileDlg->SetDisplayFolder( aRecommendedDir );
1038 const OUString aRecommendedName {GetRecommendedName( aSuggestedName, aAdjustToType )};
1039 if ( !aRecommendedName.isEmpty() )
1040 pFileDlg->SetFileName( aRecommendedName );
1042 uno::Reference < view::XSelectionSupplier > xSel( GetModel()->getCurrentController(), uno::UNO_QUERY );
1043 if ( xSel.is() && xSel->getSelection().hasValue() )
1044 GetMediaDescr()[OUString("SelectionOnly")] <<= true;
1046 // This is a temporary hardcoded solution must be removed when
1047 // dialogs do not need parameters in SidSet representation any more
1048 sal_uInt16 nSlotID = getSlotIDFromMode( nStoreMode );
1049 if ( !nSlotID )
1050 throw lang::IllegalArgumentException(); // TODO:
1052 // generate SidSet from MediaDescriptor and provide it into FileDialog
1053 // than merge changed SidSet back
1054 std::optional<SfxAllItemSet> pDialogParams( SfxGetpApp()->GetPool() );
1055 TransformParameters( nSlotID,
1056 GetMediaDescr().getAsConstPropertyValueList(),
1057 *pDialogParams );
1059 const SfxPoolItem* pItem = nullptr;
1060 if ( bPreselectPassword && pDialogParams->GetItemState( SID_ENCRYPTIONDATA, true, &pItem ) != SfxItemState::SET )
1062 // the file dialog preselects the password checkbox if the provided mediadescriptor has encryption data entry
1063 // after dialog execution the password interaction flag will be either removed or not
1064 pDialogParams->Put( SfxBoolItem( SID_PASSWORDINTERACTION, true ) );
1067 // aFilterName is a pure output parameter, pDialogParams is an in/out parameter
1068 OUString aFilterName;
1069 // in LOK case we don't show File Picker so it will fail, but execute to do other preparations
1070 if ( pFileDlg->Execute( pDialogParams, aFilterName ) != ERRCODE_NONE
1071 && !comphelper::LibreOfficeKit::isActive() )
1073 throw task::ErrorCodeIOException(
1074 "ModelData_Impl::OutputFileDialog: ERRCODE_IO_ABORT",
1075 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
1077 else if (comphelper::LibreOfficeKit::isActive())
1079 aFilterName = aPreselectedFilterPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
1082 // the following two arguments can not be converted in MediaDescriptor,
1083 // so they should be removed from the ItemSet after retrieving
1084 const SfxBoolItem* pRecommendReadOnly = SfxItemSet::GetItem<SfxBoolItem>(&*pDialogParams, SID_RECOMMENDREADONLY, false);
1085 m_bRecommendReadOnly = ( pRecommendReadOnly && pRecommendReadOnly->GetValue() );
1086 pDialogParams->ClearItem( SID_RECOMMENDREADONLY );
1088 uno::Sequence< beans::PropertyValue > aPropsFromDialog;
1089 TransformItems( nSlotID, *pDialogParams, aPropsFromDialog );
1090 GetMediaDescr() << aPropsFromDialog;
1092 // get the path from the dialog
1093 INetURLObject aURL( pFileDlg->GetPath() );
1095 if (comphelper::LibreOfficeKit::isActive())
1097 // keep name with extension
1098 aSuggestedName = aRecommendedName;
1099 OUString aExtension;
1100 if (size_t nPos = aSuggestedName.lastIndexOf('.') + 1)
1101 aExtension = aSuggestedName.copy(nPos, aSuggestedName.getLength() - nPos);
1102 aURL.SetExtension(aExtension);
1104 else
1106 // the path should be provided outside since it might be used for further calls to the dialog
1107 aSuggestedName = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1109 aSuggestedDir = pFileDlg->GetDisplayDirectory();
1111 // old filter options should be cleared in case different filter is used
1113 const OUString aFilterFromMediaDescr = GetMediaDescr().getUnpackedValueOrDefault( sFilterNameString, OUString() );
1114 const OUString aOldFilterName = GetDocProps().getUnpackedValueOrDefault( sFilterNameString, OUString() );
1116 if ( aFilterName == aFilterFromMediaDescr )
1118 // preserve current settings if any
1119 // if there no current settings and the name is the same
1120 // as old filter name use old filter settings
1122 if ( aFilterFromMediaDescr == aOldFilterName )
1124 ::comphelper::SequenceAsHashMap::const_iterator aIter =
1125 GetDocProps().find( aFilterOptionsString );
1126 if ( aIter != GetDocProps().end()
1127 && GetMediaDescr().find( aFilterOptionsString ) == GetMediaDescr().end() )
1128 GetMediaDescr()[aIter->first] = aIter->second;
1130 aIter = GetDocProps().find( aFilterDataString );
1131 if ( aIter != GetDocProps().end()
1132 && GetMediaDescr().find( aFilterDataString ) == GetMediaDescr().end() )
1133 GetMediaDescr()[aIter->first] = aIter->second;
1136 else
1138 GetMediaDescr().erase( aFilterDataString );
1139 GetMediaDescr().erase( aFilterOptionsString );
1141 if ( aFilterName == aOldFilterName )
1143 // merge filter option of the document filter
1145 ::comphelper::SequenceAsHashMap::const_iterator aIter =
1146 GetDocProps().find( aFilterOptionsString );
1147 if ( aIter != GetDocProps().end() )
1148 GetMediaDescr()[aIter->first] = aIter->second;
1150 aIter = GetDocProps().find( aFilterDataString );
1151 if ( aIter != GetDocProps().end() )
1152 GetMediaDescr()[aIter->first] = aIter->second;
1156 uno::Reference< ui::dialogs::XFilePickerControlAccess > xExtFileDlg( pFileDlg->GetFilePicker(), uno::UNO_QUERY );
1157 if ( xExtFileDlg.is() )
1159 if ( SfxStoringHelper::CheckFilterOptionsAppearance( m_pOwner->GetFilterConfiguration(), aFilterName ) )
1160 bUseFilterOptions = true;
1162 if ( ( !( nStoreMode & EXPORT_REQUESTED ) || ( nStoreMode & WIDEEXPORT_REQUESTED ) ) && bUseFilterOptions )
1166 // for exporters: always show dialog if format uses options
1167 // for save: show dialog if format uses options and no options given or if forced by user
1168 uno::Any aVal =
1169 xExtFileDlg->getValue( ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS, 0 );
1171 aVal >>= bUseFilterOptions;
1172 if ( !bUseFilterOptions )
1173 bUseFilterOptions =
1174 ( GetMediaDescr().find( aFilterDataString ) == GetMediaDescr().end()
1175 && GetMediaDescr().find( aFilterOptionsString ) == GetMediaDescr().end() );
1177 catch( const lang::IllegalArgumentException& )
1182 // merge in results of the dialog execution
1183 GetMediaDescr()[OUString("URL")] <<= aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1184 GetMediaDescr()[sFilterNameString] <<= aFilterName;
1186 return bUseFilterOptions;
1190 bool ModelData_Impl::ShowDocumentInfoDialog()
1192 bool bDialogUsed = false;
1194 try {
1195 uno::Reference< frame::XController > xController = GetModel()->getCurrentController();
1196 if ( xController.is() )
1198 uno::Reference< frame::XDispatchProvider > xFrameDispatch( xController->getFrame(), uno::UNO_QUERY );
1199 if ( xFrameDispatch.is() )
1201 util::URL aURL;
1202 aURL.Complete = ".uno:SetDocumentProperties";
1204 uno::Reference < util::XURLTransformer > xTransformer( util::URLTransformer::create( comphelper::getProcessComponentContext() ) );
1205 if ( xTransformer->parseStrict( aURL ) )
1207 uno::Reference< frame::XDispatch > xDispatch = xFrameDispatch->queryDispatch(
1208 aURL,
1209 "_self",
1210 0 );
1211 if ( xDispatch.is() )
1213 // tdf#119206 use (abuse?) a SynchronMode of true,
1214 // which will become SfxRequest::IsSynchronCall of true
1215 // in SfxObjectShell::ExecFile_Impl to request that we
1216 // do not want the properties dialog to be run async
1217 uno::Sequence< beans::PropertyValue > aProperties{
1218 comphelper::makePropertyValue("SynchronMode", true)
1220 xDispatch->dispatch(aURL, aProperties);
1221 bDialogUsed = true;
1227 catch ( const uno::Exception& )
1231 return bDialogUsed;
1235 OUString ModelData_Impl::GetRecommendedExtension( const OUString& aTypeName )
1237 if ( aTypeName.isEmpty() )
1238 return OUString();
1240 uno::Reference< container::XNameAccess > xTypeDetection(
1241 comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"),
1242 uno::UNO_QUERY );
1243 if ( xTypeDetection.is() )
1245 uno::Sequence< beans::PropertyValue > aTypeNameProps;
1246 if ( ( xTypeDetection->getByName( aTypeName ) >>= aTypeNameProps ) && aTypeNameProps.hasElements() )
1248 ::comphelper::SequenceAsHashMap aTypeNamePropsHM( aTypeNameProps );
1249 uno::Sequence< OUString > aExtensions = aTypeNamePropsHM.getUnpackedValueOrDefault(
1250 "Extensions",
1251 ::uno::Sequence< OUString >() );
1252 if ( aExtensions.hasElements() )
1253 return aExtensions[0];
1257 return OUString();
1261 OUString ModelData_Impl::GetRecommendedDir( const OUString& aSuggestedDir )
1263 if ( ( !aSuggestedDir.isEmpty() || GetStorable()->hasLocation() )
1264 && !GetMediaDescr().getUnpackedValueOrDefault("RepairPackage", false ) )
1266 INetURLObject aLocation;
1267 if ( !aSuggestedDir.isEmpty() )
1268 aLocation = INetURLObject( aSuggestedDir );
1269 else
1271 const OUString aOldURL = GetStorable()->getLocation();
1272 if ( !aOldURL.isEmpty() )
1274 INetURLObject aTmp( aOldURL );
1275 if ( aTmp.removeSegment() )
1276 aLocation = aTmp;
1279 if ( aLocation.HasError() )
1280 aLocation = INetURLObject();
1283 OUString sLocationURL( aLocation.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1284 bool bIsInTempPath( false );
1285 OUString sSysTempPath;
1286 if( osl::FileBase::getTempDirURL( sSysTempPath ) == osl::FileBase::E_None )
1287 bIsInTempPath = !sSysTempPath.isEmpty() && sLocationURL.startsWith( sSysTempPath );
1288 #ifdef _WIN32
1289 if( !bIsInTempPath )
1291 wchar_t sPath[MAX_PATH+1];
1292 HRESULT hRes = SHGetFolderPathW( nullptr, CSIDL_INTERNET_CACHE, nullptr, SHGFP_TYPE_CURRENT, sPath );
1293 if( SUCCEEDED(hRes) )
1295 OUString sTempINetFiles;
1296 if( osl::FileBase::getFileURLFromSystemPath(OUString(o3tl::toU(sPath)), sTempINetFiles) == osl::FileBase::E_None )
1297 bIsInTempPath = !sTempINetFiles.isEmpty() && sLocationURL.startsWith( sTempINetFiles );
1300 #endif
1301 // Suggest somewhere other than the system's temp directory
1302 if( bIsInTempPath )
1303 aLocation = INetURLObject();
1305 aLocation.setFinalSlash();
1306 if ( !aLocation.HasError() )
1307 return aLocation.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1309 return OUString();
1312 return OUString();
1316 OUString ModelData_Impl::GetRecommendedName( const OUString& aSuggestedName, const OUString& aTypeName )
1318 // the last used name might be provided by aSuggestedName from the old selection, or from the MediaDescriptor
1319 if ( !aSuggestedName.isEmpty() )
1320 return aSuggestedName;
1322 OUString aRecommendedName{ INetURLObject(GetStorable()->getLocation())
1323 .GetLastName(INetURLObject::DecodeMechanism::WithCharset) };
1324 if ( aRecommendedName.isEmpty() )
1326 try {
1327 uno::Reference< frame::XTitle > xTitle( GetModel(), uno::UNO_QUERY_THROW );
1328 aRecommendedName = xTitle->getTitle();
1329 } catch( const uno::Exception& ) {}
1332 if ( !aRecommendedName.isEmpty() && !aTypeName.isEmpty() )
1334 // adjust the extension to the type
1335 uno::Reference< container::XNameAccess > xTypeDetection(
1336 comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"),
1337 uno::UNO_QUERY );
1338 if ( xTypeDetection.is() )
1340 INetURLObject aObj( "c:/" + aRecommendedName, INetProtocol::File,
1341 INetURLObject::EncodeMechanism::All, RTL_TEXTENCODING_UTF8, FSysStyle::Dos );
1343 const OUString aExtension = GetRecommendedExtension( aTypeName );
1344 if ( !aExtension.isEmpty() )
1345 aObj.SetExtension( aExtension );
1347 aRecommendedName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1351 return aRecommendedName;
1357 SfxStoringHelper::SfxStoringHelper()
1362 uno::Reference< container::XNameAccess > const & SfxStoringHelper::GetFilterConfiguration()
1364 if ( !m_xFilterCFG.is() )
1366 m_xFilterCFG.set( comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.FilterFactory"),
1367 uno::UNO_QUERY_THROW );
1370 return m_xFilterCFG;
1374 uno::Reference< container::XContainerQuery > const & SfxStoringHelper::GetFilterQuery()
1376 if ( !m_xFilterQuery.is() )
1378 m_xFilterQuery.set( GetFilterConfiguration(), uno::UNO_QUERY_THROW );
1381 return m_xFilterQuery;
1385 uno::Reference< css::frame::XModuleManager2 > const & SfxStoringHelper::GetModuleManager()
1387 if ( !m_xModuleManager.is() )
1389 m_xModuleManager = frame::ModuleManager::create(
1390 comphelper::getProcessComponentContext() );
1393 return m_xModuleManager;
1396 bool SfxStoringHelper::GUIStoreModel( const uno::Reference< frame::XModel >& xModel,
1397 std::u16string_view aSlotName,
1398 uno::Sequence< beans::PropertyValue >& aArgsSequence,
1399 bool bPreselectPassword,
1400 SignatureState nDocumentSignatureState,
1401 bool bIsAsync)
1403 m_xModelData = std::make_shared<ModelData_Impl>( *this, xModel, aArgsSequence );
1404 m_aArgsSequence = aArgsSequence;
1405 ModelData_Impl& aModelData = *m_xModelData;
1407 m_bDialogUsed = false;
1409 m_bSetStandardName = false; // can be set only for SaveAs
1410 m_bPreselectPassword = bPreselectPassword;
1412 // parse the slot name
1413 m_bRemote = false;
1414 m_nStoreMode = getStoreModeFromSlotName( aSlotName );
1416 if ( m_nStoreMode == SAVEASREMOTE_REQUESTED )
1418 m_nStoreMode = SAVEAS_REQUESTED;
1419 m_bRemote = true;
1422 sal_Int8 nStatusSave = STATUS_NO_ACTION;
1424 ::comphelper::SequenceAsHashMap::const_iterator aSaveACopyIter =
1425 aModelData.GetMediaDescr().find( OUString("SaveACopy") );
1426 if ( aSaveACopyIter != aModelData.GetMediaDescr().end() )
1428 bool bSaveACopy = false;
1429 aSaveACopyIter->second >>= bSaveACopy;
1430 if ( bSaveACopy )
1431 m_nStoreMode = EXPORT_REQUESTED | SAVEACOPY_REQUESTED | WIDEEXPORT_REQUESTED;
1433 // handle the special cases
1434 if ( m_nStoreMode & SAVEAS_REQUESTED )
1436 ::comphelper::SequenceAsHashMap::const_iterator aSaveToIter =
1437 aModelData.GetMediaDescr().find( OUString("SaveTo") );
1438 if ( aSaveToIter != aModelData.GetMediaDescr().end() )
1440 bool bWideExport = false;
1441 aSaveToIter->second >>= bWideExport;
1442 if ( bWideExport )
1443 m_nStoreMode = EXPORT_REQUESTED | WIDEEXPORT_REQUESTED;
1446 // if saving is not acceptable the warning must be shown even in case of SaveAs operation
1447 if ( ( m_nStoreMode & SAVEAS_REQUESTED ) && aModelData.CheckSaveAcceptable( STATUS_SAVEAS ) == STATUS_NO_ACTION )
1448 throw task::ErrorCodeIOException(
1449 "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT",
1450 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
1452 else if ( m_nStoreMode & SAVE_REQUESTED )
1454 // if saving is not acceptable by the configuration the warning must be shown
1455 nStatusSave = aModelData.CheckSaveAcceptable( STATUS_SAVE );
1457 if ( nStatusSave == STATUS_NO_ACTION )
1458 throw task::ErrorCodeIOException(
1459 "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT",
1460 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
1461 else if ( nStatusSave == STATUS_SAVE )
1463 // check whether it is possible to use save operation
1464 nStatusSave = aModelData.CheckStateForSave();
1467 if ( nStatusSave == STATUS_NO_ACTION )
1469 throw task::ErrorCodeIOException(
1470 "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT",
1471 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
1473 else if ( nStatusSave != STATUS_SAVE )
1475 // this should be a usual SaveAs operation
1476 m_nStoreMode = SAVEAS_REQUESTED;
1477 if ( nStatusSave == STATUS_SAVEAS_STANDARDNAME )
1478 m_bSetStandardName = true;
1482 if (!comphelper::LibreOfficeKit::isActive() && !( m_nStoreMode & EXPORT_REQUESTED ) )
1484 // if it is no export, warn user that the signature will be removed
1485 if ( SignatureState::OK == nDocumentSignatureState
1486 || SignatureState::INVALID == nDocumentSignatureState
1487 || SignatureState::NOTVALIDATED == nDocumentSignatureState
1488 || SignatureState::PARTIAL_OK == nDocumentSignatureState)
1490 std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(SfxStoringHelper::GetModelWindow(xModel),
1491 VclMessageType::Question, VclButtonsType::YesNo, SfxResId(RID_SVXSTR_XMLSEC_QUERY_LOSINGSIGNATURE)));
1492 if (xMessageBox->run() != RET_YES)
1494 // the user has decided not to store the document
1495 throw task::ErrorCodeIOException(
1496 "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_ABORT (Preserve Signature)",
1497 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_ABORT));
1502 if ( m_nStoreMode & SAVE_REQUESTED && nStatusSave == STATUS_SAVE )
1504 // Document properties can contain streams that should be freed before storing
1505 aModelData.FreeDocumentProps();
1507 if ( aModelData.GetStorable2().is() )
1511 aModelData.GetStorable2()->storeSelf( aModelData.GetMediaDescr().getAsConstPropertyValueList() );
1513 catch (const lang::IllegalArgumentException&)
1515 TOOLS_WARN_EXCEPTION("sfx.doc", "Ignoring parameters! ModelData considers this illegal");
1516 aModelData.GetStorable()->store();
1519 else
1521 OSL_FAIL( "XStorable2 is not supported by the model!" );
1522 aModelData.GetStorable()->store();
1525 return false;
1528 // preselect a filter for the storing process
1529 uno::Sequence< beans::PropertyValue > aFilterProps = aModelData.GetPreselectedFilter_Impl( m_nStoreMode );
1531 DBG_ASSERT( aFilterProps.hasElements(), "No filter for storing!\n" );
1532 if ( !aFilterProps.hasElements() )
1533 throw task::ErrorCodeIOException(
1534 "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_INVALIDPARAMETER",
1535 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
1537 ::comphelper::SequenceAsHashMap aFilterPropsHM( aFilterProps );
1538 OUString aFilterName = aFilterPropsHM.getUnpackedValueOrDefault( "Name", OUString() );
1540 const OUString aFilterFromMediaDescr = aModelData.GetMediaDescr().getUnpackedValueOrDefault( aFilterNameString, OUString() );
1541 const OUString aOldFilterName = aModelData.GetDocProps().getUnpackedValueOrDefault( aFilterNameString, OUString() );
1543 ::comphelper::SequenceAsHashMap::const_iterator aFileNameIter = aModelData.GetMediaDescr().find( OUString("URL") );
1545 bool bPDFOptions = (m_nStoreMode & PDFEXPORT_REQUESTED) && !(m_nStoreMode & PDFDIRECTEXPORT_REQUESTED);
1546 bool bEPUBOptions = (m_nStoreMode & EPUBEXPORT_REQUESTED) && !(m_nStoreMode & EPUBDIRECTEXPORT_REQUESTED);
1547 if ( ( m_nStoreMode & EXPORT_REQUESTED ) && (bPDFOptions || bEPUBOptions) )
1549 // this is PDF or EPUB export, the filter options dialog should be shown before the export
1550 aModelData.GetMediaDescr()[aFilterNameString] <<= aFilterName;
1551 if ( aModelData.GetMediaDescr().find( "FilterFlags" ) == aModelData.GetMediaDescr().end()
1552 && aModelData.GetMediaDescr().find( aFilterOptionsString ) == aModelData.GetMediaDescr().end()
1553 && aModelData.GetMediaDescr().find( aFilterDataString ) == aModelData.GetMediaDescr().end() )
1555 // execute filter options dialog since no options are set in the media descriptor
1556 if ( aModelData.ExecuteFilterDialog_Impl( aFilterName, bIsAsync ) )
1557 m_bDialogUsed = true;
1561 if (bIsAsync)
1562 return false;
1564 return SfxStoringHelper::FinishGUIStoreModel(aFileNameIter, aModelData, m_bRemote, m_nStoreMode, aFilterProps,
1565 m_bSetStandardName, m_bPreselectPassword, m_bDialogUsed,
1566 aFilterFromMediaDescr, aOldFilterName, aArgsSequence, aFilterName);
1569 bool SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::const_iterator& aFileNameIter,
1570 ModelData_Impl& aModelData, bool bRemote, sal_Int16 nStoreMode,
1571 uno::Sequence< beans::PropertyValue >& aFilterProps,
1572 bool bSetStandardName, bool bPreselectPassword, bool bDialogUsed,
1573 std::u16string_view aFilterFromMediaDescr,
1574 std::u16string_view aOldFilterName,
1575 uno::Sequence< beans::PropertyValue >& aArgsSequence,
1576 OUString aFilterName)
1578 const OUString sFilterNameString(aFilterNameString);
1579 const OUString sFilterOptionsString(aFilterOptionsString);
1580 const OUString sFilterDataString(aFilterDataString);
1581 bool bUseFilterOptions = false;
1582 INetURLObject aURL;
1584 if ( aFileNameIter == aModelData.GetMediaDescr().end() )
1586 sal_Int16 nDialog = SFX2_IMPL_DIALOG_CONFIG;
1588 if( bRemote )
1590 nDialog = SFX2_IMPL_DIALOG_REMOTE;
1592 else
1594 ::comphelper::SequenceAsHashMap::const_iterator aDlgIter =
1595 aModelData.GetMediaDescr().find( OUString("UseSystemDialog") );
1596 if ( aDlgIter != aModelData.GetMediaDescr().end() )
1598 bool bUseSystemDialog = true;
1599 if ( aDlgIter->second >>= bUseSystemDialog )
1601 if ( bUseSystemDialog )
1602 nDialog = SFX2_IMPL_DIALOG_SYSTEM;
1603 else
1604 nDialog = SFX2_IMPL_DIALOG_OOO;
1609 // The Dispatch supports parameter FolderName that overwrites SuggestedSaveAsDir
1610 OUString aSuggestedDir = aModelData.GetMediaDescr().getUnpackedValueOrDefault("FolderName", OUString() );
1611 if ( aSuggestedDir.isEmpty() )
1613 aSuggestedDir = aModelData.GetMediaDescr().getUnpackedValueOrDefault("SuggestedSaveAsDir", OUString() );
1614 if ( aSuggestedDir.isEmpty() )
1615 aSuggestedDir = aModelData.GetDocProps().getUnpackedValueOrDefault("SuggestedSaveAsDir", OUString() );
1618 OUString aSuggestedName = aModelData.GetMediaDescr().getUnpackedValueOrDefault("SuggestedSaveAsName", OUString() );
1619 if ( aSuggestedName.isEmpty() )
1620 aSuggestedName = aModelData.GetDocProps().getUnpackedValueOrDefault("SuggestedSaveAsName", OUString() );
1622 OUString sStandardDir;
1623 ::comphelper::SequenceAsHashMap::const_iterator aStdDirIter =
1624 aModelData.GetMediaDescr().find( OUString("StandardDir") );
1625 if ( aStdDirIter != aModelData.GetMediaDescr().end() )
1626 aStdDirIter->second >>= sStandardDir;
1628 css::uno::Sequence< OUString > aDenyList;
1630 ::comphelper::SequenceAsHashMap::const_iterator aDenyListIter =
1631 aModelData.GetMediaDescr().find( OUString("DenyList") );
1632 if ( aDenyListIter != aModelData.GetMediaDescr().end() )
1633 aDenyListIter->second >>= aDenyList;
1635 for (;;)
1637 // in case the dialog is opened a second time the folder should be the same as previously navigated to by the user, not what was handed over by initial parameters
1638 bUseFilterOptions = aModelData.OutputFileDialog( nStoreMode, aFilterProps, bSetStandardName, aSuggestedName, bPreselectPassword, aSuggestedDir, nDialog, sStandardDir, aDenyList );
1639 if ( nStoreMode == SAVEAS_REQUESTED )
1641 // in case of saving check filter for possible alien warning
1642 const OUString aSelFilterName = aModelData.GetMediaDescr().getUnpackedValueOrDefault( sFilterNameString, OUString() );
1643 sal_Int8 nStatusFilterSave = aModelData.CheckFilter( aSelFilterName );
1644 if ( nStatusFilterSave == STATUS_SAVEAS_STANDARDNAME )
1646 // switch to best filter
1647 bSetStandardName = true;
1649 else if ( nStatusFilterSave == STATUS_SAVE )
1651 // user confirmed alien filter or "good" filter is used
1652 break;
1655 else
1656 break;
1659 bDialogUsed = true;
1660 aFileNameIter = aModelData.GetMediaDescr().find( OUString("URL") );
1662 else
1664 // the target file name is provided so check if new filter options
1665 // are provided or old options can be used
1666 if ( aFilterFromMediaDescr == aOldFilterName )
1668 ::comphelper::SequenceAsHashMap::const_iterator aIter =
1669 aModelData.GetDocProps().find( sFilterOptionsString );
1670 if ( aIter != aModelData.GetDocProps().end()
1671 && aModelData.GetMediaDescr().find( sFilterOptionsString ) == aModelData.GetMediaDescr().end() )
1672 aModelData.GetMediaDescr()[aIter->first] = aIter->second;
1674 aIter = aModelData.GetDocProps().find( sFilterDataString );
1675 if ( aIter != aModelData.GetDocProps().end()
1676 && aModelData.GetMediaDescr().find( sFilterDataString ) == aModelData.GetMediaDescr().end() )
1677 aModelData.GetMediaDescr()[aIter->first] = aIter->second;
1681 if ( aFileNameIter != aModelData.GetMediaDescr().end() )
1683 OUString aFileName;
1684 aFileNameIter->second >>= aFileName;
1685 aURL.SetURL( aFileName );
1686 DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" );
1688 ::comphelper::SequenceAsHashMap::const_iterator aIter =
1689 aModelData.GetMediaDescr().find( sFilterNameString );
1691 if ( aIter != aModelData.GetMediaDescr().end() )
1692 aIter->second >>= aFilterName;
1693 else
1694 aModelData.GetMediaDescr()[sFilterNameString] <<= aFilterName;
1696 DBG_ASSERT( !aFilterName.isEmpty(), "Illegal filter!" );
1698 else
1700 SAL_WARN( "sfx.doc", "This code must be unreachable!" );
1701 throw task::ErrorCodeIOException(
1702 "SfxStoringHelper::GUIStoreModel: ERRCODE_IO_INVALIDPARAMETER",
1703 uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
1706 ::comphelper::SequenceAsHashMap::const_iterator aIter =
1707 aModelData.GetMediaDescr().find( OUString("FilterFlags") );
1708 bool bFilterFlagsSet = ( aIter != aModelData.GetMediaDescr().end() );
1710 // check if the filter Dialog has not been called before
1711 if( !( nStoreMode & PDFEXPORT_REQUESTED ) && !( nStoreMode & EPUBEXPORT_REQUESTED ) && !bFilterFlagsSet
1712 && ( ( nStoreMode & EXPORT_REQUESTED ) || bUseFilterOptions ) )
1714 // execute filter options dialog
1715 if ( aModelData.ExecuteFilterDialog_Impl( aFilterName, false ) )
1717 bDialogUsed = true;
1718 // check if the file is a pdf or not and change the storing mode at convenience
1719 if (aFilterName.endsWith("pdf_Export"))
1720 nStoreMode = EXPORT_REQUESTED | PDFEXPORT_REQUESTED;
1724 // so the arguments will not change any more and can be stored to the main location
1725 aArgsSequence = aModelData.GetMediaDescr().getAsConstPropertyValueList();
1727 // store the document and handle it's docinfo
1729 DocumentSettingsGuard aSettingsGuard( aModelData.GetModel(), aModelData.IsRecommendReadOnly(), nStoreMode & EXPORT_REQUESTED );
1731 OSL_ENSURE( aModelData.GetMediaDescr().find( OUString( "Password" ) ) == aModelData.GetMediaDescr().end(), "The Password property of MediaDescriptor should not be used here!" );
1732 if ( officecfg::Office::Common::Save::Document::EditProperty::get()
1733 && ( !aModelData.GetStorable()->hasLocation()
1734 || INetURLObject( aModelData.GetStorable()->getLocation() ) != aURL ) )
1736 // this is definitely not a Save operation
1737 // so the document info can be updated
1739 // on export document info must be preserved
1740 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1741 aModelData.GetModel(), uno::UNO_QUERY_THROW);
1742 uno::Reference<util::XCloneable> xCloneable(
1743 xDPS->getDocumentProperties(), uno::UNO_QUERY_THROW);
1744 uno::Reference<document::XDocumentProperties> xOldDocProps(
1745 xCloneable->createClone(), uno::UNO_QUERY_THROW);
1747 // use dispatch API to show document info dialog
1748 if ( aModelData.ShowDocumentInfoDialog() )
1749 bDialogUsed = true;
1750 else
1752 OSL_FAIL( "Can't execute document info dialog!" );
1755 try {
1756 // Document properties can contain streams that should be freed before storing
1757 aModelData.FreeDocumentProps();
1758 if ( nStoreMode & EXPORT_REQUESTED )
1759 aModelData.GetStorable()->storeToURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
1760 else
1761 aModelData.GetStorable()->storeAsURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
1763 catch( const uno::Exception& )
1765 if ( nStoreMode & EXPORT_REQUESTED )
1767 SetDocInfoState(aModelData.GetModel(), xOldDocProps);
1769 throw;
1772 if ( nStoreMode & EXPORT_REQUESTED )
1774 SetDocInfoState(aModelData.GetModel(), xOldDocProps);
1777 else
1779 // Document properties can contain streams that should be freed before storing
1780 aModelData.FreeDocumentProps();
1782 // this is actually a save operation with different parameters
1783 // so storeTo or storeAs without DocInfo operations are used
1784 if ( nStoreMode & EXPORT_REQUESTED )
1785 aModelData.GetStorable()->storeToURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
1786 else
1787 aModelData.GetStorable()->storeAsURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aArgsSequence );
1790 // Launch PDF viewer
1791 if ( nStoreMode & PDFEXPORT_REQUESTED && !comphelper::LibreOfficeKit::isActive() )
1793 FilterConfigItem aItem(u"Office.Common/Filter/PDF/Export/");
1794 bool aViewPDF = aItem.ReadBool( "ViewPDFAfterExport", false );
1796 if ( aViewPDF )
1798 uno::Reference<XSystemShellExecute> xSystemShellExecute(SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) );
1799 xSystemShellExecute->execute( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), "", SystemShellExecuteFlags::URIS_ONLY );
1803 if ( comphelper::LibreOfficeKit::isActive() )
1805 if ( SfxViewShell* pShell = SfxViewShell::Current() )
1807 OUString sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1808 pShell->libreOfficeKitViewCallback( LOK_CALLBACK_EXPORT_FILE,
1809 OUStringToOString(sURL, RTL_TEXTENCODING_UTF8).getStr() );
1813 return bDialogUsed;
1817 // static
1818 bool SfxStoringHelper::CheckFilterOptionsAppearance(
1819 const uno::Reference< container::XNameAccess >& xFilterCFG,
1820 const OUString& aFilterName )
1822 bool bUseFilterOptions = false;
1824 DBG_ASSERT( xFilterCFG.is(), "No filter configuration!\n" );
1825 if( xFilterCFG.is() )
1827 try {
1828 uno::Sequence < beans::PropertyValue > aProps;
1829 uno::Any aAny = xFilterCFG->getByName( aFilterName );
1830 if ( aAny >>= aProps )
1832 ::comphelper::SequenceAsHashMap aPropsHM( aProps );
1833 if( !aPropsHM.getUnpackedValueOrDefault( "UIComponent", OUString() ).isEmpty() )
1834 bUseFilterOptions = true;
1837 catch( const uno::Exception& )
1842 return bUseFilterOptions;
1846 // static
1847 void SfxStoringHelper::SetDocInfoState(
1848 const uno::Reference< frame::XModel >& xModel,
1849 const uno::Reference< document::XDocumentProperties>& i_xOldDocProps )
1851 uno::Reference<document::XDocumentPropertiesSupplier> const
1852 xModelDocPropsSupplier(xModel, uno::UNO_QUERY_THROW);
1853 uno::Reference<document::XDocumentProperties> const xDocPropsToFill =
1854 xModelDocPropsSupplier->getDocumentProperties();
1855 uno::Reference< beans::XPropertySet > const xPropSet(
1856 i_xOldDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
1858 uno::Reference< util::XModifiable > xModifiable( xModel, uno::UNO_QUERY );
1859 if ( !xModifiable.is() )
1860 throw uno::RuntimeException();
1862 bool bIsModified = xModifiable->isModified();
1866 uno::Reference< beans::XPropertySet > const xSet(
1867 xDocPropsToFill->getUserDefinedProperties(), uno::UNO_QUERY);
1868 uno::Reference< beans::XPropertyContainer > xContainer( xSet, uno::UNO_QUERY );
1869 uno::Reference< beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo();
1870 const uno::Sequence< beans::Property > lProps = xSetInfo->getProperties();
1871 for (const beans::Property& rProp : lProps)
1873 uno::Any aValue = xPropSet->getPropertyValue( rProp.Name );
1874 if ( rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE )
1878 // QUESTION: DefaultValue?!
1879 xContainer->addProperty( rProp.Name, rProp.Attributes, aValue );
1881 catch (beans::PropertyExistException const&) {}
1884 // it is possible that the propertysets from XML and binary files differ; we shouldn't break then
1885 xSet->setPropertyValue( rProp.Name, aValue );
1887 catch ( const uno::Exception& ) {}
1891 // sigh... have to set these manually I'm afraid... wonder why
1892 // SfxObjectShell doesn't handle this internally, should be easier
1893 xDocPropsToFill->setAuthor(i_xOldDocProps->getAuthor());
1894 xDocPropsToFill->setGenerator(i_xOldDocProps->getGenerator());
1895 xDocPropsToFill->setCreationDate(i_xOldDocProps->getCreationDate());
1896 xDocPropsToFill->setTitle(i_xOldDocProps->getTitle());
1897 xDocPropsToFill->setSubject(i_xOldDocProps->getSubject());
1898 xDocPropsToFill->setDescription(i_xOldDocProps->getDescription());
1899 xDocPropsToFill->setKeywords(i_xOldDocProps->getKeywords());
1900 xDocPropsToFill->setModifiedBy(i_xOldDocProps->getModifiedBy());
1901 xDocPropsToFill->setModificationDate(i_xOldDocProps->getModificationDate());
1902 xDocPropsToFill->setPrintedBy(i_xOldDocProps->getPrintedBy());
1903 xDocPropsToFill->setPrintDate(i_xOldDocProps->getPrintDate());
1904 xDocPropsToFill->setAutoloadURL(i_xOldDocProps->getAutoloadURL());
1905 xDocPropsToFill->setAutoloadSecs(i_xOldDocProps->getAutoloadSecs());
1906 xDocPropsToFill->setDefaultTarget(i_xOldDocProps->getDefaultTarget());
1907 xDocPropsToFill->setEditingCycles(i_xOldDocProps->getEditingCycles());
1908 xDocPropsToFill->setEditingDuration(i_xOldDocProps->getEditingDuration());
1909 // other attributes e.g. DocumentStatistics are not editable from dialog
1911 catch (const uno::Exception&)
1913 TOOLS_INFO_EXCEPTION("sfx.doc", "SetDocInfoState");
1916 // set the modified flag back if required
1917 if ( bIsModified != bool(xModifiable->isModified()) )
1918 xModifiable->setModified( bIsModified );
1922 // static
1923 bool SfxStoringHelper::WarnUnacceptableFormat( const uno::Reference< frame::XModel >& xModel,
1924 std::u16string_view aOldUIName,
1925 const OUString& aDefExtension,
1926 bool bDefIsAlien )
1928 if ( !officecfg::Office::Common::Save::Document::WarnAlienFormat::get() )
1929 return true;
1931 weld::Window* pWin = SfxStoringHelper::GetModelWindow(xModel);
1932 SfxAlienWarningDialog aDlg(pWin, aOldUIName, aDefExtension, bDefIsAlien);
1934 return aDlg.run() == RET_OK;
1937 uno::Reference<awt::XWindow> SfxStoringHelper::GetModelXWindow(const uno::Reference<frame::XModel>& xModel)
1939 try {
1940 if ( xModel.is() )
1942 uno::Reference< frame::XController > xController = xModel->getCurrentController();
1943 if ( xController.is() )
1945 uno::Reference< frame::XFrame > xFrame = xController->getFrame();
1946 if ( xFrame.is() )
1948 return xFrame->getContainerWindow();
1953 catch ( const uno::Exception& )
1957 return uno::Reference<awt::XWindow>();
1960 weld::Window* SfxStoringHelper::GetModelWindow( const uno::Reference< frame::XModel >& xModel )
1962 weld::Window* pWin = nullptr;
1966 pWin = Application::GetFrameWeld(GetModelXWindow(xModel));
1968 catch (const uno::Exception&)
1972 return pWin;
1975 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */