diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sfx2/source/view/viewfrm.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sfx2/source/view/viewfrm.cxx')
-rw-r--r-- | sfx2/source/view/viewfrm.cxx | 3706 |
1 files changed, 3706 insertions, 0 deletions
diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx new file mode 100644 index 0000000000..fe0dc0adc7 --- /dev/null +++ b/sfx2/source/view/viewfrm.cxx @@ -0,0 +1,3706 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_feature_desktop.h> +#include <config_wasm_strip.h> + +#include <osl/file.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/infobar.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <sfx2/classificationhelper.hxx> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <sfx2/pageids.hxx> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/DispatchRecorder.hpp> +#include <com/sun/star/frame/DispatchRecorderSupplier.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/task/PasswordContainer.hpp> +#include <officecfg/Office/Common.hxx> +#include <officecfg/Setup.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/wrkwin.hxx> +#include <unotools/moduleoptions.hxx> +#include <svl/intitem.hxx> +#include <svl/visitem.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> +#include <svl/undo.hxx> +#include <vcl/help.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> +#if !ENABLE_WASM_STRIP_PINGUSER +#include <unotools/VersionConfig.hxx> +#endif +#include <unotools/securityoptions.hxx> +#include <svtools/miscopt.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/frame/XDispatchRecorderSupplier.hpp> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> +#include <com/sun/star/document/XViewDataSupplier.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <unotools/ucbhelper.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/docpasswordrequest.hxx> +#include <comphelper/docpasswordhelper.hxx> + +#include <com/sun/star/uno/Reference.h> + +#include <basic/basmgr.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbmeth.hxx> +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> +#include <framework/framelistanalyzer.hxx> + +#include <optional> + +#include <comphelper/sequenceashashmap.hxx> + +#include <commandpopup/CommandPopup.hxx> + +// Due to ViewFrame::Current +#include <appdata.hxx> +#include <sfx2/app.hxx> +#include <sfx2/objface.hxx> +#include <openflag.hxx> +#include <objshimp.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/sfxresid.hxx> +#include <sfx2/viewfac.hxx> +#include <sfx2/event.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/module.hxx> +#include <sfx2/sfxuno.hxx> +#include <sfx2/progress.hxx> +#include <sfx2/sidebar/Sidebar.hxx> +#include <workwin.hxx> +#include <sfx2/minfitem.hxx> +#include <sfx2/strings.hrc> +#include "impviewframe.hxx" +#include <vcl/commandinfoprovider.hxx> +#include <vcl/svapp.hxx> + +#define ShellClass_SfxViewFrame +#include <sfxslots.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using ::com::sun::star::awt::XWindow; +using ::com::sun::star::beans::PropertyValue; +using ::com::sun::star::document::XViewDataSupplier; +using ::com::sun::star::container::XIndexContainer; + +constexpr OUString CHANGES_STR = u"private:resource/toolbar/changes"_ustr; + +SFX_IMPL_SUPERCLASS_INTERFACE(SfxViewFrame,SfxShell) + +void SfxViewFrame::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SID_BROWSER); + GetStaticInterface()->RegisterChildWindow(SID_RECORDING_FLOATWINDOW); +#if HAVE_FEATURE_DESKTOP + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_FULLSCREEN, SfxVisibilityFlags::FullScreen, ToolbarId::FullScreenToolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard, ToolbarId::EnvToolbox); +#endif +} + +namespace { +/// Asks the user if editing a read-only document is really wanted. +class SfxEditDocumentDialog : public weld::MessageDialogController +{ +public: + SfxEditDocumentDialog(weld::Widget* pParent); +}; + +SfxEditDocumentDialog::SfxEditDocumentDialog(weld::Widget* pParent) + : MessageDialogController(pParent, "sfx/ui/editdocumentdialog.ui", + "EditDocumentDialog") +{ +} + +class SfxQueryOpenAsTemplate +{ +private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; +public: + SfxQueryOpenAsTemplate(weld::Window* pParent, bool bAllowIgnoreLock, LockFileEntry& rLockData) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, + VclButtonsType::NONE, "")) + { + m_xQueryBox->add_button(SfxResId(STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN), RET_YES); + bAllowIgnoreLock + = bAllowIgnoreLock && officecfg::Office::Common::Misc::AllowOverrideLocking::get(); + if (bAllowIgnoreLock) + m_xQueryBox->add_button(SfxResId(STR_QUERY_OPENASTEMPLATE_OPEN_BTN), RET_IGNORE); + m_xQueryBox->add_button(GetStandardText( StandardButtonType::Cancel ), RET_CANCEL); + m_xQueryBox->set_primary_text(QueryString(bAllowIgnoreLock, rLockData)); + m_xQueryBox->set_default_response(RET_YES); + } + short run() { return m_xQueryBox->run(); } + +private: + static OUString QueryString(bool bAllowIgnoreLock, LockFileEntry& rLockData) + { + OUString sLockUserData; + if (!rLockData[LockFileComponent::OOOUSERNAME].isEmpty()) + sLockUserData = rLockData[LockFileComponent::OOOUSERNAME]; + else + sLockUserData = rLockData[LockFileComponent::SYSUSERNAME]; + + if (!sLockUserData.isEmpty() && !rLockData[LockFileComponent::EDITTIME].isEmpty()) + sLockUserData += " ( " + rLockData[LockFileComponent::EDITTIME] + " )"; + + if (!sLockUserData.isEmpty()) + sLockUserData = "\n\n" + sLockUserData + "\n"; + + const bool bUseLockStr = bAllowIgnoreLock || !sLockUserData.isEmpty(); + + OUString sMsg( + SfxResId(bUseLockStr ? STR_QUERY_OPENASTEMPLATE_LOCKED : STR_QUERY_OPENASTEMPLATE)); + + if (bAllowIgnoreLock) + sMsg += "\n\n" + SfxResId(STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE); + + return sMsg.replaceFirst("%LOCKINFO", sLockUserData); + } +}; + +bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& aPath, const std::shared_ptr<const SfxFilter>& pFilter, sal_uInt32 nPasswordHash, const uno::Sequence< beans::PropertyValue >& aInfo ) +{ + // TODO/LATER: In future the info should replace the direct hash completely + bool bResult = ( !nPasswordHash && !aInfo.hasElements() ); + + SAL_WARN_IF( !(pFilter && ( pFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY )), "sfx.view", + "PasswordToModify feature is active for a filter that does not support it!"); + + if ( pFilter && xHandler.is() ) + { + bool bCancel = false; + bool bFirstTime = true; + + while ( !bResult && !bCancel ) + { + bool bMSType = !pFilter->IsOwnFormat(); + + ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest( + new ::comphelper::DocPasswordRequest( + bMSType ? ::comphelper::DocPasswordRequestType::MS : ::comphelper::DocPasswordRequestType::Standard, + bFirstTime ? css::task::PasswordRequestMode_PASSWORD_ENTER : css::task::PasswordRequestMode_PASSWORD_REENTER, + aPath, + true ) ); + + xHandler->handle( pPasswordRequest ); + + if ( pPasswordRequest->isPassword() ) + { + if ( aInfo.hasElements() ) + { + bResult = ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( pPasswordRequest->getPasswordToModify(), aInfo ); + } + else + { + // the binary format + bResult = ( SfxMedium::CreatePasswordToModifyHash( pPasswordRequest->getPasswordToModify(), pFilter->GetServiceName()=="com.sun.star.text.TextDocument" ) == nPasswordHash ); + } + } + else + bCancel = true; + + bFirstTime = false; + } + } + + return bResult; +} + +bool physObjIsOlder(INetURLObject const & aMedObj, INetURLObject const & aPhysObj) { + return ::utl::UCBContentHelper::IsYounger(aMedObj.GetMainURL( INetURLObject::DecodeMechanism::NONE), + aPhysObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); +} +} + +void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) +{ + SfxObjectShell* pSh = GetObjectShell(); + switch ( rReq.GetSlot() ) + { + case SID_EDITDOC: + case SID_READONLYDOC: + { + // Due to Double occupancy in toolboxes (with or without Ctrl), + // it is also possible that the slot is enabled, but Ctrl-click + // despite this is not! + if( !pSh || !pSh->HasName() || !(pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT )) + break; + + if (pSh->isEditDocLocked()) + break; + + // Only change read-only UI and remove info bar when we succeed + struct ReadOnlyUIGuard + { + SfxViewFrame* m_pFrame; + SfxObjectShell* m_pSh; + SfxMedium* m_pMed = nullptr; + bool m_bSetRO; + ReadOnlyUIGuard(SfxViewFrame* pFrame, SfxObjectShell* p_Sh) + : m_pFrame(pFrame), m_pSh(p_Sh), m_bSetRO(p_Sh->IsReadOnlyUI()) + {} + ~ReadOnlyUIGuard() COVERITY_NOEXCEPT_FALSE + { + if (m_bSetRO != m_pSh->IsReadOnlyUI()) + { + m_pSh->SetReadOnlyUI(m_bSetRO); + if (!m_bSetRO) + m_pFrame->RemoveInfoBar(u"readonly"); + if (m_pMed) + { + bool const isEnableSetModified(m_pSh->IsEnableSetModified()); + m_pSh->EnableSetModified(false); + // tdf#116066: DoSaveCompleted should be called after SetReadOnlyUI + m_pSh->DoSaveCompleted(m_pMed); + m_pSh->Broadcast(SfxHint(SfxHintId::ModeChanged)); + m_pSh->EnableSetModified(isEnableSetModified); + } + } + } + } aReadOnlyUIGuard(this, pSh); + + 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 = pMed->GetItemSet().GetItem(SID_VIEWONLY, false); + if ( pItem && pItem->GetValue() ) + { + SfxApplication* pApp = SfxGetpApp(); + SfxAllItemSet aSet( pApp->GetPool() ); + aSet.Put( SfxStringItem( SID_FILE_NAME, pMed->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE) ) ); + aSet.Put( SfxBoolItem( SID_TEMPLATE, true ) ); + aSet.Put( SfxStringItem( SID_TARGETNAME, "_blank" ) ); + const SfxStringItem* pReferer = pMed->GetItemSet().GetItem(SID_REFERER, false); + if ( pReferer ) + aSet.Put( *pReferer ); + const SfxInt16Item* pVersionItem = pMed->GetItemSet().GetItem(SID_VERSION, false); + if ( pVersionItem ) + aSet.Put( *pVersionItem ); + + if( pMed->GetFilter() ) + { + aSet.Put( SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) ); + const SfxStringItem* pOptions = pMed->GetItemSet().GetItem(SID_FILE_FILTEROPTIONS, false); + if ( pOptions ) + aSet.Put( *pOptions ); + } + + GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet ); + return; + } + + StreamMode nOpenMode; + bool bNeedsReload = false; + bool bPasswordEntered = false; + if ( !pSh->IsReadOnly() ) + { + // Save and reload Readonly + if( pSh->IsModified() ) + { + if ( pSh->PrepareClose() ) + { + // the storing could let the medium be changed + pMed = pSh->GetMedium(); + bNeedsReload = true; + } + else + { + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) ); + return; + } + } + nOpenMode = SFX_STREAM_READONLY; + aReadOnlyUIGuard.m_bSetRO = true; + } + else + { + if ( pSh->IsReadOnlyMedium() + && ( pSh->GetModifyPasswordHash() || pSh->GetModifyPasswordInfo().hasElements() ) + && !pSh->IsModifyPasswordEntered() ) + { + const OUString aDocumentName = INetURLObject( pMed->GetOrigURL() ).GetMainURL( INetURLObject::DecodeMechanism::WithCharset ); + if( !AskPasswordToModify_Impl( pMed->GetInteractionHandler(), aDocumentName, pMed->GetFilter(), pSh->GetModifyPasswordHash(), pSh->GetModifyPasswordInfo() ) ) + { + // this is a read-only document, if it has "Password to modify" + // the user should enter password before he can edit the document + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) ); + return; + } + + pSh->SetModifyPasswordEntered(); + bPasswordEntered = true; + } + + nOpenMode = pSh->IsOriginallyReadOnlyMedium() ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE; + aReadOnlyUIGuard.m_bSetRO = false; + + // if only the view was in the readonly mode then there is no need to do the reload + if ( !pSh->IsReadOnlyMedium() ) + { + // SetReadOnlyUI causes recomputation of window title, using + // open mode among other things, so call SetOpenMode before + // SetReadOnlyUI: + pMed->SetOpenMode( nOpenMode ); + return; + } + } + + if ( rReq.IsAPI() ) + { + // Control through API if r/w or r/o + const SfxBoolItem* pEditItem = rReq.GetArg<SfxBoolItem>(SID_EDITDOC); + if ( pEditItem ) + nOpenMode = pEditItem->GetValue() ? SFX_STREAM_READWRITE : SFX_STREAM_READONLY; + } + + // doing + + OUString sTemp; + osl::FileBase::getFileURLFromSystemPath( pMed->GetPhysicalName(), sTemp ); + INetURLObject aPhysObj( sTemp ); + const SfxInt16Item* pVersionItem = pMed->GetItemSet().GetItem(SID_VERSION, false); + + INetURLObject aMedObj( pMed->GetName() ); + + // -> tdf#82744 + // the logic below is following: + // if the document seems not to need to be reloaded + // and the physical name is different to the logical one, + // then on file system it can be checked that the copy is still newer than the original and no document reload is required. + // Did some semplification to enhance readability of the 'if' expression + // + // when the 'http/https' protocol is active, the bool bPhysObjIsYounger relies upon the getlastmodified Property of a WebDAV resource. + // Said property should be implemented, but sometimes it's not. + // implemented. On this case the reload activated here will not work properly. + // TODO: change the check age method for WebDAV to etag (entity-tag) property value, need some rethinking, since the + // etag tells that the cache representation (e.g. in LO) is different from the one on the server, + // but tells nothing about the age + // Details at this link: http://tools.ietf.org/html/rfc4918#section-15, section 15.7 + bool bIsWebDAV = aMedObj.isAnyKnownWebDAVScheme(); + + // tdf#118938 Reload the document when the user enters the editing password, + // even if the physical name isn't different to the logical name. + if ( ( !bNeedsReload && ( ( aMedObj.GetProtocol() == INetProtocol::File && + ( aMedObj.getFSysPath( FSysStyle::Detect ) != aPhysObj.getFSysPath( FSysStyle::Detect ) + || bPasswordEntered ) && + !physObjIsOlder(aMedObj, aPhysObj)) + || (bIsWebDAV && !physObjIsOlder(aMedObj, aPhysObj)) + || ( pMed->IsRemote() && !bIsWebDAV ) ) ) + || pVersionItem ) + // <- tdf#82744 + { + bool bOK = false; + bool bRetryIgnoringLock = false; + bool bOpenTemplate = false; + std::optional<bool> aOrigROVal; + if (!pVersionItem) + { + auto pRO = pMed->GetItemSet().GetItem<SfxBoolItem>(SID_DOC_READONLY, false); + if (pRO) + aOrigROVal = pRO->GetValue(); + } + do { + LockFileEntry aLockData; + if ( !pVersionItem ) + { + if (bRetryIgnoringLock) + pMed->ResetError(); + + bool bHasStorage = pMed->HasStorage_Impl(); + // switching edit mode could be possible without reload + if ( bHasStorage && pMed->GetStorage() == pSh->GetStorage() ) + { + // TODO/LATER: faster creation of copy + if ( !pSh->ConnectTmpStorage_Impl( pMed->GetStorage(), pMed ) ) + return; + } + + pMed->CloseAndRelease(); + pMed->SetOpenMode( nOpenMode ); + // We need to clear the SID_DOC_READONLY item from the set, to allow + // MediaDescriptor::impl_openStreamWithURL (called indirectly by + // SfxMedium::CompleteReOpen) to properly fill input stream of the + // descriptor, even when the file can't be open in read-write mode. + // Only then can following call to SfxMedium::LockOrigFileOnDemand + // return proper information about who has locked the file, to show + // in the SfxQueryOpenAsTemplate box below; otherwise it exits right + // after call to SfxMedium::GetMedium_Impl. This mimics what happens + // when the file is opened initially, when filter detection code also + // calls MediaDescriptor::impl_openStreamWithURL without the item set. + pMed->GetItemSet().ClearItem(SID_DOC_READONLY); + pMed->CompleteReOpen(); + pMed->GetItemSet().Put( + SfxBoolItem(SID_DOC_READONLY, !(nOpenMode & StreamMode::WRITE))); + if ( nOpenMode & StreamMode::WRITE ) + { + auto eResult = pMed->LockOrigFileOnDemand( + true, true, bRetryIgnoringLock, &aLockData); + bRetryIgnoringLock + = eResult == SfxMedium::LockFileResult::FailedLockFile; + } + + // LockOrigFileOnDemand might set the readonly flag itself, it should be set back + pMed->GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); + + if ( !pMed->GetErrorCode() ) + bOK = true; + } + + if( !bOK ) + { + if (nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI()) + { + // css::sdbcx::User offering to open it as a template + SfxQueryOpenAsTemplate aBox(GetWindow().GetFrameWeld(), + bRetryIgnoringLock, aLockData); + + short nUserAnswer = aBox.run(); + 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; + } + } + while ( !bOK && bRetryIgnoringLock ); + + if( !bOK ) + { + ErrCodeMsg nErr = pMed->GetErrorCode(); + if ( pVersionItem ) + nErr = ERRCODE_IO_ACCESSDENIED; + else + { + pMed->ResetError(); + pMed->SetOpenMode( SFX_STREAM_READONLY ); + if (aOrigROVal) + pMed->GetItemSet().Put(SfxBoolItem(SID_DOC_READONLY, *aOrigROVal)); + else + pMed->GetItemSet().ClearItem(SID_DOC_READONLY); + pMed->ReOpen(); + pSh->DoSaveCompleted( pMed ); + } + + // Readonly document can not be switched to edit mode? + rReq.Done(); + + if ( nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI() ) + { + if ( bOpenTemplate ) + { + SfxApplication* pApp = SfxGetpApp(); + SfxAllItemSet aSet( pApp->GetPool() ); + aSet.Put( SfxStringItem( SID_FILE_NAME, pMed->GetName() ) ); + const SfxStringItem* pReferer = pMed->GetItemSet().GetItem(SID_REFERER, false); + if ( pReferer ) + aSet.Put( *pReferer ); + aSet.Put( SfxBoolItem( SID_TEMPLATE, true ) ); + if ( pVersionItem ) + aSet.Put( *pVersionItem ); + + if( pMed->GetFilter() ) + { + aSet.Put( SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) ); + const SfxStringItem* pOptions = pMed->GetItemSet().GetItem(SID_FILE_FILTEROPTIONS, false); + if ( pOptions ) + aSet.Put( *pOptions ); + } + + GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet ); + return; + } + + nErr = ERRCODE_NONE; + } + + // Keep the read-only UI + aReadOnlyUIGuard.m_bSetRO = true; + + ErrorHandler::HandleError( nErr ); + rReq.SetReturnValue( + SfxBoolItem( rReq.GetSlot(), false ) ); + return; + } + else + { + aReadOnlyUIGuard.m_pMed = pMed; + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), true ) ); + rReq.Done( true ); + return; + } + } + + rReq.AppendItem( SfxBoolItem(SID_FORCERELOAD, + (rReq.GetSlot() == SID_EDITDOC + // tdf#151715 exclude files loaded from /tmp to avoid Notebookbar bugs + && (!pSh->IsOriginallyReadOnlyMedium() || pSh->IsOriginallyLoadedReadOnlyMedium())) + || bNeedsReload) ); + rReq.AppendItem( SfxBoolItem( SID_SILENT, true )); + + [[fallthrough]]; //TODO ??? + } + + case SID_RELOAD: + { + // Due to Double occupancy in toolboxes (with or without Ctrl), + // it is also possible that the slot is enabled, but Ctrl-click + // despite this is not! + if ( !pSh || !pSh->CanReload_Impl() ) + break; + SfxApplication* pApp = SfxGetpApp(); + const SfxBoolItem* pForceReloadItem = rReq.GetArg<SfxBoolItem>(SID_FORCERELOAD); + if( pForceReloadItem && !pForceReloadItem->GetValue() && + !pSh->GetMedium()->IsExpired() ) + return; + if( m_pImpl->bReloading || pSh->IsInModalMode() ) + return; + + // AutoLoad is prohibited if possible + const SfxBoolItem* pAutoLoadItem = rReq.GetArg<SfxBoolItem>(SID_AUTOLOAD); + if ( pAutoLoadItem && pAutoLoadItem->GetValue() && + GetFrame().IsAutoLoadLocked_Impl() ) + return; + + SfxObjectShellLock xOldObj( pSh ); + m_pImpl->bReloading = true; + const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME); + // Open as editable? + bool bForEdit = !pSh->IsReadOnly(); + + // If possible ask the User + bool bDo = GetViewShell()->PrepareClose(); + const SfxBoolItem* pSilentItem = rReq.GetArg<SfxBoolItem>(SID_SILENT); + if (getenv("SAL_NO_QUERYSAVE")) + bDo = true; + else if (bDo && GetFrame().DocIsModified_Impl() && !rReq.IsAPI() + && (!pSilentItem || !pSilentItem->GetValue())) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( + GetWindow().GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo, + SfxResId(STR_QUERY_LASTVERSION))); + bDo = RET_YES == xBox->run(); + } + + 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() ); + + // Empty existing SfxMDIFrames for this Document + // in native format or R/O, open it now for editing? + SfxObjectShellLock xNewObj; + + // collect the views of the document + // TODO: when UNO ViewFactories are available for SFX-based documents, the below code should + // be UNOized, too + typedef ::std::pair< Reference< XFrame >, SfxInterfaceId > ViewDescriptor; + ::std::vector< ViewDescriptor > aViewFrames; + SfxViewFrame *pView = GetFirst( xOldObj ); + while ( pView ) + { + Reference< XFrame > xFrame( pView->GetFrame().GetFrameInterface() ); + SAL_WARN_IF( !xFrame.is(), "sfx.view", "SfxViewFrame::ExecReload_Impl: no XFrame?!"); + aViewFrames.emplace_back( xFrame, pView->GetCurViewId() ); + + pView = GetNext( *pView, xOldObj ); + } + + xOldObj->Get_Impl()->pReloadTimer.reset(); + + std::optional<SfxAllItemSet> pNewSet; + std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter(); + if( pURLItem ) + { + pNewSet.emplace( pApp->GetPool() ); + pNewSet->Put( *pURLItem ); + + // Filter Detection + OUString referer; + const SfxStringItem* refererItem = rReq.GetArg<SfxStringItem>(SID_REFERER); + if (refererItem != nullptr) { + referer = refererItem->GetValue(); + } + SfxMedium aMedium( pURLItem->GetValue(), referer, SFX_STREAM_READWRITE ); + SfxFilterMatcher().GuessFilter( aMedium, pFilter ); + if ( pFilter ) + pNewSet->Put( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) ); + pNewSet->Put( aMedium.GetItemSet() ); + } + else + { + pNewSet.emplace( pMedium->GetItemSet() ); + pNewSet->ClearItem( SID_VIEW_ID ); + pNewSet->ClearItem( SID_STREAM ); + pNewSet->ClearItem( SID_INPUTSTREAM ); + pNewSet->Put( SfxStringItem( SID_FILTER_NAME, pMedium->GetFilter()->GetName() ) ); + + // let the current security settings be checked again + pNewSet->Put( SfxUInt16Item( SID_MACROEXECMODE, document::MacroExecMode::USE_CONFIG ) ); + + if ( pSh->IsOriginallyReadOnlyMedium() + || pSh->IsOriginallyLoadedReadOnlyMedium() ) + // edit mode is switched or reload of readonly document + pNewSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) ); + else + // Reload of file opened for writing + pNewSet->ClearItem( SID_DOC_READONLY ); + } + + // If a salvaged file is present, do not enclose the OrigURL + // again, since the Template is invalid after reload. + const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(&*pNewSet, SID_DOC_SALVAGE, false); + if( pSalvageItem ) + { + pNewSet->ClearItem( SID_DOC_SALVAGE ); + } + +#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT + // TODO/LATER: Temporary solution, the SfxMedium must know the original URL as aLogicName + // SfxMedium::Transfer_Impl() will be forbidden then. + if ( xOldObj->IsDocShared() ) + pNewSet->Put( SfxStringItem( SID_FILE_NAME, xOldObj->GetSharedFileURL() ) ); +#endif + if ( pURLItem ) + pNewSet->Put( SfxStringItem( SID_REFERER, pMedium->GetName() ) ); + else + pNewSet->Put( SfxStringItem( SID_REFERER, OUString() ) ); + + xOldObj->CancelTransfers(); + + + if ( pSilentItem && pSilentItem->GetValue() ) + pNewSet->Put( SfxBoolItem( SID_SILENT, true ) ); + + const SfxUnoAnyItem* pInteractionItem = SfxItemSet::GetItem<SfxUnoAnyItem>(&*pNewSet, SID_INTERACTIONHANDLER, false); + const SfxUInt16Item* pMacroExecItem = SfxItemSet::GetItem<SfxUInt16Item>(&*pNewSet, SID_MACROEXECMODE, false); + const SfxUInt16Item* pDocTemplateItem = SfxItemSet::GetItem<SfxUInt16Item>(&*pNewSet, SID_UPDATEDOCMODE, false); + + if (!pInteractionItem) + { + Reference < task::XInteractionHandler2 > xHdl = task::InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr ); + if (xHdl.is()) + pNewSet->Put( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHdl)) ); + } + + if (!pMacroExecItem) + pNewSet->Put( SfxUInt16Item(SID_MACROEXECMODE,css::document::MacroExecMode::USE_CONFIG) ); + if (!pDocTemplateItem) + pNewSet->Put( SfxUInt16Item(SID_UPDATEDOCMODE,css::document::UpdateDocMode::ACCORDING_TO_CONFIG) ); + + xOldObj->SetModified( false ); + // Do not cache the old Document! Is invalid when loading + // another document. + + bool bHasStorage = pMedium->HasStorage_Impl(); + if( bHandsOff ) + { + if ( bHasStorage && pMedium->GetStorage() == xOldObj->GetStorage() ) + { + // TODO/LATER: faster creation of copy + if ( !xOldObj->ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium ) ) + return; + } + + pMedium->CloseAndRelease(); + } + + xNewObj = SfxObjectShell::CreateObject( pFilter->GetServiceName() ); + + if ( xOldObj->IsModifyPasswordEntered() ) + xNewObj->SetModifyPasswordEntered(); + + uno::Sequence < beans::PropertyValue > aLoadArgs; + TransformItems( SID_OPENDOC, *pNewSet, aLoadArgs ); + try + { + uno::Reference < frame::XLoadable > xLoad( xNewObj->GetModel(), uno::UNO_QUERY ); + xLoad->load( aLoadArgs ); + } + catch ( uno::Exception& ) + { + xNewObj->DoClose(); + xNewObj = nullptr; + pMedium->AddToCheckEditableWorkerList(); + } + + pNewSet.reset(); + + if( !xNewObj.Is() ) + { + if( bHandsOff ) + { + // back to old medium + pMedium->ReOpen(); + pMedium->LockOrigFileOnDemand( false, true ); + + xOldObj->DoSaveCompleted( pMedium ); + } + } + else + { + if ( xNewObj->GetModifyPasswordHash() && xNewObj->GetModifyPasswordHash() != xOldObj->GetModifyPasswordHash() ) + { + xNewObj->SetModifyPasswordEntered( false ); + xNewObj->SetReadOnly(); + } + else if ( rReq.GetSlot() == SID_EDITDOC || rReq.GetSlot() == SID_READONLYDOC ) + { + xNewObj->SetReadOnlyUI( !bForEdit ); + } + +#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT + if ( xNewObj->IsDocShared() ) + { + // the file is shared but the closing can change the sharing control file + xOldObj->DoNotCleanShareControlFile(); + } +#endif + // the Reload and Silent items were only temporary, remove them + xNewObj->GetMedium()->GetItemSet().ClearItem( SID_RELOAD ); + xNewObj->GetMedium()->GetItemSet().ClearItem( SID_SILENT ); + TransformItems( SID_OPENDOC, xNewObj->GetMedium()->GetItemSet(), aLoadArgs ); + + UpdateDocument_Impl(); + + auto sModule = vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame().GetFrameInterface()); + OUString sReloadNotebookBar; + if (sModule == "com.sun.star.text.TextDocument") + sReloadNotebookBar = u"modules/swriter/ui/"_ustr; + else if (sModule == "com.sun.star.sheet.SpreadsheetDocument") + sReloadNotebookBar = u"modules/scalc/ui/"_ustr; + else if (sfx2::SfxNotebookBar::IsActive() + && sModule != "presentation.PresentationDocument" + && sModule != "com.sun.star.drawing.DrawingDocument") + { + assert(false && "SID_RELOAD Notebookbar active, but not refreshed here"); + } + + try + { + for (auto const& viewFrame : aViewFrames) + { + LoadViewIntoFrame_Impl( *xNewObj, viewFrame.first, aLoadArgs, viewFrame.second, false ); + } + aViewFrames.clear(); + } + catch( const Exception& ) + { + // close the remaining frames + // Don't catch exceptions herein, if this fails, then we're left in an indetermined state, and + // crashing is better than trying to proceed + for (auto const& viewFrame : aViewFrames) + { + Reference< util::XCloseable > xClose( viewFrame.first, UNO_QUERY_THROW ); + xClose->close( true ); + } + aViewFrames.clear(); + } + + const SfxInt32Item* pPageNumber = rReq.GetArg<SfxInt32Item>(SID_PAGE_NUMBER); + if (pPageNumber && pPageNumber->GetValue() >= 0) + { + // Restore current page after reload. + uno::Reference<drawing::XDrawView> xController( + xNewObj->GetModel()->getCurrentController(), uno::UNO_QUERY); + uno::Reference<drawing::XDrawPagesSupplier> xSupplier(xNewObj->GetModel(), + uno::UNO_QUERY); + uno::Reference<drawing::XDrawPages> xDrawPages = xSupplier->getDrawPages(); + uno::Reference<drawing::XDrawPage> xDrawPage( + xDrawPages->getByIndex(pPageNumber->GetValue()), uno::UNO_QUERY); + xController->setCurrentPage(xDrawPage); + } + + // Propagate document closure. + SfxGetpApp()->NotifyEvent( SfxEventHint( SfxEventHintId::CloseDoc, GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ), xOldObj ) ); + + // tdf#126006 Calc needs to reload the notebookbar after closing the document + if (!sReloadNotebookBar.isEmpty()) + sfx2::SfxNotebookBar::ReloadNotebookBar(sReloadNotebookBar); + } + + // Record as done + rReq.Done( true ); + rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), true)); + return; + } + else + { + // Record as not done + rReq.Done(); + rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), false)); + m_pImpl->bReloading = false; + return; + } + } + } +} + +void SfxViewFrame::StateReload_Impl( SfxItemSet& rSet ) +{ + SfxObjectShell* pSh = GetObjectShell(); + if ( !pSh ) + { + // I'm just on reload and am yielding myself ... + return; + } + + SfxWhichIter aIter( rSet ); + for ( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich() ) + { + switch ( nWhich ) + { + case SID_EDITDOC: + case SID_READONLYDOC: + { + const SfxViewShell *pVSh; + const SfxShell *pFSh; + if ( !pSh->HasName() || + !( pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ) || + (pSh->isEditDocLocked()) || + ( pSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED && + ( !(pVSh = pSh->GetViewShell()) || + !(pFSh = pVSh->GetFormShell()) || + !pFSh->IsDesignMode()))) + rSet.DisableItem( nWhich ); + else + { + const SfxBoolItem* pItem = pSh->GetMedium()->GetItemSet().GetItem(SID_EDITDOC, false); + if ( pItem && !pItem->GetValue() ) + rSet.DisableItem( nWhich ); + else + { + if (nWhich==SID_EDITDOC) + rSet.Put( SfxBoolItem( nWhich, !pSh->IsReadOnly() ) ); + else if (nWhich==SID_READONLYDOC) + rSet.Put( SfxBoolItem( nWhich, pSh->IsReadOnly() ) ); + } + } + break; + } + + case SID_RELOAD: + { + if ( !pSh->CanReload_Impl() || pSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + rSet.DisableItem(nWhich); + else + { + // If any ChildFrame is reloadable, the slot is enabled, + // so you can perform CTRL-Reload + rSet.Put( SfxBoolItem( nWhich, false)); + } + + break; + } + } + } +} + +void SfxViewFrame::ExecHistory_Impl( SfxRequest &rReq ) +{ + // Is there an Undo-Manager on the top Shell? + SfxShell *pSh = GetDispatcher()->GetShell(0); + if (!pSh) + return; + + SfxUndoManager* pShUndoMgr = pSh->GetUndoManager(); + bool bOK = false; + if ( pShUndoMgr ) + { + switch ( rReq.GetSlot() ) + { + case SID_CLEARHISTORY: + pShUndoMgr->Clear(); + bOK = true; + break; + + case SID_UNDO: + pShUndoMgr->Undo(); + GetBindings().InvalidateAll(false); + bOK = true; + break; + + case SID_REDO: + pShUndoMgr->Redo(); + GetBindings().InvalidateAll(false); + bOK = true; + break; + + case SID_REPEAT: + if ( pSh->GetRepeatTarget() ) + pShUndoMgr->Repeat( *pSh->GetRepeatTarget() ); + bOK = true; + break; + } + } + else if ( GetViewShell() ) + { + // The SW has its own undo in the View + const SfxPoolItemHolder& rResult(GetViewShell()->ExecuteSlot(rReq)); + if (nullptr != rResult.getItem()) + bOK = static_cast<const SfxBoolItem*>(rResult.getItem())->GetValue(); + } + + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bOK ) ); + rReq.Done(); +} + +void SfxViewFrame::StateHistory_Impl( SfxItemSet &rSet ) +{ + // Search for Undo-Manager + SfxShell *pSh = GetDispatcher()->GetShell(0); + if ( !pSh ) + // I'm just on reload and am yielding myself ... + return; + + SfxUndoManager *pShUndoMgr = pSh->GetUndoManager(); + if ( !pShUndoMgr ) + { + // The SW has its own undo in the View + SfxWhichIter aIter( rSet ); + SfxViewShell *pViewSh = GetViewShell(); + if( !pViewSh ) return; + for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() ) + pViewSh->GetSlotState( nSID, nullptr, &rSet ); + return; + } + + if ( pShUndoMgr->GetUndoActionCount() == 0 && + pShUndoMgr->GetRedoActionCount() == 0 && + pShUndoMgr->GetRepeatActionCount() == 0 ) + rSet.DisableItem( SID_CLEARHISTORY ); + + if (pShUndoMgr->GetUndoActionCount()) + { + const SfxUndoAction* pAction = pShUndoMgr->GetUndoAction(); + SfxViewShell *pViewSh = GetViewShell(); + if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId()) + { + rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + } + else + { + rSet.Put( SfxStringItem( SID_UNDO, SvtResId(STR_UNDO)+pShUndoMgr->GetUndoActionComment() ) ); + } + } + else + rSet.DisableItem( SID_UNDO ); + + if (pShUndoMgr->GetRedoActionCount()) + { + const SfxUndoAction* pAction = pShUndoMgr->GetRedoAction(); + SfxViewShell *pViewSh = GetViewShell(); + if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId()) + { + rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + } + else + { + rSet.Put(SfxStringItem(SID_REDO, SvtResId(STR_REDO) + pShUndoMgr->GetRedoActionComment())); + } + } + else + rSet.DisableItem( SID_REDO ); + + SfxRepeatTarget *pTarget = pSh->GetRepeatTarget(); + if (pTarget && pShUndoMgr->GetRepeatActionCount() && pShUndoMgr->CanRepeat(*pTarget)) + rSet.Put( SfxStringItem( SID_REPEAT, SvtResId(STR_REPEAT)+pShUndoMgr->GetRepeatActionComment(*pTarget) ) ); + else + rSet.DisableItem( SID_REPEAT ); +} + +void SfxViewFrame::PopShellAndSubShells_Impl( SfxViewShell& i_rViewShell ) +{ + i_rViewShell.PopSubShells_Impl(); + sal_uInt16 nLevel = m_pDispatcher->GetShellLevel( i_rViewShell ); + if ( nLevel != USHRT_MAX ) + { + if ( nLevel ) + { + // more sub shells on the stack, which were not affected by PopSubShells_Impl + SfxShell *pSubShell = m_pDispatcher->GetShell( nLevel-1 ); + m_pDispatcher->Pop( *pSubShell, SfxDispatcherPopFlags::POP_UNTIL | SfxDispatcherPopFlags::POP_DELETE ); + } + m_pDispatcher->Pop( i_rViewShell ); + m_pDispatcher->Flush(); + } + +} + +/* [Description] + + This method empties the SfxViewFrame, i.e. takes the <SfxObjectShell> + from the dispatcher and ends its <SfxListener> Relationship to this + SfxObjectShell (by which they may even destroy themselves). + + Thus, by invoking ReleaseObjectShell() and SetObjectShell() the + SfxObjectShell can be replaced. + + Between ReleaseObjectShell() and SetObjectShell() the control cannot + be handed over to the system. + + [Cross-reference] + + <SfxViewFrame::SetObjectShell(SfxObjectShell&)> +*/ +void SfxViewFrame::ReleaseObjectShell_Impl() +{ + DBG_ASSERT( m_xObjSh.is(), "no SfxObjectShell to release!" ); + + GetFrame().ReleasingComponent_Impl(); + if ( GetWindow().HasChildPathFocus( true ) ) + { + GetWindow().GrabFocus(); + } + + SfxViewShell *pDyingViewSh = GetViewShell(); + if ( pDyingViewSh ) + { + PopShellAndSubShells_Impl( *pDyingViewSh ); + pDyingViewSh->DisconnectAllClients(); + SetViewShell_Impl(nullptr); + delete pDyingViewSh; + } +#ifdef DBG_UTIL + else + OSL_FAIL("No Shell"); +#endif + + if ( m_xObjSh.is() ) + { + m_pDispatcher->Pop( *m_xObjSh ); + SfxModule* pModule = m_xObjSh->GetModule(); + if( pModule ) + m_pDispatcher->RemoveShell_Impl( *pModule ); + m_pDispatcher->Flush(); + EndListening( *m_xObjSh ); + + Notify( *m_xObjSh, SfxHint(SfxHintId::TitleChanged) ); + Notify( *m_xObjSh, SfxHint(SfxHintId::DocChanged) ); + + if ( 1 == m_xObjSh->GetOwnerLockCount() && m_pImpl->bObjLocked && m_xObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + m_xObjSh->DoClose(); + SfxObjectShellRef xDyingObjSh = m_xObjSh; + m_xObjSh.clear(); + if( GetFrame().GetHasTitle() && m_pImpl->nDocViewNo ) + xDyingObjSh->GetNoSet_Impl().ReleaseIndex(m_pImpl->nDocViewNo-1); + if ( m_pImpl->bObjLocked ) + { + xDyingObjSh->OwnerLock( false ); + m_pImpl->bObjLocked = false; + } + } + + GetDispatcher()->SetDisableFlags( SfxDisableFlags::NONE ); +} + +void SfxViewFrame::Close() +{ + + DBG_ASSERT( GetFrame().IsClosing_Impl() || !GetFrame().GetFrameInterface().is(), "ViewFrame closed too early!" ); + + // If no saving have been made up until now, then embedded Objects should + // not be saved automatically anymore. + if ( GetViewShell() ) + GetViewShell()->DisconnectAllClients(); + Broadcast( SfxHint( SfxHintId::Dying ) ); + + if (SfxViewFrame::Current() == this) + SfxViewFrame::SetViewFrame( nullptr ); + + // Since the Dispatcher is emptied, it can not be used in any reasonable + // manner, thus it is better to let the dispatcher be. + GetDispatcher()->Lock(true); + delete this; +} + +void SfxViewFrame::DoActivate( bool bUI ) +{ + m_pDispatcher->DoActivate_Impl( bUI ); +} + +void SfxViewFrame::DoDeactivate(bool bUI, SfxViewFrame const * pNewFrame ) +{ + m_pDispatcher->DoDeactivate_Impl( bUI, pNewFrame ); +} + +void SfxViewFrame::InvalidateBorderImpl( const SfxViewShell* pSh ) +{ + if( !pSh || m_nAdjustPosPixelLock ) + return; + + if ( GetViewShell() && GetWindow().IsVisible() ) + { + if ( GetFrame().IsInPlace() ) + { + return; + } + + DoAdjustPosSizePixel( GetViewShell(), Point(), + GetWindow().GetOutputSizePixel(), + false ); + } +} + +void SfxViewFrame::SetBorderPixelImpl +( + const SfxViewShell* pVSh, + const SvBorder& rBorder +) + +{ + m_pImpl->aBorder = rBorder; + + if ( m_pImpl->bResizeInToOut && !GetFrame().IsInPlace() ) + { + Size aSize = pVSh->GetWindow()->GetOutputSizePixel(); + if ( aSize.Width() && aSize.Height() ) + { + aSize.AdjustWidth(rBorder.Left() + rBorder.Right() ); + aSize.AdjustHeight(rBorder.Top() + rBorder.Bottom() ); + + Size aOldSize = GetWindow().GetOutputSizePixel(); + GetWindow().SetOutputSizePixel( aSize ); + vcl::Window* pParent = &GetWindow(); + while ( pParent->GetParent() ) + pParent = pParent->GetParent(); + Size aOuterSize = pParent->GetOutputSizePixel(); + aOuterSize.AdjustWidth( aSize.Width() - aOldSize.Width() ); + aOuterSize.AdjustHeight( aSize.Height() - aOldSize.Height() ); + pParent->SetOutputSizePixel( aOuterSize ); + } + } + else + { + tools::Rectangle aEditArea( Point(), GetWindow().GetOutputSizePixel() ); + aEditArea.AdjustLeft(rBorder.Left() ); + aEditArea.AdjustRight( -(rBorder.Right()) ); + aEditArea.AdjustTop(rBorder.Top() ); + aEditArea.AdjustBottom( -(rBorder.Bottom()) ); + pVSh->GetWindow()->SetPosSizePixel( aEditArea.TopLeft(), aEditArea.GetSize() ); + } +} + +const SvBorder& SfxViewFrame::GetBorderPixelImpl() const +{ + return m_pImpl->aBorder; +} + +void SfxViewFrame::AppendReadOnlyInfobar() +{ + bool bSignPDF = m_xObjSh->IsSignPDF(); + bool bSignWithCert = false; + if (bSignPDF) + { + SfxObjectShell* pObjectShell = GetObjectShell(); + uno::Reference<security::XCertificate> xCertificate = pObjectShell->GetSignPDFCertificate(); + bSignWithCert = xCertificate.is(); + } + + auto pInfoBar = AppendInfoBar("readonly", "", + SfxResId(bSignPDF ? STR_READONLY_PDF : STR_READONLY_DOCUMENT), + InfobarType::INFO); + if (!pInfoBar) + return; + + if (bSignPDF) + { + // SID_SIGNPDF opened a read-write PDF + // read-only for signing purposes. + weld::Button& rSignButton = pInfoBar->addButton(); + if (bSignWithCert) + { + rSignButton.set_label(SfxResId(STR_READONLY_FINISH_SIGN)); + } + else + { + rSignButton.set_label(SfxResId(STR_READONLY_SIGN)); + } + + rSignButton.connect_clicked(LINK(this, SfxViewFrame, SignDocumentHandler)); + } + + bool showEditDocumentButton = true; + if (m_xObjSh->isEditDocLocked()) + showEditDocumentButton = false; + + if (showEditDocumentButton) + { + weld::Button& rBtn = pInfoBar->addButton(); + rBtn.set_label(SfxResId(STR_READONLY_EDIT)); + rBtn.connect_clicked(LINK(this, SfxViewFrame, SwitchReadOnlyHandler)); + } +} + +void SfxViewFrame::HandleSecurityInfobar(const OUString& sSecondaryMessage) +{ + if (!HasInfoBarWithID(u"securitywarn")) + { + // new info bar + if (!sSecondaryMessage.isEmpty()) + { + auto pInfoBar = AppendInfoBar("securitywarn", SfxResId(STR_HIDDENINFO_CONTAINS).replaceAll("\n\n", " "), + sSecondaryMessage, InfobarType::WARNING); + if (!pInfoBar) + return; + + weld::Button& rGetInvolvedButton = pInfoBar->addButton(); + rGetInvolvedButton.set_label(SfxResId(STR_SECURITY_OPTIONS)); + rGetInvolvedButton.connect_clicked(LINK(this, SfxViewFrame, SecurityButtonHandler)); + } + } + else + { + // info bar exists already + if (sSecondaryMessage.isEmpty()) + { + RemoveInfoBar(u"securitywarn"); + } + else + { + UpdateInfoBar(u"securitywarn", SfxResId(STR_HIDDENINFO_CONTAINS).replaceAll("\n\n", " "), + sSecondaryMessage, InfobarType::WARNING); + } + } +} + +void SfxViewFrame::AppendContainsMacrosInfobar() +{ + SfxObjectShell_Impl* pObjImpl = m_xObjSh->Get_Impl(); + + // what's the difference between pObjImpl->documentStorageHasMacros() and pObjImpl->aMacroMode.hasMacroLibrary() ? + bool bHasDocumentMacros = pObjImpl->aMacroMode.hasMacroLibrary(); + + Reference<XModel> xModel = m_xObjSh->GetModel(); + uno::Reference<document::XEventsSupplier> xSupplier(xModel, uno::UNO_QUERY); + bool bHasBoundConfigEvents(false); + if (xSupplier.is()) + { + css::uno::Reference<css::container::XNameReplace> xDocumentEvents = xSupplier->getEvents(); + + Sequence<OUString> eventNames = xDocumentEvents->getElementNames(); + sal_Int32 nEventCount = eventNames.getLength(); + for (sal_Int32 nEvent = 0; nEvent < nEventCount; ++nEvent) + { + OUString url; + try + { + Any aAny(xDocumentEvents->getByName(eventNames[nEvent])); + Sequence<beans::PropertyValue> props; + if (aAny >>= props) + { + ::comphelper::NamedValueCollection aProps(props); + url = aProps.getOrDefault("Script", url); + } + } + catch (const Exception&) + { + } + if (!url.isEmpty()) + { + bHasBoundConfigEvents = true; + break; + } + } + } + + if (bHasDocumentMacros || bHasBoundConfigEvents) + { + auto aResId = STR_CONTAINS_MACROS; + if (SvtSecurityOptions::IsMacroDisabled()) + aResId = STR_MACROS_DISABLED; + else if (pObjImpl->aMacroMode.hasUnsignedContentError()) + aResId = STR_MACROS_DISABLED_CONTENT_UNSIGNED; + auto pInfoBar = AppendInfoBar("macro", SfxResId(STR_MACROS_DISABLED_TITLE), + SfxResId(aResId), InfobarType::WARNING); + if (!pInfoBar) + return; + + // No access to macro dialog when macros are disabled globally. + if (SvtSecurityOptions::IsMacroDisabled()) + return; + + if (bHasDocumentMacros) + { + weld::Button& rMacroButton = pInfoBar->addButton(); + rMacroButton.set_label(SfxResId(STR_MACROS)); + rMacroButton.connect_clicked(LINK(this, SfxViewFrame, MacroButtonHandler)); + } + + if (bHasBoundConfigEvents) + { + weld::Button& rEventButton = pInfoBar->addButton(); + rEventButton.set_label(SfxResId(STR_EVENTS)); + rEventButton.connect_clicked(LINK(this, SfxViewFrame, EventButtonHandler)); + } + } +} + +namespace +{ +css::uno::Reference<css::frame::XLayoutManager> getLayoutManager(const SfxFrame& rFrame) +{ + css::uno::Reference<css::frame::XLayoutManager> xLayoutManager; + css::uno::Reference<css::beans::XPropertySet> xPropSet(rFrame.GetFrameInterface(), + uno::UNO_QUERY); + if (xPropSet.is()) + { + try + { + xLayoutManager.set(xPropSet->getPropertyValue("LayoutManager"), uno::UNO_QUERY); + } + catch (const Exception& e) + { + SAL_WARN("sfx.view", "Failure getting layout manager: " + e.Message); + } + } + return xLayoutManager; +} +} + +bool SfxApplication::IsHeadlessOrUITest() +{ + if (Application::IsHeadlessModeEnabled()) + return true; + + bool bIsUITest = false; //uitest.uicheck fails when the dialog is open + for (sal_uInt16 i = 0, nCount = Application::GetCommandLineParamCount(); i < nCount; ++i) + { + if (Application::GetCommandLineParam(i) == "--nologo") + { + bIsUITest = true; + break; + } + } + return bIsUITest; +} + +bool SfxApplication::IsTipOfTheDayDue() +{ + const bool bShowTipOfTheDay = officecfg::Office::Common::Misc::ShowTipOfTheDay::get(); + if (!bShowTipOfTheDay) + return false; + + const auto t0 = std::chrono::system_clock::now().time_since_epoch(); + + // show tip-of-the-day dialog ? + const sal_Int32 nLastTipOfTheDay = officecfg::Office::Common::Misc::LastTipOfTheDayShown::get(); + const sal_Int32 nDay = std::chrono::duration_cast<std::chrono::hours>(t0).count()/24; // days since 1970-01-01 + return nDay - nLastTipOfTheDay > 0; //only once per day +} + +void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + if(m_pImpl->bIsDowning) + return; + + // we know only SfxEventHint or simple SfxHint + if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint) + { + // When the Document is loaded asynchronously, was the Dispatcher + // set as ReadOnly, to what must be returned when the document itself + // is not read only, and the loading is finished. + switch (static_cast<const SfxEventHint&>(rHint).GetEventId()) + { + case SfxEventHintId::ModifyChanged: + { + SfxBindings& rBind = GetBindings(); + rBind.Invalidate( SID_DOC_MODIFIED ); + rBind.Invalidate( SID_RELOAD ); + rBind.Invalidate( SID_EDITDOC ); + break; + } + + case SfxEventHintId::OpenDoc: + case SfxEventHintId::CreateDoc: + { + if ( !m_xObjSh.is() ) + break; + + SfxBindings& rBind = GetBindings(); + rBind.Invalidate( SID_RELOAD ); + rBind.Invalidate( SID_EDITDOC ); + +#if !ENABLE_WASM_STRIP_PINGUSER + bool bIsHeadlessOrUITest = SfxApplication::IsHeadlessOrUITest(); //uitest.uicheck fails when the dialog is open + + //what's new infobar + if (utl::isProductVersionUpgraded(true) && !bIsHeadlessOrUITest) + { + VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("whatsnew", "", SfxResId(STR_WHATSNEW_TEXT), InfobarType::INFO); + if (pInfoBar) + { + weld::Button& rWhatsNewButton = pInfoBar->addButton(); + rWhatsNewButton.set_label(SfxResId(STR_WHATSNEW_BUTTON)); + rWhatsNewButton.connect_clicked(LINK(this, SfxViewFrame, WhatsNewHandler)); + } + } + + // show tip-of-the-day dialog if it due, but not if there is the impress modal template dialog + // open where SdModule::ExecuteNewDocument will launch it instead when that dialog is dismissed + if (SfxApplication::IsTipOfTheDayDue() && !bIsHeadlessOrUITest && !IsInModalMode()) + { + // tdf#127946 pass in argument for dialog parent + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface()); + GetDispatcher()->ExecuteList(SID_TIPOFTHEDAY, SfxCallMode::SLOT, {}, { &aDocFrame }); + } + + // inform about the community involvement + const auto t0 = std::chrono::system_clock::now().time_since_epoch(); + const sal_Int64 nLastGetInvolvedShown = officecfg::Setup::Product::LastTimeGetInvolvedShown::get(); + const sal_Int64 nNow = std::chrono::duration_cast<std::chrono::seconds>(t0).count(); + const sal_Int64 nPeriodSec(60 * 60 * 24 * 180); // 180 days in seconds + bool bUpdateLastTimeGetInvolvedShown = false; + + if (nLastGetInvolvedShown == 0) + bUpdateLastTimeGetInvolvedShown = true; + else if (nPeriodSec < nNow && nLastGetInvolvedShown < (nNow + nPeriodSec/2) - nPeriodSec) // 90d alternating with donation + { + bUpdateLastTimeGetInvolvedShown = true; + + VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("getinvolved", "", SfxResId(STR_GET_INVOLVED_TEXT), InfobarType::INFO); + + if (pInfoBar) + { + weld::Button& rGetInvolvedButton = pInfoBar->addButton(); + rGetInvolvedButton.set_label(SfxResId(STR_GET_INVOLVED_BUTTON)); + rGetInvolvedButton.connect_clicked(LINK(this, SfxViewFrame, GetInvolvedHandler)); + } + } + + if (bUpdateLastTimeGetInvolvedShown + && !officecfg::Setup::Product::LastTimeGetInvolvedShown::isReadOnly()) + { + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Setup::Product::LastTimeGetInvolvedShown::set(nNow, batch); + batch->commit(); + } + + // inform about donations + const sal_Int64 nLastDonateShown = officecfg::Setup::Product::LastTimeDonateShown::get(); + bool bUpdateLastTimeDonateShown = false; + + if (nLastDonateShown == 0) + bUpdateLastTimeDonateShown = true; + else if (nPeriodSec < nNow && nLastDonateShown < nNow - nPeriodSec) // 90d alternating with getinvolved + { + bUpdateLastTimeDonateShown = true; + + VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("donate", "", SfxResId(STR_DONATE_TEXT), InfobarType::INFO); + if (pInfoBar) + { + weld::Button& rDonateButton = pInfoBar->addButton(); + rDonateButton.set_label(SfxResId(STR_DONATE_BUTTON)); + rDonateButton.connect_clicked(LINK(this, SfxViewFrame, DonationHandler)); + } + } + + if (bUpdateLastTimeDonateShown + && !officecfg::Setup::Product::LastTimeDonateShown::isReadOnly()) + { + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Setup::Product::LastTimeDonateShown::set(nNow, batch); + batch->commit(); + } +#endif + if (officecfg::Office::Common::Passwords::HasMaster::get() && + officecfg::Office::Common::Passwords::StorageVersion::get() == 0) + { + // master password stored in deprecated format + VclPtr<SfxInfoBarWindow> pOldMasterPasswordInfoBar = + AppendInfoBar("oldmasterpassword", "", + SfxResId(STR_REFRESH_MASTER_PASSWORD), InfobarType::DANGER, false); + if (pOldMasterPasswordInfoBar) + { + weld::Button& rButton = pOldMasterPasswordInfoBar->addButton(); + rButton.set_label(SfxResId(STR_REFRESH_PASSWORD)); + rButton.connect_clicked(LINK(this, + SfxViewFrame, RefreshMasterPasswordHdl)); + if (Application::GetHelp()) + { + weld::Button& rHelp = pOldMasterPasswordInfoBar->addButton(); + rHelp.set_label(SfxResId(RID_STR_HELP)); + rHelp.connect_clicked(LINK(this, SfxViewFrame, HelpMasterPasswordHdl)); + } + } + } + + const bool bEmbedded = m_xObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED; + + // read-only infobar if necessary + const SfxViewShell *pVSh; + const SfxShell *pFSh; + if ( m_xObjSh->IsReadOnly() && + ! m_xObjSh->IsSecurityOptOpenReadOnly() && + ( !bEmbedded || + (( pVSh = m_xObjSh->GetViewShell()) && (pFSh = pVSh->GetFormShell()) && pFSh->IsDesignMode()))) + { + AppendReadOnlyInfobar(); + } + + if (!bEmbedded && m_xObjSh->Get_Impl()->getCurrentMacroExecMode() == css::document::MacroExecMode::NEVER_EXECUTE) + AppendContainsMacrosInfobar(); + + if (vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame().GetFrameInterface()) == "com.sun.star.text.TextDocument") + sfx2::SfxNotebookBar::ReloadNotebookBar(u"modules/swriter/ui/"); + + if (SfxClassificationHelper::IsClassified(m_xObjSh->getDocProperties())) + { + // Document has BAILS properties, display an infobar accordingly. + SfxClassificationHelper aHelper(m_xObjSh->getDocProperties()); + aHelper.UpdateInfobar(*this); + } + + // Add pending infobars + std::vector<InfobarData>& aPendingInfobars = m_xObjSh->getPendingInfobars(); + while (!aPendingInfobars.empty()) + { + InfobarData& aInfobarData = aPendingInfobars.back(); + + // don't show Track Changes infobar, if Track Changes toolbar is visible + if (aInfobarData.msId == "hiddentrackchanges") + { + if (auto xLayoutManager = getLayoutManager(GetFrame())) + { + if ( xLayoutManager->getElement(CHANGES_STR).is() ) + { + aPendingInfobars.pop_back(); + continue; + } + } + } + + // Track Changes infobar: add a button to show/hide Track Changes functions + // Hyphenation infobar: add a button to get more information + // tdf#148913 limit VclPtr usage for these + bool bTrackChanges = aInfobarData.msId == "hiddentrackchanges"; + if ( bTrackChanges || aInfobarData.msId == "hyphenationmissing" ) + { + VclPtr<SfxInfoBarWindow> pInfoBar = + AppendInfoBar(aInfobarData.msId, aInfobarData.msPrimaryMessage, + aInfobarData.msSecondaryMessage, aInfobarData.maInfobarType, + aInfobarData.mbShowCloseButton); + + // tdf#148913 don't extend this condition to keep it thread-safe + if (pInfoBar) + { + weld::Button& rButton = pInfoBar->addButton(); + rButton.set_label(SfxResId(bTrackChanges + ? STR_TRACK_CHANGES_BUTTON + : STR_HYPHENATION_BUTTON)); + if (bTrackChanges) + { + rButton.connect_clicked(LINK(this, + SfxViewFrame, HiddenTrackChangesHandler)); + } + else + { + rButton.connect_clicked(LINK(this, + SfxViewFrame, HyphenationMissingHandler)); + } + } + } + else + { + AppendInfoBar(aInfobarData.msId, aInfobarData.msPrimaryMessage, + aInfobarData.msSecondaryMessage, aInfobarData.maInfobarType, + aInfobarData.mbShowCloseButton); + } + + aPendingInfobars.pop_back(); + } + + break; + } + default: break; + } + } + else + { + switch( rHint.GetId() ) + { + case SfxHintId::ModeChanged: + { + UpdateTitle(); + + if ( !m_xObjSh.is() ) + break; + + // Switch r/o? + SfxBindings& rBind = GetBindings(); + rBind.Invalidate( SID_RELOAD ); + SfxDispatcher *pDispat = GetDispatcher(); + bool bWasReadOnly = pDispat->GetReadOnly_Impl(); + bool bIsReadOnly = m_xObjSh->IsReadOnly(); + if ( bWasReadOnly != bIsReadOnly ) + { + // Then also TITLE_CHANGED + UpdateTitle(); + rBind.Invalidate( SID_FILE_NAME ); + rBind.Invalidate( SID_DOCINFO_TITLE ); + rBind.Invalidate( SID_EDITDOC ); + + pDispat->GetBindings()->InvalidateAll(true); + pDispat->SetReadOnly_Impl( bIsReadOnly ); + + // Only force and Dispatcher-Update, if it is done next + // anyway, otherwise flickering or GPF is possible since + // the Writer for example prefers in Resize perform some + // actions which has a SetReadOnlyUI in Dispatcher as a + // result! + + if ( pDispat->IsUpdated_Impl() ) + pDispat->Update_Impl(true); + } + + Enable( !m_xObjSh->IsInModalMode() ); + break; + } + + case SfxHintId::TitleChanged: + { + UpdateTitle(); + SfxBindings& rBind = GetBindings(); + rBind.Invalidate( SID_FILE_NAME ); + rBind.Invalidate( SID_DOCINFO_TITLE ); + rBind.Invalidate( SID_EDITDOC ); + rBind.Invalidate( SID_RELOAD ); + break; + } + + case SfxHintId::DocumentRepair: + { + GetBindings().Invalidate( SID_DOC_REPAIR ); + break; + } + + case SfxHintId::Deinitializing: + { + vcl::Window* pFrameWin = GetWindow().GetFrameWindow(); + if (pFrameWin && pFrameWin->GetLOKNotifier()) + pFrameWin->ReleaseLOKNotifier(); + + GetFrame().DoClose(); + break; + } + case SfxHintId::Dying: + // when the Object is being deleted, destroy the view too + if ( m_xObjSh.is() ) + ReleaseObjectShell_Impl(); + else + GetFrame().DoClose(); + break; + default: break; + } + } +} + +#if !ENABLE_WASM_STRIP_PINGUSER +IMPL_LINK_NOARG(SfxViewFrame, WhatsNewHandler, weld::Button&, void) +{ + GetDispatcher()->Execute(SID_WHATSNEW); +} + +IMPL_LINK_NOARG(SfxViewFrame, GetInvolvedHandler, weld::Button&, void) +{ + GetDispatcher()->Execute(SID_GETINVOLVED); +} + +IMPL_LINK_NOARG(SfxViewFrame, DonationHandler, weld::Button&, void) +{ + GetDispatcher()->Execute(SID_DONATION); +} +#endif + +IMPL_LINK(SfxViewFrame, SwitchReadOnlyHandler, weld::Button&, rButton, void) +{ + if (m_xObjSh.is() && m_xObjSh->IsSignPDF()) + { + SfxEditDocumentDialog aDialog(&rButton); + if (aDialog.run() != RET_OK) + return; + } + GetDispatcher()->Execute(SID_EDITDOC); +} + +IMPL_LINK_NOARG(SfxViewFrame, SignDocumentHandler, weld::Button&, void) +{ + GetDispatcher()->Execute(SID_SIGNATURE); +} + +IMPL_LINK(SfxViewFrame, HiddenTrackChangesHandler, weld::Button&, rButton, void) +{ + // enable Track Changes toolbar, if it is disabled. + // Otherwise disable the toolbar, and close the infobar + auto xLayoutManager = getLayoutManager(GetFrame()); + if (!xLayoutManager) + return; + + if (!xLayoutManager->getElement(CHANGES_STR).is()) + { + xLayoutManager->createElement(CHANGES_STR); + xLayoutManager->showElement(CHANGES_STR); + rButton.set_label(SfxResId(STR_TRACK_CHANGES_BUTTON_HIDE)); + } + else + { + xLayoutManager->hideElement(CHANGES_STR); + xLayoutManager->destroyElement(CHANGES_STR); + RemoveInfoBar(u"hiddentrackchanges"); + } +} + +IMPL_LINK_NOARG(SfxViewFrame, HyphenationMissingHandler, weld::Button&, void) +{ + GetDispatcher()->Execute(SID_HYPHENATIONMISSING); + RemoveInfoBar(u"hyphenationmissing"); +} + +IMPL_LINK_NOARG(SfxViewFrame, MacroButtonHandler, weld::Button&, void) +{ + // start with tab 0 displayed + SfxUInt16Item aTabItem(SID_MACROORGANIZER, 0); + SfxBoolItem aCurrentDocItem(FN_PARAM_2, true); + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface()); + GetDispatcher()->ExecuteList(SID_MACROORGANIZER, SfxCallMode::ASYNCHRON, + { &aTabItem, &aCurrentDocItem }, { &aDocFrame }); +} + +IMPL_LINK_NOARG(SfxViewFrame, SecurityButtonHandler, weld::Button&, void) +{ + SfxUInt16Item aPageID(SID_OPTIONS_PAGEID, sal_uInt16(RID_SVXPAGE_INET_SECURITY)); + GetDispatcher()->ExecuteList(SID_OPTIONS_TREEDIALOG, SfxCallMode::SYNCHRON, { &aPageID }); + RemoveInfoBar(u"securitywarn"); +} + +IMPL_LINK_NOARG(SfxViewFrame, EventButtonHandler, weld::Button&, void) +{ + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, GetFrame().GetFrameInterface()); + GetDispatcher()->ExecuteList(SID_CONFIGEVENT, SfxCallMode::ASYNCHRON, + {}, { &aDocFrame }); +} + +IMPL_LINK_NOARG(SfxViewFrame, RefreshMasterPasswordHdl, weld::Button&, void) +{ + bool bChanged = false; + try + { + Reference< task::XPasswordContainer2 > xMasterPasswd( + task::PasswordContainer::create(comphelper::getProcessComponentContext())); + + css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface(); + css::uno::Reference<css::awt::XWindow> xContainerWindow = xFrame->getContainerWindow(); + + uno::Reference<task::XInteractionHandler> xTmpHandler(task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), + xContainerWindow)); + bChanged = xMasterPasswd->changeMasterPassword(xTmpHandler); + } + catch (const Exception&) + {} + if (bChanged) + RemoveInfoBar(u"oldmasterpassword"); +} + +IMPL_STATIC_LINK_NOARG(SfxViewFrame, HelpMasterPasswordHdl, weld::Button&, void) +{ + if (Help* pHelp = Application::GetHelp()) + pHelp->Start("cui/ui/optsecuritypage/savepassword"); +} + +void SfxViewFrame::Construct_Impl( SfxObjectShell *pObjSh ) +{ + m_pImpl->bResizeInToOut = true; + m_pImpl->bObjLocked = false; + m_pImpl->nCurViewId = SFX_INTERFACE_NONE; + m_pImpl->bReloading = false; + m_pImpl->bIsDowning = false; + m_pImpl->bModal = false; + m_pImpl->bEnabled = true; + m_pImpl->nDocViewNo = 0; + m_pImpl->aMargin = Size( -1, -1 ); + m_pImpl->pWindow = nullptr; + + SetPool( &SfxGetpApp()->GetPool() ); + m_pDispatcher.reset( new SfxDispatcher(this) ); + if ( !GetBindings().GetDispatcher() ) + GetBindings().SetDispatcher( m_pDispatcher.get() ); + + m_xObjSh = pObjSh; + if ( m_xObjSh.is() && m_xObjSh->IsPreview() ) + GetDispatcher()->SetQuietMode_Impl( true ); + + if ( pObjSh ) + { + m_pDispatcher->Push( *SfxGetpApp() ); + SfxModule* pModule = m_xObjSh->GetModule(); + if( pModule ) + m_pDispatcher->Push( *pModule ); + m_pDispatcher->Push( *this ); + m_pDispatcher->Push( *pObjSh ); + m_pDispatcher->Flush(); + StartListening( *pObjSh ); + Notify( *pObjSh, SfxHint(SfxHintId::TitleChanged) ); + Notify( *pObjSh, SfxHint(SfxHintId::DocChanged) ); + m_pDispatcher->SetReadOnly_Impl( pObjSh->IsReadOnly() ); + } + else + { + m_pDispatcher->Push( *SfxGetpApp() ); + m_pDispatcher->Push( *this ); + m_pDispatcher->Flush(); + } + + SfxGetpApp()->GetViewFrames_Impl().push_back(this); +} + +/* [Description] + + Constructor of SfxViewFrame for a <SfxObjectShell> from the Resource. + The 'nViewId' to the created <SfxViewShell> can be returned. + (default is the SfxViewShell-Subclass that was registered first). +*/ +SfxViewFrame::SfxViewFrame +( + SfxFrame& rFrame, + SfxObjectShell* pObjShell +) + : m_pImpl( new SfxViewFrame_Impl( rFrame ) ) + , m_pBindings( new SfxBindings ) + , m_pHelpData(CreateSVHelpData()) + , m_pWinData(CreateSVWinData()) + , m_nAdjustPosPixelLock( 0 ) + , m_pCommandPopupHandler(new CommandPopupHandler) +{ + + rFrame.SetCurrentViewFrame_Impl( this ); + rFrame.SetHasTitle( true ); + Construct_Impl( pObjShell ); + + m_pImpl->pWindow = VclPtr<SfxFrameViewWindow_Impl>::Create( this, rFrame.GetWindow() ); + m_pImpl->pWindow->SetSizePixel( rFrame.GetWindow().GetOutputSizePixel() ); + rFrame.SetOwnsBindings_Impl( true ); + rFrame.CreateWorkWindow_Impl(); +} + +SfxViewFrame::~SfxViewFrame() +{ + m_pImpl->bIsDowning = true; + + if ( SfxViewFrame::Current() == this ) + SfxViewFrame::SetViewFrame( nullptr ); + + ReleaseObjectShell_Impl(); + + if ( GetFrame().OwnsBindings_Impl() ) + // The Bindings delete the Frame! + KillDispatcher_Impl(); + + m_pImpl->pWindow.disposeAndClear(); + + if ( GetFrame().GetCurrentViewFrame() == this ) + GetFrame().SetCurrentViewFrame_Impl( nullptr ); + + // Unregister from the Frame List. + SfxApplication *pSfxApp = SfxApplication::Get(); + if (pSfxApp) + { + auto &rFrames = pSfxApp->GetViewFrames_Impl(); + auto it = std::find( rFrames.begin(), rFrames.end(), this ); + rFrames.erase( it ); + } + + // Delete Member + KillDispatcher_Impl(); + + DestroySVHelpData(m_pHelpData); + m_pHelpData = nullptr; + + DestroySVWinData(m_pWinData); + m_pWinData = nullptr; +} + +// Remove and delete the Dispatcher. +void SfxViewFrame::KillDispatcher_Impl() +{ + + SfxModule* pModule = m_xObjSh.is() ? m_xObjSh->GetModule() : nullptr; + if ( m_xObjSh.is() ) + ReleaseObjectShell_Impl(); + if ( m_pDispatcher ) + { + if( pModule ) + m_pDispatcher->Pop( *pModule, SfxDispatcherPopFlags::POP_UNTIL ); + else + m_pDispatcher->Pop( *this ); + m_pDispatcher.reset(); + } +} + +SfxViewFrame* SfxViewFrame::Current() +{ + SfxApplication* pApp = SfxApplication::Get(); + return pApp ? pApp->Get_Impl()->pViewFrame : nullptr; +} + +// returns the first window of spec. type viewing the specified doc. +SfxViewFrame* SfxViewFrame::GetFirst +( + const SfxObjectShell* pDoc, + bool bOnlyIfVisible +) +{ + SfxApplication *pSfxApp = SfxApplication::Get(); + if (!pSfxApp) + return nullptr; + + // search for a SfxDocument of the specified type + for (SfxViewFrame* pFrame : pSfxApp->GetViewFrames_Impl()) + { + if ( ( !pDoc || pDoc == pFrame->GetObjectShell() ) + && ( !bOnlyIfVisible || pFrame->IsVisible() ) + ) + return pFrame; + } + + return nullptr; +} + +// returns the next window of spec. type viewing the specified doc. +SfxViewFrame* SfxViewFrame::GetNext +( + const SfxViewFrame& rPrev, + const SfxObjectShell* pDoc, + bool bOnlyIfVisible +) +{ + SfxApplication *pSfxApp = SfxApplication::Get(); + if (!pSfxApp) + return nullptr; + + auto &rFrames = pSfxApp->GetViewFrames_Impl(); + + // refind the specified predecessor + size_t nPos; + for ( nPos = 0; nPos < rFrames.size(); ++nPos ) + if ( rFrames[nPos] == &rPrev ) + break; + + // search for a Frame of the specified type + for ( ++nPos; nPos < rFrames.size(); ++nPos ) + { + SfxViewFrame *pFrame = rFrames[nPos]; + if ( ( !pDoc || pDoc == pFrame->GetObjectShell() ) + && ( !bOnlyIfVisible || pFrame->IsVisible() ) + ) + return pFrame; + } + return nullptr; +} + +SfxProgress* SfxViewFrame::GetProgress() const +{ + SfxObjectShell *pObjSh = m_xObjSh.get(); + return pObjSh ? pObjSh->GetProgress() : nullptr; +} + +void SfxViewFrame::DoAdjustPosSizePixel //! divide on Inner.../Outer... +( + SfxViewShell* pSh, + const Point& rPos, + const Size& rSize, + bool inplaceEditModeChange +) +{ + + // Components do not use this Method! + if( pSh && pSh->GetWindow() && !m_nAdjustPosPixelLock ) + { + m_nAdjustPosPixelLock++; + if ( m_pImpl->bResizeInToOut ) + pSh->InnerResizePixel( rPos, rSize, inplaceEditModeChange ); + else + pSh->OuterResizePixel( rPos, rSize ); + m_nAdjustPosPixelLock--; + } +} + +bool SfxViewFrameItem::operator==( const SfxPoolItem &rItem ) const +{ + return SfxPoolItem::operator==(rItem) && + static_cast<const SfxViewFrameItem&>(rItem).pFrame == pFrame; +} + +SfxViewFrameItem* SfxViewFrameItem::Clone( SfxItemPool *) const +{ + return new SfxViewFrameItem( *this ); +} + +void SfxViewFrame::SetViewShell_Impl( SfxViewShell *pVSh ) +/* [Description] + + Internal Method to set the current <SfxViewShell> Instance, + that is active int this SfxViewFrame at the moment. +*/ +{ + SfxShell::SetViewShell_Impl( pVSh ); + + // Hack: InPlaceMode + if ( pVSh ) + m_pImpl->bResizeInToOut = false; +} + +void SfxViewFrame::ForceOuterResize_Impl() +{ + m_pImpl->bResizeInToOut = true; +} + +void SfxViewFrame::GetDocNumber_Impl() +{ + DBG_ASSERT( GetObjectShell(), "No Document!" ); + GetObjectShell()->SetNamedVisibility_Impl(); + m_pImpl->nDocViewNo = GetObjectShell()->GetNoSet_Impl().GetFreeIndex()+1; +} + +void SfxViewFrame::Enable( bool bEnable ) +{ + if ( bEnable == m_pImpl->bEnabled ) + return; + + m_pImpl->bEnabled = bEnable; + + vcl::Window *pWindow = &GetFrame().GetWindow(); + if ( !bEnable ) + m_pImpl->bWindowWasEnabled = pWindow->IsInputEnabled(); + if ( !bEnable || m_pImpl->bWindowWasEnabled ) + pWindow->EnableInput( bEnable ); + + // cursor and focus + SfxViewShell* pViewSh = GetViewShell(); + if ( bEnable ) + { + // show cursor + if ( pViewSh ) + pViewSh->ShowCursor(); + } + else + { + // hide cursor + if ( pViewSh ) + pViewSh->ShowCursor(false); + } +} + +/* [Description] + + This method makes the Frame-Window visible and before transmits the + window name. In addition, the document is held. In general one can never + show the window directly! +*/ +void SfxViewFrame::Show() +{ + // First lock the objectShell so that UpdateTitle() is valid: + // IsVisible() == true (:#) + if ( m_xObjSh.is() ) + { + m_xObjSh->GetMedium()->GetItemSet().ClearItem( SID_HIDDEN ); + if ( !m_pImpl->bObjLocked ) + LockObjectShell_Impl(); + + // Adjust Doc-Shell title number, get unique view-no + if ( 0 == m_pImpl->nDocViewNo ) + { + GetDocNumber_Impl(); + UpdateTitle(); + } + } + else + UpdateTitle(); + + // Display Frame-window, but only if the ViewFrame has no window of its + // own or if it does not contain a Component + GetWindow().Show(); + GetFrame().GetWindow().Show(); +} + + +bool SfxViewFrame::IsVisible() const +{ + return m_pImpl->bObjLocked; +} + + +void SfxViewFrame::LockObjectShell_Impl() +{ + DBG_ASSERT( !m_pImpl->bObjLocked, "Wrong Locked status!" ); + + DBG_ASSERT( GetObjectShell(), "No Document!" ); + GetObjectShell()->OwnerLock(true); + m_pImpl->bObjLocked = true; +} + + +void SfxViewFrame::MakeActive_Impl( bool bGrabFocus ) +{ + if ( !GetViewShell() || GetFrame().IsClosing_Impl() ) + return; + + if ( !IsVisible() ) + return; + + bool bPreview = false; + if (GetObjectShell()->IsPreview()) + { + bPreview = true; + } + + css::uno::Reference<css::frame::XFrame> xFrame = GetFrame().GetFrameInterface(); + if (!bPreview) + { + SetViewFrame(this); + GetBindings().SetActiveFrame(css::uno::Reference<css::frame::XFrame>()); + uno::Reference<frame::XFramesSupplier> xSupp(xFrame, uno::UNO_QUERY); + if (xSupp.is()) + xSupp->setActiveFrame(uno::Reference<frame::XFrame>()); + + css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow(); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xContainerWindow); + if (pWindow && pWindow->HasChildPathFocus() && bGrabFocus) + { + SfxInPlaceClient *pCli = GetViewShell()->GetUIActiveClient(); + if (!pCli || !pCli->IsObjectUIActive()) + GetFrame().GrabFocusOnComponent_Impl(); + } + } + else + { + GetBindings().SetDispatcher(GetDispatcher()); + GetBindings().SetActiveFrame(css::uno::Reference<css::frame::XFrame>()); + GetDispatcher()->Update_Impl(); + } +} + +SfxObjectShell* SfxViewFrame::GetObjectShell() +{ + return m_xObjSh.get(); +} + +const Size& SfxViewFrame::GetMargin_Impl() const +{ + return m_pImpl->aMargin; +} + +SfxViewFrame* SfxViewFrame::LoadViewIntoFrame_Impl_NoThrow( const SfxObjectShell& i_rDoc, const Reference< XFrame >& i_rFrame, + const SfxInterfaceId i_nViewId, const bool i_bHidden ) +{ + Reference< XFrame > xFrame( i_rFrame ); + bool bOwnFrame = false; + SfxViewShell* pSuccessView = nullptr; + try + { + if ( !xFrame.is() ) + { + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + + if ( !i_bHidden ) + { + try + { + // if there is a backing component, use it + ::framework::FrameListAnalyzer aAnalyzer( xDesktop, Reference< XFrame >(), FrameAnalyzerFlags::BackingComponent ); + + if ( aAnalyzer.m_xBackingComponent.is() ) + xFrame = aAnalyzer.m_xBackingComponent; + } + catch( uno::Exception& ) + {} + } + + if ( !xFrame.is() ) + xFrame.set( xDesktop->findFrame( "_blank", 0 ), UNO_SET_THROW ); + + bOwnFrame = true; + } + + pSuccessView = LoadViewIntoFrame_Impl( + i_rDoc, + xFrame, + Sequence< PropertyValue >(), // means "reuse existing model's args" + i_nViewId, + i_bHidden + ); + + if ( bOwnFrame && !i_bHidden ) + { + // ensure the frame/window is visible + Reference< XWindow > xContainerWindow( xFrame->getContainerWindow(), UNO_SET_THROW ); + xContainerWindow->setVisible( true ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.view"); + } + + if ( pSuccessView ) + return &pSuccessView->GetViewFrame(); + + if ( bOwnFrame ) + { + try + { + xFrame->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.view"); + } + } + + return nullptr; +} + +SfxViewShell* SfxViewFrame::LoadViewIntoFrame_Impl( const SfxObjectShell& i_rDoc, const Reference< XFrame >& i_rFrame, + const Sequence< PropertyValue >& i_rLoadArgs, const SfxInterfaceId i_nViewId, + const bool i_bHidden ) +{ + Reference< XModel > xDocument( i_rDoc.GetModel(), UNO_SET_THROW ); + + ::comphelper::NamedValueCollection aTransformLoadArgs( i_rLoadArgs.hasElements() ? i_rLoadArgs : xDocument->getArgs() ); + aTransformLoadArgs.put( "Model", xDocument ); + if ( i_nViewId ) + aTransformLoadArgs.put( "ViewId", sal_uInt16( i_nViewId ) ); + if ( i_bHidden ) + aTransformLoadArgs.put( "Hidden", i_bHidden ); + else + aTransformLoadArgs.remove( "Hidden" ); + + Reference< XComponentLoader > xLoader( i_rFrame, UNO_QUERY_THROW ); + xLoader->loadComponentFromURL( "private:object", "_self", 0, + aTransformLoadArgs.getPropertyValues() ); + + SfxViewShell* pViewShell = SfxViewShell::Get( i_rFrame->getController() ); + ENSURE_OR_THROW( pViewShell, + "SfxViewFrame::LoadViewIntoFrame_Impl: loading an SFX doc into a frame resulted in a non-SFX view - quite impossible" ); + return pViewShell; +} + +SfxViewFrame* SfxViewFrame::LoadHiddenDocument( SfxObjectShell const & i_rDoc, SfxInterfaceId i_nViewId ) +{ + return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, Reference< XFrame >(), i_nViewId, true ); +} + +SfxViewFrame* SfxViewFrame::LoadDocument( SfxObjectShell const & i_rDoc, SfxInterfaceId i_nViewId ) +{ + return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, Reference< XFrame >(), i_nViewId, false ); +} + +SfxViewFrame* SfxViewFrame::LoadDocumentIntoFrame( SfxObjectShell const & i_rDoc, const Reference< XFrame >& i_rTargetFrame ) +{ + return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, i_rTargetFrame, SFX_INTERFACE_NONE, false ); +} + +SfxViewFrame* SfxViewFrame::LoadDocumentIntoFrame( SfxObjectShell const & i_rDoc, const SfxFrameItem* i_pFrameItem, SfxInterfaceId i_nViewId ) +{ + return LoadViewIntoFrame_Impl_NoThrow( i_rDoc, i_pFrameItem && i_pFrameItem->GetFrame() ? i_pFrameItem->GetFrame()->GetFrameInterface() : nullptr, i_nViewId, false ); +} + +SfxViewFrame* SfxViewFrame::DisplayNewDocument( SfxObjectShell const & i_rDoc, const SfxRequest& i_rCreateDocRequest ) +{ + const SfxUnoFrameItem* pFrameItem = i_rCreateDocRequest.GetArg<SfxUnoFrameItem>(SID_FILLFRAME); + const SfxBoolItem* pHiddenItem = i_rCreateDocRequest.GetArg<SfxBoolItem>(SID_HIDDEN); + + return LoadViewIntoFrame_Impl_NoThrow( + i_rDoc, + pFrameItem ? pFrameItem->GetFrame() : nullptr, + SFX_INTERFACE_NONE, + pHiddenItem && pHiddenItem->GetValue() + ); +} + +SfxViewFrame* SfxViewFrame::Get( const Reference< XController>& i_rController, const SfxObjectShell* i_pDoc ) +{ + if ( !i_rController.is() ) + return nullptr; + + const SfxObjectShell* pDoc = i_pDoc; + if ( !pDoc ) + { + Reference< XModel > xDocument( i_rController->getModel() ); + for ( pDoc = SfxObjectShell::GetFirst( nullptr, false ); + pDoc; + pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false ) + ) + { + if ( pDoc->GetModel() == xDocument ) + break; + } + } + + SfxViewFrame* pViewFrame = nullptr; + for ( pViewFrame = SfxViewFrame::GetFirst( pDoc, false ); + pViewFrame; + pViewFrame = SfxViewFrame::GetNext( *pViewFrame, pDoc, false ) + ) + { + if ( pViewFrame->GetViewShell()->GetController() == i_rController ) + break; + } + + return pViewFrame; +} + +void SfxViewFrame::SaveCurrentViewData_Impl( const SfxInterfaceId i_nNewViewId ) +{ + SfxViewShell* pCurrentShell = GetViewShell(); + ENSURE_OR_RETURN_VOID( pCurrentShell != nullptr, "SfxViewFrame::SaveCurrentViewData_Impl: no current view shell -> no current view data!" ); + + // determine the logical (API) view name + const SfxObjectFactory& rDocFactory( pCurrentShell->GetObjectShell()->GetFactory() ); + const sal_uInt16 nCurViewNo = rDocFactory.GetViewNo_Impl( GetCurViewId(), 0 ); + const OUString sCurrentViewName = rDocFactory.GetViewFactory( nCurViewNo ).GetAPIViewName(); + const sal_uInt16 nNewViewNo = rDocFactory.GetViewNo_Impl( i_nNewViewId, 0 ); + const OUString sNewViewName = rDocFactory.GetViewFactory( nNewViewNo ).GetAPIViewName(); + if ( sCurrentViewName.isEmpty() || sNewViewName.isEmpty() ) + { + // can't say anything about the view, the respective application did not yet migrate its code to + // named view factories => bail out + OSL_FAIL( "SfxViewFrame::SaveCurrentViewData_Impl: views without API names? Shouldn't happen anymore?" ); + return; + } + SAL_WARN_IF(sNewViewName == sCurrentViewName, "sfx.view", "SfxViewFrame::SaveCurrentViewData_Impl: suspicious: new and old view name are identical!"); + + // save the view data only when we're moving from a non-print-preview to the print-preview view + if ( sNewViewName != "PrintPreview" ) + return; + + // retrieve the view data from the view + Sequence< PropertyValue > aViewData; + pCurrentShell->WriteUserDataSequence( aViewData ); + + try + { + // retrieve view data (for *all* views) from the model + const Reference< XController > xController( pCurrentShell->GetController(), UNO_SET_THROW ); + const Reference< XViewDataSupplier > xViewDataSupplier( xController->getModel(), UNO_QUERY_THROW ); + const Reference< XIndexContainer > xViewData( xViewDataSupplier->getViewData(), UNO_QUERY_THROW ); + + // look up the one view data item which corresponds to our current view, and remove it + const sal_Int32 nCount = xViewData->getCount(); + for ( sal_Int32 i=0; i<nCount; ++i ) + { + const ::comphelper::NamedValueCollection aCurViewData( xViewData->getByIndex(i) ); + const OUString sViewId( aCurViewData.getOrDefault( "ViewId", OUString() ) ); + if ( sViewId.isEmpty() ) + continue; + + const SfxViewFactory* pViewFactory = rDocFactory.GetViewFactoryByViewName( sViewId ); + if ( pViewFactory == nullptr ) + continue; + + if ( pViewFactory->GetOrdinal() == GetCurViewId() ) + { + xViewData->removeByIndex(i); + break; + } + } + + // then replace it with the most recent view data we just obtained + xViewData->insertByIndex( 0, Any( aViewData ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.view"); + } +} + +/* [Description] + + Internal Method for switching to another <SfxViewShell> subclass, + which should be created in this SfxMDIFrame. If no SfxViewShell exist + in this SfxMDIFrame, then one will first be created. + + + [Return Value] + + bool true + requested SfxViewShell was created and a + possibly existing one deleted + + false + SfxViewShell requested could not be created, + the existing SfxViewShell thus continue to exist +*/ +bool SfxViewFrame::SwitchToViewShell_Impl +( + sal_uInt16 nViewIdOrNo, /* > 0 + Registration-Id of the View, to which the + method should switch, for example the one + that will be created. + + == 0 + First use the Default view. */ + + bool bIsIndex /* true + 'nViewIdOrNo' is no Registration-Id instead + an Index of <SfxViewFrame> in <SfxObjectShell>. + */ +) +{ + try + { + ENSURE_OR_THROW( GetObjectShell() != nullptr, "not possible without a document" ); + + // if we already have a view shell, remove it + SfxViewShell* pOldSh = GetViewShell(); + OSL_PRECOND( pOldSh, "SfxViewFrame::SwitchToViewShell_Impl: that's called *switch* (not for *initial-load*) for a reason" ); + if ( pOldSh ) + { + // ask whether it can be closed + if ( !pOldSh->PrepareClose() ) + return false; + + // remove sub shells from Dispatcher before switching to new ViewShell + PopShellAndSubShells_Impl( *pOldSh ); + } + + GetBindings().ENTERREGISTRATIONS(); + LockAdjustPosSizePixel(); + + // ID of the new view + SfxObjectFactory& rDocFact = GetObjectShell()->GetFactory(); + const SfxInterfaceId nViewId = ( bIsIndex || !nViewIdOrNo ) ? rDocFact.GetViewFactory( nViewIdOrNo ).GetOrdinal() : SfxInterfaceId(nViewIdOrNo); + + // save the view data of the old view, so it can be restored later on (when needed) + SaveCurrentViewData_Impl( nViewId ); + + if (pOldSh) + pOldSh->SetDying(); + + // create and load new ViewShell + SfxViewShell* pNewSh = LoadViewIntoFrame_Impl( + *GetObjectShell(), + GetFrame().GetFrameInterface(), + Sequence< PropertyValue >(), // means "reuse existing model's args" + nViewId, + false + ); + + // allow resize events to be processed + UnlockAdjustPosSizePixel(); + + if ( GetWindow().IsReallyVisible() ) + DoAdjustPosSizePixel( pNewSh, Point(), GetWindow().GetOutputSizePixel(), false ); + + GetBindings().LEAVEREGISTRATIONS(); + delete pOldSh; + } + catch ( const css::uno::Exception& ) + { + // the SfxCode is not able to cope with exceptions thrown while creating views + // the code will crash in the stack unwinding procedure, so we shouldn't let exceptions go through here + DBG_UNHANDLED_EXCEPTION("sfx.view"); + return false; + } + + DBG_ASSERT( SfxGetpApp()->GetViewFrames_Impl().size() == SfxGetpApp()->GetViewShells_Impl().size(), "Inconsistent view arrays!" ); + return true; +} + +void SfxViewFrame::SetCurViewId_Impl( const SfxInterfaceId i_nID ) +{ + m_pImpl->nCurViewId = i_nID; +} + +SfxInterfaceId SfxViewFrame::GetCurViewId() const +{ + return m_pImpl->nCurViewId; +} + +/* [Description] + + Internal method to run the slot for the <SfxShell> Subclass in the + SfxViewFrame <SVIDL> described slots. +*/ +void SfxViewFrame::ExecView_Impl +( + SfxRequest& rReq // The executable <SfxRequest> +) +{ + + // If the Shells are just being replaced... + if ( !GetObjectShell() || !GetViewShell() ) + return; + + switch ( rReq.GetSlot() ) + { + case SID_TERMINATE_INPLACEACTIVATION : + { + SfxInPlaceClient* pClient = GetViewShell()->GetUIActiveClient(); + if ( pClient ) + pClient->DeactivateObject(); + break; + } + + case SID_VIEWSHELL: + { + const SfxUInt16Item *pItem = nullptr; + if ( rReq.GetArgs() + && (pItem = rReq.GetArgs()->GetItemIfSet( SID_VIEWSHELL, false )) + ) + { + const sal_uInt16 nViewId = pItem->GetValue(); + bool bSuccess = SwitchToViewShell_Impl( nViewId ); + rReq.SetReturnValue( SfxBoolItem( 0, bSuccess ) ); + } + break; + } + + case SID_VIEWSHELL0: + case SID_VIEWSHELL1: + case SID_VIEWSHELL2: + case SID_VIEWSHELL3: + case SID_VIEWSHELL4: + { + const sal_uInt16 nViewNo = rReq.GetSlot() - SID_VIEWSHELL0; + bool bSuccess = SwitchToViewShell_Impl( nViewNo, true ); + rReq.SetReturnValue( SfxBoolItem( 0, bSuccess ) ); + break; + } + + case SID_NEWWINDOW: + { + // Hack. at the moment a virtual Function + if ( !GetViewShell()->NewWindowAllowed() ) + { + OSL_FAIL( "You should have disabled the 'Window/New Window' slot!" ); + return; + } + + // Get ViewData of FrameSets recursively. + GetFrame().GetViewData_Impl(); + SfxMedium* pMed = GetObjectShell()->GetMedium(); + + // do not open the new window hidden + pMed->GetItemSet().ClearItem( SID_HIDDEN ); + + // the view ID (optional arg. TODO: this is currently not supported in the slot definition ...) + const SfxUInt16Item* pViewIdItem = rReq.GetArg<SfxUInt16Item>(SID_VIEW_ID); + const SfxInterfaceId nViewId = pViewIdItem ? SfxInterfaceId(pViewIdItem->GetValue()) : GetCurViewId(); + + Reference < XFrame > xFrame; + // the frame (optional arg. TODO: this is currently not supported in the slot definition ...) + const SfxUnoFrameItem* pFrameItem = rReq.GetArg<SfxUnoFrameItem>(SID_FILLFRAME); + if ( pFrameItem ) + xFrame = pFrameItem->GetFrame(); + + LoadViewIntoFrame_Impl_NoThrow( *GetObjectShell(), xFrame, nViewId, false ); + + rReq.Done(); + break; + } + + case SID_OBJECT: + { + const SfxInt16Item* pItem = rReq.GetArg<SfxInt16Item>(SID_OBJECT); + + if (pItem) + { + GetViewShell()->DoVerb( pItem->GetValue() ); + rReq.Done(); + break; + } + } + } +} + +/* TODO as96863: + This method try to collect information about the count of currently open documents. + But the algorithm is implemented very simple ... + E.g. hidden documents should be ignored here ... but they are counted. + TODO: export special helper "framework::FrameListAnalyzer" within the framework module + and use it here. +*/ +static bool impl_maxOpenDocCountReached() +{ + css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + std::optional<sal_Int32> x(officecfg::Office::Common::Misc::MaxOpenDocuments::get()); + // NIL means: count of allowed documents = infinite ! + if (!x) + return false; + sal_Int32 nMaxDocs(*x); + sal_Int32 nOpenDocs = 0; + + css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(xContext); + css::uno::Reference< css::container::XIndexAccess > xCont(xDesktop->getFrames(), css::uno::UNO_QUERY_THROW); + + sal_Int32 c = xCont->getCount(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + try + { + css::uno::Reference< css::frame::XFrame > xFrame; + xCont->getByIndex(i) >>= xFrame; + if ( ! xFrame.is()) + continue; + + // a) do not count the help window + if ( xFrame->getName() == "OFFICE_HELP_TASK" ) + continue; + + // b) count all other frames + ++nOpenDocs; + } + catch(const css::uno::Exception&) + // An IndexOutOfBoundsException can happen in multithreaded + // environments, where any other thread can change this + // container ! + { continue; } + } + + return (nOpenDocs >= nMaxDocs); +} + +/* [Description] + + This internal method returns in 'rSet' the Status for the <SfxShell> + Subclass SfxViewFrame in the <SVIDL> described <Slots>. + + Thus exactly those Slots-IDs that are recognized as being invalid by Sfx + are included as Which-ranges in 'rSet'. If there exists a mapping for + single slot-IDs of the <SfxItemPool> set in the shell, then the respective + Which-IDs are used so that items can be replaced directly with a working + Core::sun::com::star::script::Engine of the Which-IDs if possible. . +*/ +void SfxViewFrame::StateView_Impl +( + SfxItemSet& rSet /* empty <SfxItemSet> with <Which-Ranges>, + which describes the Slot Ids */ +) +{ + + SfxObjectShell *pDocSh = GetObjectShell(); + + if ( !pDocSh ) + // I'm just on reload and am yielding myself ... + return; + + const WhichRangesContainer & pRanges = rSet.GetRanges(); + assert(!pRanges.empty() && "Set with no Range"); + for ( auto const & pRange : pRanges ) + { + sal_uInt16 nStartWhich = pRange.first; + sal_uInt16 nEndWhich = pRange.second; + for ( sal_uInt16 nWhich = nStartWhich; nWhich <= nEndWhich; ++nWhich ) + { + switch(nWhich) + { + case SID_VIEWSHELL: + { + rSet.Put( SfxUInt16Item( nWhich, sal_uInt16(m_pImpl->nCurViewId )) ); + break; + } + + case SID_VIEWSHELL0: + case SID_VIEWSHELL1: + case SID_VIEWSHELL2: + case SID_VIEWSHELL3: + case SID_VIEWSHELL4: + { + sal_uInt16 nViewNo = nWhich - SID_VIEWSHELL0; + if ( GetObjectShell()->GetFactory().GetViewFactoryCount() > + nViewNo && !GetObjectShell()->IsInPlaceActive() ) + { + SfxViewFactory &rViewFactory = + GetObjectShell()->GetFactory().GetViewFactory(nViewNo); + rSet.Put( SfxBoolItem( + nWhich, m_pImpl->nCurViewId == rViewFactory.GetOrdinal() ) ); + } + else + rSet.DisableItem( nWhich ); + break; + } + + case SID_NEWWINDOW: + { + if ( !GetViewShell()->NewWindowAllowed() + || impl_maxOpenDocCountReached() + ) + rSet.DisableItem( nWhich ); + break; + } + } + } + } +} + + +void SfxViewFrame::ToTop() +{ + GetFrame().Appear(); +} + + +/* [Description] + + GetFrame returns the Frame, in which the ViewFrame is located. +*/ +SfxFrame& SfxViewFrame::GetFrame() const +{ + return m_pImpl->rFrame; +} + +SfxViewFrame* SfxViewFrame::GetTopViewFrame() const +{ + return GetFrame().GetCurrentViewFrame(); +} + +vcl::Window& SfxViewFrame::GetWindow() const +{ + return m_pImpl->pWindow ? *m_pImpl->pWindow : GetFrame().GetWindow(); +} + +weld::Window* SfxViewFrame::GetFrameWeld() const +{ + return GetWindow().GetFrameWeld(); +} + +bool SfxViewFrame::DoClose() +{ + return GetFrame().DoClose(); +} + +OUString SfxViewFrame::GetActualPresentationURL_Impl() const +{ + if ( m_xObjSh.is() ) + return m_xObjSh->GetMedium()->GetName(); + return OUString(); +} + +void SfxViewFrame::SetModalMode( bool bModal ) +{ + // no real modality for LOK + if (comphelper::LibreOfficeKit::isActive()) + return; + + m_pImpl->bModal = bModal; + if ( m_xObjSh.is() ) + { + for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst( m_xObjSh.get() ); + !bModal && pFrame; pFrame = SfxViewFrame::GetNext( *pFrame, m_xObjSh.get() ) ) + bModal = pFrame->m_pImpl->bModal; + m_xObjSh->SetModalMode_Impl( bModal ); + } +} + +bool SfxViewFrame::IsInModalMode() const +{ + return m_pImpl->bModal || GetFrame().GetWindow().IsInModalMode(); +} + +void SfxViewFrame::Resize( bool bForce ) +{ + Size aSize = GetWindow().GetOutputSizePixel(); + if ( !bForce && aSize == m_pImpl->aSize ) + return; + + m_pImpl->aSize = aSize; + SfxViewShell *pShell = GetViewShell(); + if ( pShell ) + { + if ( GetFrame().IsInPlace() ) + { + Point aPoint = GetWindow().GetPosPixel(); + DoAdjustPosSizePixel( pShell, aPoint, aSize, true ); + } + else + { + DoAdjustPosSizePixel( pShell, Point(), aSize, false ); + } + } +} + +#if HAVE_FEATURE_SCRIPTING + +#define LINE_SEP 0x0A + +static void CutLines( OUString& rStr, sal_Int32 nStartLine, sal_Int32 nLines ) +{ + sal_Int32 nStartPos = 0; + sal_Int32 nLine = 0; + while ( nLine < nStartLine ) + { + nStartPos = rStr.indexOf( LINE_SEP, nStartPos ); + if( nStartPos == -1 ) + break; + nStartPos++; // not the \n. + nLine++; + } + + SAL_WARN_IF(nStartPos == -1, "sfx.view", "CutLines: Start row not found!"); + + if ( nStartPos != -1 ) + { + sal_Int32 nEndPos = nStartPos; + for ( sal_Int32 i = 0; i < nLines; i++ ) + nEndPos = rStr.indexOf( LINE_SEP, nEndPos+1 ); + + if ( nEndPos == -1 ) // Can happen at the last row. + nEndPos = rStr.getLength(); + else + nEndPos++; + + rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( nEndPos ); + } + // erase trailing lines + if ( nStartPos != -1 ) + { + sal_Int32 n = nStartPos; + sal_Int32 nLen = rStr.getLength(); + while ( ( n < nLen ) && ( rStr[ n ] == LINE_SEP ) ) + n++; + + if ( n > nStartPos ) + rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( n ); + } +} + +#endif + +/* + add new recorded dispatch macro script into the application global basic + lib container. It generates a new unique id for it and insert the macro + by using this number as name for the module + */ +void SfxViewFrame::AddDispatchMacroToBasic_Impl( const OUString& sMacro ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) sMacro; +#else + if ( sMacro.isEmpty() ) + return; + + SfxApplication* pSfxApp = SfxGetpApp(); + SfxItemPool& rPool = pSfxApp->GetPool(); + SfxRequest aReq(SID_BASICCHOOSER, SfxCallMode::SYNCHRON, rPool); + + //seen in tdf#122598, no parent for subsequent dialog + SfxAllItemSet aSet(rPool); + css::uno::Reference< css::frame::XFrame > xFrame = + GetFrame().GetFrameInterface(); + aSet.Put(SfxUnoFrameItem(SID_FILLFRAME, xFrame)); + aReq.SetInternalArgs_Impl(aSet); + + aReq.AppendItem( SfxBoolItem(SID_RECORDMACRO,true) ); + const SfxPoolItemHolder& rResult(SfxGetpApp()->ExecuteSlot(aReq)); + OUString aScriptURL; + if (nullptr != rResult.getItem()) + aScriptURL = static_cast<const SfxStringItem*>(rResult.getItem())->GetValue(); + if ( !aScriptURL.isEmpty() ) + { + // parse scriptURL + OUString aLibName; + OUString aModuleName; + OUString aMacroName; + OUString aLocation; + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< css::uri::XUriReferenceFactory > xFactory = + css::uri::UriReferenceFactory::create( xContext ); + Reference< css::uri::XVndSunStarScriptUrl > xUrl( xFactory->parse( aScriptURL ), UNO_QUERY ); + if ( xUrl.is() ) + { + // get name + const OUString aName = xUrl->getName(); + const sal_Unicode cTok = '.'; + sal_Int32 nIndex = 0; + aLibName = aName.getToken( 0, cTok, nIndex ); + if ( nIndex != -1 ) + aModuleName = aName.getToken( 0, cTok, nIndex ); + if ( nIndex != -1 ) + aMacroName = aName.getToken( 0, cTok, nIndex ); + + // get location + aLocation = xUrl->getParameter( "location" ); + } + + BasicManager* pBasMgr = nullptr; + if ( aLocation == "application" ) + { + // application basic + pBasMgr = SfxApplication::GetBasicManager(); + } + else if ( aLocation == "document" ) + { + pBasMgr = GetObjectShell()->GetBasicManager(); + } + + OUString aOUSource; + if ( pBasMgr) + { + StarBASIC* pBasic = pBasMgr->GetLib( aLibName ); + if ( pBasic ) + { + SbModule* pModule = pBasic->FindModule( aModuleName ); + SbMethod* pMethod = pModule ? pModule->FindMethod(aMacroName, SbxClassType::Method) : nullptr; + if (pMethod) + { + aOUSource = pModule->GetSource32(); + sal_uInt16 nStart, nEnd; + pMethod->GetLineRange( nStart, nEnd ); + sal_uInt16 nlStart = nStart; + sal_uInt16 nlEnd = nEnd; + CutLines( aOUSource, nlStart-1, nlEnd-nlStart+1 ); + } + } + } + + // open lib container and break operation if it couldn't be opened + css::uno::Reference< css::script::XLibraryContainer > xLibCont; + if ( aLocation == "application" ) + { + xLibCont = SfxGetpApp()->GetBasicContainer(); + } + else if ( aLocation == "document" ) + { + xLibCont = GetObjectShell()->GetBasicContainer(); + } + + if(!xLibCont.is()) + { + SAL_WARN("sfx.view", "couldn't get access to the basic lib container. Adding of macro isn't possible."); + return; + } + + // get LibraryContainer + css::uno::Any aTemp; + + css::uno::Reference< css::container::XNameAccess > xLib; + if(xLibCont->hasByName(aLibName)) + { + // library must be loaded + aTemp = xLibCont->getByName(aLibName); + xLibCont->loadLibrary(aLibName); + aTemp >>= xLib; + } + else + { + xLib = xLibCont->createLibrary(aLibName); + } + + // pack the macro as direct usable "sub" routine + OUStringBuffer sRoutine(10000); + bool bReplace = false; + + // get module + if(xLib->hasByName(aModuleName)) + { + if ( !aOUSource.isEmpty() ) + { + sRoutine.append( aOUSource ); + } + else + { + OUString sCode; + aTemp = xLib->getByName(aModuleName); + aTemp >>= sCode; + sRoutine.append( sCode ); + } + + bReplace = true; + } + + // append new method + sRoutine.append( "\nsub " + + aMacroName + + "\n" + + sMacro + + "\nend sub\n" ); + + // create the module inside the library and insert the macro routine + aTemp <<= sRoutine.makeStringAndClear(); + if ( bReplace ) + { + css::uno::Reference< css::container::XNameContainer > xModulCont( + xLib, + css::uno::UNO_QUERY); + xModulCont->replaceByName(aModuleName,aTemp); + } + else + { + css::uno::Reference< css::container::XNameContainer > xModulCont( + xLib, + css::uno::UNO_QUERY); + xModulCont->insertByName(aModuleName,aTemp); + } + + // #i17355# update the Basic IDE + for ( SfxViewShell* pViewShell = SfxViewShell::GetFirst(); pViewShell; pViewShell = SfxViewShell::GetNext( *pViewShell ) ) + { + if ( pViewShell->GetName() == "BasicIDE" ) + { + SfxViewFrame& rViewFrame = pViewShell->GetViewFrame(); + SfxDispatcher* pDispat = rViewFrame.GetDispatcher(); + if ( pDispat ) + { + SfxMacroInfoItem aInfoItem( SID_BASICIDE_ARG_MACROINFO, pBasMgr, aLibName, aModuleName, OUString(), OUString() ); + pDispat->ExecuteList(SID_BASICIDE_UPDATEMODULESOURCE, + SfxCallMode::SYNCHRON, { &aInfoItem }); + } + } + } + } + else + { + // add code for "session only" macro + } +#endif +} + +void SfxViewFrame::MiscExec_Impl( SfxRequest& rReq ) +{ + switch ( rReq.GetSlot() ) + { + case SID_STOP_RECORDING : + case SID_RECORDMACRO : + { + // try to find any active recorder on this frame + static constexpr OUString sProperty(u"DispatchRecorderSupplier"_ustr); + css::uno::Reference< css::frame::XFrame > xFrame = + GetFrame().GetFrameInterface(); + + css::uno::Reference< css::beans::XPropertySet > xSet(xFrame,css::uno::UNO_QUERY); + css::uno::Any aProp = xSet->getPropertyValue(sProperty); + css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier; + aProp >>= xSupplier; + css::uno::Reference< css::frame::XDispatchRecorder > xRecorder; + if (xSupplier.is()) + xRecorder = xSupplier->getDispatchRecorder(); + + bool bIsRecording = xRecorder.is(); + const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(SID_RECORDMACRO); + if ( pItem && pItem->GetValue() == bIsRecording ) + return; + + if ( xRecorder.is() ) + { + // disable active recording + aProp <<= css::uno::Reference< css::frame::XDispatchRecorderSupplier >(); + xSet->setPropertyValue(sProperty,aProp); + + const SfxBoolItem* pRecordItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + if ( !pRecordItem || !pRecordItem->GetValue() ) + // insert script into basic library container of application + AddDispatchMacroToBasic_Impl(xRecorder->getRecordedMacro()); + + xRecorder->endRecording(); + xRecorder = nullptr; + GetBindings().SetRecorder_Impl( xRecorder ); + + SetChildWindow( SID_RECORDING_FLOATWINDOW, false ); + if ( rReq.GetSlot() != SID_RECORDMACRO ) + GetBindings().Invalidate( SID_RECORDMACRO ); + } + else if ( rReq.GetSlot() == SID_RECORDMACRO ) + { + // enable recording + css::uno::Reference< css::uno::XComponentContext > xContext( + ::comphelper::getProcessComponentContext()); + + xRecorder = css::frame::DispatchRecorder::create( xContext ); + + xSupplier = css::frame::DispatchRecorderSupplier::create( xContext ); + + xSupplier->setDispatchRecorder(xRecorder); + xRecorder->startRecording(xFrame); + aProp <<= xSupplier; + xSet->setPropertyValue(sProperty,aProp); + GetBindings().SetRecorder_Impl( xRecorder ); + SetChildWindow( SID_RECORDING_FLOATWINDOW, true ); + } + + rReq.Done(); + break; + } + + case SID_TOGGLESTATUSBAR: + { + if ( auto xLayoutManager = getLayoutManager(GetFrame()) ) + { + static constexpr OUString aStatusbarResString( u"private:resource/statusbar/statusbar"_ustr ); + // Evaluate parameter. + const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot()); + bool bShow( true ); + if ( !pShowItem ) + bShow = xLayoutManager->isElementVisible( aStatusbarResString ); + else + bShow = pShowItem->GetValue(); + + if ( bShow ) + { + xLayoutManager->createElement( aStatusbarResString ); + xLayoutManager->showElement( aStatusbarResString ); + } + else + xLayoutManager->hideElement( aStatusbarResString ); + + if ( !pShowItem ) + rReq.AppendItem( SfxBoolItem( SID_TOGGLESTATUSBAR, bShow ) ); + } + rReq.Done(); + break; + } + case SID_COMMAND_POPUP: + { + tools::Rectangle aRectangle(Point(0,0), GetWindow().GetSizePixel()); + weld::Window* pParent = weld::GetPopupParent(GetWindow(), aRectangle); + m_pCommandPopupHandler->showPopup(pParent, GetFrame().GetFrameInterface()); + + rReq.Done(); + break; + } + case SID_WIN_FULLSCREEN: + { + const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot()); + SfxViewFrame *pTop = GetTopViewFrame(); + if ( pTop ) + { + WorkWindow* pWork = static_cast<WorkWindow*>( pTop->GetFrame().GetTopWindow_Impl() ); + if ( pWork ) + { + Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(GetFrame()); + bool bNewFullScreenMode = pItem ? pItem->GetValue() : !pWork->IsFullScreenMode(); + if ( bNewFullScreenMode != pWork->IsFullScreenMode() ) + { + if ( bNewFullScreenMode ) + sfx2::SfxNotebookBar::LockNotebookBar(); + else + sfx2::SfxNotebookBar::UnlockNotebookBar(); + + Reference< css::beans::XPropertySet > xLMPropSet( xLayoutManager, UNO_QUERY ); + if ( xLMPropSet.is() ) + { + try + { + xLMPropSet->setPropertyValue( + "HideCurrentUI", + Any( bNewFullScreenMode )); + } + catch ( css::beans::UnknownPropertyException& ) + { + } + } + pWork->ShowFullScreenMode( bNewFullScreenMode ); + pWork->SetMenuBarMode( bNewFullScreenMode ? MenuBarMode::Hide : MenuBarMode::Normal ); + GetFrame().GetWorkWindow_Impl()->SetFullScreen_Impl( bNewFullScreenMode ); + if ( !pItem ) + rReq.AppendItem( SfxBoolItem( SID_WIN_FULLSCREEN, bNewFullScreenMode ) ); + rReq.Done(); + } + else + rReq.Ignore(); + } + } + else + rReq.Ignore(); + + GetDispatcher()->Update_Impl( true ); + break; + } + } +} + +void SfxViewFrame::MiscState_Impl(SfxItemSet &rSet) +{ + const WhichRangesContainer & pRanges = rSet.GetRanges(); + DBG_ASSERT(!pRanges.empty(), "Set without range"); + for ( auto const & pRange : pRanges ) + { + for(sal_uInt16 nWhich = pRange.first; nWhich <= pRange.second; ++nWhich) + { + switch(nWhich) + { + case SID_CURRENT_URL: + { + rSet.Put( SfxStringItem( nWhich, GetActualPresentationURL_Impl() ) ); + break; + } + + case SID_RECORDMACRO : + { + const OUString& sName{GetObjectShell()->GetFactory().GetFactoryName()}; + bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get(); + if (bMacrosDisabled || + !officecfg::Office::Common::Misc::MacroRecorderMode::get() || + ( sName!="swriter" && sName!="scalc" ) ) + { + rSet.DisableItem( nWhich ); + rSet.Put(SfxVisibilityItem(nWhich, false)); + break; + } + + css::uno::Reference< css::beans::XPropertySet > xSet( + GetFrame().GetFrameInterface(), + css::uno::UNO_QUERY); + + css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier"); + css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier; + if ( aProp >>= xSupplier ) + rSet.Put( SfxBoolItem( nWhich, xSupplier.is() ) ); + else + rSet.DisableItem( nWhich ); + break; + } + + case SID_STOP_RECORDING : + { + const OUString& sName{GetObjectShell()->GetFactory().GetFactoryName()}; + if ( !officecfg::Office::Common::Misc::MacroRecorderMode::get() || + ( sName!="swriter" && sName!="scalc" ) ) + { + rSet.DisableItem( nWhich ); + break; + } + + css::uno::Reference< css::beans::XPropertySet > xSet( + GetFrame().GetFrameInterface(), + css::uno::UNO_QUERY); + + css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier"); + css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier; + if ( !(aProp >>= xSupplier) || !xSupplier.is() ) + rSet.DisableItem( nWhich ); + break; + } + + case SID_TOGGLESTATUSBAR: + { + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + css::uno::Reference< css::beans::XPropertySet > xSet( + GetFrame().GetFrameInterface(), + css::uno::UNO_QUERY); + css::uno::Any aProp = xSet->getPropertyValue( "LayoutManager" ); + + if ( !( aProp >>= xLayoutManager )) + rSet.Put( SfxBoolItem( nWhich, false )); + else + { + bool bShow = xLayoutManager->isElementVisible( "private:resource/statusbar/statusbar" ); + rSet.Put( SfxBoolItem( nWhich, bShow )); + } + break; + } + + case SID_WIN_FULLSCREEN: + { + SfxViewFrame* pTop = GetTopViewFrame(); + if ( pTop ) + { + WorkWindow* pWork = static_cast<WorkWindow*>( pTop->GetFrame().GetTopWindow_Impl() ); + if ( pWork ) + { + rSet.Put( SfxBoolItem( nWhich, pWork->IsFullScreenMode() ) ); + break; + } + } + + rSet.DisableItem( nWhich ); + break; + } + + default: + break; + } + } + } +} + +/* [Description] + + This method can be included in the Execute method for the on- and off- + switching of ChildWindows, to implement this and API-bindings. + + Simply include as 'ExecuteMethod' in the IDL. +*/ +void SfxViewFrame::ChildWindowExecute( SfxRequest &rReq ) +{ + // Evaluate Parameter + sal_uInt16 nSID = rReq.GetSlot(); + + if (nSID == SID_SIDEBAR_DECK) + { + const SfxStringItem* pDeckIdItem = rReq.GetArg<SfxStringItem>(SID_SIDEBAR_DECK); + if (pDeckIdItem) + { + const OUString aDeckId(pDeckIdItem->GetValue()); + const SfxBoolItem* pToggleItem = rReq.GetArg<SfxBoolItem>(SID_SIDEBAR_DECK_TOGGLE); + bool bToggle = pToggleItem && pToggleItem->GetValue(); + ::sfx2::sidebar::Sidebar::ShowDeck(aDeckId, this, bToggle); + } + rReq.Done(); + return; + } + + const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSID); + if ( nSID == SID_VIEW_DATA_SOURCE_BROWSER ) + { + if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE)) + return; + Reference < XFrame > xFrame = GetFrame().GetFrameInterface(); + Reference < XFrame > xBeamer( xFrame->findFrame( "_beamer", FrameSearchFlag::CHILDREN ) ); + bool bHasChild = xBeamer.is(); + bool bShow = pShowItem ? pShowItem->GetValue() : !bHasChild; + if ( pShowItem ) + { + if( bShow == bHasChild ) + return; + } + else + rReq.AppendItem( SfxBoolItem( nSID, bShow ) ); + + if ( !bShow ) + { + SetChildWindow( SID_BROWSER, false ); + } + else + { + css::util::URL aTargetURL; + aTargetURL.Complete = ".component:DB/DataSourceBrowser"; + Reference < css::util::XURLTransformer > xTrans( + css::util::URLTransformer::create( + ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict( aTargetURL ); + + Reference < XDispatchProvider > xProv( xFrame, UNO_QUERY ); + Reference < css::frame::XDispatch > xDisp; + if ( xProv.is() ) + xDisp = xProv->queryDispatch( aTargetURL, "_beamer", 31 ); + if ( xDisp.is() ) + { + Sequence < css::beans::PropertyValue > aArgs(1); + css::beans::PropertyValue* pArg = aArgs.getArray(); + pArg[0].Name = "Referer"; + pArg[0].Value <<= OUString("private:user"); + xDisp->dispatch( aTargetURL, aArgs ); + } + } + + rReq.Done(); + return; + } + if (nSID == SID_STYLE_DESIGNER) + { + // First make sure that the sidebar is visible + ShowChildWindow(SID_SIDEBAR); + + ::sfx2::sidebar::Sidebar::ShowPanel(u"StyleListPanel", + GetFrame().GetFrameInterface(), true); + rReq.Done(); + return; + } + if (nSID == SID_NAVIGATOR) + { + if (comphelper::LibreOfficeKit::isActive()) + { + ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowDeck(u"NavigatorDeck", this, true); + rReq.Done(); + return; + } + } + + bool bHasChild = HasChildWindow(nSID); + bool bShow = pShowItem ? pShowItem->GetValue() : !bHasChild; + GetDispatcher()->Update_Impl( true ); + + // Perform action. + if ( !pShowItem || bShow != bHasChild ) + ToggleChildWindow( nSID ); + + GetBindings().Invalidate( nSID ); + + // Record if possible. + if ( nSID == SID_HYPERLINK_DIALOG || nSID == SID_SEARCH_DLG ) + { + rReq.Ignore(); + } + else + { + rReq.AppendItem( SfxBoolItem( nSID, bShow ) ); + rReq.Done(); + } +} + +/* [Description] + + This method can be used in the state method for the on and off-state + of child-windows, in order to implement this. + + Just register the IDL as 'StateMethod'. +*/ +void SfxViewFrame::ChildWindowState( SfxItemSet& rState ) +{ + SfxWhichIter aIter( rState ); + for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() ) + { + if ( nSID == SID_VIEW_DATA_SOURCE_BROWSER ) + { + rState.Put( SfxBoolItem( nSID, HasChildWindow( SID_BROWSER ) ) ); + } + else if ( nSID == SID_HYPERLINK_DIALOG ) + { + SfxPoolItemHolder aDummy; + SfxItemState eState = GetDispatcher()->QueryState(SID_HYPERLINK_SETLINK, aDummy); + if ( SfxItemState::DISABLED == eState ) + rState.DisableItem(nSID); + else + { + if ( KnowsChildWindow(nSID) ) + rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID)) ); + else + rState.DisableItem(nSID); + } + } + else if ( nSID == SID_BROWSER ) + { + Reference < XFrame > xFrame = GetFrame().GetFrameInterface()-> + findFrame( "_beamer", FrameSearchFlag::CHILDREN ); + if ( !xFrame.is() ) + rState.DisableItem( nSID ); + else if ( KnowsChildWindow(nSID) ) + rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID) ) ); + } + else if ( nSID == SID_SIDEBAR ) + { + if ( !KnowsChildWindow( nSID ) ) + { + SAL_INFO("sfx.view", "SID_SIDEBAR state requested, but no task pane child window exists for this ID!"); + rState.DisableItem( nSID ); + } + else + { + rState.Put( SfxBoolItem( nSID, HasChildWindow( nSID ) ) ); + } + } + else if ( KnowsChildWindow(nSID) ) + rState.Put( SfxBoolItem( nSID, HasChildWindow(nSID) ) ); + else + rState.DisableItem(nSID); + } +} + +SfxWorkWindow* SfxViewFrame::GetWorkWindow_Impl() +{ + SfxWorkWindow* pWork = GetFrame().GetWorkWindow_Impl(); + return pWork; +} + +void SfxViewFrame::SetChildWindow(sal_uInt16 nId, bool bOn, bool bSetFocus ) +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl(); + if ( pWork ) + pWork->SetChildWindow_Impl( nId, bOn, bSetFocus ); +} + +void SfxViewFrame::ToggleChildWindow(sal_uInt16 nId) +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl(); + if ( pWork ) + pWork->ToggleChildWindow_Impl( nId, true ); +} + +bool SfxViewFrame::HasChildWindow( sal_uInt16 nId ) +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl(); + return pWork && pWork->HasChildWindow_Impl(nId); +} + +bool SfxViewFrame::KnowsChildWindow( sal_uInt16 nId ) +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl(); + return pWork && pWork->KnowsChildWindow_Impl(nId); +} + +void SfxViewFrame::ShowChildWindow( sal_uInt16 nId, bool bVisible ) +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl(); + if ( pWork ) + { + GetDispatcher()->Update_Impl(true); + pWork->ShowChildWindow_Impl(nId, bVisible, true ); + } +} + +SfxChildWindow* SfxViewFrame::GetChildWindow(sal_uInt16 nId) +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl(); + return pWork ? pWork->GetChildWindow_Impl(nId) : nullptr; +} + +void SfxViewFrame::UpdateDocument_Impl() +{ + SfxObjectShell* pDoc = GetObjectShell(); + if ( pDoc->IsLoadingFinished() ) + pDoc->CheckSecurityOnLoading_Impl(); + + // check if document depends on a template + pDoc->UpdateFromTemplate_Impl(); +} + +void SfxViewFrame::SetViewFrame( SfxViewFrame* pFrame ) +{ + if(pFrame) + SetSVHelpData(pFrame->m_pHelpData); + + SetSVWinData(pFrame ? pFrame->m_pWinData : nullptr); + + SfxGetpApp()->SetViewFrame_Impl( pFrame ); +} + +VclPtr<SfxInfoBarWindow> SfxViewFrame::AppendInfoBar(const OUString& sId, + const OUString& sPrimaryMessage, + const OUString& sSecondaryMessage, + InfobarType aInfobarType, bool bShowCloseButton) +{ + SfxChildWindow* pChild = GetChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + if (!pChild) + return nullptr; + + if (HasInfoBarWithID(sId)) + return nullptr; + + SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow()); + auto pInfoBar = pInfoBarContainer->appendInfoBar(sId, sPrimaryMessage, sSecondaryMessage, + aInfobarType, bShowCloseButton); + ShowChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + return pInfoBar; +} + +void SfxViewFrame::UpdateInfoBar(std::u16string_view sId, const OUString& sPrimaryMessage, + const OUString& sSecondaryMessage, InfobarType eType) +{ + const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId(); + + // Make sure the InfoBar container is visible + if (!HasChildWindow(nId)) + ToggleChildWindow(nId); + + SfxChildWindow* pChild = GetChildWindow(nId); + if (pChild) + { + SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow()); + auto pInfoBar = pInfoBarContainer->getInfoBar(sId); + + if (pInfoBar) + pInfoBar->Update(sPrimaryMessage, sSecondaryMessage, eType); + } +} + +void SfxViewFrame::RemoveInfoBar( std::u16string_view sId ) +{ + const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId(); + + // Make sure the InfoBar container is visible + if (!HasChildWindow(nId)) + ToggleChildWindow(nId); + + SfxChildWindow* pChild = GetChildWindow(nId); + if (pChild) + { + SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow()); + auto pInfoBar = pInfoBarContainer->getInfoBar(sId); + pInfoBarContainer->removeInfoBar(pInfoBar); + ShowChildWindow(nId); + } +} + +bool SfxViewFrame::HasInfoBarWithID( std::u16string_view sId ) +{ + const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId(); + + SfxChildWindow* pChild = GetChildWindow(nId); + if (pChild) + { + SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow()); + return pInfoBarContainer->hasInfoBarWithID(sId); + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |