diff options
-rw-r--r-- | compilerplugins/clang/badstatics.cxx | 4 | ||||
-rw-r--r-- | include/sfx2/docfile.hxx | 12 | ||||
-rw-r--r-- | offapi/UnoApi_offapi.mk | 2 | ||||
-rw-r--r-- | offapi/com/sun/star/document/ReadOnlyOpenRequest.idl | 51 | ||||
-rw-r--r-- | offapi/com/sun/star/document/ReloadEditableRequest.idl | 50 | ||||
-rw-r--r-- | sfx2/source/doc/docfile.cxx | 517 | ||||
-rw-r--r-- | sfx2/source/view/viewfrm.cxx | 15 | ||||
-rw-r--r-- | uui/Library_uui.mk | 2 | ||||
-rw-r--r-- | uui/inc/strings.hrc | 21 | ||||
-rw-r--r-- | uui/source/alreadyopen.cxx | 1 | ||||
-rw-r--r-- | uui/source/iahndl-locking.cxx | 104 | ||||
-rw-r--r-- | uui/source/iahndl.cxx | 6 | ||||
-rw-r--r-- | uui/source/iahndl.hxx | 6 | ||||
-rw-r--r-- | uui/source/lockcorrupt.cxx | 1 | ||||
-rw-r--r-- | uui/source/lockfailed.cxx | 1 | ||||
-rw-r--r-- | uui/source/openlocked.cxx | 1 | ||||
-rw-r--r-- | uui/source/readonlyopen.cxx | 38 | ||||
-rw-r--r-- | uui/source/readonlyopen.hxx | 35 | ||||
-rw-r--r-- | uui/source/reloadeditable.cxx | 37 | ||||
-rw-r--r-- | uui/source/reloadeditable.hxx | 35 |
20 files changed, 919 insertions, 20 deletions
diff --git a/compilerplugins/clang/badstatics.cxx b/compilerplugins/clang/badstatics.cxx index bb6241eafa5e..0856d8faac39 100644 --- a/compilerplugins/clang/badstatics.cxx +++ b/compilerplugins/clang/badstatics.cxx @@ -218,6 +218,10 @@ public: // Windows-only extensions/source/scanner/scanwin.cxx, problematic // Twain::mpThread -> ShimListenerThread::mxTopWindow released via Twain::Reset // clearing mpThread + || name == "g_newReadOnlyDocs" + // sfx2/source/doc/docfile.cxx, warning about map's key + || name == "g_existingReadOnlyDocs" + // sfx2/source/doc/docfile.cxx, warning about map's key ) // these variables appear unproblematic { return true; diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx index ad316dd2fca8..83bcb91c5812 100644 --- a/include/sfx2/docfile.hxx +++ b/include/sfx2/docfile.hxx @@ -30,6 +30,7 @@ #include <svl/itemset.hxx> #include <tools/link.hxx> #include <tools/stream.hxx> +#include <mutex> namespace com::sun::star::beans { struct PropertyValue; } namespace com::sun::star::embed { class XStorage; } @@ -53,6 +54,7 @@ class SfxMedium_Impl; class INetURLObject; class SfxFrame; class DateTime; +struct ImplSVEvent; namespace weld { @@ -93,6 +95,15 @@ public: virtual ~SfxMedium() override; + DECL_STATIC_LINK(SfxMedium, ShowReloadEditableDialog, void*, void); + bool CheckCanGetLockfile() const; + void SetOriginallyReadOnly(bool val); + void AddToCheckEditableWorkerList(); + void SetWorkerReloadEvent(ImplSVEvent* pEvent); + ImplSVEvent* GetWorkerReloadEvent() const; + std::shared_ptr<std::recursive_mutex> GetCheckEditableMutex() const; + void CancelCheckEditableEntry(bool bRemoveEvent = true); + void UseInteractionHandler( bool ); css::uno::Reference< css::task::XInteractionHandler > GetInteractionHandler( bool bGetAlways = false ); @@ -291,6 +302,7 @@ private: bool bIsLoading, bool bOwnLock, bool bHandleSysLocked); enum class MessageDlg { LockFileIgnore, LockFileCorrupt }; bool ShowLockFileProblemDialog(MessageDlg nWhichDlg); + bool ShowReadOnlyOpenDialog(); }; diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 4180c1194eff..01dd48280fea 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -2211,7 +2211,9 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/document,\ NoSuchFilterRequest \ OwnLockOnDocumentRequest \ PrinterIndependentLayout \ + ReadOnlyOpenRequest \ RedlineDisplayType \ + ReloadEditableRequest \ UndoContextNotClosedException \ UndoFailedException \ UndoManagerEvent \ diff --git a/offapi/com/sun/star/document/ReadOnlyOpenRequest.idl b/offapi/com/sun/star/document/ReadOnlyOpenRequest.idl new file mode 100644 index 000000000000..49a82b7016f9 --- /dev/null +++ b/offapi/com/sun/star/document/ReadOnlyOpenRequest.idl @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 . + */ +#ifndef __com_sun_star_document_ReadOnlyOpenRequest_idl__ +#define __com_sun_star_document_ReadOnlyOpenRequest_idl__ + +#include <com/sun/star/uno/Exception.idl> + +module com +{ + module sun + { + module star + { + module document + { + /** Is used for interaction handle to query user decision regarding whether to open + a document read-only and whether to notify the user when the document becomes + editable. + + @since LibreOffice 7.2 + */ + published exception ReadOnlyOpenRequest : ::com::sun::star::uno::Exception + { + /** The URL of the document that is open but was made editable. + */ + string DocumentURL; + }; + }; + }; + }; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/offapi/com/sun/star/document/ReloadEditableRequest.idl b/offapi/com/sun/star/document/ReloadEditableRequest.idl new file mode 100644 index 000000000000..f091bcf26f69 --- /dev/null +++ b/offapi/com/sun/star/document/ReloadEditableRequest.idl @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 . + */ +#ifndef __com_sun_star_document_ReloadEditableRequest_idl__ +#define __com_sun_star_document_ReloadEditableRequest_idl__ + +#include <com/sun/star/uno/Exception.idl> + +module com +{ + module sun + { + module star + { + module document + { + /** Is used for interaction handle to query user decision regarding reloading a + document that was recently made editable. + + @since LibreOffice 7.2 + */ + published exception ReloadEditableRequest : ::com::sun::star::uno::Exception + { + /** The URL of the document that is open but was made editable. + */ + string DocumentURL; + }; + }; + }; + }; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 45aaad485f83..c7655e2c5f62 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -38,11 +38,15 @@ #include <com/sun/star/document/LockFileIgnoreRequest.hpp> #include <com/sun/star/document/LockFileCorruptRequest.hpp> #include <com/sun/star/document/ChangedByOthersRequest.hpp> +#include <com/sun/star/document/ReadOnlyOpenRequest.hpp> +#include <com/sun/star/document/ReloadEditableRequest.hpp> #include <com/sun/star/embed/XTransactedObject.hpp> #include <com/sun/star/embed/ElementModes.hpp> #include <com/sun/star/embed/UseBackupException.hpp> #include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/frame/Desktop.hpp> #include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XTerminateListener.hpp> #include <com/sun/star/graphic/XGraphic.hpp> #include <com/sun/star/ucb/ContentCreationException.hpp> #include <com/sun/star/ucb/InteractiveIOException.hpp> @@ -108,6 +112,7 @@ #include <sfx2/app.hxx> #include <sfx2/frame.hxx> +#include <sfx2/dispatch.hxx> #include <sfx2/fcontnr.hxx> #include <sfx2/docfilt.hxx> #include <sfx2/sfxsids.hrc> @@ -120,6 +125,10 @@ #include <tools/diagnose_ex.h> #include <unotools/fltrcfg.hxx> #include <sfx2/digitalsignatures.hxx> +#include <sfx2/viewfrm.hxx> +#include <comphelper/threadpool.hxx> +#include <condition_variable> +#include <comphelper/scopeguard.hxx> #include <com/sun/star/io/WrongFormatException.hpp> @@ -133,6 +142,28 @@ using namespace ::com::sun::star::beans; using namespace ::com::sun::star::io; using namespace ::com::sun::star::security; +namespace +{ + +struct ReadOnlyMediumEntry +{ + ReadOnlyMediumEntry(std::shared_ptr<std::recursive_mutex> pMutex, + std::shared_ptr<bool> pIsDestructed) + : _pMutex(pMutex) + , _pIsDestructed(pIsDestructed) + { + } + std::shared_ptr<std::recursive_mutex> _pMutex; + std::shared_ptr<bool> _pIsDestructed; +}; + +} + +static std::mutex g_chkReadOnlyGlobalMutex; +static bool g_bChkReadOnlyTaskRunning = false; +static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_newReadOnlyDocs; +static std::unordered_map<SfxMedium*, std::shared_ptr<ReadOnlyMediumEntry>> g_existingReadOnlyDocs; + namespace { #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT @@ -232,8 +263,85 @@ bool IsFileMovable(const INetURLObject& rURL) #endif } +class CheckReadOnlyTaskTerminateListener + : public ::cppu::WeakImplHelper<css::frame::XTerminateListener> +{ +public: + // XEventListener + void SAL_CALL disposing(const css::lang::EventObject& Source) override; + + // XTerminateListener + void SAL_CALL queryTermination(const css::lang::EventObject& aEvent) override; + void SAL_CALL notifyTermination(const css::lang::EventObject& aEvent) override; + + bool bIsTerminated = false; + std::condition_variable mCond; + std::mutex mMutex; +}; + +class CheckReadOnlyTask : public comphelper::ThreadTask +{ +public: + CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag); + ~CheckReadOnlyTask(); + + virtual void doWork() override; + +private: + rtl::Reference<CheckReadOnlyTaskTerminateListener> m_xListener; +}; + } // anonymous namespace +CheckReadOnlyTask::CheckReadOnlyTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag) + : ThreadTask(pTag) + , m_xListener(new CheckReadOnlyTaskTerminateListener) +{ + Reference<css::frame::XDesktop> xDesktop + = css::frame::Desktop::create(comphelper::getProcessComponentContext()); + if (xDesktop.is() && m_xListener != nullptr) + { + xDesktop->addTerminateListener(m_xListener); + } +} + +CheckReadOnlyTask::~CheckReadOnlyTask() +{ + Reference<css::frame::XDesktop> xDesktop + = css::frame::Desktop::create(comphelper::getProcessComponentContext()); + if (xDesktop.is() && m_xListener != nullptr) + { + std::unique_lock<std::mutex> lock(m_xListener->mMutex); + if (!m_xListener->bIsTerminated) + { + lock.unlock(); + xDesktop->removeTerminateListener(m_xListener); + } + } +} + +namespace +{ +void SAL_CALL +CheckReadOnlyTaskTerminateListener::disposing(const css::lang::EventObject& /*Source*/) +{ +} + +void SAL_CALL +CheckReadOnlyTaskTerminateListener::queryTermination(const css::lang::EventObject& /*aEvent*/) +{ +} + +void SAL_CALL +CheckReadOnlyTaskTerminateListener::notifyTermination(const css::lang::EventObject& /*aEvent*/) +{ + std::unique_lock<std::mutex> lock(mMutex); + bIsTerminated = true; + lock.unlock(); + mCond.notify_one(); +} +} + class SfxMedium_Impl { public: @@ -263,6 +371,7 @@ public: bool m_bInputStreamIsReadOnly:1; bool m_bInCheckIn:1; bool m_bDisableFileSync = false; + bool m_bNotifyWhenEditable = false; OUString m_aName; OUString m_aLogicName; @@ -274,6 +383,10 @@ public: std::shared_ptr<const SfxFilter> m_pFilter; std::shared_ptr<const SfxFilter> m_pCustomFilter; + std::shared_ptr<std::recursive_mutex> m_pCheckEditableWorkerMutex; + std::shared_ptr<bool> m_pIsDestructed; + ImplSVEvent* m_pReloadEvent; + std::unique_ptr<SvStream> m_pInStream; std::unique_ptr<SvStream> m_pOutStream; @@ -320,7 +433,6 @@ public: { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); } }; - SfxMedium_Impl::SfxMedium_Impl() : m_nStorOpenMode(SFX_STREAM_READWRITE), m_eError(ERRCODE_NONE), @@ -345,6 +457,7 @@ SfxMedium_Impl::SfxMedium_Impl() : m_bRemote(false), m_bInputStreamIsReadOnly(false), m_bInCheckIn(false), + m_pReloadEvent(nullptr), aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ), nLastStorageError( ERRCODE_NONE ), m_nSignatureState( SignatureState::NOSIGNATURES ) @@ -359,6 +472,9 @@ SfxMedium_Impl::~SfxMedium_Impl() pTempFile.reset(); m_pSet.reset(); + std::unique_lock<std::recursive_mutex> chkEditLock; + if (m_pCheckEditableWorkerMutex != nullptr) + chkEditLock = std::unique_lock<std::recursive_mutex>(*m_pCheckEditableWorkerMutex); m_pURLObj.reset(); } @@ -994,6 +1110,7 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntr xHandler->handle( xInteractionRequestImpl ); + bool bOpenReadOnly = false; ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection(); if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() ) { @@ -1018,15 +1135,26 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntr // User decided to ignore the alien (stale?) lock file without filesystem lock nResult = ShowLockResult::Succeeded; } - else // if ( XSelected == aContinuations[1] ) + else if (uno::Reference< task::XInteractionApprove >( xSelected.get(), uno::UNO_QUERY ).is()) + { + bOpenReadOnly = true; + } + else // user selected "Notify" + { + pImpl->m_bNotifyWhenEditable = true; + AddToCheckEditableWorkerList(); + bOpenReadOnly = true; + } + + if (bOpenReadOnly) { // own lock on loading, user has selected to open readonly // own lock on saving, user has selected to open readonly // alien lock on loading, user has selected to retry saving // TODO/LATER: alien lock on saving, user has selected to retry saving - if ( bIsLoading ) - GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); + if (bIsLoading) + GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true)); else nResult = ShowLockResult::Try; } @@ -1048,6 +1176,42 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntr return nResult; } +bool SfxMedium::ShowReadOnlyOpenDialog() +{ + uno::Reference<task::XInteractionHandler> xHandler = GetInteractionHandler(); + if (xHandler.is()) + { + OUString aDocumentURL + = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset); + ::rtl::Reference<::ucbhelper::InteractionRequest> xInteractionRequestImpl + = new ::ucbhelper::InteractionRequest(uno::makeAny(document::ReadOnlyOpenRequest( + OUString(), uno::Reference<uno::XInterface>(), aDocumentURL))); + if (xInteractionRequestImpl != nullptr) + { + sal_Int32 nContinuations = 2; + uno::Sequence<uno::Reference<task::XInteractionContinuation>> aContinuations( + nContinuations); + aContinuations[0] = new ::ucbhelper::InteractionAbort(xInteractionRequestImpl.get()); + aContinuations[1] = new ::ucbhelper::InteractionApprove(xInteractionRequestImpl.get()); + xInteractionRequestImpl->setContinuations(aContinuations); + xHandler->handle(xInteractionRequestImpl); + ::rtl::Reference<::ucbhelper::InteractionContinuation> xSelected + = xInteractionRequestImpl->getSelection(); + if (uno::Reference<task::XInteractionAbort>(xSelected.get(), uno::UNO_QUERY).is()) + { + SetError(ERRCODE_ABORT); + return false; + } + else if (!uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY) + .is()) + // user selected "Notify" + pImpl->m_bNotifyWhenEditable = true; + + return true; + } + } + return false; +} bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg) { @@ -1076,17 +1240,23 @@ bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg) xHandler->handle(xIgnoreRequestImpl); ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection(); - bool bReadOnly = uno::Reference< task::XInteractionApprove >(xSelected.get(), uno::UNO_QUERY).is(); + bool bReadOnly = true; - if (bReadOnly) + if (uno::Reference<task::XInteractionAbort>(xSelected.get(), uno::UNO_QUERY).is()) { - GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true)); + SetError(ERRCODE_ABORT); + bReadOnly = false; } - else + else if (!uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY).is()) { - SetError(ERRCODE_ABORT); + // user selected "Notify" + pImpl->m_bNotifyWhenEditable = true; + AddToCheckEditableWorkerList(); } + if (bReadOnly) + GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true)); + return bReadOnly; } @@ -1483,6 +1653,10 @@ SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bN bResult = !bContentReadonly; } } + else // read-only + { + AddToCheckEditableWorkerList(); + } } if ( !bResult && GetError() == ERRCODE_NONE ) @@ -2775,7 +2949,13 @@ void SfxMedium::Init_Impl() { if ( aUrl.HasMark() ) { + std::unique_lock<std::recursive_mutex> chkEditLock; + if (pImpl->m_pCheckEditableWorkerMutex != nullptr) + chkEditLock = std::unique_lock<std::recursive_mutex>( + *(pImpl->m_pCheckEditableWorkerMutex)); pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE ); + if (chkEditLock.owns_lock()) + chkEditLock.unlock(); GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) ); } @@ -2792,8 +2972,14 @@ void SfxMedium::Init_Impl() if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() ) { + std::unique_lock<std::recursive_mutex> chkEditLock; + if (pImpl->m_pCheckEditableWorkerMutex != nullptr) + chkEditLock + = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex)); pImpl->m_aLogicName = pSalvageItem->GetValue(); pImpl->m_pURLObj.reset(); + if (chkEditLock.owns_lock()) + chkEditLock.unlock(); pImpl->m_bSalvageMode = true; } @@ -2829,7 +3015,12 @@ void SfxMedium::Init_Impl() if (item.getFileStatus(stat) == osl::FileBase::E_None && stat.isValid(osl_FileStatus_Mask_Attributes)) { - if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0) { + if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0 +#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT + && ShowReadOnlyOpenDialog() +#endif + ) + { pImpl->m_bOriginallyReadOnly = true; } } @@ -2880,7 +3071,6 @@ SfxMedium::GetInteractionHandler( bool bGetAlways ) return pImpl->xInteraction; } - void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter ) { pImpl->m_pFilter = pFilter; @@ -3127,8 +3317,13 @@ void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL ) pImpl->aOrigURL = pImpl->m_aLogicName; if( bSetOrigURL ) pImpl->aOrigURL = aNameP; + std::unique_lock<std::recursive_mutex> chkEditLock; + if (pImpl->m_pCheckEditableWorkerMutex != nullptr) + chkEditLock = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex)); pImpl->m_aLogicName = aNameP; pImpl->m_pURLObj.reset(); + if (chkEditLock.owns_lock()) + chkEditLock.unlock(); pImpl->aContent = ::ucbhelper::Content(); Init_Impl(); } @@ -3155,7 +3350,6 @@ void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP ) } } - void SfxMedium::ReOpen() { bool bUseInteractionHandler = pImpl->bUseInteractionHandler; @@ -3164,7 +3358,6 @@ void SfxMedium::ReOpen() pImpl->bUseInteractionHandler = bUseInteractionHandler; } - void SfxMedium::CompleteReOpen() { // do not use temporary file for reopen and in case of success throw the temporary file away @@ -3336,9 +3529,11 @@ SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUS GetItemSet()->Put( *p ); } - +// NOTE: should only be called on main thread SfxMedium::~SfxMedium() { + CancelCheckEditableEntry(); + // if there is a requirement to clean the backup this is the last possibility to do it ClearBackup_Impl(); @@ -3367,6 +3562,10 @@ const OUString& SfxMedium::GetName() const const INetURLObject& SfxMedium::GetURLObject() const { + std::unique_lock<std::recursive_mutex> chkEditLock; + if (pImpl->m_pCheckEditableWorkerMutex != nullptr) + chkEditLock = std::unique_lock<std::recursive_mutex>(*(pImpl->m_pCheckEditableWorkerMutex)); + if (!pImpl->m_pURLObj) { pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) ); @@ -3589,6 +3788,11 @@ bool SfxMedium::IsOriginallyReadOnly() const return pImpl->m_bOriginallyReadOnly; } +void SfxMedium::SetOriginallyReadOnly(bool val) +{ + pImpl->m_bOriginallyReadOnly = val; +} + bool SfxMedium::IsOriginallyLoadedReadOnly() const { return pImpl->m_bOriginallyLoadedReadOnly; @@ -4302,4 +4506,289 @@ bool SfxMedium::IsInCheckIn( ) const return pImpl->m_bInCheckIn; } +// should only be called on main thread +std::shared_ptr<std::recursive_mutex> SfxMedium::GetCheckEditableMutex() const +{ + return pImpl->m_pCheckEditableWorkerMutex; +} + +// should only be called while holding pImpl->m_pCheckEditableWorkerMutex +void SfxMedium::SetWorkerReloadEvent(ImplSVEvent* pEvent) +{ + pImpl->m_pReloadEvent = pEvent; +} + +// should only be called while holding pImpl->m_pCheckEditableWorkerMutex +ImplSVEvent* SfxMedium::GetWorkerReloadEvent() const +{ + return pImpl->m_pReloadEvent; +} + +// should only be called on main thread +void SfxMedium::AddToCheckEditableWorkerList() +{ + if (!pImpl->m_bNotifyWhenEditable) + return; + + CancelCheckEditableEntry(); + + if (pImpl->m_pCheckEditableWorkerMutex == nullptr) + { + pImpl->m_pCheckEditableWorkerMutex = std::make_shared<std::recursive_mutex>(); + if (pImpl->m_pCheckEditableWorkerMutex == nullptr) + return; + } + + pImpl->m_pIsDestructed = std::make_shared<bool>(false); + if (pImpl->m_pIsDestructed == nullptr) + return; + + std::unique_lock<std::mutex> globalLock(g_chkReadOnlyGlobalMutex); + if (g_newReadOnlyDocs.find(this) == g_newReadOnlyDocs.end()) + { + bool bAddNewEntry = false; + if (!g_bChkReadOnlyTaskRunning) + { + std::shared_ptr<comphelper::ThreadTaskTag> pTag + = comphelper::ThreadPool::createThreadTaskTag(); + if (pTag != nullptr) + { + g_bChkReadOnlyTaskRunning = true; + bAddNewEntry = true; + comphelper::ThreadPool::getSharedOptimalPool().pushTask( + std::make_unique<CheckReadOnlyTask>(pTag)); + } + } + else + bAddNewEntry = true; + + if (bAddNewEntry) + { + std::shared_ptr<ReadOnlyMediumEntry> newEntry = std::make_shared<ReadOnlyMediumEntry>( + pImpl->m_pCheckEditableWorkerMutex, pImpl->m_pIsDestructed); + + if (newEntry != nullptr) + { + g_newReadOnlyDocs[this] = newEntry; + } + } + } +} + +// should only be called on main thread +void SfxMedium::CancelCheckEditableEntry(bool bRemoveEvent) +{ + if (pImpl->m_pCheckEditableWorkerMutex != nullptr) + { + std::unique_lock<std::recursive_mutex> lock(*(pImpl->m_pCheckEditableWorkerMutex)); + + if (pImpl->m_pReloadEvent != nullptr) + { + if (bRemoveEvent) + Application::RemoveUserEvent(pImpl->m_pReloadEvent); + // make sure destructor doesn't use a freed reference + // and reset the event so we can check again + pImpl->m_pReloadEvent = nullptr; + } + + if (pImpl->m_pIsDestructed != nullptr) + { + *(pImpl->m_pIsDestructed) = true; + pImpl->m_pIsDestructed = nullptr; + } + } +} + +/** callback function, which is triggered by worker thread after successfully checking if the file + is editable. Sent from <Application::PostUserEvent(..)> + Note: This method has to be run in the main thread. +*/ +IMPL_STATIC_LINK(SfxMedium, ShowReloadEditableDialog, void*, p, void) +{ + SfxMedium* pMed = static_cast<SfxMedium*>(p); + if (pMed == nullptr) + return; + + pMed->CancelCheckEditableEntry(false); + + uno::Reference<task::XInteractionHandler> xHandler = pMed->GetInteractionHandler(); + if (xHandler.is()) + { + OUString aDocumentURL + = pMed->GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset); + ::rtl::Reference<::ucbhelper::InteractionRequest> xInteractionRequestImpl + = new ::ucbhelper::InteractionRequest(uno::makeAny(document::ReloadEditableRequest( + OUString(), uno::Reference<uno::XInterface>(), aDocumentURL))); + if (xInteractionRequestImpl != nullptr) + { + sal_Int32 nContinuations = 2; + uno::Sequence<uno::Reference<task::XInteractionContinuation>> aContinuations( + nContinuations); + aContinuations[0] = new ::ucbhelper::InteractionAbort(xInteractionRequestImpl.get()); + aContinuations[1] = new ::ucbhelper::InteractionApprove(xInteractionRequestImpl.get()); + xInteractionRequestImpl->setContinuations(aContinuations); + xHandler->handle(xInteractionRequestImpl); + ::rtl::Reference<::ucbhelper::InteractionContinuation> xSelected + = xInteractionRequestImpl->getSelection(); + if (uno::Reference<task::XInteractionApprove>(xSelected.get(), uno::UNO_QUERY).is()) + { + for (SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame; + pFrame = SfxViewFrame::GetNext(*pFrame)) + { + if (pFrame->GetObjectShell()->GetMedium() == pMed) + { + // special case to ensure view isn't set to read-only in + // SfxViewFrame::ExecReload_Impl after reloading + pMed->SetOriginallyReadOnly(false); + pFrame->GetDispatcher()->Execute(SID_RELOAD); + break; + } + } + } + } + } +} + +bool SfxMedium::CheckCanGetLockfile() const +{ +#if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT + bool bCanReload = true; +#else + bool bCanReload = false; + ::svt::DocumentLockFile aLockFile(GetName()); + LockFileEntry aData; + osl::DirectoryItem rItem; + auto nError1 = osl::DirectoryItem::get(aLockFile.GetURL(), rItem); + if (nError1 == osl::FileBase::E_None) + { + try + { + aData = aLockFile.GetLockData(); + } + catch (const io::WrongFormatException&) + { + // we get empty or corrupt data + return false; + } + catch (const uno::Exception&) + { + // locked from other app + return false; + } + LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry(); + bool bOwnLock + = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME]; + if (bOwnLock + && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST] + && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL]) + { + // this is own lock from the same installation, it could remain because of crash + bCanReload = true; + } + } + else if (nError1 == osl::FileBase::E_NOENT) // file doesn't exist + { + try + { + aLockFile.CreateOwnLockFile(); + try + { + // TODO/LATER: A warning could be shown in case the file is not the own one + aLockFile.RemoveFile(); + } + catch (const io::WrongFormatException&) + { + try + { + // erase the empty or corrupt file + aLockFile.RemoveFileDirectly(); + } + catch (const uno::Exception&) + { + } + } + bCanReload = true; + } + catch (const uno::Exception&) + { + } + } +#endif + return bCanReload; +} + +// worker thread method, should only be one thread globally +void CheckReadOnlyTask::doWork() +{ + if (m_xListener == nullptr) + return; + + while (true) + { + std::unique_lock<std::mutex> termLock(m_xListener->mMutex); + if (m_xListener->mCond.wait_for(termLock, std::chrono::seconds(60), + [this] { return m_xListener->bIsTerminated; })) + // signalled, spurious wakeups should not be possible + return; + + // must have timed-out + termLock.unlock(); + std::unique_lock<std::mutex> globalLock(g_chkReadOnlyGlobalMutex); + for (const auto& [pMed, roEntry] : g_newReadOnlyDocs) + { + g_existingReadOnlyDocs[pMed] = roEntry; + g_newReadOnlyDocs.erase(pMed); + } + if (g_existingReadOnlyDocs.size() == 0) + { + g_bChkReadOnlyTaskRunning = false; + return; + } + globalLock.unlock(); + + bool bErase = false; + for (const auto& [pMed, roEntry] : g_existingReadOnlyDocs) + { + bErase = false; + comphelper::ScopeGuard g([&bErase, pMed = pMed]() { + if (bErase) + g_existingReadOnlyDocs.erase(pMed); + }); + + if (pMed == nullptr || roEntry == nullptr || roEntry->_pMutex == nullptr + || roEntry->_pIsDestructed == nullptr) + { + bErase = true; + continue; + } + + std::unique_lock<std::recursive_mutex> medLock(*(roEntry->_pMutex)); + if (*(roEntry->_pIsDestructed) || pMed->GetWorkerReloadEvent() != nullptr) + { + bErase = true; + } + else + { + osl::File aFile( + pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset)); + if (aFile.open(osl_File_OpenFlag_Write) != osl::FileBase::E_None) + continue; + + if (!pMed->CheckCanGetLockfile()) + continue; + + bErase = true; + + if (aFile.close() != osl::FileBase::E_None) + continue; + + // we can load, ask user + ImplSVEvent* pEvent = Application::PostUserEvent( + LINK(nullptr, SfxMedium, ShowReloadEditableDialog), pMed); + pMed->SetWorkerReloadEvent(pEvent); + } + } + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx index d8d5bf69465a..be8ea298b978 100644 --- a/sfx2/source/view/viewfrm.cxx +++ b/sfx2/source/view/viewfrm.cxx @@ -315,6 +315,12 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) SfxMedium* pMed = pSh->GetMedium(); + std::shared_ptr<std::recursive_mutex> pChkEditMutex = pMed->GetCheckEditableMutex(); + std::unique_lock<std::recursive_mutex> chkEditLock; + if (pChkEditMutex != nullptr) + chkEditLock = std::unique_lock<std::recursive_mutex>(*pChkEditMutex); + pMed->CancelCheckEditableEntry(); + const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false); if ( pItem && pItem->GetValue() ) { @@ -510,6 +516,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) bOpenTemplate = RET_YES == nUserAnswer; // Always reset this here to avoid infinite loop bRetryIgnoringLock = RET_IGNORE == nUserAnswer; + if (RET_CANCEL == nUserAnswer) + pMed->AddToCheckEditableWorkerList(); } else bRetryIgnoringLock = false; @@ -631,6 +639,12 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) if ( bDo ) { SfxMedium *pMedium = xOldObj->GetMedium(); + std::shared_ptr<std::recursive_mutex> pChkEditMutex + = pMedium->GetCheckEditableMutex(); + std::unique_lock<std::recursive_mutex> chkEditLock; + if (pChkEditMutex != nullptr) + chkEditLock = std::unique_lock<std::recursive_mutex>(*pChkEditMutex); + pMedium->CancelCheckEditableEntry(); bool bHandsOff = ( pMedium->GetURLObject().GetProtocol() == INetProtocol::File && !xOldObj->IsDocShared() ); @@ -772,6 +786,7 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) { xNewObj->DoClose(); xNewObj = nullptr; + pMedium->AddToCheckEditableWorkerList(); } pNewSet.reset(); diff --git a/uui/Library_uui.mk b/uui/Library_uui.mk index 4daad8403dcb..4f664ac20dba 100644 --- a/uui/Library_uui.mk +++ b/uui/Library_uui.mk @@ -62,6 +62,8 @@ $(eval $(call gb_Library_add_exception_objects,uui,\ uui/source/openlocked \ uui/source/passwordcontainer \ uui/source/passworddlg \ + uui/source/readonlyopen \ + uui/source/reloadeditable \ uui/source/requeststringresolver \ uui/source/secmacrowarnings \ uui/source/sslwarndlg \ diff --git a/uui/inc/strings.hrc b/uui/inc/strings.hrc index 364175298db7..096d7ef977a1 100644 --- a/uui/inc/strings.hrc +++ b/uui/inc/strings.hrc @@ -30,8 +30,9 @@ #define STR_PASSWORD_MISMATCH NC_("STR_PASSWORD_MISMATCH", "The confirmation password did not match the password. Set the password again by entering the same password in both boxes.") #define STR_ALREADYOPEN_TITLE NC_("STR_ALREADYOPEN_TITLE", "Document in Use") -#define STR_ALREADYOPEN_MSG NC_("STR_ALREADYOPEN_MSG", "Document file '$(ARG1)' is locked for editing by yourself on a different system since $(ARG2)\n\nOpen document read-only, or ignore own file locking and open the document for editing.") +#define STR_ALREADYOPEN_MSG NC_("STR_ALREADYOPEN_MSG", "Document file '$(ARG1)' is locked for editing by yourself on a different system since $(ARG2)\n\nOpen document read-only, or ignore own file locking and open the document for editing.\nSelect Notify to open read-only and get notified when the document becomes editable.") #define STR_ALREADYOPEN_READONLY_BTN NC_("STR_ALREADYOPEN_READONLY_BTN", "Open ~Read-Only") +#define STR_ALREADYOPEN_READONLY_NOTIFY_BTN NC_("STR_ALREADYOPEN_READONLY_NOTIFY_BTN", "~Notify") #define STR_ALREADYOPEN_OPEN_BTN NC_("STR_ALREADYOPEN_OPEN_BTN", "~Open") #define STR_ALREADYOPEN_SAVE_MSG NC_("STR_ALREADYOPEN_SAVE_MSG", "Document file '$(ARG1)' is locked for editing by yourself on a different system since $(ARG2)\n\nClose document on other system and retry saving or ignore own file locking and save current document.") #define STR_ALREADYOPEN_RETRY_SAVE_BTN NC_("STR_ALREADYOPEN_RETRY_SAVE_BTN", "~Retry Saving") @@ -42,13 +43,15 @@ #define STR_WARNING_INCOMPLETE_ENCRYPTION_TITLE NC_("STR_WARNING_INCOMPLETE_ENCRYPTION_TITLE", "Non-Encrypted Streams") #define STR_LOCKFAILED_TITLE NC_("STR_LOCKFAILED_TITLE", "Document Could Not Be Locked") -#define STR_LOCKFAILED_MSG NC_("STR_LOCKFAILED_MSG", "The lock file could not be created for exclusive access by %PRODUCTNAME, due to missing permission to create a lock file on that file location or lack of free disk space.") +#define STR_LOCKFAILED_MSG NC_("STR_LOCKFAILED_MSG", "The lock file could not be created for exclusive access by %PRODUCTNAME, due to missing permission to create a lock file on that file location or lack of free disk space.\n\nSelect Notify to open read-only and get notified when the document becomes editable.") #define STR_LOCKFAILED_OPENREADONLY_BTN NC_("STR_LOCKFAILED_OPENREADONLY_BTN", "Open ~Read-Only") +#define STR_LOCKFAILED_OPENREADONLY_NOTIFY_BTN NC_("STR_LOCKFAILED_OPENREADONLY_NOTIFY_BTN", "~Notify") #define STR_OPENLOCKED_TITLE NC_("STR_OPENLOCKED_TITLE", "Document in Use") -#define STR_OPENLOCKED_MSG NC_("STR_OPENLOCKED_MSG", "Document file '$(ARG1)' is locked for editing by:\n\n$(ARG2)\n\nOpen document read-only or open a copy of the document for editing.$(ARG3)") +#define STR_OPENLOCKED_MSG NC_("STR_OPENLOCKED_MSG", "Document file '$(ARG1)' is locked for editing by:\n\n$(ARG2)\n\nOpen document read-only or open a copy of the document for editing.\nSelect Notify to open read-only and get notified when the document becomes editable.$(ARG3)") #define STR_OPENLOCKED_ALLOWIGNORE_MSG NC_("STR_OPENLOCKED_ALLOWIGNORE_MSG", "\nYou may also ignore the file locking and open the document for editing.") #define STR_OPENLOCKED_OPENREADONLY_BTN NC_("STR_OPENLOCKED_OPENREADONLY_BTN", "Open ~Read-Only") +#define STR_OPENLOCKED_OPENREADONLY_NOTIFY_BTN NC_("STR_OPENLOCKED_OPENREADONLY_NOTIFY_BTN", "~Notify") #define STR_OPENLOCKED_OPENCOPY_BTN NC_("STR_OPENLOCKED_OPENCOPY_BTN", "Open ~Copy") #define STR_UNKNOWNUSER NC_("STR_UNKNOWNUSER", "Unknown User") @@ -73,7 +76,17 @@ #define STR_ERROR_PASSWORDS_NOT_IDENTICAL NC_("STR_ERROR_PASSWORDS_NOT_IDENTICAL", "The password confirmation does not match.") #define STR_LOCKCORRUPT_TITLE NC_("STR_LOCKCORRUPT_TITLE", "Lock file is corrupted") -#define STR_LOCKCORRUPT_MSG NC_("STR_LOCKCORRUPT_MSG", "The lock file is corrupted and probably empty. Opening the document read-only and closing it again removes the corrupted lock file.") +#define STR_LOCKCORRUPT_MSG NC_("STR_LOCKCORRUPT_MSG", "The lock file is corrupted and probably empty. Opening the document read-only and closing it again removes the corrupted lock file.\n\nSelect Notify to open read-only and get notified when the document becomes editable.") #define STR_LOCKCORRUPT_OPENREADONLY_BTN NC_("STR_LOCKCORRUPT_OPENREADONLY_BTN", "Open ~Read-Only") +#define STR_LOCKCORRUPT_OPENREADONLY_NOTIFY_BTN NC_("STR_LOCKCORRUPT_OPENREADONLY_NOTIFY_BTN", "~Notify") + +#define STR_RELOADEDITABLE_TITLE NC_("STR_RELOADEDITABLE_TITLE", "Document is now editable") +#define STR_RELOADEDITABLE_MSG NC_("STR_RELOADEDITABLE_MSG", "Document file '$(ARG1)' is now editable \n\nReload this document for editing?") +#define STR_RELOADEDITABLE_BTN NC_("STR_RELOADEDITABLE_BTN", "~Reload") + +#define STR_READONLYOPEN_TITLE NC_("STR_READONLYOPEN_TITLE", "Document is read-only") +#define STR_READONLYOPEN_MSG NC_("STR_READONLYOPEN_MSG", "Document file '$(ARG1)' is read-only.\n\nOpen read-only or select Notify to open read-only and get notified when the document becomes editable.") +#define STR_READONLYOPEN_BTN NC_("STR_READONLYOPEN_BTN", "Open ~Read-Only") +#define STR_READONLYOPEN_NOTIFY_BTN NC_("STR_READONLYOPEN_NOTIFY_BTN", "~Notify") /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uui/source/alreadyopen.cxx b/uui/source/alreadyopen.cxx index 2fe5dcbc424f..7c387d8b7a7c 100644 --- a/uui/source/alreadyopen.cxx +++ b/uui/source/alreadyopen.cxx @@ -35,6 +35,7 @@ AlreadyOpenQueryBox::AlreadyOpenQueryBox(weld::Window* pParent, const std::local else { m_xQueryBox->add_button(Translate::get(STR_ALREADYOPEN_READONLY_BTN, rLocale), RET_YES); + m_xQueryBox->add_button(Translate::get(STR_ALREADYOPEN_READONLY_NOTIFY_BTN, rLocale), RET_RETRY); m_xQueryBox->add_button(Translate::get(STR_ALREADYOPEN_OPEN_BTN, rLocale), RET_NO); } m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); diff --git a/uui/source/iahndl-locking.cxx b/uui/source/iahndl-locking.cxx index 52b9d2108347..23493c16053c 100644 --- a/uui/source/iahndl-locking.cxx +++ b/uui/source/iahndl-locking.cxx @@ -23,6 +23,8 @@ #include <com/sun/star/document/LockFileIgnoreRequest.hpp> #include <com/sun/star/document/LockFileCorruptRequest.hpp> #include <com/sun/star/document/OwnLockOnDocumentRequest.hpp> +#include <com/sun/star/document/ReadOnlyOpenRequest.hpp> +#include <com/sun/star/document/ReloadEditableRequest.hpp> #include <com/sun/star/task/XInteractionApprove.hpp> #include <com/sun/star/task/XInteractionDisapprove.hpp> #include <com/sun/star/task/XInteractionAbort.hpp> @@ -41,6 +43,8 @@ #include "filechanged.hxx" #include "lockfailed.hxx" #include "lockcorrupt.hxx" +#include "readonlyopen.hxx" +#include "reloadeditable.hxx" #include "iahndl.hxx" @@ -53,6 +57,66 @@ using namespace com::sun::star; namespace { +void handleReadOnlyOpenRequest_( + weld::Window* pParent, const OUString& aDocumentURL, + uno::Sequence<uno::Reference<task::XInteractionContinuation>> const& rContinuations) +{ + uno::Reference<task::XInteractionApprove> xApprove; + uno::Reference<task::XInteractionAbort> xAbort; + getContinuations(rContinuations, &xApprove, &xAbort); + + if (!xApprove.is() || !xAbort.is()) + return; + + SolarMutexGuard aGuard; + std::locale aResLocale = Translate::Create("uui"); + + OUString aMessage; + std::vector<OUString> aArguments; + aArguments.push_back(aDocumentURL); + + aMessage = Translate::get(STR_READONLYOPEN_MSG, aResLocale); + aMessage = UUIInteractionHelper::replaceMessageWithArguments(aMessage, aArguments); + + ReadOnlyOpenQueryBox aDialog(pParent, aResLocale, aMessage); + int nResult = aDialog.run(); + + if (nResult == RET_YES) + xApprove->select(); + else if (nResult != RET_RETRY) + xAbort->select(); +} + +void handleReloadEditableRequest_( + weld::Window* pParent, const OUString& aDocumentURL, + uno::Sequence<uno::Reference<task::XInteractionContinuation>> const& rContinuations) +{ + uno::Reference<task::XInteractionApprove> xApprove; + uno::Reference<task::XInteractionAbort> xAbort; + getContinuations(rContinuations, &xApprove, &xAbort); + + if (!xApprove.is() || !xAbort.is()) + return; + + SolarMutexGuard aGuard; + std::locale aResLocale = Translate::Create("uui"); + + OUString aMessage; + std::vector<OUString> aArguments; + aArguments.push_back(aDocumentURL); + + aMessage = Translate::get(STR_RELOADEDITABLE_MSG, aResLocale); + aMessage = UUIInteractionHelper::replaceMessageWithArguments(aMessage, aArguments); + + ReloadEditableQueryBox aDialog(pParent, aResLocale, aMessage); + int nResult = aDialog.run(); + + if (nResult == RET_YES) + xApprove->select(); + else + xAbort->select(); +} + void handleLockedDocumentRequest_( weld::Window * pParent, @@ -132,7 +196,7 @@ handleLockedDocumentRequest_( xDisapprove->select(); else if ( nResult == RET_IGNORE && xRetry.is() ) xRetry->select(); - else + else if ( nResult != RET_RETRY ) xAbort->select(); } @@ -196,7 +260,7 @@ handleLockFileProblemRequest_( if ( nResult == RET_OK ) xApprove->select(); - else + else if ( nResult != RET_RETRY ) xAbort->select(); } @@ -293,5 +357,41 @@ UUIInteractionHelper::handleLockFileProblemRequest( return false; } +bool UUIInteractionHelper::handleReadOnlyOpenRequest( + uno::Reference<task::XInteractionRequest> const& rRequest) +{ + uno::Any aAnyRequest(rRequest->getRequest()); + + document::ReadOnlyOpenRequest aReadOnlyOpenRequest; + if (aAnyRequest >>= aReadOnlyOpenRequest) + { + uno::Reference<awt::XWindow> xParent = getParentXWindow(); + handleReadOnlyOpenRequest_(Application::GetFrameWeld(xParent), + aReadOnlyOpenRequest.DocumentURL, + rRequest->getContinuations()); + return true; + } + + return false; +} + +bool UUIInteractionHelper::handleReloadEditableRequest( + uno::Reference<task::XInteractionRequest> const& rRequest) +{ + uno::Any aAnyRequest(rRequest->getRequest()); + + document::ReloadEditableRequest aReloadEditableRequest; + if (aAnyRequest >>= aReloadEditableRequest) + { + uno::Reference<awt::XWindow> xParent = getParentXWindow(); + handleReloadEditableRequest_( + Application::GetFrameWeld(xParent), aReloadEditableRequest.DocumentURL, + rRequest->getContinuations()); + return true; + } + + return false; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/uui/source/iahndl.cxx b/uui/source/iahndl.cxx index ec8cb02258db..2897a582c3e9 100644 --- a/uui/source/iahndl.cxx +++ b/uui/source/iahndl.cxx @@ -815,6 +815,12 @@ UUIInteractionHelper::handleRequest_impl( if ( handleLockFileProblemRequest( rRequest ) ) return true; + if ( handleReloadEditableRequest( rRequest ) ) + return true; + + if ( handleReadOnlyOpenRequest( rRequest ) ) + return true; + task::DocumentMacroConfirmationRequest aMacroConfirmRequest; if (aAnyRequest >>= aMacroConfirmRequest) { diff --git a/uui/source/iahndl.hxx b/uui/source/iahndl.hxx index ff6973245dad..8dc828a7dd5f 100644 --- a/uui/source/iahndl.hxx +++ b/uui/source/iahndl.hxx @@ -231,6 +231,12 @@ private: bool handleLockFileProblemRequest( css::uno::Reference< css::task::XInteractionRequest > const & rRequest); + bool handleReloadEditableRequest( + css::uno::Reference<css::task::XInteractionRequest> const& rRequest); + + bool + handleReadOnlyOpenRequest(css::uno::Reference<css::task::XInteractionRequest> const& rRequest); + bool handleCustomRequest( const css::uno::Reference< css::task::XInteractionRequest >& i_rRequest, const OUString& i_rServiceName diff --git a/uui/source/lockcorrupt.cxx b/uui/source/lockcorrupt.cxx index 28e8e71f00c4..d3abee07a185 100644 --- a/uui/source/lockcorrupt.cxx +++ b/uui/source/lockcorrupt.cxx @@ -29,6 +29,7 @@ LockCorruptQueryBox::LockCorruptQueryBox(weld::Window* pParent, const std::local { m_xQueryBox->set_title(Translate::get(STR_LOCKCORRUPT_TITLE, rResLocale)); m_xQueryBox->add_button(Translate::get(STR_LOCKCORRUPT_OPENREADONLY_BTN, rResLocale), RET_OK); + m_xQueryBox->add_button(Translate::get(STR_LOCKCORRUPT_OPENREADONLY_NOTIFY_BTN, rResLocale), RET_RETRY); m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); m_xQueryBox->set_default_response(RET_OK); } diff --git a/uui/source/lockfailed.cxx b/uui/source/lockfailed.cxx index 8254b19e509b..340cc9638806 100644 --- a/uui/source/lockfailed.cxx +++ b/uui/source/lockfailed.cxx @@ -29,6 +29,7 @@ LockFailedQueryBox::LockFailedQueryBox(weld::Window* pParent, const std::locale& { m_xQueryBox->set_title(Translate::get(STR_LOCKFAILED_TITLE, rLocale)); m_xQueryBox->add_button(Translate::get(STR_LOCKFAILED_OPENREADONLY_BTN, rLocale), RET_OK); + m_xQueryBox->add_button(Translate::get(STR_LOCKFAILED_OPENREADONLY_NOTIFY_BTN, rLocale), RET_RETRY); m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); m_xQueryBox->set_default_response(RET_OK); } diff --git a/uui/source/openlocked.cxx b/uui/source/openlocked.cxx index fa2a4616c7c1..a0284b194b14 100644 --- a/uui/source/openlocked.cxx +++ b/uui/source/openlocked.cxx @@ -29,6 +29,7 @@ OpenLockedQueryBox::OpenLockedQueryBox(weld::Window* pParent, const std::locale& { m_xQueryBox->set_title(Translate::get(STR_OPENLOCKED_TITLE, rResLocale)); m_xQueryBox->add_button(Translate::get(STR_OPENLOCKED_OPENREADONLY_BTN, rResLocale), RET_YES); + m_xQueryBox->add_button(Translate::get(STR_OPENLOCKED_OPENREADONLY_NOTIFY_BTN, rResLocale), RET_RETRY); m_xQueryBox->add_button(Translate::get(STR_OPENLOCKED_OPENCOPY_BTN, rResLocale), RET_NO); if (bEnableOverride && officecfg::Office::Common::Misc::AllowOverrideLocking::get()) { diff --git a/uui/source/readonlyopen.cxx b/uui/source/readonlyopen.cxx new file mode 100644 index 000000000000..72a3b989e079 --- /dev/null +++ b/uui/source/readonlyopen.cxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <strings.hrc> +#include "readonlyopen.hxx" +#include <officecfg/Office/Common.hxx> +#include <unotools/resmgr.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> + +ReadOnlyOpenQueryBox::ReadOnlyOpenQueryBox(weld::Window* pParent, const std::locale& rResLocale, + const OUString& rMessage) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, + VclButtonsType::NONE, rMessage)) +{ + m_xQueryBox->set_title(Translate::get(STR_READONLYOPEN_TITLE, rResLocale)); + m_xQueryBox->add_button(Translate::get(STR_READONLYOPEN_BTN, rResLocale), RET_YES); + m_xQueryBox->add_button(Translate::get(STR_READONLYOPEN_NOTIFY_BTN, rResLocale), RET_RETRY); + m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/uui/source/readonlyopen.hxx b/uui/source/readonlyopen.hxx new file mode 100644 index 000000000000..08063d10be0e --- /dev/null +++ b/uui/source/readonlyopen.hxx @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +class ReadOnlyOpenQueryBox +{ +private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; + +public: + ReadOnlyOpenQueryBox(weld::Window* pParent, const std::locale& rResLocale, + const OUString& rMessage); + int run() { return m_xQueryBox->run(); } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/uui/source/reloadeditable.cxx b/uui/source/reloadeditable.cxx new file mode 100644 index 000000000000..9dad2e183abf --- /dev/null +++ b/uui/source/reloadeditable.cxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <strings.hrc> +#include "reloadeditable.hxx" +#include <officecfg/Office/Common.hxx> +#include <unotools/resmgr.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> + +ReloadEditableQueryBox::ReloadEditableQueryBox(weld::Window* pParent, const std::locale& rResLocale, + const OUString& rMessage) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, + VclButtonsType::NONE, rMessage)) +{ + m_xQueryBox->set_title(Translate::get(STR_RELOADEDITABLE_TITLE, rResLocale)); + m_xQueryBox->add_button(Translate::get(STR_RELOADEDITABLE_BTN, rResLocale), RET_YES); + m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/uui/source/reloadeditable.hxx b/uui/source/reloadeditable.hxx new file mode 100644 index 000000000000..00a15f7ba89c --- /dev/null +++ b/uui/source/reloadeditable.hxx @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> + +class ReloadEditableQueryBox +{ +private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; + +public: + ReloadEditableQueryBox(weld::Window* pParent, const std::locale& rResLocale, + const OUString& rMessage); + int run() { return m_xQueryBox->run(); } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |