summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compilerplugins/clang/badstatics.cxx4
-rw-r--r--include/sfx2/docfile.hxx12
-rw-r--r--offapi/UnoApi_offapi.mk2
-rw-r--r--offapi/com/sun/star/document/ReadOnlyOpenRequest.idl51
-rw-r--r--offapi/com/sun/star/document/ReloadEditableRequest.idl50
-rw-r--r--sfx2/source/doc/docfile.cxx517
-rw-r--r--sfx2/source/view/viewfrm.cxx15
-rw-r--r--uui/Library_uui.mk2
-rw-r--r--uui/inc/strings.hrc21
-rw-r--r--uui/source/alreadyopen.cxx1
-rw-r--r--uui/source/iahndl-locking.cxx104
-rw-r--r--uui/source/iahndl.cxx6
-rw-r--r--uui/source/iahndl.hxx6
-rw-r--r--uui/source/lockcorrupt.cxx1
-rw-r--r--uui/source/lockfailed.cxx1
-rw-r--r--uui/source/openlocked.cxx1
-rw-r--r--uui/source/readonlyopen.cxx38
-rw-r--r--uui/source/readonlyopen.hxx35
-rw-r--r--uui/source/reloadeditable.cxx37
-rw-r--r--uui/source/reloadeditable.hxx35
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: */