From 3fa39a4dadc8e2777185465a6f7c9968c8cf44d1 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 11 Apr 2025 18:21:37 +0200 Subject: [PATCH] tdf#165917 Improve Export directory pre-selection When calling an Export dialog (PDF, epub, ...) the following folder will be preselected: For stored documents: * Current document directory * If another directory was chosen for the export, that directory will be preselected for subsequent exports (stored only during runtime - per document) For unstored documents: * The last used export directory is restored (last used in unsaved doc) Change-Id: I97595d164cf1d3604166c38aa2a5ed31be56f113 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184062 Reviewed-by: Samuel Mehrbrodt Tested-by: Jenkins --- ...b-can-be-private-symbols.functions.results | 1 - cui/source/options/optpath.cxx | 3 -- include/sfx2/app.hxx | 3 -- include/sfx2/sfxsids.hrc | 1 + include/unotools/mediadescriptor.hxx | 1 + .../com/sun/star/document/MediaDescriptor.idl | 5 ++ sfx2/inc/guisaveas.hxx | 12 ++--- sfx2/source/appl/app.cxx | 43 ----------------- sfx2/source/appl/appuno.cxx | 18 ++++++++ sfx2/source/dialog/filedlghelper.cxx | 24 ++++------ sfx2/source/dialog/filedlgimpl.hxx | 1 + sfx2/source/doc/guisaveas.cxx | 46 +++++++++++++------ sfx2/source/doc/sfxbasemodel.cxx | 8 ++++ sfx2/source/inc/appdata.hxx | 1 - sfx2/source/view/ipclient.cxx | 2 +- 15 files changed, 82 insertions(+), 87 deletions(-) diff --git a/bin/find-mergedlib-can-be-private-symbols.functions.results b/bin/find-mergedlib-can-be-private-symbols.functions.results index 3e4e10e20882..4e45fc09b1f9 100644 --- a/bin/find-mergedlib-can-be-private-symbols.functions.results +++ b/bin/find-mergedlib-can-be-private-symbols.functions.results @@ -3687,7 +3687,6 @@ SfxApplication::Invalidate(unsigned short) SfxApplication::MacroOrganizer(weld::Window*, com::sun::star::uno::Reference const&, short) SfxApplication::RegisterInterface(SfxModule const*) SfxApplication::ReleaseIndex(unsigned short) -SfxApplication::ResetLastDir() SfxApplication::SaveBasicAndDialogContainer() const SfxApplication::SfxApplication() SfxApplication::loadBrandSvg(char const*, BitmapEx&, int) diff --git a/cui/source/options/optpath.cxx b/cui/source/options/optpath.cxx index 0ec4b206913f..4ddf728a1bcf 100644 --- a/cui/source/options/optpath.cxx +++ b/cui/source/options/optpath.cxx @@ -464,9 +464,6 @@ void SvxPathTabPage::ChangeCurrentEntry( const OUString& _rFolder ) // will be used for the next open dialog. SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME ); aDlgOpt.Delete(); - // Reset also last used dir in the sfx application instance - SfxApplication *pSfxApp = SfxGetpApp(); - pSfxApp->ResetLastDir(); } } diff --git a/include/sfx2/app.hxx b/include/sfx2/app.hxx index e83cd9b2f66c..a6add5c5ed54 100644 --- a/include/sfx2/app.hxx +++ b/include/sfx2/app.hxx @@ -167,7 +167,6 @@ public: SAL_DLLPRIVATE virtual void Invalidate(sal_uInt16 nId = 0) override; void NotifyEvent(const SfxEventHint& rEvent, bool bSynchron = true ); bool IsDowning() const; - void ResetLastDir(); SAL_DLLPRIVATE SfxDispatcher* GetAppDispatcher_Impl(); SAL_DLLPRIVATE SfxDispatcher* GetDispatcher_Impl(); @@ -206,8 +205,6 @@ public: SAL_DLLPRIVATE static void OfaState_Impl(SfxItemSet &); SAL_DLLPRIVATE void SetProgress_Impl(SfxProgress *); - SAL_DLLPRIVATE const OUString& GetLastDir_Impl() const; - SAL_DLLPRIVATE void SetLastDir_Impl( const OUString & ); SAL_DLLPRIVATE static void Registrations_Impl(); SAL_DLLPRIVATE SfxWorkWindow* GetWorkWindow_Impl(const SfxViewFrame *pFrame) const; diff --git a/include/sfx2/sfxsids.hrc b/include/sfx2/sfxsids.hrc index c71ddca61559..625d59e21aba 100644 --- a/include/sfx2/sfxsids.hrc +++ b/include/sfx2/sfxsids.hrc @@ -301,6 +301,7 @@ class SvxZoomItem; #define SID_GPGSIGN TypedWhichId(SID_SFX_START + 1748) #define FN_INVERT_BACKGROUND (SID_SFX_START + 1749) #define SID_MACROMANAGER (SID_SFX_START + 1750) +#define SID_EXPORTDIRECTORY TypedWhichId(SID_SFX_START + 1751) // SID_SFX_free_END (SID_SFX_START + 3999) #define SID_OPEN_NEW_VIEW TypedWhichId(SID_SFX_START + 520) diff --git a/include/unotools/mediadescriptor.hxx b/include/unotools/mediadescriptor.hxx index 67ae2704246e..07d678d588a3 100644 --- a/include/unotools/mediadescriptor.hxx +++ b/include/unotools/mediadescriptor.hxx @@ -102,6 +102,7 @@ class UNOTOOLS_DLLPUBLIC MediaDescriptor : public comphelper::SequenceAsHashMap static constexpr OUString PROP_VIEWONLY = u"ViewOnly"_ustr; static constexpr OUString PROP_DOCUMENTBASEURL = u"DocumentBaseURL"_ustr; static constexpr OUString PROP_SUGGESTEDSAVEASNAME = u"SuggestedSaveAsName"_ustr; + static constexpr OUString PROP_EXPORTDIRECTORY = u"ExportDirectory"_ustr; static constexpr OUString PROP_AUTOSAVEEVENT = u"AutoSaveEvent"_ustr; // interface diff --git a/offapi/com/sun/star/document/MediaDescriptor.idl b/offapi/com/sun/star/document/MediaDescriptor.idl index 6d0982b5ee64..e6f73d3afdcd 100644 --- a/offapi/com/sun/star/document/MediaDescriptor.idl +++ b/offapi/com/sun/star/document/MediaDescriptor.idl @@ -449,6 +449,11 @@ service MediaDescriptor */ [optional,property] string SuggestedSaveAsName; + /** Directory to be used when exporting (to PDF, epub, ...). + Defaults to the current document directory. + */ + [optional,property] string ExportDirectory; + /** name of the template instead of the URL

diff --git a/sfx2/inc/guisaveas.hxx b/sfx2/inc/guisaveas.hxx index af12ba7ba4a8..099a5f7ea778 100644 --- a/sfx2/inc/guisaveas.hxx +++ b/sfx2/inc/guisaveas.hxx @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -82,7 +82,7 @@ public: SfxStoringHelper(); bool GUIStoreModel( - const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::frame::XModel2 >& xModel, std::u16string_view aSlotName, css::uno::Sequence< css::beans::PropertyValue >& aArgsSequence, bool bPreselectPassword, @@ -95,18 +95,18 @@ public: static void SetDocInfoState( - const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::frame::XModel2 >& xModel, const css::uno::Reference< css::document::XDocumentProperties>& i_xOldDocInfo ); static bool WarnUnacceptableFormat( - const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::frame::XModel2 >& xModel, std::u16string_view aOldUIName, std::u16string_view aExtension, const OUString& aDefExtension, bool rDefaultIsAlien ); - static css::uno::Reference GetModelXWindow(const css::uno::Reference& rModel); - static weld::Window* GetModelWindow( const css::uno::Reference< css::frame::XModel >& xModel ); + static css::uno::Reference GetModelXWindow(const css::uno::Reference& rModel); + static weld::Window* GetModelWindow( const css::uno::Reference< css::frame::XModel2 >& xModel ); }; diff --git a/sfx2/source/appl/app.cxx b/sfx2/source/appl/app.cxx index b2a932c16e67..30ce6d0f6b6e 100644 --- a/sfx2/source/appl/app.cxx +++ b/sfx2/source/appl/app.cxx @@ -183,49 +183,6 @@ SfxApplication::~SfxApplication() } -const OUString& SfxApplication::GetLastDir_Impl() const - -/* [Description] - - Internal method by which the last set directory with the method - in SFX is returned. - - This is usually the most recently addressed by the - SfxFileDialog directory. - - [Cross-reference] - -*/ - -{ - return pImpl->aLastDir; -} - -void SfxApplication::SetLastDir_Impl -( - const OUString& rNewDir /* Complete directory path as a string */ -) - -/* [Description] - - Internal Method, by which a directory path is set that was last addressed - (eg by the SfxFileDialog). - - [Cross-reference] - -*/ - -{ - pImpl->aLastDir = rNewDir; -} - - -void SfxApplication::ResetLastDir() -{ - pImpl->aLastDir.clear(); -} - - SfxDispatcher* SfxApplication::GetDispatcher_Impl() { return pImpl->pViewFrame ? pImpl->pViewFrame->GetDispatcher() : &*pImpl->pAppDispat; diff --git a/sfx2/source/appl/appuno.cxx b/sfx2/source/appl/appuno.cxx index e0a8ad545977..7a0e32473344 100644 --- a/sfx2/source/appl/appuno.cxx +++ b/sfx2/source/appl/appuno.cxx @@ -144,6 +144,7 @@ constexpr OUString sDenyList = u"DenyList"_ustr; constexpr OUString sModifyPasswordInfo = u"ModifyPasswordInfo"_ustr; constexpr OUString sSuggestedSaveAsDir = u"SuggestedSaveAsDir"_ustr; constexpr OUString sSuggestedSaveAsName = u"SuggestedSaveAsName"_ustr; +constexpr OUString sExportDirectory = u"ExportDirectory"_ustr; constexpr OUString sEncryptionData = u"EncryptionData"_ustr; constexpr OUString sFailOnWarning = u"FailOnWarning"_ustr; constexpr OUString sDocumentService = u"DocumentService"_ustr; @@ -810,6 +811,14 @@ void TransformParameters( sal_uInt16 nSlotId, const uno::Sequence>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for ExportDirectoy" ); + if (bOK) + rSet.Put( SfxStringItem( SID_EXPORTDIRECTORY, sVal ) ); + } else if (aName == sDocumentService) { OUString aVal; @@ -1086,6 +1095,8 @@ void TransformItems( sal_uInt16 nSlotId, const SfxItemSet& rSet, uno::SequenceGetValue(); } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_EXPORTDIRECTORY, false) ) + { + pValue[nActProp].Name = sExportDirectory; + pValue[nActProp++].Value <<= pItem->GetValue(); + } if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_SERVICE, false) ) { pValue[nActProp].Name = sDocumentService; diff --git a/sfx2/source/dialog/filedlghelper.cxx b/sfx2/source/dialog/filedlghelper.cxx index 04c4cbe50459..1950de53b3ab 100644 --- a/sfx2/source/dialog/filedlghelper.cxx +++ b/sfx2/source/dialog/filedlghelper.cxx @@ -925,7 +925,8 @@ FileDialogHelper_Impl::FileDialogHelper_Impl( const OUString& sStandardDir, const css::uno::Sequence< OUString >& rDenyList ) - :maPreviewIdle("sfx2 FileDialogHelper_Impl maPreviewIdle") + :msStandardDir ( sStandardDir ) + ,maPreviewIdle("sfx2 FileDialogHelper_Impl maPreviewIdle") ,m_nDialogType ( nDialogType ) ,meContext ( FileDialogHelper::UnknownContext ) { @@ -2194,16 +2195,11 @@ void FileDialogHelper_Impl::saveConfig() aDlgOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) ); } - // Store to config, if explicit context is set. Otherwise store in (global) runtime var. - if (meContext != FileDialogHelper::UnknownContext) + // Store to config, if explicit context is set (and default directory is not given) + if (meContext != FileDialogHelper::UnknownContext && msStandardDir.isEmpty()) { SaveLastDirectory(FileDialogHelper::contextToString(meContext), getPath()); } - else - { - SfxApplication *pSfxApp = SfxGetpApp(); - pSfxApp->SetLastDir_Impl( getPath() ); - } } OUString FileDialogHelper_Impl::getInitPath(std::u16string_view _rFallback, @@ -2211,7 +2207,12 @@ OUString FileDialogHelper_Impl::getInitPath(std::u16string_view _rFallback, { OUString sPath; // Load from config, if explicit context is set. Otherwise load from (global) runtime var. - if (meContext != FileDialogHelper::UnknownContext) + if (meContext == FileDialogHelper::UnknownContext || !msStandardDir.isEmpty()) + { + // For export, the default directory is passed on + sPath = msStandardDir; + } + else { OUString sContext = FileDialogHelper::contextToString(meContext); Reference set(officecfg::Office::Common::Misc::FilePickerLastDirectory::get()); @@ -2226,11 +2227,6 @@ OUString FileDialogHelper_Impl::getInitPath(std::u16string_view _rFallback, { } } - else - { - SfxApplication *pSfxApp = SfxGetpApp(); - sPath = pSfxApp->GetLastDir_Impl(); - } if ( sPath.isEmpty() ) sPath = o3tl::getToken(_rFallback, _nFallbackToken, ' ' ); diff --git a/sfx2/source/dialog/filedlgimpl.hxx b/sfx2/source/dialog/filedlgimpl.hxx index b7ec174d0bb1..6809d8ca053f 100644 --- a/sfx2/source/dialog/filedlgimpl.hxx +++ b/sfx2/source/dialog/filedlgimpl.hxx @@ -61,6 +61,7 @@ namespace sfx2 OUString maCurFilter; OUString maSelectFilter; OUString maButtonLabel; + OUString msStandardDir; Idle maPreviewIdle; Graphic maGraphic; diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx index 631511eea7be..7a06f10c795d 100644 --- a/sfx2/source/doc/guisaveas.cxx +++ b/sfx2/source/doc/guisaveas.cxx @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -220,7 +221,7 @@ class DocumentSettingsGuard bool m_bRestoreSettings; public: - DocumentSettingsGuard( const uno::Reference< frame::XModel >& xModel, bool bReadOnly, bool bRestore ) + DocumentSettingsGuard( const uno::Reference< frame::XModel2 >& xModel, bool bReadOnly, bool bRestore ) : m_bPreserveReadOnly( false ) , m_bReadOnlySupported( false ) , m_bRestoreSettings( bRestore ) @@ -272,7 +273,7 @@ public: class ModelData_Impl { SfxStoringHelper* m_pOwner; - uno::Reference< frame::XModel > m_xModel; + uno::Reference< frame::XModel2 > m_xModel; uno::Reference< frame::XStorable > m_xStorable; uno::Reference< frame::XStorable2 > m_xStorable2; @@ -292,14 +293,14 @@ class ModelData_Impl public: ModelData_Impl( SfxStoringHelper& aOwner, - uno::Reference< frame::XModel > xModel, + uno::Reference< frame::XModel2 > xModel, const uno::Sequence< beans::PropertyValue >& aMediaDescr ); ~ModelData_Impl(); void FreeDocumentProps(); - uno::Reference< frame::XModel > const & GetModel() const; + uno::Reference< frame::XModel2 > const & GetModel() const; uno::Reference< frame::XStorable > const & GetStorable(); uno::Reference< frame::XStorable2 > const & GetStorable2(); @@ -338,7 +339,7 @@ public: bool bPreselectPassword, OUString& aSuggestedDir, sal_Int16 nDialog, - const OUString& rStandardDir, + OUString& rStandardDir, const css::uno::Sequence< OUString >& rDenyList ); @@ -352,7 +353,7 @@ public: ModelData_Impl::ModelData_Impl( SfxStoringHelper& aOwner, - uno::Reference< frame::XModel > xModel, + uno::Reference< frame::XModel2 > xModel, const uno::Sequence< beans::PropertyValue >& aMediaDescr ) : m_pOwner( &aOwner ) , m_xModel(std::move( xModel )) @@ -380,7 +381,7 @@ void ModelData_Impl::FreeDocumentProps() } -uno::Reference< frame::XModel > const & ModelData_Impl::GetModel() const +uno::Reference< frame::XModel2 > const & ModelData_Impl::GetModel() const { if ( !m_xModel.is() ) throw uno::RuntimeException(); @@ -388,7 +389,6 @@ uno::Reference< frame::XModel > const & ModelData_Impl::GetModel() const return m_xModel; } - uno::Reference< frame::XStorable > const & ModelData_Impl::GetStorable() { if ( !m_xStorable.is() ) @@ -894,7 +894,7 @@ bool ModelData_Impl::OutputFileDialog( sal_Int16 nStoreMode, bool bPreselectPassword, OUString& aSuggestedDir, sal_Int16 nDialog, - const OUString& rStandardDir, + OUString& rStandardDir, const css::uno::Sequence< OUString >& rDenyList) { if ( nStoreMode == SAVEASREMOTE_REQUESTED ) @@ -955,6 +955,13 @@ bool ModelData_Impl::OutputFileDialog( sal_Int16 nStoreMode, weld::Window* pFrameWin = SfxStoringHelper::GetModelWindow(m_xModel); if ( ( nStoreMode & EXPORT_REQUESTED ) && !( nStoreMode & WIDEEXPORT_REQUESTED ) ) { + const OUString aBaseUrl = GetDocProps().getUnpackedValueOrDefault("DocumentBaseURL", OUString()); + OUString aExportDir = GetDocProps().getUnpackedValueOrDefault("ExportDirectory", aBaseUrl); + INetURLObject aObj( aExportDir ); + aObj.removeSegment(); + aExportDir = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if (!aExportDir.isEmpty()) + rStandardDir = aExportDir; if ( ( nStoreMode & PDFEXPORT_REQUESTED ) && !aPreselectedFilterPropsHM.empty() ) { // this is a PDF export @@ -985,7 +992,6 @@ bool ModelData_Impl::OutputFileDialog( sal_Int16 nStoreMode, eCtxt = sfx2::FileDialogHelper::WriterExport; else if ( aDocServiceName == "com.sun.star.sheet.SpreadsheetDocument" ) eCtxt = sfx2::FileDialogHelper::CalcExport; - if ( eCtxt != sfx2::FileDialogHelper::UnknownContext ) pFileDlg->SetContext( eCtxt ); @@ -1247,6 +1253,16 @@ bool ModelData_Impl::OutputFileDialog( sal_Int16 nStoreMode, GetMediaDescr()[u"URL"_ustr] <<= aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); GetMediaDescr()[sFilterNameString] <<= aFilterName; + // for Export - keep a runtime var for each document where the document was last exported to + if (GetStorable()->hasLocation() && (nStoreMode & EXPORT_REQUESTED)) + { + uno::Sequence< beans::PropertyValue > descriptor{ + beans::PropertyValue(u"ExportDirectory"_ustr, + -1, uno::Any(aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE )), beans::PropertyState_DIRECT_VALUE), + }; + GetModel()->setArgs(descriptor); + } + return bUseFilterOptions; } @@ -1457,7 +1473,7 @@ uno::Reference< css::frame::XModuleManager2 > const & SfxStoringHelper::GetModul return m_xModuleManager; } -bool SfxStoringHelper::GUIStoreModel( const uno::Reference< frame::XModel >& xModel, +bool SfxStoringHelper::GUIStoreModel( const uno::Reference< frame::XModel2 >& xModel, std::u16string_view aSlotName, uno::Sequence< beans::PropertyValue >& aArgsSequence, bool bPreselectPassword, @@ -2023,7 +2039,7 @@ bool SfxStoringHelper::CheckFilterOptionsAppearance( // static void SfxStoringHelper::SetDocInfoState( - const uno::Reference< frame::XModel >& xModel, + const uno::Reference< frame::XModel2 >& xModel, const uno::Reference< document::XDocumentProperties>& i_xOldDocProps ) { uno::Reference const @@ -2098,7 +2114,7 @@ void SfxStoringHelper::SetDocInfoState( // static -bool SfxStoringHelper::WarnUnacceptableFormat( const uno::Reference< frame::XModel >& xModel, +bool SfxStoringHelper::WarnUnacceptableFormat( const uno::Reference< frame::XModel2 >& xModel, std::u16string_view aOldUIName, std::u16string_view aExtension, const OUString& aDefExtension, @@ -2135,7 +2151,7 @@ bool SfxStoringHelper::WarnUnacceptableFormat( const uno::Reference< frame::XMod return nResult == RET_YES; } -uno::Reference SfxStoringHelper::GetModelXWindow(const uno::Reference& xModel) +uno::Reference SfxStoringHelper::GetModelXWindow(const uno::Reference& xModel) { try { if ( xModel.is() ) @@ -2158,7 +2174,7 @@ uno::Reference SfxStoringHelper::GetModelXWindow(const uno::Refere return uno::Reference(); } -weld::Window* SfxStoringHelper::GetModelWindow( const uno::Reference< frame::XModel >& xModel ) +weld::Window* SfxStoringHelper::GetModelWindow( const uno::Reference< frame::XModel2 >& xModel ) { weld::Window* pWin = nullptr; diff --git a/sfx2/source/doc/sfxbasemodel.cxx b/sfx2/source/doc/sfxbasemodel.cxx index 2d9518530416..8b21c5f74ba5 100644 --- a/sfx2/source/doc/sfxbasemodel.cxx +++ b/sfx2/source/doc/sfxbasemodel.cxx @@ -1135,6 +1135,14 @@ void SAL_CALL SfxBaseModel::setArgs(const Sequence& aArgs) ok = true; } } + else if (rArg.Name == "ExportDirectory") + { + if (rArg.Value >>= sValue) + { + pMedium->GetItemSet().Put(SfxStringItem(SID_EXPORTDIRECTORY, sValue)); + ok = true; + } + } else if (rArg.Name == "LockContentExtraction") { if (rArg.Value >>= bValue) diff --git a/sfx2/source/inc/appdata.hxx b/sfx2/source/inc/appdata.hxx index 400dcae776d6..491a2ba042ac 100644 --- a/sfx2/source/inc/appdata.hxx +++ b/sfx2/source/inc/appdata.hxx @@ -61,7 +61,6 @@ class SfxAppData_Impl { public: IndexBitSet aIndexBitSet; // for counting noname documents - OUString aLastDir; // for IO dialog // DDE stuff std::unique_ptr pDdeService; diff --git a/sfx2/source/view/ipclient.cxx b/sfx2/source/view/ipclient.cxx index 2215e17469e1..a44d7419373c 100644 --- a/sfx2/source/view/ipclient.cxx +++ b/sfx2/source/view/ipclient.cxx @@ -906,7 +906,7 @@ ErrCodeMsg SfxInPlaceClient::DoVerb(sal_Int32 nVerb) { svt::EmbeddedObjectRef::TryRunningState( m_xImp->m_xObject ); // TODO/LATER: this special verb should disappear when outplace activation is completely available - uno::Reference< frame::XModel > xEmbModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY ); + uno::Reference< frame::XModel2 > xEmbModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY ); if ( xEmbModel.is() ) { bSaveCopyAs = true;