diff options
Diffstat (limited to 'sfx2/source/appl')
47 files changed, 22539 insertions, 0 deletions
diff --git a/sfx2/source/appl/app.cxx b/sfx2/source/appl/app.cxx new file mode 100644 index 0000000000..6ae9810345 --- /dev/null +++ b/sfx2/source/appl/app.cxx @@ -0,0 +1,554 @@ +/* -*- 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 <sal/log.hxx> +#include <tools/debug.hxx> + +#include <sfx2/app.hxx> +#include <sfx2/frame.hxx> +#include <basic/sberrors.hxx> + +#include <svl/svdde.hxx> +#include <unotools/configmgr.hxx> +#include <com/sun/star/frame/XFrame.hpp> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> +#include <basic/basmgr.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/sfxhelp.hxx> +#include <sfx2/progress.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/viewfrm.hxx> +#include <appdata.hxx> +#include <sfx2/module.hxx> +#include <sfx2/event.hxx> +#include <workwin.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <sfx2/tbxctrl.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/stbitem.hxx> +#include <sfx2/dockwin.hxx> + +#include <officecfg/Office/Common.hxx> +#include <rtl/strbuf.hxx> +#include <memory> +#include <mutex> +#include <framework/sfxhelperfunctions.hxx> +#include <fwkhelper.hxx> + +#include "getbasctlfunction.hxx" + +using namespace ::com::sun::star; + +static SfxApplication* g_pSfxApplication = nullptr; + +#if HAVE_FEATURE_XMLHELP +static SfxHelp* pSfxHelp = nullptr; +#endif + +SfxApplication* SfxApplication::Get() +{ + return g_pSfxApplication; +} + +void SfxApplication::SetModule(SfxToolsModule nSharedLib, std::unique_ptr<SfxModule> pModule) +{ + assert(g_pSfxApplication != nullptr); + + g_pSfxApplication->pImpl->aModules[nSharedLib] = std::move(pModule); +} + +SfxModule* SfxApplication::GetModule(SfxToolsModule nSharedLib) +{ + if (!g_pSfxApplication) // It is possible GetModule is called before SfxApplication is initialised via GetOrCreate() + return nullptr; + return g_pSfxApplication->pImpl->aModules[nSharedLib].get(); +} + +SfxApplication* SfxApplication::GetOrCreate() +{ + static std::mutex theApplicationMutex; + + // SFX on demand + std::unique_lock aGuard(theApplicationMutex); + if (!g_pSfxApplication) + { + SAL_INFO( "sfx.appl", "SfxApplication::SetApp" ); + + g_pSfxApplication = new SfxApplication; + + // at the moment a bug may occur when Initialize_Impl returns FALSE, + // but this is only temporary because all code that may cause such + // a fault will be moved outside the SFX + g_pSfxApplication->Initialize_Impl(); + + ::framework::SetRefreshToolbars( RefreshToolbars ); + ::framework::SetToolBoxControllerCreator( SfxToolBoxControllerFactory ); + ::framework::SetStatusBarControllerCreator( SfxStatusBarControllerFactory ); + ::framework::SetDockingWindowCreator( SfxDockingWindowFactory ); + ::framework::SetIsDockingWindowVisible( IsDockingWindowVisible ); +#if HAVE_FEATURE_XMLHELP + Application::SetHelp( pSfxHelp ); +#endif +#if HAVE_FEATURE_XMLHELP || defined(EMSCRIPTEN) + bool bHelpTip = officecfg::Office::Common::Help::Tip::get(); + bool bExtendedHelpTip = officecfg::Office::Common::Help::ExtendedTip::get(); + if (!utl::ConfigManager::IsFuzzing() && bHelpTip) + Help::EnableQuickHelp(); + else + Help::DisableQuickHelp(); + if (!utl::ConfigManager::IsFuzzing() && bHelpTip && bExtendedHelpTip) + Help::EnableBalloonHelp(); + else + Help::DisableBalloonHelp(); +#endif + } + return g_pSfxApplication; +} + +SfxApplication::SfxApplication() + : pImpl( new SfxAppData_Impl ) +{ + SetName( "StarOffice" ); + + SAL_INFO( "sfx.appl", "{ initialize DDE" ); + + bool bOk = InitializeDde(); + +#ifdef DBG_UTIL + if( !bOk ) + { + OStringBuffer aStr("No DDE-Service possible. Error: "); + if( GetDdeService() ) + aStr.append(static_cast<sal_Int32>(GetDdeService()->GetError())); + else + aStr.append('?'); + SAL_WARN( "sfx.appl", aStr.getStr() ); + } +#else + (void)bOk; +#endif + +#if HAVE_FEATURE_XMLHELP + pSfxHelp = new SfxHelp; +#endif + +#if HAVE_FEATURE_SCRIPTING + StarBASIC::SetGlobalErrorHdl( LINK( this, SfxApplication, GlobalBasicErrorHdl_Impl ) ); +#endif + + SAL_INFO( "sfx.appl", "} initialize DDE" ); +} + +SfxApplication::~SfxApplication() +{ + SAL_WARN_IF(GetObjectShells_Impl().size() != 0, "sfx.appl", "Memory leak: some object shells were not removed!"); + + Broadcast( SfxHint(SfxHintId::Dying) ); + + for (auto &module : pImpl->aModules) // Clear modules + module.reset(); + +#if HAVE_FEATURE_XMLHELP + delete pSfxHelp; + Application::SetHelp(); +#endif + + if ( !pImpl->bDowning ) + Deinitialize(); + + g_pSfxApplication = nullptr; +} + + +const OUString& SfxApplication::GetLastDir_Impl() const + +/* [Description] + + Internal method by which the last set directory with the method + <SfxApplication::SetLastDir_Impl()> in SFX is returned. + + This is usually the most recently addressed by the + SfxFileDialog directory. + + [Cross-reference] + <SfxApplication::SetLastDir_Impl()> +*/ + +{ + return pImpl->aLastDir; +} + +void SfxApplication::SetLastDir_Impl +( + const OUString& rNewDir /* Complete directory path as a string */ +) + +/* [Description] + + Internal Method, by which a directory path is set that was last addressed + (eg by the SfxFileDialog). + + [Cross-reference] + <SfxApplication::GetLastDir_Impl()> +*/ + +{ + pImpl->aLastDir = rNewDir; +} + + +void SfxApplication::ResetLastDir() +{ + pImpl->aLastDir.clear(); +} + + +SfxDispatcher* SfxApplication::GetDispatcher_Impl() +{ + return pImpl->pViewFrame ? pImpl->pViewFrame->GetDispatcher() : &*pImpl->pAppDispat; +} + + +void SfxApplication::SetViewFrame_Impl( SfxViewFrame *pFrame ) +{ + if ( pFrame != pImpl->pViewFrame ) + { + SfxViewFrame *pOldFrame = pImpl->pViewFrame; + + // DocWinActivate : both frames belong to the same TopWindow + // TopWinActivate : both frames belong to different TopWindows + + bool bTaskActivate = pOldFrame != pFrame; + + if ( pOldFrame ) + { + if ( bTaskActivate ) + NotifyEvent( SfxViewEventHint( SfxEventHintId::DeactivateDoc, GlobalEventConfig::GetEventName(GlobalEventId::DEACTIVATEDOC), pOldFrame->GetObjectShell(), pOldFrame->GetFrame().GetController() ) ); + + pOldFrame->DoDeactivate( bTaskActivate, pFrame ); + + if( pOldFrame->GetProgress() ) + pOldFrame->GetProgress()->Suspend(); + } + + pImpl->pViewFrame = pFrame; + + if( pFrame ) + { + pFrame->DoActivate( bTaskActivate ); + if ( bTaskActivate && pFrame->GetObjectShell() ) + { + pFrame->GetObjectShell()->PostActivateEvent_Impl( pFrame ); + NotifyEvent(SfxViewEventHint(SfxEventHintId::ActivateDoc, GlobalEventConfig::GetEventName(GlobalEventId::ACTIVATEDOC), pFrame->GetObjectShell(), pFrame->GetFrame().GetController() ) ); + } + + SfxProgress *pProgress = pFrame->GetProgress(); + if ( pProgress ) + { + if( pProgress->IsSuspended() ) + pProgress->Resume(); + else + pProgress->SetState( pProgress->GetState() ); + } + + if ( pImpl->pViewFrame->GetViewShell() ) + { + SfxDispatcher* pDisp = pImpl->pViewFrame->GetDispatcher(); + pDisp->Flush(); + pDisp->Update_Impl(true); + } + } + } + + // even if the frame actually didn't change, ensure its document is forwarded + // to SfxObjectShell::SetCurrentComponent. + // Otherwise, the CurrentComponent might not be correct, in case it has meanwhile + // been reset to some other document, by some non-SFX component. #i49133# + if ( pFrame && pFrame->GetViewShell() ) + pFrame->GetViewShell()->SetCurrentDocument(); +} + +void SfxApplication::SetProgress_Impl +( + SfxProgress *pProgress +) +{ + DBG_ASSERT( ( !pImpl->pProgress && pProgress ) || + ( pImpl->pProgress && !pProgress ), + "Progress activation/deactivation mismatch" ); + + if ( pImpl->pProgress && pProgress ) + { + pImpl->pProgress->Suspend(); + delete pImpl->pProgress; + } + + pImpl->pProgress = pProgress; +} + + +sal_uInt16 SfxApplication::GetFreeIndex() +{ + return pImpl->aIndexBitSet.GetFreeIndex()+1; +} + + +void SfxApplication::ReleaseIndex(sal_uInt16 i) +{ + pImpl->aIndexBitSet.ReleaseIndex(i-1); +} + + +weld::Window* SfxApplication::GetTopWindow() const +{ + SfxWorkWindow* pWork = GetWorkWindow_Impl( SfxViewFrame::Current() ); + if (!pWork) + return nullptr; + vcl::Window* pWindow = pWork->GetWindow(); + if (!pWindow) + return nullptr; + return pWindow->GetFrameWeld(); +} + +SfxTbxCtrlFactory* SfxApplication::GetTbxCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const +{ + // search for a factory with the given slot id + for (auto& rFactory : pImpl->maTbxCtrlFactories) + if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == nSlotID ) + return &rFactory; + + // if no factory exists for the given slot id, see if we + // have a generic factory with the correct slot type and slot id == 0 + for (auto& rFactory : pImpl->maTbxCtrlFactories) + if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == 0 ) + return &rFactory; + + return nullptr; +} + +SfxStbCtrlFactory* SfxApplication::GetStbCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const +{ + for (auto& rFactory : pImpl->maStbCtrlFactories) + if ( rFactory.nTypeId == rSlotType && + ( rFactory.nSlotId == 0 || rFactory.nSlotId == nSlotID ) ) + return &rFactory; + return nullptr; +} + +std::vector<SfxViewFrame*>& SfxApplication::GetViewFrames_Impl() const +{ + return pImpl->maViewFrames; +} + +std::vector<SfxViewShell*>& SfxApplication::GetViewShells_Impl() const +{ + return pImpl->maViewShells; +} + +std::unordered_map<OUString, css::uno::Reference<css::ui::XAcceleratorConfiguration>>& SfxApplication::GetAcceleratorConfs_Impl() const +{ + return pImpl->maAcceleratorConfs; +} + +std::vector<SfxObjectShell*>& SfxApplication::GetObjectShells_Impl() const +{ + return pImpl->maObjShells; +} + +void SfxApplication::Invalidate( sal_uInt16 nId ) +{ + for( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame; pFrame = SfxViewFrame::GetNext( *pFrame ) ) + Invalidate_Impl( pFrame->GetBindings(), nId ); +} + +#if HAVE_FEATURE_SCRIPTING + +#ifndef DISABLE_DYNLOADING + +typedef long (*basicide_handle_basic_error)(void const *); +typedef void (*basicide_macro_organizer)(void *, void *, sal_Int16); + +#else + +extern "C" long basicide_handle_basic_error(void const*); +extern "C" void basicide_macro_organizer(void*, void*, sal_Int16); + +#endif + +#endif + +IMPL_STATIC_LINK( SfxApplication, GlobalBasicErrorHdl_Impl, StarBASIC*, pStarBasic, bool ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) pStarBasic; + return false; +#else + + if (comphelper::LibreOfficeKit::isActive()) + { + OUString aError; + ErrCodeMsg nErr = StarBASIC::GetErrorCode(); + if (ErrorStringFactory::CreateString(nErr, aError)) + { + const SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + std::shared_ptr<weld::MessageDialog> xBox; + xBox.reset(Application::CreateMessageDialog( + pViewFrame ? pViewFrame->GetFrameWeld() : nullptr, + VclMessageType::Error, + VclButtonsType::Ok, + aError, + GetpApp())); + + xBox->runAsync(xBox, [](sal_Int32 /*nResult*/) {}); + } + return true; + } + +#ifndef DISABLE_DYNLOADING + basicide_handle_basic_error pSymbol = reinterpret_cast<basicide_handle_basic_error>(sfx2::getBasctlFunction("basicide_handle_basic_error")); + + // call basicide_handle_basic_error in basctl + bool bRet = pSymbol( pStarBasic ); + +#else + + bool bRet = basicide_handle_basic_error( pStarBasic ); + +#endif + + return bRet; + +#endif +} + +bool SfxApplication::IsXScriptURL( const OUString& rScriptURL ) +{ + bool result = false; + +#if !HAVE_FEATURE_SCRIPTING + (void) rScriptURL; +#else + css::uno::Reference< css::uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + + css::uno::Reference< css::uri::XUriReferenceFactory > + xFactory = css::uri::UriReferenceFactory::create( xContext ); + + try + { + css::uno::Reference< css::uri::XVndSunStarScriptUrl > + xUrl( xFactory->parse( rScriptURL ), css::uno::UNO_QUERY ); + + if ( xUrl.is() ) + { + result = true; + } + } + catch (const css::uno::RuntimeException&) + { + // ignore, will just return FALSE + } +#endif + return result; +} + +OUString +SfxApplication::ChooseScript(weld::Window *pParent) +{ + OUString aScriptURL; + +#if HAVE_FEATURE_SCRIPTING + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + SAL_INFO( "sfx.appl", "create selector dialog"); + + const SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + const SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr; + uno::Reference< frame::XFrame > xFrame( pFrame ? pFrame->GetFrameInterface() : uno::Reference< frame::XFrame >() ); + + ScopedVclPtr<AbstractScriptSelectorDialog> pDlg(pFact->CreateScriptSelectorDialog(pParent, xFrame)); + + SAL_INFO( "sfx.appl", "done, now exec it"); + + sal_uInt16 nRet = pDlg->Execute(); + + SAL_INFO( "sfx.appl", "has returned"); + + if ( nRet == RET_OK ) + { + aScriptURL = pDlg->GetScriptURL(); + } +#else + (void) pParent; +#endif + return aScriptURL; +} + +void SfxApplication::MacroOrganizer(weld::Window* pParent, const uno::Reference<frame::XFrame>& xDocFrame, sal_Int16 nTabId) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) pParent; + (void) nTabId; +#else + +#ifndef DISABLE_DYNLOADING + basicide_macro_organizer pSymbol = reinterpret_cast<basicide_macro_organizer>(sfx2::getBasctlFunction("basicide_macro_organizer")); + + // call basicide_macro_organizer in basctl + pSymbol(pParent, xDocFrame.get(), nTabId); + +#else + + basicide_macro_organizer(pParent, xDocFrame.get(), nTabId); + +#endif + +#endif +} + +ErrCode SfxApplication::CallBasic( const OUString& rCode, BasicManager* pMgr, SbxArray* pArgs, SbxValue* pRet ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) rCode; + (void) pMgr; + (void) pArgs; + (void) pRet; + return ERRCODE_BASIC_CANNOT_LOAD; +#else + (void) ERRCODE_BASIC_CANNOT_LOAD; // So that the !HAVE_FEATURE_SCRIPTING case isn't broken again by IWYU + return pMgr->ExecuteMacro( rCode, pArgs, pRet); +#endif +} + +sfx2::sidebar::Theme & SfxApplication::GetSidebarTheme() +{ + if (!pImpl->m_pSidebarTheme.is()) + { + pImpl->m_pSidebarTheme.set(new sfx2::sidebar::Theme); + pImpl->m_pSidebarTheme->InitializeTheme(); + } + return *pImpl->m_pSidebarTheme; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appbas.cxx b/sfx2/source/appl/appbas.cxx new file mode 100644 index 0000000000..1cedcd9785 --- /dev/null +++ b/sfx2/source/appl/appbas.cxx @@ -0,0 +1,155 @@ +/* -*- 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_features.h> + +#include <sal/config.h> + +#include <cassert> + +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <officecfg/Office/Common.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> +#include <svl/voiditem.hxx> +#include <basic/sbstar.hxx> + +#include <sfx2/frame.hxx> +#include <sfx2/dinfdlg.hxx> +#include <sfx2/app.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <appdata.hxx> +#include <basic/basmgr.hxx> +#include <unotools/configmgr.hxx> +#include <sorgitm.hxx> +#include <appbaslib.hxx> +#include <basic/basicmanagerrepository.hxx> + +#define SFX_TYPEMAP +#include <sfxslots.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; + +using ::basic::BasicManagerRepository; + + +void SfxApplication::SaveBasicAndDialogContainer() const +{ + if ( pImpl->pBasicManager->isValid() ) + pImpl->pBasicManager->storeAllLibraries(); +} + +BasicManager* SfxApplication::GetBasicManager() +{ +#if !HAVE_FEATURE_SCRIPTING + return nullptr; +#else + if (utl::ConfigManager::IsFuzzing()) + return nullptr; + return BasicManagerRepository::getApplicationBasicManager(); +#endif +} + +XLibraryContainer * SfxApplication::GetDialogContainer() +{ +#if !HAVE_FEATURE_SCRIPTING + return nullptr; +#else + if (utl::ConfigManager::IsFuzzing()) + return nullptr; + if ( !pImpl->pBasicManager->isValid() ) + GetBasicManager(); + return pImpl->pBasicManager->getLibraryContainer( SfxBasicManagerHolder::DIALOGS ); +#endif +} + + +XLibraryContainer * SfxApplication::GetBasicContainer() +{ +#if !HAVE_FEATURE_SCRIPTING + return nullptr; +#else + if (utl::ConfigManager::IsFuzzing()) + return nullptr; + if ( !pImpl->pBasicManager->isValid() ) + GetBasicManager(); + return pImpl->pBasicManager->getLibraryContainer( SfxBasicManagerHolder::SCRIPTS ); +#endif +} + +StarBASIC* SfxApplication::GetBasic() +{ +#if !HAVE_FEATURE_SCRIPTING + return nullptr; +#else + if (utl::ConfigManager::IsFuzzing()) + return nullptr; + return GetBasicManager()->GetLib(0); +#endif +} + +void SfxApplication::PropExec_Impl( SfxRequest const &rReq ) +{ + sal_uInt16 nSID = rReq.GetSlot(); + switch ( nSID ) + { + case SID_ATTR_UNDO_COUNT: + { + if (const SfxUInt16Item* pCountItem = rReq.GetArg<SfxUInt16Item>(nSID)) + { + std::shared_ptr< comphelper::ConfigurationChanges > batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Undo::Steps::set( + pCountItem->GetValue(), batch); + batch->commit(); + } + break; + } + + default: + assert(false); + } +} + +void SfxApplication::PropState_Impl( SfxItemSet &rSet ) +{ + SfxWhichIter aIter(rSet); + for ( sal_uInt16 nSID = aIter.FirstWhich(); nSID; nSID = aIter.NextWhich() ) + { + switch ( nSID ) + { + case SID_ATTR_UNDO_COUNT: + rSet.Put( + SfxUInt16Item( + SID_ATTR_UNDO_COUNT, + officecfg::Office::Common::Undo::Steps::get())); + break; + + default: + assert(false); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appbaslib.cxx b/sfx2/source/appl/appbaslib.cxx new file mode 100644 index 0000000000..bfeafa1b1b --- /dev/null +++ b/sfx2/source/appl/appbaslib.cxx @@ -0,0 +1,188 @@ +/* -*- 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_features.h> + +#include <appbaslib.hxx> + +#include <sfx2/app.hxx> + +#include <basic/basmgr.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::embed; + + +SfxBasicManagerHolder::SfxBasicManagerHolder() + :mpBasicManager( nullptr ) +{ +} + +void SfxBasicManagerHolder::Notify(SfxBroadcaster& rBC, SfxHint const& rHint) +{ + if (!mpBasicManager || &rBC != mpBasicManager) + return; + if (SfxHintId::Dying == rHint.GetId()) + { + mpBasicManager = nullptr; + mxBasicContainer.clear(); + mxDialogContainer.clear(); + } +} + +void SfxBasicManagerHolder::reset( BasicManager* _pBasicManager ) +{ + impl_releaseContainers(); + +#if !HAVE_FEATURE_SCRIPTING + (void) _pBasicManager; +#else + // Note: we do not delete the old BasicManager. BasicManager instances are + // nowadays obtained from the BasicManagerRepository, and the ownership is with + // the repository. + // @see basic::BasicManagerRepository::getApplicationBasicManager + // @see basic::BasicManagerRepository::getDocumentBasicManager + mpBasicManager = _pBasicManager; + + if ( !mpBasicManager ) + return; + + StartListening(*mpBasicManager); + try + { + mxBasicContainer.set( mpBasicManager->GetScriptLibraryContainer(), UNO_QUERY_THROW ); + mxDialogContainer.set( mpBasicManager->GetDialogLibraryContainer(), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.appl"); + } +#endif +} + +void SfxBasicManagerHolder::storeAllLibraries() +{ +#if HAVE_FEATURE_SCRIPTING + OSL_PRECOND( isValid(), "SfxBasicManagerHolder::storeAllLibraries: not initialized!" ); + try + { + if ( mxBasicContainer.is() ) + mxBasicContainer->storeLibraries(); + if ( mxDialogContainer.is() ) + mxDialogContainer->storeLibraries(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.appl"); + } +#endif +} + +void SfxBasicManagerHolder::setStorage( const Reference< XStorage >& _rxStorage ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) _rxStorage; +#else + try + { + if ( mxBasicContainer.is() ) + mxBasicContainer->setRootStorage( _rxStorage ); + if ( mxDialogContainer.is() ) + mxDialogContainer->setRootStorage( _rxStorage ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.appl"); + } +#endif +} + +void SfxBasicManagerHolder::storeLibrariesToStorage( const Reference< XStorage >& _rxStorage ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) _rxStorage; +#else + OSL_PRECOND( isValid(), "SfxBasicManagerHolder::storeLibrariesToStorage: not initialized!" ); + + if ( mxBasicContainer.is() ) + mxBasicContainer->storeLibrariesToStorage( _rxStorage ); + if ( mxDialogContainer.is() ) + mxDialogContainer->storeLibrariesToStorage( _rxStorage ); +#endif +} + +XLibraryContainer * SfxBasicManagerHolder::getLibraryContainer( ContainerType _eType ) +{ + OSL_PRECOND( isValid(), "SfxBasicManagerHolder::getLibraryContainer: not initialized!" ); + + switch ( _eType ) + { + case SCRIPTS: return mxBasicContainer.get(); + case DIALOGS: return mxDialogContainer.get(); + } + OSL_FAIL( "SfxBasicManagerHolder::getLibraryContainer: illegal container type!" ); + return nullptr; +} + +void SfxBasicManagerHolder::impl_releaseContainers() +{ + mxBasicContainer.clear(); + mxDialogContainer.clear(); +} + +bool SfxBasicManagerHolder::ImgVersion12PsswdBinaryLimitExceeded( std::vector< OUString >& sModules ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) sModules; +#else + if ( mpBasicManager ) + return mpBasicManager->ImgVersion12PsswdBinaryLimitExceeded( sModules ); +#endif + return true; +} + +// Service for application library container +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_sfx2_ApplicationDialogLibraryContainer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + SfxApplication::GetBasicManager(); + css::uno::XInterface* pRet = SfxGetpApp()->GetDialogContainer(); + pRet->acquire(); + return pRet; +} + +// Service for application library container +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_sfx2_ApplicationScriptLibraryContainer_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + SfxApplication::GetBasicManager(); + css::uno::XInterface* pRet = SfxGetpApp()->GetBasicContainer(); + pRet->acquire(); + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appcfg.cxx b/sfx2/source/appl/appcfg.cxx new file mode 100644 index 0000000000..ee45f9da26 --- /dev/null +++ b/sfx2/source/appl/appcfg.cxx @@ -0,0 +1,343 @@ +/* -*- 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 <memory> +#include <osl/file.hxx> + +#include <rtl/ustring.hxx> +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <svl/slstitm.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svl/undo.hxx> +#include <svl/whiter.hxx> + +#include <sfx2/sfxsids.hrc> + +#include <officecfg/Inet.hxx> +#include <officecfg/Office/Common.hxx> +#include <officecfg/Office/Recovery.hxx> +#include <unotools/securityoptions.hxx> +#include <unotools/pathoptions.hxx> +#include <svtools/miscopt.hxx> +#include <svtools/imgdef.hxx> +#include <sal/log.hxx> +#include <vcl/idle.hxx> + +#include <sfx2/app.hxx> +#include <sfx2/event.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objsh.hxx> +#include <comphelper/lok.hxx> +#include <objshimp.hxx> +#include "shutdownicon.hxx" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; + +namespace { + +class SfxEventAsyncer_Impl : public SfxListener +{ + SfxEventHint aHint; + std::unique_ptr<Idle> pIdle; + +public: + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + explicit SfxEventAsyncer_Impl(const SfxEventHint& rHint); + DECL_LINK( IdleHdl, Timer*, void ); +}; + +} + +void SfxEventAsyncer_Impl::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if( rHint.GetId() == SfxHintId::Dying && pIdle->IsActive() ) + { + pIdle->Stop(); + delete this; + } +} + + +SfxEventAsyncer_Impl::SfxEventAsyncer_Impl( const SfxEventHint& rHint ) + : aHint( rHint ) +{ + if( rHint.GetObjShell() ) + StartListening( *rHint.GetObjShell() ); + pIdle.reset( new Idle("sfx::SfxEventAsyncer_Impl pIdle") ); + pIdle->SetInvokeHandler( LINK(this, SfxEventAsyncer_Impl, IdleHdl) ); + pIdle->SetPriority( TaskPriority::HIGH_IDLE ); + pIdle->Start(); +} + + +IMPL_LINK(SfxEventAsyncer_Impl, IdleHdl, Timer*, pAsyncIdle, void) +{ + SfxObjectShellRef xRef( aHint.GetObjShell() ); + pAsyncIdle->Stop(); + SAL_INFO_IF(!xRef.is(), "sfx.appl", "SfxEvent: " << aHint.GetEventName()); + SfxGetpApp()->Broadcast( aHint ); + if ( xRef.is() ) + xRef->Broadcast( aHint ); + delete this; +} + +namespace +{ +template <class Cfg, class Item> bool toSet(SfxItemSet& rSet, TypedWhichId<Item> wid) +{ + return rSet.Put(Item(wid, Cfg::get())); +} +template <class Cfg, class Item, class Val> +bool toSet_withDefault(SfxItemSet& rSet, TypedWhichId<Item> wid, Val&& defVal) +{ + return rSet.Put(Item(wid, Cfg::get().value_or(std::move(defVal)))); +} +template <class Cfg, class Item> bool toSet_ifRW(SfxItemSet& rSet, TypedWhichId<Item> wid) +{ + return Cfg::isReadOnly() || toSet<Cfg>(rSet, wid); +} + +template <class Cfg, class Item> +void toCfg_ifSet(const SfxItemSet& rSet, TypedWhichId<Item> wid, + std::shared_ptr<comphelper::ConfigurationChanges> const& batch) +{ + if (const auto* pItem = rSet.GetItemIfSet(wid)) + Cfg::set(pItem->GetValue(), batch); +} +} + +void SfxApplication::GetOptions( SfxItemSet& rSet ) +{ + SfxWhichIter iter(rSet); + for (auto nWhich = iter.FirstWhich(); nWhich; nWhich = iter.NextWhich()) + { + bool bRet = false; + switch(nWhich) + { + case SID_ATTR_BACKUP: + bRet = true; + if (!officecfg::Office::Common::Save::Document::CreateBackup::isReadOnly()) + if (!rSet.Put( SfxBoolItem( SID_ATTR_BACKUP, + (officecfg::Office::Common::Save::Document::CreateBackup::get() && !comphelper::LibreOfficeKit::isActive()) ))) + bRet = false; + break; + case SID_ATTR_BACKUP_BESIDE_ORIGINAL: + bRet = toSet_ifRW<officecfg::Office::Common::Save::Document::BackupIntoDocumentFolder>( + rSet, SID_ATTR_BACKUP_BESIDE_ORIGINAL); + break; + case SID_ATTR_PRETTYPRINTING: + bRet = toSet_ifRW<officecfg::Office::Common::Save::Document::PrettyPrinting>( + rSet, SID_ATTR_PRETTYPRINTING); + break; + case SID_ATTR_WARNALIENFORMAT: + bRet = toSet_ifRW<officecfg::Office::Common::Save::Document::WarnAlienFormat>( + rSet, SID_ATTR_WARNALIENFORMAT); + break; + case SID_ATTR_AUTOSAVE: + bRet = toSet_ifRW<officecfg::Office::Recovery::AutoSave::Enabled>( + rSet, SID_ATTR_AUTOSAVE); + break; + case SID_ATTR_AUTOSAVEMINUTE: + bRet = toSet_ifRW<officecfg::Office::Recovery::AutoSave::TimeIntervall>( + rSet, SID_ATTR_AUTOSAVEMINUTE); + break; + case SID_ATTR_USERAUTOSAVE: + bRet = toSet_ifRW<officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled>( + rSet, SID_ATTR_USERAUTOSAVE); + break; + case SID_ATTR_DOCINFO: + bRet = toSet_ifRW<officecfg::Office::Common::Save::Document::EditProperty>( + rSet, SID_ATTR_DOCINFO); + break; + case SID_ATTR_QUICKLAUNCHER: + if ( ShutdownIcon::IsQuickstarterInstalled() ) + { + if ( rSet.Put( SfxBoolItem( SID_ATTR_QUICKLAUNCHER, + ShutdownIcon::GetAutostart() ) ) ) + bRet = true; + } + else + { + rSet.DisableItem( SID_ATTR_QUICKLAUNCHER ); + bRet = true; + } + break; + case SID_SAVEREL_INET: + bRet = toSet_ifRW<officecfg::Office::Common::Save::URL::Internet>( + rSet, SID_SAVEREL_INET); + break; + case SID_SAVEREL_FSYS: + bRet = toSet_ifRW<officecfg::Office::Common::Save::URL::FileSystem>( + rSet, SID_SAVEREL_FSYS); + break; + case SID_SECURE_URL: + bRet = true; + if (!SvtSecurityOptions::IsReadOnly(SvtSecurityOptions::EOption::SecureUrls)) + { + std::vector< OUString > seqURLs = SvtSecurityOptions::GetSecureURLs(); + + if( !rSet.Put( SfxStringListItem( SID_SECURE_URL, &seqURLs ) ) ) + bRet = false; + } + break; + case SID_INET_HTTP_PROXY_NAME: + bRet = toSet<officecfg::Inet::Settings::ooInetHTTPProxyName>( + rSet, SID_INET_HTTP_PROXY_NAME); + break; + case SID_INET_HTTP_PROXY_PORT: + bRet = toSet_withDefault<officecfg::Inet::Settings::ooInetHTTPProxyPort>( + rSet, SID_INET_HTTP_PROXY_PORT, 0); + break; + case SID_INET_NOPROXY: + bRet = toSet<officecfg::Inet::Settings::ooInetNoProxy>(rSet, SID_INET_NOPROXY); + break; + + default: + SAL_INFO( "sfx.appl", "W1:Wrong ID while getting Options!" ); + break; + } + SAL_WARN_IF(!bRet, "sfx.appl", "Putting options failed!"); + } +} + +void SfxApplication::SetOptions(const SfxItemSet &rSet) +{ + std::shared_ptr< comphelper::ConfigurationChanges > batch( + comphelper::ConfigurationChanges::create()); + + // Backup + toCfg_ifSet<officecfg::Office::Common::Save::Document::CreateBackup>( + rSet, SID_ATTR_BACKUP, batch); + + toCfg_ifSet<officecfg::Office::Common::Save::Document::BackupIntoDocumentFolder>( + rSet, SID_ATTR_BACKUP_BESIDE_ORIGINAL, batch); + + // PrettyPrinting + toCfg_ifSet<officecfg::Office::Common::Save::Document::PrettyPrinting>( + rSet, SID_ATTR_PRETTYPRINTING, batch); + + // WarnAlienFormat + toCfg_ifSet<officecfg::Office::Common::Save::Document::WarnAlienFormat>( + rSet, SID_ATTR_WARNALIENFORMAT, batch); + + // AutoSave + toCfg_ifSet<officecfg::Office::Recovery::AutoSave::Enabled>(rSet, SID_ATTR_AUTOSAVE, batch); + + // AutoSave-Time + toCfg_ifSet<officecfg::Office::Recovery::AutoSave::TimeIntervall>( + rSet, SID_ATTR_AUTOSAVEMINUTE, batch); + + // UserAutoSave + toCfg_ifSet<officecfg::Office::Recovery::AutoSave::UserAutoSaveEnabled>( + rSet, SID_ATTR_USERAUTOSAVE, batch); + + // DocInfo + toCfg_ifSet<officecfg::Office::Common::Save::Document::EditProperty>( + rSet, SID_ATTR_DOCINFO, batch); + + // HelpBalloons + toCfg_ifSet<officecfg::Office::Common::Help::ExtendedTip>(rSet, SID_HELPBALLOONS, batch); + + // HelpTips + toCfg_ifSet<officecfg::Office::Common::Help::Tip>(rSet, SID_HELPTIPS, batch); + + // SaveRelINet + toCfg_ifSet<officecfg::Office::Common::Save::URL::Internet>(rSet, SID_SAVEREL_INET, batch); + + // SaveRelFSys + toCfg_ifSet<officecfg::Office::Common::Save::URL::FileSystem>(rSet, SID_SAVEREL_FSYS, batch); + + // Undo-Count + if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet(SID_ATTR_UNDO_COUNT)) + { + sal_uInt16 nUndoCount = pItem->GetValue(); + officecfg::Office::Common::Undo::Steps::set(nUndoCount, batch); + + // To catch all Undo-Managers: Iterate over all Frames + for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst(); + pFrame; + pFrame = SfxViewFrame::GetNext(*pFrame) ) + { + // Get the Dispatcher of the Frames + SfxDispatcher *pDispat = pFrame->GetDispatcher(); + pDispat->Flush(); + + // Iterate over all SfxShells on the Dispatchers Stack + sal_uInt16 nIdx = 0; + for ( SfxShell *pSh = pDispat->GetShell(nIdx); + pSh; + ++nIdx, pSh = pDispat->GetShell(nIdx) ) + { + SfxUndoManager *pShUndoMgr = pSh->GetUndoManager(); + if ( pShUndoMgr ) + pShUndoMgr->SetMaxUndoActionCount( nUndoCount ); + } + } + } + + // Office autostart + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_ATTR_QUICKLAUNCHER)) + { + ShutdownIcon::SetAutostart( pItem->GetValue() ); + } + + toCfg_ifSet<officecfg::Inet::Settings::ooInetProxyType>(rSet, SID_INET_PROXY_TYPE, batch); + + toCfg_ifSet<officecfg::Inet::Settings::ooInetHTTPProxyName>( + rSet, SID_INET_HTTP_PROXY_NAME, batch); + toCfg_ifSet<officecfg::Inet::Settings::ooInetHTTPProxyPort>( + rSet, SID_INET_HTTP_PROXY_PORT, batch); + toCfg_ifSet<officecfg::Inet::Settings::ooInetNoProxy>(rSet, SID_INET_NOPROXY, batch); + + // Secure-Referrer + if ( const SfxStringListItem *pListItem = rSet.GetItemIfSet(SID_SECURE_URL)) + { + SvtSecurityOptions::SetSecureURLs( std::vector(pListItem->GetList()) ); + } + + // Store changed data + batch->commit(); +} + + +void SfxApplication::NotifyEvent( const SfxEventHint& rEventHint, bool bSynchron ) +{ + SfxObjectShell *pDoc = rEventHint.GetObjShell(); + if ( pDoc && ( pDoc->IsPreview() || !pDoc->Get_Impl()->bInitialized ) ) + return; + + if ( bSynchron ) + { + SAL_INFO_IF(!pDoc, "sfx.appl", "SfxEvent: " << rEventHint.GetEventName()); + Broadcast(rEventHint); + if ( pDoc ) + pDoc->Broadcast( rEventHint ); + } + else + new SfxEventAsyncer_Impl( rEventHint ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appchild.cxx b/sfx2/source/appl/appchild.cxx new file mode 100644 index 0000000000..246eb44135 --- /dev/null +++ b/sfx2/source/appl/appchild.cxx @@ -0,0 +1,66 @@ +/* -*- 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 <sfx2/app.hxx> +#include <appdata.hxx> +#include <workwin.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/module.hxx> +#include <sfx2/viewfrm.hxx> + + +void SfxApplication::RegisterChildWindow_Impl( SfxModule *pMod, const SfxChildWinFactory& rFact ) +{ + if ( pMod ) + { + pMod->RegisterChildWindow( rFact ); + return; + } + + for (size_t nFactory=0; nFactory<pImpl->maFactories.size(); ++nFactory) + { + if (rFact.nId == pImpl->maFactories[nFactory].nId) + { + pImpl->maFactories.erase( pImpl->maFactories.begin() + nFactory ); + } + } + + pImpl->maFactories.push_back( rFact ); +} + +SfxChildWinFactory* SfxApplication::GetChildWinFactoryById(sal_uInt16 nId) const +{ + for (auto& rFactory : pImpl->maFactories) + if (rFactory.nId == nId) + return &rFactory; + return nullptr; +} + +SfxWorkWindow* SfxApplication::GetWorkWindow_Impl(const SfxViewFrame *pFrame) const +{ + if ( pFrame ) + return pFrame->GetFrame().GetWorkWindow_Impl(); + else if ( pImpl->pViewFrame ) + return pImpl->pViewFrame->GetFrame().GetWorkWindow_Impl(); + else + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appdata.cxx b/sfx2/source/appl/appdata.cxx new file mode 100644 index 0000000000..819d703782 --- /dev/null +++ b/sfx2/source/appl/appdata.cxx @@ -0,0 +1,127 @@ +/* -*- 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_features.h> + +#include <appdata.hxx> +#include <sfx2/tbxctrl.hxx> +#include <sfx2/stbitem.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/doctempl.hxx> +#include <sfx2/module.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <sfx2/objsh.hxx> +#include <appbaslib.hxx> +#include <unoctitm.hxx> +#include <svl/svdde.hxx> + +#include <basic/basicmanagerrepository.hxx> +#include <basic/basmgr.hxx> +#include <basic/basrdll.hxx> + +using ::basic::BasicManagerRepository; +using ::basic::BasicManagerCreationListener; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::uno::XInterface; + +static BasicDLL* pBasic = nullptr; + +class SfxBasicManagerCreationListener : public ::basic::BasicManagerCreationListener +{ +private: + SfxAppData_Impl& m_rAppData; + +public: + explicit SfxBasicManagerCreationListener(SfxAppData_Impl& _rAppData) + : m_rAppData(_rAppData) + { + } + + virtual ~SfxBasicManagerCreationListener(); + + virtual void onBasicManagerCreated( const Reference< XModel >& _rxForDocument, BasicManager& _rBasicManager ) override; +}; + +SfxBasicManagerCreationListener::~SfxBasicManagerCreationListener() +{ +} + +void SfxBasicManagerCreationListener::onBasicManagerCreated( const Reference< XModel >& _rxForDocument, BasicManager& _rBasicManager ) +{ + if ( _rxForDocument == nullptr ) + m_rAppData.OnApplicationBasicManagerCreated( _rBasicManager ); +} + +SfxAppData_Impl::SfxAppData_Impl() + : pPool(nullptr) + , pProgress(nullptr) + , nDocModalMode(0) + , nRescheduleLocks(0) + , pBasicManager( new SfxBasicManagerHolder ) + , pBasMgrListener( new SfxBasicManagerCreationListener( *this ) ) + , pViewFrame( nullptr ) + , bDowning( true ) + , bInQuit( false ) + +{ + pBasic = new BasicDLL; + +#if HAVE_FEATURE_SCRIPTING + BasicManagerRepository::registerCreationListener( *pBasMgrListener ); +#endif +} + +SfxAppData_Impl::~SfxAppData_Impl() +{ + DeInitDDE(); + pBasicManager.reset(); + +#if HAVE_FEATURE_SCRIPTING + BasicManagerRepository::revokeCreationListener( *pBasMgrListener ); + pBasMgrListener.reset(); +#endif + + delete pBasic; +} + +SfxDocumentTemplates* SfxAppData_Impl::GetDocumentTemplates() +{ + if ( !pTemplates ) + pTemplates.emplace(); + else + pTemplates->ReInitFromComponent(); + return &*pTemplates; +} + +void SfxAppData_Impl::OnApplicationBasicManagerCreated( BasicManager& _rBasicManager ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) _rBasicManager; +#else + pBasicManager->reset( &_rBasicManager ); + + // global constants, additionally to the ones already added by createApplicationBasicManager: + // ThisComponent + Reference< XInterface > xCurrentComponent = SfxObjectShell::GetCurrentComponent(); + _rBasicManager.SetGlobalUNOConstant( "ThisComponent", css::uno::Any( xCurrentComponent ) ); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appdde.cxx b/sfx2/source/appl/appdde.cxx new file mode 100644 index 0000000000..2f013cfc63 --- /dev/null +++ b/sfx2/source/appl/appdde.cxx @@ -0,0 +1,567 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <config_features.h> +#include <rtl/character.hxx> +#include <rtl/malformeduriexception.hxx> +#include <rtl/uri.hxx> +#include <sot/exchange.hxx> +#include <svl/eitem.hxx> +#include <basic/sbstar.hxx> +#include <svl/stritem.hxx> +#include <svl/svdde.hxx> +#include <sfx2/lnkbase.hxx> +#include <sfx2/linkmgr.hxx> + +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/pathoptions.hxx> +#include <vcl/svapp.hxx> + +#include <sfx2/app.hxx> +#include <appdata.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/docfile.hxx> +#include <ucbhelper/content.hxx> +#include <comphelper/processfactory.hxx> + +#if defined(_WIN32) + +static OUString SfxDdeServiceName_Impl( const OUString& sIn ) +{ + OUStringBuffer sReturn(sIn.getLength()); + + for ( sal_uInt16 n = sIn.getLength(); n; --n ) + { + sal_Unicode cChar = sIn[n-1]; + if (rtl::isAsciiAlphanumeric(cChar)) + sReturn.append(cChar); + } + + return sReturn.makeStringAndClear(); +} + +namespace { + +class ImplDdeService : public DdeService +{ +public: + explicit ImplDdeService( const OUString& rNm ) + : DdeService( rNm ) + {} + virtual bool MakeTopic( const OUString& ); + + virtual OUString Topics(); + + virtual bool SysTopicExecute( const OUString* pStr ); +}; + + bool lcl_IsDocument( std::u16string_view rContent ) + { + using namespace com::sun::star; + + bool bRet = false; + INetURLObject aObj( rContent ); + DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" ); + + try + { + ::ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + bRet = aCnt.isDocument(); + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "" ); + } + + return bRet; + } +} + +bool ImplDdeService::MakeTopic( const OUString& rNm ) +{ + // Workaround for Event after Main() under OS/2 + // happens when exiting starts the App again + if ( !Application::IsInExecute() ) + return false; + + // The Topic rNm is sought, do we have it? + // First only loop over the ObjectShells to find those + // with the specific name: + bool bRet = false; + OUString sNm( rNm.toAsciiLowerCase() ); + SfxObjectShell* pShell = SfxObjectShell::GetFirst(); + while( pShell ) + { + OUString sTmp( pShell->GetTitle(SFX_TITLE_FULLNAME) ); + if( sNm == sTmp.toAsciiLowerCase() ) + { + SfxGetpApp()->AddDdeTopic( pShell ); + bRet = true; + break; + } + pShell = SfxObjectShell::GetNext( *pShell ); + } + + if( !bRet ) + { + bool abs; + OUString url; + try { + url = rtl::Uri::convertRelToAbs(SvtPathOptions().GetWorkPath(), rNm); + abs = true; + } catch (rtl::MalformedUriException &) { + abs = false; + } + if ( abs && lcl_IsDocument( url ) ) + { + // File exists? then try to load it: + SfxStringItem aName( SID_FILE_NAME, url ); + SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, true); + + SfxBoolItem aSilent(SID_SILENT, true); + const SfxPoolItemHolder aResult(SfxGetpApp()->GetDispatcher_Impl()->ExecuteList(SID_OPENDOC, + SfxCallMode::SYNCHRON, + { &aName, &aNewView, &aSilent })); + + if( auto const item = dynamic_cast< const SfxViewFrameItem *>(aResult.getItem()); + item && + item->GetFrame() && + nullptr != ( pShell = item->GetFrame()->GetObjectShell() ) ) + { + SfxGetpApp()->AddDdeTopic( pShell ); + bRet = true; + } + } + } + return bRet; +} + +OUString ImplDdeService::Topics() +{ + OUString sRet; + if( GetSysTopic() ) + sRet += GetSysTopic()->GetName(); + + SfxObjectShell* pShell = SfxObjectShell::GetFirst(); + while( pShell ) + { + if( SfxViewFrame::GetFirst( pShell ) ) + { + if( !sRet.isEmpty() ) + sRet += "\t"; + sRet += pShell->GetTitle(SFX_TITLE_FULLNAME); + } + pShell = SfxObjectShell::GetNext( *pShell ); + } + if( !sRet.isEmpty() ) + sRet += "\r\n"; + return sRet; +} + +bool ImplDdeService::SysTopicExecute( const OUString* pStr ) +{ + return SfxApplication::DdeExecute( *pStr ); +} +#endif + +class SfxDdeDocTopic_Impl : public DdeTopic +{ +#if defined(_WIN32) +public: + SfxObjectShell* pSh; + DdeData aData; + css::uno::Sequence< sal_Int8 > aSeq; + + explicit SfxDdeDocTopic_Impl( SfxObjectShell* pShell ) + : DdeTopic( pShell->GetTitle(SFX_TITLE_FULLNAME) ), pSh( pShell ) + {} + + virtual DdeData* Get( SotClipboardFormatId ) override; + virtual bool Put( const DdeData* ) override; + virtual bool Execute( const OUString* ) override; + virtual bool StartAdviseLoop() override; + virtual bool MakeItem( const OUString& rItem ) override; +#endif +}; + + +#if defined(_WIN32) + +namespace { + +/* [Description] + + Checks if 'rCmd' of the event 'rEvent' is (without '(') and then assemble + this data into a <ApplicationEvent>, which is then executed through + <Application::AppEvent()>. If 'rCmd' is the given event 'rEvent', then + TRUE is returned, otherwise FALSE. + + [Example] + + rCmd = "Open(\"d:\doc\doc.sdw\")" + rEvent = "Open" +*/ +bool SfxAppEvent_Impl( const OUString& rCmd, std::u16string_view rEvent, + ApplicationEvent::Type eType ) +{ + OUString sEvent(OUString::Concat(rEvent) + "("); + if (rCmd.startsWithIgnoreAsciiCase(sEvent)) + { + sal_Int32 start = sEvent.getLength(); + if ( rCmd.getLength() - start >= 2 ) + { + // Transform into the ApplicationEvent Format + //TODO: I /assume/ that rCmd should match the syntax of + // <http://msdn.microsoft.com/en-us/library/ms648995.aspx> + // "WM_DDE_EXECUTE message" but does not (handle commands enclosed + // in [...]; handle commas separating multiple arguments; handle + // double "", ((, )), [[, ]] in quoted arguments); see also the mail + // thread starting at <http://lists.freedesktop.org/archives/ + // libreoffice/2013-July/054779.html> "DDE on Windows." + std::vector<OUString> aData; + for ( sal_Int32 n = start; n < rCmd.getLength() - 1; ) + { + // Resiliently read arguments either starting with " and + // spanning to the next " (if any; TODO: do we need to undo any + // escaping within the string?) or with neither " nor SPC and + // spanning to the next SPC (if any; TODO: is this from not + // wrapped in "..." relevant? it would have been parsed by the + // original code even if that was only by accident, so I left it + // in), with runs of SPCs treated like single ones: + switch ( rCmd[n] ) + { + case '"': + { + sal_Int32 i = rCmd.indexOf('"', ++n); + if (i < 0 || i > rCmd.getLength() - 1) { + i = rCmd.getLength() - 1; + } + aData.push_back(rCmd.copy(n, i - n)); + n = i + 1; + break; + } + case ' ': + ++n; + break; + default: + { + sal_Int32 i = rCmd.indexOf(' ', n); + if (i < 0 || i > rCmd.getLength() - 1) { + i = rCmd.getLength() - 1; + } + aData.push_back(rCmd.copy(n, i - n)); + n = i + 1; + break; + } + } + } + + GetpApp()->AppEvent( ApplicationEvent(eType, std::move(aData)) ); + return true; + } + } + + return false; +} + +} + +/* Description] + + This method can be overridden by application developers, to receive + DDE-commands directed to their SfxApplication subclass. + + The base implementation understands the API functionality of the + relevant SfxApplication subclass in BASIC syntax. Return values can + not be transferred, unfortunately. +*/ +bool SfxApplication::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax +{ + // Print or Open-Event? + if ( !( SfxAppEvent_Impl( rCmd, u"Print", ApplicationEvent::Type::Print ) || + SfxAppEvent_Impl( rCmd, u"Open", ApplicationEvent::Type::Open ) ) ) + { + // all others are BASIC + StarBASIC* pBasic = GetBasic(); + DBG_ASSERT( pBasic, "Where is the Basic???" ); + SbxVariable* pRet = pBasic->Execute( rCmd ); + if( !pRet ) + { + SbxBase::ResetError(); + return false; + } + } + return true; +} + +/* [Description] + + This method can be overridden by application developers, to receive + DDE-commands directed to the their SfxApplication subclass. + + The base implementation does nothing and returns 0. +*/ +bool SfxObjectShell::DdeExecute( const OUString& rCmd ) // Expressed in our BASIC-Syntax +{ +#if !HAVE_FEATURE_SCRIPTING + (void) rCmd; +#else + StarBASIC* pBasic = GetBasic(); + DBG_ASSERT( pBasic, "Where is the Basic???" ) ; + SbxVariable* pRet = pBasic->Execute( rCmd ); + if( !pRet ) + { + SbxBase::ResetError(); + return false; + } +#endif + return true; +} + +/* [Description] + + This method can be overridden by application developers, to receive + DDE-data-requests directed to their SfxApplication subclass. + + The base implementation provides no data and returns false. +*/ +bool SfxObjectShell::DdeGetData( const OUString&, // the Item to be addressed + const OUString&, // in: Format + css::uno::Any& )// out: requested data +{ + return false; +} + + +/* [Description] + + This method can be overridden by application developers, to receive + DDE-data directed to their SfxApplication subclass. + + The base implementation is not receiving any data and returns false. +*/ +bool SfxObjectShell::DdeSetData( const OUString&, // the Item to be addressed + const OUString&, // in: Format + const css::uno::Any& )// out: requested data +{ + return false; +} + +#endif + +/* [Description] + + This method can be overridden by application developers, to establish + a DDE-hotlink to their SfxApplication subclass. + + The base implementation is not generate a link and returns 0. +*/ +::sfx2::SvLinkSource* SfxObjectShell::DdeCreateLinkSource( const OUString& ) // the Item to be addressed +{ + return nullptr; +} + +void SfxObjectShell::ReconnectDdeLink(SfxObjectShell& /*rServer*/) +{ +} + +void SfxObjectShell::ReconnectDdeLinks(SfxObjectShell& rServer) +{ + SfxObjectShell* p = GetFirst(nullptr, false); + while (p) + { + if (&rServer != p) + p->ReconnectDdeLink(rServer); + + p = GetNext(*p, nullptr, false); + } +} + +bool SfxApplication::InitializeDde() +{ + int nError = 0; +#if defined(_WIN32) + DBG_ASSERT( !pImpl->pDdeService, + "Dde can not be initialized multiple times" ); + + pImpl->pDdeService.reset(new ImplDdeService( Application::GetAppName() )); + nError = pImpl->pDdeService->GetError(); + if( !nError ) + { + // we certainly want to support RTF! + pImpl->pDdeService->AddFormat( SotClipboardFormatId::RTF ); + pImpl->pDdeService->AddFormat( SotClipboardFormatId::RICHTEXT ); + + // Config path as a topic because of multiple starts + INetURLObject aOfficeLockFile( SvtPathOptions().GetUserConfigPath() ); + aOfficeLockFile.insertName( u"soffice.lck" ); + OUString aService( SfxDdeServiceName_Impl( + aOfficeLockFile.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) ) ); + aService = aService.toAsciiUpperCase(); + pImpl->pDdeService2.reset( new ImplDdeService( aService )); + pImpl->pTriggerTopic.reset(new SfxDdeTriggerTopic_Impl); + pImpl->pDdeService2->AddTopic( *pImpl->pTriggerTopic ); + } +#endif + return !nError; +} + +void SfxAppData_Impl::DeInitDDE() +{ + pTriggerTopic.reset(); + pDdeService2.reset(); + maDocTopics.clear(); + pDdeService.reset(); +} + +#if defined(_WIN32) +void SfxApplication::AddDdeTopic( SfxObjectShell* pSh ) +{ + //OV: DDE is disconnected in server mode! + if( pImpl->maDocTopics.empty() ) + return; + + // prevent double submit + OUString sShellNm; + bool bFnd = false; + for (size_t n = pImpl->maDocTopics.size(); n;) + { + if( pImpl->maDocTopics[ --n ]->pSh == pSh ) + { + // If the document is untitled, is still a new Topic is created! + if( !bFnd ) + { + bFnd = true; + sShellNm = pSh->GetTitle(SFX_TITLE_FULLNAME).toAsciiLowerCase(); + } + OUString sNm( pImpl->maDocTopics[ n ]->GetName() ); + if( sShellNm == sNm.toAsciiLowerCase() ) + return ; + } + } + + SfxDdeDocTopic_Impl *const pTopic = new SfxDdeDocTopic_Impl(pSh); + pImpl->maDocTopics.push_back(pTopic); + pImpl->pDdeService->AddTopic( *pTopic ); +} +#endif + +void SfxApplication::RemoveDdeTopic( SfxObjectShell const * pSh ) +{ +#if defined(_WIN32) + //OV: DDE is disconnected in server mode! + if( pImpl->maDocTopics.empty() ) + return; + + for (size_t n = pImpl->maDocTopics.size(); n; ) + { + SfxDdeDocTopic_Impl *const pTopic = pImpl->maDocTopics[ --n ]; + if (pTopic->pSh == pSh) + { + pImpl->pDdeService->RemoveTopic( *pTopic ); + delete pTopic; + pImpl->maDocTopics.erase( pImpl->maDocTopics.begin() + n ); + } + } +#else + (void) pSh; +#endif +} + +const DdeService* SfxApplication::GetDdeService() const +{ + return pImpl->pDdeService.get(); +} + +DdeService* SfxApplication::GetDdeService() +{ + return pImpl->pDdeService.get(); +} + +#if defined(_WIN32) + +DdeData* SfxDdeDocTopic_Impl::Get(SotClipboardFormatId nFormat) +{ + OUString sMimeType( SotExchange::GetFormatMimeType( nFormat )); + css::uno::Any aValue; + bool bRet = pSh->DdeGetData( GetCurItem(), sMimeType, aValue ); + if( bRet && aValue.hasValue() && ( aValue >>= aSeq ) ) + { + aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat ); + return &aData; + } + aSeq.realloc( 0 ); + return nullptr; +} + +bool SfxDdeDocTopic_Impl::Put( const DdeData* pData ) +{ + aSeq = css::uno::Sequence< sal_Int8 >( + static_cast<sal_Int8 const *>(pData->getData()), pData->getSize() ); + bool bRet; + if( aSeq.getLength() ) + { + css::uno::Any aValue; + aValue <<= aSeq; + OUString sMimeType( SotExchange::GetFormatMimeType( pData->GetFormat() )); + bRet = pSh->DdeSetData( GetCurItem(), sMimeType, aValue ); + } + else + bRet = false; + return bRet; +} + +bool SfxDdeDocTopic_Impl::Execute( const OUString* pStr ) +{ + return pStr && pSh->DdeExecute( *pStr ); +} + +bool SfxDdeDocTopic_Impl::MakeItem( const OUString& rItem ) +{ + AddItem( DdeItem( rItem ) ); + return true; +} + +bool SfxDdeDocTopic_Impl::StartAdviseLoop() +{ + bool bRet = false; + ::sfx2::SvLinkSource* pNewObj = pSh->DdeCreateLinkSource( GetCurItem() ); + if( pNewObj ) + { + // then we also establish a corresponding SvBaseLink + OUString sNm, sTmp( Application::GetAppName() ); + ::sfx2::MakeLnkName( sNm, &sTmp, pSh->GetTitle(SFX_TITLE_FULLNAME), GetCurItem() ); + new ::sfx2::SvBaseLink( sNm, sfx2::SvBaseLinkObjectType::DdeExternal, pNewObj ); + bRet = true; + } + return bRet; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appdispatchprovider.cxx b/sfx2/source/appl/appdispatchprovider.cxx new file mode 100644 index 0000000000..363aa391d5 --- /dev/null +++ b/sfx2/source/appl/appdispatchprovider.cxx @@ -0,0 +1,231 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/frame/XAppDispatchProvider.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/DispatchDescriptor.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/util/URL.hpp> + +#include <comphelper/sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/sfxbasecontroller.hxx> +#include <unoctitm.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::uno; + +namespace { + +class SfxAppDispatchProvider : public ::cppu::WeakImplHelper< css::frame::XAppDispatchProvider, + css::lang::XServiceInfo, + css::lang::XInitialization > +{ + css::uno::WeakReference < css::frame::XFrame > m_xFrame; +public: + SfxAppDispatchProvider() {} + + virtual void SAL_CALL initialize( + css::uno::Sequence<css::uno::Any> const & aArguments) override; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL queryDispatch( + const css::util::URL& aURL, const OUString& sTargetFrameName, + sal_Int32 eSearchFlags ) override; + + virtual css::uno::Sequence< css::uno::Reference < css::frame::XDispatch > > SAL_CALL queryDispatches( + const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescriptor ) override; + + virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSupportedCommandGroups() override; + + virtual css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL getConfigurableDispatchInformation( sal_Int16 ) override; +}; + +void SfxAppDispatchProvider::initialize( + css::uno::Sequence<css::uno::Any> const & aArguments) +{ + css::uno::Reference<css::frame::XFrame> f; + if (aArguments.getLength() != 1 || !(aArguments[0] >>= f)) { + throw css::lang::IllegalArgumentException( + "SfxAppDispatchProvider::initialize expects one XFrame argument", + getXWeak(), 0); + } + m_xFrame = f; +} + +OUString SAL_CALL SfxAppDispatchProvider::getImplementationName() +{ + return "com.sun.star.comp.sfx2.AppDispatchProvider"; +} + +sal_Bool SAL_CALL SfxAppDispatchProvider::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL SfxAppDispatchProvider::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ProtocolHandler", "com.sun.star.frame.AppDispatchProvider" }; +} + +Reference < XDispatch > SAL_CALL SfxAppDispatchProvider::queryDispatch( + const util::URL& aURL, + const OUString& /*sTargetFrameName*/, + sal_Int32 /*eSearchFlags*/ ) +{ + SolarMutexGuard guard; + + bool bMasterCommand( false ); + Reference < XDispatch > xDisp; + const SfxSlot* pSlot = nullptr; + SfxApplication* pApp = SfxGetpApp(); + if ( !pApp ) + return xDisp; + SfxDispatcher* pAppDisp = pApp->GetAppDispatcher_Impl(); + if ( aURL.Protocol == "slot:" || aURL.Protocol == "commandId:" ) + { + sal_uInt16 nId = static_cast<sal_uInt16>(aURL.Path.toInt32()); + SfxShell* pShell; + pAppDisp->GetShellAndSlot_Impl( nId, &pShell, &pSlot, true, true ); + } + else if ( aURL.Protocol == ".uno:" ) + { + // Support ".uno" commands. Map commands to slotid + bMasterCommand = SfxOfficeDispatch::IsMasterUnoCommand( aURL ); + if ( bMasterCommand ) + pSlot = pAppDisp->GetSlot( SfxOfficeDispatch::GetMasterUnoCommand( aURL ) ); + else + pSlot = pAppDisp->GetSlot( aURL.Main ); + } + + if ( pSlot ) + { + rtl::Reference<SfxOfficeDispatch> pDispatch = new SfxOfficeDispatch( pAppDisp, pSlot, aURL ) ; + pDispatch->SetFrame(m_xFrame); + pDispatch->SetMasterUnoCommand( bMasterCommand ); + xDisp = pDispatch; + } + + return xDisp; +} + +Sequence< Reference < XDispatch > > SAL_CALL SfxAppDispatchProvider::queryDispatches( const Sequence < DispatchDescriptor >& seqDescriptor ) +{ + sal_Int32 nCount = seqDescriptor.getLength(); + uno::Sequence< uno::Reference < frame::XDispatch > > lDispatcher(nCount); + std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(), + [this](const DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return lDispatcher; +} + +Sequence< sal_Int16 > SAL_CALL SfxAppDispatchProvider::getSupportedCommandGroups() +{ + SolarMutexGuard aGuard; + + std::vector< sal_Int16 > aGroupList; + SfxSlotPool& rAppSlotPool = SfxGetpApp()->GetAppSlotPool_Impl(); + + const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG ); + + // Select group ( group 0 is internal ) + for (sal_uInt16 i=0; i< rAppSlotPool.GetGroupCount(); ++i) + { + rAppSlotPool.SeekGroup(i); + const SfxSlot* pSfxSlot = rAppSlotPool.FirstSlot(); + while ( pSfxSlot ) + { + if ( pSfxSlot->GetMode() & nMode ) + { + sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() ); + aGroupList.push_back( nCommandGroup ); + break; + } + pSfxSlot = rAppSlotPool.NextSlot(); + } + } + + return comphelper::containerToSequence( aGroupList ); +} + +Sequence< frame::DispatchInformation > SAL_CALL SfxAppDispatchProvider::getConfigurableDispatchInformation( sal_Int16 nCmdGroup ) +{ + std::vector< frame::DispatchInformation > aCmdVector; + + SolarMutexGuard aGuard; + SfxSlotPool& rAppSlotPool = SfxGetpApp()->GetAppSlotPool_Impl(); + + const SfxSlotMode nMode( SfxSlotMode::TOOLBOXCONFIG|SfxSlotMode::ACCELCONFIG|SfxSlotMode::MENUCONFIG ); + + // Select group ( group 0 is internal ) + for (sal_uInt16 i=0; i< rAppSlotPool.GetGroupCount(); ++i) + { + rAppSlotPool.SeekGroup(i); + const SfxSlot* pSfxSlot = rAppSlotPool.FirstSlot(); + if ( pSfxSlot ) + { + sal_Int16 nCommandGroup = MapGroupIDToCommandGroup( pSfxSlot->GetGroupId() ); + if ( nCommandGroup == nCmdGroup ) + { + while ( pSfxSlot ) + { + if ( pSfxSlot->GetMode() & nMode ) + { + frame::DispatchInformation aCmdInfo; + aCmdInfo.Command = pSfxSlot->GetCommand(); + aCmdInfo.GroupId = nCommandGroup; + aCmdVector.push_back( aCmdInfo ); + } + pSfxSlot = rAppSlotPool.NextSlot(); + } + } + } + } + + return comphelper::containerToSequence( aCmdVector ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_sfx2_AppDispatchProvider_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SfxAppDispatchProvider); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appinit.cxx b/sfx2/source/appl/appinit.cxx new file mode 100644 index 0000000000..52afe118ec --- /dev/null +++ b/sfx2/source/appl/appinit.cxx @@ -0,0 +1,233 @@ +/* -*- 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_features.h> + +#include <sfx2/app.hxx> +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <basic/sbdef.hxx> +#include <tools/svlibrary.h> +#include <svtools/soerr.hxx> +#include <unotools/configmgr.hxx> +#include <svtools/ehdl.hxx> +#include <comphelper/processfactory.hxx> +#include <osl/module.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <vcl/specialchars.hxx> +#include <vcl/help.hxx> +#include <vcl/svapp.hxx> + +#include <unoctitm.hxx> +#include <appdata.hxx> +#include <sfx2/dispatch.hxx> +#include <nochaos.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star; + +namespace { + +class SfxTerminateListener_Impl : public ::cppu::WeakImplHelper< XTerminateListener, XServiceInfo > +{ +public: + + // XTerminateListener + virtual void SAL_CALL queryTermination( const EventObject& aEvent ) override; + virtual void SAL_CALL notifyTermination( const EventObject& aEvent ) override; + virtual void SAL_CALL disposing( const EventObject& Source ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +} + +void SAL_CALL SfxTerminateListener_Impl::disposing( const EventObject& ) +{ +} + +void SAL_CALL SfxTerminateListener_Impl::queryTermination( const EventObject& ) +{ +} + +void SAL_CALL SfxTerminateListener_Impl::notifyTermination( const EventObject& aEvent ) +{ + Reference< XDesktop > xDesktop( aEvent.Source, UNO_QUERY ); + if( xDesktop.is() ) + xDesktop->removeTerminateListener( this ); + + SolarMutexGuard aGuard; + utl::ConfigManager::storeConfigItems(); + + SfxApplication* pApp = SfxGetpApp(); + pApp->Broadcast( SfxHint( SfxHintId::Deinitializing ) ); + pApp->Get_Impl()->mxAppDispatch->ReleaseAll(); + pApp->Get_Impl()->mxAppDispatch.clear(); + + css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + css::uno::Reference< css::document::XDocumentEventListener > xGlobalBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_QUERY_THROW); + + css::document::DocumentEvent aEvent2; + aEvent2.EventName = "OnCloseApp"; + xGlobalBroadcaster->documentEventOccured(aEvent2); + + delete pApp; + Application::Quit(); +} + +OUString SAL_CALL SfxTerminateListener_Impl::getImplementationName() +{ + return "com.sun.star.comp.sfx2.SfxTerminateListener"; +} + +sal_Bool SAL_CALL SfxTerminateListener_Impl::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +Sequence< OUString > SAL_CALL SfxTerminateListener_Impl::getSupportedServiceNames() +{ + // Note: That service does not really exists .-) + // But this implementation is not thought to be registered really within our service.rdb. + // At least we need the implementation name only to identify these service at the global desktop instance. + // The desktop must know, which listener will terminate the SfxApplication in real ! + // It must call this special listener as last one ... otherwise we shutdown the SfxApplication BEFORE other listener + // can react ... + return { "com.sun.star.frame.TerminateListener" }; +} + + +typedef bool (*PFunc_getSpecialCharsForEdit)(weld::Widget* i_pParent, const vcl::Font& i_rFont, OUString& o_rOutString); + + +// Lazy binding of the GetSpecialCharsForEdit function as it resides in +// a library above us. + + +#ifndef DISABLE_DYNLOADING + +extern "C" { static void thisModule() {} } + +#else + +extern "C" bool GetSpecialCharsForEdit(weld::Widget* i_pParent, const vcl::Font& i_rFont, OUString& o_rOutString); + +#endif + +static OUString SfxGetSpecialCharsForEdit(weld::Widget* pParent, const vcl::Font& rFont) +{ + static const PFunc_getSpecialCharsForEdit pfunc_getSpecialCharsForEdit = [] { + PFunc_getSpecialCharsForEdit pfunc = nullptr; +#ifndef DISABLE_DYNLOADING + osl::Module aMod; + aMod.loadRelative(&thisModule, SVLIBRARY("cui")); + + // get symbol + pfunc = reinterpret_cast<PFunc_getSpecialCharsForEdit>(aMod.getFunctionSymbol("GetSpecialCharsForEdit")); + DBG_ASSERT( pfunc, "GetSpecialCharsForEdit() not found!" ); + aMod.release(); +#else + pfunc = GetSpecialCharsForEdit; +#endif + return pfunc; + }(); + + OUString aRet; + if ( pfunc_getSpecialCharsForEdit ) + { + SolarMutexGuard aGuard; + (*pfunc_getSpecialCharsForEdit)( pParent, rFont, aRet ); + } + return aRet; +} + + +void SfxApplication::Initialize_Impl() +{ +#ifdef TLX_VALIDATE + StgIo::SetErrorLink( LINK( this, SfxStorageErrHdl, Error ) ); +#endif + + Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() ); + xDesktop->addTerminateListener( new SfxTerminateListener_Impl ); + + pImpl->mxAppDispatch = new SfxStatusDispatcher; + + // SV-Look + Help::EnableContextHelp(); + Help::EnableExtHelp(); + + pImpl->m_pToolsErrorHdl.emplace( + RID_ERRHDL, ErrCodeArea::Io, ErrCodeArea::Vcl); + + pImpl->m_pSoErrorHdl.emplace( + RID_SO_ERROR_HANDLER, ErrCodeArea::So, ErrCodeArea::So, SvtResLocale()); +#if HAVE_FEATURE_SCRIPTING + pImpl->m_pSbxErrorHdl.emplace( + RID_BASIC_START, ErrCodeArea::Sbx, ErrCodeArea::Sbx, BasResLocale()); +#endif + + if (!utl::ConfigManager::IsFuzzing()) + { + SolarMutexGuard aGuard; + //ensure instantiation of listener that manages the internal recently-used + //list + pImpl->mxAppPickList.emplace(*this); + } + + DBG_ASSERT( !pImpl->pAppDispat, "AppDispatcher already exists" ); + pImpl->pAppDispat.emplace(); + pImpl->pSlotPool.emplace(); + + Registrations_Impl(); + + // initialize the subclass + pImpl->bDowning = false; + + // get CHAOS item pool... + pImpl->pPool = NoChaos::GetItemPool(); + SetPool( pImpl->pPool ); + + if ( pImpl->bDowning ) + return; + + // build the app dispatcher + pImpl->pAppDispat->Push(*this); + pImpl->pAppDispat->Flush(); + pImpl->pAppDispat->DoActivate_Impl( true ); + + { + SolarMutexGuard aGuard; + // Set special characters callback on vcl edit control + vcl::SetGetSpecialCharsFunction(&SfxGetSpecialCharsForEdit); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appmain.cxx b/sfx2/source/appl/appmain.cxx new file mode 100644 index 0000000000..5f24bbc8aa --- /dev/null +++ b/sfx2/source/appl/appmain.cxx @@ -0,0 +1,38 @@ +/* -*- 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 <svl/urihelper.hxx> + +#include <appdata.hxx> +#include <sfx2/app.hxx> +#include <sfx2/fcontnr.hxx> + + +SfxFilterMatcher& SfxApplication::GetFilterMatcher() +{ + if( !pImpl->pMatcher ) + { + pImpl->pMatcher.emplace(); + URIHelper::SetMaybeFileHdl( LINK( + &*pImpl->pMatcher, SfxFilterMatcher, MaybeFileHdl_Impl ) ); + } + return *pImpl->pMatcher; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appmisc.cxx b/sfx2/source/appl/appmisc.cxx new file mode 100644 index 0000000000..948a1de40f --- /dev/null +++ b/sfx2/source/appl/appmisc.cxx @@ -0,0 +1,211 @@ +/* -*- 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_folders.h> +#include <ucbhelper/content.hxx> + +#include <vcl/canvastools.hxx> +#include <vcl/vectorgraphicdata.hxx> +#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/graphic/Primitive2DTools.hpp> +#include <com/sun/star/uno/Reference.h> +#include <unotools/configmgr.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> +#include <rtl/bootstrap.hxx> +#include <svl/stritem.hxx> +#include <tools/urlobj.hxx> + +#include <sfx2/app.hxx> +#include <appdata.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objface.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> + +#define ShellClass_SfxApplication +#include <sfxslots.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +SFX_IMPL_INTERFACE(SfxApplication,SfxShell) + +void SfxApplication::InitInterface_Impl() +{ + GetStaticInterface()->RegisterStatusBar(StatusBarId::GenericStatusBar); + + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_0); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_1); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_2); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_3); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_4); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_5); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_6); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_7); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_8); + GetStaticInterface()->RegisterChildWindow(SID_DOCKWIN_9); +} + +/** Returns the running SfxProgress for the entire application or 0 if + none is running for the entire application. + + [Cross-reference] + + <SfxProgress::GetActiveProgress(SfxViewFrame*)> + <SfxViewFrame::GetProgress()const> +*/ +SfxProgress* SfxApplication::GetProgress() const +{ + return pImpl->pProgress; +} + +SfxModule* SfxApplication::GetModule_Impl() +{ + SfxModule* pModule = SfxModule::GetActiveModule(); + if ( !pModule ) + pModule = SfxModule::GetActiveModule( SfxViewFrame::GetFirst( nullptr, false ) ); + if( pModule ) + return pModule; + else + { + OSL_FAIL( "No module!" ); + return nullptr; + } +} + +bool SfxApplication::IsDowning() const { return pImpl->bDowning; } +SfxDispatcher* SfxApplication::GetAppDispatcher_Impl() { return &*pImpl->pAppDispat; } +SfxSlotPool& SfxApplication::GetAppSlotPool_Impl() const { return *pImpl->pSlotPool; } + +static bool FileExists( const INetURLObject& rURL ) +{ + bool bRet = false; + + if( rURL.GetProtocol() != INetProtocol::NotValid ) + { + try + { + ::ucbhelper::Content aCnt( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + OUString aTitle; + + aCnt.getPropertyValue("Title") >>= aTitle; + bRet = ( !aTitle.isEmpty() ); + } + catch(const Exception&) + { + return false; + } + } + + return bRet; +} + +bool SfxApplication::loadBrandSvg(const char *pName, BitmapEx &rBitmap, int nWidth) +{ + // Load from disk + + OUString aBaseName = "/" + OUString::createFromAscii( pName ); + + OUString uri = "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER + aBaseName + ".svg"; + rtl::Bootstrap::expandMacros( uri ); + + INetURLObject aObj( uri ); + if ( !FileExists(aObj) ) + return false; + + VectorGraphicData aVectorGraphicData(aObj.PathToFileName(), VectorGraphicDataType::Svg); + + // transform into [0,0,width,width*aspect] std dimensions + + basegfx::B2DRange aRange(aVectorGraphicData.getRange()); + const double fAspectRatio( + aRange.getHeight() == 0.0 ? 1.0 : aRange.getWidth()/aRange.getHeight()); + basegfx::B2DHomMatrix aTransform( + basegfx::utils::createTranslateB2DHomMatrix( + -aRange.getMinX(), + -aRange.getMinY())); + aTransform.scale( + aRange.getWidth() == 0.0 ? 1.0 : nWidth / aRange.getWidth(), + (aRange.getHeight() == 0.0 + ? 1.0 : nWidth / fAspectRatio / aRange.getHeight())); + const drawinglayer::primitive2d::Primitive2DReference xTransformRef( + new drawinglayer::primitive2d::TransformPrimitive2D( + aTransform, + drawinglayer::primitive2d::Primitive2DContainer(aVectorGraphicData.getPrimitive2DSequence()))); + + // UNO dance to render from drawinglayer + + uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext()); + + try + { + const uno::Reference< graphic::XPrimitive2DRenderer > xPrimitive2DRenderer = + graphic::Primitive2DTools::create( xContext ); + + // cancel out rasterize's mm2pixel conversion + // see fFactor100th_mmToInch in + // drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx + const double fFakeDPI=2.54 * 1000.0; + + geometry::RealRectangle2D aRealRect( + 0, 0, + nWidth, nWidth / fAspectRatio); + + const uno::Reference< rendering::XBitmap > xBitmap( + xPrimitive2DRenderer->rasterize( + drawinglayer::primitive2d::Primitive2DContainer{xTransformRef}.toSequence(), + uno::Sequence< beans::PropertyValue >(), + fFakeDPI, + fFakeDPI, + aRealRect, + 500000)); + + if(xBitmap.is()) + { + const uno::Reference< rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW); + rBitmap = vcl::unotools::bitmapExFromXBitmap(xIntBmp); + return true; + } + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sfx.appl", "Got no graphic::XPrimitive2DRenderer (!)" ); + } + return false; +} + +/** loads the application logo as used in the impress slideshow pause screen */ +BitmapEx SfxApplication::GetApplicationLogo(tools::Long nWidth) +{ + BitmapEx aBitmap; + SfxApplication::loadBrandSvg("shell/about", aBitmap, nWidth); + return aBitmap; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx new file mode 100644 index 0000000000..4e1b06e522 --- /dev/null +++ b/sfx2/source/appl/appopen.cxx @@ -0,0 +1,1154 @@ +/* -*- 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 <com/sun/star/uno/Reference.h> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/system/SystemShellExecuteException.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <com/sun/star/task/ErrorCodeRequest.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <rtl/ustring.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/string.hxx> +#include <comphelper/synchronousdispatch.hxx> + +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <sfx2/doctempl.hxx> +#include <svtools/sfxecode.hxx> +#include <preventduplicateinteraction.hxx> +#include <svtools/ehdl.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/securityoptions.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/extendedsecurityoptions.hxx> +#include <comphelper/docpasswordhelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/objitem.hxx> +#include <sfx2/objsh.hxx> +#include <svl/slstitm.hxx> +#include <appopen.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxresid.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/strings.hrc> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxuno.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/templatedlg.hxx> +#include <sfx2/sfxsids.hrc> +#include <o3tl/string_view.hxx> +#include <openuriexternally.hxx> + +#include <officecfg/Office/ProtocolHandler.hxx> +#include <officecfg/Office/Security.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::container; +using namespace ::cppu; +using namespace ::sfx2; + +void SetTemplate_Impl( std::u16string_view rFileName, + const OUString &rLongName, + SfxObjectShell *pDoc) +{ + // write TemplateName to DocumentProperties of document + // TemplateDate stays as default (=current date) + pDoc->ResetFromTemplate( rLongName, rFileName ); +} + +namespace { + +class SfxDocPasswordVerifier : public ::comphelper::IDocPasswordVerifier +{ +public: + explicit SfxDocPasswordVerifier(SfxMedium& rMedium) + : m_rMedium(rMedium) + , mxStorage(rMedium.GetStorage()) + { + } + + virtual ::comphelper::DocPasswordVerifierResult + verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData ) override; + virtual ::comphelper::DocPasswordVerifierResult + verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData ) override; + +private: + SfxMedium & m_rMedium; + Reference< embed::XStorage > mxStorage; +}; + +} + +::comphelper::DocPasswordVerifierResult SfxDocPasswordVerifier::verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData ) +{ + o_rEncryptionData = ::comphelper::OStorageHelper::CreatePackageEncryptionData( rPassword ); + return verifyEncryptionData( o_rEncryptionData ); +} + + +::comphelper::DocPasswordVerifierResult SfxDocPasswordVerifier::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData ) +{ + ::comphelper::DocPasswordVerifierResult eResult = ::comphelper::DocPasswordVerifierResult::WrongPassword; + try + { + // check the encryption data + // if the data correct is the stream will be opened successfully + // and immediately closed + ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( mxStorage, rEncryptionData ); + + // for new ODF encryption, try to extract the encrypted inner package + // (it will become the SfxObjectShell storage) + if (!m_rMedium.TryEncryptedInnerPackage(mxStorage)) + { // ... old ODF encryption: + mxStorage->openStreamElement( + "content.xml", + embed::ElementModes::READ | embed::ElementModes::NOCREATE ); + } + + // no exception -> success + eResult = ::comphelper::DocPasswordVerifierResult::OK; + } + catch( const packages::WrongPasswordException& ) + { + eResult = ::comphelper::DocPasswordVerifierResult::WrongPassword; + } + catch( const uno::Exception& ) + { + // unknown error, report it as wrong password + // TODO/LATER: we need an additional way to report unknown problems in this case + eResult = ::comphelper::DocPasswordVerifierResult::WrongPassword; + } + return eResult; +} + + +ErrCode CheckPasswd_Impl +( + SfxObjectShell* pDoc, + SfxMedium* pFile // the Medium and its Password should be obtained +) + +/* [Description] + + Ask for the password for a medium, only works if it concerns storage. + If the password flag is set in the Document Info, then the password is + requested through a user dialogue and the set at the Set of the medium. + If the set does not exist the it is created. +*/ +{ + ErrCode nRet = ERRCODE_NONE; + + if( !pFile->GetFilter() || pFile->IsStorage() ) + { + uno::Reference< embed::XStorage > xStorage = pFile->GetStorage(); + if( xStorage.is() ) + { + uno::Reference< beans::XPropertySet > xStorageProps( xStorage, uno::UNO_QUERY ); + if ( xStorageProps.is() ) + { + bool bIsEncrypted = false; + uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProperties; + try { + xStorageProps->getPropertyValue("HasEncryptedEntries") + >>= bIsEncrypted; + xStorageProps->getPropertyValue("EncryptionGpGProperties") + >>= aGpgProperties; + } catch( uno::Exception& ) + { + // TODO/LATER: + // the storage either has no encrypted elements or it's just + // does not allow to detect it, probably it should be implemented later + } + + if ( bIsEncrypted ) + { + css::uno::Reference<css::awt::XWindow> xWin(pDoc ? pDoc->GetDialogParent(pFile) : nullptr); + if (xWin) + xWin->setVisible(true); + + nRet = ERRCODE_SFX_CANTGETPASSWD; + + SfxItemSet& rSet = pFile->GetItemSet(); + Reference< css::task::XInteractionHandler > xInteractionHandler = pFile->GetInteractionHandler(); + if( xInteractionHandler.is() ) + { + // use the comphelper password helper to request a password + OUString aPassword; + const SfxStringItem* pPasswordItem = rSet.GetItem(SID_PASSWORD, false); + if ( pPasswordItem ) + aPassword = pPasswordItem->GetValue(); + + uno::Sequence< beans::NamedValue > aEncryptionData; + const SfxUnoAnyItem* pEncryptionDataItem = rSet.GetItem(SID_ENCRYPTIONDATA, false); + if ( pEncryptionDataItem ) + pEncryptionDataItem->GetValue() >>= aEncryptionData; + + // try if one of the public key entries is + // decryptable, then extract session key + // from it + if ( !aEncryptionData.hasElements() && aGpgProperties.hasElements() ) + aEncryptionData = ::comphelper::DocPasswordHelper::decryptGpgSession(aGpgProperties); + + // tdf#93389: if recovering a document, encryption data should contain + // entries for the real filter, not only for recovery ODF, to keep it + // encrypted. Pass this in encryption data. + // TODO: pass here the real filter (from AutoRecovery::implts_openDocs) + // to marshal this to requestAndVerifyDocPassword + if (rSet.GetItemState(SID_DOC_SALVAGE, false) == SfxItemState::SET) + { + aEncryptionData = comphelper::concatSequences( + aEncryptionData, std::initializer_list<beans::NamedValue>{ + { "ForSalvage", css::uno::Any(true) } }); + } + + SfxDocPasswordVerifier aVerifier(*pFile); + aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword( + aVerifier, aEncryptionData, aPassword, xInteractionHandler, pFile->GetOrigURL(), comphelper::DocPasswordRequestType::Standard ); + + rSet.ClearItem( SID_PASSWORD ); + rSet.ClearItem( SID_ENCRYPTIONDATA ); + + if ( aEncryptionData.hasElements() ) + { + rSet.Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) ); + + try + { + // update the version list of the medium using the new password + pFile->GetVersionList(); + } + catch( uno::Exception& ) + { + // TODO/LATER: set the error code + } + + nRet = ERRCODE_NONE; + } + else + nRet = ERRCODE_IO_ABORT; + } + } + } + else + { + OSL_FAIL( "A storage must implement XPropertySet interface!" ); + nRet = ERRCODE_SFX_CANTGETPASSWD; + } + } + } + + return nRet; +} + + +ErrCodeMsg SfxApplication::LoadTemplate( SfxObjectShellLock& xDoc, const OUString &rFileName, std::unique_ptr<SfxItemSet> pSet ) +{ + std::shared_ptr<const SfxFilter> pFilter; + SfxMedium aMedium( rFileName, ( StreamMode::READ | StreamMode::SHARE_DENYNONE ) ); + + if ( !aMedium.GetStorage( false ).is() ) + aMedium.GetInStream(); + + if ( aMedium.GetErrorIgnoreWarning() ) + { + return aMedium.GetErrorCode(); + } + + aMedium.UseInteractionHandler( true ); + ErrCode nErr = GetFilterMatcher().GuessFilter( aMedium, pFilter, SfxFilterFlags::TEMPLATE, SfxFilterFlags::NONE ); + if ( ERRCODE_NONE != nErr) + { + return ERRCODE_SFX_NOTATEMPLATE; + } + + if( !pFilter || !pFilter->IsAllowedAsTemplate() ) + { + return ERRCODE_SFX_NOTATEMPLATE; + } + + if ( pFilter->GetFilterFlags() & SfxFilterFlags::STARONEFILTER ) + { + DBG_ASSERT( !xDoc.Is(), "Sorry, not implemented!" ); + SfxStringItem aName( SID_FILE_NAME, rFileName ); + SfxStringItem aReferer( SID_REFERER, "private:user" ); + SfxStringItem aFlags( SID_OPTIONS, "T" ); + SfxBoolItem aHidden( SID_HIDDEN, true ); + const SfxPoolItemHolder aRet(GetDispatcher_Impl()->ExecuteList( + SID_OPENDOC, SfxCallMode::SYNCHRON, + { &aName, &aHidden, &aReferer, &aFlags } )); + const SfxObjectItem* pObj(dynamic_cast<const SfxObjectItem*>(aRet.getItem())); + if ( pObj ) + xDoc = dynamic_cast<SfxObjectShell*>( pObj->GetShell() ); + else + { + const SfxViewFrameItem* pView(dynamic_cast<const SfxViewFrameItem*>(aRet.getItem())); + if ( pView ) + { + SfxViewFrame *pFrame = pView->GetFrame(); + if ( pFrame ) + xDoc = pFrame->GetObjectShell(); + } + } + + if ( !xDoc.Is() ) + return ERRCODE_SFX_DOLOADFAILED; + } + else + { + if ( !xDoc.Is() ) + xDoc = SfxObjectShell::CreateObject( pFilter->GetServiceName() ); + + //pMedium takes ownership of pSet + SfxMedium *pMedium = new SfxMedium( rFileName, StreamMode::STD_READ, pFilter, std::move(pSet) ); + if(!xDoc->DoLoad(pMedium)) + { + ErrCodeMsg nErrCode = xDoc->GetErrorCode(); + xDoc->DoClose(); + xDoc.Clear(); + return nErrCode; + } + } + + try + { + // TODO: introduce error handling + + uno::Reference< embed::XStorage > xTempStorage = ::comphelper::OStorageHelper::GetTemporaryStorage(); + if( !xTempStorage.is() ) + throw uno::RuntimeException(); + + xDoc->GetStorage()->copyToStorage( xTempStorage ); + + if ( !xDoc->DoSaveCompleted( new SfxMedium( xTempStorage, OUString() ) ) ) + throw uno::RuntimeException(); + } + catch( uno::Exception& ) + { + xDoc->DoClose(); + xDoc.Clear(); + + // TODO: transfer correct error outside + return ERRCODE_SFX_GENERAL; + } + + SetTemplate_Impl( rFileName, OUString(), xDoc ); + + xDoc->SetNoName(); + xDoc->InvalidateName(); + xDoc->SetModified(false); + xDoc->ResetError(); + + css::uno::Reference< css::frame::XModel > xModel = xDoc->GetModel(); + if ( xModel.is() ) + { + std::unique_ptr<SfxItemSet> pNew = xDoc->GetMedium()->GetItemSet().Clone(); + pNew->ClearItem( SID_PROGRESS_STATUSBAR_CONTROL ); + pNew->ClearItem( SID_FILTER_NAME ); + css::uno::Sequence< css::beans::PropertyValue > aArgs; + TransformItems( SID_OPENDOC, *pNew, aArgs ); + sal_Int32 nLength = aArgs.getLength(); + aArgs.realloc( nLength + 1 ); + auto pArgs = aArgs.getArray(); + pArgs[nLength].Name = "Title"; + pArgs[nLength].Value <<= xDoc->GetTitle( SFX_TITLE_DETECT ); + xModel->attachResource( OUString(), aArgs ); + } + + return xDoc->GetErrorCode(); +} + + +void SfxApplication::NewDocDirectExec_Impl( SfxRequest& rReq ) +{ + const SfxStringItem* pFactoryItem = rReq.GetArg<SfxStringItem>(SID_NEWDOCDIRECT); + OUString aFactName; + if ( pFactoryItem ) + aFactName = pFactoryItem->GetValue(); + else + aFactName = SvtModuleOptions().GetDefaultModuleName(); + + SfxRequest aReq( SID_OPENDOC, SfxCallMode::SYNCHRON, GetPool() ); + aReq.AppendItem( SfxStringItem( SID_FILE_NAME, "private:factory/" + aFactName ) ); + aReq.AppendItem( SfxFrameItem( SID_DOCFRAME, GetFrame() ) ); + aReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) ); + + // TODO/LATER: Should the other arguments be transferred as well? + const SfxStringItem* pDefaultPathItem = rReq.GetArg<SfxStringItem>(SID_DEFAULTFILEPATH); + if ( pDefaultPathItem ) + aReq.AppendItem( *pDefaultPathItem ); + const SfxStringItem* pDefaultNameItem = rReq.GetArg<SfxStringItem>(SID_DEFAULTFILENAME); + if ( pDefaultNameItem ) + aReq.AppendItem( *pDefaultNameItem ); + + SfxGetpApp()->ExecuteSlot( aReq ); + const SfxViewFrameItem* pItem(dynamic_cast<const SfxViewFrameItem*>(aReq.GetReturnValue().getItem())); + if (nullptr != pItem) + rReq.SetReturnValue(SfxFrameItem(0, pItem->GetFrame())); +} + +void SfxApplication::NewDocDirectState_Impl( SfxItemSet &rSet ) +{ + rSet.Put(SfxStringItem(SID_NEWDOCDIRECT, "private:factory/" + SvtModuleOptions().GetDefaultModuleName())); +} + +void SfxApplication::NewDocExec_Impl( SfxRequest& rReq ) +{ + // No Parameter from BASIC only Factory given? + const SfxStringItem* pTemplNameItem = rReq.GetArg<SfxStringItem>(SID_TEMPLATE_NAME); + const SfxStringItem* pTemplFileNameItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME); + const SfxStringItem* pTemplRegionNameItem = rReq.GetArg<SfxStringItem>(SID_TEMPLATE_REGIONNAME); + + SfxObjectShellLock xDoc; + + OUString aTemplateRegion, aTemplateName, aTemplateFileName; + bool bDirect = false; // through FileName instead of Region/Template + SfxErrorContext aEc(ERRCTX_SFX_NEWDOC); + if ( !pTemplNameItem && !pTemplFileNameItem ) + { + bool bNewWin = false; + weld::Window* pTopWin = GetTopWindow(); + + SfxObjectShell* pCurrentShell = SfxObjectShell::Current(); + Reference<XModel> xModel; + if(pCurrentShell) + xModel = pCurrentShell->GetModel(); + + SfxTemplateManagerDlg aTemplDlg(rReq.GetFrameWeld()); + + if (xModel.is()) + aTemplDlg.setDocumentModel(xModel); + + int nRet = aTemplDlg.run(); + if ( nRet == RET_OK ) + { + rReq.Done(); + if ( pTopWin != GetTopWindow() ) + { + // the dialogue opens a document -> a new TopWindow appears + pTopWin = GetTopWindow(); + bNewWin = true; + } + } + + if (bNewWin && pTopWin) + { + // after the destruction of the dialogue its parent comes to top, + // but we want that the new document is on top + pTopWin->present(); + } + + return; + } + else + { + // Template-Name + if ( pTemplNameItem ) + aTemplateName = pTemplNameItem->GetValue(); + + // Template-Region + if ( pTemplRegionNameItem ) + aTemplateRegion = pTemplRegionNameItem->GetValue(); + + // Template-File-Name + if ( pTemplFileNameItem ) + { + aTemplateFileName = pTemplFileNameItem->GetValue(); + bDirect = true; + } + } + + ErrCode lErr = ERRCODE_NONE; + if ( !bDirect ) + { + SfxDocumentTemplates aTmpFac; + if( aTemplateFileName.isEmpty() ) + aTmpFac.GetFull( aTemplateRegion, aTemplateName, aTemplateFileName ); + + if( aTemplateFileName.isEmpty() ) + lErr = ERRCODE_SFX_TEMPLATENOTFOUND; + } + + INetURLObject aObj( aTemplateFileName ); + SfxErrorContext aEC( ERRCTX_SFX_LOADTEMPLATE, aObj.PathToFileName() ); + + if ( lErr != ERRCODE_NONE ) + { + ErrCode lFatalErr = lErr.IgnoreWarning(); + if ( lFatalErr ) + ErrorHandler::HandleError(lErr); + } + else + { + SfxCallMode eMode = SfxCallMode::SYNCHRON; + SfxPoolItemHolder aRet; + SfxStringItem aReferer( SID_REFERER, "private:user" ); + SfxStringItem aTarget( SID_TARGETNAME, "_default" ); + if ( !aTemplateFileName.isEmpty() ) + { + DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" ); + + SfxStringItem aName( SID_FILE_NAME, aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + SfxStringItem aTemplName( SID_TEMPLATE_NAME, aTemplateName ); + SfxStringItem aTemplRegionName( SID_TEMPLATE_REGIONNAME, aTemplateRegion ); + aRet = GetDispatcher_Impl()->ExecuteList(SID_OPENDOC, eMode, + {&aName, &aTarget, &aReferer, &aTemplName, &aTemplRegionName}); + } + else + { + SfxStringItem aName( SID_FILE_NAME, "private:factory" ); + aRet = GetDispatcher_Impl()->ExecuteList(SID_OPENDOC, eMode, + { &aName, &aTarget, &aReferer } ); + } + + if ( nullptr != aRet.getItem() ) + rReq.SetReturnValue( *aRet.getItem() ); + } +} + + +namespace { + +/** + * Check if a given filter type should open the hyperlinked document + * natively. + * + * @param rFilter filter object + */ +bool lcl_isFilterNativelySupported(const SfxFilter& rFilter) +{ + if (rFilter.IsOwnFormat()) + return true; + + const OUString& aName = rFilter.GetFilterName(); + // We can handle all Excel variants natively. + return aName.startsWith("MS Excel"); +} + +} + +void SfxApplication::OpenDocExec_Impl( SfxRequest& rReq ) +{ + OUString aDocService; + const SfxStringItem* pDocSrvItem = rReq.GetArg<SfxStringItem>(SID_DOC_SERVICE); + if (pDocSrvItem) + aDocService = pDocSrvItem->GetValue(); + + sal_uInt16 nSID = rReq.GetSlot(); + const SfxStringItem* pFileNameItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME); + if ( pFileNameItem ) + { + OUString aCommand( pFileNameItem->GetValue() ); + const SfxSlot* pSlot = GetInterface()->GetSlot( aCommand ); + if ( pSlot ) + { + pFileNameItem = nullptr; + } + else + { + if ( aCommand.startsWith("slot:") ) + { + sal_uInt16 nSlotId = static_cast<sal_uInt16>(o3tl::toInt32(aCommand.subView(5))); + if ( nSlotId == SID_OPENDOC ) + pFileNameItem = nullptr; + } + } + } + + if ( !pFileNameItem ) + { + // get FileName from dialog + std::vector<OUString> aURLList; + OUString aFilter; + std::optional<SfxAllItemSet> pSet; + OUString aPath; + const SfxStringItem* pFolderNameItem = rReq.GetArg<SfxStringItem>(SID_PATH); + if ( pFolderNameItem ) + aPath = pFolderNameItem->GetValue(); + else if ( nSID == SID_OPENTEMPLATE ) + { + aPath = SvtPathOptions().GetTemplatePath(); + if (!aPath.isEmpty()) // if not empty then get last token + aPath = aPath.copy(aPath.lastIndexOf(';')+1); // lastIndexOf+copy works whether separator (';') is there or not + } + + sal_Int16 nDialog = SFX2_IMPL_DIALOG_CONFIG; + const SfxBoolItem* pSystemDialogItem = rReq.GetArg<SfxBoolItem>(SID_FILE_DIALOG); + if ( pSystemDialogItem ) + nDialog = pSystemDialogItem->GetValue() ? SFX2_IMPL_DIALOG_SYSTEM : SFX2_IMPL_DIALOG_OOO; + + const SfxBoolItem* pRemoteDialogItem = rReq.GetArg<SfxBoolItem>(SID_REMOTE_DIALOG); + if ( pRemoteDialogItem && pRemoteDialogItem->GetValue()) + nDialog = SFX2_IMPL_DIALOG_REMOTE; + + sal_Int16 nDialogType = ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION; + FileDialogFlags eDialogFlags = FileDialogFlags::MultiSelection; + const SfxBoolItem* pSignPDFItem = rReq.GetArg<SfxBoolItem>(SID_SIGNPDF); + if (pSignPDFItem && pSignPDFItem->GetValue()) + { + eDialogFlags |= FileDialogFlags::SignPDF; + nDialogType = ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE; + } + + OUString sStandardDir; + + const SfxStringItem* pStandardDirItem = rReq.GetArg<SfxStringItem>(SID_STANDARD_DIR); + if ( pStandardDirItem ) + sStandardDir = pStandardDirItem->GetValue(); + + css::uno::Sequence< OUString > aDenyList; + + const SfxStringListItem* pDenyListItem = rReq.GetArg<SfxStringListItem>(SID_DENY_LIST); + if ( pDenyListItem ) + pDenyListItem->GetStringList( aDenyList ); + + weld::Window* pTopWindow = GetTopWindow(); + ErrCode nErr = sfx2::FileOpenDialog_Impl(pTopWindow, + nDialogType, + eDialogFlags, aURLList, + aFilter, pSet, &aPath, nDialog, sStandardDir, aDenyList); + + if ( nErr == ERRCODE_ABORT ) + { + aURLList.clear(); + return; + } + + rReq.SetArgs( *pSet ); + if ( !aFilter.isEmpty() ) + rReq.AppendItem( SfxStringItem( SID_FILTER_NAME, aFilter ) ); + rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) ); + rReq.AppendItem( SfxStringItem( SID_REFERER, "private:user" ) ); + pSet.reset(); + + if(!aURLList.empty()) + { + if ( nSID == SID_OPENTEMPLATE ) + rReq.AppendItem( SfxBoolItem( SID_TEMPLATE, false ) ); + + // This helper wraps an existing (or may new created InteractionHandler) + // intercept all incoming interactions and provide useful information + // later if the following transaction was finished. + + rtl::Reference<sfx2::PreventDuplicateInteraction> pHandler = new sfx2::PreventDuplicateInteraction(comphelper::getProcessComponentContext()); + uno::Reference<task::XInteractionHandler> xHandler(pHandler); + uno::Reference<task::XInteractionHandler> xWrappedHandler; + + // wrap existing handler or create new UUI handler + const SfxUnoAnyItem* pInteractionItem = rReq.GetArg<SfxUnoAnyItem>(SID_INTERACTIONHANDLER); + if (pInteractionItem) + { + pInteractionItem->GetValue() >>= xWrappedHandler; + rReq.RemoveItem( SID_INTERACTIONHANDLER ); + } + if (xWrappedHandler.is()) + pHandler->setHandler(xWrappedHandler); + else + pHandler->useDefaultUUIHandler(); + rReq.AppendItem( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHandler)) ); + + // define rules for this handler + css::uno::Type aInteraction = ::cppu::UnoType<css::task::ErrorCodeRequest>::get(); + ::sfx2::PreventDuplicateInteraction::InteractionInfo aRule(aInteraction); + pHandler->addInteractionRule(aRule); + + if (!aDocService.isEmpty()) + { + rReq.RemoveItem(SID_DOC_SERVICE); + rReq.AppendItem(SfxStringItem(SID_DOC_SERVICE, aDocService)); + } + + for (auto const& url : aURLList) + { + rReq.RemoveItem( SID_FILE_NAME ); + rReq.AppendItem( SfxStringItem( SID_FILE_NAME, url ) ); + + // Run synchronous, so that not the next document is loaded + // when rescheduling + // TODO/LATER: use URLList argument and always remove one document after another, each step in asynchronous execution, until finished + // but only if reschedule is a problem + GetDispatcher_Impl()->Execute( SID_OPENDOC, SfxCallMode::SYNCHRON, *rReq.GetArgs() ); + + // check for special interaction "NO MORE DOCUMENTS ALLOWED" and + // break loop then. Otherwise we risk showing the same interaction more than once. + if ( pHandler->getInteractionInfo(aInteraction, &aRule) ) + { + if (aRule.m_nCallCount > 0) + { + if (aRule.m_xRequest.is()) + { + css::task::ErrorCodeRequest aRequest; + if (aRule.m_xRequest->getRequest() >>= aRequest) + { + if (aRequest.ErrCode == sal_Int32(sal_uInt32(ERRCODE_SFX_NOMOREDOCUMENTSALLOWED))) + break; + } + } + } + } + } + + aURLList.clear(); + return; + } + aURLList.clear(); + } + + bool bHyperlinkUsed = false; + + if ( SID_OPENURL == nSID ) + { + // SID_OPENURL does the same as SID_OPENDOC! + rReq.SetSlot( SID_OPENDOC ); + } + else if ( nSID == SID_OPENTEMPLATE ) + { + rReq.AppendItem( SfxBoolItem( SID_TEMPLATE, false ) ); + } + // pass URL to OS by using ShellExecuter or open it internal + // if it seems to be an own format. + /* Attention! + There exist two possibilities to open hyperlinks: + a) using SID_OPENHYPERLINK (new) + b) using SID_BROWSE (old) + */ + else if ( nSID == SID_OPENHYPERLINK ) + { + rReq.SetSlot( SID_OPENDOC ); + bHyperlinkUsed = true; + } + + // no else here! It's optional ... + if (!bHyperlinkUsed) + { + const SfxBoolItem* pHyperLinkUsedItem = rReq.GetArg<SfxBoolItem>(SID_BROWSE); + if ( pHyperLinkUsedItem ) + bHyperlinkUsed = pHyperLinkUsedItem->GetValue(); + // no "official" item, so remove it from ItemSet before using UNO-API + rReq.RemoveItem( SID_BROWSE ); + } + + const SfxStringItem* pFileName = rReq.GetArg<SfxStringItem>(SID_FILE_NAME); + assert(pFileName && "SID_FILE_NAME is required"); + OUString aFileName = pFileName->GetValue(); + + OUString aReferer; + const SfxStringItem* pRefererItem = rReq.GetArg<SfxStringItem>(SID_REFERER); + if ( pRefererItem ) + aReferer = pRefererItem->GetValue(); + + const SfxStringItem* pFileFlagsItem = rReq.GetArg<SfxStringItem>(SID_OPTIONS); + if ( pFileFlagsItem ) + { + const OUString aFileFlags = pFileFlagsItem->GetValue().toAsciiUpperCase(); + if ( aFileFlags.indexOf('T') >= 0 ) + { + rReq.RemoveItem( SID_TEMPLATE ); + rReq.AppendItem( SfxBoolItem( SID_TEMPLATE, true ) ); + } + + if ( aFileFlags.indexOf('H') >= 0 ) + { + rReq.RemoveItem( SID_HIDDEN ); + rReq.AppendItem( SfxBoolItem( SID_HIDDEN, true ) ); + } + + if ( aFileFlags.indexOf('R') >= 0 ) + { + rReq.RemoveItem( SID_DOC_READONLY ); + rReq.AppendItem( SfxBoolItem( SID_DOC_READONLY, true ) ); + } + + if ( aFileFlags.indexOf('B') >= 0 ) + { + rReq.RemoveItem( SID_PREVIEW ); + rReq.AppendItem( SfxBoolItem( SID_PREVIEW, true ) ); + } + + rReq.RemoveItem( SID_OPTIONS ); + } + + // Mark without URL cannot be handled by hyperlink code + if ( bHyperlinkUsed && !aFileName.isEmpty() && aFileName[0] != '#' ) + { + uno::Reference<document::XTypeDetection> xTypeDetection( + comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), UNO_QUERY); + + if ( xTypeDetection.is() ) + { + URL aURL; + + aURL.Complete = aFileName; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict( aURL ); + + INetProtocol aINetProtocol = INetURLObject( aURL.Complete ).GetProtocol(); + auto eMode = officecfg::Office::Security::Hyperlinks::Open::get(); + + if ( eMode == SvtExtendedSecurityOptions::OPEN_NEVER && aINetProtocol != INetProtocol::VndSunStarHelp ) + { + SolarMutexGuard aGuard; + weld::Window *pWindow = SfxGetpApp()->GetTopWindow(); + + std::unique_ptr<weld::MessageDialog> xSecurityWarningBox(Application::CreateMessageDialog(pWindow, + VclMessageType::Warning, VclButtonsType::Ok, SfxResId(STR_SECURITY_WARNING_NO_HYPERLINKS))); + xSecurityWarningBox->set_title(SfxResId(RID_SECURITY_WARNING_TITLE)); + xSecurityWarningBox->run(); + return; + } + + std::shared_ptr<const SfxFilter> pFilter{}; + + // attempt loading native documents only if they are from a known protocol + // it might be sensible to limit the set of protocols even further, but that + // may cause regressions, needs further testing + // see tdf#136427 for details + if (aINetProtocol != INetProtocol::NotValid) { + const OUString aTypeName { xTypeDetection->queryTypeByURL( aURL.Main ) }; + SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); + pFilter = rMatcher.GetFilter4EA( aTypeName ); + } + + if (!pFilter || !lcl_isFilterNativelySupported(*pFilter)) + { + // hyperlink does not link to own type => special handling (http, ftp) browser and (other external protocols) OS + if ( aINetProtocol == INetProtocol::Mailto ) + { + // don't dispatch mailto hyperlink to desktop dispatcher + rReq.RemoveItem( SID_TARGETNAME ); + rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_self" ) ); + } + else if ( aINetProtocol == INetProtocol::Ftp || + aINetProtocol == INetProtocol::Http || + aINetProtocol == INetProtocol::Https ) + { + sfx2::openUriExternally(aURL.Complete, true, rReq.GetFrameWeld()); + return; + } + else + { + // check for "internal" protocols that should not be forwarded to the system + // add special protocols that always should be treated as internal + std::vector < OUString > aProtocols { "private:*", "vnd.sun.star.*" }; + + // get registered protocol handlers from configuration + Reference < XNameAccess > xAccess(officecfg::Office::ProtocolHandler::HandlerSet::get()); + const Sequence < OUString > aNames = xAccess->getElementNames(); + for ( const auto& rName : aNames ) + { + Reference < XPropertySet > xSet; + Any aRet = xAccess->getByName( rName ); + aRet >>= xSet; + if ( xSet.is() ) + { + // copy protocols + aRet = xSet->getPropertyValue("Protocols"); + Sequence < OUString > aTmp; + aRet >>= aTmp; + + aProtocols.insert(aProtocols.end(),std::cbegin(aTmp),std::cend(aTmp)); + } + } + + bool bFound = false; + for (const OUString & rProtocol : aProtocols) + { + WildCard aPattern(rProtocol); + if ( aPattern.Matches( aURL.Complete ) ) + { + bFound = true; + break; + } + } + + if ( !bFound ) + { + bool bLoadInternal = false; + try + { + sfx2::openUriExternally( + aURL.Complete, pFilter == nullptr, rReq.GetFrameWeld()); + } + catch ( css::system::SystemShellExecuteException& ) + { + rReq.RemoveItem( SID_TARGETNAME ); + rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) ); + bLoadInternal = true; + } + if ( !bLoadInternal ) + return; + } + } + } + else + { + // hyperlink document must be loaded into a new frame + rReq.RemoveItem( SID_TARGETNAME ); + rReq.AppendItem( SfxStringItem( SID_TARGETNAME, "_default" ) ); + } + } + } + + if (!SvtSecurityOptions::isSecureMacroUri(aFileName, aReferer)) + { + SfxErrorContext aCtx( ERRCTX_SFX_OPENDOC, aFileName ); + ErrorHandler::HandleError( ERRCODE_IO_ACCESSDENIED ); + return; + } + + SfxFrame* pTargetFrame = nullptr; + Reference< XFrame > xTargetFrame; + + const SfxFrameItem* pFrameItem = rReq.GetArg<SfxFrameItem>(SID_DOCFRAME); + if ( pFrameItem ) + pTargetFrame = pFrameItem->GetFrame(); + + if ( !pTargetFrame ) + { + const SfxUnoFrameItem* pUnoFrameItem = rReq.GetArg<SfxUnoFrameItem>(SID_FILLFRAME); + if ( pUnoFrameItem ) + xTargetFrame = pUnoFrameItem->GetFrame(); + } + + if (!pTargetFrame && !xTargetFrame.is()) + { + if (const SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pTargetFrame = &pViewFrame->GetFrame(); + } + + // check if caller has set a callback + std::unique_ptr<SfxLinkItem> pLinkItem; + + // remove from Itemset, because it confuses the parameter transformation + if (auto pParamLinkItem = rReq.GetArg<SfxLinkItem>(SID_DONELINK)) + pLinkItem.reset(pParamLinkItem->Clone()); + + rReq.RemoveItem( SID_DONELINK ); + + // check if the view must be hidden + bool bHidden = false; + const SfxBoolItem* pHidItem = rReq.GetArg<SfxBoolItem>(SID_HIDDEN); + if ( pHidItem ) + bHidden = pHidItem->GetValue(); + + // This request is a UI call. We have to set the right values inside the MediaDescriptor + // for: InteractionHandler, StatusIndicator, MacroExecutionMode and DocTemplate. + // But we have to look for already existing values or for real hidden requests. + const SfxBoolItem* pPreviewItem = rReq.GetArg<SfxBoolItem>(SID_PREVIEW); + if (!bHidden && ( !pPreviewItem || !pPreviewItem->GetValue() ) ) + { + const SfxUnoAnyItem* pInteractionItem = rReq.GetArg<SfxUnoAnyItem>(SID_INTERACTIONHANDLER); + const SfxUInt16Item* pMacroExecItem = rReq.GetArg<SfxUInt16Item>(SID_MACROEXECMODE); + const SfxUInt16Item* pDocTemplateItem = rReq.GetArg<SfxUInt16Item>(SID_UPDATEDOCMODE); + + if (!pInteractionItem) + { + Reference < task::XInteractionHandler2 > xHdl = task::InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr ); + rReq.AppendItem( SfxUnoAnyItem(SID_INTERACTIONHANDLER,css::uno::Any(xHdl)) ); + } + if (!pMacroExecItem) + rReq.AppendItem( SfxUInt16Item(SID_MACROEXECMODE,css::document::MacroExecMode::USE_CONFIG) ); + if (!pDocTemplateItem) + rReq.AppendItem( SfxUInt16Item(SID_UPDATEDOCMODE,css::document::UpdateDocMode::ACCORDING_TO_CONFIG) ); + } + + // extract target name + OUString aTarget; + const SfxStringItem* pTargetItem = rReq.GetArg<SfxStringItem>(SID_TARGETNAME); + if ( pTargetItem ) + aTarget = pTargetItem->GetValue(); + else + { + const SfxBoolItem* pNewViewItem = rReq.GetArg<SfxBoolItem>(SID_OPEN_NEW_VIEW); + if ( pNewViewItem && pNewViewItem->GetValue() ) + aTarget = "_blank" ; + } + + if ( bHidden ) + { + aTarget = "_blank"; + DBG_ASSERT( rReq.IsSynchronCall() || pLinkItem, "Hidden load process must be done synchronously!" ); + } + + Reference < XController > xController; + // if a frame is given, it must be used for the starting point of the targeting mechanism + // this code is also used if asynchronous loading is possible, because loadComponent always is synchron + if ( !xTargetFrame.is() ) + { + if ( pTargetFrame ) + { + xTargetFrame = pTargetFrame->GetFrameInterface(); + } + else + { + xTargetFrame = Desktop::create(::comphelper::getProcessComponentContext()); + } + } + + // make URL ready + const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_FILE_NAME); + aFileName = pURLItem->GetValue(); + if( aFileName.startsWith("#") ) // Mark without URL + { + SfxViewFrame *pView = pTargetFrame ? pTargetFrame->GetCurrentViewFrame() : nullptr; + if ( !pView ) + pView = SfxViewFrame::Current(); + pView->GetViewShell()->JumpToMark( aFileName.copy(1) ); + rReq.SetReturnValue( SfxViewFrameItem( pView ) ); + return; + } + + // convert items to properties for framework API calls + Sequence < PropertyValue > aArgs; + TransformItems( SID_OPENDOC, *rReq.GetArgs(), aArgs ); + // Any Referer (that was relevant in the above call to + // SvtSecurityOptions::isSecureMacroUri) is no longer relevant, assuming + // this "open" request is initiated directly by the user: + auto pArg = std::find_if(std::cbegin(aArgs), std::cend(aArgs), + [](const PropertyValue& rArg) { return rArg.Name == "Referer"; }); + if (pArg != std::cend(aArgs)) + { + auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(aArgs), pArg)); + comphelper::removeElementAt(aArgs, nIndex); + } + + // TODO/LATER: either remove LinkItem or create an asynchronous process for it + if( bHidden || pLinkItem || rReq.IsSynchronCall() ) + { + // if loading must be done synchron, we must wait for completion to get a return value + // find frame by myself; I must know the exact frame to get the controller for the return value from it + Reference < XComponent > xComp; + + try + { + xComp = ::comphelper::SynchronousDispatch::dispatch( xTargetFrame, aFileName, aTarget, aArgs ); + } + catch(const RuntimeException&) + { + throw; + } + catch(const css::uno::Exception&) + { + } + + Reference < XModel > xModel( xComp, UNO_QUERY ); + if ( xModel.is() ) + xController = xModel->getCurrentController(); + else + xController.set( xComp, UNO_QUERY ); + + } + else + { + URL aURL; + aURL.Complete = aFileName; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict( aURL ); + + Reference < XDispatchProvider > xProv( xTargetFrame, UNO_QUERY ); + Reference < XDispatch > xDisp = xProv.is() ? xProv->queryDispatch( aURL, aTarget, FrameSearchFlag::ALL ) : Reference < XDispatch >(); + if ( xDisp.is() ) + xDisp->dispatch( aURL, aArgs ); + } + + if ( xController.is() ) + { + // try to find the SfxFrame for the controller + SfxFrame* pCntrFrame = nullptr; + for ( SfxViewShell* pShell = SfxViewShell::GetFirst( false ); pShell; pShell = SfxViewShell::GetNext( *pShell, false ) ) + { + if ( pShell->GetController() == xController ) + { + pCntrFrame = &pShell->GetViewFrame().GetFrame(); + break; + } + } + + if ( pCntrFrame ) + { + SfxObjectShell* pSh = pCntrFrame->GetCurrentDocument(); + DBG_ASSERT( pSh, "Controller without ObjectShell ?!" ); + + rReq.SetReturnValue( SfxViewFrameItem( pCntrFrame->GetCurrentViewFrame() ) ); + + if ( bHidden ) + pSh->RestoreNoDelete(); + } + } + + if (pLinkItem) + { + const SfxPoolItem* pRetValue(rReq.GetReturnValue().getItem()); + if (pRetValue) + { + pLinkItem->GetValue().Call(pRetValue); + } + } +} + +void SfxApplication::OpenRemoteExec_Impl( SfxRequest& rReq ) +{ + rReq.AppendItem( SfxBoolItem( SID_REMOTE_DIALOG, true ) ); + GetDispatcher_Impl()->Execute( SID_OPENDOC, SfxCallMode::SYNCHRON, *rReq.GetArgs() ); +} + +void SfxApplication::SignPDFExec_Impl(SfxRequest& rReq) +{ + rReq.AppendItem(SfxBoolItem(SID_SIGNPDF, true)); + GetDispatcher_Impl()->Execute(SID_OPENDOC, SfxCallMode::SYNCHRON, *rReq.GetArgs()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appquit.cxx b/sfx2/source/appl/appquit.cxx new file mode 100644 index 0000000000..b34550dc7f --- /dev/null +++ b/sfx2/source/appl/appquit.cxx @@ -0,0 +1,103 @@ +/* -*- 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_features.h> + +#include <basic/sbstar.hxx> +#include <tools/debug.hxx> + +#include <sfx2/app.hxx> +#include <appdata.hxx> +#include <sfx2/stbitem.hxx> +#include <sfx2/tbxctrl.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/dispatch.hxx> +#include <nochaos.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objsh.hxx> +#include <appbaslib.hxx> +#include <basic/basicmanagerrepository.hxx> + +using ::basic::BasicManagerRepository; + +void SfxApplication::Deinitialize() +{ + if (pImpl->bDowning) + return; + +#if HAVE_FEATURE_SCRIPTING + StarBASIC::Stop(); + + SaveBasicAndDialogContainer(); +#endif + + pImpl->bDowning = true; // due to Timer from DecAliveCount and QueryExit + + pImpl->pTemplates.reset(); + + // By definition there shouldn't be any open view frames when we reach + // this method. Therefore this call makes no sense and is the source of + // some stack traces, which we don't understand. + // For more information see: + pImpl->bDowning = false; + DBG_ASSERT(!SfxViewFrame::GetFirst(), "existing SfxViewFrame after Execute"); + DBG_ASSERT(!SfxObjectShell::GetFirst(), "existing SfxObjectShell after Execute"); + pImpl->pAppDispat->Pop(*this, SfxDispatcherPopFlags::POP_UNTIL); + pImpl->pAppDispat->Flush(); + pImpl->bDowning = true; + pImpl->pAppDispat->DoDeactivate_Impl(true, nullptr); + + // Release Controller and others + // then the remaining components should also disappear ( Beamer! ) + +#if HAVE_FEATURE_SCRIPTING + BasicManagerRepository::resetApplicationBasicManager(); + pImpl->pBasicManager->reset(nullptr); // this will also delete pBasMgr +#endif + + DBG_ASSERT(pImpl->pViewFrame == nullptr, "active foreign ViewFrame"); + + // free administration managers + pImpl->pAppDispat.reset(); + + // from here no SvObjects have to exists + pImpl->pMatcher.reset(); + + pImpl->pSlotPool.reset(); + pImpl->maFactories.clear(); + + pImpl->maTbxCtrlFactories.clear(); + pImpl->maStbCtrlFactories.clear(); + pImpl->maViewFrames.clear(); + pImpl->maViewShells.clear(); + pImpl->maObjShells.clear(); + + //TODO/CLEANUP + //ReleaseArgs could be used instead! + pImpl->pPool = nullptr; + NoChaos::ReleaseItemPool(); + +#if HAVE_FEATURE_SCRIPTING + pImpl->m_pSbxErrorHdl.reset(); +#endif + pImpl->m_pSoErrorHdl.reset(); + pImpl->m_pToolsErrorHdl.reset(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appreg.cxx b/sfx2/source/appl/appreg.cxx new file mode 100644 index 0000000000..662e485b62 --- /dev/null +++ b/sfx2/source/appl/appreg.cxx @@ -0,0 +1,105 @@ +/* -*- 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 <sal/log.hxx> + +#include <sfx2/app.hxx> +#include <appdata.hxx> +#include <inettbc.hxx> +#include <sfx2/stbitem.hxx> +#include <sfx2/infobar.hxx> +#include <sfx2/module.hxx> +#include <sfx2/viewfrm.hxx> +#include <partwnd.hxx> +#include <sfx2/sfxsids.hrc> +#include <recfloat.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewsh.hxx> + + +void SfxApplication::Registrations_Impl() +{ + // Interfaces + SfxApplication::RegisterInterface(); + SfxModule::RegisterInterface(); + SfxViewFrame::RegisterInterface(); + SfxObjectShell::RegisterInterface(); + SfxViewShell::RegisterInterface(); + + // ChildWindows + SfxRecordingFloatWrapper_Impl::RegisterChildWindow(); + SfxPartChildWnd_Impl::RegisterChildWindow(); + SfxDockingWrapper::RegisterChildWindow(); + SfxInfoBarContainerChild::RegisterChildWindow( true, nullptr, SfxChildWindowFlags::NEVERHIDE ); + + // Controller + SfxToolBoxControl::RegisterControl(SID_REPEAT); + SfxURLToolBoxControl_Impl::RegisterControl(SID_OPENURL); +} + + +void SfxApplication::RegisterToolBoxControl_Impl( SfxModule *pMod, const SfxTbxCtrlFactory& rFact ) +{ + if ( pMod ) + { + pMod->RegisterToolBoxControl( rFact ); + return; + } + +#ifdef DBG_UTIL + for ( size_t n=0; n<pImpl->maTbxCtrlFactories.size(); n++ ) + { + SfxTbxCtrlFactory *pF = &pImpl->maTbxCtrlFactories[n]; + if ( pF->nTypeId == rFact.nTypeId && + (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) ) + { + SAL_INFO("sfx", "TbxController registration is not clearly defined!"); + } + } +#endif + + pImpl->maTbxCtrlFactories.push_back( rFact ); +} + + +void SfxApplication::RegisterStatusBarControl_Impl( SfxModule *pMod, const SfxStbCtrlFactory& rFact ) +{ + if ( pMod ) + { + pMod->RegisterStatusBarControl( rFact ); + return; + } + +#ifdef DBG_UTIL + for ( size_t n=0; n<pImpl->maStbCtrlFactories.size(); n++ ) + { + SfxStbCtrlFactory *pF = &pImpl->maStbCtrlFactories[n]; + if ( pF->nTypeId == rFact.nTypeId && + (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) ) + { + SAL_INFO("sfx", "StbController registration is not clearly defined!"); + } + } +#endif + + pImpl->maStbCtrlFactories.push_back( rFact ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appserv.cxx b/sfx2/source/appl/appserv.cxx new file mode 100644 index 0000000000..cccc2abc1a --- /dev/null +++ b/sfx2/source/appl/appserv.cxx @@ -0,0 +1,1808 @@ +/* -*- 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_features.h> +#include <config_wasm_strip.h> + +#include <com/sun/star/drawing/ModuleDispatcher.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/DispatchResultEvent.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/DispatchHelper.hpp> +#include <com/sun/star/frame/UnknownModuleException.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XSynchronousFrameLoader.hpp> +#include <com/sun/star/sdbc/DriverManager.hpp> +#include <com/sun/star/text/ModuleDispatcher.hpp> +#include <com/sun/star/task/OfficeRestartManager.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ui/dialogs/AddressBookSourcePilot.hpp> +#include <com/sun/star/ui/UIElementType.hpp> +#include <com/sun/star/ui/XUIElement.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp> + +#include <comphelper/dispatchcommand.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> + +#include <svtools/addresstemplate.hxx> +#include <svtools/restartdialog.hxx> +#include <svtools/colorcfg.hxx> +#include <svl/visitem.hxx> + +#include <unotools/configmgr.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/weld.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <basic/sbstar.hxx> +#include <basic/basrdll.hxx> +#include <basic/sberrors.hxx> +#include <vcl/help.hxx> +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <vcl/EnumContext.hxx> +#include <vcl/toolbox.hxx> + +#include <unotools/moduleoptions.hxx> +#include <rtl/bootstrap.hxx> + +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <sfx2/app.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/minfitem.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> +#include <appdata.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/sfxsids.hrc> +#include <sorgitm.hxx> +#include <sfx2/sfxhelp.hxx> +#include <sfx2/zoomitem.hxx> +#include <sfx2/templatedlg.hxx> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <sfx2/sidebar/SidebarController.hxx> +#include <sfx2/safemode.hxx> +#include <sfx2/sfxuno.hxx> +#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx> + +#include <comphelper/types.hxx> +#include <officecfg/Office/Common.hxx> +#include <unotools/confignode.hxx> +#include <memory> + +#include <openuriexternally.hxx> + +#include "getbasctlfunction.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + +namespace +{ + OUString lcl_getAppName( vcl::EnumContext::Application eApp ) + { + switch ( eApp ) + { + case vcl::EnumContext::Application::Writer: + return "Writer"; + case vcl::EnumContext::Application::Calc: + return "Calc"; + case vcl::EnumContext::Application::Impress: + return "Impress"; + case vcl::EnumContext::Application::Draw: + return "Draw"; + case vcl::EnumContext::Application::Formula: + return "Formula"; + case vcl::EnumContext::Application::Base: + return "Base"; + default: + return OUString(); + } + } + + // lp#527938, debian#602953, fdo#33266, i#105408 + bool lcl_isBaseAvailable() + { + try + { + // if we get css::sdbc::DriverManager, libsdbc2 is there + // and the bibliography is assumed to work + return css::sdbc::DriverManager::create(comphelper::getProcessComponentContext()).is(); + } + catch (const Exception &) + { + TOOLS_INFO_EXCEPTION("sfx.appl", "assuming Base to be missing"); + return false; + } + } + void lcl_tryLoadBibliography() + { + // lp#527938, debian#602953, fdo#33266, i#105408 + // make sure we actually can instantiate services from base first + if(!lcl_isBaseAvailable()) + { + if (officecfg::Office::Common::PackageKit::EnableBaseInstallation::get()) + { + try + { + using namespace org::freedesktop::PackageKit; + using namespace svtools; + Reference< XSyncDbusSessionHelper > xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext())); + Sequence< OUString > vPackages { "libreoffice-base" }; + xSyncDbusSessionHelper->InstallPackageNames(vPackages, OUString()); + // I'll be back (hopefully)! + SolarMutexGuard aGuard; + executeRestartDialog(comphelper::getProcessComponentContext(), nullptr, RESTART_REASON_BIBLIOGRAPHY_INSTALL); + } + catch (const Exception &) + { + TOOLS_INFO_EXCEPTION("sfx.appl", "trying to install LibreOffice Base"); + } + } + return; + } + + try // fdo#48775 + { + SfxStringItem aURL(SID_FILE_NAME, ".component:Bibliography/View1"); + SfxStringItem aRef(SID_REFERER, "private:user"); + SfxStringItem aTarget(SID_TARGETNAME, "_blank"); + if (const SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + { + pViewFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON, { &aURL, &aRef, &aTarget }); + } + } + catch (const Exception &) + { + TOOLS_INFO_EXCEPTION( "sfx.appl", "trying to load bibliography database"); + } + } +} +/// Find the correct location of the document (CREDITS.fodt, etc.), and return +/// it in rURL if found. +static bool checkURL( const char *pName, const char *pExt, OUString &rURL ) +{ + using namespace osl; + DirectoryItem aDirItem; + +#ifdef MACOSX + rURL = "$BRAND_BASE_DIR/Resources/" + OUString::createFromAscii( pName ) + + OUString::createFromAscii( pExt ); +#else + rURL = "$BRAND_BASE_DIR/" + OUString::createFromAscii( pName ) + + OUString::createFromAscii( pExt ); +#endif + rtl::Bootstrap::expandMacros( rURL ); + + if (!rURL.isEmpty()) + return DirectoryItem::get( rURL, aDirItem ) == DirectoryItem::E_None; + else + return false; +} + +/// Displays CREDITS or LICENSE in any of the available version +static void showDocument( const char* pBaseName ) +{ + try { + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + auto args(::comphelper::InitPropertySequence({ + {"ViewOnly", Any(true)}, + {"ReadOnly", Any(true)} + })); + + OUString aURL; + if ( checkURL ( pBaseName, ".fodt", aURL ) || + checkURL ( pBaseName, ".html", aURL ) || + checkURL ( pBaseName, "", aURL ) ) { + xDesktop->loadComponentFromURL( aURL, "_blank", 0, args ); + } + } catch (const css::uno::Exception &) { + } +} + +namespace +{ + Reference<XFrame> GetRequestFrame(const SfxRequest& rReq) + { + const SfxItemSet* pArgs = rReq.GetInternalArgs_Impl(); + const SfxUnoFrameItem* pItem = nullptr; + Reference <XFrame> xFrame; + if (pArgs && (pItem = pArgs->GetItemIfSet(SID_FILLFRAME, false))) + { + xFrame = pItem->GetFrame(); + } + return xFrame; + } + + Reference<XFrame> GetDocFrame(const SfxRequest& rReq) + { + const SfxFrameItem* pFrameItem = rReq.GetArg<SfxFrameItem>(SID_DOCFRAME); + SfxFrame* pFrame = pFrameItem ? pFrameItem->GetFrame() : nullptr; + return pFrame ? pFrame->GetFrameInterface() : nullptr; + } + + class LicenseDialog : public weld::GenericDialogController + { + public: + LicenseDialog(weld::Window* pParent) + : GenericDialogController(pParent, "sfx/ui/licensedialog.ui", "LicenseDialog") + { + } + + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + showDocument("LICENSE"); + return nRet; + } + }; + + class SafeModeQueryDialog : public weld::MessageDialogController + { + public: + SafeModeQueryDialog(weld::Window* pParent) + : MessageDialogController(pParent, "sfx/ui/safemodequerydialog.ui", "SafeModeQueryDialog") + { + } + + virtual short run() override + { + short nRet = MessageDialogController::run(); + if (nRet == RET_OK) + { + sfx2::SafeMode::putFlag(); + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + css::task::OfficeRestartManager::get(xContext)->requestRestart( + css::uno::Reference< css::task::XInteractionHandler >()); + } + return nRet; + } + }; +} + +weld::Window* SfxRequest::GetFrameWeld() const +{ + const SfxItemSet* pIntArgs = GetInternalArgs_Impl(); + const SfxUnoAnyItem* pItem = nullptr; + if (pIntArgs && (pItem = pIntArgs->GetItemIfSet(SID_DIALOG_PARENT, false))) + { + auto aAny = pItem->GetValue(); + Reference<awt::XWindow> xWindow; + aAny >>= xWindow; + return Application::GetFrameWeld(xWindow); + } + + Reference<XFrame> xFrame(GetRequestFrame(*this)); + if (!xFrame) + xFrame = GetDocFrame(*this); + if (!xFrame) + { + SAL_WARN("sfx.appl", "no parent for dialogs"); + return nullptr; + } + return Application::GetFrameWeld(xFrame->getContainerWindow()); +} + +void SfxApplication::MiscExec_Impl( SfxRequest& rReq ) +{ + bool bDone = false; + switch ( rReq.GetSlot() ) + { + case SID_SETOPTIONS: + { + if( rReq.GetArgs() ) + SetOptions( *rReq.GetArgs() ); + break; + } + + case SID_QUITAPP: + case SID_LOGOUT: + { + // protect against reentrant calls + if ( pImpl->bInQuit ) + return; + + if ( rReq.GetSlot() == SID_LOGOUT ) + { + for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst(); + pObjSh; pObjSh = SfxObjectShell::GetNext( *pObjSh ) ) + { + if ( !pObjSh->IsModified() ) + continue; + + SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pObjSh ); + if ( !pFrame || !pFrame->GetWindow().IsReallyVisible() ) + continue; + + if (pObjSh->PrepareClose()) + pObjSh->SetModified( false ); + else + return; + } + + SfxStringItem aNameItem( SID_FILE_NAME, "vnd.sun.star.cmd:logout" ); + SfxStringItem aReferer( SID_REFERER, "private/user" ); + pImpl->pAppDispat->ExecuteList(SID_OPENDOC, + SfxCallMode::SLOT, { &aNameItem, &aReferer }); + return; + } + + // try from nested requests again after 100ms + if( Application::GetDispatchLevel() > 1 ) + { + /* Don't save the request for closing the application and try it later + again. This is an UI bound functionality ... and the user will try it again + if the dialog is closed. But we should not close the application automatically + if this dialog is closed by the user ... + So we ignore this request now and wait for a new user decision. + */ + SAL_INFO("sfx.appl", "QueryExit => sal_False, DispatchLevel == " << Application::GetDispatchLevel() ); + return; + } + + // block reentrant calls + pImpl->bInQuit = true; + Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() ); + + rReq.ForgetAllArgs(); + + // if terminate() failed, pImpl->bInQuit will now be sal_False, allowing further calls of SID_QUITAPP + bool bTerminated = xDesktop->terminate(); + if (!bTerminated) + // if terminate() was successful, SfxApplication is now dead! + pImpl->bInQuit = false; + + // Set return value, terminate if possible + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bTerminated ) ); + return; + } + + case SID_CONFIG: + case SID_TOOLBOXOPTIONS: + case SID_CONFIGSTATUSBAR: + case SID_CONFIGMENU: + case SID_CONFIGACCEL: + case SID_CONFIGEVENT: + { + SfxAbstractDialogFactory* pFact = + SfxAbstractDialogFactory::Create(); + + const SfxStringItem* pStringItem = rReq.GetArg<SfxStringItem>(SID_CONFIG); + + SfxItemSetFixed<SID_CONFIG, SID_CONFIG, SID_MACROINFO, SID_MACROINFO> aSet( GetPool() ); + + // SID_CONFIG property will determine the default page shown + if ( pStringItem ) + { + aSet.Put( SfxStringItem( + SID_CONFIG, pStringItem->GetValue() ) ); + } + else if (rReq.GetSlot() == SID_CONFIGEVENT) + { + aSet.Put( SfxStringItem( + SID_CONFIG, "private:resource/event/" ) ); + } + else if (rReq.GetSlot() == SID_TOOLBOXOPTIONS) + { + aSet.Put( SfxStringItem( + SID_CONFIG, "private:resource/toolbar/" ) ); + } + +#if HAVE_FEATURE_SCRIPTING + // Preselect a macro in the 'keyboard' page + if (auto const item = rReq.GetArg<SfxMacroInfoItem>(SID_MACROINFO)) { + aSet.Put(*item); + } +#endif + + Reference <XFrame> xFrame(GetRequestFrame(rReq)); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateCustomizeTabDialog(rReq.GetFrameWeld(), + &aSet, xFrame )); + + const short nRet = pDlg->Execute(); + + if ( nRet ) + bDone = true; + break; + } + + case SID_CLOSEDOCS: + { + + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + Reference< XIndexAccess > xTasks = xDesktop->getFrames(); + if ( !xTasks.is() ) + break; + + sal_Int32 n=0; + do + { + if ( xTasks->getCount() <= n ) + break; + + Any aAny = xTasks->getByIndex(n); + Reference < XCloseable > xTask; + aAny >>= xTask; + try + { + xTask->close(true); + n++; + } + catch( CloseVetoException& ) + { + } + } + while( true ); + + bool bOk = ( n == 0); + rReq.SetReturnValue( SfxBoolItem( 0, bOk ) ); + bDone = true; + break; + } + + case SID_SAVEDOCS: + { + bool bOK = true; + for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst(); + pObjSh; + pObjSh = SfxObjectShell::GetNext( *pObjSh ) ) + { + SfxRequest aReq( SID_SAVEDOC, SfxCallMode::SLOT, pObjSh->GetPool() ); + if ( pObjSh->IsModified() && !pObjSh->isSaveLocked()) + { + pObjSh->ExecuteSlot( aReq ); + const SfxBoolItem* pItem(dynamic_cast<const SfxBoolItem*>(aReq.GetReturnValue().getItem())); + if ( !pItem || !pItem->GetValue() ) + bOK = false; + } + } + + rReq.SetReturnValue( SfxBoolItem( 0, bOK ) ); + rReq.Done(); + break; + } + + case SID_SEND_FEEDBACK: + { + OUString module = SfxHelp::GetCurrentModuleIdentifier(); + OUString sURL(officecfg::Office::Common::Menus::SendFeedbackURL::get() + //officecfg/registry/data/org/openoffice/Office/Common.xcu => https://hub.libreoffice.org/send-feedback/ + "?LOversion=" + utl::ConfigManager::getAboutBoxProductVersion() + + "&LOlocale=" + utl::ConfigManager::getUILocale() + + "&LOmodule=" + module.subView(module.lastIndexOf('.') + 1 ) ); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } + + case SID_Q_AND_A: + { + // Askbot has URL's normalized to languages, not locales + // Get language from locale: ll or lll or ll-CC or lll-CC + + OUString sURL(officecfg::Office::Common::Menus::QA_URL::get() + //https://hub.libreoffice.org/forum/ + "?LOlocale=" + utl::ConfigManager::getUILocale()); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } + case SID_DOCUMENTATION: + { + // Open documentation page based on locales + OUString sURL(officecfg::Office::Common::Menus::DocumentationURL::get() + //https://hub.libreoffice.org/documentation/ + "?LOlocale=" + utl::ConfigManager::getUILocale()); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } +#if !ENABLE_WASM_STRIP_PINGUSER + case SID_GETINVOLVED: + { + // Open get involved/join us page based on locales + OUString sURL(officecfg::Office::Common::Menus::GetInvolvedURL::get() + //https://hub.libreoffice.org/joinus/ + "?LOlocale=" + utl::ConfigManager::getUILocale()); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } + case SID_DONATION: + { + // Open donation page based on language + script (BCP47) with language as fall back. + OUString aLang = LanguageTag(utl::ConfigManager::getUILocale()).getLanguage(); + OUString aBcp47 = LanguageTag(utl::ConfigManager::getUILocale()).getBcp47(); + OUString sURL(officecfg::Office::Common::Menus::DonationURL::get() + //https://hub.libreoffice.org/donation/ + "?BCP47=" + aBcp47 + "&LOlang=" + aLang ); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } + case SID_WHATSNEW: + { + // Open release notes depending on version and locale + OUString sURL(officecfg::Office::Common::Menus::ReleaseNotesURL::get() + //https://hub.libreoffice.org/ReleaseNotes/ + "?LOvers=" + utl::ConfigManager::getProductVersion() + + "&LOlocale=" + LanguageTag(utl::ConfigManager::getUILocale()).getBcp47() ); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } + case SID_HYPHENATIONMISSING: + { + // Open wiki page about hyphenation + OUString sURL(officecfg::Office::Common::Menus::HyphenationMissingURL::get() + //https://hub.libreoffice.org/HyphenationMissing/ + "?LOlocale=" + utl::ConfigManager::getUILocale()); + sfx2::openUriExternally(sURL, false, rReq.GetFrameWeld()); + break; + } +#endif + case SID_SHOW_LICENSE: + { + LicenseDialog aDialog(rReq.GetFrameWeld()); + aDialog.run(); + break; + } + + case SID_SHOW_CREDITS: + { + showDocument( "CREDITS" ); + break; + } + + case FN_CHANGE_THEME: + { + const SfxStringItem* pNewThemeArg = rReq.GetArg<SfxStringItem>(FN_PARAM_NEW_THEME); + if (!pNewThemeArg) + { + SAL_WARN("sfx.appl", "FN_CHANGE_THEME: no theme name"); + break; + } + const OUString& rSchemeName = pNewThemeArg->GetValue(); + svtools::EditableColorConfig aEditableConfig; + // kit explicitly ignores changes to the global color scheme, except for the current ViewShell, + // so an attempted change to the same global color scheme when the now current ViewShell ignored + // the last change requires re-sending the change. In which case individual shells will have to + // decide if this color-scheme change is a change from their perspective to avoid unnecessary + // invalidations. + if (aEditableConfig.GetCurrentSchemeName() != rSchemeName || comphelper::LibreOfficeKit::isActive()) + aEditableConfig.LoadScheme(rSchemeName); + break; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + case SID_HELPINDEX: + { + Help* pHelp = Application::GetHelp(); + if ( pHelp ) + { + pHelp->Start(".uno:HelpIndex", rReq.GetFrameWeld()); // show start page + bDone = true; + } + break; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + case SID_HELPTIPS: + { + // Evaluate Parameter + const SfxBoolItem* pOnItem = rReq.GetArg<SfxBoolItem>(SID_HELPTIPS); + bool bOn = pOnItem + ? pOnItem->GetValue() + : !Help::IsQuickHelpEnabled(); + + if ( bOn ) + Help::EnableQuickHelp(); + else + Help::DisableQuickHelp(); + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Help::Tip::set(bOn, xChanges); + xChanges->commit(); + Invalidate(SID_HELPTIPS); + bDone = true; + + // Record if possible + if ( !rReq.IsAPI() ) + rReq.AppendItem( SfxBoolItem( SID_HELPTIPS, bOn) ); + break; + } + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + case SID_EXTENDEDHELP: + { + Help::StartExtHelp(); + break; + } + case SID_HELPBALLOONS: + { + // Evaluate Parameter + const SfxBoolItem* pOnItem = rReq.GetArg<SfxBoolItem>(SID_HELPBALLOONS); + bool bOn = pOnItem + ? pOnItem->GetValue() + : !Help::IsBalloonHelpEnabled(); + + if ( bOn ) + Help::EnableBalloonHelp(); + else + Help::DisableBalloonHelp(); + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Help::ExtendedTip::set(bOn, xChanges); + xChanges->commit(); + Invalidate(SID_HELPBALLOONS); + bDone = true; + + // Record if possible + if ( !rReq.IsAPI() ) + rReq.AppendItem( SfxBoolItem( SID_HELPBALLOONS, bOn) ); + break; + } + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#if !ENABLE_WASM_STRIP_PINGUSER + case SID_TIPOFTHEDAY: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateTipOfTheDayDialog(rReq.GetFrameWeld())); + pDlg->StartExecuteAsync(nullptr); + bDone = true; + break; + } +#endif + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + case SID_WIDGET_TEST_DIALOG: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + VclPtr<VclAbstractDialog> pDlg(pFact->CreateWidgetTestDialog(rReq.GetFrameWeld())); + pDlg->StartExecuteAsync([pDlg](sal_Int32 /*nResult*/){ + pDlg->disposeOnce(); + }); + bDone = true; + break; + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + case SID_ABOUT: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateAboutDialog(rReq.GetFrameWeld())); + pDlg->StartExecuteAsync(nullptr); + bDone = true; + break; + } + + case SID_TEMPLATE_MANAGER: + { + SfxTemplateManagerDlg aDialog(rReq.GetFrameWeld()); + aDialog.run(); + bDone = true; + break; + } + + case SID_TEMPLATE_ADDRESSBOOKSOURCE: + { + svt::AddressBookSourceDialog aDialog(rReq.GetFrameWeld(), ::comphelper::getProcessComponentContext()); + aDialog.run(); + bDone = true; + break; + } + +#if HAVE_FEATURE_SCRIPTING + case SID_BASICSTOP: + StarBASIC::Stop(); + break; + + case SID_BASICBREAK : + BasicDLL::BasicBreak(); + break; +#endif + + case SID_ZOOM_50_PERCENT: + case SID_ZOOM_75_PERCENT: + case SID_ZOOM_100_PERCENT: + case SID_ZOOM_150_PERCENT: + case SID_ZOOM_200_PERCENT: + case SID_ZOOM_OPTIMAL: + case SID_ZOOM_ENTIRE_PAGE: + case SID_ZOOM_PAGE_WIDTH: + { + SfxObjectShell* pCurrentShell = SfxObjectShell::Current(); + if (!pCurrentShell) + return; + + // make sure aZoom is initialized with a proper value if SetType + // doesn't work + SvxZoomItem aZoom( SvxZoomType::PERCENT, 100 ); + + switch (rReq.GetSlot()) + { + case SID_ZOOM_50_PERCENT: + aZoom.SetValue(50); + break; + case SID_ZOOM_75_PERCENT: + aZoom.SetValue(75); + break; + case SID_ZOOM_100_PERCENT: + aZoom.SetValue(100); + break; + case SID_ZOOM_150_PERCENT: + aZoom.SetValue(150); + break; + case SID_ZOOM_200_PERCENT: + aZoom.SetValue(200); + break; + case SID_ZOOM_OPTIMAL: + aZoom.SetType( SvxZoomType::OPTIMAL ); + break; + case SID_ZOOM_ENTIRE_PAGE: + aZoom.SetType( SvxZoomType::WHOLEPAGE ); + break; + case SID_ZOOM_PAGE_WIDTH: + aZoom.SetType( SvxZoomType::PAGEWIDTH ); + break; + } + + pCurrentShell->GetDispatcher()->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::ASYNCHRON, { &aZoom }); + + break; + } + case SID_TOOLBAR_MODE: + { + const SfxStringItem* pModeName = rReq.GetArg<SfxStringItem>( SID_TOOLBAR_MODE ); + + if ( !pModeName ) + { + bDone = true; + break; + } + + OUString aNewName(pModeName->GetValue()); + uno::Reference< uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + + // Get information about current frame and module + Reference<XFrame> xCurrentFrame; + vcl::EnumContext::Application eCurrentApp = vcl::EnumContext::Application::NONE; + OUString aCurrentMode; + + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if (pViewFrame) + { + xCurrentFrame = pViewFrame->GetFrame().GetFrameInterface(); + + const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext ); + eCurrentApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xCurrentFrame ) ); + + OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" + + lcl_getAppName( eCurrentApp ); + + const utl::OConfigurationTreeRoot aAppNode( + xContext, + aPath, + true); + if ( !aAppNode.isValid() ) + { + bDone = true; + break; + } + + aCurrentMode = comphelper::getString( aAppNode.getNodeValue( "Active" ) ); + + if ( !comphelper::LibreOfficeKit::isActive() && aCurrentMode == aNewName ) + { + bDone = true; + break; + } + + // Save new toolbar mode for a current module + aAppNode.setNodeValue( "Active", Any( aNewName ) ); + aAppNode.commit(); + } + + // Apply settings for all frames + pViewFrame = SfxViewFrame::GetFirst(); + while( pViewFrame ) + { + // in LOK case we want to apply changes only to the current view + if (comphelper::LibreOfficeKit::isActive() && + pViewFrame != &SfxViewShell::Current()->GetViewFrame()) + { + pViewFrame = SfxViewFrame::GetNext( *pViewFrame ); + continue; + } + + Reference<XFrame> xFrame = pViewFrame->GetFrame().GetFrameInterface(); + + // We want to change mode only for a current app module, ignore other apps + const Reference<frame::XModuleManager> xModuleManager = frame::ModuleManager::create( xContext ); + vcl::EnumContext::Application eApp = vcl::EnumContext::GetApplicationEnum( xModuleManager->identify( xFrame ) ); + if ( eApp != eCurrentApp ) + { + pViewFrame = SfxViewFrame::GetNext( *pViewFrame ); + continue; + } + + Reference<css::beans::XPropertySet> xPropSet( xFrame, UNO_QUERY ); + Reference<css::frame::XLayoutManager> xLayoutManager; + if ( xPropSet.is() ) + { + try + { + Any aValue = xPropSet->getPropertyValue( "LayoutManager" ); + aValue >>= xLayoutManager; + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + { + } + } + + if ( xLayoutManager.is() ) + { + css::uno::Sequence<OUString> aMandatoryToolbars; + css::uno::Sequence<OUString> aUserToolbars; + std::vector<OUString> aBackupList; + OUString aSidebarMode; + + OUString aPath = "org.openoffice.Office.UI.ToolbarMode/Applications/" + + lcl_getAppName( eApp ) + + "/Modes"; + + // Read mode settings + const utl::OConfigurationTreeRoot aModesNode( + xContext, + aPath, + true); + if ( !aModesNode.isValid() ) + { + bDone = true; + break; + } + + const Sequence<OUString> aModeNodeNames( aModesNode.getNodeNames() ); + + for ( const auto& rModeNodeName : aModeNodeNames ) + { + const utl::OConfigurationNode aModeNode( aModesNode.openNode( rModeNodeName ) ); + if ( !aModeNode.isValid() ) + continue; + + OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) ); + + if ( aCommandArg == aNewName ) + { + aMandatoryToolbars = aModeNode.getNodeValue( "Toolbars" ).get< uno::Sequence<OUString> >(); + aUserToolbars = aModeNode.getNodeValue( "UserToolbars" ).get< uno::Sequence<OUString> >(); + aSidebarMode = comphelper::getString( aModeNode.getNodeValue( "Sidebar" ) ); + break; + } + } + + // Backup visible toolbar list and hide all toolbars + const Sequence<Reference<XUIElement>> aUIElements = xLayoutManager->getElements(); + for ( const Reference< XUIElement >& xUIElement : aUIElements ) + { + Reference< XPropertySet > xPropertySet( xUIElement, UNO_QUERY ); + if ( xPropertySet.is() && xUIElement.is() ) + { + try + { + OUString aResName; + sal_Int16 nType( -1 ); + xPropertySet->getPropertyValue( "Type" ) >>= nType; + xPropertySet->getPropertyValue( "ResourceURL" ) >>= aResName; + + if (( nType == css::ui::UIElementType::TOOLBAR ) && + !aResName.isEmpty() ) + { + if ( xLayoutManager->isElementVisible( aResName ) ) + { + aBackupList.push_back( aResName ); + } + xLayoutManager->hideElement( aResName ); + } + } + catch ( const Exception& ) + { + } + } + } + + // Show/Hide the Notebookbar + const SfxStringItem pItem(SID_NOTEBOOKBAR, aNewName); + pViewFrame->GetDispatcher()->ExecuteList(SID_NOTEBOOKBAR, SfxCallMode::SYNCHRON, {&pItem}); + SfxPoolItemHolder aNbItem; + pViewFrame->GetDispatcher()->QueryState(SID_NOTEBOOKBAR, aNbItem); + + // Show toolbars + for ( const OUString& rName : std::as_const(aMandatoryToolbars) ) + { + xLayoutManager->createElement( rName ); + xLayoutManager->showElement( rName ); + } + + for ( const OUString& rName : std::as_const(aUserToolbars) ) + { + xLayoutManager->createElement( rName ); + xLayoutManager->showElement( rName ); + } + + // Sidebar + pViewFrame->ShowChildWindow( SID_SIDEBAR ); + + if (comphelper::LibreOfficeKit::isActive()) + aSidebarMode = "Opened"; + + sfx2::sidebar::SidebarController* pSidebar = + sfx2::sidebar::SidebarController::GetSidebarControllerForFrame( xFrame ); + if ( pSidebar ) + { + if ( aSidebarMode == "Arrow" ) + { + pSidebar->FadeOut(); + } + else if ( aSidebarMode == "Tabs" ) + { + pSidebar->FadeIn(); + pSidebar->RequestOpenDeck(); + pSidebar->RequestCloseDeck(); + } + else if ( aSidebarMode == "Opened" ) + { + pSidebar->FadeIn(); + pSidebar->RequestOpenDeck(); + } + } + + // Save settings + if ( pViewFrame == SfxViewFrame::Current() ) + { + css::uno::Sequence<OUString> aBackup( comphelper::containerToSequence(aBackupList) ); + + for ( const auto& rModeNodeName : aModeNodeNames ) + { + const utl::OConfigurationNode aModeNode( aModesNode.openNode( rModeNodeName ) ); + if ( !aModeNode.isValid() ) + continue; + + OUString aCommandArg = comphelper::getString( aModeNode.getNodeValue( "CommandArg" ) ); + + if ( aCommandArg == aCurrentMode ) + { + aModeNode.setNodeValue( "UserToolbars", Any( aBackup ) ); + break; + } + } + aModesNode.commit(); + } + } + + pViewFrame = SfxViewFrame::GetNext(*pViewFrame); + } + + bDone = true; + break; + } + case SID_TOOLBAR_MODE_UI: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg( + pFact->CreateToolbarmodeDialog(rReq.GetFrameWeld())); + pDlg->Execute(); + bDone = true; + break; + } + case SID_AVAILABLE_TOOLBARS: + { + const SfxStringItem* pToolbarName = rReq.GetArg<SfxStringItem>(SID_AVAILABLE_TOOLBARS); + + if ( pToolbarName ) + { + Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() ); + Reference< XFrame > xFrame = xDesktop->getActiveFrame(); + + Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + if ( xPropSet.is() ) + { + try + { + Any aValue = xPropSet->getPropertyValue("LayoutManager"); + aValue >>= xLayoutManager; + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + { + } + } + + if ( xLayoutManager.is() ) + { + OUString aToolbarName = "private:resource/toolbar/" + + pToolbarName->GetValue(); + + // Evaluate Parameter + bool bShow( !xLayoutManager->isElementVisible( aToolbarName )); + + if ( bShow ) + { + xLayoutManager->createElement( aToolbarName ); + xLayoutManager->showElement( aToolbarName ); + } + else + xLayoutManager->hideElement( aToolbarName ); + } + } + + bDone = true; + break; + } + case SID_MENUBAR: + { + sfx2::SfxNotebookBar::ToggleMenubar(); + bDone = true; + break; + } + case SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW: + { + SfxViewShell* pViewShell = SfxViewShell::Current(); + SfxViewFrame& rViewFrame = pViewShell->GetViewFrame(); + auto nID = rReq.GetSlot(); + rViewFrame.ToggleChildWindow(nID); + + bDone = true; + break; + } + case SID_INSPECT_SELECTED_OBJECT: + { + SfxViewShell* pViewShell = SfxViewShell::Current(); + SfxViewFrame& rViewFrame = pViewShell->GetViewFrame(); + + rViewFrame.ShowChildWindow(SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW, true); + + SfxChildWindow* pChild = rViewFrame.GetChildWindow(SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW); + if (!pChild) + return; + + auto pDockingWin = dynamic_cast<DevelopmentToolDockingWindow*>(pChild->GetWindow()); + if (pDockingWin) + { + pDockingWin->changeToCurrentSelection(); + } + + bDone = true; + break; + } + case SID_SAFE_MODE: + { + SafeModeQueryDialog aDialog(rReq.GetFrameWeld()); + aDialog.run(); + break; + } + case SID_TOOLBAR_LOCK: + { + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + { + Reference<XFrame> xCurrentFrame; + uno::Reference<uno::XComponentContext> xContext + = ::comphelper::getProcessComponentContext(); + xCurrentFrame = pViewFrame->GetFrame().GetFrameInterface(); + const Reference<frame::XModuleManager> xModuleManager + = frame::ModuleManager::create(xContext); + const utl::OConfigurationTreeRoot aAppNode( + xContext, "org.openoffice.Office.UI.GlobalSettings/Toolbars/States", true); + if (aAppNode.isValid()) + { + bool isLocked = comphelper::getBOOL(aAppNode.getNodeValue("Locked")); + aAppNode.setNodeValue("Locked", Any(!isLocked)); + aAppNode.commit(); + //TODO: apply immediately w/o restart needed + SolarMutexGuard aGuard; + svtools::executeRestartDialog(comphelper::getProcessComponentContext(), nullptr, + svtools::RESTART_REASON_UI_CHANGE); + } + } + break; + } + default: + break; + } + + if ( bDone ) + rReq.Done(); +} + +void SfxApplication::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_TEMPLATE_ADDRESSBOOKSOURCE: + if ( !SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE) ) + rSet.Put(SfxVisibilityItem(nWhich, false)); + break; + case SID_QUITAPP: + { + if ( pImpl->nDocModalMode ) + rSet.DisableItem(nWhich); + else + rSet.Put(SfxStringItem(nWhich, SfxResId(STR_QUITAPP))); + break; + } + + case SID_CONFIG: + case SID_TOOLBOXOPTIONS: + case SID_CONFIGSTATUSBAR: + case SID_CONFIGMENU: + case SID_CONFIGACCEL: + case SID_CONFIGEVENT: + { + if( officecfg::Office::Common::Misc::DisableUICustomization::get() ) + rSet.DisableItem(nWhich); + break; + } + +#if HAVE_FEATURE_SCRIPTING + case SID_BASICSTOP: + if ( !StarBASIC::IsRunning() ) + rSet.DisableItem(nWhich); + break; +#endif + + case SID_HELPTIPS: + { + rSet.Put( SfxBoolItem( SID_HELPTIPS, Help::IsQuickHelpEnabled() ) ); + } + break; + case SID_HELPBALLOONS: + { + rSet.Put( SfxBoolItem( SID_HELPBALLOONS, Help::IsBalloonHelpEnabled() ) ); + } + break; + + case SID_EXTENDEDHELP: + { + } + break; + + case SID_CLOSEDOCS: + { + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + Reference< XIndexAccess > xTasks = xDesktop->getFrames(); + if ( !xTasks.is() || !xTasks->getCount() ) + rSet.DisableItem(nWhich); + break; + } + + case SID_SAVEDOCS: + { + bool bModified = false; + for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst(); + pObjSh; + pObjSh = SfxObjectShell::GetNext( *pObjSh ) ) + { + if ( pObjSh->IsModified() && !pObjSh->isSaveLocked() ) + { + bModified = true; + break; + } + } + + if ( !bModified ) + rSet.DisableItem( nWhich ); + break; + } + + case SID_TEMPLATE_MANAGER: + { + if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() ) + { + rSet.DisableItem( nWhich ); + rSet.Put( SfxVisibilityItem( nWhich, false ) ); + } + } + break; + + case SID_ZOOM_50_PERCENT: + case SID_ZOOM_75_PERCENT: + case SID_ZOOM_100_PERCENT: + case SID_ZOOM_150_PERCENT: + case SID_ZOOM_200_PERCENT: + case SID_ZOOM_OPTIMAL: + case SID_ZOOM_ENTIRE_PAGE: + case SID_ZOOM_PAGE_WIDTH: + { + SfxObjectShell* pCurrentShell(SfxObjectShell::Current()); + + SfxPoolItemHolder aResult; + const SfxItemState aState(pCurrentShell ? + pCurrentShell->GetDispatcher()->QueryState(SID_ATTR_ZOOM, aResult) : SfxItemState::DISABLED); + if ( aState == SfxItemState::DISABLED ) + rSet.DisableItem( nWhich ); + } + break; + + case SID_MENUBAR: + { + Reference < XDesktop2 > xDesktop = Desktop::create ( ::comphelper::getProcessComponentContext() ); + Reference< XFrame > xFrame = xDesktop->getActiveFrame(); + + Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + if ( xPropSet.is() ) + { + try + { + Any aValue = xPropSet->getPropertyValue("LayoutManager"); + aValue >>= xLayoutManager; + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + { + } + } + + if ( xLayoutManager.is() ) + { + const bool bState + = xLayoutManager->getElement("private:resource/menubar/menubar").is() + && xLayoutManager->isElementVisible( + "private:resource/menubar/menubar"); + + SfxBoolItem aItem( SID_MENUBAR, bState ); + rSet.Put( aItem ); + } + break; + } + case SID_SAFE_MODE: + { + // no restart in safe mode when already in safe mode + if ( Application::IsSafeModeEnabled() ) + rSet.DisableItem( SID_SAFE_MODE ); + break; + } + case SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW: + { + bool bSuccess = false; + auto* pViewShell = SfxViewShell::Current(); + if (pViewShell) + { + auto& rViewFrame = pViewShell->GetViewFrame(); + if (rViewFrame.KnowsChildWindow(nWhich)) + { + rSet.Put(SfxBoolItem(nWhich, rViewFrame.HasChildWindow(nWhich))); + bSuccess = true; + } + } + + if (!bSuccess) + rSet.DisableItem(nWhich); + } + break; + case SID_INSPECT_SELECTED_OBJECT: + { + bool bSuccess = false; + auto* pViewShell = SfxViewShell::Current(); + if (pViewShell) + { + auto& rViewFrame = pViewShell->GetViewFrame(); + if (rViewFrame.KnowsChildWindow(SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW)) + { + bSuccess = true; + } + } + if (!bSuccess) + rSet.DisableItem(nWhich); + } + break; + case SID_TOOLBAR_LOCK: + { + rSet.Put( SfxBoolItem( SID_TOOLBAR_LOCK, ToolBox::AlwaysLocked() )); + } + break; + default: + break; + } + } + } +} + +#if HAVE_FEATURE_SCRIPTING + +#ifndef DISABLE_DYNLOADING + +typedef rtl_uString* (*basicide_choose_macro)(void*, void*, void*, sal_Bool); + +#else + +extern "C" rtl_uString* basicide_choose_macro(void*, void*, void*, sal_Bool); + +#endif + +static OUString ChooseMacro(weld::Window* pParent, const Reference<XModel>& rxLimitToDocument, const Reference<XFrame>& xDocFrame, bool bChooseOnly) +{ +#ifndef DISABLE_DYNLOADING + basicide_choose_macro pSymbol = reinterpret_cast<basicide_choose_macro>(sfx2::getBasctlFunction("basicide_choose_macro")); +#else +#define pSymbol basicide_choose_macro +#endif + + // call basicide_choose_macro in basctl + rtl_uString* pScriptURL = pSymbol(pParent, rxLimitToDocument.get(), xDocFrame.get(), bChooseOnly); + OUString aScriptURL( pScriptURL ); + rtl_uString_release( pScriptURL ); + return aScriptURL; + +#ifdef DISABLE_DYNLOADING +#undef pSymbol +#endif +} + +#endif + +namespace +{ +#if HAVE_FEATURE_SCRIPTING + weld::Window* lcl_getDialogParent(const Reference<XFrame>& rxFrame) + { + Reference<awt::XWindow> xContainerWindow; + if (rxFrame.is()) + xContainerWindow = rxFrame->getContainerWindow(); + return Application::GetFrameWeld(xContainerWindow); + } + + SfxViewFrame* lcl_getBasicIDEViewFrame( SfxObjectShell const * i_pBasicIDE ) + { + SfxViewFrame* pView = SfxViewFrame::GetFirst( i_pBasicIDE ); + while ( pView ) + { + if ( pView->GetObjectShell()->GetFactory().GetDocumentServiceName() == "com.sun.star.script.BasicIDE" ) + break; + pView = SfxViewFrame::GetNext( *pView, i_pBasicIDE ); + } + return pView; + } + Reference< XFrame > lcl_findStartModuleFrame( const Reference<XComponentContext> & rxContext ) + { + try + { + Reference < XDesktop2 > xDesktop = Desktop::create( rxContext ); + Reference < XIndexAccess > xContainer( xDesktop->getFrames(), UNO_QUERY_THROW ); + + Reference< XModuleManager2 > xCheck = ModuleManager::create(rxContext); + + sal_Int32 nCount = xContainer->getCount(); + for ( sal_Int32 i=0; i<nCount; ++i ) + { + try + { + Reference < XFrame > xFrame( xContainer->getByIndex(i), UNO_QUERY_THROW ); + OUString sModule = xCheck->identify( xFrame ); + if ( sModule == "com.sun.star.frame.StartModule" ) + return xFrame; + } + catch( const UnknownModuleException& ) + { + // silence + } + catch(const Exception&) + { + // re-throw, caught below + throw; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.appl"); + } + return nullptr; + } +#endif // HAVE_FEATURE_SCRIPTING +} + +void SfxApplication::OfaExec_Impl( SfxRequest& rReq ) +{ + switch ( rReq.GetSlot() ) + { + case SID_OPTIONS_TREEDIALOG: + { + OUString sPageURL; + const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(SID_OPTIONS_PAGEURL); + if ( pURLItem ) + sPageURL = pURLItem->GetValue(); + + sal_uInt16 nPageID = 0; + const SfxUInt16Item* pIDItem = rReq.GetArg<SfxUInt16Item>(SID_OPTIONS_PAGEID); + if (pIDItem) + nPageID = pIDItem->GetValue(); + + Reference <XFrame> xFrame(GetRequestFrame(rReq)); + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + VclPtr<VclAbstractDialog> pDlg = + pFact->CreateFrameDialog(rReq.GetFrameWeld(), xFrame, rReq.GetSlot(), nPageID, sPageURL); + short nRet = pDlg->Execute(); + pDlg.disposeAndClear(); + SfxViewFrame* pView = SfxViewFrame::GetFirst(); + while ( pView ) + { + if (nRet == RET_OK) + { + SfxObjectShell* pObjSh = pView->GetObjectShell(); + if (pObjSh) + pObjSh->SetConfigOptionsChecked(false); + } + pView->GetBindings().InvalidateAll(false); + pView = SfxViewFrame::GetNext( *pView ); + } + break; + } + + case SID_MORE_DICTIONARIES: + { + uno::Sequence<beans::PropertyValue> aArgs{ comphelper::makePropertyValue( + "AdditionsTag", OUString("Dictionary")) }; + comphelper::dispatchCommand(".uno:AdditionsDialog", aArgs); + break; + } +#if HAVE_FEATURE_SCRIPTING + case SID_BASICIDE_APPEAR: + { + SfxViewFrame* pView = lcl_getBasicIDEViewFrame( nullptr ); + if ( !pView ) + { + SfxObjectShell* pBasicIDE = SfxObjectShell::CreateObject( "com.sun.star.script.BasicIDE" ); + pBasicIDE->DoInitNew(); + pBasicIDE->SetModified( false ); + try + { + // load the Basic IDE via direct access to the SFX frame loader. A generic loadComponentFromURL + // (which could be done via SfxViewFrame::LoadDocumentIntoFrame) is not feasible here, since the Basic IDE + // does not really play nice with the framework's concept. For instance, it is a "singleton document", + // which conflicts, at the latest, with the framework's concept of loading into _blank frames. + // So, since we know that our frame loader can handle it, we skip the generic framework loader + // mechanism, and the type detection (which doesn't know about the Basic IDE). + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XSynchronousFrameLoader > xLoader( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.office.FrameLoader", xContext), + UNO_QUERY_THROW ); + ::comphelper::NamedValueCollection aLoadArgs; + aLoadArgs.put( "Model", pBasicIDE->GetModel() ); + aLoadArgs.put( "URL", OUString( "private:factory/sbasic" ) ); + + Reference< XFrame > xTargetFrame( lcl_findStartModuleFrame( xContext ) ); + if ( !xTargetFrame.is() ) + xTargetFrame = SfxFrame::CreateBlankFrame(); + ENSURE_OR_THROW( xTargetFrame.is(), "could not obtain a frameto load the Basic IDE into!" ); + + xLoader->load( aLoadArgs.getPropertyValues(), xTargetFrame ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sfx.appl"); + } + + pView = lcl_getBasicIDEViewFrame( pBasicIDE ); + if ( pView ) + pView->SetName( "BASIC:1" ); + } + + if ( pView ) + pView->GetFrame().Appear(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if ( pArgs && pView ) + { + SfxViewShell* pViewShell = pView->GetViewShell(); + SfxObjectShell* pObjShell = pView->GetObjectShell(); + if ( pViewShell && pObjShell ) + { + SfxRequest aReq( SID_BASICIDE_SHOWWINDOW, SfxCallMode::SYNCHRON, pObjShell->GetPool() ); + aReq.SetArgs( *pArgs ); + pViewShell->ExecuteSlot( aReq ); + } + } + + rReq.Done(); + } + break; + + case SID_BASICCHOOSER: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxBoolItem* pItem; + bool bChooseOnly = false; + Reference< XModel > xLimitToModel; + if(pArgs && (pItem = pArgs->GetItemIfSet(SID_RECORDMACRO, false)) ) + { + bool bRecord = pItem->GetValue(); + if ( bRecord ) + { + // !Hack + bChooseOnly = false; + SfxObjectShell* pCurrentShell = SfxObjectShell::Current(); + OSL_ENSURE( pCurrentShell, "macro recording outside an SFX document?" ); + if ( pCurrentShell ) + xLimitToModel = pCurrentShell->GetModel(); + } + } + + Reference <XFrame> xFrame(GetRequestFrame(rReq)); + rReq.SetReturnValue(SfxStringItem(rReq.GetSlot(), ChooseMacro(rReq.GetFrameWeld(), xLimitToModel, xFrame, bChooseOnly))); + rReq.Done(); + } + break; + + case SID_MACROORGANIZER: + { + SAL_INFO("sfx.appl", "handling SID_MACROORGANIZER"); + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_Int16 nTabId = 0; + Reference <XFrame> xFrame; + if (pArgs) + { + if (const SfxUInt16Item* pItem = pArgs->GetItemIfSet(SID_MACROORGANIZER, false)) + nTabId = pItem->GetValue(); + if (const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_2)) + { + // if set then default to showing the macros of the document associated + // with this frame + if (pItem->GetValue()) + xFrame = GetRequestFrame(rReq); + } + } + SfxApplication::MacroOrganizer(rReq.GetFrameWeld(), xFrame, nTabId); + rReq.Done(); + } + break; + + case SID_RUNMACRO: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + SAL_INFO("sfx.appl", "SfxApplication::OfaExec_Impl: case ScriptOrg"); + + Reference <XFrame> xFrame(GetRequestFrame(rReq)); + if ( !xFrame.is() ) + { + if (const SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + xFrame = pViewFrame->GetFrame().GetFrameInterface(); + } + + do // artificial loop for flow control + { + VclPtr<AbstractScriptSelectorDialog> pDlg(pFact->CreateScriptSelectorDialog(lcl_getDialogParent(xFrame), xFrame)); + OSL_ENSURE( pDlg, "SfxApplication::OfaExec_Impl( SID_RUNMACRO ): no dialog!" ); + if ( !pDlg ) + break; + pDlg->SetRunLabel(); + + pDlg->StartExecuteAsync([pDlg, xFrame](sal_Int32 nDialogResult) { + if ( !nDialogResult ) + { + pDlg->disposeOnce(); + return; + } + + Sequence< Any > args; + Sequence< sal_Int16 > outIndex; + Sequence< Any > outArgs; + Any ret; + + Reference< XInterface > xScriptContext; + + Reference< XController > xController; + if ( xFrame.is() ) + xController = xFrame->getController(); + if ( xController.is() ) + xScriptContext = xController->getModel(); + if ( !xScriptContext.is() ) + xScriptContext = xController; + + SfxObjectShell::CallXScript( xScriptContext, pDlg->GetScriptURL(), args, ret, outIndex, outArgs ); + pDlg->disposeOnce(); + }); + } + while ( false ); + rReq.Done(); + } + break; + + case SID_SCRIPTORGANIZER: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + SAL_INFO("sfx.appl", "SfxApplication::OfaExec_Impl: case ScriptOrg"); + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxScriptOrganizerItem* pItem; + OUString aLanguage; + if(pArgs && (pItem = pArgs->GetItemIfSet(SID_SCRIPTORGANIZER, false) )) + { + aLanguage = pItem->getLanguage(); + } + + OUString aLang( aLanguage ); + SAL_INFO("sfx.appl", "SfxApplication::OfaExec_Impl: about to create dialog for: " << aLang); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSvxScriptOrgDialog(rReq.GetFrameWeld(), aLanguage)); + if( pDlg ) + { + pDlg->Execute(); + } + else + { + SAL_WARN("sfx.appl", "no dialog!!!"); + } + rReq.Done(); + } + break; +#endif // HAVE_FEATURE_SCRIPTING + + case SID_OFFICE_CHECK_PLZ: + { + bool bRet = false; + const SfxStringItem* pStringItem = rReq.GetArg<SfxStringItem>(rReq.GetSlot()); + + if ( pStringItem ) + { + bRet = true /*!!!SfxIniManager::CheckPLZ( aPLZ )*/; + } +#if HAVE_FEATURE_SCRIPTING + else + SbxBase::SetError( ERRCODE_BASIC_WRONG_ARGS ); +#endif + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bRet ) ); + } + break; + + case SID_AUTO_CORRECT_DLG: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + SfxItemSetFixed<SID_AUTO_CORRECT_DLG, SID_AUTO_CORRECT_DLG> aSet(GetPool()); + const SfxPoolItem* pItem=nullptr; + const SfxItemSet* pSet = rReq.GetArgs(); + if ( pSet && pSet->GetItemState( SID_AUTO_CORRECT_DLG, false, &pItem ) == SfxItemState::SET ) + aSet.Put( *pItem ); + + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateAutoCorrTabDialog(rReq.GetFrameWeld(), &aSet)); + pDlg->Execute(); + + break; + } + + case SID_NEWSD : + { + SvtModuleOptions aModuleOpt; + if ( !aModuleOpt.IsImpress() ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(rReq.GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SfxResId(STR_MODULENOTINSTALLED))); + xBox->run(); + return; + } + + Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< frame::XDispatchProvider > xProv = drawing::ModuleDispatcher::create( xContext ); + + OUString aCmd = GetInterface()->GetSlot( rReq.GetSlot() )->GetUnoName(); + Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create(xContext) ); + Sequence < beans::PropertyValue > aSeq; + if ( rReq.GetArgs() ) + TransformItems( rReq.GetSlot(), *rReq.GetArgs(), aSeq ); + Any aResult = xHelper->executeDispatch( xProv, aCmd, OUString(), 0, aSeq ); + frame::DispatchResultEvent aEvent; + bool bSuccess = (aResult >>= aEvent) && + (aEvent.State == frame::DispatchResultState::SUCCESS); + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bSuccess ) ); + } + break; + + case FN_LABEL : + case FN_BUSINESS_CARD : + case FN_XFORMS_INIT : + { + Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< frame::XDispatchProvider > xProv = text::ModuleDispatcher::create( xContext ); + + OUString aCmd = GetInterface()->GetSlot( rReq.GetSlot() )->GetUnoName(); + Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create(xContext) ); + Sequence < beans::PropertyValue > aSeq; + if ( rReq.GetArgs() ) + TransformItems( rReq.GetSlot(), *rReq.GetArgs(), aSeq ); + Any aResult = xHelper->executeDispatch( xProv, aCmd, OUString(), 0, aSeq ); + frame::DispatchResultEvent aEvent; + bool bSuccess = (aResult >>= aEvent) && + (aEvent.State == frame::DispatchResultState::SUCCESS); + rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), bSuccess ) ); + } + break; + + case SID_ADDRESS_DATA_SOURCE: + { + try + { + Reference< uno::XComponentContext > xORB = ::comphelper::getProcessComponentContext(); + Reference< ui::dialogs::XExecutableDialog > xDialog = ui::dialogs::AddressBookSourcePilot::createWithParent(xORB, nullptr); + xDialog->execute(); + } + catch(const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sfx.appl"); + } + } + break; + + case SID_COMP_BIBLIOGRAPHY: + lcl_tryLoadBibliography(); + break; + } +} + +void SfxApplication::OfaState_Impl(SfxItemSet &rSet) +{ + SvtModuleOptions aModuleOpt; + + if( !aModuleOpt.IsWriter()) + { + rSet.DisableItem( FN_LABEL ); + rSet.DisableItem( FN_BUSINESS_CARD ); + rSet.DisableItem( FN_XFORMS_INIT ); + } + if ( comphelper::LibreOfficeKit::isActive() ) + rSet.DisableItem( SID_AUTO_CORRECT_DLG ); + + bool bMacrosDisabled + = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get(); + if (bMacrosDisabled) + { + rSet.DisableItem(SID_RUNMACRO); + rSet.DisableItem(SID_MACROORGANIZER); + rSet.DisableItem(SID_SCRIPTORGANIZER); + rSet.DisableItem(SID_BASICIDE_APPEAR); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/appuno.cxx b/sfx2/source/appl/appuno.cxx new file mode 100644 index 0000000000..386cce711c --- /dev/null +++ b/sfx2/source/appl/appuno.cxx @@ -0,0 +1,1845 @@ +/* -*- 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 <fltoptint.hxx> +#include <sfx2/brokenpackageint.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/msg.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/sfxuno.hxx> +#include <sfxslots.hxx> + +#include <sal/config.h> +#include <sal/log.hxx> +#include <comphelper/interaction.hxx> +#include <osl/diagnose.h> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svl/itempool.hxx> +#include <svl/slstitm.hxx> +#include <svl/stritem.hxx> +#include <tools/debug.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/document/BrokenPackageRequest.hpp> +#include <com/sun/star/document/FilterOptionsRequest.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/ucb/XContent.hpp> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::io; + +// needs to be converted to a better data structure +SfxFormalArgument const aFormalArgs[] = { + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "SuggestedSaveAsName", SID_DEFAULTFILENAME }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "SuggestedSaveAsDir", SID_DEFAULTFILEPATH }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "VersionAuthor", SID_DOCINFO_AUTHOR }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "VersionComment", SID_DOCINFO_COMMENTS }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "DontTerminateEdit", FN_PARAM_1 }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "VersionMajor", SID_DOCINFO_MAJOR }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "FilterOptions", SID_FILE_FILTEROPTIONS }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "FilterName", SID_FILTER_NAME }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Margin1", SID_RULER_MARGIN1 }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Margin2", SID_RULER_MARGIN2 }, +// { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "FileName", SID_FILE_NAME }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "URL", SID_FILE_NAME }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "OpenFlags", SID_OPTIONS }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "Overwrite", SID_OVERWRITE }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Password", SID_PASSWORD }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "PasswordInteraction", SID_PASSWORDINTERACTION }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Referer", SID_REFERER }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "SaveTo", SID_SAVETO }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "TemplateName", SID_TEMPLATE_NAME }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "TemplateRegion", SID_TEMPLATE_REGIONNAME }, +// { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Region", SID_TEMPLATE_REGIONNAME }, +// { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "Name", SID_TEMPLATE_NAME }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "Unpacked", SID_UNPACK }, + { reinterpret_cast<SfxType*>(&aSfxInt16Item_Impl), "Version", SID_VERSION }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "SaveACopy", SID_SAVEACOPYITEM }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "NoFileSync", SID_NO_FILE_SYNC }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "NoThumbnail", SID_NO_THUMBNAIL }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "NoEmbDataSet", SID_NO_EMBEDDED_DS }, + { reinterpret_cast<SfxType*>(&aSfxBoolItem_Impl), "IsRedactMode", SID_IS_REDACT_MODE }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "RedactionStyle", SID_REDACTION_STYLE }, + { reinterpret_cast<SfxType*>(&aSfxStringItem_Impl), "AdditionsTag", FN_PARAM_ADDITIONS_TAG }, +}; + +sal_uInt16 const nMediaArgsCount = SAL_N_ELEMENTS(aFormalArgs); + +constexpr OUString sTemplateRegionName = u"TemplateRegionName"_ustr; +constexpr OUString sTemplateName = u"TemplateName"_ustr; +constexpr OUString sAsTemplate = u"AsTemplate"_ustr; +constexpr OUString sOpenNewView = u"OpenNewView"_ustr; +constexpr OUString sViewId = u"ViewId"_ustr; +constexpr OUString sPluginMode = u"PluginMode"_ustr; +constexpr OUString sReadOnly = u"ReadOnly"_ustr; +constexpr OUString sDdeReconnect = u"DDEReconnect"_ustr; +constexpr OUString sStartPresentation = u"StartPresentation"_ustr; +constexpr OUString sFrameName = u"FrameName"_ustr; +constexpr OUString sMediaType = u"MediaType"_ustr; +constexpr OUString sPostData = u"PostData"_ustr; +constexpr OUString sCharacterSet = u"CharacterSet"_ustr; +constexpr OUString sInputStream = u"InputStream"_ustr; +constexpr OUString sStream = u"Stream"_ustr; +constexpr OUString sOutputStream = u"OutputStream"_ustr; +constexpr OUString sHidden = u"Hidden"_ustr; +constexpr OUString sPreview = u"Preview"_ustr; +constexpr OUString sViewOnly = u"ViewOnly"_ustr; +constexpr OUString sDontEdit = u"DontEdit"_ustr; +constexpr OUString sSilent = u"Silent"_ustr; +constexpr OUString sJumpMark = u"JumpMark"_ustr; +constexpr OUString sSalvagedFile = u"SalvagedFile"_ustr; +constexpr OUString sStatusInd = u"StatusIndicator"_ustr; +constexpr OUString sModel = u"Model"_ustr; +constexpr OUString sFrame = u"Frame"_ustr; +constexpr OUString sViewData = u"ViewData"_ustr; +constexpr OUString sFilterData = u"FilterData"_ustr; +constexpr OUString sSelectionOnly = u"SelectionOnly"_ustr; +constexpr OUString sMacroExecMode = u"MacroExecutionMode"_ustr; +constexpr OUString sUpdateDocMode = u"UpdateDocMode"_ustr; +constexpr OUString sMinimized = u"Minimized"_ustr; +constexpr OUString sInteractionHdl = u"InteractionHandler"_ustr; +constexpr OUString sUCBContent = u"UCBContent"_ustr; +constexpr OUString sRepairPackage = u"RepairPackage"_ustr; +constexpr OUString sDocumentTitle = u"DocumentTitle"_ustr; +constexpr OUString sComponentData = u"ComponentData"_ustr; +constexpr OUString sComponentContext = u"ComponentContext"_ustr; +constexpr OUString sDocumentBaseURL = u"DocumentBaseURL"_ustr; +constexpr OUString sHierarchicalDocumentName = u"HierarchicalDocumentName"_ustr; +constexpr OUString sCopyStreamIfPossible = u"CopyStreamIfPossible"_ustr; +constexpr OUString sNoAutoSave = u"NoAutoSave"_ustr; +constexpr OUString sFolderName = u"FolderName"_ustr; +constexpr OUString sUseSystemDialog = u"UseSystemDialog"_ustr; +constexpr OUString sStandardDir = u"StandardDir"_ustr; +constexpr OUString sDenyList = u"DenyList"_ustr; +constexpr OUString sModifyPasswordInfo = u"ModifyPasswordInfo"_ustr; +constexpr OUString sSuggestedSaveAsDir = u"SuggestedSaveAsDir"_ustr; +constexpr OUString sSuggestedSaveAsName = u"SuggestedSaveAsName"_ustr; +constexpr OUString sEncryptionData = u"EncryptionData"_ustr; +constexpr OUString sFailOnWarning = u"FailOnWarning"_ustr; +constexpr OUString sDocumentService = u"DocumentService"_ustr; +constexpr OUString sFilterProvider = u"FilterProvider"_ustr; +constexpr OUString sImageFilter = u"ImageFilter"_ustr; +constexpr OUString sLockContentExtraction = u"LockContentExtraction"_ustr; +constexpr OUString sLockExport = u"LockExport"_ustr; +constexpr OUString sLockPrint = u"LockPrint"_ustr; +constexpr OUString sLockSave = u"LockSave"_ustr; +constexpr OUString sLockEditDoc = u"LockEditDoc"_ustr; +constexpr OUString sReplaceable = u"Replaceable"_ustr; + +static bool isMediaDescriptor( sal_uInt16 nSlotId ) +{ + return ( nSlotId == SID_OPENDOC || nSlotId == SID_EXPORTDOC || + nSlotId == SID_SAVEASDOC || nSlotId == SID_SAVEDOC || + nSlotId == SID_SAVETO || nSlotId == SID_SAVEACOPY || + nSlotId == SID_EXPORTDOCASPDF || nSlotId == SID_DIRECTEXPORTDOCASPDF || + nSlotId == SID_EXPORTDOCASEPUB || nSlotId == SID_DIRECTEXPORTDOCASEPUB || + nSlotId == SID_REDACTDOC || nSlotId == SID_AUTOREDACTDOC || + nSlotId == SID_SAVEACOPYITEM); +} + +void TransformParameters( sal_uInt16 nSlotId, const uno::Sequence<beans::PropertyValue>& rArgs, SfxAllItemSet& rSet, const SfxSlot* pSlot ) +{ + if ( !pSlot ) + pSlot = SFX_SLOTPOOL().GetSlot( nSlotId ); + + if ( !pSlot ) + return; + + if ( nSlotId == SID_OPENURL ) + nSlotId = SID_OPENDOC; + + const sal_Int32 nCount = rArgs.getLength(); + if ( !nCount ) + return; + + const beans::PropertyValue* pPropsVal = rArgs.getConstArray(); + if ( !pSlot->IsMode(SfxSlotMode::METHOD) ) + { + // slot is a property + const SfxType* pType = pSlot->GetType(); + std::unique_ptr<SfxPoolItem> pItem(pType->CreateItem()); + + if ( !pItem ) + { + SAL_WARN( "sfx", "No creator method for item: " << nSlotId ); + return; + } + + sal_uInt16 nWhich = rSet.GetPool()->GetWhich(nSlotId); + bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip ); + pItem->SetWhich( nWhich ); + sal_uInt16 nSubCount = pType->nAttribs; + + const beans::PropertyValue& rProp = pPropsVal[0]; + const OUString& rName = rProp.Name; + if ( nCount == 1 && rName == pSlot->pUnoName ) + { + // there is only one parameter and its name matches the name of the property, + // so it's either a simple property or a complex property in one single UNO struct + if( pItem->PutValue( rProp.Value, bConvertTwips ? CONVERT_TWIPS : 0 ) ) + // only use successfully converted items + rSet.Put( std::move(pItem) ); + else + { + SAL_WARN( "sfx", "Property not convertible: " << pSlot->pUnoName ); + } + } +#ifdef DBG_UTIL + else if ( nSubCount == 0 ) + { + // for a simple property there can be only one parameter and its name *must* match + SAL_WARN("sfx.appl", "Property name does not match: " << rName); + } +#endif + else + { + // there is more than one parameter and the property is a complex one +#ifdef DBG_UTIL + // if the dispatch API is used for UI purposes or from the testtool, + // it is possible to skip some or all arguments, + // but it indicates an error for macro recording; + // so this should be notified as a warning only + if ( nCount != nSubCount ) + { + SAL_INFO("sfx.appl", "MacroPlayer: wrong number of parameters for slot: " << nSlotId ); + } +#endif + // complex property; collect sub items from the parameter set and reconstruct complex item + sal_uInt16 nFound=0; + for ( const beans::PropertyValue& rPropValue : rArgs ) + { + sal_uInt16 nSub; + for ( nSub=0; nSub<nSubCount; nSub++ ) + { + // search sub item by name + OUString aStr = pSlot->pUnoName + "." + OUString::createFromAscii(pType->aAttrib[nSub].pName); + if ( rPropValue.Name == aStr ) + { + sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(pType->aAttrib[nSub].nAID)); + if ( bConvertTwips ) + nSubId |= CONVERT_TWIPS; + if ( pItem->PutValue( rPropValue.Value, nSubId ) ) + nFound++; + else + { + SAL_WARN( "sfx.appl", "Property not convertible: " << pSlot->pUnoName); + } + break; + } + } + + // there was a parameter with a name that didn't match to any of the members + SAL_WARN_IF( nSub >= nSubCount, "sfx.appl", "Property name does not match: " << rPropValue.Name ); + } + + // at least one part of the complex item must be present; other parts can have default values + if ( nFound > 0 ) + rSet.Put( std::move(pItem) ); + } + + return; + } + +#ifdef DBG_UTIL + // detect parameters that don't match to any formal argument or one of its members + sal_Int32 nFoundArgs = 0; +#endif + // slot is a method + bool bIsMediaDescriptor = isMediaDescriptor( nSlotId ); + sal_uInt16 nMaxArgs = bIsMediaDescriptor ? nMediaArgsCount : pSlot->nArgDefCount; + for ( sal_uInt16 nArgs=0; nArgs<nMaxArgs; nArgs++ ) + { + const SfxFormalArgument &rArg = bIsMediaDescriptor ? aFormalArgs[nArgs] : pSlot->GetFormalArgument( nArgs ); + std::unique_ptr<SfxPoolItem> pItem(rArg.CreateItem()); + if ( !pItem ) + { + SAL_WARN( "sfx", "No creator method for argument: " << rArg.pName ); + return; + } + + sal_uInt16 nWhich = rSet.GetPool()->GetWhich(rArg.nSlotId); + bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip ); + pItem->SetWhich( nWhich ); + const SfxType* pType = rArg.pType; + sal_uInt16 nSubCount = pType->nAttribs; + if ( nSubCount == 0 ) + { + // "simple" (base type) argument + auto pProp = std::find_if(rArgs.begin(), rArgs.end(), + [&rArg](const beans::PropertyValue& rProp) { return rProp.Name.equalsAscii(rArg.pName); }); + if (pProp != rArgs.end()) + { +#ifdef DBG_UTIL + ++nFoundArgs; +#endif + if( pItem->PutValue( pProp->Value, 0 ) ) + // only use successfully converted items + rSet.Put( std::move(pItem) ); + else + { + SAL_WARN( "sfx", "Property not convertible: " << rArg.pName ); + } + } + } + else + { + // complex argument, could be passed in one struct + bool bAsWholeItem = false; + for ( const beans::PropertyValue& rProp : rArgs ) + { + const OUString& rName = rProp.Name; + if ( rName == OUString(rArg.pName, strlen(rArg.pName), RTL_TEXTENCODING_UTF8) ) + { + bAsWholeItem = true; +#ifdef DBG_UTIL + ++nFoundArgs; +#endif + if( pItem->PutValue( rProp.Value, 0 ) ) + // only use successfully converted items + rSet.Put( std::move(pItem) ); + else + { + SAL_WARN( "sfx", "Property not convertible: " << rArg.pName ); + } + } + } + + if ( !bAsWholeItem ) + { + // complex argument; collect sub items from argument array and reconstruct complex item + // only put item if at least one member was found and had the correct type + // (is this a good idea?! Should we ask for *all* members?) + bool bRet = false; + for ( const beans::PropertyValue& rProp : rArgs ) + { + for ( sal_uInt16 nSub=0; nSub<nSubCount; nSub++ ) + { + // search sub item by name + OString aStr = OString::Concat(rArg.pName) + "." + pType->aAttrib[nSub].pName; + if ( rProp.Name.equalsAsciiL(aStr.getStr(), aStr.getLength()) ) + { + // at least one member found ... + bRet = true; +#ifdef DBG_UTIL + ++nFoundArgs; +#endif + sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(pType->aAttrib[nSub].nAID)); + if ( bConvertTwips ) + nSubId |= CONVERT_TWIPS; + if (!pItem->PutValue( rProp.Value, nSubId ) ) + { + // ... but it was not convertible + bRet = false; + SAL_WARN( "sfx", "Property not convertible: " << rArg.pName ); + } + + break; + } + } + } + + if ( bRet ) + // only use successfully converted items + rSet.Put( std::move(pItem) ); + + } + } + } + + // special additional parameters for some slots not seen in the slot definitions + // Some of these slots are not considered to be used for macro recording, because they shouldn't be recorded as slots, + // but as dispatching or factory or arbitrary URLs to the frame + // Some also can use additional arguments that are not recordable (will be changed later, + // f.e. "SaveAs" shouldn't support parameters not in the slot definition!) + if ( nSlotId == SID_NEWWINDOW ) + { + for ( const beans::PropertyValue& rProp : rArgs ) + { + const OUString& rName = rProp.Name; + if ( rName == sFrame ) + { + Reference< XFrame > xFrame; + OSL_VERIFY( rProp.Value >>= xFrame ); + rSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrame ) ); + } + else + if ( rName == sHidden ) + { + bool bVal = false; + if (rProp.Value >>= bVal) + rSet.Put( SfxBoolItem( SID_HIDDEN, bVal ) ); + } + } + } + else if ( bIsMediaDescriptor ) + { + for ( const beans::PropertyValue& rProp : rArgs ) + { +#ifdef DBG_UTIL + ++nFoundArgs; +#endif + const OUString& aName = rProp.Name; + if ( aName == sModel ) + rSet.Put( SfxUnoAnyItem( SID_DOCUMENT, rProp.Value ) ); + else if ( aName == sComponentData ) + { + rSet.Put( SfxUnoAnyItem( SID_COMPONENTDATA, rProp.Value ) ); + } + else if ( aName == sComponentContext ) + { + rSet.Put( SfxUnoAnyItem( SID_COMPONENTCONTEXT, rProp.Value ) ); + } + else if ( aName == sStatusInd ) + { + Reference<task::XStatusIndicator> xVal; + bool bOK = (rProp.Value >>= xVal); + DBG_ASSERT( bOK, "invalid type for StatusIndicator" ); + if (bOK && xVal.is()) + rSet.Put( SfxUnoAnyItem( SID_PROGRESS_STATUSBAR_CONTROL, rProp.Value ) ); + } + else if ( aName == sInteractionHdl ) + { + Reference<task::XInteractionHandler> xVal; + bool bOK = (rProp.Value >>= xVal); + DBG_ASSERT( bOK, "invalid type for InteractionHandler" ); + if (bOK && xVal.is()) + rSet.Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, rProp.Value ) ); + } + else if ( aName == sViewData ) + rSet.Put( SfxUnoAnyItem( SID_VIEW_DATA, rProp.Value ) ); + else if ( aName == sFilterData ) + rSet.Put( SfxUnoAnyItem( SID_FILTER_DATA, rProp.Value ) ); + else if ( aName == sInputStream ) + { + Reference< XInputStream > xVal; + bool bOK = ((rProp.Value >>= xVal) && xVal.is()); + DBG_ASSERT( bOK, "invalid type for InputStream" ); + if (bOK) + rSet.Put( SfxUnoAnyItem( SID_INPUTSTREAM, rProp.Value ) ); + } + else if ( aName == sStream ) + { + Reference< XInputStream > xVal; + bool bOK = ((rProp.Value >>= xVal) && xVal.is()); + DBG_ASSERT( bOK, "invalid type for Stream" ); + if (bOK) + rSet.Put( SfxUnoAnyItem( SID_STREAM, rProp.Value ) ); + } + else if ( aName == sUCBContent ) + { + Reference< XContent > xVal; + bool bOK = ((rProp.Value >>= xVal) && xVal.is()); + DBG_ASSERT( bOK, "invalid type for UCBContent" ); + if (bOK) + rSet.Put( SfxUnoAnyItem( SID_CONTENT, rProp.Value ) ); + } + else if ( aName == sOutputStream ) + { + Reference< XOutputStream > xVal; + bool bOK = ((rProp.Value >>= xVal) && xVal.is()); + DBG_ASSERT( bOK, "invalid type for OutputStream" ); + if (bOK) + rSet.Put( SfxUnoAnyItem( SID_OUTPUTSTREAM, rProp.Value ) ); + } + else if ( aName == sPostData ) + { + Reference< XInputStream > xVal; + bool bOK = (rProp.Value >>= xVal); + DBG_ASSERT( bOK, "invalid type for PostData" ); + if (bOK) + rSet.Put( SfxUnoAnyItem( SID_POSTDATA, rProp.Value ) ); + } + else if ( aName == sFrame ) + { + Reference< XFrame > xFrame; + bool bOK = (rProp.Value >>= xFrame); + DBG_ASSERT( bOK, "invalid type for Frame" ); + if (bOK) + rSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrame ) ); + } + else if ( aName == sAsTemplate ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for AsTemplate" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_TEMPLATE, bVal ) ); + } + else if ( aName == sOpenNewView ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for OpenNewView" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_OPEN_NEW_VIEW, bVal ) ); + } + else if ( aName == sFailOnWarning ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for FailOnWarning" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_FAIL_ON_WARNING, bVal ) ); + } + else if ( aName == sViewId ) + { + sal_Int16 nVal = -1; + bool bOK = ((rProp.Value >>= nVal) && (nVal != -1)); + DBG_ASSERT( bOK, "invalid type for ViewId" ); + if (bOK) + rSet.Put( SfxUInt16Item( SID_VIEW_ID, nVal ) ); + } + else if ( aName == sPluginMode ) + { + sal_Int16 nVal = -1; + bool bOK = ((rProp.Value >>= nVal) && (nVal != -1)); + DBG_ASSERT( bOK, "invalid type for PluginMode" ); + if (bOK) + rSet.Put( SfxUInt16Item( SID_PLUGIN_MODE, nVal ) ); + } + else if ( aName == sReadOnly ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for ReadOnly" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_DOC_READONLY, bVal ) ); + } + else if ( aName == sDdeReconnect ) + { + bool bVal = true; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for DDEReconnect" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_DDE_RECONNECT_ONLOAD, bVal ) ); + } + else if ( aName == sStartPresentation ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for StartPresentation" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_DOC_STARTPRESENTATION, bVal ) ); + } + else if ( aName == sSelectionOnly ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for SelectionOnly" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_SELECTION, bVal ) ); + } + else if ( aName == sHidden ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for Hidden" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_HIDDEN, bVal ) ); + } + else if ( aName == sMinimized ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for Minimized" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_MINIMIZED, bVal ) ); + } + else if ( aName == sSilent ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for Silent" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_SILENT, bVal ) ); + } + else if ( aName == sPreview ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for Preview" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_PREVIEW, bVal ) ); + } + else if ( aName == sViewOnly ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for ViewOnly" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_VIEWONLY, bVal ) ); + } + else if ( aName == sDontEdit ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for ViewOnly" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_EDITDOC, !bVal ) ); + } + else if ( aName == sUseSystemDialog ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for ViewOnly" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_FILE_DIALOG, bVal ) ); + } + else if ( aName == sStandardDir ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for StandardDir" ); + if (bOK) + rSet.Put( SfxStringItem( SID_STANDARD_DIR, sVal ) ); + } + else if ( aName == sDenyList ) + { + uno::Sequence<OUString> xVal; + bool bOK = (rProp.Value >>= xVal); + DBG_ASSERT( bOK, "invalid type or value for DenyList" ); + if (bOK) + { + SfxStringListItem stringList(SID_DENY_LIST); + stringList.SetStringList( xVal ); + rSet.Put( stringList ); + } + } + else if ( aName == "FileName" ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for FileName" ); + if (bOK) + rSet.Put( SfxStringItem( SID_FILE_NAME, sVal ) ); + } + else if ( aName == sSalvagedFile ) + { + OUString sVal; + bool bOK = (rProp.Value >>= sVal); + DBG_ASSERT( bOK, "invalid type or value for SalvagedFile" ); + if (bOK) + rSet.Put( SfxStringItem( SID_DOC_SALVAGE, sVal ) ); + } + else if ( aName == sFolderName ) + { + OUString sVal; + bool bOK = (rProp.Value >>= sVal); + DBG_ASSERT( bOK, "invalid type or value for FolderName" ); + if (bOK) + rSet.Put( SfxStringItem( SID_PATH, sVal ) ); + } + else if ( aName == sFrameName ) + { + OUString sVal; + bool bOK = (rProp.Value >>= sVal); + DBG_ASSERT( bOK, "invalid type for FrameName" ); + if (bOK && !sVal.isEmpty()) + rSet.Put( SfxStringItem( SID_TARGETNAME, sVal ) ); + } + else if ( aName == sMediaType ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for MediaType" ); + if (bOK) + rSet.Put( SfxStringItem( SID_CONTENTTYPE, sVal ) ); + } + else if ( aName == sTemplateName ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for TemplateName" ); + if (bOK) + rSet.Put( SfxStringItem( SID_TEMPLATE_NAME, sVal ) ); + } + else if ( aName == sTemplateRegionName ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for TemplateRegionName" ); + if (bOK) + rSet.Put( SfxStringItem( SID_TEMPLATE_REGIONNAME, sVal ) ); + } + else if ( aName == sJumpMark ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for JumpMark" ); + if (bOK) + rSet.Put( SfxStringItem( SID_JUMPMARK, sVal ) ); + } + else if ( aName == sCharacterSet ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for CharacterSet" ); + if (bOK) + rSet.Put( SfxStringItem( SID_CHARSET, sVal ) ); + } + else if ( aName == "FilterFlags" ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for FilterFlags" ); + if (bOK) + rSet.Put( SfxStringItem( SID_FILE_FILTEROPTIONS, sVal ) ); + } + else if ( aName == sImageFilter ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for FilterFlags" ); + if (bOK) + rSet.Put( SfxStringItem( SID_CONVERT_IMAGES, sVal ) ); + } + else if ( aName == sMacroExecMode ) + { + sal_Int16 nVal =-1; + bool bOK = ((rProp.Value >>= nVal) && (nVal != -1)); + DBG_ASSERT( bOK, "invalid type for MacroExecMode" ); + if (bOK) + rSet.Put( SfxUInt16Item( SID_MACROEXECMODE, nVal ) ); + } + else if ( aName == sUpdateDocMode ) + { + sal_Int16 nVal =-1; + bool bOK = ((rProp.Value >>= nVal) && (nVal != -1)); + DBG_ASSERT( bOK, "invalid type for UpdateDocMode" ); + if (bOK) + rSet.Put( SfxUInt16Item( SID_UPDATEDOCMODE, nVal ) ); + } + else if ( aName == sRepairPackage ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for RepairPackage" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_REPAIRPACKAGE, bVal ) ); + } + else if ( aName == sDocumentTitle ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for DocumentTitle" ); + if (bOK) + rSet.Put( SfxStringItem( SID_DOCINFO_TITLE, sVal ) ); + } + else if ( aName == sDocumentBaseURL ) + { + OUString sVal; + // the base url can be set to empty ( for embedded objects for example ) + bool bOK = (rProp.Value >>= sVal); + DBG_ASSERT( bOK, "invalid type or value for DocumentBaseURL" ); + if (bOK) + rSet.Put( SfxStringItem( SID_DOC_BASEURL, sVal ) ); + } + else if ( aName == sHierarchicalDocumentName ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for HierarchicalDocumentName" ); + if (bOK) + rSet.Put( SfxStringItem( SID_DOC_HIERARCHICALNAME, sVal ) ); + } + else if ( aName == sCopyStreamIfPossible ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for CopyStreamIfPossible" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_COPY_STREAM_IF_POSSIBLE, bVal ) ); + } + else if ( aName == sNoAutoSave ) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for NoAutoSave" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_NOAUTOSAVE, bVal ) ); + } + else if ( aName == sModifyPasswordInfo ) + { + rSet.Put( SfxUnoAnyItem( SID_MODIFYPASSWORDINFO, rProp.Value ) ); + } + else if ( aName == sEncryptionData ) + { + rSet.Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, rProp.Value ) ); + } + else if ( aName == sSuggestedSaveAsDir ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for SuggestedSaveAsDir" ); + if (bOK) + rSet.Put( SfxStringItem( SID_SUGGESTEDSAVEASDIR, sVal ) ); + } + else if ( aName == sSuggestedSaveAsName ) + { + OUString sVal; + bool bOK = ((rProp.Value >>= sVal) && !sVal.isEmpty()); + DBG_ASSERT( bOK, "invalid type or value for SuggestedSaveAsName" ); + if (bOK) + rSet.Put( SfxStringItem( SID_SUGGESTEDSAVEASNAME, sVal ) ); + } + else if (aName == sDocumentService) + { + OUString aVal; + bool bOK = ((rProp.Value >>= aVal) && !aVal.isEmpty()); + if (bOK) + rSet.Put(SfxStringItem(SID_DOC_SERVICE, aVal)); + } + else if (aName == sFilterProvider) + { + OUString aVal; + bool bOK = ((rProp.Value >>= aVal) && !aVal.isEmpty()); + if (bOK) + rSet.Put(SfxStringItem(SID_FILTER_PROVIDER, aVal)); + } + else if (aName == sLockContentExtraction) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for LockContentExtraction" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_LOCK_CONTENT_EXTRACTION, bVal ) ); + } + else if (aName == sLockExport) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for LockExport" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_LOCK_EXPORT, bVal ) ); + } + else if (aName == sLockPrint) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for LockPrint" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_LOCK_PRINT, bVal ) ); + } + else if (aName == sLockSave) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for LockSave" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_LOCK_SAVE, bVal ) ); + } + else if (aName == sLockEditDoc) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT( bOK, "invalid type for LockEditDoc" ); + if (bOK) + rSet.Put( SfxBoolItem( SID_LOCK_EDITDOC, bVal ) ); + } + else if (aName == sReplaceable) + { + bool bVal = false; + bool bOK = (rProp.Value >>= bVal); + DBG_ASSERT(bOK, "invalid type for Replaceable"); + if (bOK) + rSet.Put(SfxBoolItem(SID_REPLACEABLE, bVal)); + } +#ifdef DBG_UTIL + else + --nFoundArgs; +#endif + } + } + // API to raise options dialog with a specified options ab page (#i83757#) + else + { + // transform parameter "OptionsPageURL" of slot "OptionsTreeDialog" + if ( "OptionsTreeDialog" == pSlot->pUnoName ) + { + auto pProp = std::find_if(rArgs.begin(), rArgs.end(), + [](const PropertyValue& rProp) { return rProp.Name == "OptionsPageURL" || rProp.Name == "OptionsPageID"; }); + if (pProp != rArgs.end()) + { + OUString sURL; + sal_uInt16 nPageID; + if ( pProp->Name == "OptionsPageURL" && (pProp->Value >>= sURL) ) + rSet.Put( SfxStringItem( SID_OPTIONS_PAGEURL, sURL ) ); + else if ( pProp->Name == "OptionsPageID" && (pProp->Value >>= nPageID) ) + rSet.Put( SfxUInt16Item( SID_OPTIONS_PAGEID, nPageID ) ); + } + } + } +#ifdef DBG_UTIL + if ( nFoundArgs == nCount ) + { + // except for the "special" slots: assure that every argument was convertible + SAL_INFO( "sfx.appl", "MacroPlayer: Some properties didn't match to any formal argument for slot: "<< pSlot->pUnoName ); + } +#endif +} + +void TransformItems( sal_uInt16 nSlotId, const SfxItemSet& rSet, uno::Sequence<beans::PropertyValue>& rArgs, const SfxSlot* pSlot ) +{ + if ( !pSlot ) + pSlot = SFX_SLOTPOOL().GetSlot( nSlotId ); + + if ( !pSlot) + return; + + if ( nSlotId == SID_OPENURL ) + nSlotId = SID_OPENDOC; + if ( nSlotId == SID_SAVEASREMOTE ) + nSlotId = SID_SAVEASDOC; + + // find number of properties to avoid permanent reallocations in the sequence + sal_Int32 nProps=0; + +#ifdef DBG_UTIL + // trace number of items and compare with number of properties for debugging purposes + sal_Int32 nItems=0; +#endif + + const SfxType *pType = pSlot->GetType(); + if ( !pSlot->IsMode(SfxSlotMode::METHOD) ) + { + // slot is a property + sal_uInt16 nWhich = rSet.GetPool()->GetWhich(nSlotId); + if ( rSet.GetItemState( nWhich ) == SfxItemState::SET ) //??? + { + sal_uInt16 nSubCount = pType->nAttribs; + if ( nSubCount ) + // it's a complex property, we want it split into simple types + // so we expect to get as many items as we have (sub) members + nProps = nSubCount; + else + // simple property: we expect to get exactly one item + nProps++; + } + else + { + // we will not rely on the "toggle" ability of some property slots + SAL_WARN( "sfx", "Processing property slot without argument: " << nSlotId ); + } + +#ifdef DBG_UTIL + nItems++; +#endif + } + else + { + // slot is a method + bool bIsMediaDescriptor = isMediaDescriptor( nSlotId ); + sal_uInt16 nFormalArgs = bIsMediaDescriptor ? nMediaArgsCount : pSlot->GetFormalArgumentCount(); + for ( sal_uInt16 nArg=0; nArg<nFormalArgs; ++nArg ) + { + // check every formal argument of the method + const SfxFormalArgument &rArg = bIsMediaDescriptor ? aFormalArgs[nArg] : pSlot->GetFormalArgument( nArg ); + + sal_uInt16 nWhich = rSet.GetPool()->GetWhich( rArg.nSlotId ); + if ( rSet.GetItemState( nWhich ) == SfxItemState::SET ) //??? + { + sal_uInt16 nSubCount = rArg.pType->nAttribs; + if ( nSubCount ) + // argument has a complex type, we want it split into simple types + // so for this argument we expect to get as many items as we have (sub) members + nProps += nSubCount; + else + // argument of simple type: we expect to get exactly one item for it + nProps++; +#ifdef DBG_UTIL + nItems++; +#endif + } + } + + // special treatment for slots that are *not* meant to be recorded as slots (except SaveAs/To) + if ( bIsMediaDescriptor ) + { + sal_Int32 nAdditional=0; + if ( rSet.GetItemState( SID_PROGRESS_STATUSBAR_CONTROL ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_INTERACTIONHANDLER ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOC_SALVAGE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_PATH ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_FILE_DIALOG ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_STANDARD_DIR ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DENY_LIST ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_CONTENT ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_INPUTSTREAM ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_STREAM ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_OUTPUTSTREAM ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_TEMPLATE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_OPEN_NEW_VIEW ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_FAIL_ON_WARNING ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_VIEW_ID ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_VIEW_DATA ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_FILTER_DATA ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_PLUGIN_MODE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOC_READONLY ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DDE_RECONNECT_ONLOAD ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOC_STARTPRESENTATION ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_SELECTION ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_CONTENTTYPE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_POSTDATA ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_FILLFRAME ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_CHARSET ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_TARGETNAME ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_TEMPLATE_NAME ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_TEMPLATE_REGIONNAME ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_HIDDEN ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_MINIMIZED ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_PREVIEW ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_VIEWONLY ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_EDITDOC ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_SILENT ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_JUMPMARK ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOCUMENT ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_MACROEXECMODE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_UPDATEDOCMODE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_REPAIRPACKAGE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOCINFO_TITLE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_COMPONENTDATA ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_COMPONENTCONTEXT ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOC_BASEURL ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOC_HIERARCHICALNAME ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_COPY_STREAM_IF_POSSIBLE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_NOAUTOSAVE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_MODIFYPASSWORDINFO ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_SUGGESTEDSAVEASDIR ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_ENCRYPTIONDATA ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_SUGGESTEDSAVEASNAME ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_DOC_SERVICE ) == SfxItemState::SET ) + nAdditional++; + if (rSet.HasItem(SID_FILTER_PROVIDER)) + ++nAdditional; + if ( rSet.GetItemState( SID_CONVERT_IMAGES ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_LOCK_CONTENT_EXTRACTION ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_LOCK_EXPORT ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_LOCK_PRINT ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_LOCK_SAVE ) == SfxItemState::SET ) + nAdditional++; + if ( rSet.GetItemState( SID_LOCK_EDITDOC ) == SfxItemState::SET ) + nAdditional++; + if (rSet.GetItemState(SID_REPLACEABLE) == SfxItemState::SET) + nAdditional++; + + // consider additional arguments + nProps += nAdditional; +#ifdef DBG_UTIL + nItems += nAdditional; +#endif + } + } + +#ifdef DBG_UTIL + // now check the itemset: is there any item that is not convertible using the list of formal arguments + // or the table of additional items?! + if ( rSet.Count() != nItems ) + { + // detect unknown item and present error message + for ( auto const & rPair : rSet.GetRanges() ) + { + sal_uInt16 nStartWhich = rPair.first; + sal_uInt16 nEndWhich = rPair.second; + for(sal_uInt16 nId = nStartWhich; nId <= nEndWhich; ++nId) + { + if ( rSet.GetItemState(nId) < SfxItemState::SET ) //??? + // not really set + continue; + + if ( !pSlot->IsMode(SfxSlotMode::METHOD) && nId == rSet.GetPool()->GetWhich( pSlot->GetSlotId() ) ) + continue; + + bool bIsMediaDescriptor = isMediaDescriptor( nSlotId ); + sal_uInt16 nFormalArgs = bIsMediaDescriptor ? nMediaArgsCount : pSlot->nArgDefCount; + sal_uInt16 nArg; + for ( nArg=0; nArg<nFormalArgs; ++nArg ) + { + const SfxFormalArgument &rArg = bIsMediaDescriptor ? aFormalArgs[nArg] : pSlot->GetFormalArgument( nArg ); + sal_uInt16 nWhich = rSet.GetPool()->GetWhich( rArg.nSlotId ); + if ( nId == nWhich ) + break; + } + + if ( nArg<nFormalArgs ) + continue; + + if ( bIsMediaDescriptor ) + { + if ( nId == SID_DOCFRAME ) + continue; + if ( nId == SID_PROGRESS_STATUSBAR_CONTROL ) + continue; + if ( nId == SID_INTERACTIONHANDLER ) + continue; + if ( nId == SID_VIEW_DATA ) + continue; + if ( nId == SID_FILTER_DATA ) + continue; + if ( nId == SID_DOCUMENT ) + continue; + if ( nId == SID_CONTENT ) + continue; + if ( nId == SID_INPUTSTREAM ) + continue; + if ( nId == SID_STREAM ) + continue; + if ( nId == SID_OUTPUTSTREAM ) + continue; + if ( nId == SID_POSTDATA ) + continue; + if ( nId == SID_FILLFRAME ) + continue; + if ( nId == SID_TEMPLATE ) + continue; + if ( nId == SID_OPEN_NEW_VIEW ) + continue; + if ( nId == SID_VIEW_ID ) + continue; + if ( nId == SID_PLUGIN_MODE ) + continue; + if ( nId == SID_DOC_READONLY ) + continue; + if ( nId == SID_DOC_STARTPRESENTATION ) + continue; + if ( nId == SID_SELECTION ) + continue; + if ( nId == SID_HIDDEN ) + continue; + if ( nId == SID_MINIMIZED ) + continue; + if ( nId == SID_SILENT ) + continue; + if ( nId == SID_PREVIEW ) + continue; + if ( nId == SID_VIEWONLY ) + continue; + if ( nId == SID_EDITDOC ) + continue; + if ( nId == SID_TARGETNAME ) + continue; + if ( nId == SID_DOC_SALVAGE ) + continue; + if ( nId == SID_PATH ) + continue; + if ( nId == SID_FILE_DIALOG ) + continue; + if ( nId == SID_STANDARD_DIR ) + continue; + if ( nId == SID_DENY_LIST ) + continue; + if ( nId == SID_CONTENTTYPE ) + continue; + if ( nId == SID_TEMPLATE_NAME ) + continue; + if ( nId == SID_TEMPLATE_REGIONNAME ) + continue; + if ( nId == SID_JUMPMARK ) + continue; + if ( nId == SID_CHARSET ) + continue; + if ( nId == SID_MACROEXECMODE ) + continue; + if ( nId == SID_UPDATEDOCMODE ) + continue; + if ( nId == SID_REPAIRPACKAGE ) + continue; + if ( nId == SID_DOCINFO_TITLE ) + continue; + if ( nId == SID_COMPONENTDATA ) + continue; + if ( nId == SID_COMPONENTCONTEXT ) + continue; + if ( nId == SID_DOC_BASEURL ) + continue; + if ( nId == SID_DOC_HIERARCHICALNAME ) + continue; + if ( nId == SID_COPY_STREAM_IF_POSSIBLE ) + continue; + if ( nId == SID_NOAUTOSAVE ) + continue; + if ( nId == SID_ENCRYPTIONDATA ) + continue; + if ( nId == SID_DOC_SERVICE ) + continue; + if (nId == SID_FILTER_PROVIDER) + continue; + if ( nId == SID_CONVERT_IMAGES ) + continue; + + // used only internally + if ( nId == SID_SAVETO ) + continue; + if ( nId == SID_SAVEACOPYITEM ) + continue; + if ( nId == SID_MODIFYPASSWORDINFO ) + continue; + if ( nId == SID_SUGGESTEDSAVEASDIR ) + continue; + if ( nId == SID_SUGGESTEDSAVEASNAME ) + continue; + if ( nId == SID_LOCK_CONTENT_EXTRACTION ) + continue; + if ( nId == SID_LOCK_EXPORT ) + continue; + if ( nId == SID_LOCK_PRINT ) + continue; + if ( nId == SID_LOCK_SAVE ) + continue; + if ( nId == SID_LOCK_EDITDOC ) + continue; + if (nId == SID_REPLACEABLE) + continue; + } + + OString aDbg = "Unknown item detected: " + OString::number(static_cast<sal_Int32>(nId)); + DBG_ASSERT(nArg<nFormalArgs, aDbg.getStr()); + } + } + } +#endif + + if ( !nProps ) + return; + + // convert every item into a property + uno::Sequence<beans::PropertyValue> aSequ(nProps); + beans::PropertyValue *pValue = aSequ.getArray(); + + sal_Int32 nActProp=0; + if ( !pSlot->IsMode(SfxSlotMode::METHOD) ) + { + // slot is a property + sal_uInt16 nWhich = rSet.GetPool()->GetWhich(nSlotId); + bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip ); + const SfxPoolItem* pItem = rSet.GetItem<SfxPoolItem>(nWhich, false); + if ( pItem ) //??? + { + sal_uInt16 nSubCount = pType->nAttribs; + if ( !nSubCount ) + { + pValue[nActProp].Name = pSlot->pUnoName; + if ( !pItem->QueryValue( pValue[nActProp].Value ) ) + { + SAL_WARN( "sfx", "Item not convertible: " << nSlotId ); + } + } + else + { + // complex type, add a property value for every member of the struct + for ( sal_uInt16 n=1; n<=nSubCount; ++n ) + { + sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(pType->aAttrib[n-1].nAID)); + if ( bConvertTwips ) + nSubId |= CONVERT_TWIPS; + + DBG_ASSERT(( pType->aAttrib[n-1].nAID ) <= 127, "Member ID out of range" ); + pValue[nActProp].Name = pSlot->pUnoName + + "." + + OUString::createFromAscii( pType->aAttrib[n-1].pName ); + if ( !pItem->QueryValue( pValue[nActProp++].Value, nSubId ) ) + { + SAL_WARN( "sfx", "Sub item " << pType->aAttrib[n-1].nAID + << " not convertible in slot: " << nSlotId ); + } + } + } + } + + rArgs = aSequ; + return; + } + + // slot is a method + sal_uInt16 nFormalArgs = pSlot->GetFormalArgumentCount(); + for ( sal_uInt16 nArg=0; nArg<nFormalArgs; ++nArg ) + { + const SfxFormalArgument &rArg = pSlot->GetFormalArgument( nArg ); + sal_uInt16 nWhich = rSet.GetPool()->GetWhich( rArg.nSlotId ); + bool bConvertTwips = ( rSet.GetPool()->GetMetric( nWhich ) == MapUnit::MapTwip ); + const SfxPoolItem* pItem = rSet.GetItem<SfxPoolItem>(nWhich, false); + if ( pItem ) //??? + { + sal_uInt16 nSubCount = rArg.pType->nAttribs; + if ( !nSubCount ) + { + pValue[nActProp].Name = OUString::createFromAscii( rArg.pName ) ; + if ( !pItem->QueryValue( pValue[nActProp++].Value ) ) + { + SAL_WARN( "sfx", "Item not convertible: " << rArg.nSlotId ); + } + } + else + { + // complex type, add a property value for every member of the struct + for ( sal_uInt16 n = 1; n <= nSubCount; ++n ) + { + sal_uInt8 nSubId = static_cast<sal_uInt8>(static_cast<sal_Int8>(rArg.pType->aAttrib[n-1].nAID)); + if ( bConvertTwips ) + nSubId |= CONVERT_TWIPS; + + DBG_ASSERT((rArg.pType->aAttrib[n-1].nAID) <= 127, "Member ID out of range" ); + pValue[nActProp].Name = OUString::createFromAscii( rArg.pName ) + + "." + + OUString::createFromAscii( rArg.pType->aAttrib[n-1].pName ) ; + if ( !pItem->QueryValue( pValue[nActProp++].Value, nSubId ) ) + { + SAL_WARN( "sfx", "Sub item " + << rArg.pType->aAttrib[n-1].nAID + << " not convertible in slot: " + << rArg.nSlotId ); + } + } + } + } + } + + if ( nSlotId == SID_OPENDOC || nSlotId == SID_EXPORTDOC || nSlotId == SID_SAVEASDOC || nSlotId == SID_SAVEDOC || + nSlotId == SID_SAVETO || nSlotId == SID_EXPORTDOCASPDF || nSlotId == SID_DIRECTEXPORTDOCASPDF || + nSlotId == SID_EXPORTDOCASEPUB || nSlotId == SID_DIRECTEXPORTDOCASEPUB || + nSlotId == SID_REDACTDOC || nSlotId == SID_AUTOREDACTDOC || nSlotId == SID_SAVEACOPY ) + { + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_COMPONENTDATA, false) ) + { + pValue[nActProp].Name = sComponentData; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_COMPONENTCONTEXT, false) ) + { + pValue[nActProp].Name = sComponentContext; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_PROGRESS_STATUSBAR_CONTROL, false) ) + { + pValue[nActProp].Name = sStatusInd; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_INTERACTIONHANDLER, false) ) + { + pValue[nActProp].Name = sInteractionHdl; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_VIEW_DATA, false) ) + { + pValue[nActProp].Name = sViewData; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_FILTER_DATA, false) ) + { + pValue[nActProp].Name = sFilterData; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_DOCUMENT, false) ) + { + pValue[nActProp].Name = sModel; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_CONTENT, false) ) + { + pValue[nActProp].Name = sUCBContent; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_INPUTSTREAM, false) ) + { + pValue[nActProp].Name = sInputStream; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_STREAM, false) ) + { + pValue[nActProp].Name = sStream; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_OUTPUTSTREAM, false) ) + { + pValue[nActProp].Name = sOutputStream; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_POSTDATA, false) ) + { + pValue[nActProp].Name = sPostData; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxPoolItem *pItem = nullptr; SfxItemState::SET == rSet.GetItemState( SID_FILLFRAME, false, &pItem) ) + { + pValue[nActProp].Name = sFrame; + if ( auto pUsrAnyItem = dynamic_cast< const SfxUnoAnyItem *>( pItem ) ) + { + OSL_FAIL( "TransformItems: transporting an XFrame via an SfxUnoAnyItem is not deprecated!" ); + pValue[nActProp++].Value = pUsrAnyItem->GetValue(); + } + else if ( auto pUnoFrameItem = dynamic_cast< const SfxUnoFrameItem *>( pItem ) ) + pValue[nActProp++].Value <<= pUnoFrameItem->GetFrame(); + else + OSL_FAIL( "TransformItems: invalid item type for SID_FILLFRAME!" ); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE, false) ) + { + pValue[nActProp].Name = sAsTemplate; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_OPEN_NEW_VIEW, false) ) + { + pValue[nActProp].Name = sOpenNewView; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_FAIL_ON_WARNING, false) ) + { + pValue[nActProp].Name = sFailOnWarning; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_VIEW_ID, false) ) + { + pValue[nActProp].Name = sViewId; + pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue()); + } + if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_PLUGIN_MODE, false) ) + { + pValue[nActProp].Name = sPluginMode; + pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue()); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_DOC_READONLY, false) ) + { + pValue[nActProp].Name = sReadOnly; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_DDE_RECONNECT_ONLOAD, false) ) + { + pValue[nActProp].Name = sDdeReconnect; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_DOC_STARTPRESENTATION, false) ) + { + pValue[nActProp].Name = sStartPresentation; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_SELECTION, false) ) + { + pValue[nActProp].Name = sSelectionOnly; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_HIDDEN, false) ) + { + pValue[nActProp].Name = sHidden; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_MINIMIZED, false) ) + { + pValue[nActProp].Name = sMinimized; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_SILENT, false) ) + { + pValue[nActProp].Name = sSilent; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_PREVIEW, false) ) + { + pValue[nActProp].Name = sPreview; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_VIEWONLY, false) ) + { + pValue[nActProp].Name = sViewOnly; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_EDITDOC, false) ) + { + pValue[nActProp].Name = sDontEdit; + pValue[nActProp++].Value <<= !pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_FILE_DIALOG, false) ) + { + pValue[nActProp].Name = sUseSystemDialog; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_STANDARD_DIR, false) ) + { + pValue[nActProp].Name = sStandardDir; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringListItem *pItem = rSet.GetItemIfSet( SID_DENY_LIST, false) ) + { + pValue[nActProp].Name = sDenyList; + + css::uno::Sequence< OUString > aList; + pItem->GetStringList( aList ); + pValue[nActProp++].Value <<= aList ; + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TARGETNAME, false) ) + { + pValue[nActProp].Name = sFrameName; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_SALVAGE, false) ) + { + pValue[nActProp].Name = sSalvagedFile; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_PATH, false) ) + { + pValue[nActProp].Name = sFolderName; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_CONTENTTYPE, false) ) + { + pValue[nActProp].Name = sMediaType; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE_NAME, false) ) + { + pValue[nActProp].Name = sTemplateName; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_TEMPLATE_REGIONNAME, false) ) + { + pValue[nActProp].Name = sTemplateRegionName; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_JUMPMARK, false) ) + { + pValue[nActProp].Name = sJumpMark; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_CHARSET, false) ) + { + pValue[nActProp].Name = sCharacterSet; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_MACROEXECMODE, false) ) + { + pValue[nActProp].Name = sMacroExecMode; + pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue()); + } + if ( const SfxUInt16Item *pItem = rSet.GetItemIfSet( SID_UPDATEDOCMODE, false) ) + { + pValue[nActProp].Name = sUpdateDocMode; + pValue[nActProp++].Value <<= static_cast<sal_Int16>(pItem->GetValue()); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_REPAIRPACKAGE, false) ) + { + pValue[nActProp].Name = sRepairPackage; + pValue[nActProp++].Value <<= pItem->GetValue() ; + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOCINFO_TITLE, false) ) + { + pValue[nActProp].Name = sDocumentTitle; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_BASEURL, false) ) + { + pValue[nActProp].Name = sDocumentBaseURL; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_HIERARCHICALNAME, false) ) + { + pValue[nActProp].Name = sHierarchicalDocumentName; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_COPY_STREAM_IF_POSSIBLE, false) ) + { + pValue[nActProp].Name = sCopyStreamIfPossible; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_NOAUTOSAVE, false) ) + { + pValue[nActProp].Name = sNoAutoSave; + pValue[nActProp++].Value <<= pItem->GetValue() ; + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_MODIFYPASSWORDINFO, false) ) + { + pValue[nActProp].Name = sModifyPasswordInfo; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxUnoAnyItem *pItem = rSet.GetItemIfSet( SID_ENCRYPTIONDATA, false) ) + { + pValue[nActProp].Name = sEncryptionData; + pValue[nActProp++].Value = pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_SUGGESTEDSAVEASDIR, false) ) + { + pValue[nActProp].Name = sSuggestedSaveAsDir; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_SUGGESTEDSAVEASNAME, false) ) + { + pValue[nActProp].Name = sSuggestedSaveAsName; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxStringItem *pItem = rSet.GetItemIfSet( SID_DOC_SERVICE, false) ) + { + pValue[nActProp].Name = sDocumentService; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if (const SfxStringItem *pItem = rSet.GetItemIfSet(SID_FILTER_PROVIDER)) + { + pValue[nActProp].Name = sFilterProvider; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if (const SfxStringItem *pItem = rSet.GetItemIfSet(SID_CONVERT_IMAGES)) + { + pValue[nActProp].Name = sImageFilter; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_CONTENT_EXTRACTION, false) ) + { + pValue[nActProp].Name = sLockContentExtraction; + pValue[nActProp++].Value <<= pItem->GetValue() ; + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_EXPORT, false) ) + { + pValue[nActProp].Name = sLockExport; + pValue[nActProp++].Value <<= pItem->GetValue() ; + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_PRINT, false) ) + { + pValue[nActProp].Name = sLockPrint; + pValue[nActProp++].Value <<= pItem->GetValue() ; + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_SAVE, false) ) + { + pValue[nActProp].Name = sLockSave; + pValue[nActProp++].Value <<= pItem->GetValue() ; + } + if ( const SfxBoolItem *pItem = rSet.GetItemIfSet( SID_LOCK_EDITDOC, false) ) + { + pValue[nActProp].Name = sLockEditDoc; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + if (const SfxBoolItem *pItem = rSet.GetItemIfSet(SID_REPLACEABLE, false)) + { + pValue[nActProp].Name = sReplaceable; + pValue[nActProp++].Value <<= pItem->GetValue(); + } + } + + rArgs = aSequ; +} + +void SAL_CALL FilterOptionsContinuation::setFilterOptions( + const uno::Sequence<beans::PropertyValue>& rProps ) +{ + rProperties = rProps; +} + +uno::Sequence< beans::PropertyValue > SAL_CALL + FilterOptionsContinuation::getFilterOptions() +{ + return rProperties; +} + + +RequestFilterOptions::RequestFilterOptions( uno::Reference< frame::XModel > const & rModel, + const uno::Sequence< beans::PropertyValue >& rProperties ) +{ + uno::Reference< uno::XInterface > temp2; + document::FilterOptionsRequest aOptionsRequest( OUString(), + temp2, + rModel, + rProperties ); + + m_aRequest <<= aOptionsRequest; + + m_xAbort = new comphelper::OInteractionAbort; + m_xOptions = new FilterOptionsContinuation; +} + +uno::Any SAL_CALL RequestFilterOptions::getRequest() +{ + return m_aRequest; +} + +uno::Sequence< uno::Reference< task::XInteractionContinuation > > + SAL_CALL RequestFilterOptions::getContinuations() +{ + return { m_xAbort, m_xOptions }; +} + + +class RequestPackageReparation_Impl : public ::cppu::WeakImplHelper< task::XInteractionRequest > +{ + uno::Any m_aRequest; + rtl::Reference<comphelper::OInteractionApprove> m_xApprove; + rtl::Reference<comphelper::OInteractionDisapprove> m_xDisapprove; + +public: + explicit RequestPackageReparation_Impl( const OUString& aName ); + bool isApproved() const; + virtual uno::Any SAL_CALL getRequest() override; + virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override; +}; + +RequestPackageReparation_Impl::RequestPackageReparation_Impl( const OUString& aName ) +{ + uno::Reference< uno::XInterface > temp2; + document::BrokenPackageRequest aBrokenPackageRequest( OUString(), temp2, aName ); + m_aRequest <<= aBrokenPackageRequest; + m_xApprove = new comphelper::OInteractionApprove; + m_xDisapprove = new comphelper::OInteractionDisapprove; +} + +bool RequestPackageReparation_Impl::isApproved() const +{ + return m_xApprove->wasSelected(); +} + +uno::Any SAL_CALL RequestPackageReparation_Impl::getRequest() +{ + return m_aRequest; +} + +uno::Sequence< uno::Reference< task::XInteractionContinuation > > + SAL_CALL RequestPackageReparation_Impl::getContinuations() +{ + return { m_xApprove, m_xDisapprove }; +} + +RequestPackageReparation::RequestPackageReparation( const OUString& aName ) + : mxImpl(new RequestPackageReparation_Impl( aName )) +{ +} + +RequestPackageReparation::~RequestPackageReparation() +{ +} + +bool RequestPackageReparation::isApproved() const +{ + return mxImpl->isApproved(); +} + +css::uno::Reference < task::XInteractionRequest > RequestPackageReparation::GetRequest() const +{ + return mxImpl; +} + + +class NotifyBrokenPackage_Impl : public ::cppu::WeakImplHelper< task::XInteractionRequest > +{ + uno::Any m_aRequest; + rtl::Reference<comphelper::OInteractionAbort> m_xAbort; + +public: + explicit NotifyBrokenPackage_Impl(const OUString& rName); + virtual uno::Any SAL_CALL getRequest() override; + virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override; +}; + +NotifyBrokenPackage_Impl::NotifyBrokenPackage_Impl( const OUString& aName ) +{ + uno::Reference< uno::XInterface > temp2; + document::BrokenPackageRequest aBrokenPackageRequest( OUString(), temp2, aName ); + m_aRequest <<= aBrokenPackageRequest; + m_xAbort = new comphelper::OInteractionAbort; +} + +uno::Any SAL_CALL NotifyBrokenPackage_Impl::getRequest() +{ + return m_aRequest; +} + +uno::Sequence< uno::Reference< task::XInteractionContinuation > > + SAL_CALL NotifyBrokenPackage_Impl::getContinuations() +{ + return { m_xAbort }; +} + +NotifyBrokenPackage::NotifyBrokenPackage( const OUString& aName ) + : mxImpl(new NotifyBrokenPackage_Impl( aName )) +{ +} + +NotifyBrokenPackage::~NotifyBrokenPackage() +{ +} + +css::uno::Reference < task::XInteractionRequest > NotifyBrokenPackage::GetRequest() const +{ + return mxImpl; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/childwin.cxx b/sfx2/source/appl/childwin.cxx new file mode 100644 index 0000000000..6bf2814599 --- /dev/null +++ b/sfx2/source/appl/childwin.cxx @@ -0,0 +1,623 @@ +/* -*- 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 <memory> +#include <unotools/viewoptions.hxx> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <comphelper/string.hxx> +#include <cppuhelper/implbase.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <vcl/svapp.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/module.hxx> +#include <sfx2/dockwin.hxx> +#include <sfx2/dispatch.hxx> +#include <workwin.hxx> + +#include <sfx2/sfxsids.hrc> +#include <o3tl/string_view.hxx> + +const sal_uInt16 nVersion = 2; + +SfxChildWinFactory::SfxChildWinFactory( SfxChildWinCtor pTheCtor, sal_uInt16 nID, + sal_uInt16 n ) + : pCtor(pTheCtor) + , nId( nID ) + , nPos(n) +{} + +struct SfxChildWindow_Impl +{ + css::uno::Reference< css::frame::XFrame > xFrame; + css::uno::Reference< css::lang::XEventListener > xListener; + SfxChildWinFactory aFact = { nullptr, 0, 0 }; + bool bHideNotDelete; + bool bVisible; + bool bWantsFocus; + SfxWorkWindow* pWorkWin; +}; + +namespace { + +class DisposeListener : public ::cppu::WeakImplHelper< css::lang::XEventListener > +{ + public: + DisposeListener( SfxChildWindow* pOwner , + SfxChildWindow_Impl* pData ) + : m_pOwner( pOwner ) + , m_pData ( pData ) + {} + + virtual void SAL_CALL disposing( const css::lang::EventObject& aSource ) override + { + css::uno::Reference< css::lang::XEventListener > xSelfHold( this ); + + css::uno::Reference< css::lang::XComponent > xComp( aSource.Source, css::uno::UNO_QUERY ); + if( xComp.is() ) + xComp->removeEventListener( this ); + + if( !m_pOwner || !m_pData ) + return; + + m_pData->xListener.clear(); + + if ( m_pData->pWorkWin ) + { + // m_pOwner and m_pData will be killed + m_pData->xFrame.clear(); + m_pData->pWorkWin->GetBindings().Execute( m_pOwner->GetType() ); + } + else + { + delete m_pOwner; + } + + m_pOwner = nullptr; + m_pData = nullptr; + } + + private: + SfxChildWindow* m_pOwner; + SfxChildWindow_Impl* m_pData ; +}; + +} + +bool GetPosSizeFromString( std::u16string_view rStr, Point& rPos, Size& rSize ) +{ + if ( comphelper::string::getTokenCount(rStr, '/') != 4 ) + return false; + + sal_Int32 nIdx = 0; + rPos.setX( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) ); + rPos.setY( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) ); + rSize.setWidth( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) ); + rSize.setHeight( o3tl::toInt32(o3tl::getToken(rStr, 0, '/', nIdx)) ); + + // negative sizes are invalid + return rSize.Width() >= 0 && rSize.Height() >= 0; +} + +bool GetSplitSizeFromString( std::u16string_view rStr, Size& rSize ) +{ + size_t nIndex = rStr.find( ',' ); + if ( nIndex != std::u16string_view::npos ) + { + std::u16string_view aStr = rStr.substr( nIndex+1 ); + + sal_Int32 nCount = comphelper::string::getTokenCount(aStr, ';'); + if ( nCount != 2 ) + return false; + + sal_Int32 nIdx{ 0 }; + rSize.setWidth( o3tl::toInt32(o3tl::getToken(aStr, 0, ';', nIdx )) ); + rSize.setHeight( o3tl::toInt32(o3tl::getToken(aStr, 0, ';', nIdx )) ); + + // negative sizes are invalid + return rSize.Width() >= 0 && rSize.Height() >= 0; + } + + return false; +} + +SfxChildWindow::SfxChildWindow(vcl::Window *pParentWindow, sal_uInt16 nId) + : pParent(pParentWindow) + , pImpl(new SfxChildWindow_Impl) + , eChildAlignment(SfxChildAlignment::NOALIGNMENT) + , nType(nId) +{ + pImpl->bHideNotDelete = false; + pImpl->bWantsFocus = true; + pImpl->bVisible = true; + pImpl->pWorkWin = nullptr; +} + +void SfxChildWindow::Destroy() +{ + if ( GetFrame().is() ) + { + ClearWorkwin(); + try + { + css::uno::Reference < css::util::XCloseable > xClose( GetFrame(), css::uno::UNO_QUERY ); + if ( xClose.is() ) + xClose->close( true ); + else + GetFrame()->dispose(); + } + catch (const css::uno::Exception&) + { + } + } + else + delete this; +} + +void SfxChildWindow::ClearWorkwin() +{ + if (pImpl->pWorkWin) + { + if (pImpl->pWorkWin->GetActiveChild_Impl() == pWindow) + pImpl->pWorkWin->SetActiveChild_Impl(nullptr); + pImpl->pWorkWin = nullptr; + } +} + +SfxChildWindow::~SfxChildWindow() +{ + ClearWorkwin(); + if (xController) + { + xController->ChildWinDispose(); + xController.reset(); + } + pWindow.disposeAndClear(); +} + +std::unique_ptr<SfxChildWindow> SfxChildWindow::CreateChildWindow( sal_uInt16 nId, + vcl::Window *pParent, SfxBindings* pBindings, SfxChildWinInfo const & rInfo) +{ + std::unique_ptr<SfxChildWindow> pChild; + SfxChildWinFactory* pFact=nullptr; + SystemWindowFlags nOldMode = Application::GetSystemWindowMode(); + + // First search for ChildWindow in SDT; Overlay windows are realized + // by using ChildWindowContext + SfxApplication *pApp = SfxGetpApp(); + { + pFact = pApp->GetChildWinFactoryById(nId); + if ( pFact ) + { + if ( rInfo.bVisible ) + { + if ( pBindings ) + pBindings->ENTERREGISTRATIONS(); + SfxChildWinInfo aInfo = rInfo; + Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE ); + pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo ); + Application::SetSystemWindowMode( nOldMode ); + if ( pBindings ) + pBindings->LEAVEREGISTRATIONS(); + } + } + } + + SfxDispatcher *pDisp = pBindings ? pBindings->GetDispatcher_Impl() : nullptr; + SfxModule *pMod = pDisp ? SfxModule::GetActiveModule( pDisp->GetFrame() ) : nullptr; + if (!pChild && pMod) + { + pFact = pMod->GetChildWinFactoryById(nId); + if ( pFact ) + { + if ( rInfo.bVisible ) + { + if ( pBindings ) + pBindings->ENTERREGISTRATIONS(); + SfxChildWinInfo aInfo = rInfo; + Application::SetSystemWindowMode( SystemWindowFlags::NOAUTOMODE ); + pChild = pFact->pCtor( pParent, nId, pBindings, &aInfo ); + Application::SetSystemWindowMode( nOldMode ); + if ( pBindings ) + pBindings->LEAVEREGISTRATIONS(); + } + } + } + + if (pChild) + { + assert(pFact && "pChild is returned by a call on pFact, so pFact cannot be null"); + pChild->SetFactory_Impl( pFact ); + } + + DBG_ASSERT(pFact && (pChild || !rInfo.bVisible), "ChildWindow-Typ not registered!"); + + if (pChild && (!pChild->pWindow && !pChild->xController)) + { + pChild.reset(); + SAL_INFO("sfx.appl", "ChildWindow has no Window!"); + } + + return pChild; +} + + +void SfxChildWindow::SaveStatus(const SfxChildWinInfo& rInfo) +{ + sal_uInt16 nID = GetType(); + + OUString aInfoVisible = rInfo.bVisible ? OUString("V") : OUString("H"); + + OUString aWinData = "V" + + OUString::number(static_cast<sal_Int32>(nVersion)) + + "," + + aInfoVisible + + "," + + OUString::number(static_cast<sal_Int32>(rInfo.nFlags)); + + if ( !rInfo.aExtraString.isEmpty() ) + aWinData += "," + rInfo.aExtraString; + + OUString sName(OUString::number(nID)); + //Try and save window state per-module, e.g. sidebar on in one application + //but off in another + if (!rInfo.aModule.isEmpty()) + sName = rInfo.aModule + "/" + sName; + SvtViewOptions aWinOpt(EViewType::Window, sName); + aWinOpt.SetWindowState(rInfo.aWinState); + + css::uno::Sequence < css::beans::NamedValue > aSeq + { { "Data", css::uno::Any(aWinData) } }; + aWinOpt.SetUserData( aSeq ); + + // ... but save status at runtime! + pImpl->aFact.aInfo = rInfo; +} + +void SfxChildWindow::SetAlignment(SfxChildAlignment eAlign) +{ + eChildAlignment = eAlign; +} + +SfxChildWinInfo SfxChildWindow::GetInfo() const +{ + SfxChildWinInfo aInfo(pImpl->aFact.aInfo); + if (xController) + { + weld::Dialog* pDialog = xController->getDialog(); + aInfo.aPos = pDialog->get_position(); + aInfo.aSize = pDialog->get_size(); + vcl::WindowDataMask nMask = vcl::WindowDataMask::Pos | vcl::WindowDataMask::State; + if (pDialog->get_resizable()) + nMask |= vcl::WindowDataMask::Size; + aInfo.aWinState = pDialog->get_window_state(nMask); + } + else if (pWindow) + { + aInfo.aPos = pWindow->GetPosPixel(); + aInfo.aSize = pWindow->GetSizePixel(); + if ( pWindow->IsSystemWindow() ) + { + vcl::WindowDataMask nMask = vcl::WindowDataMask::Pos | vcl::WindowDataMask::State; + if ( pWindow->GetStyle() & WB_SIZEABLE ) + nMask |= vcl::WindowDataMask::Size; + aInfo.aWinState = static_cast<SystemWindow*>(pWindow.get())->GetWindowState( nMask ); + } + else if (DockingWindow* pDockingWindow = dynamic_cast<DockingWindow*>(pWindow.get())) + { + if (pDockingWindow->GetFloatingWindow()) + aInfo.aWinState = pDockingWindow->GetFloatingWindow()->GetWindowState(); + else if (SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(pDockingWindow)) + { + SfxChildWinInfo aTmpInfo; + pSfxDockingWindow->FillInfo( aTmpInfo ); + aInfo.aExtraString = aTmpInfo.aExtraString; + } + } + } + + aInfo.bVisible = pImpl->bVisible; + aInfo.nFlags = SfxChildWindowFlags::NONE; + return aInfo; +} + +sal_uInt16 SfxChildWindow::GetPosition() const +{ + return pImpl->aFact.nPos; +} + +void SfxChildWindow::InitializeChildWinFactory_Impl(sal_uInt16 nId, SfxChildWinInfo& rInfo) +{ + // load configuration + + std::optional<SvtViewOptions> xWinOpt; + // first see if a module specific id exists + if (rInfo.aModule.getLength()) + xWinOpt.emplace(EViewType::Window, rInfo.aModule + "/" + OUString::number(nId)); + + // if not then try the generic id + if (!xWinOpt || !xWinOpt->Exists()) + xWinOpt.emplace(EViewType::Window, OUString::number(nId)); + + if (xWinOpt->Exists() && xWinOpt->HasVisible() ) + rInfo.bVisible = xWinOpt->IsVisible(); // set state from configuration. Can be overwritten by UserData, see below + + css::uno::Sequence < css::beans::NamedValue > aSeq = xWinOpt->GetUserData(); + + OUString aTmp; + if ( aSeq.hasElements() ) + aSeq[0].Value >>= aTmp; + + OUString aWinData( aTmp ); + rInfo.aWinState = xWinOpt->GetWindowState(); + + if ( aWinData.isEmpty() ) + return; + + // Search for version ID + if ( aWinData[0] != 0x0056 ) // 'V' = 56h + // A version ID, so do not use + return; + + // Delete 'V' + aWinData = aWinData.copy(1); + + // Read version + char cToken = ','; + sal_Int32 nPos = aWinData.indexOf( cToken ); + sal_uInt16 nActVersion = static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( 0, nPos + 1 ))); + if ( nActVersion != nVersion ) + return; + + aWinData = aWinData.copy(nPos+1); + + // Load Visibility: is coded as a char + rInfo.bVisible = (aWinData[0] == 0x0056); // 'V' = 56h + aWinData = aWinData.copy(1); + nPos = aWinData.indexOf( cToken ); + if (nPos == -1) + return; + + sal_Int32 nNextPos = aWinData.indexOf( cToken, 2 ); + if ( nNextPos != -1 ) + { + // there is extra information + rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( nPos+1, nNextPos - nPos - 1 )))); + aWinData = aWinData.replaceAt( nPos, nNextPos-nPos+1, u"" ); + rInfo.aExtraString = aWinData; + } + else + rInfo.nFlags = static_cast<SfxChildWindowFlags>(static_cast<sal_uInt16>(o3tl::toInt32(aWinData.subView( nPos+1 )))); +} + +bool ParentIsFloatingWindow(const vcl::Window *pParent) +{ + if (!pParent) + return false; + if (pParent->GetType() == WindowType::DOCKINGWINDOW || pParent->GetType() == WindowType::TOOLBOX) + return static_cast<const DockingWindow*>(pParent)->GetFloatingWindow() != nullptr; + if (pParent->GetType() == WindowType::FLOATINGWINDOW) + return true; + return false; +} + +void SfxChildWindow::SetFactory_Impl( const SfxChildWinFactory *pF ) +{ + pImpl->aFact = *pF; +} + +void SfxChildWindow::SetHideNotDelete( bool bOn ) +{ + pImpl->bHideNotDelete = bOn; +} + +bool SfxChildWindow::IsHideNotDelete() const +{ + return pImpl->bHideNotDelete; +} + +void SfxChildWindow::SetWantsFocus( bool bSet ) +{ + pImpl->bWantsFocus = bSet; +} + +bool SfxChildWindow::WantsFocus() const +{ + return pImpl->bWantsFocus; +} + +bool SfxChildWinInfo::GetExtraData_Impl +( + SfxChildAlignment *pAlign +) const +{ + // invalid? + if ( aExtraString.isEmpty() ) + return false; + OUString aStr; + sal_Int32 nPos = aExtraString.indexOf("AL:"); + if ( nPos == -1 ) + return false; + + // Try to read the alignment string "ALIGN :(...)", but if + // it is not present, then use an older version + sal_Int32 n1 = aExtraString.indexOf('(', nPos); + if ( n1 != -1 ) + { + sal_Int32 n2 = aExtraString.indexOf(')', n1); + if ( n2 != -1 ) + { + // Cut out Alignment string + aStr = aExtraString.copy(nPos, n2 - nPos + 1); + aStr = aStr.replaceAt(nPos, n1-nPos+1, u""); + } + } + + // First extract the Alignment + if ( aStr.isEmpty() ) + return false; + if ( pAlign ) + *pAlign = static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32())); + + // then the LastAlignment + nPos = aStr.indexOf(','); + if ( nPos == -1 ) + return false; + aStr = aStr.copy(nPos+1); + + // Then the splitting information + nPos = aStr.indexOf(','); + if ( nPos == -1 ) + // No docking in a Splitwindow + return true; + aStr = aStr.copy(nPos+1); + Point aChildPos; + Size aChildSize; + return GetPosSizeFromString( aStr, aChildPos, aChildSize ); +} + +bool SfxChildWindow::IsVisible() const +{ + return pImpl->bVisible; +} + +void SfxChildWindow::SetVisible_Impl( bool bVis ) +{ + pImpl->bVisible = bVis; +} + +void SfxChildWindow::Hide() +{ + if (xController) + xController->EndDialog(nCloseResponseToJustHide); + else + pWindow->Hide(); +} + +void SfxChildWindow::Show( ShowFlags nFlags ) +{ + if (xController) + { + if (!xController->getDialog()->get_visible()) + { + if (!xController->CloseOnHide()) + { + // tdf#155708 - do not run a new (Async) validation window, + // because we already have one in sync mode, just show the running one + xController->getDialog()->show(); + } + else + { + weld::DialogController::runAsync(xController, + [this](sal_Int32 nResult) { + if (nResult == nCloseResponseToJustHide) + return; + xController->Close(); + }); + } + } + } + else + pWindow->Show(true, nFlags); +} + +void SfxChildWindow::SetWorkWindow_Impl( SfxWorkWindow* pWin ) +{ + pImpl->pWorkWin = pWin; + if (pWin) + { + if ( (xController && xController->getDialog()->has_toplevel_focus()) || + (pWindow && pWindow->HasChildPathFocus()) ) + { + pImpl->pWorkWin->SetActiveChild_Impl( pWindow ); + } + } +} + +void SfxChildWindow::Activate_Impl() +{ + if(pImpl->pWorkWin!=nullptr) + pImpl->pWorkWin->SetActiveChild_Impl( pWindow ); +} + +bool SfxChildWindow::QueryClose() +{ + bool bAllow = true; + + if ( pImpl->xFrame.is() ) + { + css::uno::Reference< css::frame::XController > xCtrl = pImpl->xFrame->getController(); + if ( xCtrl.is() ) + bAllow = xCtrl->suspend( true ); + } + + if ( bAllow ) + { + if (GetController()) + { + weld::Dialog* pDialog = GetController()->getDialog(); + bAllow = !pDialog->get_visible() || !pDialog->get_modal(); + } + else if (GetWindow()) + bAllow = !GetWindow()->IsInModalMode(); + } + + return bAllow; +} + +const css::uno::Reference< css::frame::XFrame >& SfxChildWindow::GetFrame() const +{ + return pImpl->xFrame; +} + +void SfxChildWindow::SetFrame( const css::uno::Reference< css::frame::XFrame > & rFrame ) +{ + // Do nothing if nothing will be changed ... + if( pImpl->xFrame == rFrame ) + return; + + // ... but stop listening on old frame, if connection exist! + if( pImpl->xFrame.is() ) + pImpl->xFrame->removeEventListener( pImpl->xListener ); + + // If new frame is not NULL -> we must guarantee valid listener for disposing events. + // Use already existing or create new one. + if( rFrame.is() ) + if( !pImpl->xListener.is() ) + pImpl->xListener.set( new DisposeListener( this, pImpl.get() ) ); + + // Set new frame in data container + // and build new listener connection, if necessary. + pImpl->xFrame = rFrame; + if( pImpl->xFrame.is() ) + pImpl->xFrame->addEventListener( pImpl->xListener ); +} + +void SfxChildWindow::RegisterChildWindow(SfxModule* pMod, const SfxChildWinFactory& rFact) +{ + SfxGetpApp()->RegisterChildWindow_Impl( pMod, rFact ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/fileobj.cxx b/sfx2/source/appl/fileobj.cxx new file mode 100644 index 0000000000..18aea4a590 --- /dev/null +++ b/sfx2/source/appl/fileobj.cxx @@ -0,0 +1,435 @@ +/* -*- 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 <tools/urlobj.hxx> +#include <tools/stream.hxx> +#include <sot/formats.hxx> +#include <sal/log.hxx> +#include <sfx2/lnkbase.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sot/exchange.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <sfx2/docfac.hxx> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <unotools/mediadescriptor.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/opengrf.hxx> +#include <sfx2/sfxresid.hxx> +#include <sfx2/objsh.hxx> +#include "fileobj.hxx" +#include <sfx2/strings.hrc> +#include <vcl/svapp.hxx> + +enum class SvFileObjectType +{ + Text = 1, Graphic = 2, Object = 3 +}; + +SvFileObject::SvFileObject() + : nPostUserEventId(nullptr) + , nType(SvFileObjectType::Text) + , bLoadAgain(true) + , bSynchron(false) + , bLoadError(false) + , bWaitForData(false) + , bDataReady(false) + , bClearMedium(false) + , bStateChangeCalled(false) +{ +} + +SvFileObject::~SvFileObject() +{ + if (xMed.is()) + { + xMed->SetDoneLink( Link<void*,void>() ); + xMed.clear(); + } + if (nPostUserEventId) + Application::RemoveUserEvent(nPostUserEventId); +} + +bool SvFileObject::GetData( css::uno::Any & rData, + const OUString & rMimeType, + bool /*bGetSynchron*/ ) +{ + SotClipboardFormatId nFmt = SotExchange::RegisterFormatMimeType( rMimeType ); + switch( nType ) + { + case SvFileObjectType::Text: + if( SotClipboardFormatId::SIMPLE_FILE == nFmt ) + { + // The media in the application must be opened to lookup the + // relative file links!! This is done through the link manager + // of the Storage. + rData <<= sFileNm; + } + break; + + case SvFileObjectType::Graphic: + if (SotClipboardFormatId::GDIMETAFILE == nFmt + || SotClipboardFormatId::BITMAP == nFmt + || SotClipboardFormatId::SVXB == nFmt) + { + rData <<= sFileNm; + } + break; + case SvFileObjectType::Object: + // TODO/LATER: possibility to insert a new object + rData <<= sFileNm; + break; + } + return true/*0 != aTypeList.Count()*/; +} + +bool SvFileObject::Connect( sfx2::SvBaseLink* pLink ) +{ + if( !pLink || !pLink->GetLinkManager() ) + return false; + + // Test if not another link of the same connection already exists + sfx2::LinkManager::GetDisplayNames( pLink, nullptr, &sFileNm, nullptr, &sFilter ); + + if( sfx2::SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() ) + { + SfxObjectShellRef pShell = pLink->GetLinkManager()->GetPersist(); + if( pShell.is() ) + { + if( pShell->IsAbortingImport() ) + return false; + + if( pShell->GetMedium() ) + sReferer = pShell->GetMedium()->GetName(); + } + } + + switch( pLink->GetObjType() ) + { + case sfx2::SvBaseLinkObjectType::ClientGraphic: + nType = SvFileObjectType::Graphic; + bSynchron = pLink->IsSynchron(); + break; + + case sfx2::SvBaseLinkObjectType::ClientFile: + nType = SvFileObjectType::Text; + break; + + case sfx2::SvBaseLinkObjectType::ClientOle: + nType = SvFileObjectType::Object; + // TODO/LATER: introduce own type to be used for exchanging + break; + + default: + return false; + } + + SetUpdateTimeout( 0 ); + + // and now register by this or other found Pseudo-Object + AddDataAdvise( pLink, SotExchange::GetFormatMimeType( pLink->GetContentType()), 0 ); + return true; +} + +bool SvFileObject::LoadFile_Impl() +{ + // We are still at Loading!! + if( bWaitForData || !bLoadAgain || xMed.is() ) + return false; + + // at the moment on the current DocShell + xMed = new SfxMedium( sFileNm, sReferer, StreamMode::STD_READ ); + SvLinkSource::StreamToLoadFrom aStreamToLoadFrom = + getStreamToLoadFrom(); + xMed->setStreamToLoadFrom( + aStreamToLoadFrom.m_xInputStreamToLoadFrom, + aStreamToLoadFrom.m_bIsReadOnly); + + if( !bSynchron ) + { + bLoadAgain = bDataReady = false; + bWaitForData = true; + + tools::SvRef<SfxMedium> xTmpMed = xMed; + xMed->Download( LINK( this, SvFileObject, LoadGrfReady_Impl ) ); + + bClearMedium = !xMed.is(); + if( bClearMedium ) + xMed = xTmpMed; // If already finished in Download + return bDataReady; + } + + bWaitForData = true; + bDataReady = false; + xMed->Download(); + bLoadAgain = !xMed->IsRemote(); + bWaitForData = false; + + // Graphic is finished, also send DataChanged of the Status change: + SendStateChg_Impl( xMed->GetInStream() && xMed->GetInStream()->GetError() + ? sfx2::LinkManager::STATE_LOAD_ERROR : sfx2::LinkManager::STATE_LOAD_OK ); + return true; +} + + +/** detect the filter of the given file + + @param _rURL + specifies the URL of the file which filter is to detected.<br/> + If the URL doesn't denote a valid (existent and accessible) file, the + request is silently dropped. +*/ +static OUString impl_getFilter( const OUString& _rURL ) +{ + OUString sFilter; + if ( _rURL.isEmpty() ) + return sFilter; + + try + { + css::uno::Reference< css::document::XTypeDetection > xTypeDetection( + ::comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.document.TypeDetection" ), + css::uno::UNO_QUERY ); + if ( xTypeDetection.is() ) + { + utl::MediaDescriptor aDescr; + aDescr[ utl::MediaDescriptor::PROP_URL ] <<= _rURL; + css::uno::Sequence< css::beans::PropertyValue > aDescrList = + aDescr.getAsConstPropertyValueList(); + OUString sType = xTypeDetection->queryTypeByDescriptor( aDescrList, true ); + if ( !sType.isEmpty() ) + { + // Honor a selected/detected filter. + for (const auto& rDescr : std::as_const(aDescrList)) + { + if (rDescr.Name == "FilterName") + { + if (rDescr.Value >>= sFilter) + break; + } + } + if (sFilter.isEmpty()) + { + css::uno::Reference< css::container::XNameAccess > xTypeCont( xTypeDetection, + css::uno::UNO_QUERY ); + if ( xTypeCont.is() ) + { + /* XXX: for fdo#69948 scenario the sequence returned by + * getByName() contains an empty PreferredFilter + * property value (since? expected?) */ + ::comphelper::SequenceAsHashMap lTypeProps( xTypeCont->getByName( sType ) ); + sFilter = lTypeProps.getUnpackedValueOrDefault( + "PreferredFilter", OUString() ); + } + } + } + } + } + catch( const css::uno::Exception& ) + { + } + + return sFilter; +} + +void SvFileObject::Edit(weld::Window* pParent, sfx2::SvBaseLink* pLink, const Link<const OUString&, void>& rEndEditHdl) +{ + aEndEditLink = rEndEditHdl; + OUString sFile, sRange, sTmpFilter; + if( !pLink || !pLink->GetLinkManager() ) + return; + + sfx2::LinkManager::GetDisplayNames( pLink, nullptr, &sFile, &sRange, &sTmpFilter ); + + switch( pLink->GetObjType() ) + { + case sfx2::SvBaseLinkObjectType::ClientGraphic: + { + nType = SvFileObjectType::Graphic; // If not set already + + SvxOpenGraphicDialog aDlg(SfxResId(RID_SVXSTR_EDITGRFLINK), pParent); + aDlg.EnableLink(false); + aDlg.SetPath( sFile, true ); + aDlg.SetCurrentFilter( sTmpFilter ); + + if( !aDlg.Execute() ) + { + sFile = aDlg.GetPath() + + OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator) + + aDlg.GetDetectedFilter(); + + aEndEditLink.Call( sFile ); + } + else + sFile.clear(); + } + break; + + case sfx2::SvBaseLinkObjectType::ClientOle: + { + nType = SvFileObjectType::Object; // if not set already + + ::sfx2::FileDialogHelper & rFileDlg = + pLink->GetInsertFileDialog( OUString() ); + rFileDlg.SetContext(sfx2::FileDialogHelper::LinkClientOLE); + rFileDlg.StartExecuteModal( + LINK( this, SvFileObject, DialogClosedHdl ) ); + } + break; + + case sfx2::SvBaseLinkObjectType::ClientFile: + { + nType = SvFileObjectType::Text; // if not set already + + OUString sFactory; + SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); + if ( pShell ) + sFactory = pShell->GetFactory().GetFactoryName(); + + ::sfx2::FileDialogHelper & rFileDlg = + pLink->GetInsertFileDialog(sFactory); + rFileDlg.SetContext(sfx2::FileDialogHelper::LinkClientFile); + rFileDlg.StartExecuteModal( + LINK( this, SvFileObject, DialogClosedHdl ) ); + } + break; + + default: + sFile.clear(); + } +} + +IMPL_LINK_NOARG( SvFileObject, LoadGrfReady_Impl, void*, void ) +{ + // When we come from here there it can not be an error no more. + bLoadError = false; + bWaitForData = false; + + if( !bDataReady ) + { + // Graphic is finished, also send DataChanged from Status change + bDataReady = true; + SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_OK ); + + // and then send the data again + NotifyDataChanged(); + } + + if( bDataReady ) + { + bLoadAgain = true; + if( xMed.is() ) + { + xMed->SetDoneLink( Link<void*,void>() ); + mxDelMed = xMed; + nPostUserEventId = Application::PostUserEvent( + LINK( this, SvFileObject, DelMedium_Impl )); + xMed.clear(); + } + } +} + +IMPL_LINK_NOARG( SvFileObject, DelMedium_Impl, void*, void ) +{ + nPostUserEventId = nullptr; + mxDelMed.clear(); +} + +IMPL_LINK( SvFileObject, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void ) +{ + OUString sFile; + + if ( SvFileObjectType::Text == nType || SvFileObjectType::Object == nType ) + { + if ( _pFileDlg && _pFileDlg->GetError() == ERRCODE_NONE ) + { + OUString sURL( _pFileDlg->GetPath() ); + sFile = sURL + OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator) + + impl_getFilter( sURL ); + } + } + else + { + SAL_WARN( "sfx.appl", "SvFileObject::DialogClosedHdl(): wrong file type" ); + } + + aEndEditLink.Call( sFile ); +} + +/* + The method determines whether the data-object can be read from a DDE. +*/ +bool SvFileObject::IsPending() const +{ + return SvFileObjectType::Graphic == nType && !bLoadError && bWaitForData; +} + +bool SvFileObject::IsDataComplete() const +{ + bool bRet = false; + if( SvFileObjectType::Graphic != nType ) + bRet = true; + else if( !bLoadError && !bWaitForData ) + { + SvFileObject* pThis = const_cast<SvFileObject*>(this); + if( bDataReady || + ( bSynchron && pThis->LoadFile_Impl() && xMed.is() ) ) + bRet = true; + else + { + INetURLObject aUrl( sFileNm ); + if( aUrl.HasError() || + INetProtocol::NotValid == aUrl.GetProtocol() ) + bRet = true; + } + } + return bRet; +} + + +void SvFileObject::CancelTransfers() +{ + // unsubscribe from the cache if in the middle of loading + if( !bDataReady ) + { + // Do not set-up again + bLoadAgain = false; + bDataReady = bLoadError = bWaitForData = true; + SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_ABORT ); + } +} + + +void SvFileObject::SendStateChg_Impl( sfx2::LinkManager::LinkState nState ) +{ + if( !bStateChangeCalled && HasDataLinks() ) + { + DataChanged( SotExchange::GetFormatName( + sfx2::LinkManager::RegisterStatusInfoId()), css::uno::Any(OUString::number( nState )) ); + bStateChangeCalled = true; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/fileobj.hxx b/sfx2/source/appl/fileobj.hxx new file mode 100644 index 0000000000..7362f6b1a4 --- /dev/null +++ b/sfx2/source/appl/fileobj.hxx @@ -0,0 +1,82 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SFX2_SOURCE_APPL_FILEOBJ_HXX +#define INCLUDED_SFX2_SOURCE_APPL_FILEOBJ_HXX + +#include <sfx2/linksrc.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/linkmgr.hxx> + +class Graphic; +struct ImplSVEvent; +namespace sfx2 { class FileDialogHelper; } + +enum class SvFileObjectType; + +class SvFileObject : public sfx2::SvLinkSource +{ + OUString sFileNm; + OUString sFilter; + OUString sReferer; + Link<const OUString&, void> aEndEditLink; + tools::SvRef<SfxMedium> xMed; + ImplSVEvent* nPostUserEventId; + tools::SvRef<SfxMedium> mxDelMed; + + SvFileObjectType nType; + + bool bLoadAgain : 1; + bool bSynchron : 1; + bool bLoadError : 1; + bool bWaitForData : 1; + bool bDataReady : 1; + bool bClearMedium : 1; + bool bStateChangeCalled : 1; + + bool LoadFile_Impl(); + void SendStateChg_Impl( sfx2::LinkManager::LinkState nState ); + + DECL_LINK( DelMedium_Impl, void*, void ); + DECL_LINK( LoadGrfReady_Impl, void*, void ); + DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void ); + +protected: + virtual ~SvFileObject() override; + +public: + SvFileObject(); + + virtual bool GetData( css::uno::Any & rData /*out param*/, + const OUString & rMimeType, + bool bSynchron = false ) override; + + virtual bool Connect( sfx2::SvBaseLink* ) override; + virtual void Edit(weld::Window *, sfx2::SvBaseLink *, const Link<const OUString&, void>& rEndEditHdl) override; + + // Ask whether you can access data directly or whether it has to be triggered + virtual bool IsPending() const override; + virtual bool IsDataComplete() const override; + + void CancelTransfers(); +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/flatpak.cxx b/sfx2/source/appl/flatpak.cxx new file mode 100644 index 0000000000..c6edf6f232 --- /dev/null +++ b/sfx2/source/appl/flatpak.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstdlib> +#include <cstring> + +#include <osl/file.hxx> +#include <osl/thread.h> +#include <rtl/textcvt.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sfx2/flatpak.hxx> +#include <tools/debug.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/ucbhelper.hxx> + +bool flatpak::isFlatpak() { + static auto const flatpak = [] { return std::getenv("LIBO_FLATPAK") != nullptr; }(); + return flatpak; +} + +namespace { + +// Must only be accessed with SolarMutex locked: +struct { + bool created = false; + OUString url; +} temporaryHtmlDirectoryStatus; + +} + +bool flatpak::createTemporaryHtmlDirectory(OUString ** url) { + assert(url != nullptr); + DBG_TESTSOLARMUTEX(); + if (!temporaryHtmlDirectoryStatus.created) { + // coverity[tainted_return_value] - we trust the contents of this variable + auto const env = std::getenv("XDG_CACHE_HOME"); + if (env == nullptr) { + SAL_WARN("sfx.appl", "LIBO_FLATPAK mode but unset XDG_CACHE_HOME"); + return false; + } + OUString path; + if (!rtl_convertStringToUString( + &path.pData, env, std::strlen(env), osl_getThreadTextEncoding(), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode failure converting XDG_CACHE_HOME \"" << env << "\" encoding"); + return false; + } + OUString parent; + auto const err = osl::FileBase::getFileURLFromSystemPath(path, parent); + if (err != osl::FileBase::E_None) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode failure converting XDG_CACHE_HOME \"" << path << "\" to URL: " + << err); + return false; + } + if (!parent.endsWith("/")) { + parent += "/"; + } + temporaryHtmlDirectoryStatus.url = utl::CreateTempURL(&parent, true); + if (temporaryHtmlDirectoryStatus.url.isEmpty()) { + SAL_WARN( + "sfx.appl", "LIBO_FLATPAK mode failure creating temp dir at <" << parent << ">"); + return false; + } + temporaryHtmlDirectoryStatus.created = true; + } + *url = &temporaryHtmlDirectoryStatus.url; + return true; +} + +void flatpak::removeTemporaryHtmlDirectory() { + DBG_TESTSOLARMUTEX(); + if (temporaryHtmlDirectoryStatus.created) { + if (!utl::UCBContentHelper::Kill(temporaryHtmlDirectoryStatus.url)) { + SAL_INFO( + "sfx.appl", + "LIBO_FLATPAK mode failure removing directory <" + << temporaryHtmlDirectoryStatus.url << ">"); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sfx2/source/appl/fwkhelper.cxx b/sfx2/source/appl/fwkhelper.cxx new file mode 100644 index 0000000000..6a7eee8fb8 --- /dev/null +++ b/sfx2/source/appl/fwkhelper.cxx @@ -0,0 +1,52 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/frame/XFrame.hpp> + +#include <vcl/svapp.hxx> + +#include <fwkhelper.hxx> +#include <workwin.hxx> +#include <sfx2/frame.hxx> + +void RefreshToolbars( css::uno::Reference< css::frame::XFrame > const & xFrame ) +{ + SolarMutexGuard aGuard; + if ( !xFrame.is() ) + return; + + SfxFrame* pFrame=nullptr; + for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) ) + { + if ( pFrame->GetFrameInterface() == xFrame ) + break; + } + + if ( pFrame ) + { + SfxWorkWindow* pWrkWin = pFrame->GetWorkWindow_Impl(); + if ( pWrkWin ) + pWrkWin->UpdateObjectBars_Impl(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/getbasctlfunction.cxx b/sfx2/source/appl/getbasctlfunction.cxx new file mode 100644 index 0000000000..fd7f487301 --- /dev/null +++ b/sfx2/source/appl/getbasctlfunction.cxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include <config_features.h> +#include <config_options.h> +#include <osl/module.h> +#include <osl/module.hxx> +#include <tools/svlibrary.h> + +#include "getbasctlfunction.hxx" + +#if HAVE_FEATURE_SCRIPTING +#ifndef DISABLE_DYNLOADING + +extern "C" { static void thisModule() {} } + +oslGenericFunction sfx2::getBasctlFunction(char const* name) +{ + osl::Module aMod; + + // load basctl module + auto const ok = aMod.loadRelative( + &thisModule, +#if ENABLE_MERGELIBS + SVLIBRARY("merged") +#else + SVLIBRARY("basctl") +#endif + ); + assert(ok); + (void) ok; + + // get symbol + auto pSymbol = aMod.getFunctionSymbol(name); + assert(pSymbol); + aMod.release(); + + return pSymbol; +} + +#endif +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sfx2/source/appl/getbasctlfunction.hxx b/sfx2/source/appl/getbasctlfunction.hxx new file mode 100644 index 0000000000..5fa4bc5570 --- /dev/null +++ b/sfx2/source/appl/getbasctlfunction.hxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <sal/config.h> +#include <config_features.h> + +#include <osl/module.h> + +#ifndef DISABLE_DYNLOADING +#if HAVE_FEATURE_SCRIPTING + +namespace sfx2 +{ +oslGenericFunction getBasctlFunction(char const* name); +} + +#endif +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sfx2/source/appl/helpdispatch.cxx b/sfx2/source/appl/helpdispatch.cxx new file mode 100644 index 0000000000..ec6b5dfea0 --- /dev/null +++ b/sfx2/source/appl/helpdispatch.cxx @@ -0,0 +1,106 @@ +/* -*- 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 "helpdispatch.hxx" +#include "newhelp.hxx" +#include <tools/debug.hxx> +#include <utility> + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; + +// class HelpInterceptor_Impl -------------------------------------------- + +HelpDispatch_Impl::HelpDispatch_Impl( HelpInterceptor_Impl& _rInterceptor, + css::uno::Reference< css::frame::XDispatch > _xDisp ) : + + m_rInterceptor ( _rInterceptor ), + m_xRealDispatch (std::move( _xDisp )) + +{ +} + + +HelpDispatch_Impl::~HelpDispatch_Impl() +{ +} + + +// XDispatch + +void SAL_CALL HelpDispatch_Impl::dispatch( + + const URL& aURL, const Sequence< PropertyValue >& aArgs ) + +{ + DBG_ASSERT( m_xRealDispatch.is(), "invalid dispatch" ); + + // search for a keyword (dispatch from the basic ide) + bool bHasKeyword = false; + OUString sKeyword; + for ( const PropertyValue& rArg : aArgs ) + { + if ( rArg.Name == "HelpKeyword" ) + { + OUString sHelpKeyword; + if ( ( rArg.Value >>= sHelpKeyword ) && !sHelpKeyword.isEmpty() ) + { + sKeyword = sHelpKeyword; + bHasKeyword = !sKeyword.isEmpty(); + break; + } + } + } + + // if a keyword was found, then open it + SfxHelpWindow_Impl* pHelpWin = m_rInterceptor.GetHelpWindow(); + DBG_ASSERT( pHelpWin, "invalid HelpWindow" ); + if ( bHasKeyword ) + { + pHelpWin->OpenKeyword( sKeyword ); + return; + } + + pHelpWin->loadHelpContent(aURL.Complete); +} + + +void SAL_CALL HelpDispatch_Impl::addStatusListener( + + const Reference< XStatusListener >& xControl, const URL& aURL ) + +{ + DBG_ASSERT( m_xRealDispatch.is(), "invalid dispatch" ); + m_xRealDispatch->addStatusListener( xControl, aURL ); +} + + +void SAL_CALL HelpDispatch_Impl::removeStatusListener( + + const Reference< XStatusListener >& xControl, const URL& aURL ) + +{ + DBG_ASSERT( m_xRealDispatch.is(), "invalid dispatch" ); + m_xRealDispatch->removeStatusListener( xControl, aURL ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/helpdispatch.hxx b/sfx2/source/appl/helpdispatch.hxx new file mode 100644 index 0000000000..c1e6aa8f98 --- /dev/null +++ b/sfx2/source/appl/helpdispatch.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SFX2_SOURCE_APPL_HELPDISPATCH_HXX +#define INCLUDED_SFX2_SOURCE_APPL_HELPDISPATCH_HXX + +#include <com/sun/star/frame/XDispatch.hpp> +#include <cppuhelper/implbase.hxx> + +#include "helpinterceptor.hxx" + +class HelpDispatch_Impl : public ::cppu::WeakImplHelper< css::frame::XDispatch > +{ +private: + HelpInterceptor_Impl& m_rInterceptor; + css::uno::Reference< css::frame::XDispatch > + m_xRealDispatch; + +public: + HelpDispatch_Impl( HelpInterceptor_Impl& _rInterceptor, + css::uno::Reference< css::frame::XDispatch > _xDisp ); + virtual ~HelpDispatch_Impl() override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override; + virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override; + virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override; +}; + +#endif // INCLUDED_SFX2_SOURCE_APPL_HELPDISPATCH_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/helpinterceptor.cxx b/sfx2/source/appl/helpinterceptor.cxx new file mode 100644 index 0000000000..a9ff761015 --- /dev/null +++ b/sfx2/source/appl/helpinterceptor.cxx @@ -0,0 +1,262 @@ +/* -*- 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 "helpinterceptor.hxx" +#include "helpdispatch.hxx" +#include "newhelp.hxx" +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; + +HelpInterceptor_Impl::HelpInterceptor_Impl() : + + m_pWindow ( nullptr ), + m_nCurPos ( 0 ) + +{ +} + + +HelpInterceptor_Impl::~HelpInterceptor_Impl() +{ +} + + +void HelpInterceptor_Impl::addURL( const OUString& rURL ) +{ + size_t nCount = m_vHistoryUrls.size(); + if ( nCount && m_nCurPos < ( nCount - 1 ) ) + { + m_vHistoryUrls.erase( + m_vHistoryUrls.begin() + m_nCurPos + 1, + m_vHistoryUrls.end()); + } + Reference<XFrame> xFrame(m_xIntercepted, UNO_QUERY); + Reference<XController> xController; + if(xFrame.is()) + xController = xFrame->getController(); + + m_aCurrentURL = rURL; + m_vHistoryUrls.emplace_back( rURL ); + m_nCurPos = m_vHistoryUrls.size() - 1; +// TODO ? + if ( m_xListener.is() ) + { + css::frame::FeatureStateEvent aEvent; + URL aURL; + aURL.Complete = rURL; + aEvent.FeatureURL = aURL; + aEvent.Source = static_cast<css::frame::XDispatch*>(this); + m_xListener->statusChanged( aEvent ); + } + + m_pWindow->UpdateToolbox(); +} + + +void HelpInterceptor_Impl::setInterception( const Reference< XFrame >& xFrame ) +{ + m_xIntercepted.set( xFrame, UNO_QUERY ); + + if ( m_xIntercepted.is() ) + m_xIntercepted->registerDispatchProviderInterceptor( static_cast<XDispatchProviderInterceptor*>(this) ); +} + + +bool HelpInterceptor_Impl::HasHistoryPred() const +{ + return m_nCurPos > 0; +} + +bool HelpInterceptor_Impl::HasHistorySucc() const +{ + return m_nCurPos < ( m_vHistoryUrls.size() - 1 ); +} + + +// XDispatchProvider + +Reference< XDispatch > SAL_CALL HelpInterceptor_Impl::queryDispatch( + + const URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags ) + +{ + Reference< XDispatch > xResult; + if ( m_xSlaveDispatcher.is() ) + xResult = m_xSlaveDispatcher->queryDispatch( aURL, aTargetFrameName, nSearchFlags ); + + bool bHelpURL = aURL.Complete.toAsciiLowerCase().match("vnd.sun.star.help",0); + + if ( bHelpURL ) + { + DBG_ASSERT( xResult.is(), "invalid dispatch" ); + xResult = new HelpDispatch_Impl( *this, xResult ); + } + + return xResult; +} + + +Sequence < Reference < XDispatch > > SAL_CALL HelpInterceptor_Impl::queryDispatches( + + const Sequence< DispatchDescriptor >& aDescripts ) + +{ + Sequence< Reference< XDispatch > > aReturn( aDescripts.getLength() ); + std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(), + [this](const DispatchDescriptor& rDescr) -> Reference<XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return aReturn; +} + + +// XDispatchProviderInterceptor + +Reference< XDispatchProvider > SAL_CALL HelpInterceptor_Impl::getSlaveDispatchProvider() + +{ + return m_xSlaveDispatcher; +} + + +void SAL_CALL HelpInterceptor_Impl::setSlaveDispatchProvider( const Reference< XDispatchProvider >& xNewSlave ) + +{ + m_xSlaveDispatcher = xNewSlave; +} + + +Reference< XDispatchProvider > SAL_CALL HelpInterceptor_Impl::getMasterDispatchProvider() + +{ + return m_xMasterDispatcher; +} + + +void SAL_CALL HelpInterceptor_Impl::setMasterDispatchProvider( const Reference< XDispatchProvider >& xNewMaster ) + +{ + m_xMasterDispatcher = xNewMaster; +} + + +// XInterceptorInfo + +Sequence< OUString > SAL_CALL HelpInterceptor_Impl::getInterceptedURLs() + +{ + Sequence<OUString> aURLList { "vnd.sun.star.help://*" }; + return aURLList; +} + + +// XDispatch + +void SAL_CALL HelpInterceptor_Impl::dispatch( + const URL& aURL, const Sequence< css::beans::PropertyValue >& ) +{ + bool bBack = aURL.Complete == ".uno:Backward"; + if ( !bBack && aURL.Complete != ".uno:Forward" ) + return; + + if ( m_vHistoryUrls.empty() ) + return; + + size_t nPos = ( bBack && m_nCurPos > 0 ) ? --m_nCurPos + : ( !bBack && m_nCurPos < m_vHistoryUrls.size() - 1 ) + ? ++m_nCurPos + : std::numeric_limits<std::size_t>::max(); + + if ( nPos < std::numeric_limits<std::size_t>::max() ) + { + m_pWindow->loadHelpContent(m_vHistoryUrls[nPos], false); // false => don't add item to history again! + } + + m_pWindow->UpdateToolbox(); +} + + +void SAL_CALL HelpInterceptor_Impl::addStatusListener( + const Reference< XStatusListener >& xControl, const URL& ) +{ + DBG_ASSERT( !m_xListener.is(), "listener already exists" ); + m_xListener = xControl; +} + + +void SAL_CALL HelpInterceptor_Impl::removeStatusListener( + const Reference< XStatusListener >&, const URL&) +{ + m_xListener = nullptr; +} + +// HelpListener_Impl ----------------------------------------------------- + +HelpListener_Impl::HelpListener_Impl( HelpInterceptor_Impl* pInter ) +{ + pInterceptor = pInter; + pInterceptor->addStatusListener( this, css::util::URL() ); +} + + +void SAL_CALL HelpListener_Impl::statusChanged( const css::frame::FeatureStateEvent& Event ) +{ + INetURLObject aObj( Event.FeatureURL.Complete ); + aFactory = aObj.GetHost(); + aChangeLink.Call( *this ); +} + + +void SAL_CALL HelpListener_Impl::disposing( const css::lang::EventObject& ) +{ + pInterceptor->removeStatusListener( this, css::util::URL() ); + pInterceptor = nullptr; +} + +HelpStatusListener_Impl::HelpStatusListener_Impl( + Reference < XDispatch > const & aDispatch, URL const & rURL) +{ + aDispatch->addStatusListener(this, rURL); +} + +HelpStatusListener_Impl::~HelpStatusListener_Impl() +{ + if(xDispatch.is()) + xDispatch->removeStatusListener(this, css::util::URL()); +} + +void HelpStatusListener_Impl::statusChanged( + const FeatureStateEvent& rEvent ) +{ + aStateEvent = rEvent; +} + +void HelpStatusListener_Impl::disposing( const EventObject& ) +{ + xDispatch->removeStatusListener(this, css::util::URL()); + xDispatch = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/helpinterceptor.hxx b/sfx2/source/appl/helpinterceptor.hxx new file mode 100644 index 0000000000..2f08b1ac3b --- /dev/null +++ b/sfx2/source/appl/helpinterceptor.hxx @@ -0,0 +1,141 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SFX2_SOURCE_APPL_HELPINTERCEPTOR_HXX +#define INCLUDED_SFX2_SOURCE_APPL_HELPINTERCEPTOR_HXX + +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProviderInterception.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/frame/XStatusListener.hpp> +#include <tools/link.hxx> +#include <vcl/vclptr.hxx> +#include "newhelp.hxx" +#include <vector> + +class SfxHelpWindow_Impl; +class HelpInterceptor_Impl : public ::cppu::WeakImplHelper< + css::frame::XDispatchProviderInterceptor, + css::frame::XInterceptorInfo, + css::frame::XDispatch > + +{ +private: +friend class HelpDispatch_Impl; +friend class SfxHelpWindow_Impl; + + // the component which's dispatches we're intercepting + css::uno::Reference< css::frame::XDispatchProviderInterception > m_xIntercepted; + + // chaining + css::uno::Reference< css::frame::XDispatchProvider > m_xSlaveDispatcher; + css::uno::Reference< css::frame::XDispatchProvider > m_xMasterDispatcher; + + css::uno::Reference< css::frame::XStatusListener > m_xListener; + + std::vector<OUString> m_vHistoryUrls; + VclPtr<SfxHelpWindow_Impl> m_pWindow; + size_t m_nCurPos; + OUString m_aCurrentURL; + + void addURL( const OUString& rURL ); + +public: + HelpInterceptor_Impl(); + virtual ~HelpInterceptor_Impl() override; + + void setInterception( const css::uno::Reference< css::frame::XFrame >& xFrame ); + const OUString& GetCurrentURL() const { return m_aCurrentURL; } + + bool HasHistoryPred() const; // is there a predecessor for the current in the history + bool HasHistorySucc() const; // is there a successor for the current in the history + + // XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL + queryDispatch( const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL + queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& aDescripts ) override; + + // XDispatchProviderInterceptor + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL + getSlaveDispatchProvider( ) override; + virtual void SAL_CALL setSlaveDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewSlave ) override; + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL + getMasterDispatchProvider( ) override; + virtual void SAL_CALL setMasterDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewMaster ) override; + + // XInterceptorInfo + virtual css::uno::Sequence< OUString > SAL_CALL + getInterceptedURLs( ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override; + virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override; + virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override; + + // extras + void InitWaiter( SfxHelpWindow_Impl* pWindow ) + { m_pWindow = pWindow; } + SfxHelpWindow_Impl* GetHelpWindow() const { return m_pWindow; } +}; + +// HelpListener_Impl ----------------------------------------------------- + +class HelpListener_Impl : public ::cppu::WeakImplHelper< css::frame::XStatusListener > +{ +private: + HelpInterceptor_Impl* pInterceptor; + Link<HelpListener_Impl&,void> aChangeLink; + OUString aFactory; + +public: + explicit HelpListener_Impl( HelpInterceptor_Impl* pInter ); + + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& obj ) override; + + void SetChangeHdl( const Link<HelpListener_Impl&,void>& rLink ) { aChangeLink = rLink; } + const OUString& GetFactory() const { return aFactory; } +}; +// HelpStatusListener_Impl ----------------------------------------------------- + +class HelpStatusListener_Impl : public cppu::WeakImplHelper< css::frame::XStatusListener > +{ +private: + css::uno::Reference < css::frame::XDispatch > xDispatch; + css::frame::FeatureStateEvent aStateEvent; + +public: + HelpStatusListener_Impl( + css::uno::Reference < css::frame::XDispatch > const & xDispatch, + css::util::URL const & rURL); + virtual ~HelpStatusListener_Impl() override; + + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& obj ) override; + const css::frame::FeatureStateEvent& + GetStateEvent() const {return aStateEvent;} +}; + + +#endif // INCLUDED_SFX2_SOURCE_APPL_HELPINTERCEPTOR_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/impldde.cxx b/sfx2/source/appl/impldde.cxx new file mode 100644 index 0000000000..c70cb4cb6a --- /dev/null +++ b/sfx2/source/appl/impldde.cxx @@ -0,0 +1,348 @@ +/* -*- 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 . + */ + + +#if defined(_WIN32) +#include <prewin.h> +#include <postwin.h> +#endif + +#include "impldde.hxx" + +#include <vcl/weld.hxx> +#include <sot/exchange.hxx> +#include <rtl/ustring.hxx> + +#include <sfx2/lnkbase.hxx> +#include <sfx2/linkmgr.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include <svl/svdde.hxx> +#include <sot/formats.hxx> + +using namespace ::com::sun::star::uno; + +namespace sfx2 +{ + +namespace { + +class SvDDELinkEditDialog : public weld::GenericDialogController +{ + std::unique_ptr<weld::Entry> m_xEdDdeApp; + std::unique_ptr<weld::Entry> m_xEdDdeTopic; + std::unique_ptr<weld::Entry> m_xEdDdeItem; + std::unique_ptr<weld::Button> m_xOKButton; + + DECL_LINK(EditHdl_Impl, weld::Entry&, void); +public: + SvDDELinkEditDialog(weld::Window* pParent, SvBaseLink const*); + OUString GetCmd() const; +}; + +} + +SvDDELinkEditDialog::SvDDELinkEditDialog(weld::Window* pParent, SvBaseLink const * pLink) + : GenericDialogController(pParent, "sfx/ui/linkeditdialog.ui", "LinkEditDialog") + , m_xEdDdeApp(m_xBuilder->weld_entry("app")) + , m_xEdDdeTopic(m_xBuilder->weld_entry("file")) + , m_xEdDdeItem(m_xBuilder->weld_entry("category")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + OUString sServer, sTopic, sItem; + sfx2::LinkManager::GetDisplayNames( pLink, &sServer, &sTopic, &sItem ); + + m_xEdDdeApp->set_text( sServer ); + m_xEdDdeTopic->set_text( sTopic ); + m_xEdDdeItem->set_text( sItem ); + + m_xEdDdeApp->connect_changed( LINK( this, SvDDELinkEditDialog, EditHdl_Impl)); + m_xEdDdeTopic->connect_changed( LINK( this, SvDDELinkEditDialog, EditHdl_Impl)); + m_xEdDdeItem->connect_changed( LINK( this, SvDDELinkEditDialog, EditHdl_Impl)); + + m_xOKButton->set_sensitive(!sServer.isEmpty() && !sTopic.isEmpty() && !sItem.isEmpty()); +} + +OUString SvDDELinkEditDialog::GetCmd() const +{ + OUString sCmd( m_xEdDdeApp->get_text() ), sRet; + ::sfx2::MakeLnkName( sRet, &sCmd, m_xEdDdeTopic->get_text(), m_xEdDdeItem->get_text() ); + return sRet; +} + +IMPL_LINK_NOARG( SvDDELinkEditDialog, EditHdl_Impl, weld::Entry&, void) +{ + m_xOKButton->set_sensitive(!m_xEdDdeApp->get_text().isEmpty() && + !m_xEdDdeTopic->get_text().isEmpty() && + !m_xEdDdeItem->get_text().isEmpty() ); +} + +SvDDEObject::SvDDEObject() + : pGetData( nullptr ) +{ + SetUpdateTimeout( 100 ); + bWaitForData = false; +} + +SvDDEObject::~SvDDEObject() +{ + pLink.reset(); + pRequest.reset(); + pConnection.reset(); +} + +bool SvDDEObject::GetData( css::uno::Any & rData /*out param*/, + const OUString & rMimeType, + bool bSynchron ) +{ + if( !pConnection ) + return false; + + if( pConnection->GetError() ) // then we try once more + { + OUString sServer( pConnection->GetServiceName() ); + OUString sTopic( pConnection->GetTopicName() ); + + pConnection.reset( new DdeConnection( sServer, sTopic ) ); + } + + if( bWaitForData ) // we are in a recursive loop, get out again + return false; + + // Lock against Reentrance + bWaitForData = true; + + // if you want to print, we'll wait until the data is available + if( bSynchron ) + { + DdeRequest aReq( *pConnection, sItem, 5000 ); + aReq.SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) ); + aReq.SetFormat( SotExchange::GetFormatIdFromMimeType( rMimeType )); + + pGetData = &rData; + + do { + aReq.Execute(); + } while( aReq.GetError() && ImplHasOtherFormat( aReq ) ); + + bWaitForData = false; + } + else + { + // otherwise it will be executed asynchronously + { + pRequest.reset( new DdeRequest( *pConnection, sItem ) ); + pRequest->SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) ); + pRequest->SetDoneHdl( LINK( this, SvDDEObject, ImplDoneDDEData ) ); + pRequest->SetFormat( SotExchange::GetFormatIdFromMimeType( + rMimeType ) ); + pRequest->Execute(); + } + + rData <<= OUString(); + } + return 0 == pConnection->GetError(); +} + + +bool SvDDEObject::Connect( SvBaseLink * pSvLink ) +{ + SfxLinkUpdateMode nLinkType = pSvLink->GetUpdateMode(); + if( pConnection ) // Connection is already made + { + // well, then just add it as dependent + AddDataAdvise( pSvLink, + SotExchange::GetFormatMimeType( pSvLink->GetContentType()), + SfxLinkUpdateMode::ONCALL == nLinkType + ? ADVISEMODE_ONLYONCE + : 0 ); + AddConnectAdvise( pSvLink ); + + return true; + } + + if( !pSvLink->GetLinkManager() ) + return false; + + OUString sServer, sTopic; + sfx2::LinkManager::GetDisplayNames( pSvLink, &sServer, &sTopic, &sItem ); + + if( sServer.isEmpty() || sTopic.isEmpty() || sItem.isEmpty() ) + return false; + + pConnection.reset( new DdeConnection( sServer, sTopic ) ); + if( pConnection->GetError() ) + { + // check if the DDE server knows the "SYSTEM" topic + bool bSysTopic = false; + if (!sTopic.equalsIgnoreAsciiCase("SYSTEM")) + { + DdeConnection aTmp(sServer, "SYSTEM"); + bSysTopic = !aTmp.GetError(); + } + + if( bSysTopic ) + { + // if the system topic works then the server is up but just doesn't know the original topic + return false; + } + } + + if( SfxLinkUpdateMode::ALWAYS == nLinkType && !pLink && !pConnection->GetError() ) + { + // Setting up Hot Link, Data will be available at some point later on + pLink.reset( new DdeHotLink( *pConnection, sItem ) ); + pLink->SetDataHdl( LINK( this, SvDDEObject, ImplGetDDEData ) ); + pLink->SetDoneHdl( LINK( this, SvDDEObject, ImplDoneDDEData ) ); + pLink->SetFormat( pSvLink->GetContentType() ); + pLink->Execute(); + } + + if( pConnection->GetError() ) + return false; + + AddDataAdvise( pSvLink, + SotExchange::GetFormatMimeType( pSvLink->GetContentType()), + SfxLinkUpdateMode::ONCALL == nLinkType + ? ADVISEMODE_ONLYONCE + : 0 ); + AddConnectAdvise( pSvLink ); + SetUpdateTimeout( 0 ); + return true; +} + +void SvDDEObject::Edit(weld::Window* pParent, sfx2::SvBaseLink* pBaseLink, const Link<const OUString&, void>& rEndEditHdl) +{ + SvDDELinkEditDialog aDlg(pParent, pBaseLink); + if (RET_OK == aDlg.run() && rEndEditHdl.IsSet()) + { + OUString sCommand = aDlg.GetCmd(); + rEndEditHdl.Call( sCommand ); + } +} + +bool SvDDEObject::ImplHasOtherFormat( DdeTransaction& rReq ) +{ + SotClipboardFormatId nFmt = SotClipboardFormatId::NONE; + switch( rReq.GetFormat() ) + { + case SotClipboardFormatId::RTF: + nFmt = SotClipboardFormatId::STRING; + break; + + case SotClipboardFormatId::HTML_SIMPLE: + case SotClipboardFormatId::HTML: + nFmt = SotClipboardFormatId::RTF; + break; + + case SotClipboardFormatId::GDIMETAFILE: + nFmt = SotClipboardFormatId::BITMAP; + break; + + case SotClipboardFormatId::SVXB: + nFmt = SotClipboardFormatId::GDIMETAFILE; + break; + + // something else? + default: break; + } + if( nFmt != SotClipboardFormatId::NONE ) + rReq.SetFormat( nFmt ); // try it once more + return SotClipboardFormatId::NONE != nFmt; +} + +bool SvDDEObject::IsPending() const +/* + The method determines whether the data-object can be read from a DDE. +*/ +{ + return bWaitForData; +} + +bool SvDDEObject::IsDataComplete() const +{ + return bWaitForData; +} + +IMPL_LINK( SvDDEObject, ImplGetDDEData, const DdeData*, pData, void ) +{ + SotClipboardFormatId nFmt = pData->GetFormat(); + switch( nFmt ) + { + case SotClipboardFormatId::GDIMETAFILE: + break; + + case SotClipboardFormatId::BITMAP: + break; + + default: + { + const char* p = static_cast<char const *>(pData->getData()); + tools::Long nLen = SotClipboardFormatId::STRING == nFmt ? (p ? strlen( p ) : 0) : pData->getSize(); + + Sequence< sal_Int8 > aSeq( reinterpret_cast<const sal_Int8*>(p), nLen ); + if( pGetData ) + { + *pGetData <<= aSeq; // Copy Data + pGetData = nullptr; // reset the pointer here + } + else + { + Any aVal; + aVal <<= aSeq; + DataChanged( SotExchange::GetFormatMimeType( + pData->GetFormat() ), aVal ); + bWaitForData = false; + } + } + } +} + +IMPL_LINK( SvDDEObject, ImplDoneDDEData, bool, bValid, void ) +{ + if( !bValid && ( pRequest || pLink )) + { + DdeTransaction* pReq = nullptr; + if( !pLink || ( pLink && pLink->IsBusy() )) + pReq = pRequest.get(); // only the one that is ready + else if( pRequest && pRequest->IsBusy() ) + pReq = pLink.get(); // only the one that is ready + + if( pReq ) + { + if( ImplHasOtherFormat( *pReq ) ) + { + pReq->Execute(); + } + else if( pReq == pRequest.get() ) + { + bWaitForData = false; + } + } + } + else + // End waiting + bWaitForData = false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/impldde.hxx b/sfx2/source/appl/impldde.hxx new file mode 100644 index 0000000000..ee2f2ecb92 --- /dev/null +++ b/sfx2/source/appl/impldde.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SFX2_SOURCE_APPL_IMPLDDE_HXX +#define INCLUDED_SFX2_SOURCE_APPL_IMPLDDE_HXX + +#include <rtl/ustring.hxx> +#include <sfx2/linksrc.hxx> +#include <tools/link.hxx> + +class DdeConnection; +class DdeData; +class DdeLink; +class DdeRequest; +class DdeTransaction; + +namespace sfx2 +{ + +class SvDDEObject : public SvLinkSource +{ + OUString sItem; + + std::unique_ptr<DdeConnection> pConnection; + std::unique_ptr<DdeLink> pLink; + std::unique_ptr<DdeRequest> pRequest; + css::uno::Any * pGetData; + + bool bWaitForData; // waiting for data? + + + static bool ImplHasOtherFormat( DdeTransaction& ); + DECL_LINK( ImplGetDDEData, const DdeData*, void ); + DECL_LINK( ImplDoneDDEData, bool, void ); + +protected: + virtual ~SvDDEObject() override; + +public: + SvDDEObject(); + + virtual bool GetData( css::uno::Any & rData /*out param*/, + const OUString & aMimeType, + bool bSynchron = false ) override; + + virtual bool Connect( SvBaseLink * ) override; + virtual void Edit(weld::Window* pParent, sfx2::SvBaseLink* pBaseLink, const Link<const OUString&, void>& rEndEditHdl) override; + + virtual bool IsPending() const override; + virtual bool IsDataComplete() const override; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/linkmgr2.cxx b/sfx2/source/appl/linkmgr2.cxx new file mode 100644 index 0000000000..a20501a1ba --- /dev/null +++ b/sfx2/source/appl/linkmgr2.cxx @@ -0,0 +1,722 @@ +/* -*- 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 <comphelper/string.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/sfxsids.hrc> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <officecfg/Office/Common.hxx> +#include <osl/file.hxx> +#include <sfx2/objsh.hxx> +#include <svl/urihelper.hxx> +#include <sot/formats.hxx> +#include <tools/urlobj.hxx> +#include <sot/exchange.hxx> +#include <tools/debug.hxx> +#include <vcl/filter/SvmReader.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/gdimtf.hxx> +#include <sfx2/lnkbase.hxx> +#include <sfx2/app.hxx> +#include <vcl/graph.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <vcl/dibtools.hxx> +#include <unotools/charclass.hxx> +#include <unotools/securityoptions.hxx> +#include <vcl/GraphicLoader.hxx> +#include <vcl/TypeSerializer.hxx> + +#include "fileobj.hxx" +#include "impldde.hxx" +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::lang::XComponent; +using ::com::sun::star::util::XCloseable; + +namespace sfx2 +{ + +namespace { + +class SvxInternalLink : public sfx2::SvLinkSource +{ +public: + SvxInternalLink() {} + + virtual bool Connect( sfx2::SvBaseLink* ) override; +}; + +} + +LinkManager::LinkManager(SfxObjectShell* p) + : pPersist( p ) +{ +} + +LinkManager::~LinkManager() +{ + for(tools::SvRef<SvBaseLink> & rTmp : aLinkTbl) + { + if( rTmp.is() ) + { + rTmp->Disconnect(); + rTmp->SetLinkManager( nullptr ); + } + } +} + +void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp) +{ + maCachedComps.push_back(xComp); +} + +void LinkManager::CloseCachedComps() +{ + for (const auto& rxCachedComp : maCachedComps) + { + Reference<XCloseable> xCloseable(rxCachedComp, UNO_QUERY); + if (!xCloseable.is()) + continue; + + xCloseable->close(true); + } + maCachedComps.clear(); +} + +void LinkManager::Remove( SvBaseLink const *pLink ) +{ + // No duplicate links inserted + bool bFound = false; + for( size_t n = 0; n < aLinkTbl.size(); ) + { + tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ]; + if( pLink == rTmp.get() ) + { + rTmp->Disconnect(); + rTmp->SetLinkManager( nullptr ); + rTmp.clear(); + bFound = true; + } + + // Remove empty ones if they exist + if( !rTmp.is() ) + { + aLinkTbl.erase( aLinkTbl.begin() + n ); + if( bFound ) + return ; + } + else + ++n; + } +} + +void LinkManager::Remove( size_t nPos, size_t nCnt ) +{ + if( !nCnt || nPos >= aLinkTbl.size() ) + return; + + if (sal::static_int_cast<size_t>(nPos + nCnt) > aLinkTbl.size()) + nCnt = aLinkTbl.size() - nPos; + + for( size_t n = nPos; n < nPos + nCnt; ++n) + { + tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ]; + if( rTmp.is() ) + { + rTmp->Disconnect(); + rTmp->SetLinkManager( nullptr ); + } + } + aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt ); +} + +bool LinkManager::Insert( SvBaseLink* pLink ) +{ + for( size_t n = 0; n < aLinkTbl.size(); ++n ) + { + tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ]; + if( !rTmp.is() ) + { + aLinkTbl.erase( aLinkTbl.begin() + n-- ); + } + else if( pLink == rTmp.get() ) + return false; // No duplicate links inserted + } + + pLink->SetLinkManager( this ); + aLinkTbl.emplace_back(pLink ); + return true; +} + +bool LinkManager::InsertLink( SvBaseLink * pLink, + SvBaseLinkObjectType nObjType, + SfxLinkUpdateMode nUpdateMode, + const OUString* pName ) +{ + // This First + pLink->SetObjType( nObjType ); + if( pName ) + pLink->SetName( *pName ); + pLink->SetUpdateMode( nUpdateMode ); + return Insert( pLink ); +} + +void LinkManager::InsertDDELink( SvBaseLink * pLink, + const OUString& rServer, + std::u16string_view rTopic, + std::u16string_view rItem ) +{ + if( !isClientType( pLink->GetObjType() ) ) + return; + + OUString sCmd; + ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem ); + + pLink->SetObjType( SvBaseLinkObjectType::ClientDde ); + pLink->SetName( sCmd ); + Insert( pLink ); +} + +void LinkManager::InsertDDELink( SvBaseLink * pLink ) +{ + DBG_ASSERT( isClientType(pLink->GetObjType()), "no OBJECT_CLIENT_SO" ); + if( !isClientType( pLink->GetObjType() ) ) + return; + + if( pLink->GetObjType() == SvBaseLinkObjectType::ClientSo ) + pLink->SetObjType( SvBaseLinkObjectType::ClientDde ); + + Insert( pLink ); +} + +// Obtain the string for the dialog +bool LinkManager::GetDisplayNames( const SvBaseLink * pLink, + OUString* pType, + OUString* pFile, + OUString* pLinkStr, + OUString* pFilter ) +{ + bool bRet = false; + const OUString& sLNm( pLink->GetLinkSourceName() ); + if( !sLNm.isEmpty() ) + { + switch( pLink->GetObjType() ) + { + case SvBaseLinkObjectType::ClientFile: + case SvBaseLinkObjectType::ClientGraphic: + case SvBaseLinkObjectType::ClientOle: + { + sal_Int32 nPos = 0; + OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) ); + OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) ); + + if( pFile ) + *pFile = sFile; + if( pLinkStr ) + *pLinkStr = sRange; + if( pFilter ) + *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos); + + if( pType ) + { + SvBaseLinkObjectType nObjType = pLink->GetObjType(); + *pType = SfxResId( + ( SvBaseLinkObjectType::ClientFile == nObjType || SvBaseLinkObjectType::ClientOle == nObjType ) + ? RID_SVXSTR_FILELINK + : RID_SVXSTR_GRAPHICLINK); + } + bRet = true; + } + break; + case SvBaseLinkObjectType::ClientDde: + { + sal_Int32 nTmp = 0; + OUString sServer( sLNm.getToken( 0, cTokenSeparator, nTmp ) ); + OUString sTopic( sLNm.getToken( 0, cTokenSeparator, nTmp ) ); + + if( pType ) + *pType = sServer; + if( pFile ) + *pFile = sTopic; + if( pLinkStr ) + *pLinkStr = nTmp != -1 ? sLNm.copy(nTmp) : OUString(); + bRet = true; + } + break; + default: + break; + } + } + + return bRet; +} + +void LinkManager::UpdateAllLinks( + bool bAskUpdate, + bool bUpdateGrfLinks, + weld::Window* pParentWin ) +{ + // when active content is disabled don't bother updating all links + // also (when bAskUpdate == true) don't show the pop up. + if(officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) + return; + + // First make a copy of the array in order to update links + // links in ... no contact between them! + std::vector<SvBaseLink*> aTmpArr; + for( size_t n = 0; n < aLinkTbl.size(); ++n ) + { + tools::SvRef<SvBaseLink>& rLink = aLinkTbl[ n ]; + if( !rLink.is() ) + { + Remove( n-- ); + continue; + } + aTmpArr.push_back( rLink.get() ); + } + + for(SvBaseLink* pLink : aTmpArr) + { + // search first in the array after the entry + bool bFound = false; + for(const tools::SvRef<SvBaseLink> & i : aLinkTbl) + if( pLink == i.get() ) + { + bFound = true; + break; + } + + if( !bFound ) + continue; // was not available! + + // Graphic-Links not to update yet + if( !pLink->IsVisible() || + ( !bUpdateGrfLinks && SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() )) + continue; + + if( bAskUpdate ) + { + OUString aMsg = SfxResId(STR_QUERY_UPDATE_LINKS); + INetURLObject aURL(pPersist->getDocumentBaseURL()); + aMsg = aMsg.replaceFirst("%{filename}", aURL.GetLastName()); + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParentWin, + VclMessageType::Question, VclButtonsType::YesNo, aMsg)); + xQueryBox->set_default_response(RET_YES); + + int nRet = xQueryBox->run(); + if( RET_YES != nRet ) + { + SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); + + if(pShell) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); + } + + return ; // nothing should be updated + } + bAskUpdate = false; // once is enough + } + + pLink->Update(); + } + CloseCachedComps(); +} + +SvLinkSourceRef LinkManager::CreateObj( SvBaseLink const * pLink ) +{ + switch( pLink->GetObjType() ) + { + case SvBaseLinkObjectType::ClientFile: + case SvBaseLinkObjectType::ClientGraphic: + case SvBaseLinkObjectType::ClientOle: + return new SvFileObject; + case SvBaseLinkObjectType::Internal: + if(officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) + return SvLinkSourceRef(); + return new SvxInternalLink; + case SvBaseLinkObjectType::ClientDde: + if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) + return SvLinkSourceRef(); + return new SvDDEObject; + default: + return SvLinkSourceRef(); + } +} + +bool LinkManager::InsertServer( SvLinkSource* pObj ) +{ + // no duplicate inserts + if( !pObj ) + return false; + + return aServerTbl.insert( pObj ).second; +} + +void LinkManager::RemoveServer( SvLinkSource* pObj ) +{ + aServerTbl.erase( pObj ); +} + +void MakeLnkName( OUString& rName, const OUString* pType, std::u16string_view rFile, + std::u16string_view rLink, const OUString* pFilter ) +{ + if( pType ) + { + rName = comphelper::string::strip(*pType, ' ') + + OUStringChar(cTokenSeparator); + } + else + rName.clear(); + + rName += rFile; + + rName = comphelper::string::strip(rName, ' ') + + OUStringChar(cTokenSeparator); + rName = comphelper::string::strip(rName, ' ') + rLink; + if( pFilter ) + { + rName += OUStringChar(cTokenSeparator) + *pFilter; + rName = comphelper::string::strip(rName, ' '); + } +} + +void LinkManager::ReconnectDdeLink(SfxObjectShell& rServer) +{ + SfxMedium* pMed = rServer.GetMedium(); + if (!pMed) + return; + + const ::sfx2::SvBaseLinks& rLinks = GetLinks(); + size_t n = rLinks.size(); + + for (size_t i = 0; i < n; ++i) + { + ::sfx2::SvBaseLink* p = rLinks[i].get(); + OUString aType, aFile, aLink, aFilter; + if (!GetDisplayNames(p, &aType, &aFile, &aLink, &aFilter)) + continue; + + if (aType != "soffice") + // DDE connections between OOo apps are always named 'soffice'. + continue; + + OUString aTmp; + OUString aURL = aFile; + if (osl::FileBase::getFileURLFromSystemPath(aFile, aTmp) + == osl::FileBase::E_None) + aURL = aTmp; + + if (!aURL.equalsIgnoreAsciiCase(pMed->GetName())) + // This DDE link is not associated with this server shell... Skip it. + continue; + + if (aLink.isEmpty()) + continue; + + LinkServerShell(aLink, rServer, *p); + } +} + +void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink) +{ + ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath); + if (pSrvSrc) + { + css::datatransfer::DataFlavor aFl; + SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl); + rLink.SetObj(pSrvSrc); + pSrvSrc->AddDataAdvise( + &rLink, aFl.MimeType, + SfxLinkUpdateMode::ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0); + } +} + +void LinkManager::InsertFileLink( + sfx2::SvBaseLink& rLink, SvBaseLinkObjectType nFileType, std::u16string_view rFileNm, + const OUString* pFilterNm, const OUString* pRange) +{ + if (!isClientType(rLink.GetObjType())) + return; + + OUStringBuffer aBuf(64); + aBuf.append(rFileNm + OUStringChar(sfx2::cTokenSeparator)); + + if (pRange) + aBuf.append(*pRange); + + if (pFilterNm) + { + aBuf.append(OUStringChar(sfx2::cTokenSeparator) + *pFilterNm); + } + + OUString aCmd = aBuf.makeStringAndClear(); + InsertLink(&rLink, nFileType, SfxLinkUpdateMode::ONCALL, &aCmd); +} + +// A transfer is aborted, so cancel all download media +// (for now this is only of interest for the file links!) +void LinkManager::CancelTransfers() +{ + + const sfx2::SvBaseLinks& rLnks = GetLinks(); + for( size_t n = rLnks.size(); n; ) + { + const sfx2::SvBaseLink& rLnk = *rLnks[--n]; + if (isClientFileType(rLnk.GetObjType())) + { + if (SvFileObject* pFileObj = static_cast<SvFileObject*>(rLnk.GetObj())) + pFileObj->CancelTransfers(); + } + } +} + +// For the purpose of sending Status information from the file object to +// the base link, there exist a dedicated ClipBoardId. The SvData-object +// gets the appropriate information as a string +// For now this is required for file object in conjunction with JavaScript +// - needs information about Load/Abort/Error +SotClipboardFormatId LinkManager::RegisterStatusInfoId() +{ + static SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + + if( nFormat == SotClipboardFormatId::NONE ) + { + nFormat = SotExchange::RegisterFormatName( + "StatusInfo from SvxInternalLink"); + } + return nFormat; +} + +bool LinkManager::GetGraphicFromAny(std::u16string_view rMimeType, + const css::uno::Any & rValue, + Graphic& rGraphic, + weld::Window* pParentWin) +{ + bool bRet = false; + + if (!rValue.hasValue()) + return bRet; + + if (rValue.has<OUString>()) + { + OUString sReferer; + SfxObjectShell* sh = GetPersist(); + if (sh && sh->HasName()) + sReferer = sh->GetMedium()->GetName(); + + OUString sURL = rValue.get<OUString>(); + if (!SvtSecurityOptions::isUntrustedReferer(sReferer)) + rGraphic = vcl::graphic::loadFromURL(sURL, pParentWin); + if (rGraphic.IsNone()) + rGraphic.SetDefaultType(); + rGraphic.setOriginURL(sURL); + return true; + } + else if (rValue.has<css::uno::Sequence<sal_Int8>>()) + { + auto aSeq = rValue.get<css::uno::Sequence<sal_Int8>>(); + + SvMemoryStream aMemStm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), + StreamMode::READ ); + aMemStm.Seek( 0 ); + + switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) + { + case SotClipboardFormatId::SVXB: + { + TypeSerializer aSerializer(aMemStm); + aSerializer.readGraphic(rGraphic); + bRet = true; + } + break; + case SotClipboardFormatId::GDIMETAFILE: + { + GDIMetaFile aMtf; + SvmReader aReader( aMemStm ); + aReader.Read( aMtf ); + rGraphic = aMtf; + bRet = true; + } + break; + case SotClipboardFormatId::BITMAP: + { + Bitmap aBmp; + ReadDIB(aBmp, aMemStm, true); + rGraphic = BitmapEx(aBmp); + bRet = true; + } + break; + default: break; + } + } + return bRet; +} + +static OUString lcl_DDE_RelToAbs( const OUString& rTopic, std::u16string_view rBaseURL ) +{ + OUString sRet; + INetURLObject aURL( rTopic ); + if( INetProtocol::NotValid == aURL.GetProtocol() ) + osl::FileBase::getFileURLFromSystemPath(rTopic, sRet); + if( sRet.isEmpty() ) + sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl() ); + return sRet; +} + +bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink ) +{ + SfxObjectShell* pFndShell = nullptr; + sal_uInt16 nUpdateMode = css::document::UpdateDocMode::NO_UPDATE; + OUString sTopic, sItem, sReferer; + LinkManager* pLinkMgr = pLink->GetLinkManager(); + if (pLinkMgr && sfx2::LinkManager::GetDisplayNames(pLink, nullptr, &sTopic, &sItem) && !sTopic.isEmpty()) + { + // first only loop over the DocumentShells the shells and find those + // with the name: + CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) ); + + bool bFirst = true; + SfxObjectShell* pShell = pLinkMgr->GetPersist(); + if( pShell && pShell->GetMedium() ) + { + sReferer = pShell->GetMedium()->GetBaseURL(); + const SfxUInt16Item* pItem = pShell->GetMedium()->GetItemSet().GetItem(SID_UPDATEDOCMODE, false); + if ( pItem ) + nUpdateMode = pItem->GetValue(); + } + + OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer))); + + if ( !pShell ) + { + bFirst = false; + pShell = SfxObjectShell::GetFirst( nullptr, false ); + } + + OUString sTmp; + while( pShell ) + { + if( sTmp.isEmpty() ) + { + sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME ); + sTmp = lcl_DDE_RelToAbs(sTmp, sReferer ); + } + + + sTmp = aCC.lowercase( sTmp ); + if( sTmp == sNmURL ) // we want these + { + pFndShell = pShell; + break; + } + + if( bFirst ) + { + bFirst = false; + pShell = SfxObjectShell::GetFirst( nullptr, false ); + } + else + pShell = SfxObjectShell::GetNext( *pShell, nullptr, false ); + + sTmp.clear(); + } + } + + // empty topics are not allowed - which document is it + if( sTopic.isEmpty() ) + return false; + + if (pFndShell) + { + sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem ); + if( pNewSrc ) + { + css::datatransfer::DataFlavor aFl; + SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl ); + + pLink->SetObj( pNewSrc ); + pNewSrc->AddDataAdvise( pLink, aFl.MimeType, + SfxLinkUpdateMode::ONCALL == pLink->GetUpdateMode() + ? ADVISEMODE_ONLYONCE + : 0 ); + return true; + } + } + else + { + // then try to download the file: + INetURLObject aURL( sTopic ); + INetProtocol eOld = aURL.GetProtocol(); + sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ); + aURL.SetURL( sTopic ); + if( INetProtocol::NotValid != eOld || + INetProtocol::Http != aURL.GetProtocol() ) + { + SfxStringItem aName( SID_FILE_NAME, sTopic ); + SfxBoolItem aMinimized(SID_MINIMIZED, true); + SfxBoolItem aHidden(SID_HIDDEN, true); + SfxStringItem aTarget( SID_TARGETNAME, "_blank" ); + SfxStringItem aReferer( SID_REFERER, sReferer ); + SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode ); + SfxBoolItem aReadOnly(SID_DOC_READONLY, false); + + // Disable automatic re-connection to avoid this link instance + // being destroyed at re-connection. + SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false); + + // #i14200# (DDE-link crashes wordprocessor) + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + aArgs.Put(aReferer); + aArgs.Put(aTarget); + aArgs.Put(aHidden); + aArgs.Put(aMinimized); + aArgs.Put(aName); + aArgs.Put(aUpdate); + aArgs.Put(aReadOnly); + aArgs.Put(aDdeConnect); + Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs); + pFndShell = SfxObjectShell::GetShellFromComponent(xComp); + if (xComp.is() && pFndShell && pLinkMgr) + { + pLinkMgr->InsertCachedComp(xComp); + sfx2::LinkManager::LinkServerShell(sItem, *pFndShell, *pLink); + return true; + } + } + } + + return false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/linksrc.cxx b/sfx2/source/appl/linksrc.cxx new file mode 100644 index 0000000000..3a1af228f6 --- /dev/null +++ b/sfx2/source/appl/linksrc.cxx @@ -0,0 +1,418 @@ +/* -*- 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 <sfx2/linksrc.hxx> +#include <sfx2/lnkbase.hxx> +#include <com/sun/star/uno/Any.hxx> + +#include <utility> +#include <vcl/timer.hxx> +#include <memory> +#include <vector> +#include <algorithm> + + +using namespace ::com::sun::star::uno; + +namespace sfx2 +{ + +namespace { + +class SvLinkSourceTimer : public Timer +{ + SvLinkSource * pOwner; + virtual void Invoke() override; +public: + explicit SvLinkSourceTimer( SvLinkSource * pOwn ); +}; + +} + +SvLinkSourceTimer::SvLinkSourceTimer( SvLinkSource * pOwn ) + : Timer("sfx2 SvLinkSourceTimer"), pOwner( pOwn ) +{ +} + +void SvLinkSourceTimer::Invoke() +{ + // Secure against being destroyed in Handler + SvLinkSourceRef xHoldAlive( pOwner ); + pOwner->SendDataChanged(); +} + +static void StartTimer( std::unique_ptr<SvLinkSourceTimer>& pTimer, SvLinkSource * pOwner, + sal_uInt64 nTimeout ) +{ + if( !pTimer ) + { + pTimer.reset( new SvLinkSourceTimer( pOwner ) ); + pTimer->SetTimeout( nTimeout ); + pTimer->Start(); + } +} + +namespace { + +struct SvLinkSource_Entry_Impl +{ + tools::SvRef<SvBaseLink> xSink; + OUString aDataMimeType; + sal_uInt16 nAdviseModes; + bool bIsDataSink; + + SvLinkSource_Entry_Impl( SvBaseLink* pLink, OUString aMimeType, + sal_uInt16 nAdvMode ) + : xSink( pLink ), aDataMimeType(std::move( aMimeType )), + nAdviseModes( nAdvMode ), bIsDataSink( true ) + {} + + explicit SvLinkSource_Entry_Impl( SvBaseLink* pLink ) + : xSink( pLink ), nAdviseModes( 0 ), bIsDataSink( false ) + {} +}; + +class SvLinkSource_Array_Impl +{ +friend class SvLinkSource_EntryIter_Impl; +private: + std::vector<std::unique_ptr<SvLinkSource_Entry_Impl>> mvData; + +public: + SvLinkSource_Array_Impl() {} + + size_t size() const { return mvData.size(); } + SvLinkSource_Entry_Impl *operator[](size_t idx) const { return mvData[idx].get(); } + void push_back(SvLinkSource_Entry_Impl* rData) { mvData.emplace_back(rData); } + + void DeleteAndDestroy(SvLinkSource_Entry_Impl const * p) + { + auto it = std::find_if(mvData.begin(), mvData.end(), + [&p](const std::unique_ptr<SvLinkSource_Entry_Impl>& rxData) { return rxData.get() == p; }); + if (it != mvData.end()) + mvData.erase(it); + } +}; + +class SvLinkSource_EntryIter_Impl +{ + std::vector<SvLinkSource_Entry_Impl*> aArr; + const SvLinkSource_Array_Impl& rOrigArr; + sal_uInt16 nPos; +public: + explicit SvLinkSource_EntryIter_Impl( const SvLinkSource_Array_Impl& rArr ); + SvLinkSource_Entry_Impl* Curr() + { return nPos < aArr.size() ? aArr[ nPos ] : nullptr; } + SvLinkSource_Entry_Impl* Next(); + bool IsValidCurrValue( SvLinkSource_Entry_Impl const * pEntry ); +}; + +} + +SvLinkSource_EntryIter_Impl::SvLinkSource_EntryIter_Impl( + const SvLinkSource_Array_Impl& rArr ) + : rOrigArr( rArr ), nPos( 0 ) +{ + for (auto const & i : rArr.mvData) + aArr.push_back(i.get()); +} + +bool SvLinkSource_EntryIter_Impl::IsValidCurrValue( SvLinkSource_Entry_Impl const * pEntry ) +{ + if ( nPos >= aArr.size() ) + return false; + if (aArr[nPos] != pEntry) + return false; + for (auto const & i : rOrigArr.mvData) + if (i.get() == pEntry) + return true; + return false; +} + +SvLinkSource_Entry_Impl* SvLinkSource_EntryIter_Impl::Next() +{ + SvLinkSource_Entry_Impl* pRet = nullptr; + if( nPos + 1 < static_cast<sal_uInt16>(aArr.size()) ) + { + ++nPos; + if( rOrigArr.size() == aArr.size() && + rOrigArr[ nPos ] == aArr[ nPos ] ) + pRet = aArr[ nPos ]; + else + { + // then we must search the current (or the next) in the orig + do { + pRet = aArr[ nPos ]; + for (auto const & i : rOrigArr.mvData) + if (i.get() == pRet) + return pRet; + pRet = nullptr; + ++nPos; + } while( nPos < aArr.size() ); + + if( nPos >= aArr.size() ) + pRet = nullptr; + } + } + return pRet; +} + +struct SvLinkSource_Impl +{ + SvLinkSource_Array_Impl aArr; + OUString aDataMimeType; + std::unique_ptr<SvLinkSourceTimer> + pTimer; + sal_uInt64 nTimeout; + css::uno::Reference<css::io::XInputStream> + m_xInputStreamToLoadFrom; + bool m_bIsReadOnly; + + SvLinkSource_Impl() + : nTimeout(3000) + , m_bIsReadOnly(false) + { + } +}; + +SvLinkSource::SvLinkSource() + : pImpl( new SvLinkSource_Impl ) +{ +} + +SvLinkSource::~SvLinkSource() +{ +} + + +SvLinkSource::StreamToLoadFrom SvLinkSource::getStreamToLoadFrom() +{ + return StreamToLoadFrom( + pImpl->m_xInputStreamToLoadFrom, + pImpl->m_bIsReadOnly); +} + +void SvLinkSource::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly ) +{ + pImpl->m_xInputStreamToLoadFrom = xInputStream; + pImpl->m_bIsReadOnly = bIsReadOnly; +} + +// #i88291# +void SvLinkSource::clearStreamToLoadFrom() +{ + pImpl->m_xInputStreamToLoadFrom.clear(); +} + +void SvLinkSource::Closed() +{ + SvLinkSource_EntryIter_Impl aIter( pImpl->aArr ); + for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() ) + if( !p->bIsDataSink ) + p->xSink->Closed(); +} + +sal_uInt64 SvLinkSource::GetUpdateTimeout() const +{ + return pImpl->nTimeout; +} + +void SvLinkSource::SetUpdateTimeout( sal_uInt64 nTimeout ) +{ + pImpl->nTimeout = nTimeout; + if( pImpl->pTimer ) + pImpl->pTimer->SetTimeout( nTimeout ); +} + +void SvLinkSource::SendDataChanged() +{ + SvLinkSource_EntryIter_Impl aIter( pImpl->aArr ); + for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() ) + { + if( p->bIsDataSink ) + { + OUString sDataMimeType( pImpl->aDataMimeType ); + if( sDataMimeType.isEmpty() ) + sDataMimeType = p->aDataMimeType; + + Any aVal; + if( ( p->nAdviseModes & ADVISEMODE_NODATA ) || + GetData( aVal, sDataMimeType, true ) ) + { + p->xSink->DataChanged( sDataMimeType, aVal ); + + if ( !aIter.IsValidCurrValue( p ) ) + continue; + + if( p->nAdviseModes & ADVISEMODE_ONLYONCE ) + { + pImpl->aArr.DeleteAndDestroy( p ); + } + + } + } + } + pImpl->pTimer.reset(); + pImpl->aDataMimeType.clear(); +} + +void SvLinkSource::NotifyDataChanged() +{ + if( pImpl->nTimeout ) + StartTimer( pImpl->pTimer, this, pImpl->nTimeout ); // New timeout + else + { + SvLinkSource_EntryIter_Impl aIter( pImpl->aArr ); + for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() ) + if( p->bIsDataSink ) + { + Any aVal; + if( ( p->nAdviseModes & ADVISEMODE_NODATA ) || + GetData( aVal, p->aDataMimeType, true ) ) + { + tools::SvRef<sfx2::SvBaseLink> xLink(p->xSink); + xLink->DataChanged( p->aDataMimeType, aVal ); + + if ( !aIter.IsValidCurrValue( p ) ) + continue; + + if( p->nAdviseModes & ADVISEMODE_ONLYONCE ) + { + pImpl->aArr.DeleteAndDestroy( p ); + } + } + } + + pImpl->pTimer.reset(); + } +} + +// notify the sink, the mime type is not +// a selection criterion +void SvLinkSource::DataChanged( const OUString & rMimeType, + const css::uno::Any & rVal ) +{ + if( pImpl->nTimeout && !rVal.hasValue() ) + { // only when no data was included + // fire all data to the sink, independent of the requested format + pImpl->aDataMimeType = rMimeType; + StartTimer( pImpl->pTimer, this, pImpl->nTimeout ); // New timeout + } + else + { + SvLinkSource_EntryIter_Impl aIter( pImpl->aArr ); + for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() ) + { + if( p->bIsDataSink ) + { + p->xSink->DataChanged( rMimeType, rVal ); + + if ( !aIter.IsValidCurrValue( p ) ) + continue; + + if( p->nAdviseModes & ADVISEMODE_ONLYONCE ) + { + pImpl->aArr.DeleteAndDestroy( p ); + } + } + } + + pImpl->pTimer.reset(); + } +} + + +// only one link is correct +void SvLinkSource::AddDataAdvise( SvBaseLink * pLink, const OUString& rMimeType, + sal_uInt16 nAdviseModes ) +{ + SvLinkSource_Entry_Impl* pNew = new SvLinkSource_Entry_Impl( + pLink, rMimeType, nAdviseModes ); + pImpl->aArr.push_back( pNew ); +} + +void SvLinkSource::RemoveAllDataAdvise( SvBaseLink const * pLink ) +{ + SvLinkSource_EntryIter_Impl aIter( pImpl->aArr ); + for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() ) + if( p->bIsDataSink && p->xSink.get() == pLink ) + { + pImpl->aArr.DeleteAndDestroy( p ); + } +} + +// only one link is correct +void SvLinkSource::AddConnectAdvise( SvBaseLink * pLink ) +{ + SvLinkSource_Entry_Impl* pNew = new SvLinkSource_Entry_Impl( pLink ); + pImpl->aArr.push_back( pNew ); +} + +void SvLinkSource::RemoveConnectAdvise( SvBaseLink const * pLink ) +{ + SvLinkSource_EntryIter_Impl aIter( pImpl->aArr ); + for( SvLinkSource_Entry_Impl* p = aIter.Curr(); p; p = aIter.Next() ) + if( !p->bIsDataSink && p->xSink.get() == pLink ) + { + pImpl->aArr.DeleteAndDestroy( p ); + } +} + +bool SvLinkSource::HasDataLinks() const +{ + bool bRet = false; + for( sal_uInt16 n = 0, nEnd = pImpl->aArr.size(); n < nEnd; ++n ) + if( pImpl->aArr[ n ]->bIsDataSink ) + { + bRet = true; + break; + } + return bRet; +} + +// sal_True => waitinmg for data +bool SvLinkSource::IsPending() const +{ + return false; +} + +// sal_True => data complete loaded +bool SvLinkSource::IsDataComplete() const +{ + return true; +} + +bool SvLinkSource::Connect( SvBaseLink* ) +{ + return true; +} + +bool SvLinkSource::GetData( css::uno::Any &, const OUString &, bool ) +{ + return false; +} + +void SvLinkSource::Edit(weld::Window *, SvBaseLink *, const Link<const OUString&, void>&) +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/lnkbase2.cxx b/sfx2/source/appl/lnkbase2.cxx new file mode 100644 index 0000000000..eea1751cd5 --- /dev/null +++ b/sfx2/source/appl/lnkbase2.cxx @@ -0,0 +1,604 @@ +/* -*- 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 <memory> +#include <sfx2/lnkbase.hxx> +#include <sot/exchange.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <sfx2/linkmgr.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> +#include <sfx2/filedlghelper.hxx> +#include <tools/debug.hxx> +#include <svl/svdde.hxx> +#include <osl/diagnose.h> +#include <officecfg/Office/Common.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sfx2 +{ + +namespace { +class ImplDdeItem; +} + +// only for internal management +struct ImplBaseLinkData +{ + struct tClientType + { + // applies for all links + SotClipboardFormatId nCntntType; // Update Format + // Not Ole-Links + bool bIntrnlLnk; // It is an internal link + SfxLinkUpdateMode nUpdateMode; // UpdateMode + }; + + struct tDDEType + { + ImplDdeItem* pItem; + }; + + union { + tClientType ClientType; + tDDEType DDEType; + }; + ImplBaseLinkData() + { + ClientType.nCntntType = SotClipboardFormatId::NONE; + ClientType.bIntrnlLnk = false; + ClientType.nUpdateMode = SfxLinkUpdateMode::NONE; + DDEType.pItem = nullptr; + } +}; + +namespace { + +class ImplDdeItem : public DdeGetPutItem +{ + SvBaseLink* pLink; + DdeData aData; + Sequence< sal_Int8 > aSeq; // Datacontainer for DdeData !!! + bool bIsValidData : 1; + bool bIsInDTOR : 1; +public: +#if defined(_WIN32) + ImplDdeItem( SvBaseLink& rLink, const OUString& rStr ) + : DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( false ), + bIsInDTOR( false ) + {} +#endif + virtual ~ImplDdeItem() override; + + virtual DdeData* Get( SotClipboardFormatId ) override; + virtual bool Put( const DdeData* ) override; + virtual void AdviseLoop( bool ) override; + + void Notify() + { + bIsValidData = false; + DdeGetPutItem::NotifyClient(); + } + + bool IsInDTOR() const { return bIsInDTOR; } +}; + +} + +SvBaseLink::SvBaseLink() + : m_pLinkMgr( nullptr ) + , m_pParentWin( nullptr ) + , m_bIsConnect( false ) + , m_bIsReadOnly(false) +{ + mnObjType = SvBaseLinkObjectType::ClientSo; + pImplData.reset( new ImplBaseLinkData ); + bVisible = bSynchron = true; + bWasLastEditOK = false; +} + + +SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode, SotClipboardFormatId nContentType ) + : m_pLinkMgr( nullptr ) + , m_pParentWin( nullptr ) + , m_bIsConnect( false ) + , m_bIsReadOnly(false) +{ + mnObjType = SvBaseLinkObjectType::ClientSo; + pImplData.reset( new ImplBaseLinkData ); + bVisible = bSynchron = true; + bWasLastEditOK = false; + + // It is going to be an OLE-Link, + pImplData->ClientType.nUpdateMode = nUpdateMode; + pImplData->ClientType.nCntntType = nContentType; + pImplData->ClientType.bIntrnlLnk = false; +} + +#if defined(_WIN32) + +static DdeTopic* FindTopic( const OUString & rLinkName, sal_uInt16* pItemStt ) +{ + if( rLinkName.isEmpty() ) + return nullptr; + + OUString sNm( rLinkName ); + sal_Int32 nTokenPos = 0; + OUString sService( sNm.getToken( 0, cTokenSeparator, nTokenPos ) ); + + DdeServices& rSvc = DdeService::GetServices(); + for (auto const& elem : rSvc) + { + if(elem->GetName() == sService) + { + // then we search for the Topic + OUString sTopic( sNm.getToken( 0, cTokenSeparator, nTokenPos ) ); + if( pItemStt ) + *pItemStt = nTokenPos; + + std::vector<DdeTopic*>& rTopics = elem->GetTopics(); + + for (auto const& topic : rTopics) + if( topic->GetName() == sTopic ) + return topic; + break; + } + } + return nullptr; +} + +SvBaseLink::SvBaseLink( const OUString& rLinkName, SvBaseLinkObjectType nObjectType, SvLinkSource* pObj ) + : m_pLinkMgr( nullptr ) + , m_pParentWin( nullptr ) + , m_bIsConnect( false ) + , m_bIsReadOnly(false) +{ + bVisible = bSynchron = true; + bWasLastEditOK = false; + aLinkName = rLinkName; + pImplData.reset( new ImplBaseLinkData ); + mnObjType = nObjectType; + + if( !pObj ) + { + DBG_ASSERT( pObj, "Where is my left-most object" ); + return; + } + + if( SvBaseLinkObjectType::DdeExternal == mnObjType ) + { + sal_uInt16 nItemStt = 0; + DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt ); + if( pTopic ) + { + // then we have it all together + // MM_TODO how do I get the name + OUString aStr = aLinkName; // xLinkName->GetDisplayName(); + aStr = aStr.copy( nItemStt ); + pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr ); + pTopic->InsertItem( pImplData->DDEType.pItem ); + + // store the Advice + xObj = pObj; + } + } + else if( pObj->Connect( this ) ) + xObj = pObj; +} + +#endif + +SvBaseLink::~SvBaseLink() +{ + Disconnect(); + + if( mnObjType == SvBaseLinkObjectType::DdeExternal ) + { + if( !pImplData->DDEType.pItem->IsInDTOR() ) + delete pImplData->DDEType.pItem; + } + + pImplData.reset(); +} + +IMPL_LINK( SvBaseLink, EndEditHdl, const OUString&, _rNewName, void ) +{ + OUString sNewName = _rNewName; + if ( !ExecuteEdit( sNewName ) ) + sNewName.clear(); + bWasLastEditOK = !sNewName.isEmpty(); + m_aEndEditLink.Call( *this ); +} + + +void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP ) +{ + DBG_ASSERT( mnObjType != SvBaseLinkObjectType::ClientDde, "type already set" ); + DBG_ASSERT( !xObj.is(), "object exist" ); + + mnObjType = mnObjTypeP; +} + + +void SvBaseLink::SetName( const OUString & rNm ) +{ + aLinkName = rNm; +} + + +void SvBaseLink::SetObj( SvLinkSource * pObj ) +{ + DBG_ASSERT( (isClientType(mnObjType) && + pImplData->ClientType.bIntrnlLnk) || + mnObjType == SvBaseLinkObjectType::ClientGraphic, + "no intern link" ); + xObj = pObj; +} + + +void SvBaseLink::SetLinkSourceName( const OUString & rLnkNm ) +{ + if( aLinkName == rLnkNm ) + return; + + AddNextRef(); // should be superfluous + // remove old connection + Disconnect(); + + aLinkName = rLnkNm; + + // New Connection + GetRealObject_(); + ReleaseRef(); // should be superfluous +} + + +void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode ) +{ + if( isClientType(mnObjType) && + pImplData->ClientType.nUpdateMode != nMode ) + { + AddNextRef(); + Disconnect(); + + pImplData->ClientType.nUpdateMode = nMode; + GetRealObject_(); + ReleaseRef(); + } +} + +// #i88291# +void SvBaseLink::clearStreamToLoadFrom() +{ + m_xInputStreamToLoadFrom.clear(); + if( xObj.is() ) + { + xObj->clearStreamToLoadFrom(); + } +} + +bool SvBaseLink::Update() +{ + if(officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) + return false; + + if( isClientType(mnObjType) ) + { + AddNextRef(); + Disconnect(); + + GetRealObject_(); + ReleaseRef(); + if( xObj.is() ) + { + xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly); + OUString sMimeType( SotExchange::GetFormatMimeType( + pImplData->ClientType.nCntntType )); + Any aData; + + if( xObj->GetData( aData, sMimeType ) ) + { + UpdateResult eRes = DataChanged(sMimeType, aData); + bool bSuccess = eRes == SUCCESS; + //for manual Updates there is no need to hold the ServerObject + if( SvBaseLinkObjectType::ClientDde == mnObjType && + SfxLinkUpdateMode::ONCALL == GetUpdateMode() && xObj.is() ) + xObj->RemoveAllDataAdvise( this ); + return bSuccess; + } + if( xObj.is() ) + { + // should be asynchronous? + if( xObj->IsPending() ) + return true; + + // we do not need the object anymore + AddNextRef(); + Disconnect(); + ReleaseRef(); + } + } + } + return false; +} + + +SfxLinkUpdateMode SvBaseLink::GetUpdateMode() const +{ + return isClientType(mnObjType) + ? pImplData->ClientType.nUpdateMode + : SfxLinkUpdateMode::ONCALL; +} + + +void SvBaseLink::GetRealObject_( bool bConnect) +{ + if( !m_pLinkMgr ) + return; + + DBG_ASSERT( !xObj.is(), "object already exist" ); + + if( SvBaseLinkObjectType::ClientDde == mnObjType ) + { + OUString sServer; + if( sfx2::LinkManager::GetDisplayNames( this, &sServer ) && + sServer == Application::GetAppName() ) // internal Link !!! + { + // so that the Internal link can be created! + mnObjType = SvBaseLinkObjectType::Internal; + xObj = sfx2::LinkManager::CreateObj( this ); + + pImplData->ClientType.bIntrnlLnk = true; + mnObjType = SvBaseLinkObjectType::ClientDde; // so we know what it once was! + } + else + { + pImplData->ClientType.bIntrnlLnk = false; + xObj = sfx2::LinkManager::CreateObj( this ); + } + } + else if( isClientType(mnObjType) ) + xObj = sfx2::LinkManager::CreateObj( this ); + + if( bConnect && ( !xObj.is() || !xObj->Connect( this ) ) ) + Disconnect(); +} + +SotClipboardFormatId SvBaseLink::GetContentType() const +{ + if( isClientType(mnObjType) ) + return pImplData->ClientType.nCntntType; + + return SotClipboardFormatId::NONE; // all Formats ? +} + + +void SvBaseLink::SetContentType( SotClipboardFormatId nType ) +{ + if( isClientType(mnObjType) ) + { + pImplData->ClientType.nCntntType = nType; + } +} + +LinkManager* SvBaseLink::GetLinkManager() +{ + return m_pLinkMgr; +} + +const LinkManager* SvBaseLink::GetLinkManager() const +{ + return m_pLinkMgr; +} + +void SvBaseLink::SetLinkManager( LinkManager* _pMgr ) +{ + m_pLinkMgr = _pMgr; +} + +void SvBaseLink::Disconnect() +{ + if( xObj.is() ) + { + xObj->RemoveAllDataAdvise( this ); + xObj->RemoveConnectAdvise( this ); + xObj.clear(); + } +} + +SvBaseLink::UpdateResult SvBaseLink::DataChanged( const OUString &, const css::uno::Any & ) +{ + if ( mnObjType == SvBaseLinkObjectType::DdeExternal ) + { + if( pImplData->DDEType.pItem ) + pImplData->DDEType.pItem->Notify(); + } + return SUCCESS; +} + +void SvBaseLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& rEndEditHdl ) +{ + m_pParentWin = pParent; + m_aEndEditLink = rEndEditHdl; + m_bIsConnect = xObj.is(); + if( !m_bIsConnect ) + GetRealObject_( xObj.is() ); + + bool bAsync = false; + Link<const OUString&, void> aLink = LINK( this, SvBaseLink, EndEditHdl ); + + if( isClientType(mnObjType) && pImplData->ClientType.bIntrnlLnk ) + { + if( m_pLinkMgr ) + { + SvLinkSourceRef ref = sfx2::LinkManager::CreateObj( this ); + if( ref.is() ) + { + ref->Edit( pParent, this, aLink ); + bAsync = true; + } + } + } + else + { + xObj->Edit( pParent, this, aLink ); + bAsync = true; + } + + if ( !bAsync ) + { + ExecuteEdit( OUString() ); + bWasLastEditOK = false; + m_aEndEditLink.Call( *this ); + } +} + +bool SvBaseLink::ExecuteEdit( const OUString& _rNewName ) +{ + if( !_rNewName.isEmpty() ) + { + SetLinkSourceName( _rNewName ); + if( !Update() ) + { + OUString sApp, sTopic, sItem, sError; + sfx2::LinkManager::GetDisplayNames( this, &sApp, &sTopic, &sItem ); + if( mnObjType == SvBaseLinkObjectType::ClientDde ) + { + sError = SfxResId(STR_DDE_ERROR); + + sal_Int32 nFndPos = sError.indexOf( "%1" ); + if( -1 != nFndPos ) + { + sError = sError.replaceAt( nFndPos, 2, sApp ); + nFndPos = nFndPos + sApp.getLength(); + + if( -1 != ( nFndPos = sError.indexOf( "%2", nFndPos ))) + { + sError = sError.replaceAt( nFndPos, 2, sTopic ); + nFndPos = nFndPos + sTopic.getLength(); + + if( -1 != ( nFndPos = sError.indexOf( "%3", nFndPos ))) + sError = sError.replaceAt( nFndPos, 2, sItem ); + } + } + } + else + return false; + + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pParentWin, + VclMessageType::Warning, VclButtonsType::Ok, sError)); + xBox->run(); + } + } + else if( !m_bIsConnect ) + Disconnect(); + m_bIsConnect = false; + return true; +} + +void SvBaseLink::Closed() +{ + if( xObj.is() ) + xObj->RemoveAllDataAdvise( this ); +} + +FileDialogHelper & SvBaseLink::GetInsertFileDialog(const OUString& rFactory) +{ + m_pFileDlg.reset( new FileDialogHelper( + ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::Insert, rFactory, SfxFilterFlags::NONE, SfxFilterFlags::NONE, m_pParentWin) ); + return *m_pFileDlg; +} + +ImplDdeItem::~ImplDdeItem() +{ + bIsInDTOR = true; + // So that no-one gets the idea to delete the pointer when Disconnecting! + tools::SvRef<SvBaseLink> aRef( pLink ); + aRef->Disconnect(); +} + +DdeData* ImplDdeItem::Get( SotClipboardFormatId nFormat ) +{ + if( pLink->GetObj() ) + { + // is it still valid? + if( bIsValidData && nFormat == aData.GetFormat() ) + return &aData; + + Any aValue; + OUString sMimeType( SotExchange::GetFormatMimeType( nFormat )); + if( pLink->GetObj()->GetData( aValue, sMimeType ) ) + { + if( aValue >>= aSeq ) + { + aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat ); + + bIsValidData = true; + return &aData; + } + } + } + aSeq.realloc( 0 ); + bIsValidData = false; + return nullptr; +} + + +bool ImplDdeItem::Put( const DdeData* ) +{ + OSL_FAIL( "ImplDdeItem::Put not implemented" ); + return false; +} + + +void ImplDdeItem::AdviseLoop( bool bOpen ) +{ + // Connection is closed, so also unsubscribe link + if( !pLink->GetObj() ) + return; + + if( bOpen ) + { + // A connection is re-established + if( SvBaseLinkObjectType::DdeExternal == pLink->GetObjType() ) + { + pLink->GetObj()->AddDataAdvise( pLink, "text/plain;charset=utf-16", ADVISEMODE_NODATA ); + pLink->GetObj()->AddConnectAdvise( pLink ); + } + } + else + { + // So that no-one gets the idea to delete the pointer + // when Disconnecting! + tools::SvRef<SvBaseLink> aRef( pLink ); + aRef->Disconnect(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/macroloader.cxx b/sfx2/source/appl/macroloader.cxx new file mode 100644 index 0000000000..da9f83a2b7 --- /dev/null +++ b/sfx2/source/appl/macroloader.cxx @@ -0,0 +1,344 @@ +/* -*- 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_features.h> + +#include <macroloader.hxx> + +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <basic/basmgr.hxx> +#include <basic/sbuno.hxx> +#include <basic/sberrors.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakref.hxx> +#include <framework/documentundoguard.hxx> +#include <sfx2/app.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/objsh.hxx> +#include <tools/urlobj.hxx> +#include <vcl/svapp.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; + +SfxMacroLoader::SfxMacroLoader(const css::uno::Sequence< css::uno::Any >& aArguments) +{ + Reference < XFrame > xFrame; + if ( aArguments.hasElements() ) + { + aArguments[0] >>= xFrame; + m_xFrame = xFrame; + } +} + +OUString SAL_CALL SfxMacroLoader::getImplementationName() +{ + return "com.sun.star.comp.sfx2.SfxMacroLoader"; +} + +sal_Bool SAL_CALL SfxMacroLoader::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL SfxMacroLoader::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ProtocolHandler" }; +} + +SfxObjectShell* SfxMacroLoader::GetObjectShell(const Reference <XFrame>& xFrame) +{ + SfxObjectShell* pDocShell = nullptr; + + if ( xFrame.is() ) + { + SfxFrame* pFrame=nullptr; + for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) ) + { + if ( pFrame->GetFrameInterface() == xFrame ) + break; + } + + if ( pFrame ) + pDocShell = pFrame->GetCurrentDocument(); + } + + return pDocShell; +} + +SfxObjectShell* SfxMacroLoader::GetObjectShell_Impl() +{ + Reference < XFrame > xFrame( m_xFrame.get(), UNO_QUERY ); + return SfxMacroLoader::GetObjectShell(xFrame); +} + +uno::Reference<frame::XDispatch> SAL_CALL SfxMacroLoader::queryDispatch( + const util::URL& aURL , + const OUString& /*sTargetFrameName*/, + sal_Int32 /*nSearchFlags*/ ) +{ + uno::Reference<frame::XDispatch> xDispatcher; + if(aURL.Complete.startsWith("macro:")) + xDispatcher = this; + return xDispatcher; +} + + +uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL + SfxMacroLoader::queryDispatches( const uno::Sequence < frame::DispatchDescriptor >& seqDescriptor ) +{ + sal_Int32 nCount = seqDescriptor.getLength(); + uno::Sequence< uno::Reference<frame::XDispatch> > lDispatcher(nCount); + std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(), + [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return lDispatcher; +} + + +void SAL_CALL SfxMacroLoader::dispatchWithNotification( + const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/, + const uno::Reference<frame::XDispatchResultListener>& xListener ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + ErrCode nErr = loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() ); + if( !xListener.is() ) + return; + + // always call dispatchFinished(), because we didn't load a document but + // executed a macro instead! + frame::DispatchResultEvent aEvent; + + aEvent.Source = getXWeak(); + if( nErr == ERRCODE_NONE ) + aEvent.State = frame::DispatchResultState::SUCCESS; + else + aEvent.State = frame::DispatchResultState::FAILURE; + + xListener->dispatchFinished( aEvent ) ; +} + +uno::Any SAL_CALL SfxMacroLoader::dispatchWithReturnValue( + const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& ) +{ + uno::Any aRet; + ErrCode nErr = loadMacro( aURL.Complete, aRet, GetObjectShell_Impl() ); + + // aRet gets set to a different value only if nErr == ERRCODE_NONE + // Return it in such case to preserve the original behaviour + + // In all other cases (nErr != ERRCODE_NONE), the calling code gets + // the actual error code back + if ( nErr != ERRCODE_NONE ) + { + beans::PropertyValue aErrorCode; + + aErrorCode.Name = "ErrorCode"; + aErrorCode.Value <<= sal_uInt32(nErr); + + aRet <<= aErrorCode; + } + + return aRet; +} + +void SAL_CALL SfxMacroLoader::dispatch( + const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/ ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() ); +} + +void SAL_CALL SfxMacroLoader::addStatusListener( + const uno::Reference< frame::XStatusListener >& , + const util::URL& ) +{ + /* TODO + How we can handle different listener for further coming or currently running dispatch() jobs + without any inconsistency! + */ +} + + +void SAL_CALL SfxMacroLoader::removeStatusListener( + const uno::Reference< frame::XStatusListener >&, + const util::URL& ) +{ +} + +ErrCode SfxMacroLoader::loadMacro( const OUString& rURL, css::uno::Any& rRetval, SfxObjectShell* pSh ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) rURL; + (void) rRetval; + (void) pSh; + return ERRCODE_BASIC_PROC_UNDEFINED; +#else + SfxObjectShell* pCurrent = pSh; + if ( !pCurrent ) + // all not full qualified names use the BASIC of the given or current document + pCurrent = SfxObjectShell::Current(); + + // 'macro:///lib.mod.proc(args)' => macro of App-BASIC + // 'macro://[docname|.]/lib.mod.proc(args)' => macro of current or qualified document + // 'macro://obj.method(args)' => direct API call, execute it via App-BASIC + const OUString& aMacro( rURL ); + sal_Int32 nThirdSlashPos = aMacro.indexOf( '/', 8 ); + sal_Int32 nArgsPos = aMacro.indexOf( '(' ); + BasicManager *pAppMgr = SfxApplication::GetBasicManager(); + BasicManager *pBasMgr = nullptr; + ErrCode nErr = ERRCODE_NONE; + + // should a macro function be executed ( no direct API call)? + if ( -1 != nThirdSlashPos && ( -1 == nArgsPos || nThirdSlashPos < nArgsPos ) ) + { + // find BasicManager + SfxObjectShell* pDoc = nullptr; + OUString aBasMgrName( INetURLObject::decode(aMacro.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset) ); + if ( aBasMgrName.isEmpty() ) + pBasMgr = pAppMgr; + else if ( aBasMgrName == "." ) + { + // current/actual document + pDoc = pCurrent; + if (pDoc) + pBasMgr = pDoc->GetBasicManager(); + } + else + { + // full qualified name, find document by name + for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst(); + pObjSh && !pBasMgr; + pObjSh = SfxObjectShell::GetNext(*pObjSh) ) + if ( aBasMgrName == pObjSh->GetTitle(SFX_TITLE_APINAME) ) + { + pDoc = pObjSh; + pBasMgr = pDoc->GetBasicManager(); + } + } + + if ( pBasMgr ) + { + const bool bIsAppBasic = ( pBasMgr == pAppMgr ); + const bool bIsDocBasic = ( pBasMgr != pAppMgr ); + + if ( pDoc ) + { + // security check for macros from document basic if an SFX doc is given + if ( !pDoc->AdjustMacroMode() ) + // check forbids execution + return ERRCODE_IO_ACCESSDENIED; + } + + // find BASIC method + OUString aQualifiedMethod( INetURLObject::decode(aMacro.subView( nThirdSlashPos+1 ), INetURLObject::DecodeMechanism::WithCharset) ); + OUString aArgs; + if ( -1 != nArgsPos ) + { + // remove arguments from macro name + aArgs = aQualifiedMethod.copy( nArgsPos - nThirdSlashPos - 1 ); + aQualifiedMethod = aQualifiedMethod.copy( 0, nArgsPos - nThirdSlashPos - 1 ); + } + + if ( pBasMgr->HasMacro( aQualifiedMethod ) ) + { + Any aOldThisComponent; + const bool bSetDocMacroMode = ( pDoc != nullptr ) && bIsDocBasic; + const bool bSetGlobalThisComponent = ( pDoc != nullptr ) && bIsAppBasic; + if ( bSetDocMacroMode ) + { + // mark document: it executes an own macro, so it's in a modal mode + pDoc->SetMacroMode_Impl(); + } + + if ( bSetGlobalThisComponent ) + { + // document is executed via AppBASIC, adjust ThisComponent variable + pAppMgr->SetGlobalUNOConstant( "ThisComponent", Any( pDoc->GetModel() ), &aOldThisComponent ); + } + + // just to let the shell be alive + SfxObjectShellRef xKeepDocAlive = pDoc; + + { + // attempt to protect the document against the script tampering with its Undo Context + std::optional< ::framework::DocumentUndoGuard > pUndoGuard; + if ( bIsDocBasic ) + pUndoGuard.emplace( pDoc->GetModel() ); + + // execute the method + SbxVariableRef retValRef = new SbxVariable; + nErr = pBasMgr->ExecuteMacro( aQualifiedMethod, aArgs, retValRef.get() ); + if ( nErr == ERRCODE_NONE ) + rRetval = sbxToUnoValue( retValRef.get() ); + } + + if ( bSetGlobalThisComponent ) + { + pAppMgr->SetGlobalUNOConstant( "ThisComponent", aOldThisComponent ); + } + + if ( bSetDocMacroMode ) + { + // remove flag for modal mode + pDoc->SetMacroMode_Impl( false ); + } + } + else + nErr = ERRCODE_BASIC_PROC_UNDEFINED; + } + else + nErr = ERRCODE_IO_NOTEXISTS; + } + else + { + // direct API call on a specified object + OUString aCall = + "[" + + INetURLObject::decode(aMacro.subView(6), + INetURLObject::DecodeMechanism::WithCharset) + + "]"; + pAppMgr->GetLib(0)->Execute(aCall); + nErr = SbxBase::GetError(); + } + + SbxBase::ResetError(); + return nErr; +#endif +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &arguments) +{ + return cppu::acquire(new SfxMacroLoader(arguments)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/module.cxx b/sfx2/source/appl/module.cxx new file mode 100644 index 0000000000..03f4fc2fa8 --- /dev/null +++ b/sfx2/source/appl/module.cxx @@ -0,0 +1,267 @@ +/* -*- 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 <sfx2/module.hxx> +#include <sfx2/app.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/tbxctrl.hxx> +#include <sfx2/stbitem.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/tabdlg.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/intitem.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/resmgr.hxx> +#include <sal/log.hxx> + +#define ShellClass_SfxModule +#include <sfxslots.hxx> +#include <optional> + +class SfxModule_Impl +{ +public: + + std::optional<SfxSlotPool> pSlotPool; + std::vector<SfxTbxCtrlFactory> maTbxCtrlFactories; + std::vector<SfxStbCtrlFactory> maStbCtrlFactories; + std::vector<SfxChildWinFactory> maFactories; + OString maResName; + + SfxModule_Impl(); + ~SfxModule_Impl(); +}; + +SfxModule_Impl::SfxModule_Impl() +{ +} + +SfxModule_Impl::~SfxModule_Impl() +{ + pSlotPool.reset(); + maTbxCtrlFactories.clear(); + maStbCtrlFactories.clear(); +} + +SFX_IMPL_SUPERCLASS_INTERFACE(SfxModule, SfxShell) + +SfxModule::SfxModule(const OString& rResName, std::initializer_list<SfxObjectFactory*> pFactoryList) + : pImpl(nullptr) +{ + Construct_Impl(rResName); + for (auto pFactory : pFactoryList) + { + if (pFactory) + pFactory->SetModule_Impl( this ); + } +} + +void SfxModule::Construct_Impl(const OString& rResName) +{ + SfxApplication *pApp = SfxApplication::GetOrCreate(); + pImpl = new SfxModule_Impl; + pImpl->pSlotPool.emplace(&pApp->GetAppSlotPool_Impl()); + pImpl->maResName = rResName; + + SetPool( &pApp->GetPool() ); +} + +SfxModule::~SfxModule() +{ + //TODO how to silence useuniqueptr + if (true) + { + delete pImpl; + } +} + +std::locale SfxModule::GetResLocale() const +{ + return Translate::Create(pImpl->maResName); +} + +SfxSlotPool* SfxModule::GetSlotPool() const +{ + return &*pImpl->pSlotPool; +} + + +void SfxModule::RegisterChildWindow(const SfxChildWinFactory& rFact) +{ + DBG_ASSERT( pImpl, "No real Module!" ); + + for (size_t nFactory=0; nFactory<pImpl->maFactories.size(); ++nFactory) + { + if (rFact.nId == pImpl->maFactories[nFactory].nId) + { + pImpl->maFactories.erase( pImpl->maFactories.begin() + nFactory ); + SAL_WARN("sfx.appl", "ChildWindow registered multiple times!"); + return; + } + } + + pImpl->maFactories.push_back( rFact ); +} + + +void SfxModule::RegisterToolBoxControl( const SfxTbxCtrlFactory& rFact ) +{ +#ifdef DBG_UTIL + for ( size_t n=0; n<pImpl->maTbxCtrlFactories.size(); n++ ) + { + SfxTbxCtrlFactory *pF = &pImpl->maTbxCtrlFactories[n]; + if ( pF->nTypeId == rFact.nTypeId && + (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) ) + { + SAL_INFO("sfx.appl", "TbxController-Registering is not clearly defined!"); + } + } +#endif + + pImpl->maTbxCtrlFactories.push_back( rFact ); +} + + +void SfxModule::RegisterStatusBarControl( const SfxStbCtrlFactory& rFact ) +{ +#ifdef DBG_UTIL + for ( size_t n=0; n<pImpl->maStbCtrlFactories.size(); n++ ) + { + SfxStbCtrlFactory *pF = &pImpl->maStbCtrlFactories[n]; + if ( pF->nTypeId == rFact.nTypeId && + (pF->nSlotId == rFact.nSlotId || pF->nSlotId == 0) ) + { + SAL_INFO("sfx.appl", "TbxController-Registering is not clearly defined!"); + } + } +#endif + + pImpl->maStbCtrlFactories.push_back( rFact ); +} + + +SfxTbxCtrlFactory* SfxModule::GetTbxCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const +{ + // search for a factory with the given slot id + for (auto& rFactory : pImpl->maTbxCtrlFactories) + if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == nSlotID ) + return &rFactory; + + // if no factory exists for the given slot id, see if we + // have a generic factory with the correct slot type and slot id == 0 + for (auto& rFactory : pImpl->maTbxCtrlFactories) + if( rFactory.nTypeId == rSlotType && rFactory.nSlotId == 0 ) + return &rFactory; + + return nullptr; +} + + +SfxStbCtrlFactory* SfxModule::GetStbCtrlFactory(const std::type_info& rSlotType, sal_uInt16 nSlotID) const +{ + for (auto& rFactory : pImpl->maStbCtrlFactories) + if ( rFactory.nTypeId == rSlotType && + ( rFactory.nSlotId == 0 || rFactory.nSlotId == nSlotID ) ) + return &rFactory; + return nullptr; +} + +SfxChildWinFactory* SfxModule::GetChildWinFactoryById(sal_uInt16 nId) const +{ + for (auto& rFactory : pImpl->maFactories) + if (rFactory.nId == nId) + return &rFactory; + return nullptr; +} + +std::unique_ptr<SfxTabPage> SfxModule::CreateTabPage(sal_uInt16, weld::Container*, weld::DialogController*, const SfxItemSet&) +{ + return nullptr; +} + +void SfxModule::Invalidate( sal_uInt16 nId ) +{ + for( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(); pFrame; pFrame = SfxViewFrame::GetNext( *pFrame ) ) + if ( pFrame->GetObjectShell()->GetModule() == this ) + Invalidate_Impl( pFrame->GetBindings(), nId ); +} + +SfxModule* SfxModule::GetActiveModule( SfxViewFrame* pFrame ) +{ + if ( !pFrame ) + pFrame = SfxViewFrame::Current(); + SfxObjectShell* pSh = nullptr; + if( pFrame ) + pSh = pFrame->GetObjectShell(); + return pSh ? pSh->GetModule() : nullptr; +} + +FieldUnit SfxModule::GetModuleFieldUnit( css::uno::Reference< css::frame::XFrame > const & i_frame ) +{ + ENSURE_OR_RETURN( i_frame.is(), "SfxModule::GetModuleFieldUnit: invalid frame!", FieldUnit::MM_100TH ); + + // find SfxViewFrame for the given XFrame + SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(); + while ( pViewFrame != nullptr ) + { + if ( pViewFrame->GetFrame().GetFrameInterface() == i_frame ) + break; + pViewFrame = SfxViewFrame::GetNext( *pViewFrame ); + } + ENSURE_OR_RETURN( + pViewFrame != nullptr, + "SfxModule::GetModuleFieldUnit: unable to find an SfxViewFrame for the given XFrame", + FieldUnit::MM_100TH); + + // find the module + SfxModule const * pModule = GetActiveModule( pViewFrame ); + ENSURE_OR_RETURN(pModule != nullptr, + "SfxModule::GetModuleFieldUnit: no SfxModule for the given frame!", + FieldUnit::MM_100TH); + return pModule->GetFieldUnit(); +} + +FieldUnit SfxModule::GetCurrentFieldUnit() +{ + FieldUnit eUnit = FieldUnit::INCH; + SfxModule* pModule = GetActiveModule(); + if ( pModule ) + { + const SfxPoolItem* pItem = pModule->GetItem( SID_ATTR_METRIC ); + if ( pItem ) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + } + else + SAL_WARN( "sfx.appl", "GetModuleFieldUnit(): no module found" ); + return eUnit; +} + +FieldUnit SfxModule::GetFieldUnit() const +{ + FieldUnit eUnit = FieldUnit::INCH; + const SfxPoolItem* pItem = GetItem( SID_ATTR_METRIC ); + if ( pItem ) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + return eUnit; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/newhelp.cxx b/sfx2/source/appl/newhelp.cxx new file mode 100644 index 0000000000..7e51602371 --- /dev/null +++ b/sfx2/source/appl/newhelp.cxx @@ -0,0 +1,2666 @@ +/* -*- 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 "newhelp.hxx" +#include <sfx2/sfxresid.hxx> +#include "helpinterceptor.hxx" +#include <helper.hxx> +#include <srchdlg.hxx> +#include <sfx2/sfxhelp.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <o3tl/string_view.hxx> + +#include <sfx2/strings.hrc> +#include <helpids.h> +#include <bitmaps.hlst> + +#include <rtl/ustrbuf.hxx> +#include <comphelper/configurationhelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/Frame.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextCursor.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/XSearchable.hpp> +#include <com/sun/star/util/XSearchDescriptor.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <unotools/historyoptions.hxx> +#include <unotools/viewoptions.hxx> +#include <tools/urlobj.hxx> +#include <svtools/imagemgr.hxx> +#include <svtools/miscopt.hxx> +#include <utility> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/i18nhelp.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/unohelp.hxx> +#include <vcl/weld.hxx> + +#include <ucbhelper/content.hxx> +#include <unotools/ucbhelper.hxx> + +#include <string_view> +#include <unordered_map> +#include <vector> + +using namespace ::ucbhelper; +using namespace ::com::sun::star::ucb; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::ui; + +using namespace ::comphelper; + +// defines --------------------------------------------------------------- + +constexpr OUString CONFIGNAME_HELPWIN = u"OfficeHelp"_ustr; +constexpr OUString CONFIGNAME_INDEXWIN = u"OfficeHelpIndex"_ustr; +constexpr OUString CONFIGNAME_SEARCHPAGE = u"OfficeHelpSearch"_ustr; +constexpr OUString IMAGE_URL = u"private:factory/"_ustr; + +constexpr OUString PROPERTY_KEYWORDLIST = u"KeywordList"_ustr; +constexpr OUString PROPERTY_KEYWORDREF = u"KeywordRef"_ustr; +constexpr OUString PROPERTY_ANCHORREF = u"KeywordAnchorForRef"_ustr; +constexpr OUString PROPERTY_TITLEREF = u"KeywordTitleForRef"_ustr; +constexpr OUString PROPERTY_TITLE = u"Title"_ustr; +constexpr OUString HELP_URL = u"vnd.sun.star.help://"_ustr; +constexpr OUStringLiteral HELP_SEARCH_TAG = u"/?Query="; +constexpr OUString USERITEM_NAME = u"UserItem"_ustr; + +constexpr OUStringLiteral PACKAGE_SETUP = u"/org.openoffice.Setup"; +constexpr OUString PATH_OFFICE_FACTORIES = u"Office/Factories/"_ustr; +constexpr OUString KEY_HELP_ON_OPEN = u"ooSetupFactoryHelpOnOpen"_ustr; +constexpr OUStringLiteral KEY_UI_NAME = u"ooSetupFactoryUIName"; + +namespace sfx2 +{ + + + /** Prepare a search string for searching or selecting. + For searching every search word needs the postfix '*' and the delimiter ' ' if necessary. + For selecting the delimiter '|' is required to search with regular expressions. + Samples: + search string | output for searching | output for selecting + ----------------------------------------------------------- + "text" | "text*" | "text" + "text*" | "text*" | "text" + "text menu" | "text* menu*" | "text|menu" + */ + static OUString PrepareSearchString( const OUString& rSearchString, + const Reference< XBreakIterator >& xBreak, bool bForSearch ) + { + OUStringBuffer sSearchStr; + sal_Int32 nStartPos = 0; + const lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale(); + Boundary aBoundary = xBreak->getWordBoundary( + rSearchString, nStartPos, aLocale, WordType::ANYWORD_IGNOREWHITESPACES, true ); + + while ( aBoundary.startPos < aBoundary.endPos ) + { + nStartPos = aBoundary.endPos; + OUString sSearchToken( rSearchString.copy( + static_cast<sal_uInt16>(aBoundary.startPos), static_cast<sal_uInt16>(aBoundary.endPos) - static_cast<sal_uInt16>(aBoundary.startPos) ) ); + if ( !sSearchToken.isEmpty() && ( sSearchToken.getLength() > 1 || sSearchToken[0] != '.' ) ) + { + if ( bForSearch && sSearchToken[ sSearchToken.getLength() - 1 ] != '*' ) + sSearchToken += "*"; + + if ( sSearchToken.getLength() > 1 || + ( sSearchToken.getLength() > 0 && sSearchToken[ 0 ] != '*' ) ) + { + if ( !sSearchStr.isEmpty() ) + { + if ( bForSearch ) + sSearchStr.append(" "); + else + sSearchStr.append("|"); + } + sSearchStr.append(sSearchToken); + } + } + aBoundary = xBreak->nextWord( rSearchString, nStartPos, + aLocale, WordType::ANYWORD_IGNOREWHITESPACES ); + } + + return sSearchStr.makeStringAndClear(); + } + +// namespace sfx2 +} + + +// struct IndexEntry_Impl ------------------------------------------------ + +namespace { + +struct IndexEntry_Impl +{ + bool m_bSubEntry; + OUString m_aURL; + + IndexEntry_Impl( OUString aURL, bool bSubEntry ) : + m_bSubEntry( bSubEntry ), m_aURL(std::move( aURL )) {} +}; + +// struct ContentEntry_Impl ---------------------------------------------- + +struct ContentEntry_Impl +{ + OUString aURL; + bool bIsFolder; + + ContentEntry_Impl( OUString _aURL, bool bFolder ) : + aURL(std::move( _aURL )), bIsFolder( bFolder ) {} +}; + +} + +void ContentTabPage_Impl::InitRoot() +{ + std::vector< OUString > aList = + SfxContentHelper::GetHelpTreeViewContents( "vnd.sun.star.hier://com.sun.star.help.TreeView/" ); + + for (const OUString & aRow : aList) + { + sal_Int32 nIdx = 0; + OUString aTitle = aRow.getToken( 0, '\t', nIdx ); + OUString aURL = aRow.getToken( 0, '\t', nIdx ); + sal_Unicode cFolder = o3tl::getToken(aRow, 0, '\t', nIdx )[0]; + bool bIsFolder = ( '1' == cFolder ); + OUString sId; + if (bIsFolder) + sId = weld::toId(new ContentEntry_Impl(aURL, true)); + m_xContentBox->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get()); + m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage); + } +} + +void ContentTabPage_Impl::ClearChildren(const weld::TreeIter* pParent) +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator(pParent); + bool bEntry = m_xContentBox->iter_children(*xEntry); + while (bEntry) + { + ClearChildren(xEntry.get()); + delete weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry)); + bEntry = m_xContentBox->iter_next_sibling(*xEntry); + } + +} + +IMPL_LINK(ContentTabPage_Impl, ExpandingHdl, const weld::TreeIter&, rIter, bool) +{ + ContentEntry_Impl* pContentEntry = weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(rIter)); + if (!m_xContentBox->iter_has_child(rIter)) + { + try + { + if (pContentEntry) + { + std::vector<OUString > aList = SfxContentHelper::GetHelpTreeViewContents(pContentEntry->aURL); + + for (const OUString & aRow : aList) + { + sal_Int32 nIdx = 0; + OUString aTitle = aRow.getToken( 0, '\t', nIdx ); + OUString aURL = aRow.getToken( 0, '\t', nIdx ); + sal_Unicode cFolder = o3tl::getToken(aRow, 0, '\t', nIdx )[0]; + bool bIsFolder = ( '1' == cFolder ); + if ( bIsFolder ) + { + OUString sId = weld::toId(new ContentEntry_Impl(aURL, true)); + m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, true, m_xScratchIter.get()); + m_xContentBox->set_image(*m_xScratchIter, aClosedBookImage); + } + else + { + Any aAny( ::utl::UCBContentHelper::GetProperty( aURL, "TargetURL" ) ); + OUString sId; + OUString aTargetURL; + if ( aAny >>= aTargetURL ) + sId = weld::toId(new ContentEntry_Impl(aTargetURL, false)); + m_xContentBox->insert(&rIter, -1, &aTitle, &sId, nullptr, nullptr, false, m_xScratchIter.get()); + m_xContentBox->set_image(*m_xScratchIter, aDocumentImage); + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "ContentListBox_Impl::RequestingChildren(): unexpected exception" ); + } + } + + if (!pContentEntry || pContentEntry->bIsFolder) + m_xContentBox->set_image(rIter, aOpenBookImage); + + return true; +} + +IMPL_LINK(ContentTabPage_Impl, CollapsingHdl, const weld::TreeIter&, rIter, bool) +{ + ContentEntry_Impl* pContentEntry = weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(rIter)); + if (!pContentEntry || pContentEntry->bIsFolder) + m_xContentBox->set_image(rIter, aClosedBookImage); + + return true; +} + +OUString ContentTabPage_Impl::GetSelectedEntry() const +{ + OUString aRet; + ContentEntry_Impl* pEntry = weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_selected_id()); + if (pEntry && !pEntry->bIsFolder) + aRet = pEntry->aURL; + return aRet; +} + +// class HelpTabPage_Impl ------------------------------------------------ +HelpTabPage_Impl::HelpTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin, + const OUString& rID, const OUString& rUIXMLDescription) + : BuilderPage(pParent, nullptr, rUIXMLDescription, rID) + , m_pIdxWin(pIdxWin) +{ +} + +HelpTabPage_Impl::~HelpTabPage_Impl() +{ +} + +// class ContentTabPage_Impl --------------------------------------------- +ContentTabPage_Impl::ContentTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin) + : HelpTabPage_Impl(pParent, pIdxWin, "HelpContentPage", + "sfx/ui/helpcontentpage.ui") + , m_xContentBox(m_xBuilder->weld_tree_view("content")) + , m_xScratchIter(m_xContentBox->make_iterator()) + , aOpenBookImage(BMP_HELP_CONTENT_BOOK_OPEN) + , aClosedBookImage(BMP_HELP_CONTENT_BOOK_CLOSED) + , aDocumentImage(BMP_HELP_CONTENT_DOC) +{ + m_xContentBox->set_size_request(m_xContentBox->get_approximate_digit_width() * 30, + m_xContentBox->get_height_rows(20)); + m_xContentBox->connect_row_activated(LINK(this, ContentTabPage_Impl, DoubleClickHdl)); + m_xContentBox->connect_expanding(LINK(this, ContentTabPage_Impl, ExpandingHdl)); + m_xContentBox->connect_collapsing(LINK(this, ContentTabPage_Impl, CollapsingHdl)); + + InitRoot(); +} + +IMPL_LINK_NOARG(ContentTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return false; +} + +void ContentTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink) +{ + aDoubleClickHdl = rLink; +} + +ContentTabPage_Impl::~ContentTabPage_Impl() +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xContentBox->make_iterator(); + bool bEntry = m_xContentBox->get_iter_first(*xEntry); + while (bEntry) + { + ClearChildren(xEntry.get()); + delete weld::fromId<ContentEntry_Impl*>(m_xContentBox->get_id(*xEntry)); + bEntry = m_xContentBox->iter_next_sibling(*xEntry); + } +} + +void IndexTabPage_Impl::SelectExecutableEntry() +{ + sal_Int32 nPos = m_xIndexList->find_text(m_xIndexEntry->get_text()); + if (nPos == -1) + return; + + sal_Int32 nOldPos = nPos; + OUString aEntryText; + IndexEntry_Impl* pEntry = weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(nPos)); + sal_Int32 nCount = m_xIndexList->n_children(); + while ( nPos < nCount && ( !pEntry || pEntry->m_aURL.isEmpty() ) ) + { + pEntry = weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(++nPos)); + aEntryText = m_xIndexList->get_text(nPos); + } + + if ( nOldPos != nPos ) + m_xIndexEntry->set_text(aEntryText); +} + +// class IndexTabPage_Impl ----------------------------------------------- +IndexTabPage_Impl::IndexTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin) + : HelpTabPage_Impl(pParent, pIdxWin, "HelpIndexPage", "sfx/ui/helpindexpage.ui") + , m_xIndexEntry(m_xBuilder->weld_entry("termentry")) + , m_xIndexList(m_xBuilder->weld_tree_view("termlist")) + , m_xOpenBtn(m_xBuilder->weld_button("display")) + , aFactoryIdle("sfx2 appl IndexTabPage_Impl Factory") + , aAutoCompleteIdle("sfx2 appl IndexTabPage_Impl AutoComplete") + , aKeywordTimer("sfx2::IndexTabPage_Impl aKeywordTimer") + , bIsActivated(false) + , nRowHeight(m_xIndexList->get_height_rows(1)) + , nAllHeight(0) + , nLastCharCode(0) +{ + m_xIndexList->set_size_request(m_xIndexList->get_approximate_digit_width() * 30, -1); + + m_xOpenBtn->connect_clicked(LINK(this, IndexTabPage_Impl, OpenHdl)); + aFactoryIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, IdleHdl )); + aAutoCompleteIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, AutoCompleteHdl )); + aKeywordTimer.SetInvokeHandler( LINK( this, IndexTabPage_Impl, TimeoutHdl ) ); + m_xIndexList->connect_row_activated(LINK(this, IndexTabPage_Impl, DoubleClickHdl)); + m_xIndexList->connect_changed(LINK(this, IndexTabPage_Impl, TreeChangeHdl)); + m_xIndexList->connect_custom_get_size(LINK(this, IndexTabPage_Impl, CustomGetSizeHdl)); + m_xIndexList->connect_custom_render(LINK(this, IndexTabPage_Impl, CustomRenderHdl)); + m_xIndexList->set_column_custom_renderer(0, true); + m_xIndexList->connect_size_allocate(LINK(this, IndexTabPage_Impl, ResizeHdl)); + m_xIndexEntry->connect_key_press(LINK(this, IndexTabPage_Impl, KeyInputHdl)); + m_xIndexEntry->connect_changed(LINK(this, IndexTabPage_Impl, EntryChangeHdl)); + m_xIndexEntry->connect_activate(LINK(this, IndexTabPage_Impl, ActivateHdl)); +} + +IMPL_LINK(IndexTabPage_Impl, ResizeHdl, const Size&, rSize, void) +{ + nAllHeight = rSize.Height(); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, Size) +{ + return Size(m_xIndexList->get_size_request().Width(), nRowHeight); +} + +IMPL_LINK(IndexTabPage_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void) +{ + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + rRenderContext.Push(vcl::PushFlags::TEXTCOLOR); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + + Point aPos(rRect.TopLeft()); + aPos.AdjustY((rRect.GetHeight() - rRenderContext.GetTextHeight()) / 2); + + int nIndex = m_xIndexList->find_id(rId); + OUString aEntry(m_xIndexList->get_text(nIndex)); + + IndexEntry_Impl* pEntry = weld::fromId<IndexEntry_Impl*>(rId); + if (pEntry && pEntry->m_bSubEntry) + { + // indent sub entries + aPos.AdjustX(8); + sal_Int32 nPos = aEntry.indexOf(';'); + rRenderContext.DrawText(aPos, (nPos !=-1) ? aEntry.copy(nPos + 1) : aEntry); + } + else + rRenderContext.DrawText(aPos, aEntry); + + rRenderContext.Pop(); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, TreeChangeHdl, weld::TreeView&, void) +{ + m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, EntryChangeHdl, weld::Entry&, void) +{ + switch (nLastCharCode) + { + case css::awt::Key::DELETE_WORD_BACKWARD: + case css::awt::Key::DELETE_WORD_FORWARD: + case css::awt::Key::DELETE_TO_BEGIN_OF_LINE: + case css::awt::Key::DELETE_TO_END_OF_LINE: + case KEY_BACKSPACE: + case KEY_DELETE: + aAutoCompleteIdle.Stop(); + break; + default: + aAutoCompleteIdle.Start(); + break; + } +} + +IMPL_LINK(IndexTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const vcl::KeyCode& rKCode = rKEvt.GetKeyCode(); + if (rKCode.GetModifier()) // only with no modifiers held + return false; + + sal_uInt16 nCode = rKCode.GetCode(); + + if (nCode == KEY_UP || nCode == KEY_PAGEUP || + nCode == KEY_DOWN || nCode == KEY_PAGEDOWN) + { +// disable_notify_events(); + sal_Int32 nIndex = m_xIndexList->get_selected_index(); + sal_Int32 nOrigIndex = nIndex; + sal_Int32 nCount = m_xIndexList->n_children(); + if (nIndex == -1) + { + m_xIndexList->set_cursor(0); + m_xIndexList->select(0); + m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); + } + else + { + if (nCode == KEY_UP) + --nIndex; + else if (nCode == KEY_DOWN) + ++nIndex; + else if (nCode == KEY_PAGEUP) + { + int nVisRows = nAllHeight / nRowHeight; + nIndex -= nVisRows; + } + else if (nCode == KEY_PAGEDOWN) + { + int nVisRows = nAllHeight / nRowHeight; + nIndex += nVisRows; + } + + if (nIndex < 0) + nIndex = 0; + if (nIndex >= nCount) + nIndex = nCount - 1; + + if (nIndex != nOrigIndex) + { + m_xIndexList->set_cursor(nIndex); + m_xIndexList->select(nIndex); + m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); + } + +// m_xIndexList->grab_focus(); +// g_signal_emit_by_name(pWidget, "key-press-event", pEvent, &ret); +// m_xIndexEntry->set_text(m_xIndexList->get_selected_text()); +// m_xIndexEntry->grab_focus(); + } + m_xIndexEntry->select_region(0, -1); +// enable_notify_events(); +// m_bTreeChange = true; +// m_pEntry->fire_signal_changed(); +// m_bTreeChange = false; + return true; + } + + nLastCharCode = nCode; + return false; +} + +IndexTabPage_Impl::~IndexTabPage_Impl() +{ + ClearIndex(); +} + +namespace sfx2 { + + typedef std::unordered_map< OUString, int > KeywordInfo; +} + +void IndexTabPage_Impl::InitializeIndex() +{ + weld::WaitObject aWaitCursor(m_pIdxWin->GetFrameWeld()); + + // By now more than 256 equal entries are not allowed + sal_Unicode append[256]; + for(sal_Unicode & k : append) + k = ' '; + + sfx2::KeywordInfo aInfo; + m_xIndexList->freeze(); + + try + { + OUStringBuffer aURL(HELP_URL + sFactory); + AppendConfigToken(aURL, true); + + Content aCnt( aURL.makeStringAndClear(), Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties(); + if ( xInfo->hasPropertyByName( PROPERTY_ANCHORREF ) ) + { + css::uno::Sequence< OUString > aPropSeq{ PROPERTY_KEYWORDLIST, PROPERTY_KEYWORDREF, + PROPERTY_ANCHORREF, PROPERTY_TITLEREF }; + + // abi: use one possibly remote call only + css::uno::Sequence< css::uno::Any > aAnySeq = + aCnt.getPropertyValues( aPropSeq ); + + css::uno::Sequence< OUString > aKeywordList; + css::uno::Sequence< css::uno::Sequence< OUString > > aKeywordRefList; + css::uno::Sequence< css::uno::Sequence< OUString > > aAnchorRefList; + css::uno::Sequence< css::uno::Sequence< OUString > > aTitleRefList; + + if ( ( aAnySeq[0] >>= aKeywordList ) && ( aAnySeq[1] >>= aKeywordRefList ) && + ( aAnySeq[2] >>= aAnchorRefList ) && ( aAnySeq[3] >>= aTitleRefList ) ) + { + int ndx,tmp; + OUString aIndex, aTempString; + sfx2::KeywordInfo::iterator it; + + for ( int i = 0; i < aKeywordList.getLength(); ++i ) + { + // abi: Do not copy, but use references + const OUString& aKeywordPair = aKeywordList[i]; + DBG_ASSERT( !aKeywordPair.isEmpty(), "invalid help index" ); + const css::uno::Sequence< OUString >& aRefList = aKeywordRefList[i]; + const css::uno::Sequence< OUString >& aAnchorList = aAnchorRefList[i]; + const css::uno::Sequence< OUString >& aTitleList = aTitleRefList[i]; + + DBG_ASSERT( aRefList.getLength() == aAnchorList.getLength(),"reference list and title list of different length" ); + + ndx = aKeywordPair.indexOf( ';' ); + const bool insert = ndx != -1; + + OUString sId; + + if ( insert ) + { + aTempString = aKeywordPair.copy( 0, ndx ); + if ( aIndex != aTempString ) + { + aIndex = aTempString; + it = aInfo.emplace(aTempString, 0).first; + sId = weld::toId(new IndexEntry_Impl(OUString(), false)); + if ( (tmp = it->second++) != 0) + m_xIndexList->append( + sId, aTempString + std::u16string_view(append, tmp)); + else + m_xIndexList->append(sId, aTempString); + } + } + else + aIndex.clear(); + + sal_uInt32 nRefListLen = aRefList.getLength(); + + DBG_ASSERT( aAnchorList.hasElements(), "*IndexTabPage_Impl::InitializeIndex(): AnchorList is empty!" ); + DBG_ASSERT( nRefListLen, "*IndexTabPage_Impl::InitializeIndex(): RefList is empty!" ); + + if ( aAnchorList.hasElements() && nRefListLen ) + { + if ( aAnchorList[0].getLength() > 0 ) + { + sId = weld::toId(new IndexEntry_Impl(aRefList[0] + "#" + aAnchorList[0], insert)); + } + else + sId = weld::toId(new IndexEntry_Impl(aRefList[0], insert)); + } + + // Assume the token is trimmed + it = aInfo.emplace(aKeywordPair, 0).first; + if ((tmp = it->second++) != 0) + m_xIndexList->append(sId, aKeywordPair + std::u16string_view(append, tmp)); + else + m_xIndexList->append(sId, aKeywordPair); + + for ( sal_uInt32 j = 1; j < nRefListLen ; ++j ) + { + aTempString = aKeywordPair + " - " + aTitleList[j]; + + if ( aAnchorList[j].getLength() > 0 ) + sId = weld::toId(new IndexEntry_Impl(aRefList[j] + "#" + aAnchorList[j], insert)); + else + sId = weld::toId(new IndexEntry_Impl(aRefList[j], insert)); + + it = aInfo.emplace(aTempString, 0).first; + if ( (tmp = it->second++) != 0 ) + m_xIndexList->append( + sId, aTempString + std::u16string_view(append, tmp)); + else + m_xIndexList->append(sId, aTempString); + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "IndexTabPage_Impl::InitializeIndex(): unexpected exception" ); + } + + m_xIndexList->thaw(); + + if ( !sKeyword.isEmpty() ) + aKeywordLink.Call( *this ); +} + +void IndexTabPage_Impl::ClearIndex() +{ + const sal_Int32 nCount = m_xIndexList->n_children(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + delete weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(i)); + m_xIndexList->clear(); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, OpenHdl, weld::Button&, void) +{ + aDoubleClickHdl.Call(nullptr); +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, ActivateHdl, weld::Entry&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, IdleHdl, Timer*, void) +{ + InitializeIndex(); +} + +int IndexTabPage_Impl::starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive) +{ + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + + int nRet = nStartRow; + int nCount = m_xIndexList->n_children(); + while (nRet < nCount) + { + OUString aStr(m_xIndexList->get_text(nRet)); + const bool bMatch = !bCaseSensitive ? rI18nHelper.MatchString(rStr, aStr) : aStr.startsWith(rStr); + if (bMatch) + return nRet; + ++nRet; + } + + return -1; +} + +IMPL_LINK_NOARG(IndexTabPage_Impl, AutoCompleteHdl, Timer*, void) +{ + OUString aStartText = m_xIndexEntry->get_text(); + int nStartPos, nEndPos; + m_xIndexEntry->get_selection_bounds(nStartPos, nEndPos); + int nMaxSelection = std::max(nStartPos, nEndPos); + if (nMaxSelection != aStartText.getLength()) + return; + + int nActive = m_xIndexList->get_selected_index(); + int nStart = nActive; + + if (nStart == -1) + nStart = 0; + + // Try match case insensitive from current position + int nPos = starts_with(aStartText, nStart, false); + if (nPos == -1 && nStart != 0) + { + // Try match case insensitive, but from start + nPos = starts_with(aStartText, 0, false); + } + + if (nPos == -1) + { + // Try match case sensitive from current position + nPos = starts_with(aStartText, nStart, true); + if (nPos == -1 && nStart != 0) + { + // Try match case sensitive, but from start + nPos = starts_with(aStartText, 0, true); + } + } + + if (nPos != -1) + { + m_xIndexList->set_cursor(nPos); + m_xIndexList->select(nPos); + OUString aText = m_xIndexList->get_text(nPos); + if (aText != aStartText) + m_xIndexEntry->set_text(aText); + m_xIndexEntry->select_region(aText.getLength(), aStartText.getLength()); + } +} + +IMPL_LINK( IndexTabPage_Impl, TimeoutHdl, Timer*, pTimer, void) +{ + if(&aKeywordTimer == pTimer && !sKeyword.isEmpty()) + aKeywordLink.Call(*this); +} + +void IndexTabPage_Impl::Activate() +{ + if ( !bIsActivated ) + { + bIsActivated = true; + aFactoryIdle.Start(); + } +} + +void IndexTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink) +{ + aDoubleClickHdl = rLink; +} + +void IndexTabPage_Impl::SetFactory( const OUString& rFactory ) +{ + OUString sNewFactory( rFactory ); + DBG_ASSERT( !sNewFactory.isEmpty(), "empty factory" ); + bool bValid = m_pIdxWin->IsValidFactory( rFactory ); + + if ( sFactory.isEmpty() && !bValid ) + { + sNewFactory = SfxHelp::GetDefaultHelpModule(); + bValid = true; + } + + if ( sNewFactory != sFactory && bValid ) + { + sFactory = sNewFactory; + ClearIndex(); + if ( bIsActivated ) + aFactoryIdle.Start(); + } +} + +OUString IndexTabPage_Impl::GetSelectedEntry() const +{ + OUString aRet; + IndexEntry_Impl* pEntry = weld::fromId<IndexEntry_Impl*>(m_xIndexList->get_id(m_xIndexList->find_text(m_xIndexEntry->get_text()))); + if (pEntry) + aRet = pEntry->m_aURL; + return aRet; +} + +void IndexTabPage_Impl::SetKeyword( const OUString& rKeyword ) +{ + sKeyword = rKeyword; + + if (m_xIndexList->n_children() > 0) + aKeywordTimer.Start(); + else if ( !bIsActivated ) + aFactoryIdle.Start(); +} + + +bool IndexTabPage_Impl::HasKeyword() const +{ + bool bRet = false; + if ( !sKeyword.isEmpty() ) + { + sal_Int32 nPos = m_xIndexList->find_text( sKeyword ); + bRet = nPos != -1; + } + + return bRet; +} + + +bool IndexTabPage_Impl::HasKeywordIgnoreCase() +{ + bool bRet = false; + if ( !sKeyword.isEmpty() ) + { + sal_Int32 nEntries = m_xIndexList->n_children(); + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper(); + for ( sal_Int32 n = 0; n < nEntries; n++) + { + const OUString sIndexItem {m_xIndexList->get_text(n)}; + if (rI18nHelper.MatchString( sIndexItem, sKeyword )) + { + sKeyword = sIndexItem; + bRet = true; + } + } + } + + return bRet; +} + +void IndexTabPage_Impl::OpenKeyword() +{ + if ( !sKeyword.isEmpty() ) + { + m_xIndexEntry->set_text(sKeyword); + aDoubleClickHdl.Call(nullptr); + sKeyword.clear(); + } +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, ActivateHdl, weld::ComboBox&, bool) +{ + Search(); + return true; +} + +// class SearchTabPage_Impl ---------------------------------------------- + +SearchTabPage_Impl::SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin) + : HelpTabPage_Impl(pParent, pIdxWin, "HelpSearchPage", + "sfx/ui/helpsearchpage.ui") + , m_xSearchED(m_xBuilder->weld_combo_box("search")) + , m_xSearchBtn(m_xBuilder->weld_button("find")) + , m_xFullWordsCB(m_xBuilder->weld_check_button("completewords")) + , m_xScopeCB(m_xBuilder->weld_check_button("headings")) + , m_xResultsLB(m_xBuilder->weld_tree_view("results")) + , m_xOpenBtn(m_xBuilder->weld_button("display")) + , xBreakIterator(vcl::unohelper::CreateBreakIterator()) +{ + m_xResultsLB->set_size_request(m_xResultsLB->get_approximate_digit_width() * 30, + m_xResultsLB->get_height_rows(15)); + + m_xSearchBtn->connect_clicked(LINK(this, SearchTabPage_Impl, ClickHdl)); + m_xSearchED->connect_changed(LINK(this, SearchTabPage_Impl, ModifyHdl)); + m_xSearchED->connect_entry_activate(LINK(this, SearchTabPage_Impl, ActivateHdl)); + m_xOpenBtn->connect_clicked(LINK(this, SearchTabPage_Impl, OpenHdl)); + m_xResultsLB->connect_row_activated(LINK(this, SearchTabPage_Impl, DoubleClickHdl)); + + SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE ); + if ( aViewOpt.Exists() ) + { + OUString aUserData; + Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME ); + if ( aUserItem >>= aUserData ) + { + sal_Int32 nIdx {0}; + bool bChecked = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx)) == 1; + m_xFullWordsCB->set_active(bChecked); + bChecked = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx)) == 1; + m_xScopeCB->set_active(bChecked); + + while ( nIdx > 0 ) + { + m_xSearchED->append_text( INetURLObject::decode( + o3tl::getToken(aUserData, 0, ';', nIdx), + INetURLObject::DecodeMechanism::WithCharset ) ); + } + } + } + + ModifyHdl(*m_xSearchED); +} + +SearchTabPage_Impl::~SearchTabPage_Impl() +{ + SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE ); + OUStringBuffer aUserData = + OUString::number(m_xFullWordsCB->get_active() ? 1 : 0) + + ";" + + OUString::number(m_xScopeCB->get_active() ? 1 : 0); + sal_Int32 nCount = std::min(m_xSearchED->get_count(), 10); // save only 10 entries + + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + aUserData.append(";" + + INetURLObject::encode( + m_xSearchED->get_text(i), + INetURLObject::PART_UNO_PARAM_VALUE, + INetURLObject::EncodeMechanism::All )); + } + + Any aUserItem( aUserData.makeStringAndClear() ); + aViewOpt.SetUserItem( USERITEM_NAME, aUserItem ); + + m_xSearchED.reset(); + m_xSearchBtn.reset(); + m_xFullWordsCB.reset(); + m_xScopeCB.reset(); + m_xResultsLB.reset(); + m_xOpenBtn.reset(); +} + +void SearchTabPage_Impl::ClearSearchResults() +{ + m_xResultsLB->clear(); +} + +void SearchTabPage_Impl::RememberSearchText( const OUString& rSearchText ) +{ + for (sal_Int32 i = 0, nEntryCount = m_xSearchED->get_count(); i < nEntryCount; ++i) + { + if (rSearchText == m_xSearchED->get_text(i)) + { + m_xSearchED->remove(i); + break; + } + } + + m_xSearchED->insert_text(0, rSearchText); +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, ClickHdl, weld::Button&, void) +{ + Search(); +} + +void SearchTabPage_Impl::Search() +{ + OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' '); + if ( aSearchText.isEmpty() ) + return; + + std::unique_ptr<weld::WaitObject> xWaitCursor(new weld::WaitObject(m_pIdxWin->GetFrameWeld())); + ClearSearchResults(); + RememberSearchText( aSearchText ); + OUStringBuffer aSearchURL(HELP_URL + aFactory + HELP_SEARCH_TAG); + if (!m_xFullWordsCB->get_active()) + aSearchText = sfx2::PrepareSearchString( aSearchText, xBreakIterator, true ); + aSearchURL.append(aSearchText); + AppendConfigToken(aSearchURL, false); + if (m_xScopeCB->get_active()) + aSearchURL.append("&Scope=Heading"); + std::vector< OUString > aFactories = SfxContentHelper::GetResultSet(aSearchURL.makeStringAndClear()); + for (const OUString & rRow : aFactories) + { + sal_Int32 nIdx = 0; + OUString aTitle = rRow.getToken(0, '\t', nIdx); + OUString sURL(rRow.getToken(1, '\t', nIdx)); + m_xResultsLB->append(sURL, aTitle); + } + xWaitCursor.reset(); + + if ( aFactories.empty() ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xContainer.get(), + VclMessageType::Info, VclButtonsType::Ok, + SfxResId(STR_INFO_NOSEARCHRESULTS))); + xBox->run(); + } +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, OpenHdl, weld::Button&, void) +{ + aDoubleClickHdl.Call(nullptr); +} + +IMPL_LINK(SearchTabPage_Impl, ModifyHdl, weld::ComboBox&, rComboBox, void) +{ + OUString aSearchText = comphelper::string::strip(m_xSearchED->get_active_text(), ' '); + m_xSearchBtn->set_sensitive(!aSearchText.isEmpty()); + + if (rComboBox.changed_by_direct_pick()) + Search(); +} + +IMPL_LINK_NOARG(SearchTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +void SearchTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink) +{ + aDoubleClickHdl = rLink; +} + +OUString SearchTabPage_Impl::GetSelectedEntry() const +{ + return m_xResultsLB->get_selected_id(); +} + +void SearchTabPage_Impl::ClearPage() +{ + ClearSearchResults(); + m_xSearchED->set_entry_text(OUString()); +} + +bool SearchTabPage_Impl::OpenKeyword( const OUString& rKeyword ) +{ + bool bRet = false; + m_xSearchED->set_entry_text(rKeyword); + Search(); + if (m_xResultsLB->n_children() > 0) + { + // found keyword -> open it + m_xResultsLB->select(0); + OpenHdl(*m_xOpenBtn); + bRet = true; + } + return bRet; +} + +// class BookmarksTabPage_Impl ------------------------------------------- + +void BookmarksTabPage_Impl::DoAction(std::u16string_view rAction) +{ + if (rAction == u"display") + aDoubleClickHdl.Call(nullptr); + else if (rAction == u"rename") + { + sal_Int32 nPos = m_xBookmarksBox->get_selected_index(); + if (nPos != -1) + { + SfxAddHelpBookmarkDialog_Impl aDlg(m_xBookmarksBox.get(), true); + aDlg.SetTitle(m_xBookmarksBox->get_text(nPos)); + if (aDlg.run() == RET_OK) + { + OUString sURL = m_xBookmarksBox->get_id(nPos); + m_xBookmarksBox->remove(nPos); + m_xBookmarksBox->append(sURL, aDlg.GetTitle(), + SvFileInformationManager::GetImageId(INetURLObject(rtl::Concat2View(IMAGE_URL+INetURLObject(sURL).GetHost())))); + m_xBookmarksBox->select(m_xBookmarksBox->n_children() - 1); + } + } + } + else if (rAction == u"delete") + { + sal_Int32 nPos = m_xBookmarksBox->get_selected_index(); + if (nPos != -1) + { + m_xBookmarksBox->remove(nPos); + const sal_Int32 nCount = m_xBookmarksBox->n_children(); + if (nCount) + { + if (nPos >= nCount) + nPos = nCount - 1; + m_xBookmarksBox->select(nPos); + } + } + } +} + +IMPL_LINK(BookmarksTabPage_Impl, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xBookmarksBox.get(), "sfx/ui/bookmarkmenu.ui")); + std::unique_ptr<weld::Menu> xMenu = xBuilder->weld_menu("menu"); + + OUString sIdent = xMenu->popup_at_rect(m_xBookmarksBox.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sIdent.isEmpty()) + DoAction(sIdent); + return true; +} + +IMPL_LINK(BookmarksTabPage_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + if (KEY_DELETE == nCode && m_xBookmarksBox->n_children() > 0) + { + DoAction(u"delete"); + bHandled = true; + } + return bHandled; +} + +// class BookmarksTabPage_Impl ------------------------------------------- +BookmarksTabPage_Impl::BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin) + : HelpTabPage_Impl(pParent, _pIdxWin, "HelpBookmarkPage", + "sfx/ui/helpbookmarkpage.ui") + , m_xBookmarksBox(m_xBuilder->weld_tree_view("bookmarks")) + , m_xBookmarksPB(m_xBuilder->weld_button("display")) +{ + m_xBookmarksBox->set_size_request(m_xBookmarksBox->get_approximate_digit_width() * 30, + m_xBookmarksBox->get_height_rows(20)); + + m_xBookmarksPB->connect_clicked( LINK(this, BookmarksTabPage_Impl, OpenHdl)); + m_xBookmarksBox->connect_row_activated(LINK(this, BookmarksTabPage_Impl, DoubleClickHdl)); + m_xBookmarksBox->connect_popup_menu(LINK(this, BookmarksTabPage_Impl, CommandHdl)); + m_xBookmarksBox->connect_key_press(LINK(this, BookmarksTabPage_Impl, KeyInputHdl)); + + // load bookmarks from configuration + const std::vector< SvtHistoryOptions::HistoryItem > aBookmarkSeq = SvtHistoryOptions::GetList( EHistoryType::HelpBookmarks ); + for ( const auto& rBookmark : aBookmarkSeq ) + { + AddBookmarks( rBookmark.sTitle, rBookmark.sURL ); + } +} + +BookmarksTabPage_Impl::~BookmarksTabPage_Impl() +{ + // save bookmarks to configuration + SvtHistoryOptions::Clear( EHistoryType::HelpBookmarks ); + const sal_Int32 nCount = m_xBookmarksBox->n_children(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + SvtHistoryOptions::AppendItem(EHistoryType::HelpBookmarks, m_xBookmarksBox->get_id(i), "", + m_xBookmarksBox->get_text(i), std::nullopt, std::nullopt); + } + + m_xBookmarksBox.reset(); + m_xBookmarksPB.reset(); +} + +IMPL_LINK_NOARG(BookmarksTabPage_Impl, OpenHdl, weld::Button&, void) +{ + aDoubleClickHdl.Call(nullptr); +} + +IMPL_LINK_NOARG(BookmarksTabPage_Impl, DoubleClickHdl, weld::TreeView&, bool) +{ + aDoubleClickHdl.Call(nullptr); + return true; +} + +void BookmarksTabPage_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink) +{ + aDoubleClickHdl = rLink; +} + +OUString BookmarksTabPage_Impl::GetSelectedEntry() const +{ + return m_xBookmarksBox->get_selected_id(); +} + +void BookmarksTabPage_Impl::AddBookmarks(const OUString& rTitle, const OUString& rURL) +{ + const OUString aImageURL {IMAGE_URL + INetURLObject(rURL).GetHost()}; + m_xBookmarksBox->append(rURL, rTitle, SvFileInformationManager::GetImageId(INetURLObject(aImageURL))); +} + +OUString SfxHelpWindow_Impl::buildHelpURL(std::u16string_view sFactory , + std::u16string_view sContent , + std::u16string_view sAnchor) +{ + OUStringBuffer sHelpURL(256); + sHelpURL.append(HELP_URL + sFactory + sContent); + AppendConfigToken(sHelpURL, true/*bUseQuestionMark*/); + if (!sAnchor.empty()) + sHelpURL.append(sAnchor); + return sHelpURL.makeStringAndClear(); +} + +void SfxHelpWindow_Impl::loadHelpContent(const OUString& sHelpURL, bool bAddToHistory) +{ + Reference< XComponentLoader > xLoader(getTextFrame(), UNO_QUERY); + if (!xLoader.is()) + return; + + // If a print job runs do not open a new page + Reference< XFrame2 > xTextFrame = pTextWin->getFrame(); + Reference< XController > xTextController ; + if (xTextFrame.is()) + xTextController = xTextFrame->getController (); + if ( xTextController.is() && !xTextController->suspend( true ) ) + { + xTextController->suspend( false ); + return; + } + + // save url to history + if (bAddToHistory) + pHelpInterceptor->addURL(sHelpURL); + + if ( !IsWait() ) + EnterWait(); + bool bSuccess = false; +// TODO implement locale fallback ... see below while(true) + { + try + { + Reference< XComponent > xContent = xLoader->loadComponentFromURL(sHelpURL, "_self", 0, Sequence< PropertyValue >()); + if (xContent.is()) + { + bSuccess = true; + } + } + catch(const RuntimeException&) + { throw; } + catch(const Exception&) + { /*break;*/ } + + /* TODO try next locale ... + no further locale available? => break loop and show error page + */ + } + openDone(sHelpURL, bSuccess); + if ( IsWait() ) + LeaveWait(); +} + +IMPL_LINK(SfxHelpIndexWindow_Impl, ActivatePageHdl, const OUString&, rPage, void) +{ + GetPage(rPage)->Activate(); +} + +SfxHelpIndexWindow_Impl::SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* _pParent, weld::Container* pContainer) + : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/helpcontrol.ui")) + , m_xContainer(m_xBuilder->weld_container("HelpControl")) + , m_xActiveLB(m_xBuilder->weld_combo_box("active")) + , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , aIdle("sfx2 appl SfxHelpIndexWindow_Impl") + , aIndexKeywordLink(LINK(this, SfxHelpIndexWindow_Impl, KeywordHdl)) + , pParentWin(_pParent) + , bIsInitDone(false) +{ + // create the pages + GetContentPage(); + GetIndexPage(); + GetSearchPage(); + GetBookmarksPage(); + + OUString sPageId("index"); + SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN ); + if ( aViewOpt.Exists() ) + { + OUString sSavedPageId = aViewOpt.GetPageID(); + if (m_xTabCtrl->get_page_index(sSavedPageId) != -1) + sPageId = sSavedPageId; + } + m_xTabCtrl->set_current_page(sPageId); + ActivatePageHdl(sPageId); + m_xActiveLB->connect_changed(LINK(this, SfxHelpIndexWindow_Impl, SelectHdl)); + + m_xTabCtrl->connect_enter_page(LINK(this, SfxHelpIndexWindow_Impl, ActivatePageHdl)); + + aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, InitHdl ) ); + aIdle.Start(); + + m_xContainer->show(); +} + +SfxHelpIndexWindow_Impl::~SfxHelpIndexWindow_Impl() +{ + SvtViewOptions aViewOpt(EViewType::TabDialog, CONFIGNAME_INDEXWIN); + aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident()); + + xCPage.reset(); + xIPage.reset(); + xSPage.reset(); + xBPage.reset(); +} + +void SfxHelpIndexWindow_Impl::Initialize() +{ + OUStringBuffer aHelpURL(HELP_URL); + AppendConfigToken(aHelpURL, true); + std::vector<OUString> aFactories = SfxContentHelper::GetResultSet(aHelpURL.makeStringAndClear()); + for (const OUString & rRow : aFactories) + { + sal_Int32 nIdx = 0; + OUString aTitle = rRow.getToken( 0, '\t', nIdx ); // token 0 + std::u16string_view aURL = o3tl::getToken(rRow, 1, '\t', nIdx ); // token 2 + OUString aFactory(INetURLObject(aURL).GetHost()); + m_xActiveLB->append(aFactory, aTitle); + } + + if (m_xActiveLB->get_active() == -1) + SetActiveFactory(); +} + +void SfxHelpIndexWindow_Impl::SetActiveFactory() +{ + DBG_ASSERT( xIPage, "index page not initialized" ); + if (!bIsInitDone && !m_xActiveLB->get_count()) + { + aIdle.Stop(); + InitHdl( nullptr ); + } + + for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i) + { + OUString aFactory = m_xActiveLB->get_id(i); + aFactory = aFactory.toAsciiLowerCase(); + if (aFactory == xIPage->GetFactory()) + { + if (m_xActiveLB->get_active() != i) + { + m_xActiveLB->set_active(i); + aSelectFactoryLink.Call(nullptr); + } + break; + } + } +} + +HelpTabPage_Impl* SfxHelpIndexWindow_Impl::GetPage(std::u16string_view rName) +{ + HelpTabPage_Impl* pPage = nullptr; + + if (rName == u"contents") + pPage = GetContentPage(); + else if (rName == u"index") + pPage = GetIndexPage(); + else if (rName == u"find") + pPage = GetSearchPage(); + else if (rName == u"bookmarks") + pPage = GetBookmarksPage(); + + assert(pPage && "SfxHelpIndexWindow_Impl::GetCurrentPage(): no current page"); + + return pPage; +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectHdl, weld::ComboBox&, void) +{ + aIdle.Start(); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, InitHdl, Timer *, void) +{ + bIsInitDone = true; + Initialize(); + + // now use the timer for selection + aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, SelectFactoryHdl ) ); + aIdle.SetPriority( TaskPriority::LOWEST ); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectFactoryHdl, Timer *, void) +{ + OUString aFactory = m_xActiveLB->get_active_id(); + if (!aFactory.isEmpty()) + { + SetFactory(aFactory.toAsciiLowerCase(), false); + aSelectFactoryLink.Call(this); + } +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, KeywordHdl, IndexTabPage_Impl&, void) +{ + // keyword found on index? + bool bIndex = xIPage->HasKeyword(); + + if( !bIndex) + bIndex = xIPage->HasKeywordIgnoreCase(); + // then set index or search page as current. + OUString sPageId = bIndex ? OUString("index") : OUString("find"); + if (sPageId != m_xTabCtrl->get_current_page_ident()) + m_xTabCtrl->set_current_page(sPageId); + + // at last we open the keyword + if ( bIndex ) + xIPage->OpenKeyword(); + else if ( !xSPage->OpenKeyword( sKeyword ) ) + pParentWin->ShowStartPage(); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl, LinkParamNone*, void) +{ + aPageDoubleClickLink.Call(nullptr); +} + +void SfxHelpIndexWindow_Impl::SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink) +{ + aPageDoubleClickLink = rLink; +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl, LinkParamNone*, void) +{ + aPageDoubleClickLink.Call(nullptr); +} + +IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl, LinkParamNone*, void) +{ + aPageDoubleClickLink.Call(nullptr); +} + +void SfxHelpIndexWindow_Impl::SetFactory( const OUString& rFactory, bool bActive ) +{ + if ( !rFactory.isEmpty() ) + { + GetIndexPage()->SetFactory( rFactory ); + // the index page made a check if rFactory is valid, + // so the index page always returns a valid factory + GetSearchPage()->SetFactory( GetIndexPage()->GetFactory() ); + if ( bActive ) + SetActiveFactory(); + } +} + +OUString SfxHelpIndexWindow_Impl::GetSelectedEntry() const +{ + OUString sRet; + + OUString sName(m_xTabCtrl->get_current_page_ident()); + + if (sName == "contents") + { + sRet = xCPage->GetSelectedEntry(); + } + else if (sName == "index") + { + sRet = xIPage->GetSelectedEntry(); + } + else if (sName == "find") + { + sRet = xSPage->GetSelectedEntry(); + } + else if (sName == "bookmarks") + { + sRet = xBPage->GetSelectedEntry(); + } + + return sRet; +} + +void SfxHelpIndexWindow_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL ) +{ + GetBookmarksPage()->AddBookmarks( rTitle, rURL ); +} + +bool SfxHelpIndexWindow_Impl::IsValidFactory( std::u16string_view _rFactory ) +{ + bool bValid = false; + for (sal_Int32 i = 0, nEntryCount = m_xActiveLB->get_count(); i < nEntryCount; ++i) + { + OUString aFactory = m_xActiveLB->get_id(i); + if (aFactory == _rFactory) + { + bValid = true; + break; + } + } + return bValid; +} + +void SfxHelpIndexWindow_Impl::ClearSearchPage() +{ + if ( xSPage ) + xSPage->ClearPage(); +} + +void SfxHelpIndexWindow_Impl::GrabFocusBack() +{ + OUString sName(m_xTabCtrl->get_current_page_ident()); + + if (sName == "contents" && xCPage) + xCPage->SetFocusOnBox(); + else if (sName == "index" && xIPage) + xIPage->SetFocusOnBox(); + else if (sName == "find" && xSPage) + xSPage->SetFocusOnBox(); + else if (sName == "bookmarks" && xBPage) + xBPage->SetFocusOnBox(); +} + +bool SfxHelpIndexWindow_Impl::HasFocusOnEdit() const +{ + bool bRet = false; + OUString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "index" && xIPage) + bRet = xIPage->HasFocusOnEdit(); + else if (sName == "find" && xSPage) + bRet = xSPage->HasFocusOnEdit(); + return bRet; +} + +OUString SfxHelpIndexWindow_Impl::GetSearchText() const +{ + OUString sRet; + OUString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "find" && xSPage) + sRet = xSPage->GetSearchText(); + return sRet; +} + +bool SfxHelpIndexWindow_Impl::IsFullWordSearch() const +{ + bool bRet = false; + OUString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "find" && xSPage) + bRet = xSPage->IsFullWordSearch(); + return bRet; +} + +void SfxHelpIndexWindow_Impl::OpenKeyword( const OUString& rKeyword ) +{ + sKeyword = rKeyword; + DBG_ASSERT( xIPage, "invalid index page" ); + xIPage->SetKeyword( sKeyword ); +} + +void SfxHelpIndexWindow_Impl::SelectExecutableEntry() +{ + OUString sName(m_xTabCtrl->get_current_page_ident()); + if (sName == "index" && xIPage ) + xIPage->SelectExecutableEntry(); +} + +weld::Window* SfxHelpIndexWindow_Impl::GetFrameWeld() const +{ + return pParentWin->GetFrameWeld(); +} + +// class TextWin_Impl ---------------------------------------------------- +TextWin_Impl::TextWin_Impl( vcl::Window* p ) : DockingWindow( p, 0 ) +{ +} + +bool TextWin_Impl::EventNotify( NotifyEvent& rNEvt ) +{ + if( ( rNEvt.GetType() == NotifyEventType::KEYINPUT ) && rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB ) + return GetParent()->EventNotify( rNEvt ); + else + return DockingWindow::EventNotify( rNEvt ); +} + + +// remove docking area acceptor from layoutmanager, so it will not layout anything further .-) +static void lcl_disableLayoutOfFrame(const Reference< XFrame2 >& xFrame) +{ + xFrame->setLayoutManager( Reference< XLayoutManager >() ); +} + +// class SfxHelpTextWindow_Impl ------------------------------------------ + +SfxHelpTextWindow_Impl::SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent) : + + Window( pParent, WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL ), + + xToolBox ( rBuilder.weld_toolbar("toolbar") ), + xOnStartupCB ( rBuilder.weld_check_button("checkbutton") ), + xMenu ( rBuilder.weld_menu("menu") ), + aSelectIdle ( "sfx2 appl SfxHelpTextWindow_Impl Select" ), + aIndexOnImage ( BMP_HELP_TOOLBOX_INDEX_ON ), + aIndexOffImage ( BMP_HELP_TOOLBOX_INDEX_OFF ), + aIndexOnText ( SfxResId( STR_HELP_BUTTON_INDEX_ON ) ), + aIndexOffText ( SfxResId( STR_HELP_BUTTON_INDEX_OFF ) ), + aOnStartupText ( SfxResId( RID_HELP_ONSTARTUP_TEXT ) ), + xHelpWin ( pHelpWin ), + pTextWin ( VclPtr<TextWin_Impl>::Create( this ) ), + bIsDebug ( false ), + bIsIndexOn ( false ), + bIsInClose ( false ), + bIsFullWordSearch ( false ) +{ + xFrame = Frame::create( ::comphelper::getProcessComponentContext() ); + xFrame->initialize( VCLUnoHelper::GetInterface ( pTextWin ) ); + xFrame->setName( "OFFICE_HELP" ); + lcl_disableLayoutOfFrame(xFrame); + + xToolBox->set_help_id(HID_HELP_TOOLBOX); + + xToolBox->set_item_tooltip_text("index", aIndexOffText ); + xToolBox->set_item_help_id("index", HID_HELP_TOOLBOXITEM_INDEX); + xToolBox->set_item_help_id("backward", HID_HELP_TOOLBOXITEM_BACKWARD); + xToolBox->set_item_help_id("forward", HID_HELP_TOOLBOXITEM_FORWARD); + xToolBox->set_item_help_id("start", HID_HELP_TOOLBOXITEM_START); + xToolBox->set_item_help_id("print", HID_HELP_TOOLBOXITEM_PRINT); + xToolBox->set_item_help_id("bookmarks", HID_HELP_TOOLBOXITEM_BOOKMARKS ); + xToolBox->set_item_help_id("searchdialog", HID_HELP_TOOLBOXITEM_SEARCHDIALOG); + + InitToolBoxImages(); + InitOnStartupBox(); + xOnStartupCB->connect_toggled(LINK(this, SfxHelpTextWindow_Impl, CheckHdl)); + + aSelectIdle.SetInvokeHandler( LINK( this, SfxHelpTextWindow_Impl, SelectHdl ) ); + aSelectIdle.SetPriority( TaskPriority::LOWEST ); + + char* pEnv = getenv( "help_debug" ); + if ( pEnv ) + bIsDebug = true; + + SvtMiscOptions().AddListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) ); +} + +SfxHelpTextWindow_Impl::~SfxHelpTextWindow_Impl() +{ + disposeOnce(); +} + +void SfxHelpTextWindow_Impl::dispose() +{ + bIsInClose = true; + SvtMiscOptions().RemoveListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) ); + m_xSrchDlg.reset(); + xToolBox.reset(); + xOnStartupCB.reset(); + xHelpWin.clear(); + pTextWin.disposeAndClear(); + vcl::Window::dispose(); +} + +bool SfxHelpTextWindow_Impl::HasSelection() const +{ + // is there any selection in the text and not only a cursor? + bool bRet = false; + Reference < XTextRange > xRange = getCursor(); + if ( xRange.is() ) + { + Reference < XText > xText = xRange->getText(); + Reference < XTextCursor > xCursor = xText->createTextCursorByRange( xRange ); + bRet = !xCursor->isCollapsed(); + } + + return bRet; +} + +void SfxHelpTextWindow_Impl::InitToolBoxImages() +{ + xToolBox->set_item_icon_name("index", bIsIndexOn ? aIndexOffImage : aIndexOnImage); +} + +void SfxHelpTextWindow_Impl::InitOnStartupBox() +{ + sCurrentFactory = SfxHelp::GetCurrentModuleIdentifier(); + + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + const OUString sPath { PATH_OFFICE_FACTORIES + sCurrentFactory }; + + // Attention: This check boy knows two states: + // 1) Reading of the config key fails with an exception or by getting an empty Any (!) => check box must be hidden + // 2) We read sal_True/sal_False => check box must be shown and enabled/disabled + + bool bHideBox = true; + bool bHelpAtStartup = false; + try + { + xConfiguration = ConfigurationHelper::openConfig( + xContext, PACKAGE_SETUP, EConfigurationModes::Standard ); + if ( xConfiguration.is() ) + { + Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_HELP_ON_OPEN ); + if (aAny >>= bHelpAtStartup) + bHideBox = false; + } + } + catch( Exception& ) + { + bHideBox = true; + } + + if ( bHideBox ) + xOnStartupCB->hide(); + else + { + // detect module name + OUString sModuleName; + + if ( xConfiguration.is() ) + { + OUString sTemp; + try + { + Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, KEY_UI_NAME ); + aAny >>= sTemp; + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::InitOnStartupBox()" ); + } + sModuleName = sTemp; + } + + if ( !sModuleName.isEmpty() ) + { + // set module name in checkbox text + xOnStartupCB->set_label(aOnStartupText.replaceFirst("%MODULENAME", sModuleName)); + // and show it + xOnStartupCB->show(); + // set check state + xOnStartupCB->set_active(bHelpAtStartup); + xOnStartupCB->save_state(); + } + } +} + +Reference< XBreakIterator > const & SfxHelpTextWindow_Impl::GetBreakIterator() +{ + if ( !xBreakIterator.is() ) + xBreakIterator = vcl::unohelper::CreateBreakIterator(); + DBG_ASSERT( xBreakIterator.is(), "Could not create BreakIterator" ); + return xBreakIterator; +} + +Reference< XTextRange > SfxHelpTextWindow_Impl::getCursor() const +{ + // return the current cursor + Reference< XTextRange > xCursor; + + try + { + Reference < XSelectionSupplier > xSelSup( xFrame->getController(), UNO_QUERY ); + if ( xSelSup.is() ) + { + Any aAny = xSelSup->getSelection(); + Reference < XIndexAccess > xSelection; + if ( aAny >>= xSelection ) + { + if ( xSelection->getCount() == 1 ) + { + aAny = xSelection->getByIndex(0); + aAny >>= xCursor; + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::getCursor(): unexpected exception" ); + } + + return xCursor; +} + + +bool SfxHelpTextWindow_Impl::isHandledKey( const vcl::KeyCode& _rKeyCode ) +{ + bool bRet = false; + sal_uInt16 nCode = _rKeyCode.GetCode(); + + // the keys <CTRL><A> (select all), <CTRL><C> (copy), + // <CTRL><F> (find), <CTRL><P> (print) and <CTRL><W> (close window) + // were handled in help + if ( _rKeyCode.IsMod1() && + ( KEY_A == nCode || KEY_C == nCode || KEY_F == nCode || KEY_P == nCode || KEY_W == nCode ) ) + { + if ( KEY_F == nCode ) + DoSearch(); + else + bRet = true; + } + + return bRet; +} + + +IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, SelectHdl, Timer *, void) +{ + try + { + // select the words, which are equal to the search text of the search page + Reference < XController > xController = xFrame->getController(); + if ( xController.is() ) + { + // get document + Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY ); + if ( xSearchable.is() ) + { + // create descriptor, set string and find all words + Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor(); + xSrchDesc->setPropertyValue( "SearchRegularExpression", Any( true ) ); + if ( bIsFullWordSearch ) + xSrchDesc->setPropertyValue( "SearchWords", Any( true ) ); + + xSrchDesc->setSearchString( sfx2::PrepareSearchString( aSearchText, GetBreakIterator(), false ) ); + Reference< XIndexAccess > xSelection = xSearchable->findAll( xSrchDesc ); + + // then select all found words + Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY ); + if ( xSelectionSup.is() ) + { + xSelectionSup->select( Any(xSelection) ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" ); + } +} + + +IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, NotifyHdl, LinkParamNone*, void ) +{ + InitToolBoxImages(); + Resize(); +} + +IMPL_LINK( SfxHelpTextWindow_Impl, FindHdl, sfx2::SearchDialog&, rDlg, void ) +{ + FindHdl(&rDlg); +} +void SfxHelpTextWindow_Impl::FindHdl(sfx2::SearchDialog* pDlg) +{ + bool bWrapAround = ( nullptr == pDlg ); + if ( bWrapAround ) + pDlg = m_xSrchDlg.get(); + DBG_ASSERT( pDlg, "invalid search dialog" ); + try + { + // select the words, which are equal to the search text of the search page + Reference < XController > xController = xFrame->getController(); + if ( xController.is() ) + { + // get document + Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY ); + if ( xSearchable.is() ) + { + // create descriptor, set string and find all words + Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor(); + xSrchDesc->setPropertyValue( "SearchWords", Any(pDlg->IsOnlyWholeWords()) ); + xSrchDesc->setPropertyValue( "SearchCaseSensitive", Any(pDlg->IsMarchCase()) ); + xSrchDesc->setPropertyValue( "SearchBackwards", Any(pDlg->IsSearchBackwards()) ); + xSrchDesc->setSearchString( pDlg->GetSearchText() ); + Reference< XInterface > xSelection; + Reference< XTextRange > xCursor = getCursor(); + + if ( xCursor.is() ) + { + if ( pDlg->IsSearchBackwards() ) + xCursor = xCursor->getStart(); + xSelection = xSearchable->findNext( xCursor, xSrchDesc ); + } + else + xSelection = xSearchable->findFirst( xSrchDesc ); + + // then select the found word + if ( xSelection.is() ) + { + Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY ); + if ( xSelectionSup.is() ) + { + xSelectionSup->select( Any(xSelection) ); + } + } + else if ( pDlg->IsWrapAround() && !bWrapAround ) + { + Reference < text::XTextViewCursorSupplier > xCrsrSupp( xController, uno::UNO_QUERY ); + Reference < text::XTextViewCursor > xTVCrsr = xCrsrSupp->getViewCursor(); + if ( xTVCrsr.is() ) + { + Reference < text::XTextDocument > xDoc( xController->getModel(), uno::UNO_QUERY ); + Reference < text::XText > xText = xDoc->getText(); + if ( xText.is() ) + { + if ( pDlg->IsSearchBackwards() ) + xTVCrsr->gotoRange( xText->getEnd(), false ); + else + xTVCrsr->gotoRange( xText->getStart(), false ); + FindHdl( nullptr ); + } + } + } + else + { + assert(m_xSrchDlg && "no search dialog"); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xSrchDlg->getDialog(), + VclMessageType::Info, VclButtonsType::Ok, SfxResId(STR_INFO_NOSEARCHTEXTFOUND))); + xBox->run(); + m_xSrchDlg->SetFocusOnEdit(); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" ); + } +} + +IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CloseHdl, LinkParamNone*, void) +{ + m_xSrchDlg.reset(); +} + +IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, CheckHdl, weld::Toggleable&, void) +{ + if ( !xConfiguration.is() ) + return; + + bool bChecked = xOnStartupCB->get_active(); + try + { + ConfigurationHelper::writeRelativeKey( + xConfiguration, PATH_OFFICE_FACTORIES + sCurrentFactory, KEY_HELP_ON_OPEN, Any( bChecked ) ); + ConfigurationHelper::flush( xConfiguration ); + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::CheckHdl()" ); + } +} + +void SfxHelpTextWindow_Impl::Resize() +{ + Size aSize = GetOutputSizePixel(); + pTextWin->SetPosSizePixel( Point(0, 0), aSize ); +} + +bool SfxHelpTextWindow_Impl::PreNotify( NotifyEvent& rNEvt ) +{ + bool bDone = false; + NotifyEventType nType = rNEvt.GetType(); + if ( NotifyEventType::COMMAND == nType && rNEvt.GetCommandEvent() ) + { + const CommandEvent* pCmdEvt = rNEvt.GetCommandEvent(); + vcl::Window* pCmdWin = rNEvt.GetWindow(); + + if ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu && pCmdWin != this ) + { + Point aPos; + if ( pCmdEvt->IsMouseEvent() ) + aPos = pCmdEvt->GetMousePosPixel(); + else + aPos = Point( pTextWin->GetPosPixel().X() + 20, 20 ); + + xMenu->clear(); + + if (bIsIndexOn) + xMenu->append("index", aIndexOffText, BMP_HELP_TOOLBOX_INDEX_OFF); + else + xMenu->append("index", aIndexOnText, BMP_HELP_TOOLBOX_INDEX_ON); + + xMenu->append_separator("separator1"); + xMenu->append("backward", SfxResId(STR_HELP_BUTTON_PREV), BMP_HELP_TOOLBOX_PREV); + xMenu->set_sensitive("backward", xHelpWin->HasHistoryPredecessor()); + xMenu->append("forward", SfxResId(STR_HELP_BUTTON_NEXT), BMP_HELP_TOOLBOX_NEXT); + xMenu->set_sensitive("forward", xHelpWin->HasHistorySuccessor()); + xMenu->append("start", SfxResId(STR_HELP_BUTTON_START), BMP_HELP_TOOLBOX_START); + xMenu->append_separator("separator2"); + xMenu->append("print", SfxResId(STR_HELP_BUTTON_PRINT), BMP_HELP_TOOLBOX_PRINT); + xMenu->append("bookmarks", SfxResId(STR_HELP_BUTTON_ADDBOOKMARK), BMP_HELP_TOOLBOX_BOOKMARKS); + xMenu->append("searchdialog", SfxResId(STR_HELP_BUTTON_SEARCHDIALOG), BMP_HELP_TOOLBOX_SEARCHDIALOG); + xMenu->append_separator("separator3"); + xMenu->append_check("selectionmode", SfxResId(STR_HELP_MENU_TEXT_SELECTION_MODE)); + URL aURL; + aURL.Complete = ".uno:SelectTextMode"; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict(aURL); + Reference < XDispatch > xDisp = xFrame->queryDispatch( aURL, OUString(), 0 ); + if(xDisp.is()) + { + rtl::Reference<HelpStatusListener_Impl> pStateListener = + new HelpStatusListener_Impl(xDisp, aURL ); + FeatureStateEvent rEvent = pStateListener->GetStateEvent(); + bool bCheck = false; + rEvent.State >>= bCheck; + xMenu->set_active("selectionmode", bCheck); + } + xMenu->append_separator("separator4"); + xMenu->append("copy", SfxResId(STR_HELP_MENU_TEXT_COPY), BMP_HELP_TOOLBOX_COPY); + xMenu->set_sensitive("copy", HasSelection()); + + if ( bIsDebug ) + { + xMenu->append_separator("separator5"); + xMenu->append("sourceview", SfxResId(STR_HELP_BUTTON_SOURCEVIEW)); + } + + int x, y, width, height; + weld::Window* pTopLevel = GetFrameWeld(); + xHelpWin->GetContainer()->get_extents_relative_to(*pTopLevel, x, y, width, height); + aPos.AdjustX(x); + aPos.AdjustY(y); + + xHelpWin->DoAction(xMenu->popup_at_rect(pTopLevel, tools::Rectangle(aPos, Size(1,1)))); + bDone = true; + } + } + else if ( NotifyEventType::KEYINPUT == nType && rNEvt.GetKeyEvent() ) + { + const KeyEvent* pKEvt = rNEvt.GetKeyEvent(); + const vcl::KeyCode& rKeyCode = pKEvt->GetKeyCode(); + sal_uInt16 nKeyGroup = rKeyCode.GetGroup(); + sal_uInt16 nKey = rKeyCode.GetCode(); + if ( KEYGROUP_ALPHA == nKeyGroup && !isHandledKey( rKeyCode ) ) + { + // do nothing disables the writer accelerators + bDone = true; + } + else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) ) + { + // <CTRL><F4> or <CTRL><W> -> close top frame + xHelpWin->CloseWindow(); + bDone = true; + } + else if ( KEY_TAB == nKey && xOnStartupCB->has_focus() ) + { + xToolBox->grab_focus(); + bDone = true; + } + } + + return bDone || Window::PreNotify( rNEvt ); +} + + +void SfxHelpTextWindow_Impl::GetFocus() +{ + if ( bIsInClose ) + return; + + try + { + if( xFrame.is() ) + { + Reference< css::awt::XWindow > xWindow = xFrame->getComponentWindow(); + if( xWindow.is() ) + xWindow->setFocus(); + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::GetFocus()" ); + } +} + + +void SfxHelpTextWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) || + ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) && + ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) ) + { + SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) ); + InitToolBoxImages(); + } +} + +void SfxHelpTextWindow_Impl::ToggleIndex( bool bOn ) +{ + bIsIndexOn = bOn; + if ( bIsIndexOn ) + { + xToolBox->set_item_icon_name("index", aIndexOffImage); + xToolBox->set_item_tooltip_text("index", aIndexOffText); + } + else + { + xToolBox->set_item_icon_name("index", aIndexOnImage); + xToolBox->set_item_tooltip_text("index", aIndexOnText); + } +} + +void SfxHelpTextWindow_Impl::SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch ) +{ + aSearchText = rSearchText; + bIsFullWordSearch = _bIsFullWordSearch; + aSelectIdle.Start(); +} + + +void SfxHelpTextWindow_Impl::SetPageStyleHeaderOff() const +{ + bool bSetOff = false; + // set off the pagestyle header to prevent print output of the help URL + try + { + Reference < XController > xController = xFrame->getController(); + Reference < XSelectionSupplier > xSelSup( xController, UNO_QUERY ); + if ( xSelSup.is() ) + { + Reference < XIndexAccess > xSelection; + if ( xSelSup->getSelection() >>= xSelection ) + { + Reference < XTextRange > xRange; + if ( xSelection->getByIndex(0) >>= xRange ) + { + Reference < XText > xText = xRange->getText(); + Reference < XPropertySet > xProps( xText->createTextCursorByRange( xRange ), UNO_QUERY ); + OUString sStyleName; + if ( xProps->getPropertyValue( "PageStyleName" ) >>= sStyleName ) + { + Reference < XStyleFamiliesSupplier > xStyles( xController->getModel(), UNO_QUERY ); + Reference < XNameContainer > xContainer; + if ( xStyles->getStyleFamilies()->getByName( "PageStyles" ) + >>= xContainer ) + { + Reference < XStyle > xStyle; + if ( xContainer->getByName( sStyleName ) >>= xStyle ) + { + Reference < XPropertySet > xPropSet( xStyle, UNO_QUERY ); + xPropSet->setPropertyValue( "HeaderIsOn", Any( false ) ); + + Reference< XModifiable > xReset(xStyles, UNO_QUERY); + xReset->setModified(false); + bSetOff = true; + } + } + } + } + } + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff()" ); + } + + SAL_WARN_IF( !bSetOff, "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): set off failed" ); +} + + +void SfxHelpTextWindow_Impl::CloseFrame() +{ + bIsInClose = true; + try + { + css::uno::Reference< css::util::XCloseable > xCloseable ( xFrame, css::uno::UNO_QUERY ); + if (xCloseable.is()) + xCloseable->close(true); + } + catch( css::util::CloseVetoException& ) + { + } +} + + +void SfxHelpTextWindow_Impl::DoSearch() +{ + if (m_xSrchDlg) + return; + + // create the search dialog + m_xSrchDlg = std::make_shared<sfx2::SearchDialog>(pTextWin->GetFrameWeld(), "HelpSearchDialog"); + // set handler + m_xSrchDlg->SetFindHdl( LINK( this, SfxHelpTextWindow_Impl, FindHdl ) ); + m_xSrchDlg->SetCloseHdl( LINK( this, SfxHelpTextWindow_Impl, CloseHdl ) ); + // get selected text of the help page to set it as the search text + Reference< XTextRange > xCursor = getCursor(); + if ( xCursor.is() ) + { + OUString sText = xCursor->getString(); + if ( !sText.isEmpty() ) + m_xSrchDlg->SetSearchText( sText ); + } + sfx2::SearchDialog::runAsync(m_xSrchDlg); +} + +void SfxHelpWindow_Impl::GetFocus() +{ + if (pTextWin) + pTextWin->GrabFocus(); + else + ResizableDockingWindow::GetFocus(); +} + +void SfxHelpWindow_Impl::MakeLayout() +{ + Split(); + + m_xHelpPaneWindow->set_visible(bIndex); +} + +IMPL_LINK(SfxHelpWindow_Impl, ResizeHdl, const Size&, rSize, void) +{ + int nNewWidth = rSize.Width(); + if (!nNewWidth) + return; + if (bSplit) + nIndexSize = round(m_xContainer->get_position() * 100.0 / nNewWidth); + nWidth = nNewWidth; + Split(); + nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth); +} + +void SfxHelpWindow_Impl::Split() +{ + if (!nWidth) + return; + m_xContainer->set_position(nWidth * nIndexSize / 100); + bSplit = true; +} + +void SfxHelpWindow_Impl::LoadConfig() +{ + SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN ); + if ( !aViewOpt.Exists() ) + return; + bIndex = aViewOpt.IsVisible(); + + Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME ); + OUString aUserData; + if ( aUserItem >>= aUserData ) + { + DBG_ASSERT( comphelper::string::getTokenCount(aUserData, ';') == 6, "invalid user data" ); + sal_Int32 nIdx = 0; + nIndexSize = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )); + o3tl::getToken(aUserData, 0, ';', nIdx); // ignore nTextSize + sal_Int32 nOldWidth = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )); + sal_Int32 nOldHeight = o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )); + aWinSize = Size(nOldWidth, nOldHeight); + aWinPos.setX( o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )) ); + aWinPos.setY( o3tl::toInt32(o3tl::getToken(aUserData, 0, ';', nIdx )) ); + } + + pTextWin->ToggleIndex( bIndex ); +} + +void SfxHelpWindow_Impl::SaveConfig() +{ + SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN ); + sal_Int32 nW = 0, nH = 0; + + if ( xWindow.is() ) + { + css::awt::Rectangle aRect = xWindow->getPosSize(); + nW = aRect.Width; + nH = aRect.Height; + } + + aViewOpt.SetVisible( bIndex ); + VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow( xWindow ); + aWinPos = pScreenWin->GetWindowExtentsAbsolute().TopLeft(); + if (bSplit) + nIndexSize = round(m_xContainer->get_position() * 100.0 / nWidth); + const OUString aUserData = OUString::number( nIndexSize ) + + ";" + OUString::number( 100 - nIndexSize ) + + ";" + OUString::number( nW ) + + ";" + OUString::number( nH ) + + ";" + OUString::number( aWinPos.X() ) + + ";" + OUString::number( aWinPos.Y() ); + + aViewOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) ); +} + +void SfxHelpWindow_Impl::ShowStartPage() +{ + loadHelpContent(SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), u"/start", u"")); +} + +IMPL_LINK(SfxHelpWindow_Impl, SelectHdl, const OUString&, rCurItem, void) +{ + bGrabFocusToToolBox = pTextWin->GetToolBox().has_focus(); + DoAction(rCurItem); +} + +IMPL_LINK_NOARG(SfxHelpWindow_Impl, OpenHdl, LinkParamNone*, void) +{ + xIndexWin->SelectExecutableEntry(); + OUString aEntry = xIndexWin->GetSelectedEntry(); + + if ( aEntry.isEmpty() ) + return; + + OUString sHelpURL; + + bool bComplete = aEntry.toAsciiLowerCase().match("vnd.sun.star.help"); + + if (bComplete) + sHelpURL = aEntry; + else + { + std::u16string_view aId; + OUString aAnchor('#'); + if ( comphelper::string::getTokenCount(aEntry, '#') == 2 ) + { + sal_Int32 nIdx{ 0 }; + aId = o3tl::getToken(aEntry, 0, '#', nIdx ); + aAnchor += o3tl::getToken(aEntry, 0, '#', nIdx ); + } + else + aId = aEntry; + + sHelpURL = SfxHelpWindow_Impl::buildHelpURL(xIndexWin->GetFactory(), Concat2View(OUString::Concat("/") + aId), aAnchor); + } + + loadHelpContent(sHelpURL); +} + +IMPL_LINK( SfxHelpWindow_Impl, SelectFactoryHdl, SfxHelpIndexWindow_Impl* , pWin, void ) +{ + if ( sTitle.isEmpty() ) + sTitle = GetParent()->GetText(); + + Reference< XTitle > xTitle(xFrame, UNO_QUERY); + if (xTitle.is ()) + xTitle->setTitle(sTitle + " - " + xIndexWin->GetActiveFactoryTitle()); + + if ( pWin ) + ShowStartPage(); + xIndexWin->ClearSearchPage(); +} + + +IMPL_LINK( SfxHelpWindow_Impl, ChangeHdl, HelpListener_Impl&, rListener, void ) +{ + SetFactory( rListener.GetFactory() ); +} + + +void SfxHelpWindow_Impl::openDone(std::u16string_view sURL , + bool bSuccess) +{ + INetURLObject aObj( sURL ); + if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp ) + SetFactory( aObj.GetHost() ); + if ( IsWait() ) + LeaveWait(); + if ( bGrabFocusToToolBox ) + { + pTextWin->GetToolBox().grab_focus(); + bGrabFocusToToolBox = false; + } + else + xIndexWin->GrabFocusBack(); + if ( !bSuccess ) + return; + + // set some view settings: "prevent help tips" and "helpid == 68245" + try + { + Reference < XController > xController = pTextWin->getFrame()->getController(); + if ( xController.is() ) + { + Reference < XViewSettingsSupplier > xSettings( xController, UNO_QUERY ); + Reference < XPropertySet > xViewProps = xSettings->getViewSettings(); + Reference< XPropertySetInfo > xInfo = xViewProps->getPropertySetInfo(); + xViewProps->setPropertyValue( "ShowContentTips", Any( false ) ); + xViewProps->setPropertyValue( "ShowGraphics", Any( true ) ); + xViewProps->setPropertyValue( "ShowTables", Any( true ) ); + xViewProps->setPropertyValue( "HelpURL", Any( OUString("HID:SFX2_HID_HELP_ONHELP") ) ); + OUString sProperty( "IsExecuteHyperlinks" ); + if ( xInfo->hasPropertyByName( sProperty ) ) + xViewProps->setPropertyValue( sProperty, Any( true ) ); + xController->restoreViewData(Any()); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::OpenDoneHdl(): unexpected exception" ); + } + + // When the SearchPage opens the help doc, then select all words, which are equal to its text + OUString sSearchText = comphelper::string::strip(xIndexWin->GetSearchText(), ' '); + if ( !sSearchText.isEmpty() ) + pTextWin->SelectSearchText( sSearchText, xIndexWin->IsFullWordSearch() ); + + // no page style header -> this prevents a print output of the URL + pTextWin->SetPageStyleHeaderOff(); +} + + +SfxHelpWindow_Impl::SfxHelpWindow_Impl( + const css::uno::Reference < css::frame::XFrame2 >& rFrame, + vcl::Window* pParent ) : + + ResizableDockingWindow(pParent), + + xFrame ( rFrame ), + pTextWin ( nullptr ), + pHelpInterceptor ( new HelpInterceptor_Impl() ), + pHelpListener ( new HelpListener_Impl( pHelpInterceptor ) ), + bIndex ( true ), + bGrabFocusToToolBox ( false ), + bSplit ( false ), + nWidth ( 0 ), + nIndexSize ( 40 ), // % of width + aWinPos ( 0, 0 ), + aWinSize ( 0, 0 ), + sTitle ( pParent->GetText() ) +{ + SetStyle(GetStyle() & ~WB_DOCKABLE); + + SetHelpId( HID_HELP_WINDOW ); + + m_xBuilder = Application::CreateInterimBuilder(m_xBox.get(), "sfx/ui/helpwindow.ui", false); + m_xContainer = m_xBuilder->weld_paned("HelpWindow"); + m_xContainer->connect_size_allocate(LINK(this, SfxHelpWindow_Impl, ResizeHdl)); + m_xHelpPaneWindow = m_xBuilder->weld_container("helppanewindow"); + m_xHelpTextWindow = m_xBuilder->weld_container("helptextwindow"); + m_xHelpTextXWindow = m_xHelpTextWindow->CreateChildFrame(); + + pHelpInterceptor->InitWaiter( this ); + xIndexWin.reset(new SfxHelpIndexWindow_Impl(this, m_xHelpPaneWindow.get())); + xIndexWin->SetDoubleClickHdl( LINK( this, SfxHelpWindow_Impl, OpenHdl ) ); + xIndexWin->SetSelectFactoryHdl( LINK( this, SfxHelpWindow_Impl, SelectFactoryHdl ) ); + + pTextWin = VclPtr<SfxHelpTextWindow_Impl>::Create(this, *m_xBuilder, VCLUnoHelper::GetWindow(m_xHelpTextXWindow)); + Reference < XFrames > xFrames = rFrame->getFrames(); + xFrames->append( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) ); + pTextWin->SetSelectHdl( LINK( this, SfxHelpWindow_Impl, SelectHdl ) ); + pTextWin->Show(); + pHelpInterceptor->setInterception( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) ); + pHelpListener->SetChangeHdl( LINK( this, SfxHelpWindow_Impl, ChangeHdl ) ); + LoadConfig(); +} + +SfxHelpWindow_Impl::~SfxHelpWindow_Impl() +{ + disposeOnce(); +} + +void SfxHelpWindow_Impl::dispose() +{ + SaveConfig(); + xIndexWin.reset(); + pTextWin->CloseFrame(); + pTextWin.disposeAndClear(); + + m_xHelpTextXWindow->dispose(); + m_xHelpTextXWindow.clear(); + m_xHelpTextWindow.reset(); + m_xHelpPaneWindow.reset(); + m_xContainer.reset(); + m_xBuilder.reset(); + + ResizableDockingWindow::dispose(); +} + +bool SfxHelpWindow_Impl::PreNotify( NotifyEvent& rNEvt ) +{ + bool bHandled = false; + if ( rNEvt.GetType() == NotifyEventType::KEYINPUT ) + { + // Backward == <ALT><LEFT> or <BACKSPACE> Forward == <ALT><RIGHT> + const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode(); + sal_uInt16 nKey = rKeyCode.GetCode(); + if ( ( rKeyCode.IsMod2() && ( KEY_LEFT == nKey || KEY_RIGHT == nKey ) ) || + ( !rKeyCode.GetModifier() && KEY_BACKSPACE == nKey && !xIndexWin->HasFocusOnEdit() ) ) + { + DoAction( rKeyCode.GetCode() == KEY_RIGHT ? u"forward" : u"backward" ); + bHandled = true; + } + else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) ) + { + // <CTRL><F4> or <CTRL><W> -> close top frame + CloseWindow(); + bHandled = true; + } + } + return bHandled || Window::PreNotify( rNEvt ); +} + +void SfxHelpWindow_Impl::setContainerWindow( const Reference < css::awt::XWindow >& xWin ) +{ + xWindow = xWin; + MakeLayout(); + if (xWindow.is()) + { + VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow(xWindow); + if (aWinSize.Width() && aWinSize.Height()) + pScreenWin->SetPosSizePixel(Point(aWinPos), aWinSize); + else + pScreenWin->SetPosPixel(Point(aWinPos)); + } +} + +void SfxHelpWindow_Impl::SetFactory( const OUString& rFactory ) +{ + xIndexWin->SetFactory( rFactory, true ); +} + +void SfxHelpWindow_Impl::SetHelpURL( std::u16string_view rURL ) +{ + INetURLObject aObj( rURL ); + if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp ) + SetFactory( aObj.GetHost() ); +} + +void SfxHelpWindow_Impl::DoAction(std::u16string_view rActionId) +{ + if (rActionId == u"index") + { + bIndex = !bIndex; + MakeLayout(); + pTextWin->ToggleIndex( bIndex ); + } + else if (rActionId == u"start") + { + ShowStartPage(); + } + else if (rActionId == u"backward" || rActionId == u"forward") + { + URL aURL; + aURL.Complete = ".uno:Backward"; + if (rActionId == u"forward") + aURL.Complete = ".uno:Forward"; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict(aURL); + pHelpInterceptor->dispatch( aURL, Sequence < PropertyValue >() ); + } + else if (rActionId == u"searchdialog") + { + pTextWin->DoSearch(); + } + else if (rActionId == u"print" || rActionId == u"sourceview" || rActionId == u"copy" || rActionId == u"selectionmode") + { + Reference < XDispatchProvider > xProv = pTextWin->getFrame(); + if ( xProv.is() ) + { + URL aURL; + if (rActionId == u"print") + aURL.Complete = ".uno:Print"; + else if (rActionId == u"sourceview") + aURL.Complete = ".uno:SourceView"; + else if (rActionId == u"copy") + aURL.Complete = ".uno:Copy"; + else // rActionId == "selectionmode" + aURL.Complete = ".uno:SelectTextMode"; + Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict(aURL); + Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + if ( xDisp.is() ) + xDisp->dispatch( aURL, Sequence < PropertyValue >() ); + } + } + else if (rActionId == u"bookmarks") + { + OUString aURL = pHelpInterceptor->GetCurrentURL(); + if ( !aURL.isEmpty() ) + { + try + { + Content aCnt( aURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties(); + if ( xInfo->hasPropertyByName( PROPERTY_TITLE ) ) + { + css::uno::Any aAny = aCnt.getPropertyValue( PROPERTY_TITLE ); + OUString aValue; + if ( aAny >>= aValue ) + { + SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), false); + aDlg.SetTitle(aValue); + if (aDlg.run() == RET_OK ) + { + xIndexWin->AddBookmarks( aDlg.GetTitle(), aURL ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::DoAction(): unexpected exception" ); + } + } + } +} + +void SfxHelpWindow_Impl::CloseWindow() +{ + try + { + // search for top frame + Reference< XFramesSupplier > xCreator = getTextFrame()->getCreator(); + while ( xCreator.is() && !xCreator->isTop() ) + { + xCreator = xCreator->getCreator(); + } + + // when found, close it + if ( xCreator.is() && xCreator->isTop() ) + { + Reference < XCloseable > xCloser( xCreator, UNO_QUERY ); + if ( xCloser.is() ) + xCloser->close( false ); + } + } + catch( Exception const & ) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelpWindow_Impl::CloseWindow()" ); + } +} + + +void SfxHelpWindow_Impl::UpdateToolbox() +{ + pTextWin->GetToolBox().set_item_sensitive("backward", pHelpInterceptor->HasHistoryPred()); + pTextWin->GetToolBox().set_item_sensitive("forward", pHelpInterceptor->HasHistorySucc()); +} + + +bool SfxHelpWindow_Impl::HasHistoryPredecessor() const +{ + return pHelpInterceptor->HasHistoryPred(); +} + + +bool SfxHelpWindow_Impl::HasHistorySuccessor() const +{ + return pHelpInterceptor->HasHistorySucc(); +} + +// class SfxAddHelpBookmarkDialog_Impl ----------------------------------- + +SfxAddHelpBookmarkDialog_Impl::SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename) + : GenericDialogController(pParent, "sfx/ui/bookmarkdialog.ui", "BookmarkDialog") + , m_xTitleED(m_xBuilder->weld_entry("entry")) + , m_xAltTitle(m_xBuilder->weld_label("alttitle")) +{ + if (bRename) + m_xDialog->set_title(m_xAltTitle->get_label()); +} + +void SfxAddHelpBookmarkDialog_Impl::SetTitle(const OUString& rTitle) +{ + m_xTitleED->set_text(rTitle); + m_xTitleED->select_region(0, -1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/newhelp.hxx b/sfx2/source/appl/newhelp.hxx new file mode 100644 index 0000000000..8e828dad95 --- /dev/null +++ b/sfx2/source/appl/newhelp.hxx @@ -0,0 +1,511 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_SFX2_SOURCE_APPL_NEWHELP_HXX +#define INCLUDED_SFX2_SOURCE_APPL_NEWHELP_HXX + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/frame/XFrame2.hpp> + +#include <rtl/ustrbuf.hxx> +#include <vcl/builderpage.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/idle.hxx> +#include <vcl/keycod.hxx> +#include <vcl/weld.hxx> +#include <vcl/window.hxx> + +#include <srchdlg.hxx> + +// context menu ids +#define MID_OPEN 1 +#define MID_RENAME 2 +#define MID_DELETE 3 + +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::i18n { class XBreakIterator; } +namespace com::sun::star::text { class XTextRange; } + +// class HelpTabPage_Impl ------------------------------------------------ + +class SfxHelpIndexWindow_Impl; + +class HelpTabPage_Impl : public BuilderPage +{ +protected: + SfxHelpIndexWindow_Impl* m_pIdxWin; + +public: + HelpTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin, + const OUString& rID, const OUString& rUIXMLDescription); + virtual ~HelpTabPage_Impl() override; +}; + +// class ContentTabPage_Impl --------------------------------------------- + +class ContentTabPage_Impl : public HelpTabPage_Impl +{ +private: + std::unique_ptr<weld::TreeView> m_xContentBox; + std::unique_ptr<weld::TreeIter> m_xScratchIter; + OUString aOpenBookImage; + OUString aClosedBookImage; + OUString aDocumentImage; + + Link<LinkParamNone*, void> aDoubleClickHdl; + + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(ExpandingHdl, const weld::TreeIter&, bool); + DECL_LINK(CollapsingHdl, const weld::TreeIter&, bool); + + void ClearChildren(const weld::TreeIter* pParent); + void InitRoot(); +public: + ContentTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* _pIdxWin); + virtual ~ContentTabPage_Impl() override; + + void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink); + OUString GetSelectedEntry() const; + void SetFocusOnBox() { m_xContentBox->grab_focus(); } +}; + +class IndexTabPage_Impl : public HelpTabPage_Impl +{ +private: + std::unique_ptr<weld::Entry> m_xIndexEntry; + std::unique_ptr<weld::TreeView> m_xIndexList; + std::unique_ptr<weld::Button> m_xOpenBtn; + + Idle aFactoryIdle; + Idle aAutoCompleteIdle; + Timer aKeywordTimer; + Link<IndexTabPage_Impl&,void> aKeywordLink; + + OUString sFactory; + OUString sKeyword; + + bool bIsActivated; + int nRowHeight; + int nAllHeight; + sal_uInt16 nLastCharCode; + + void InitializeIndex(); + void ClearIndex(); + + Link<LinkParamNone*, void> aDoubleClickHdl; + + DECL_LINK(OpenHdl, weld::Button&, void); + DECL_LINK(IdleHdl, Timer*, void); + DECL_LINK(AutoCompleteHdl, Timer*, void); + DECL_LINK(TimeoutHdl, Timer*, void); + DECL_LINK(TreeChangeHdl, weld::TreeView&, void); + DECL_LINK(EntryChangeHdl, weld::Entry&, void); + DECL_LINK(ActivateHdl, weld::Entry&, bool); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(CustomGetSizeHdl, weld::TreeView::get_size_args, Size); + DECL_LINK(CustomRenderHdl, weld::TreeView::render_args, void); + DECL_LINK(ResizeHdl, const Size&, void); + + int starts_with(const OUString& rStr, int nStartRow, bool bCaseSensitive); + +public: + IndexTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin); + virtual ~IndexTabPage_Impl() override; + + virtual void Activate() override; + + void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink); + void SetFactory( const OUString& rFactory ); + const OUString& GetFactory() const { return sFactory; } + OUString GetSelectedEntry() const; + void SetFocusOnBox() { m_xIndexEntry->grab_focus(); } + bool HasFocusOnEdit() const { return m_xIndexEntry->has_focus(); } + + void SetKeywordHdl( const Link<IndexTabPage_Impl&,void>& rLink ) { aKeywordLink = rLink; } + void SetKeyword( const OUString& rKeyword ); + bool HasKeyword() const; + bool HasKeywordIgnoreCase(); + void OpenKeyword(); + + void SelectExecutableEntry(); +}; + +class SearchTabPage_Impl : public HelpTabPage_Impl +{ +private: + std::unique_ptr<weld::ComboBox> m_xSearchED; + std::unique_ptr<weld::Button> m_xSearchBtn; + std::unique_ptr<weld::CheckButton> m_xFullWordsCB; + std::unique_ptr<weld::CheckButton> m_xScopeCB; + std::unique_ptr<weld::TreeView> m_xResultsLB; + std::unique_ptr<weld::Button> m_xOpenBtn; + + Link<LinkParamNone*, void> aDoubleClickHdl; + + OUString aFactory; + + css::uno::Reference< css::i18n::XBreakIterator > + xBreakIterator; + + void ClearSearchResults(); + void RememberSearchText( const OUString& rSearchText ); + void Search(); + + DECL_LINK(ClickHdl, weld::Button&, void); + DECL_LINK(OpenHdl, weld::Button&, void); + DECL_LINK(ModifyHdl, weld::ComboBox&, void); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(ActivateHdl, weld::ComboBox&, bool); + +public: + SearchTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin); + virtual ~SearchTabPage_Impl() override; + + void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink); + void SetFactory( const OUString& rFactory ) { aFactory = rFactory; } + OUString GetSelectedEntry() const; + void ClearPage(); + void SetFocusOnBox() { m_xResultsLB->grab_focus(); } + bool HasFocusOnEdit() const { return m_xSearchED->has_focus(); } + OUString GetSearchText() const { return m_xSearchED->get_active_text(); } + bool IsFullWordSearch() const { return m_xFullWordsCB->get_active(); } + bool OpenKeyword( const OUString& rKeyword ); +}; + +class BookmarksTabPage_Impl : public HelpTabPage_Impl +{ +private: + std::unique_ptr<weld::TreeView> m_xBookmarksBox; + std::unique_ptr<weld::Button> m_xBookmarksPB; + + Link<LinkParamNone*, void> aDoubleClickHdl; + + DECL_LINK(OpenHdl, weld::Button&, void); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + + void DoAction(std::u16string_view rAction); + +public: + BookmarksTabPage_Impl(weld::Widget* pParent, SfxHelpIndexWindow_Impl* pIdxWin); + virtual ~BookmarksTabPage_Impl() override; + + void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink); + OUString GetSelectedEntry() const; + void AddBookmarks( const OUString& rTitle, const OUString& rURL ); + void SetFocusOnBox() { m_xBookmarksBox->grab_focus(); } +}; + +// class SfxHelpIndexWindow_Impl ----------------------------------------- + +class SfxHelpWindow_Impl; + +class SfxHelpIndexWindow_Impl +{ +private: + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::ComboBox> m_xActiveLB; + std::unique_ptr<weld::Notebook> m_xTabCtrl; + + Idle aIdle; + + Link<SfxHelpIndexWindow_Impl*,void> aSelectFactoryLink; + Link<LinkParamNone*,void> aPageDoubleClickLink; + Link<IndexTabPage_Impl&,void> aIndexKeywordLink; + OUString sKeyword; + + VclPtr<SfxHelpWindow_Impl> pParentWin; + + std::unique_ptr<ContentTabPage_Impl> xCPage; + std::unique_ptr<IndexTabPage_Impl> xIPage; + std::unique_ptr<SearchTabPage_Impl> xSPage; + std::unique_ptr<BookmarksTabPage_Impl> xBPage; + + bool bIsInitDone; + + void Initialize(); + void SetActiveFactory(); + HelpTabPage_Impl* GetPage(std::u16string_view ); + + inline ContentTabPage_Impl* GetContentPage(); + inline IndexTabPage_Impl* GetIndexPage(); + inline SearchTabPage_Impl* GetSearchPage(); + inline BookmarksTabPage_Impl* GetBookmarksPage(); + + DECL_LINK(ActivatePageHdl, const OUString&, void); + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(InitHdl, Timer *, void); + DECL_LINK(SelectFactoryHdl, Timer *, void); + DECL_LINK(KeywordHdl, IndexTabPage_Impl&, void); + DECL_LINK(ContentTabPageDoubleClickHdl, LinkParamNone*, void); + DECL_LINK(TabPageDoubleClickHdl, LinkParamNone*, void); + DECL_LINK(IndexTabPageDoubleClickHdl, LinkParamNone*, void); + +public: + explicit SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* pParent, weld::Container* pContainer); + ~SfxHelpIndexWindow_Impl(); + + void SetDoubleClickHdl(const Link<LinkParamNone*, void>& rLink); + void SetSelectFactoryHdl( const Link<SfxHelpIndexWindow_Impl*,void>& rLink ) { aSelectFactoryLink = rLink; } + void SetFactory( const OUString& rFactory, bool bActive ); + OUString const & GetFactory() const { return xIPage->GetFactory(); } + OUString GetSelectedEntry() const; + void AddBookmarks( const OUString& rTitle, const OUString& rURL ); + bool IsValidFactory( std::u16string_view _rFactory ); + OUString GetActiveFactoryTitle() const { return m_xActiveLB->get_active_text(); } + void ClearSearchPage(); + void GrabFocusBack(); + bool HasFocusOnEdit() const; + OUString GetSearchText() const; + bool IsFullWordSearch() const; + void OpenKeyword( const OUString& rKeyword ); + void SelectExecutableEntry(); + + weld::Window* GetFrameWeld() const; +}; + +// inlines --------------------------------------------------------------- + +ContentTabPage_Impl* SfxHelpIndexWindow_Impl::GetContentPage() +{ + if (!xCPage) + { + xCPage.reset(new ContentTabPage_Impl(m_xTabCtrl->get_page("contents"), this)); + xCPage->SetDoubleClickHdl(LINK(this, SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl)); + } + return xCPage.get(); +} + +IndexTabPage_Impl* SfxHelpIndexWindow_Impl::GetIndexPage() +{ + if (!xIPage) + { + xIPage.reset(new IndexTabPage_Impl(m_xTabCtrl->get_page("index"), this)); + xIPage->SetDoubleClickHdl( LINK(this, SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl) ); + xIPage->SetKeywordHdl( aIndexKeywordLink ); + } + return xIPage.get(); +} + +SearchTabPage_Impl* SfxHelpIndexWindow_Impl::GetSearchPage() +{ + if (!xSPage) + { + xSPage.reset(new SearchTabPage_Impl(m_xTabCtrl->get_page("find"), this)); + xSPage->SetDoubleClickHdl( LINK(this, SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl) ); + } + return xSPage.get(); +} + +BookmarksTabPage_Impl* SfxHelpIndexWindow_Impl::GetBookmarksPage() +{ + if (!xBPage) + { + xBPage.reset(new BookmarksTabPage_Impl(m_xTabCtrl->get_page("bookmarks"), this)); + xBPage->SetDoubleClickHdl( LINK(this, SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl) ); + } + return xBPage.get(); +} + +// class TextWin_Impl ---------------------------------------------------- +class TextWin_Impl : public DockingWindow +{ +public: + explicit TextWin_Impl( vcl::Window* pParent ); + + virtual bool EventNotify( NotifyEvent& rNEvt ) override; +}; + +// class SfxHelpTextWindow_Impl ------------------------------------------ + +class SvtMiscOptions; +class SfxHelpWindow_Impl; + +class SfxHelpTextWindow_Impl : public vcl::Window +{ +private: + std::unique_ptr<weld::Toolbar> xToolBox; + std::unique_ptr<weld::CheckButton> xOnStartupCB; + std::unique_ptr<weld::Menu> xMenu; + Idle aSelectIdle; + OUString aIndexOnImage; + OUString aIndexOffImage; + OUString aIndexOnText; + OUString aIndexOffText; + OUString aSearchText; + OUString aOnStartupText; + OUString sCurrentFactory; + + VclPtr<SfxHelpWindow_Impl> xHelpWin; + VclPtr<vcl::Window> pTextWin; + std::shared_ptr<sfx2::SearchDialog> m_xSrchDlg; + css::uno::Reference < css::frame::XFrame2 > + xFrame; + css::uno::Reference< css::i18n::XBreakIterator > + xBreakIterator; + css::uno::Reference< css::uno::XInterface > + xConfiguration; + bool bIsDebug; + bool bIsIndexOn; + bool bIsInClose; + bool bIsFullWordSearch; + + bool HasSelection() const; + void InitToolBoxImages(); + void InitOnStartupBox(); + + css::uno::Reference< css::i18n::XBreakIterator > const & + GetBreakIterator(); + css::uno::Reference< css::text::XTextRange > + getCursor() const; + bool isHandledKey( const vcl::KeyCode& _rKeyCode ); + + DECL_LINK( SelectHdl, Timer *, void); + DECL_LINK( NotifyHdl, LinkParamNone*, void ); + DECL_LINK( FindHdl, sfx2::SearchDialog&, void ); + DECL_LINK( CloseHdl, LinkParamNone*, void ); + DECL_LINK( CheckHdl, weld::Toggleable&, void ); + void FindHdl(sfx2::SearchDialog*); + +public: + explicit SfxHelpTextWindow_Impl(SfxHelpWindow_Impl* pHelpWin, weld::Builder& rBuilder, vcl::Window* pParent); + virtual ~SfxHelpTextWindow_Impl() override; + virtual void dispose() override; + + virtual void Resize() override; + virtual bool PreNotify( NotifyEvent& rNEvt ) override; + virtual void GetFocus() override; + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + + const css::uno::Reference < css::frame::XFrame2 >& + getFrame() const { return xFrame; } + + void SetSelectHdl(const Link<const OUString&, void>& rLink) { xToolBox->connect_clicked(rLink); } + void ToggleIndex( bool bOn ); + void SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch ); + void SetPageStyleHeaderOff() const; + weld::Toolbar& GetToolBox() { return *xToolBox; } + void CloseFrame(); + void DoSearch(); +}; + +// class SfxHelpWindow_Impl ---------------------------------------------- + +class HelpInterceptor_Impl; +class HelpListener_Impl; +class SfxHelpWindow_Impl : public ResizableDockingWindow +{ +private: +friend class SfxHelpIndexWindow_Impl; + + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Paned> m_xContainer; + std::unique_ptr<weld::Container> m_xHelpPaneWindow; + std::unique_ptr<weld::Container> m_xHelpTextWindow; + css::uno::Reference<css::awt::XWindow> m_xHelpTextXWindow; + + css::uno::Reference < css::awt::XWindow > + xWindow; + css::uno::Reference < css::frame::XFrame2 > + xFrame; + + std::unique_ptr<SfxHelpIndexWindow_Impl> xIndexWin; + VclPtr<SfxHelpTextWindow_Impl> pTextWin; + HelpInterceptor_Impl* pHelpInterceptor; + rtl::Reference<HelpListener_Impl> pHelpListener; + + bool bIndex; + bool bGrabFocusToToolBox; + bool bSplit; + int nWidth; + int nIndexSize; + AbsoluteScreenPixelPoint aWinPos; + Size aWinSize; + OUString sTitle; + + virtual void GetFocus() override; + + void MakeLayout(); + void LoadConfig(); + void SaveConfig(); + void ShowStartPage(); + void Split(); + + DECL_LINK(SelectHdl, const OUString&, void); + DECL_LINK(OpenHdl, LinkParamNone*, void); + DECL_LINK(SelectFactoryHdl, SfxHelpIndexWindow_Impl*, void); + DECL_LINK(ChangeHdl, HelpListener_Impl&, void); + DECL_LINK(ResizeHdl, const Size&, void); + +public: + SfxHelpWindow_Impl( const css::uno::Reference < css::frame::XFrame2 >& rFrame, + vcl::Window* pParent ); + virtual ~SfxHelpWindow_Impl() override; + virtual void dispose() override; + + virtual bool PreNotify( NotifyEvent& rNEvt ) override; + + void setContainerWindow( const css::uno::Reference < css::awt::XWindow >& xWin ); + css::uno::Reference < css::frame::XFrame2 > const & + getTextFrame() const { return pTextWin->getFrame(); } + + void SetFactory( const OUString& rFactory ); + void SetHelpURL( std::u16string_view rURL ); + void DoAction(std::u16string_view rAction); + void CloseWindow(); + + weld::Container* GetContainer() { return m_xHelpTextWindow.get(); } + + void UpdateToolbox(); + void OpenKeyword( const OUString& rKeyword ) { xIndexWin->OpenKeyword( rKeyword ); } + + bool HasHistoryPredecessor() const; // forward to interceptor + bool HasHistorySuccessor() const; // forward to interceptor + + void openDone(std::u16string_view sURL , + bool bSuccess); + + static OUString buildHelpURL(std::u16string_view sFactory , + std::u16string_view sContent , + std::u16string_view sAnchor); + + void loadHelpContent(const OUString& sHelpURL , + bool bAddToHistory = true); +}; + +class SfxAddHelpBookmarkDialog_Impl : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Entry> m_xTitleED; + std::unique_ptr<weld::Label> m_xAltTitle; +public: + SfxAddHelpBookmarkDialog_Impl(weld::Widget* pParent, bool bRename); + + void SetTitle( const OUString& rTitle ); + OUString GetTitle() const { return m_xTitleED->get_text(); } +}; + +/// Appends ?Language=xy&System=abc to the help URL in rURL +void AppendConfigToken(OUStringBuffer& rURL, bool bQuestionMark); + +#endif // INCLUDED_SFX2_SOURCE_APPL_NEWHELP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/opengrf.cxx b/sfx2/source/appl/opengrf.cxx new file mode 100644 index 0000000000..3c94ca7ffb --- /dev/null +++ b/sfx2/source/appl/opengrf.cxx @@ -0,0 +1,286 @@ +/* -*- 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 <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <o3tl/any.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/opengrf.hxx> +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> +#include <osl/diagnose.h> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::uno; +using namespace ::cppu; + +static TranslateId SvxOpenGrfErr2ResId( ErrCode err ) +{ + if (err == ERRCODE_GRFILTER_OPENERROR) + return RID_SVXSTR_GRFILTER_OPENERROR; + else if (err == ERRCODE_GRFILTER_IOERROR) + return RID_SVXSTR_GRFILTER_IOERROR; + else if (err == ERRCODE_GRFILTER_VERSIONERROR) + return RID_SVXSTR_GRFILTER_VERSIONERROR; + else if (err == ERRCODE_GRFILTER_FILTERERROR) + return RID_SVXSTR_GRFILTER_FILTERERROR; + else + return RID_SVXSTR_GRFILTER_FORMATERROR; +} + +struct SvxOpenGrf_Impl +{ + SvxOpenGrf_Impl(weld::Window* pPreferredParent, + sal_Int16 nDialogType); + + sfx2::FileDialogHelper aFileDlg; + OUString sDetectedFilter; + weld::Window* pDialogParent; + uno::Reference < XFilePickerControlAccess > xCtrlAcc; +}; + + +SvxOpenGrf_Impl::SvxOpenGrf_Impl(weld::Window* pPreferredParent, + sal_Int16 nDialogType) + : aFileDlg(nDialogType, FileDialogFlags::Graphic, pPreferredParent) + , pDialogParent(pPreferredParent) +{ + uno::Reference < XFilePicker3 > xFP = aFileDlg.GetFilePicker(); + xCtrlAcc.set(xFP, UNO_QUERY); +} + + +SvxOpenGraphicDialog::SvxOpenGraphicDialog(const OUString& rTitle, weld::Window* pPreferredParent) + : mpImpl(new SvxOpenGrf_Impl(pPreferredParent, ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW)) +{ + mpImpl->aFileDlg.SetTitle(rTitle); + mpImpl->aFileDlg.SetContext(sfx2::FileDialogHelper::InsertImage); +} + +SvxOpenGraphicDialog::SvxOpenGraphicDialog(const OUString& rTitle, weld::Window* pPreferredParent, + sal_Int16 nDialogType) + : mpImpl(new SvxOpenGrf_Impl(pPreferredParent, nDialogType)) +{ + mpImpl->aFileDlg.SetTitle(rTitle); + mpImpl->aFileDlg.SetContext(sfx2::FileDialogHelper::InsertImage); +} + +SvxOpenGraphicDialog::~SvxOpenGraphicDialog() +{ +} + +ErrCode SvxOpenGraphicDialog::Execute() +{ + ErrCode nImpRet; + bool bQuitLoop(false); + + while( !bQuitLoop && + mpImpl->aFileDlg.Execute() == ERRCODE_NONE ) + { + if( !GetPath().isEmpty() ) + { + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + INetURLObject aObj( GetPath() ); + + // check whether we can load the graphic + OUString aCurFilter( GetCurrentFilter() ); + sal_uInt16 nFormatNum = rFilter.GetImportFormatNumber( aCurFilter ); + sal_uInt16 nRetFormat = 0; + sal_uInt16 nFound = USHRT_MAX; + + // non-local? + if ( INetProtocol::File != aObj.GetProtocol() ) + { + SfxMedium aMed( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ); + aMed.Download(); + SvStream* pStream = aMed.GetInStream(); + + if( pStream ) + nImpRet = rFilter.CanImportGraphic( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pStream, nFormatNum, &nRetFormat ); + else + nImpRet = rFilter.CanImportGraphic( aObj, nFormatNum, &nRetFormat ); + + if ( ERRCODE_NONE != nImpRet ) + { + if ( !pStream ) + nImpRet = rFilter.CanImportGraphic( aObj, GRFILTER_FORMAT_DONTKNOW, &nRetFormat ); + else + nImpRet = rFilter.CanImportGraphic( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pStream, + GRFILTER_FORMAT_DONTKNOW, &nRetFormat ); + } + } + else + { + nImpRet = rFilter.CanImportGraphic( aObj, nFormatNum, &nRetFormat ); + if( nImpRet != ERRCODE_NONE ) + nImpRet = rFilter.CanImportGraphic( aObj, GRFILTER_FORMAT_DONTKNOW, &nRetFormat ); + } + + if ( ERRCODE_NONE == nImpRet ) + nFound = nRetFormat; + + // could not load? + if ( nFound == USHRT_MAX ) + { + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(mpImpl->pDialogParent, + VclMessageType::Warning, VclButtonsType::NONE, + SfxResId(SvxOpenGrfErr2ResId(nImpRet)))); + xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY); + xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + bQuitLoop = xWarn->run() != RET_RETRY; + } + else + { + if( rFilter.GetImportFormatCount() ) + { + // store detected appropriate filter + OUString aFormatName(rFilter.GetImportFormatName(nFound)); + SetDetectedFilter(aFormatName); + } + else + { + SetDetectedFilter(mpImpl->aFileDlg.GetCurrentFilter()); + } + + return nImpRet; + } + } + } + + // cancel + return ErrCode(sal_uInt32(-1)); +} + + +void SvxOpenGraphicDialog::SetPath( const OUString& rPath, bool bLinkState ) +{ + mpImpl->aFileDlg.SetDisplayDirectory(rPath); + AsLink(bLinkState); +} + + +void SvxOpenGraphicDialog::EnableLink( bool state ) +{ + if( !mpImpl->xCtrlAcc.is() ) + return; + + try + { + mpImpl->xCtrlAcc->enableControl( ExtendedFilePickerElementIds::CHECKBOX_LINK, state ); + } + catch(const IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot enable \"link\" checkbox" ); +#endif + } +} + + +void SvxOpenGraphicDialog::AsLink(bool bState) +{ + if( !mpImpl->xCtrlAcc.is() ) + return; + + try + { + mpImpl->xCtrlAcc->setValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, Any(bState) ); + } + catch(const IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot check \"link\" checkbox" ); +#endif + } +} + + +bool SvxOpenGraphicDialog::IsAsLink() const +{ + try + { + if( mpImpl->xCtrlAcc.is() ) + { + Any aVal = mpImpl->xCtrlAcc->getValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0 ); + DBG_ASSERT(aVal.hasValue(), "Value CBX_INSERT_AS_LINK not found"); + return aVal.hasValue() && *o3tl::doAccess<bool>(aVal); + } + } + catch(const IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access \"link\" checkbox" ); +#endif + } + + return false; +} + +ErrCode SvxOpenGraphicDialog::GetGraphic(Graphic& rGraphic) const +{ + return mpImpl->aFileDlg.GetGraphic(rGraphic); +} + +OUString SvxOpenGraphicDialog::GetPath() const +{ + return mpImpl->aFileDlg.GetPath(); +} + +OUString SvxOpenGraphicDialog::GetCurrentFilter() const +{ + return mpImpl->aFileDlg.GetCurrentFilter(); +} + +OUString const & SvxOpenGraphicDialog::GetDetectedFilter() const +{ + return mpImpl->sDetectedFilter; +} + +void SvxOpenGraphicDialog::SetCurrentFilter(const OUString& rStr) +{ + mpImpl->aFileDlg.SetCurrentFilter(rStr); +} + +void SvxOpenGraphicDialog::SetDetectedFilter(const OUString& rStr) +{ + mpImpl->sDetectedFilter = rStr; +} + +Reference<ui::dialogs::XFilePickerControlAccess> const & SvxOpenGraphicDialog::GetFilePickerControlAccess() const +{ + return mpImpl->xCtrlAcc; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/openuriexternally.cxx b/sfx2/source/appl/openuriexternally.cxx new file mode 100644 index 0000000000..bdf8d72766 --- /dev/null +++ b/sfx2/source/appl/openuriexternally.cxx @@ -0,0 +1,141 @@ +/* -*- 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/. + */ + +#include <sal/config.h> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/security/AccessControlException.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteException.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <comphelper/processfactory.hxx> +#include <rtl/ustring.hxx> +#include <sfx2/sfxresid.hxx> +#include <tools/urlobj.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <openuriexternally.hxx> +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <sfx2/viewsh.hxx> +#include <sfx2/strings.hrc> + +namespace { + +class URITools +{ +private: + Timer aOpenURITimer { "sfx2::openUriExternallyTimer" }; + OUString msURI; + weld::Widget* mpDialogParent; + bool mbHandleSystemShellExecuteException; + DECL_LINK(onOpenURI, Timer*, void); + +public: + URITools(weld::Widget* pDialogParent) + : mpDialogParent(pDialogParent) + , mbHandleSystemShellExecuteException(false) + { + } + void openURI(const OUString& sURI, bool bHandleSystemShellExecuteException); +}; + +} + +void URITools::openURI(const OUString& sURI, bool bHandleSystemShellExecuteException) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + if (SfxViewShell* pViewShell = SfxViewShell::Current()) + { + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, + sURI.toUtf8()); + } + delete this; + return; + } + + mbHandleSystemShellExecuteException = bHandleSystemShellExecuteException; + msURI = sURI; + + // tdf#116305 Workaround: Use timer to bring browsers to the front + aOpenURITimer.SetInvokeHandler(LINK(this, URITools, onOpenURI)); +#ifdef _WIN32 + // 200ms seems to be the best compromise between responsiveness and success rate + aOpenURITimer.SetTimeout(200); +#else + aOpenURITimer.SetTimeout(0); +#endif + aOpenURITimer.Start(); +} + +IMPL_LINK_NOARG(URITools, onOpenURI, Timer*, void) +{ + std::unique_ptr<URITools> guard(this); + css::uno::Reference< css::system::XSystemShellExecute > exec( + css::system::SystemShellExecute::create(comphelper::getProcessComponentContext())); + for (sal_Int32 flags = css::system::SystemShellExecuteFlags::URIS_ONLY;;) { + try { + exec->execute(msURI, OUString(), flags); + } catch (css::security::AccessControlException & e) { + if (e.LackingPermission.hasValue() || flags == 0) { + throw css::uno::RuntimeException( + "unexpected AccessControlException: " + e.Message); + } + SolarMutexGuard g; + std::unique_ptr<weld::MessageDialog> eb( + Application::CreateMessageDialog( + mpDialogParent, VclMessageType::Warning, VclButtonsType::YesNo, + SfxResId(STR_DANGEROUS_TO_OPEN))); + eb->set_primary_text(eb->get_primary_text().replaceFirst("$(ARG1)", INetURLObject::decode(msURI, INetURLObject::DecodeMechanism::Unambiguous))); + eb->set_default_response(RET_NO); + if (eb->run() == RET_YES) { + flags = 0; + continue; + } + } catch (css::lang::IllegalArgumentException & e) { + if (e.ArgumentPosition != 0) { + throw css::uno::RuntimeException( + "unexpected IllegalArgumentException: " + e.Message); + } + SolarMutexGuard g; + std::unique_ptr<weld::MessageDialog> eb(Application::CreateMessageDialog(mpDialogParent, + VclMessageType::Warning, VclButtonsType::Ok, + SfxResId(STR_NO_ABS_URI_REF))); + eb->set_primary_text(eb->get_primary_text().replaceFirst("$(ARG1)", INetURLObject::decode(msURI, INetURLObject::DecodeMechanism::Unambiguous))); + eb->run(); + } catch (css::system::SystemShellExecuteException & e) { + if (!mbHandleSystemShellExecuteException) { + throw; + } + SolarMutexGuard g; + std::unique_ptr<weld::MessageDialog> eb(Application::CreateMessageDialog(mpDialogParent, + VclMessageType::Warning, VclButtonsType::Ok, + SfxResId(STR_NO_WEBBROWSER_FOUND))); + eb->set_primary_text( + eb->get_primary_text().replaceFirst("$(ARG1)", msURI) + .replaceFirst("$(ARG2)", OUString::number(e.PosixError)) + .replaceFirst("$(ARG3)", e.Message)); + //TODO: avoid subsequent replaceFirst acting on previous replacement + eb->run(); + } + break; + } +} + +void sfx2::openUriExternally(const OUString& sURI, bool bHandleSystemShellExecuteException, weld::Widget* pDialogParent) +{ + URITools* uriTools = new URITools(pDialogParent); + uriTools->openURI(sURI, bHandleSystemShellExecuteException); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/preventduplicateinteraction.cxx b/sfx2/source/appl/preventduplicateinteraction.cxx new file mode 100644 index 0000000000..c7d6341fa8 --- /dev/null +++ b/sfx2/source/appl/preventduplicateinteraction.cxx @@ -0,0 +1,220 @@ +/* -*- 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 <preventduplicateinteraction.hxx> + +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <utility> + +namespace sfx2 { + +PreventDuplicateInteraction::PreventDuplicateInteraction(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext(std::move(xContext)) +{ +} + +PreventDuplicateInteraction::~PreventDuplicateInteraction() +{ +} + +void PreventDuplicateInteraction::setHandler(const css::uno::Reference< css::task::XInteractionHandler >& xHandler) +{ + // SAFE -> + std::unique_lock aLock(m_aLock); + m_xWarningDialogsParent.reset(); + m_xHandler = xHandler; + // <- SAFE +} + +void PreventDuplicateInteraction::useDefaultUUIHandler() +{ + //if we use the default handler, set the parent to a window belonging to this object so that the dialogs + //don't block unrelated windows. + m_xWarningDialogsParent.reset(new WarningDialogsParentScope(m_xContext)); + css::uno::Reference<css::task::XInteractionHandler> xHandler(css::task::InteractionHandler::createWithParent( + m_xContext, m_xWarningDialogsParent->GetDialogParent()), css::uno::UNO_QUERY_THROW); + + // SAFE -> + std::unique_lock aLock(m_aLock); + m_xHandler = xHandler; + // <- SAFE +} + +css::uno::Any SAL_CALL PreventDuplicateInteraction::queryInterface( const css::uno::Type& aType ) +{ + if ( aType.equals( cppu::UnoType<XInteractionHandler2>::get() ) ) + { + std::unique_lock aLock(m_aLock); + css::uno::Reference< css::task::XInteractionHandler2 > xHandler( m_xHandler, css::uno::UNO_QUERY ); + if ( !xHandler.is() ) + return css::uno::Any(); + } + return ::cppu::WeakImplHelper<css::lang::XInitialization, css::task::XInteractionHandler2>::queryInterface(aType); +} + +void SAL_CALL PreventDuplicateInteraction::handle(const css::uno::Reference< css::task::XInteractionRequest >& xRequest) +{ + css::uno::Any aRequest = xRequest->getRequest(); + bool bHandleIt = true; + + // SAFE -> + std::unique_lock aLock(m_aLock); + + auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(), + [&aRequest](const InteractionInfo& rInfo) { return aRequest.isExtractableTo(rInfo.m_aInteraction); }); + if (pIt != m_lInteractionRules.end()) + { + InteractionInfo& rInfo = *pIt; + + ++rInfo.m_nCallCount; + rInfo.m_xRequest = xRequest; + bHandleIt = (rInfo.m_nCallCount <= rInfo.m_nMaxCount); + } + + css::uno::Reference< css::task::XInteractionHandler > xHandler = m_xHandler; + + aLock.unlock(); + // <- SAFE + + if ( bHandleIt && xHandler.is() ) + { + xHandler->handle(xRequest); + } + else + { + const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations(); + for (const auto& rContinuation : lContinuations) + { + css::uno::Reference< css::task::XInteractionAbort > xAbort(rContinuation, css::uno::UNO_QUERY); + if (xAbort.is()) + { + xAbort->select(); + break; + } + } + } +} + +sal_Bool SAL_CALL PreventDuplicateInteraction::handleInteractionRequest( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) +{ + css::uno::Any aRequest = xRequest->getRequest(); + bool bHandleIt = true; + + // SAFE -> + std::unique_lock aLock(m_aLock); + + auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(), + [&aRequest](const InteractionInfo& rInfo) { return aRequest.isExtractableTo(rInfo.m_aInteraction); }); + if (pIt != m_lInteractionRules.end()) + { + InteractionInfo& rInfo = *pIt; + + ++rInfo.m_nCallCount; + rInfo.m_xRequest = xRequest; + bHandleIt = (rInfo.m_nCallCount <= rInfo.m_nMaxCount); + } + + css::uno::Reference< css::task::XInteractionHandler2 > xHandler( m_xHandler, css::uno::UNO_QUERY ); + OSL_ENSURE( xHandler.is() || !m_xHandler.is(), + "PreventDuplicateInteraction::handleInteractionRequest: inconsistency!" ); + + aLock.unlock(); + // <- SAFE + + if ( bHandleIt && xHandler.is() ) + { + return xHandler->handleInteractionRequest(xRequest); + } + else + { + const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations(); + for (const auto& rContinuation : lContinuations) + { + css::uno::Reference< css::task::XInteractionAbort > xAbort(rContinuation, css::uno::UNO_QUERY); + if (xAbort.is()) + { + xAbort->select(); + break; + } + } + } + return false; +} + +void PreventDuplicateInteraction::addInteractionRule(const PreventDuplicateInteraction::InteractionInfo& aInteractionInfo) +{ + // SAFE -> + std::unique_lock aLock(m_aLock); + + auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(), + [&aInteractionInfo](const InteractionInfo& rInfo) { return rInfo.m_aInteraction == aInteractionInfo.m_aInteraction; }); + if (pIt != m_lInteractionRules.end()) + { + InteractionInfo& rInfo = *pIt; + rInfo.m_nMaxCount = aInteractionInfo.m_nMaxCount; + rInfo.m_nCallCount = aInteractionInfo.m_nCallCount; + return; + } + + m_lInteractionRules.push_back(aInteractionInfo); + // <- SAFE +} + +bool PreventDuplicateInteraction::getInteractionInfo(const css::uno::Type& aInteraction, + PreventDuplicateInteraction::InteractionInfo* pReturn ) const +{ + // SAFE -> + std::unique_lock aLock(m_aLock); + + auto pIt = std::find_if(m_lInteractionRules.begin(), m_lInteractionRules.end(), + [&aInteraction](const InteractionInfo& rInfo) { return rInfo.m_aInteraction == aInteraction; }); + if (pIt != m_lInteractionRules.end()) + { + *pReturn = *pIt; + return true; + } + // <- SAFE + + return false; +} + +void SAL_CALL PreventDuplicateInteraction::initialize(const css::uno::Sequence<css::uno::Any>& rArguments) +{ + // If we're re-initialized to set a specific new window as a parent then drop our temporary + // dialog parent + css::uno::Reference<css::lang::XInitialization> xHandler(m_xHandler, css::uno::UNO_QUERY); + if (xHandler.is()) + { + m_xWarningDialogsParent.reset(); + xHandler->initialize(rArguments); + } +} + +IMPL_STATIC_LINK_NOARG(WarningDialogsParent, TerminateDesktop, void*, void) +{ + css::frame::Desktop::create(comphelper::getProcessComponentContext())->terminate(); +} + +} // namespace sfx2 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/sfxhelp.cxx b/sfx2/source/appl/sfxhelp.cxx new file mode 100644 index 0000000000..b596a4e33e --- /dev/null +++ b/sfx2/source/appl/sfxhelp.cxx @@ -0,0 +1,1401 @@ +/* -*- 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_folders.h> +#include <sfx2/sfxhelp.hxx> + +#include <string_view> +#include <algorithm> +#include <cassert> +#include <cstddef> +#ifdef MACOSX +#include <premac.h> +#include <Foundation/NSString.h> +#include <CoreFoundation/CFURL.h> +#include <CoreServices/CoreServices.h> +#include <postmac.h> +#endif + +#include <sal/log.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/UnknownModuleException.hpp> +#include <com/sun/star/frame/XFrame2.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <unotools/configmgr.hxx> +#include <unotools/moduleoptions.hxx> +#include <tools/urlobj.hxx> +#include <ucbhelper/content.hxx> +#include <unotools/pathoptions.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/ustring.hxx> +#include <o3tl/string_view.hxx> +#include <officecfg/Office/Common.hxx> +#include <osl/process.h> +#include <osl/file.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/securityoptions.hxx> +#include <rtl/uri.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/keycod.hxx> +#include <vcl/settings.hxx> +#include <vcl/locktoplevels.hxx> +#include <vcl/weld.hxx> +#include <openuriexternally.hxx> + +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <sfx2/viewsh.hxx> + +#include "newhelp.hxx" +#include <sfx2/flatpak.hxx> +#include <sfx2/sfxresid.hxx> +#include <helper.hxx> +#include <sfx2/strings.hrc> +#include <vcl/svapp.hxx> +#include <rtl/string.hxx> +#include <svtools/langtab.hxx> +#include <comphelper/diagnose_ex.hxx> + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; + +namespace { + +class NoHelpErrorBox +{ +private: + std::unique_ptr<weld::MessageDialog> m_xErrBox; +public: + DECL_STATIC_LINK(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool); +public: + explicit NoHelpErrorBox(weld::Widget* pParent) + : m_xErrBox(Application::CreateMessageDialog(pParent, VclMessageType::Error, VclButtonsType::Ok, + SfxResId(RID_STR_HLPFILENOTEXIST))) + { + // Error message: "No help available" + m_xErrBox->connect_help(LINK(nullptr, NoHelpErrorBox, HelpRequestHdl)); + } + void run() + { + m_xErrBox->run(); + } +}; + +} + +IMPL_STATIC_LINK_NOARG(NoHelpErrorBox, HelpRequestHdl, weld::Widget&, bool) +{ + // do nothing, because no help available + return false; +} + +static OUString const & HelpLocaleString(); + +namespace { + +/// Root path of the help. +OUString const & getHelpRootURL() +{ + static OUString const s_instURL = []() + { + OUString tmp = officecfg::Office::Common::Path::Current::Help::get(); + if (tmp.isEmpty()) + { + // try to determine path from default + tmp = "$(instpath)/" LIBO_SHARE_HELP_FOLDER; + } + + // replace anything like $(instpath); + SvtPathOptions aOptions; + tmp = aOptions.SubstituteVariable(tmp); + + OUString url; + if (osl::FileBase::getFileURLFromSystemPath(tmp, url) == osl::FileBase::E_None) + tmp = url; + return tmp; + }(); + return s_instURL; +} + +bool impl_checkHelpLocalePath(OUString const & rpPath) +{ + osl::DirectoryItem directoryItem; + bool bOK = false; + + osl::FileStatus fileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName); + if (osl::DirectoryItem::get(rpPath, directoryItem) == osl::FileBase::E_None && + directoryItem.getFileStatus(fileStatus) == osl::FileBase::E_None && + fileStatus.isDirectory()) + { + bOK = true; + } + return bOK; +} + +/// Check for built-in help +/// Check if help/<lang>/err.html file exist +bool impl_hasHelpInstalled() +{ + if (comphelper::LibreOfficeKit::isActive()) + return false; + + // detect installed locale + static OUString const aLocaleStr = HelpLocaleString(); + + OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/err.html"; + bool bOK = false; + osl::DirectoryItem directoryItem; + if(osl::DirectoryItem::get(helpRootURL, directoryItem) == osl::FileBase::E_None){ + bOK=true; + } + + SAL_INFO( "sfx.appl", "Checking old help installed " << bOK); + return bOK; +} + +/// Check for html built-in help +/// Check if help/lang/text folder exist. Only html has it. +bool impl_hasHTMLHelpInstalled() +{ + if (comphelper::LibreOfficeKit::isActive()) + return false; + + // detect installed locale + static OUString const aLocaleStr = HelpLocaleString(); + + OUString helpRootURL = getHelpRootURL() + "/" + aLocaleStr + "/text"; + bool bOK = impl_checkHelpLocalePath( helpRootURL ); + SAL_INFO( "sfx.appl", "Checking new help (html) installed " << bOK); + return bOK; +} + +} // namespace + +/// Return the locale we prefer for displaying help +static OUString const & HelpLocaleString() +{ + if (comphelper::LibreOfficeKit::isActive()) + return comphelper::LibreOfficeKit::getLanguageTag().getBcp47(); + + static OUString aLocaleStr; + if (!aLocaleStr.isEmpty()) + return aLocaleStr; + + static constexpr OUString aEnglish(u"en-US"_ustr); + // detect installed locale + aLocaleStr = utl::ConfigManager::getUILocale(); + + if ( aLocaleStr.isEmpty() ) + { + aLocaleStr = aEnglish; + return aLocaleStr; + } + + // get fall-back language (country) + OUString sLang = aLocaleStr; + sal_Int32 nSepPos = sLang.indexOf( '-' ); + if (nSepPos != -1) + { + sLang = sLang.copy( 0, nSepPos ); + } + OUString sHelpPath(""); + sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aLocaleStr; + if (impl_checkHelpLocalePath(sHelpPath)) + { + return aLocaleStr; + } + sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + sLang; + if (impl_checkHelpLocalePath(sHelpPath)) + { + aLocaleStr = sLang; + return aLocaleStr; + } + sHelpPath = getHelpRootURL() + "/" + aLocaleStr; + if (impl_checkHelpLocalePath(sHelpPath)) + { + return aLocaleStr; + } + sHelpPath = getHelpRootURL() + "/" + sLang; + if (impl_checkHelpLocalePath(sHelpPath)) + { + aLocaleStr = sLang; + return aLocaleStr; + } + sHelpPath = getHelpRootURL() + "/" + utl::ConfigManager::getProductVersion() + "/" + aEnglish; + if (impl_checkHelpLocalePath(sHelpPath)) + { + aLocaleStr = aEnglish; + return aLocaleStr; + } + sHelpPath = getHelpRootURL() + "/" + aEnglish; + if (impl_checkHelpLocalePath(sHelpPath)) + { + aLocaleStr = aEnglish; + return aLocaleStr; + } + return aLocaleStr; +} + + + +void AppendConfigToken( OUStringBuffer& rURL, bool bQuestionMark ) +{ + OUString aLocaleStr = HelpLocaleString(); + + // query part exists? + if ( bQuestionMark ) + // no, so start with '?' + rURL.append('?'); + else + // yes, so only append with '&' + rURL.append('&'); + + // set parameters + rURL.append("Language="); + rURL.append(aLocaleStr); + rURL.append("&System="); + rURL.append(officecfg::Office::Common::Help::System::get()); + rURL.append("&Version="); + rURL.append(utl::ConfigManager::getProductVersion()); +} + +static bool GetHelpAnchor_Impl( std::u16string_view _rURL, OUString& _rAnchor ) +{ + bool bRet = false; + + try + { + ::ucbhelper::Content aCnt( INetURLObject( _rURL ).GetMainURL( INetURLObject::DecodeMechanism::NONE ), + Reference< css::ucb::XCommandEnvironment >(), + comphelper::getProcessComponentContext() ); + OUString sAnchor; + if ( aCnt.getPropertyValue("AnchorName") >>= sAnchor ) + { + + if ( !sAnchor.isEmpty() ) + { + _rAnchor = sAnchor; + bRet = true; + } + } + else + { + SAL_WARN( "sfx.appl", "Property 'AnchorName' is missing" ); + } + } + catch (const css::uno::Exception&) + { + } + + return bRet; +} + +namespace { + +class SfxHelp_Impl +{ +public: + static OUString GetHelpText( const OUString& aCommandURL, const OUString& rModule ); +}; + +} + +OUString SfxHelp_Impl::GetHelpText( const OUString& aCommandURL, const OUString& rModule ) +{ + // create help url + OUStringBuffer aHelpURL( SfxHelp::CreateHelpURL( aCommandURL, rModule ) ); + // added 'active' parameter + sal_Int32 nIndex = aHelpURL.lastIndexOf( '#' ); + if ( nIndex < 0 ) + nIndex = aHelpURL.getLength(); + aHelpURL.insert( nIndex, "&Active=true" ); + // load help string + return SfxContentHelper::GetActiveHelpString( aHelpURL.makeStringAndClear() ); +} + +SfxHelp::SfxHelp() + : bIsDebug(false) + , bLaunchingHelp(false) +{ + // read the environment variable "HELP_DEBUG" + // if it's set, you will see debug output on active help + OUString sHelpDebug; + OUString sEnvVarName( "HELP_DEBUG" ); + osl_getEnvironment( sEnvVarName.pData, &sHelpDebug.pData ); + bIsDebug = !sHelpDebug.isEmpty(); +} + +SfxHelp::~SfxHelp() +{ +} + +static OUString getDefaultModule_Impl() +{ + OUString sDefaultModule; + SvtModuleOptions aModOpt; + if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) ) + sDefaultModule = "swriter"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) ) + sDefaultModule = "scalc"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) ) + sDefaultModule = "simpress"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) ) + sDefaultModule = "sdraw"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) ) + sDefaultModule = "smath"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::CHART ) ) + sDefaultModule = "schart"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::BASIC ) ) + sDefaultModule = "sbasic"; + else if ( aModOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) ) + sDefaultModule = "sdatabase"; + else + { + SAL_WARN( "sfx.appl", "getDefaultModule_Impl(): no module installed" ); + } + return sDefaultModule; +} + +static OUString getCurrentModuleIdentifier_Impl() +{ + OUString sIdentifier; + Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference < XModuleManager2 > xModuleManager = ModuleManager::create(xContext); + Reference < XDesktop2 > xDesktop = Desktop::create(xContext); + Reference < XFrame > xCurrentFrame = xDesktop->getCurrentFrame(); + + if ( xCurrentFrame.is() ) + { + try + { + sIdentifier = xModuleManager->identify( xCurrentFrame ); + } + catch (const css::frame::UnknownModuleException&) + { + SAL_INFO( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): unknown module (help in help?)" ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::getCurrentModuleIdentifier_Impl(): exception of XModuleManager::identify()" ); + } + } + + return sIdentifier; +} + +namespace +{ + OUString MapModuleIdentifier(const OUString &rFactoryShortName) + { + OUString aFactoryShortName(rFactoryShortName); + + // Map some module identifiers to their "real" help module string. + if ( aFactoryShortName == "chart2" ) + aFactoryShortName = "schart" ; + else if ( aFactoryShortName == "BasicIDE" ) + aFactoryShortName = "sbasic"; + else if ( aFactoryShortName == "sweb" + || aFactoryShortName == "sglobal" + || aFactoryShortName == "swxform" ) + aFactoryShortName = "swriter" ; + else if ( aFactoryShortName == "dbquery" + || aFactoryShortName == "dbbrowser" + || aFactoryShortName == "dbrelation" + || aFactoryShortName == "dbtable" + || aFactoryShortName == "dbapp" + || aFactoryShortName == "dbreport" + || aFactoryShortName == "dbtdata" + || aFactoryShortName == "swreport" + || aFactoryShortName == "swform" ) + aFactoryShortName = "sdatabase"; + else if ( aFactoryShortName == "sbibliography" + || aFactoryShortName == "sabpilot" + || aFactoryShortName == "scanner" + || aFactoryShortName == "spropctrlr" + || aFactoryShortName == "StartModule" ) + aFactoryShortName.clear(); + + return aFactoryShortName; + } +} + +OUString SfxHelp::GetHelpModuleName_Impl(std::u16string_view rHelpID) +{ + OUString aFactoryShortName; + + //rhbz#1438876 detect preferred module for this help id, e.g. csv dialog + //for calc import before any toplevel is created and so context is + //otherwise unknown. Cosmetic, same help is shown in any case because its + //in the shared section, but title bar would state "Writer" when context is + //expected to be "Calc" + std::u16string_view sRemainder; + if (o3tl::starts_with(rHelpID, u"modules/", &sRemainder)) + { + std::size_t nEndModule = sRemainder.find(u'/'); + aFactoryShortName = nEndModule != std::u16string_view::npos + ? sRemainder.substr(0, nEndModule) : sRemainder; + } + + if (aFactoryShortName.isEmpty()) + { + OUString aModuleIdentifier = getCurrentModuleIdentifier_Impl(); + if (!aModuleIdentifier.isEmpty()) + { + try + { + Reference < XModuleManager2 > xModuleManager( + ModuleManager::create(::comphelper::getProcessComponentContext()) ); + Sequence< PropertyValue > lProps; + xModuleManager->getByName( aModuleIdentifier ) >>= lProps; + auto pProp = std::find_if(std::cbegin(lProps), std::cend(lProps), + [](const PropertyValue& rProp) { return rProp.Name == "ooSetupFactoryShortName"; }); + if (pProp != std::cend(lProps)) + pProp->Value >>= aFactoryShortName; + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sfx.appl", "SfxHelp::GetHelpModuleName_Impl()" ); + } + } + } + + if (!aFactoryShortName.isEmpty()) + aFactoryShortName = MapModuleIdentifier(aFactoryShortName); + if (aFactoryShortName.isEmpty()) + aFactoryShortName = getDefaultModule_Impl(); + + return aFactoryShortName; +} + +OUString SfxHelp::CreateHelpURL_Impl( const OUString& aCommandURL, const OUString& rModuleName ) +{ + // build up the help URL + OUStringBuffer aHelpURL("vnd.sun.star.help://"); + bool bHasAnchor = false; + OUString aAnchor; + + OUString aModuleName( rModuleName ); + if (aModuleName.isEmpty()) + aModuleName = getDefaultModule_Impl(); + + aHelpURL.append(aModuleName); + + if ( aCommandURL.isEmpty() ) + aHelpURL.append("/start"); + else + { + aHelpURL.append("/" + + rtl::Uri::encode(aCommandURL, + rtl_UriCharClassRelSegment, + rtl_UriEncodeKeepEscapes, + RTL_TEXTENCODING_UTF8)); + + OUStringBuffer aTempURL = aHelpURL; + AppendConfigToken( aTempURL, true ); + bHasAnchor = GetHelpAnchor_Impl(aTempURL, aAnchor); + } + + AppendConfigToken( aHelpURL, true ); + + if ( bHasAnchor ) + aHelpURL.append("#" + aAnchor); + + return aHelpURL.makeStringAndClear(); +} + +static SfxHelpWindow_Impl* impl_createHelp(Reference< XFrame2 >& rHelpTask , + Reference< XFrame >& rHelpContent) +{ + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + + // otherwise - create new help task + Reference< XFrame2 > xHelpTask( + xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::TASKS | FrameSearchFlag::CREATE), + UNO_QUERY); + if (!xHelpTask.is()) + return nullptr; + + // create all internal windows and sub frames ... + Reference< css::awt::XWindow > xParentWindow = xHelpTask->getContainerWindow(); + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow( xParentWindow ); + VclPtrInstance<SfxHelpWindow_Impl> pHelpWindow( xHelpTask, pParentWindow ); + Reference< css::awt::XWindow > xHelpWindow = VCLUnoHelper::GetInterface( pHelpWindow ); + + Reference< XFrame > xHelpContent; + if (xHelpTask->setComponent( xHelpWindow, Reference< XController >() )) + { + // Customize UI ... + xHelpTask->setName("OFFICE_HELP_TASK"); + + Reference< XPropertySet > xProps(xHelpTask, UNO_QUERY); + if (xProps.is()) + xProps->setPropertyValue( + "Title", + Any(SfxResId(STR_HELP_WINDOW_TITLE))); + + pHelpWindow->setContainerWindow( xParentWindow ); + xParentWindow->setVisible(true); + xHelpWindow->setVisible(true); + + // This sub frame is created internally (if we called new SfxHelpWindow_Impl() ...) + // It should exist :-) + xHelpContent = xHelpTask->findFrame("OFFICE_HELP", FrameSearchFlag::CHILDREN); + } + + if (!xHelpContent.is()) + { + pHelpWindow.disposeAndClear(); + return nullptr; + } + + xHelpContent->setName("OFFICE_HELP"); + + rHelpTask = xHelpTask; + rHelpContent = xHelpContent; + return pHelpWindow; +} + +OUString SfxHelp::GetHelpText( const OUString& aCommandURL, const vcl::Window* pWindow ) +{ + OUString sModuleName = GetHelpModuleName_Impl(aCommandURL); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, getCurrentModuleIdentifier_Impl()); + OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName ); + + OUString aNewHelpId; + + if (pWindow && sHelpText.isEmpty()) + { + // no help text found -> try with parent help id. + vcl::Window* pParent = pWindow->GetParent(); + while ( pParent ) + { + aNewHelpId = pParent->GetHelpId(); + sHelpText = SfxHelp_Impl::GetHelpText( aNewHelpId, sModuleName ); + if (!sHelpText.isEmpty()) + pParent = nullptr; + else + pParent = pParent->GetParent(); + } + + if (bIsDebug && sHelpText.isEmpty()) + aNewHelpId.clear(); + } + + // add some debug information? + if ( bIsDebug ) + { + sHelpText += "\n-------------\n" + + sModuleName + ": " + aCommandURL; + if ( !aNewHelpId.isEmpty() ) + { + sHelpText += " - " + aNewHelpId; + } + } + + return sHelpText; +} + +OUString SfxHelp::GetHelpText(const OUString& aCommandURL, const weld::Widget* pWidget) +{ + OUString sModuleName = GetHelpModuleName_Impl(aCommandURL); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommandURL, getCurrentModuleIdentifier_Impl()); + OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + OUString sHelpText = SfxHelp_Impl::GetHelpText( sRealCommand.isEmpty() ? aCommandURL : sRealCommand, sModuleName ); + + OUString aNewHelpId; + + if (pWidget && sHelpText.isEmpty()) + { + // no help text found -> try with parent help id. + std::unique_ptr<weld::Widget> xParent(pWidget->weld_parent()); + while (xParent) + { + aNewHelpId = xParent->get_help_id(); + sHelpText = SfxHelp_Impl::GetHelpText( aNewHelpId, sModuleName ); + if (!sHelpText.isEmpty()) + xParent.reset(); + else + xParent = xParent->weld_parent(); + } + + if (bIsDebug && sHelpText.isEmpty()) + aNewHelpId.clear(); + } + + // add some debug information? + if ( bIsDebug ) + { + sHelpText += "\n-------------\n" + + sModuleName + ": " + aCommandURL; + if ( !aNewHelpId.isEmpty() ) + { + sHelpText += " - " + aNewHelpId; + } + } + + return sHelpText; +} + +OUString SfxHelp::GetURLHelpText(std::u16string_view aURL) +{ + bool bCtrlClickHlink = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink); + + // "ctrl-click to follow link:" for not MacOS + // "⌘-click to follow link:" for MacOs + vcl::KeyCode aCode(KEY_SPACE); + vcl::KeyCode aModifiedCode(KEY_SPACE, KEY_MOD1); + OUString aModStr(aModifiedCode.GetName()); + aModStr = aModStr.replaceFirst(aCode.GetName(), ""); + aModStr = aModStr.replaceAll("+", ""); + OUString aHelpStr + = bCtrlClickHlink ? SfxResId(STR_CTRLCLICKHYPERLINK) : SfxResId(STR_CLICKHYPERLINK); + aHelpStr = aHelpStr.replaceFirst("%{key}", aModStr); + aHelpStr = aHelpStr.replaceFirst("%{link}", aURL); + return aHelpStr; +} + +void SfxHelp::SearchKeyword( const OUString& rKeyword ) +{ + Start_Impl(OUString(), static_cast<weld::Widget*>(nullptr), rKeyword); +} + +bool SfxHelp::Start( const OUString& rURL, const vcl::Window* pWindow ) +{ + if (bLaunchingHelp) + return true; + bLaunchingHelp = true; + bool bRet = Start_Impl( rURL, pWindow ); + bLaunchingHelp = false; + return bRet; +} + +bool SfxHelp::Start(const OUString& rURL, weld::Widget* pWidget) +{ + if (bLaunchingHelp) + return true; + bLaunchingHelp = true; + bool bRet = Start_Impl(rURL, pWidget, OUString()); + bLaunchingHelp = false; + return bRet; +} + +/// Redirect the vnd.sun.star.help:// urls to http://help.libreoffice.org +static bool impl_showOnlineHelp(const OUString& rURL, weld::Widget* pDialogParent) +{ + static constexpr OUString aInternal(u"vnd.sun.star.help://"_ustr); + if ( rURL.getLength() <= aInternal.getLength() || !rURL.startsWith(aInternal) ) + return false; + + OUString aHelpLink = officecfg::Office::Common::Help::HelpRootURL::get(); + OUString aTarget = OUString::Concat("Target=") + rURL.subView(aInternal.getLength()); + aTarget = aTarget.replaceAll("%2F", "/").replaceAll("?", "&"); + aHelpLink += aTarget; + + if (comphelper::LibreOfficeKit::isActive()) + { + if(SfxViewShell* pViewShell = SfxViewShell::Current()) + { + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, + aHelpLink.toUtf8()); + return true; + } + else if (GetpApp()) + { + GetpApp()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, + aHelpLink.toUtf8()); + return true; + } + + return false; + } + + try + { +#ifdef MACOSX + LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault, + CFStringCreateWithCString(kCFAllocatorDefault, + aHelpLink.toUtf8().getStr(), + kCFStringEncodingUTF8), + nullptr), + nullptr); + (void)pDialogParent; +#else + sfx2::openUriExternally(aHelpLink, false, pDialogParent); +#endif + return true; + } + catch (const Exception&) + { + } + return false; +} + +namespace { + +bool rewriteFlatpakHelpRootUrl(OUString * helpRootUrl) { + assert(helpRootUrl != nullptr); + //TODO: this function for now assumes that the passed-in *helpRootUrl references + // /app/libreoffice/help (which belongs to the org.libreoffice.LibreOffice.Help + // extension); it replaces it with the corresponding file URL as seen outside the flatpak + // sandbox: + struct Failure: public std::exception {}; + try { + static auto const url = [] { + // From /.flatpak-info [Instance] section, read + // app-path=<path> + // app-extensions=...;org.libreoffice.LibreOffice.Help=<sha>;... + // lines: + osl::File ini("file:///.flatpak-info"); + auto err = ini.open(osl_File_OpenFlag_Read); + if (err != osl::FileBase::E_None) { + SAL_WARN("sfx.appl", "LIBO_FLATPAK mode failure opening /.flatpak-info: " << err); + throw Failure(); + } + OUString path; + OUString extensions; + bool havePath = false; + bool haveExtensions = false; + for (bool instance = false; !(havePath && haveExtensions);) { + rtl::ByteSequence bytes; + err = ini.readLine(bytes); + if (err != osl::FileBase::E_None) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode reading /.flatpak-info fails with " << err + << " before [Instance] app-path"); + throw Failure(); + } + std::string_view const line( + reinterpret_cast<char const *>(bytes.getConstArray()), bytes.getLength()); + if (instance) { + static constexpr auto keyPath = std::string_view("app-path="); + static constexpr auto keyExtensions = std::string_view("app-extensions="); + if (!havePath && line.length() >= keyPath.size() + && line.substr(0, keyPath.size()) == keyPath.data()) + { + auto const value = line.substr(keyPath.size()); + if (!rtl_convertStringToUString( + &path.pData, value.data(), value.length(), + osl_getThreadTextEncoding(), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode failure converting app-path \"" << value + << "\" encoding"); + throw Failure(); + } + havePath = true; + } else if (!haveExtensions && line.length() >= keyExtensions.size() + && line.substr(0, keyExtensions.size()) == keyExtensions.data()) + { + auto const value = line.substr(keyExtensions.size()); + if (!rtl_convertStringToUString( + &extensions.pData, value.data(), value.length(), + osl_getThreadTextEncoding(), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode failure converting app-extensions \"" << value + << "\" encoding"); + throw Failure(); + } + haveExtensions = true; + } else if (line.length() > 0 && line[0] == '[') { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode /.flatpak-info lacks [Instance] app-path and" + " app-extensions"); + throw Failure(); + } + } else if (line == "[Instance]") { + instance = true; + } + } + ini.close(); + // Extract <sha> from ...;org.libreoffice.LibreOffice.Help=<sha>;...: + OUString sha; + for (sal_Int32 i = 0;;) { + OUString elem = extensions.getToken(0, ';', i); + if (elem.startsWith("org.libreoffice.LibreOffice.Help=", &sha)) { + break; + } + if (i == -1) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode /.flatpak-info [Instance] app-extensions \"" + << extensions << "\" org.libreoffice.LibreOffice.Help"); + throw Failure(); + } + } + // Assuming that <path> is of the form + // /.../app/org.libreoffice.LibreOffice/<arch>/<branch>/<sha'>/files + // rewrite it as + // /.../runtime/org.libreoffice.LibreOffice.Help/<arch>/<branch>/<sha>/files + // because the extension's files are stored at a different place than the app's files, + // so use this hack until flatpak itself provides a better solution: + static constexpr OUString segments = u"/app/org.libreoffice.LibreOffice/"_ustr; + auto const i1 = path.lastIndexOf(segments); + // use lastIndexOf instead of indexOf, in case the user-controlled prefix /.../ + // happens to contain such segments + if (i1 == -1) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path + << "\" doesn't contain /app/org.libreoffice.LibreOffice/"); + throw Failure(); + } + auto const i2 = i1 + segments.getLength(); + auto i3 = path.indexOf('/', i2); + if (i3 == -1) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path + << "\" doesn't contain branch segment"); + throw Failure(); + } + i3 = path.indexOf('/', i3 + 1); + if (i3 == -1) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path + << "\" doesn't contain sha segment"); + throw Failure(); + } + ++i3; + auto const i4 = path.indexOf('/', i3); + if (i4 == -1) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode /.flatpak-info [Instance] app-path \"" << path + << "\" doesn't contain files segment"); + throw Failure(); + } + path = path.subView(0, i1) + OUString::Concat("/runtime/org.libreoffice.LibreOffice.Help/") + + path.subView(i2, i3 - i2) + sha + path.subView(i4); + // Turn <path> into a file URL: + OUString url_; + err = osl::FileBase::getFileURLFromSystemPath(path, url_); + if (err != osl::FileBase::E_None) { + SAL_WARN( + "sfx.appl", + "LIBO_FLATPAK mode failure converting app-path \"" << path << "\" to URL: " + << err); + throw Failure(); + } + return url_; + }(); + *helpRootUrl = url; + return true; + } catch (Failure &) { + return false; + } +} + +} + +// add <noscript> meta for browsers without javascript + +constexpr OUStringLiteral SHTML1 = u"<!DOCTYPE HTML><html lang=\"en-US\"><head><meta charset=\"UTF-8\">"; +constexpr OUStringLiteral SHTML2 = u"<noscript><meta http-equiv=\"refresh\" content=\"0; url='"; +constexpr OUStringLiteral SHTML3 = u"/noscript.html'\"></noscript><meta http-equiv=\"refresh\" content=\"1; url='"; +constexpr OUStringLiteral SHTML4 = u"'\"><script type=\"text/javascript\"> window.location.href = \""; +constexpr OUStringLiteral SHTML5 = u"\";</script><title>Help Page Redirection</title></head><body></body></html>"; + +// use a tempfile since e.g. xdg-open doesn't support URL-parameters with file:// URLs +static bool impl_showOfflineHelp(const OUString& rURL, weld::Widget* pDialogParent) +{ + OUString aBaseInstallPath = getHelpRootURL(); + // For the flatpak case, find the pathname outside the flatpak sandbox that corresponds to + // aBaseInstallPath, because that is what needs to be stored in aTempFile below: + if (flatpak::isFlatpak() && !rewriteFlatpakHelpRootUrl(&aBaseInstallPath)) { + return false; + } + + OUString aHelpLink( aBaseInstallPath + "/index.html?" ); + OUString aTarget = OUString::Concat("Target=") + rURL.subView(RTL_CONSTASCII_LENGTH("vnd.sun.star.help://")); + aTarget = aTarget.replaceAll("%2F","/").replaceAll("?","&"); + aHelpLink += aTarget; + + // Get a html tempfile (for the flatpak case, create it in XDG_CACHE_HOME instead of /tmp for + // technical reasons, so that it can be accessed by the browser running outside the sandbox): + static constexpr OUStringLiteral aExtension(u".html"); + OUString * parent = nullptr; + if (flatpak::isFlatpak() && !flatpak::createTemporaryHtmlDirectory(&parent)) { + return false; + } + ::utl::TempFileNamed aTempFile(u"NewHelp", true, aExtension, parent, false ); + + SvStream* pStream = aTempFile.GetStream(StreamMode::WRITE); + pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); + + OUString aTempStr = SHTML1 + SHTML2 + + aBaseInstallPath + "/" + HelpLocaleString() + SHTML3 + + aHelpLink + SHTML4 + + aHelpLink + SHTML5; + + pStream->WriteUnicodeOrByteText(aTempStr); + + aTempFile.CloseStream(); + try + { +#ifdef MACOSX + LSOpenCFURLRef(CFURLCreateWithString(kCFAllocatorDefault, + CFStringCreateWithCString(kCFAllocatorDefault, + aTempFile.GetURL().toUtf8().getStr(), + kCFStringEncodingUTF8), + nullptr), + nullptr); + (void)pDialogParent; +#else + sfx2::openUriExternally(aTempFile.GetURL(), false, pDialogParent); +#endif + return true; + } + catch (const Exception&) + { + } + aTempFile.EnableKillingFile(); + return false; +} + +namespace +{ + // tdf#119579 skip floating windows as potential parent for missing help dialog + const vcl::Window* GetBestParent(const vcl::Window* pWindow) + { + while (pWindow) + { + if (pWindow->IsSystemWindow() && pWindow->GetType() != WindowType::FLOATINGWINDOW) + break; + pWindow = pWindow->GetParent(); + } + return pWindow; + } +} + +namespace { + +class HelpManualMessage : public weld::MessageDialogController +{ +private: + std::unique_ptr<weld::CheckButton> m_xHideOfflineHelpCB; + +public: + HelpManualMessage(weld::Widget* pParent) + : MessageDialogController(pParent, "sfx/ui/helpmanual.ui", "onlinehelpmanual", "hidedialog") + , m_xHideOfflineHelpCB(m_xBuilder->weld_check_button("hidedialog")) + { + LanguageType aLangType = Application::GetSettings().GetUILanguageTag().getLanguageType(); + OUString sLocaleString = SvtLanguageTable::GetLanguageString(aLangType); + OUString sPrimText = get_primary_text(); + set_primary_text(sPrimText.replaceAll("$UILOCALE", sLocaleString)); + } + + bool GetOfflineHelpPopUp() const { return !m_xHideOfflineHelpCB->get_active(); } +}; + +} + +bool SfxHelp::Start_Impl(const OUString& rURL, const vcl::Window* pWindow) +{ + OUStringBuffer aHelpRootURL("vnd.sun.star.help://"); + AppendConfigToken(aHelpRootURL, true); + SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear()); + + /* rURL may be + * - a "real" URL + * - a HelpID (formerly a long, now a string) + * If rURL is a URL, CreateHelpURL should be called for this URL + * If rURL is an arbitrary string, the same should happen, but the URL should be tried out + * if it delivers real help content. In case only the Help Error Document is returned, the + * parent of the window for that help was called, is asked for its HelpID. + * For compatibility reasons this upward search is not implemented for "real" URLs. + * Help keyword search now is implemented as own method; in former versions it + * was done via Help::Start, but this implementation conflicted with the upward search. + */ + OUString aHelpURL; + INetURLObject aParser( rURL ); + INetProtocol nProtocol = aParser.GetProtocol(); + + switch ( nProtocol ) + { + case INetProtocol::VndSunStarHelp: + // already a vnd.sun.star.help URL -> nothing to do + aHelpURL = rURL; + break; + default: + { + OUString aHelpModuleName(GetHelpModuleName_Impl(rURL)); + OUString aRealCommand; + + if ( nProtocol == INetProtocol::Uno ) + { + // Command can be just an alias to another command. + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rURL, getCurrentModuleIdentifier_Impl()); + aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + } + + // no URL, just a HelpID (maybe empty in case of keyword search) + aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName ); + + if ( impl_hasHelpInstalled() && pWindow && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) ) + { + // no help found -> try with parent help id. + vcl::Window* pParent = pWindow->GetParent(); + while ( pParent ) + { + OUString aHelpId = pParent->GetHelpId(); + aHelpURL = CreateHelpURL( aHelpId, aHelpModuleName ); + + if ( !SfxContentHelper::IsHelpErrorDocument( aHelpURL ) ) + { + break; + } + else + { + pParent = pParent->GetParent(); + if (!pParent) + { + // create help url of start page ( helpid == 0 -> start page) + aHelpURL = CreateHelpURL( OUString(), aHelpModuleName ); + } + } + } + } + break; + } + } + + pWindow = GetBestParent(pWindow); + weld::Window* pWeldWindow = pWindow ? pWindow->GetFrameWeld() : nullptr; + + if ( comphelper::LibreOfficeKit::isActive() ) + { + impl_showOnlineHelp(aHelpURL, pWeldWindow); + return true; + } +#ifdef MACOSX + if (@available(macOS 10.14, *)) { + // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder + // force online-help instead if Safari is default browser. + CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL( + CFURLCreateWithString( + kCFAllocatorDefault, + static_cast<CFStringRef>(@"https://www.libreoffice.org"), + nullptr), + kLSRolesAll, nullptr); + if([static_cast<NSString*>(CFURLGetString(pBrowser)) hasSuffix:@"/Applications/Safari.app/"]) { + impl_showOnlineHelp(aHelpURL, pWeldWindow); + return true; + } + } +#endif + + // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content, + // that implies that this help content belongs to an extension (and thus would not be available + // in neither the offline nor online HTML help); in that case, fall through to the "old-help to + // display" code below: + if (SfxContentHelper::IsHelpErrorDocument(aHelpURL)) + { + if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL, pWeldWindow) ) + { + return true; + } + + if ( !impl_hasHelpInstalled() ) + { + bool bShowOfflineHelpPopUp = officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get(); + + TopLevelWindowLocker aBusy; + + if(bShowOfflineHelpPopUp) + { + aBusy.incBusy(pWeldWindow); + HelpManualMessage aQueryBox(pWeldWindow); + short OnlineHelpBox = aQueryBox.run(); + bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK; + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox.GetOfflineHelpPopUp(), xChanges); + xChanges->commit(); + aBusy.decBusy(); + } + if(!bShowOfflineHelpPopUp) + { + if ( impl_showOnlineHelp(aHelpURL, pWeldWindow) ) + return true; + else + { + aBusy.incBusy(pWeldWindow); + NoHelpErrorBox aErrBox(pWeldWindow); + aErrBox.run(); + aBusy.decBusy(); + return false; + } + } + else + { + return false; + } + } + } + + // old-help to display + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + + // check if help window is still open + // If not, create a new one and return access directly to the internal sub frame showing the help content + // search must be done here; search one desktop level could return an arbitrary frame + Reference< XFrame2 > xHelp( + xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN), + UNO_QUERY); + Reference< XFrame > xHelpContent = xDesktop->findFrame( + "OFFICE_HELP", + FrameSearchFlag::CHILDREN); + + SfxHelpWindow_Impl* pHelpWindow = nullptr; + if (!xHelp.is()) + pHelpWindow = impl_createHelp(xHelp, xHelpContent); + else + pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow())); + if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow) + return false; + + SAL_INFO("sfx.appl", "HelpId = " << aHelpURL); + + pHelpWindow->SetHelpURL( aHelpURL ); + pHelpWindow->loadHelpContent(aHelpURL); + + Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY ); + if ( xTopWindow.is() ) + xTopWindow->toFront(); + + return true; +} + +bool SfxHelp::Start_Impl(const OUString& rURL, weld::Widget* pWidget, const OUString& rKeyword) +{ + OUStringBuffer aHelpRootURL("vnd.sun.star.help://"); + AppendConfigToken(aHelpRootURL, true); + SfxContentHelper::GetResultSet(aHelpRootURL.makeStringAndClear()); + + /* rURL may be + * - a "real" URL + * - a HelpID (formerly a long, now a string) + * If rURL is a URL, CreateHelpURL should be called for this URL + * If rURL is an arbitrary string, the same should happen, but the URL should be tried out + * if it delivers real help content. In case only the Help Error Document is returned, the + * parent of the window for that help was called, is asked for its HelpID. + * For compatibility reasons this upward search is not implemented for "real" URLs. + * Help keyword search now is implemented as own method; in former versions it + * was done via Help::Start, but this implementation conflicted with the upward search. + */ + OUString aHelpURL; + INetURLObject aParser( rURL ); + INetProtocol nProtocol = aParser.GetProtocol(); + + switch ( nProtocol ) + { + case INetProtocol::VndSunStarHelp: + // already a vnd.sun.star.help URL -> nothing to do + aHelpURL = rURL; + break; + default: + { + OUString aHelpModuleName(GetHelpModuleName_Impl(rURL)); + OUString aRealCommand; + + if ( nProtocol == INetProtocol::Uno ) + { + // Command can be just an alias to another command. + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rURL, getCurrentModuleIdentifier_Impl()); + aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties); + } + + // no URL, just a HelpID (maybe empty in case of keyword search) + aHelpURL = CreateHelpURL_Impl( aRealCommand.isEmpty() ? rURL : aRealCommand, aHelpModuleName ); + + if ( impl_hasHelpInstalled() && pWidget && SfxContentHelper::IsHelpErrorDocument( aHelpURL ) ) + { + bool bUseFinalFallback = true; + // no help found -> try ids of parents. + pWidget->help_hierarchy_foreach([&aHelpModuleName, &aHelpURL, &bUseFinalFallback](const OUString& rHelpId){ + if (rHelpId.isEmpty()) + return false; + aHelpURL = CreateHelpURL(rHelpId, aHelpModuleName); + bool bFinished = !SfxContentHelper::IsHelpErrorDocument(aHelpURL); + if (bFinished) + bUseFinalFallback = false; + return bFinished; + }); + + if (bUseFinalFallback) + { + // create help url of start page ( helpid == 0 -> start page) + aHelpURL = CreateHelpURL( OUString(), aHelpModuleName ); + } + } + break; + } + } + + if ( comphelper::LibreOfficeKit::isActive() ) + { + impl_showOnlineHelp(aHelpURL, pWidget); + return true; + } +#ifdef MACOSX + if (@available(macOS 10.14, *)) { + // Workaround: Safari sandboxing prevents it from accessing files in the LibreOffice.app folder + // force online-help instead if Safari is default browser. + CFURLRef pBrowser = LSCopyDefaultApplicationURLForURL( + CFURLCreateWithString( + kCFAllocatorDefault, + static_cast<CFStringRef>(@"https://www.libreoffice.org"), + nullptr), + kLSRolesAll, nullptr); + if([static_cast<NSString*>(CFURLGetString(pBrowser)) hasSuffix:@"/Applications/Safari.app/"]) { + impl_showOnlineHelp(aHelpURL, pWidget); + return true; + } + } +#endif + + // If the HTML or no help is installed, but aHelpURL nevertheless references valid help content, + // that implies that help content belongs to an extension (and thus would not be available + // in neither the offline nor online HTML help); in that case, fall through to the "old-help to + // display" code below: + if (SfxContentHelper::IsHelpErrorDocument(aHelpURL)) + { + if ( impl_hasHTMLHelpInstalled() && impl_showOfflineHelp(aHelpURL, pWidget) ) + { + return true; + } + + if ( !impl_hasHelpInstalled() ) + { + bool bShowOfflineHelpPopUp = officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::get(); + + TopLevelWindowLocker aBusy; + + if(bShowOfflineHelpPopUp) + { + aBusy.incBusy(pWidget); + HelpManualMessage aQueryBox(pWidget); + short OnlineHelpBox = aQueryBox.run(); + bShowOfflineHelpPopUp = OnlineHelpBox != RET_OK; + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Help::BuiltInHelpNotInstalledPopUp::set(aQueryBox.GetOfflineHelpPopUp(), xChanges); + xChanges->commit(); + aBusy.decBusy(); + } + if(!bShowOfflineHelpPopUp) + { + if ( impl_showOnlineHelp(aHelpURL, pWidget) ) + return true; + else + { + aBusy.incBusy(pWidget); + NoHelpErrorBox aErrBox(pWidget); + aErrBox.run(); + aBusy.decBusy(); + return false; + } + } + else + { + return false; + } + + } + } + + // old-help to display + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + + // check if help window is still open + // If not, create a new one and return access directly to the internal sub frame showing the help content + // search must be done here; search one desktop level could return an arbitrary frame + Reference< XFrame2 > xHelp( + xDesktop->findFrame( "OFFICE_HELP_TASK", FrameSearchFlag::CHILDREN), + UNO_QUERY); + Reference< XFrame > xHelpContent = xDesktop->findFrame( + "OFFICE_HELP", + FrameSearchFlag::CHILDREN); + + SfxHelpWindow_Impl* pHelpWindow = nullptr; + if (!xHelp.is()) + pHelpWindow = impl_createHelp(xHelp, xHelpContent); + else + pHelpWindow = static_cast<SfxHelpWindow_Impl*>(VCLUnoHelper::GetWindow(xHelp->getComponentWindow())); + if (!xHelp.is() || !xHelpContent.is() || !pHelpWindow) + return false; + + SAL_INFO("sfx.appl", "HelpId = " << aHelpURL); + + pHelpWindow->SetHelpURL( aHelpURL ); + pHelpWindow->loadHelpContent(aHelpURL); + if (!rKeyword.isEmpty()) + pHelpWindow->OpenKeyword( rKeyword ); + + Reference < css::awt::XTopWindow > xTopWindow( xHelp->getContainerWindow(), UNO_QUERY ); + if ( xTopWindow.is() ) + xTopWindow->toFront(); + + return true; +} + +OUString SfxHelp::CreateHelpURL(const OUString& aCommandURL, const OUString& rModuleName) +{ + SfxHelp* pHelp = static_cast< SfxHelp* >(Application::GetHelp()); + return pHelp ? SfxHelp::CreateHelpURL_Impl( aCommandURL, rModuleName ) : OUString(); +} + +OUString SfxHelp::GetDefaultHelpModule() +{ + return getDefaultModule_Impl(); +} + +OUString SfxHelp::GetCurrentModuleIdentifier() +{ + return getCurrentModuleIdentifier_Impl(); +} + +bool SfxHelp::IsHelpInstalled() +{ + return impl_hasHelpInstalled(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/sfxpicklist.cxx b/sfx2/source/appl/sfxpicklist.cxx new file mode 100644 index 0000000000..64ebcc1a34 --- /dev/null +++ b/sfx2/source/appl/sfxpicklist.cxx @@ -0,0 +1,227 @@ +/* -*- 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 <comphelper/lok.hxx> +#include <comphelper/base64.hxx> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <unotools/historyoptions.hxx> +#include <unotools/useroptions.hxx> +#include <tools/datetime.hxx> +#include <tools/urlobj.hxx> +#include <svl/inethist.hxx> +#include <vcl/filter/PngImageWriter.hxx> +#include <vcl/svapp.hxx> +#include <officecfg/Office/Common.hxx> + + +#include <sfx2/app.hxx> +#include <sfxpicklist.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/event.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/docfile.hxx> +#include <openurlhint.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/viewfrm.hxx> + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; + +class SfxPickListImpl : public SfxListener +{ + /** + * Adds the given document to the pick list (recent documents) if it satisfies + certain requirements, e.g. being writable. Check implementation for requirement + details. + */ + static void AddDocumentToPickList(const SfxObjectShell* pDocShell, bool bNoThumbnail = false); + +public: + SfxPickListImpl(SfxApplication& rApp); + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; +}; + +void SfxPickListImpl::AddDocumentToPickList(const SfxObjectShell* pDocSh, bool bNoThumbnail) +{ + if (pDocSh->IsAvoidRecentDocs() || comphelper::LibreOfficeKit::isActive()) + return; + + SfxMedium *pMed = pDocSh->GetMedium(); + if( !pMed ) + return; + + // Unnamed Documents and embedded-Documents not in Picklist + if ( !pDocSh->HasName() || + SfxObjectCreateMode::STANDARD != pDocSh->GetCreateMode() ) + return; + + // Help not in History + INetURLObject aURL( pDocSh->IsDocShared() ? pDocSh->GetSharedFileURL() : pMed->GetOrigURL() ); + if ( aURL.GetProtocol() == INetProtocol::VndSunStarHelp ) + return; + + // add no document that forbids this + if ( !pMed->IsUpdatePickList() ) + return; + + // ignore hidden documents + if ( !SfxViewFrame::GetFirst( pDocSh ) ) + return; + + OUString aTitle = pDocSh->GetTitle(SFX_TITLE_PICKLIST); + OUString aFilter; + std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter(); + if ( pFilter ) + aFilter = pFilter->GetFilterName(); + + std::optional<OUString> aThumbnail; + + // generate the thumbnail + //fdo#74834: only generate thumbnail for history if the corresponding option is not disabled in the configuration + if (!bNoThumbnail && !pDocSh->IsModified() && !Application::IsHeadlessModeEnabled() && + officecfg::Office::Common::History::RecentDocsThumbnail::get()) + { + // not modified => the document matches what is in the shell + const SfxUnoAnyItem* pEncryptionDataItem = pMed->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false); + if ( pEncryptionDataItem ) + { + // encrypted document, will show a generic document icon instead + aThumbnail = OUString(); + } + else + { + BitmapEx aResultBitmap = pDocSh->GetPreviewBitmap(); + if (!aResultBitmap.IsEmpty()) + { + SvMemoryStream aStream(65535, 65535); + vcl::PngImageWriter aWriter(aStream); + if (aWriter.write(aResultBitmap)) + { + Sequence<sal_Int8> aSequence(static_cast<const sal_Int8*>(aStream.GetData()), aStream.Tell()); + OUStringBuffer aBuffer; + ::comphelper::Base64::encode(aBuffer, aSequence); + aThumbnail = aBuffer.makeStringAndClear(); + } + } + } + } + ::std::optional<bool> const oIsReadOnly(pMed->IsOriginallyLoadedReadOnly()); + + // add to svtool history options + SvtHistoryOptions::AppendItem( EHistoryType::PickList, + aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ), + aFilter, + aTitle, + aThumbnail, + oIsReadOnly); + + if ( aURL.GetProtocol() == INetProtocol::File ) + Application::AddToRecentDocumentList( aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ), + pFilter ? pFilter->GetMimeType() : OUString(), + pFilter ? pFilter->GetServiceName() : OUString() ); +} + +SfxPickList::SfxPickList(SfxApplication& rApp) + : mxImpl(new SfxPickListImpl(rApp)) +{ +} + +SfxPickList::~SfxPickList() +{ +} + +SfxPickListImpl::SfxPickListImpl(SfxApplication& rApp) +{ + StartListening(rApp); +} + +void SfxPickListImpl::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + const SfxOpenUrlHint* pOpenUrlHint = dynamic_cast<const SfxOpenUrlHint*>(&rHint); + if ( pOpenUrlHint ) + { + INetURLHistory::GetOrCreate()->PutUrl( INetURLObject( pOpenUrlHint->GetDocumentURL() )); + } + + if (rHint.GetId() != SfxHintId::ThisIsAnSfxEventHint) + return; + + const SfxEventHint& rEventHint = static_cast<const SfxEventHint&>(rHint); + // only ObjectShell-related events with media interest + SfxObjectShell* pDocSh = rEventHint.GetObjShell(); + if( !pDocSh ) + return; + + switch (rEventHint.GetEventId()) + { + case SfxEventHintId::CreateDoc: + { + bool bAllowModif = pDocSh->IsEnableSetModified(); + if ( bAllowModif ) + pDocSh->EnableSetModified( false ); + + using namespace ::com::sun::star; + uno::Reference<document::XDocumentProperties> xDocProps( + pDocSh->getDocProperties()); + if (xDocProps.is()) { + xDocProps->setAuthor( SvtUserOptions().GetFullName() ); + ::DateTime now( ::DateTime::SYSTEM ); + xDocProps->setCreationDate( now.GetUNODateTime() ); + } + + if ( bAllowModif ) + pDocSh->EnableSetModified( bAllowModif ); + } + break; + + case SfxEventHintId::OpenDoc: + case SfxEventHintId::SaveDocDone: + case SfxEventHintId::SaveAsDocDone: + case SfxEventHintId::SaveToDocDone: + case SfxEventHintId::CloseDoc: + { + const bool bNoThumbnail = rEventHint.GetEventId() == SfxEventHintId::CloseDoc; + AddDocumentToPickList(pDocSh, bNoThumbnail); + } + break; + + case SfxEventHintId::SaveAsDoc: + { + SfxMedium *pMedium = pDocSh->GetMedium(); + if (!pMedium) + return; + + // We're starting a "Save As". Add the current document (if it's + // not a "new" document) to the "Recent Documents" list before we + // switch to the new path. + // If the current document is new, path will be empty. + OUString path = pMedium->GetOrigURL(); + if (!path.isEmpty()) + { + AddDocumentToPickList(pDocSh); + } + } + break; + default: break; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/shutdownicon.cxx b/sfx2/source/appl/shutdownicon.cxx new file mode 100644 index 0000000000..fca7e56b3f --- /dev/null +++ b/sfx2/source/appl/shutdownicon.cxx @@ -0,0 +1,684 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include "shutdownicon.hxx" +#include <sfx2/strings.hrc> +#include <sfx2/app.hxx> +#include <svtools/imagemgr.hxx> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/TerminationVetoException.hpp> +#include <com/sun/star/frame/XDispatchResultListener.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/ControlActions.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/extract.hxx> +#include <officecfg/Office/Common.hxx> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <osl/file.hxx> +#include <osl/module.hxx> +#include <rtl/ref.hxx> +#include <utility> +#include <vcl/svapp.hxx> + +#include <sfx2/sfxresid.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::sfx2; + +class SfxNotificationListener_Impl : public cppu::WeakImplHelper< XDispatchResultListener > +{ +public: + virtual void SAL_CALL dispatchFinished( const DispatchResultEvent& aEvent ) override; + virtual void SAL_CALL disposing( const EventObject& aEvent ) override; +}; + +void SAL_CALL SfxNotificationListener_Impl::dispatchFinished( const DispatchResultEvent& ) +{ + ShutdownIcon::LeaveModalMode(); +} + +void SAL_CALL SfxNotificationListener_Impl::disposing( const EventObject& ) +{ +} + +OUString SAL_CALL ShutdownIcon::getImplementationName() +{ + return "com.sun.star.comp.desktop.QuickstartWrapper"; +} + +sal_Bool SAL_CALL ShutdownIcon::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL ShutdownIcon::getSupportedServiceNames() +{ + return { "com.sun.star.office.Quickstart" }; +} + +bool ShutdownIcon::bModalMode = false; +rtl::Reference<ShutdownIcon> ShutdownIcon::pShutdownIcon; + +void ShutdownIcon::initSystray() +{ + if (m_bInitialized) + return; + m_bInitialized = true; + +#ifdef ENABLE_QUICKSTART_APPLET +# ifdef _WIN32 + win32_init_sys_tray(); +# elif defined MACOSX + aqua_init_systray(); +# endif // MACOSX +#endif // ENABLE_QUICKSTART_APPLET +} + +void ShutdownIcon::deInitSystray() +{ + if (!m_bInitialized) + return; + +#ifdef ENABLE_QUICKSTART_APPLET +# ifdef _WIN32 + win32_shutdown_sys_tray(); +# elif defined MACOSX + aqua_shutdown_systray(); +# endif // MACOSX +#endif // ENABLE_QUICKSTART_APPLET + + m_bVeto = false; + + m_pFileDlg.reset(); + m_bInitialized = false; +} + +static bool UseSystemFileDialog() +{ + return !Application::IsHeadlessModeEnabled() && officecfg::Office::Common::Misc::UseSystemFileDialog::get(); +} + +ShutdownIcon::ShutdownIcon( css::uno::Reference< XComponentContext > xContext ) : + m_bVeto ( false ), + m_bListenForTermination ( false ), + m_bSystemDialogs(UseSystemFileDialog()), + m_xContext(std::move( xContext )), + m_bInitialized( false ) +{ +} + +ShutdownIcon::~ShutdownIcon() +{ + deInitSystray(); +} + + +void ShutdownIcon::OpenURL( const OUString& aURL, const OUString& rTarget, const Sequence< PropertyValue >& aArgs ) +{ + if ( !getInstance() || !getInstance()->m_xDesktop.is() ) + return; + + css::uno::Reference < XDispatchProvider > xDispatchProvider = getInstance()->m_xDesktop; + if ( !xDispatchProvider.is() ) + return; + + css::util::URL aDispatchURL; + aDispatchURL.Complete = aURL; + + css::uno::Reference< util::XURLTransformer > xURLTransformer( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + try + { + css::uno::Reference< css::frame::XDispatch > xDispatch; + + xURLTransformer->parseStrict( aDispatchURL ); + xDispatch = xDispatchProvider->queryDispatch( aDispatchURL, rTarget, 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( aDispatchURL, aArgs ); + } + catch ( css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + { + } +} + + +void ShutdownIcon::FileOpen() +{ + if ( getInstance() && getInstance()->m_xDesktop.is() ) + { + ::SolarMutexGuard aGuard; + EnterModalMode(); + getInstance()->StartFileDialog(); + } +} + + +void ShutdownIcon::FromTemplate() +{ + if ( !getInstance() || !getInstance()->m_xDesktop.is() ) + return; + + css::uno::Reference < css::frame::XFramesSupplier > xDesktop = getInstance()->m_xDesktop; + css::uno::Reference < css::frame::XFrame > xFrame( xDesktop->getActiveFrame() ); + if ( !xFrame.is() ) + xFrame = xDesktop; + + URL aTargetURL; + aTargetURL.Complete = ".uno:NewDoc"; + css::uno::Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict( aTargetURL ); + + css::uno::Reference < css::frame::XDispatchProvider > xProv( xFrame, UNO_QUERY ); + css::uno::Reference < css::frame::XDispatch > xDisp; + if ( xProv.is() ) + { + xDisp = xProv->queryDispatch( aTargetURL, "_self", 0 ); + } + if ( !xDisp.is() ) + return; + + Sequence<PropertyValue> aArgs { comphelper::makePropertyValue("Referer", OUString("private:user")) }; + css::uno::Reference< css::frame::XNotifyingDispatch > xNotifier(xDisp, UNO_QUERY); + if (xNotifier.is()) + { + EnterModalMode(); + xNotifier->dispatchWithNotification(aTargetURL, aArgs, new SfxNotificationListener_Impl); + } + else + xDisp->dispatch( aTargetURL, aArgs ); +} + +OUString ShutdownIcon::GetUrlDescription( std::u16string_view aUrl ) +{ + return SvFileInformationManager::GetDescription( INetURLObject( aUrl ) ); +} + +void ShutdownIcon::StartFileDialog() +{ + ::SolarMutexGuard aGuard; + + bool bDirty = m_bSystemDialogs != UseSystemFileDialog(); + + if ( m_pFileDlg && bDirty ) + { + // Destroy instance as changing the system file dialog setting + // forces us to create a new FileDialogHelper instance! + m_pFileDlg.reset(); + } + + if ( !m_pFileDlg ) + m_pFileDlg.reset( new FileDialogHelper( + ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION, + FileDialogFlags::MultiSelection, OUString(), SfxFilterFlags::NONE, SfxFilterFlags::NONE, nullptr ) ); + m_pFileDlg->StartExecuteModal( LINK( this, ShutdownIcon, DialogClosedHdl_Impl ) ); +} + +IMPL_LINK( ShutdownIcon, DialogClosedHdl_Impl, FileDialogHelper*, /*unused*/, void ) +{ + DBG_ASSERT( m_pFileDlg, "ShutdownIcon, DialogClosedHdl_Impl(): no file dialog" ); + + // use constructor for filling up filters automatically! + if ( ERRCODE_NONE == m_pFileDlg->GetError() ) + { + css::uno::Reference< XFilePicker3 > xPicker = m_pFileDlg->GetFilePicker(); + + try + { + + if ( xPicker.is() ) + { + + css::uno::Reference < XFilePickerControlAccess > xPickerControls ( xPicker, UNO_QUERY ); + + Sequence< OUString > sFiles = xPicker->getSelectedFiles(); + int nFiles = sFiles.getLength(); + + css::uno::Reference < css::task::XInteractionHandler2 > xInteraction( + task::InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), nullptr) ); + + int nArgs=3; + Sequence< PropertyValue > aArgs{ + comphelper::makePropertyValue("InteractionHandler", xInteraction), + comphelper::makePropertyValue("MacroExecutionMode", sal_Int16(css::document::MacroExecMode::USE_CONFIG)), + comphelper::makePropertyValue("UpdateDocMode", sal_Int16(css::document::UpdateDocMode::ACCORDING_TO_CONFIG)) + }; + + // use the filedlghelper to get the current filter name, + // because it removes the extensions before you get the filter name. + OUString aFilterName( m_pFileDlg->GetCurrentFilter() ); + + if ( xPickerControls.is() ) + { + + // Set readonly flag + + bool bReadOnly = false; + + + xPickerControls->getValue( ExtendedFilePickerElementIds::CHECKBOX_READONLY, 0 ) >>= bReadOnly; + + // Only set property if readonly is set to TRUE + + if ( bReadOnly ) + { + aArgs.realloc( ++nArgs ); + auto pArgs = aArgs.getArray(); + pArgs[nArgs-1].Name = "ReadOnly"; + pArgs[nArgs-1].Value <<= bReadOnly; + } + + // Get version string + + sal_Int32 iVersion = -1; + + xPickerControls->getValue( ExtendedFilePickerElementIds::LISTBOX_VERSION, ControlActions::GET_SELECTED_ITEM_INDEX ) >>= iVersion; + + if ( iVersion >= 0 ) + { + sal_Int16 uVersion = static_cast<sal_Int16>(iVersion); + + aArgs.realloc( ++nArgs ); + auto pArgs = aArgs.getArray(); + pArgs[nArgs-1].Name = "Version"; + pArgs[nArgs-1].Value <<= uVersion; + } + + // Retrieve the current filter + + if ( aFilterName.isEmpty() ) + xPickerControls->getValue( CommonFilePickerElementIds::LISTBOX_FILTER, ControlActions::GET_SELECTED_ITEM ) >>= aFilterName; + + } + + + // Convert UI filter name to internal filter name + + if ( !aFilterName.isEmpty() ) + { + std::shared_ptr<const SfxFilter> pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4UIName( aFilterName, SfxFilterFlags::NONE, SfxFilterFlags::NOTINFILEDLG ); + + if ( pFilter ) + { + aFilterName = pFilter->GetFilterName(); + + if ( !aFilterName.isEmpty() ) + { + aArgs.realloc( ++nArgs ); + auto pArgs = aArgs.getArray(); + pArgs[nArgs-1].Name = "FilterName"; + pArgs[nArgs-1].Value <<= aFilterName; + } + } + } + + if ( 1 == nFiles ) + OpenURL( sFiles[0], "_default", aArgs ); + else + { + OUString aBaseDirURL = sFiles[0]; + if ( !aBaseDirURL.isEmpty() && !aBaseDirURL.endsWith("/") ) + aBaseDirURL += "/"; + + int iFiles; + for ( iFiles = 1; iFiles < nFiles; iFiles++ ) + { + OUString aURL = aBaseDirURL + sFiles[iFiles]; + OpenURL( aURL, "_default", aArgs ); + } + } + } + } + catch ( ... ) + { + } + } + +#ifdef _WIN32 + // Destroy dialog to prevent problems with custom controls + // This fix is dependent on the dialog settings. Destroying the dialog here will + // crash the non-native dialog implementation! Therefore make this dependent on + // the settings. + if (UseSystemFileDialog()) + { + m_pFileDlg.reset(); + } +#endif + + LeaveModalMode(); +} + + +void ShutdownIcon::addTerminateListener() +{ + ShutdownIcon* pInst = getInstance(); + if ( ! pInst) + return; + + if (pInst->m_bListenForTermination) + return; + + css::uno::Reference< XDesktop2 > xDesktop = pInst->m_xDesktop; + if ( ! xDesktop.is()) + return; + + xDesktop->addTerminateListener( pInst ); + pInst->m_bListenForTermination = true; +} + + +void ShutdownIcon::terminateDesktop() +{ + ShutdownIcon* pInst = getInstance(); + if ( ! pInst) + return; + + css::uno::Reference< XDesktop2 > xDesktop = pInst->m_xDesktop; + if ( ! xDesktop.is()) + return; + + // always remove ourselves as listener + pInst->m_bListenForTermination = true; + xDesktop->removeTerminateListener( pInst ); + + // terminate desktop only if no tasks exist + css::uno::Reference< XIndexAccess > xTasks = xDesktop->getFrames(); + if( xTasks.is() && xTasks->getCount() < 1 ) + Application::Quit(); + + // remove the instance pointer + ShutdownIcon::pShutdownIcon = nullptr; +} + + +ShutdownIcon* ShutdownIcon::getInstance() +{ + OSL_ASSERT( pShutdownIcon ); + return pShutdownIcon.get(); +} + + +ShutdownIcon* ShutdownIcon::createInstance() +{ + if (pShutdownIcon) + return pShutdownIcon.get(); + + try { + rtl::Reference<ShutdownIcon> pIcon(new ShutdownIcon( comphelper::getProcessComponentContext() )); + pIcon->init (); + pShutdownIcon = pIcon; + } catch (...) { + } + + return pShutdownIcon.get(); +} + +void ShutdownIcon::init() +{ + css::uno::Reference < XDesktop2 > xDesktop = Desktop::create( m_xContext ); + std::unique_lock aGuard(m_aMutex); + m_xDesktop = xDesktop; +} + + +void ShutdownIcon::disposing(std::unique_lock<std::mutex>&) +{ + m_xContext.clear(); + m_xDesktop.clear(); + + deInitSystray(); +} + + +// XEventListener +void SAL_CALL ShutdownIcon::disposing( const css::lang::EventObject& ) +{ +} + + +// XTerminateListener +void SAL_CALL ShutdownIcon::queryTermination( const css::lang::EventObject& ) +{ + SAL_INFO("sfx.appl", "ShutdownIcon::queryTermination: veto is " << m_bVeto); + std::unique_lock aGuard( m_aMutex ); + + if ( m_bVeto ) + throw css::frame::TerminationVetoException(); +} + + +void SAL_CALL ShutdownIcon::notifyTermination( const css::lang::EventObject& ) +{ +} + + +void SAL_CALL ShutdownIcon::initialize( const css::uno::Sequence< css::uno::Any>& aArguments ) +{ + std::unique_lock aGuard( m_aMutex ); + + // third argument only sets veto, everything else will be ignored! + if (aArguments.getLength() > 2) + { + bool bVeto = ::cppu::any2bool(aArguments[2]); + m_bVeto = bVeto; + return; + } + + if ( aArguments.getLength() > 0 ) + { + if ( !ShutdownIcon::pShutdownIcon ) + { + try + { + bool bQuickstart = ::cppu::any2bool( aArguments[0] ); + if( !bQuickstart && !GetAutostart() ) + return; + aGuard.unlock(); + init (); + aGuard.lock(); + if ( !m_xDesktop.is() ) + return; + + /* Create a sub-classed instance - foo */ + ShutdownIcon::pShutdownIcon = this; + initSystray(); + } + catch(const css::lang::IllegalArgumentException&) + { + } + } + } + if ( aArguments.getLength() > 1 ) + { + bool bAutostart = ::cppu::any2bool( aArguments[1] ); + if (bAutostart && !GetAutostart()) + SetAutostart( true ); + if (!bAutostart && GetAutostart()) + SetAutostart( false ); + } + +} + + +void ShutdownIcon::EnterModalMode() +{ + bModalMode = true; +} + + +void ShutdownIcon::LeaveModalMode() +{ + bModalMode = false; +} + +#ifdef _WIN32 +// defined in shutdowniconw32.cxx +#elif defined MACOSX +// defined in shutdowniconaqua.cxx +#else +bool ShutdownIcon::IsQuickstarterInstalled() +{ + return false; +} +#endif + + +#ifdef ENABLE_QUICKSTART_APPLET +#ifdef _WIN32 +OUString ShutdownIcon::getShortcutName() +{ + return GetAutostartFolderNameW32() + "\\" + SfxResId(STR_QUICKSTART_LNKNAME) + ".lnk"; +} +#endif // _WIN32 +#endif + +bool ShutdownIcon::GetAutostart( ) +{ +#if defined MACOSX + return true; +#elif defined ENABLE_QUICKSTART_APPLET + bool bRet = false; + OUString aShortcut( getShortcutName() ); + OUString aShortcutUrl; + osl::File::getFileURLFromSystemPath( aShortcut, aShortcutUrl ); + osl::File f( aShortcutUrl ); + osl::File::RC error = f.open( osl_File_OpenFlag_Read ); + if( error == osl::File::E_None ) + { + f.close(); + bRet = true; + } + return bRet; +#else // ENABLE_QUICKSTART_APPLET + return false; +#endif +} + +void ShutdownIcon::SetAutostart( bool bActivate ) +{ +#ifdef ENABLE_QUICKSTART_APPLET +#ifndef MACOSX + OUString aShortcut( getShortcutName() ); +#endif + + if( bActivate && IsQuickstarterInstalled() ) + { +#ifdef _WIN32 + EnableAutostartW32( aShortcut ); +#endif + } + else + { +#ifndef MACOSX + OUString aShortcutUrl; + ::osl::File::getFileURLFromSystemPath( aShortcut, aShortcutUrl ); + ::osl::File::remove( aShortcutUrl ); +#endif + } +#else + (void)bActivate; // unused variable +#endif // ENABLE_QUICKSTART_APPLET +} + +const ::sal_Int32 PROPHANDLE_TERMINATEVETOSTATE = 0; + +// XFastPropertySet +void SAL_CALL ShutdownIcon::setFastPropertyValue( ::sal_Int32 nHandle, + const css::uno::Any& aValue ) +{ + switch(nHandle) + { + case PROPHANDLE_TERMINATEVETOSTATE : + { + // use new value in case it's a valid information only + bool bState( false ); + if (! (aValue >>= bState)) + return; + + m_bVeto = bState; + if (m_bVeto && ! m_bListenForTermination) + addTerminateListener(); + } + break; + + default : + throw css::beans::UnknownPropertyException(OUString::number(nHandle)); + } +} + +// XFastPropertySet +css::uno::Any SAL_CALL ShutdownIcon::getFastPropertyValue( ::sal_Int32 nHandle ) +{ + css::uno::Any aValue; + switch(nHandle) + { + case PROPHANDLE_TERMINATEVETOSTATE : + { + bool bState = (m_bListenForTermination && m_bVeto); + aValue <<= bState; + } + break; + + default : + throw css::beans::UnknownPropertyException(OUString::number(nHandle)); + } + + return aValue; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_desktop_QuickstartWrapper_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ShutdownIcon(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/shutdownicon.hxx b/sfx2/source/appl/shutdownicon.hxx new file mode 100644 index 0000000000..535a24b4df --- /dev/null +++ b/sfx2/source/appl/shutdownicon.hxx @@ -0,0 +1,158 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SFX2_SOURCE_APPL_SHUTDOWNICON_HXX +#define INCLUDED_SFX2_SOURCE_APPL_SHUTDOWNICON_HXX + +#include <com/sun/star/frame/XTerminateListener.hpp> +#include <com/sun/star/frame/XDesktop2.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <rtl/ustring.hxx> +#include <rtl/ref.hxx> +#include <comphelper/compbase.hxx> +#include <tools/link.hxx> +#include <memory> + +extern "C" { + +void SAL_DLLPUBLIC_EXPORT plugin_init_sys_tray(); +void SAL_DLLPUBLIC_EXPORT plugin_shutdown_sys_tray(); + +} + +namespace sfx2 +{ + class FileDialogHelper; +} + +typedef comphelper::WeakComponentImplHelper< + css::lang::XInitialization, + css::frame::XTerminateListener, + css::lang::XServiceInfo, + css::beans::XFastPropertySet > ShutdownIconServiceBase; + +inline constexpr OUString WRITER_URL = u"private:factory/swriter"_ustr; +inline constexpr OUString CALC_URL = u"private:factory/scalc"_ustr; +inline constexpr OUString IMPRESS_URL = u"private:factory/simpress"_ustr; +inline constexpr OUString IMPRESS_WIZARD_URL = u"private:factory/simpress?slot=6686"_ustr; +inline constexpr OUString DRAW_URL = u"private:factory/sdraw"_ustr; +inline constexpr OUString MATH_URL = u"private:factory/smath"_ustr; +inline constexpr OUString BASE_URL = u"private:factory/sdatabase?Interactive"_ustr; +inline constexpr OUString STARTMODULE_URL = u".uno:ShowStartModule"_ustr; + +class ShutdownIcon : public ShutdownIconServiceBase +{ + bool m_bVeto; + bool m_bListenForTermination; + bool m_bSystemDialogs; + std::unique_ptr<sfx2::FileDialogHelper> m_pFileDlg; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XDesktop2 > m_xDesktop; + + static rtl::Reference<ShutdownIcon> pShutdownIcon; // one instance + + bool m_bInitialized; + void initSystray(); + void deInitSystray(); + + static void EnterModalMode(); + static void LeaveModalMode(); + static OUString getShortcutName(); + + friend class SfxNotificationListener_Impl; + + public: + explicit ShutdownIcon( css::uno::Reference< css::uno::XComponentContext > xContext ); + + virtual ~ShutdownIcon() override; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + static ShutdownIcon* getInstance(); + static ShutdownIcon* createInstance(); + + static void terminateDesktop(); + static void addTerminateListener(); + + static void FileOpen(); + static void OpenURL( const OUString& aURL, const OUString& rTarget, const css::uno::Sequence< css::beans::PropertyValue >& = + css::uno::Sequence< css::beans::PropertyValue >( 0 ) ); + static void FromTemplate(); + + static void SetAutostart( bool bActivate ); + static bool GetAutostart(); + static bool bModalMode; + + /// @throws css::uno::Exception + void init(); + + static OUString GetUrlDescription( std::u16string_view aUrl ); + + void SetVeto( bool bVeto ) { m_bVeto = bVeto;} + + void StartFileDialog(); + DECL_LINK(DialogClosedHdl_Impl, sfx2::FileDialogHelper*, void); + + static bool IsQuickstarterInstalled(); + + // Component Helper - force override + virtual void disposing(std::unique_lock<std::mutex>&) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XTerminateListener + virtual void SAL_CALL queryTermination( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL notifyTermination( const css::lang::EventObject& aEvent ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XFastPropertySet + virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, + const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override; + +#ifdef _WIN32 + static void EnableAutostartW32( const OUString &aShortcutName ); + static OUString GetAutostartFolderNameW32(); +#endif +}; + +extern "C" { +# ifdef _WIN32 + // builtin win32 systray + void win32_init_sys_tray(); + void win32_shutdown_sys_tray(); +# elif defined MACOSX + void aqua_init_systray(); + void aqua_shutdown_systray(); +# endif +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/shutdowniconaqua.mm b/sfx2/source/appl/shutdowniconaqua.mm new file mode 100644 index 0000000000..8cb9b6d806 --- /dev/null +++ b/sfx2/source/appl/shutdowniconaqua.mm @@ -0,0 +1,470 @@ +/* -*- 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 <unotools/moduleoptions.hxx> +#include <unotools/dynamicmenuoptions.hxx> +#include <unotools/historyoptions.hxx> +#include <rtl/ustring.hxx> +#include <tools/urlobj.hxx> +#include <osl/file.h> +#include <osl/diagnose.h> +#include <comphelper/sequenceashashmap.hxx> +#include <sfx2/app.hxx> +#include <sal/macros.h> +#include <sfx2/sfxresid.hxx> +#include <sfx2/strings.hrc> +#include <vcl/svapp.hxx> +#include "shutdownicon.hxx" + +#include <com/sun/star/util/XStringWidth.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <set> +#include <vector> + +#include <premac.h> +#include <objc/objc-runtime.h> +#include <Cocoa/Cocoa.h> +#include <postmac.h> + +#define MI_OPEN 1 +#define MI_WRITER 2 +#define MI_CALC 3 +#define MI_IMPRESS 4 +#define MI_DRAW 5 +#define MI_BASE 6 +#define MI_MATH 7 +#define MI_TEMPLATE 8 +#define MI_STARTMODULE 9 + +@interface QSMenuExecute : NSObject +{ +} +-(void)executeMenuItem: (NSMenuItem*)pItem; +-(void)dockIconClicked: (NSObject*)pSender; +@end + +@implementation QSMenuExecute +-(void)executeMenuItem: (NSMenuItem*)pItem +{ + switch( [pItem tag] ) + { + case MI_OPEN: + ShutdownIcon::FileOpen(); + break; + case MI_WRITER: + ShutdownIcon::OpenURL( WRITER_URL, "_default" ); + break; + case MI_CALC: + ShutdownIcon::OpenURL( CALC_URL, "_default" ); + break; + case MI_IMPRESS: + ShutdownIcon::OpenURL( IMPRESS_URL, "_default" ); + break; + case MI_DRAW: + ShutdownIcon::OpenURL( DRAW_URL, "_default" ); + break; + case MI_BASE: + ShutdownIcon::OpenURL( BASE_URL, "_default" ); + break; + case MI_MATH: + ShutdownIcon::OpenURL( MATH_URL, "_default" ); + break; + case MI_TEMPLATE: + ShutdownIcon::FromTemplate(); + break; + case MI_STARTMODULE: + ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); + break; + default: + break; + } +} + +-(void)dockIconClicked: (NSObject*)pSender +{ + (void)pSender; + // start module + ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); +} + +@end + +bool ShutdownIcon::IsQuickstarterInstalled() +{ + return true; +} + +static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil; +static QSMenuExecute* pExecute = nil; + +static std::set< OUString > aShortcuts; + +static NSString* getAutoreleasedString( const OUString& rStr ) +{ + return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease]; +} + +namespace { + +struct RecentMenuEntry +{ + OUString aURL; + OUString aFilter; + OUString aTitle; + OUString aPassword; +}; + +class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStringWidth > +{ + public: + RecentFilesStringLength() {} + + // XStringWidth + sal_Int32 SAL_CALL queryStringWidth( const OUString& aString ) override + { + return aString.getLength(); + } +}; + +} + +@interface RecentMenuDelegate : NSObject <NSMenuDelegate> +{ + std::vector< RecentMenuEntry >* m_pRecentFilesItems; +} +-(id)init; +-(void)dealloc; +-(void)menuNeedsUpdate:(NSMenu *)menu; +-(void)executeRecentEntry: (NSMenuItem*)item; +@end + +@implementation RecentMenuDelegate +-(id)init +{ + if( (self = [super init]) ) + { + m_pRecentFilesItems = new std::vector< RecentMenuEntry >(); + } + return self; +} + +-(void)dealloc +{ + delete m_pRecentFilesItems; + [super dealloc]; +} + +-(void)menuNeedsUpdate:(NSMenu *)menu +{ + // clear menu + int nItems = [menu numberOfItems]; + while( nItems -- ) + [menu removeItemAtIndex: 0]; + + // update recent item list + std::vector< SvtHistoryOptions::HistoryItem > aHistoryList( SvtHistoryOptions::GetList( EHistoryType::PickList ) ); + + int nPickListMenuItems = ( aHistoryList.size() > 99 ) ? 99 : aHistoryList.size(); + + m_pRecentFilesItems->clear(); + if( nPickListMenuItems > 0 ) + { + for ( int i = 0; i < nPickListMenuItems; i++ ) + { + const SvtHistoryOptions::HistoryItem & rPickListEntry = aHistoryList[i]; + RecentMenuEntry aRecentFile; + aRecentFile.aURL = rPickListEntry.sURL; + aRecentFile.aFilter = rPickListEntry.sFilter; + aRecentFile.aTitle = rPickListEntry.sTitle; + aRecentFile.aPassword = rPickListEntry.sPassword; + m_pRecentFilesItems->push_back( aRecentFile ); + } + } + + // insert new recent items + for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ ) + { + OUString aMenuTitle; + INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL ); + + if ( aURL.GetProtocol() == INetProtocol::File ) + { + // Do handle file URL differently => convert it to a system + // path and abbreviate it with a special function: + OUString aSystemPath( aURL.getFSysPath( FSysStyle::Detect ) ); + OUString aCompactedSystemPath; + + oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, nullptr ); + if ( !nError ) + aMenuTitle = aCompactedSystemPath; + else + aMenuTitle = aSystemPath; + } + else + { + // Use INetURLObject to abbreviate all other URLs + css::uno::Reference< css::util::XStringWidth > xStringLength( new RecentFilesStringLength() ); + aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DecodeMechanism::Unambiguous ); + } + + NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle ) + action: @selector(executeRecentEntry:) + keyEquivalent: @""]; + [pNewItem setTag: i]; + [pNewItem setTarget: self]; + [pNewItem setEnabled: YES]; + [menu addItem: pNewItem]; + [pNewItem autorelease]; + } +} + +-(void)executeRecentEntry: (NSMenuItem*)item +{ + sal_Int32 nIndex = [item tag]; + if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) ) + { + const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ]; + int NUM_OF_PICKLIST_ARGS = 3; + css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS ); + css::beans::PropertyValue* pArgsList = aArgsList.getArray(); + + pArgsList[0].Name = "Referer"; + pArgsList[0].Value <<= OUString( "private:user" ); + + // documents in the picklist will never be opened as templates + pArgsList[1].Name = "AsTemplate"; + pArgsList[1].Value <<= false; + + OUString aFilter( rRecentFile.aFilter ); + sal_Int32 nPos = aFilter.indexOf( '|' ); + if ( nPos >= 0 ) + { + OUString aFilterOptions; + + if ( nPos < ( aFilter.getLength() - 1 ) ) + aFilterOptions = aFilter.copy( nPos+1 ); + + pArgsList[2].Name = "FilterOptions"; + pArgsList[2].Value <<= aFilterOptions; + + aFilter = aFilter.copy( 0, nPos-1 ); + aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS ); + pArgsList = aArgsList.getArray(); + } + + pArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName"; + pArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter; + + ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList ); + } +} +@end + +static RecentMenuDelegate* pRecentDelegate = nil; + +static OUString getShortCut( const OUString& i_rTitle ) +{ + // create shortcut + OUString aKeyEquiv; + for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ ) + { + OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() ); + if( aShortcuts.find( aShortcut ) == aShortcuts.end() ) + { + aShortcuts.insert( aShortcut ); + aKeyEquiv = aShortcut; + break; + } + } + + return aKeyEquiv; +} + +static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv ) +{ + if( ! i_rTitle.getLength() ) + return; + + NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle ) + action: @selector(executeMenuItem:) + keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"") + ]; + [pItem setTag: i_nTag]; + [pItem setTarget: pExecute]; + [pItem setEnabled: YES]; + [i_pMenu addItem: pItem]; + + if( i_pDockMenu ) + { + // create a similar entry in the dock menu + pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle ) + action: @selector(executeMenuItem:) + keyEquivalent: @"" + ]; + [pItem setTag: i_nTag]; + [pItem setTarget: pExecute]; + [pItem setEnabled: YES]; + [i_pDockMenu addItem: pItem]; + } +} + +static void appendRecentMenu( NSMenu* i_pMenu, const OUString& i_rTitle ) +{ + if( ! pRecentDelegate ) + pRecentDelegate = [[RecentMenuDelegate alloc] init]; + + NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle ) + action: @selector(executeMenuItem:) + keyEquivalent: @"" + ]; + [pItem setEnabled: YES]; + NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ]; + + [pRecentMenu setDelegate: pRecentDelegate]; + + [pRecentMenu setAutoenablesItems: NO]; + [pItem setSubmenu: pRecentMenu]; +} + + +extern "C" +{ + +void aqua_init_systray() +{ + SolarMutexGuard aGuard; + + ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); + if( ! pShutdownIcon ) + return; + + // disable shutdown + pShutdownIcon->SetVeto( true ); + ShutdownIcon::addTerminateListener(); + + if( ! pDefMenu ) + { + if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] ) + { + aShortcuts.clear(); + + pExecute = [[QSMenuExecute alloc] init]; + pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""]; + pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""]; + NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )]; + [pMenu setAutoenablesItems: NO]; + NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )]; + [pDockMenu setAutoenablesItems: NO]; + + // collect the URLs of the entries in the File/New menu + SvtModuleOptions aModuleOptions; + std::set< OUString > aFileNewAppsAvailable; + std::vector < SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu ); + + for ( SvtDynMenuEntry const & newMenuProp : aNewMenu ) + { + if ( !newMenuProp.sURL.isEmpty() ) + aFileNewAppsAvailable.insert( newMenuProp.sURL ); + } + + // describe the menu entries for launching the applications + struct MenuEntryDescriptor + { + SvtModuleOptions::EModule eModuleIdentifier; + int nMenuTag; + OUString sURLDescription; + } static constexpr aMenuItems[] = + { + { SvtModuleOptions::EModule::WRITER, MI_WRITER, WRITER_URL }, + { SvtModuleOptions::EModule::CALC, MI_CALC, CALC_URL }, + { SvtModuleOptions::EModule::IMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL }, + { SvtModuleOptions::EModule::DRAW, MI_DRAW, DRAW_URL }, + { SvtModuleOptions::EModule::DATABASE, MI_BASE, BASE_URL }, + { SvtModuleOptions::EModule::MATH, MI_MATH, MATH_URL } + }; + + // insert entry for startcenter + if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) ) + { + appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" ); + if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] ) + [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute]; + else + OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" ); + + } + + // insert the menu entries for launching the applications + for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i ) + { + if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) ) + // the complete application is not even installed + continue; + + const OUString& sURL( aMenuItems[i].sURLDescription ); + + if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() ) + // the application is installed, but the entry has been configured to *not* appear in the File/New + // menu => also let not appear it in the quickstarter + continue; + + OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) ); + + appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv ); + } + + // insert the remaining menu entries + + // add recent menu + appendRecentMenu( pMenu, SfxResId(STR_QUICKSTART_RECENTDOC) ); + + OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) ); + OUString aKeyEquiv( getShortCut( aTitle ) ); + appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv ); + aTitle = SfxResId(STR_QUICKSTART_FILEOPEN); + aKeyEquiv = getShortCut( aTitle ); + appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv ); + + [pDefMenu setSubmenu: pMenu]; + [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu]; + + if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] ) + { + [pDockSubMenu setSubmenu: pDockMenu]; + // add the submenu + [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu]; + } + else + OSL_FAIL( "addDockMenuItem selector failed on NSApp" ); + } + else + OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" ); + } +} + +void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray() +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/shutdowniconw32.cxx b/sfx2/source/appl/shutdowniconw32.cxx new file mode 100644 index 0000000000..2fb7cd2b78 --- /dev/null +++ b/sfx2/source/appl/shutdowniconw32.cxx @@ -0,0 +1,803 @@ +/* + * 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 <sal/macros.h> +#include <sal/log.hxx> + +#include <unotools/moduleoptions.hxx> +#include <unotools/dynamicmenuoptions.hxx> + +#undef WB_LEFT +#undef WB_RIGHT + +#include "shutdownicon.hxx" +#include <sfx2/sfxresid.hxx> +#include <sfx2/strings.hrc> +#include <shlobj.h> +#include <objidl.h> +#include <osl/diagnose.h> +#include <osl/thread.h> +#include <systools/win32/qswin32.h> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/windowserrorstring.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <set> + + +#define EXECUTER_WINDOWCLASS L"SO Executer Class" +#define EXECUTER_WINDOWNAME L"SO Executer Window" + + +#define ID_QUICKSTART 1 +#define IDM_EXIT 2 +#define IDM_OPEN 3 +#define IDM_WRITER 4 +#define IDM_CALC 5 +#define IDM_IMPRESS 6 +#define IDM_DRAW 7 +#define IDM_BASE 8 +#define IDM_TEMPLATE 9 +#define IDM_MATH 12 +#define IDM_INSTALL 10 +#define IDM_STARTCENTER 14 + + +#define ICON_LO_DEFAULT 1 +#define ICON_TEXT_DOCUMENT 2 +#define ICON_SPREADSHEET_DOCUMENT 4 +#define ICON_DRAWING_DOCUMENT 6 +#define ICON_PRESENTATION_DOCUMENT 8 +#define ICON_TEMPLATE 11 +#define ICON_DATABASE_DOCUMENT 12 +#define ICON_MATH_DOCUMENT 13 +#define ICON_OPEN 5 // See index of open folder icon in shell32.dll + +#define SFX_TASKBAR_NOTIFICATION WM_USER+1 + +static HWND aListenerWindow = nullptr; +static HWND aExecuterWindow = nullptr; +static HMENU popupMenu = nullptr; + +static void OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis); +static void OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis); + +namespace { + +struct MYITEM +{ + OUString text; + OUString module; + UINT iconId; +}; + +} + +static void addMenuItem( HMENU hMenu, UINT id, UINT iconId, const OUString& text, int& pos, bool bOwnerdraw, const OUString& module ) +{ + MENUITEMINFOW mi = {}; + + mi.cbSize = sizeof( mi ); + if( id == static_cast<UINT>( -1 ) ) + { + mi.fMask=MIIM_FTYPE; + mi.fType=MFT_SEPARATOR; + } + else + { + if( bOwnerdraw ) + { + mi.fMask=MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_DATA; + mi.fType=MFT_OWNERDRAW; + + MYITEM *pMyItem = new MYITEM; + pMyItem->text = text; + pMyItem->iconId = iconId; + pMyItem->module = module; + mi.dwItemData = reinterpret_cast<ULONG_PTR>(pMyItem); + } + else + { + mi.fMask=MIIM_STRING | MIIM_STATE | MIIM_ID; + mi.dwTypeData = o3tl::toW( + const_cast<sal_Unicode *>(text.getStr())); + mi.cch = text.getLength(); + } + + mi.fState = MFS_ENABLED; + mi.wID = id; + if ( IDM_TEMPLATE == id ) + mi.fState |= MFS_DEFAULT; + } + + InsertMenuItemW( hMenu, pos++, TRUE, &mi ); +} + + +static HMENU createSystrayMenu( ) +{ + SvtModuleOptions aModuleOptions; + + HMENU hMenu = CreatePopupMenu(); + int pos=0; + + ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); + OSL_ENSURE( pShutdownIcon, "ShutdownIcon instance empty!"); + + if( !pShutdownIcon ) + return nullptr; + + // collect the URLs of the entries in the File/New menu + ::std::set< OUString > aFileNewAppsAvailable; + std::vector< SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu ); + for ( SvtDynMenuEntry const & newMenuProp : aNewMenu ) + { + if ( !newMenuProp.sURL.isEmpty() ) + aFileNewAppsAvailable.insert( newMenuProp.sURL ); + } + + // describe the menu entries for launching the applications + struct MenuEntryDescriptor + { + SvtModuleOptions::EModule eModuleIdentifier; + UINT nMenuItemID; + UINT nMenuIconID; + OUString sURLDescription; + } static constexpr aMenuItems[] = + { + { SvtModuleOptions::EModule::WRITER, IDM_WRITER, ICON_TEXT_DOCUMENT, WRITER_URL }, + { SvtModuleOptions::EModule::CALC, IDM_CALC, ICON_SPREADSHEET_DOCUMENT, CALC_URL }, + { SvtModuleOptions::EModule::IMPRESS, IDM_IMPRESS,ICON_PRESENTATION_DOCUMENT, IMPRESS_WIZARD_URL }, + { SvtModuleOptions::EModule::DRAW, IDM_DRAW, ICON_DRAWING_DOCUMENT, DRAW_URL }, + { SvtModuleOptions::EModule::DATABASE, IDM_BASE, ICON_DATABASE_DOCUMENT, BASE_URL }, + { SvtModuleOptions::EModule::MATH, IDM_MATH, ICON_MATH_DOCUMENT, MATH_URL }, + }; + + // insert the menu entries for launching the applications + for (const auto& [eModuleIdentifier, nMenuItemID, nMenuIconID, sURL] : aMenuItems) + { + if ( !aModuleOptions.IsModuleInstalled( eModuleIdentifier ) ) + // the complete application is not even installed + continue; + + if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() ) + // the application is installed, but the entry has been configured to *not* appear in the File/New + // menu => also let not appear it in the quickstarter + continue; + + addMenuItem( hMenu, nMenuItemID, nMenuIconID, + ShutdownIcon::GetUrlDescription( sURL ), pos, true, "" ); + } + + + // insert the remaining menu entries + addMenuItem( hMenu, IDM_TEMPLATE, ICON_TEMPLATE, + SfxResId( STR_QUICKSTART_FROMTEMPLATE ), pos, true, ""); + addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" ); + addMenuItem( hMenu, IDM_OPEN, ICON_OPEN, SfxResId(STR_QUICKSTART_FILEOPEN), pos, true, "SHELL32"); + addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" ); + addMenuItem( hMenu, IDM_INSTALL,0, SfxResId(STR_QUICKSTART_PRELAUNCH), pos, false, "" ); + addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" ); + addMenuItem( hMenu, IDM_EXIT, 0, SfxResId(STR_QUICKSTART_EXIT), pos, false, "" ); + + // indicate status of autostart folder + CheckMenuItem( hMenu, IDM_INSTALL, MF_BYCOMMAND | (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) ); + + return hMenu; +} + + +static void deleteSystrayMenu( HMENU hMenu ) +{ + if( !hMenu || !IsMenu( hMenu )) + return; + + MENUITEMINFOW mi = {}; + mi.cbSize = sizeof( mi ); + mi.fMask = MIIM_DATA; + + for (UINT pos = 0; GetMenuItemInfoW(hMenu, pos, true, &mi); ++pos) + { + if (MYITEM* pMyItem = reinterpret_cast<MYITEM*>(mi.dwItemData)) + delete pMyItem; + mi.fMask = MIIM_DATA; + } +} + + +static void addTaskbarIcon( HWND hWnd ) +{ + OUString strTip = SfxResId(STR_QUICKSTART_TIP); + + // add taskbar icon + NOTIFYICONDATAW nid; + nid.hIcon = static_cast<HICON>(LoadImageW( GetModuleHandleW( nullptr ), MAKEINTRESOURCEW( ICON_LO_DEFAULT ), + IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), + LR_DEFAULTCOLOR | LR_SHARED )); + + wcsncpy( nid.szTip, o3tl::toW(strTip.getStr()), 64 ); + + nid.cbSize = sizeof(nid); + nid.hWnd = hWnd; + nid.uID = ID_QUICKSTART; + nid.uCallbackMessage = SFX_TASKBAR_NOTIFICATION; + nid.uFlags = NIF_MESSAGE|NIF_TIP|NIF_ICON; + + Shell_NotifyIconW(NIM_ADD, &nid); +} + + +static LRESULT CALLBACK listenerWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static UINT s_uTaskbarRestart = 0; + static UINT s_uMsgKillTray = 0; + + switch (uMsg) + { + case WM_NCCREATE: + return TRUE; + case WM_CREATE: + { + // request notification when taskbar is recreated + // we then have to add our icon again + s_uTaskbarRestart = RegisterWindowMessageW(L"TaskbarCreated"); + s_uMsgKillTray = RegisterWindowMessageW( SHUTDOWN_QUICKSTART_MESSAGE ); + + // create the menu + if( !popupMenu ) + if( (popupMenu = createSystrayMenu( )) == nullptr ) + return -1; + + // and the icon + addTaskbarIcon( hWnd ); + + // disable shutdown + ShutdownIcon::getInstance()->SetVeto( true ); + ShutdownIcon::addTerminateListener(); + } + return 0; + + case WM_MEASUREITEM: + OnMeasureItem(hWnd, reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam)); + return TRUE; + + case WM_DRAWITEM: + OnDrawItem(hWnd, reinterpret_cast<LPDRAWITEMSTRUCT>(lParam)); + return TRUE; + + case SFX_TASKBAR_NOTIFICATION: + switch( lParam ) + { + case WM_LBUTTONDOWN: + { + bool const ret = PostMessageW(aExecuterWindow, WM_COMMAND, IDM_STARTCENTER, reinterpret_cast<LPARAM>(hWnd)); + SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!"); + break; + } + + case WM_RBUTTONDOWN: + { + POINT pt; + GetCursorPos(&pt); + SetForegroundWindow( hWnd ); + + // update status before showing menu, could have been changed from option page + CheckMenuItem( popupMenu, IDM_INSTALL, MF_BYCOMMAND| (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) ); + + EnableMenuItem( popupMenu, IDM_EXIT, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem( popupMenu, IDM_OPEN, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) ); + EnableMenuItem( popupMenu, IDM_TEMPLATE, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) ); + int m = TrackPopupMenuEx( popupMenu, TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON, + pt.x, pt.y, hWnd, nullptr ); + bool const ret = PostMessageW( hWnd, 0, 0, 0 ); + SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!"); + switch( m ) + { + case IDM_OPEN: + case IDM_WRITER: + case IDM_CALC: + case IDM_IMPRESS: + case IDM_DRAW: + case IDM_TEMPLATE: + case IDM_BASE: + case IDM_MATH: + break; + case IDM_INSTALL: + CheckMenuItem( popupMenu, IDM_INSTALL, MF_BYCOMMAND| (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) ); + break; + case IDM_EXIT: + // delete taskbar icon + NOTIFYICONDATAW nid; + nid.cbSize=sizeof(nid); + nid.hWnd = hWnd; + nid.uID = ID_QUICKSTART; + Shell_NotifyIconW(NIM_DELETE, &nid); + break; + } + + bool const ret2 = PostMessageW(aExecuterWindow, WM_COMMAND, m, reinterpret_cast<LPARAM>(hWnd)); + SAL_WARN_IF(!ret2, "sfx.appl", "ERROR: PostMessage() failed!"); + } + break; + } + break; + case WM_DESTROY: + deleteSystrayMenu( popupMenu ); + // We don't need the Systray Thread anymore + PostQuitMessage( 0 ); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + default: + if( uMsg == s_uTaskbarRestart ) + { + // re-create taskbar icon + addTaskbarIcon( hWnd ); + } + else if ( uMsg == s_uMsgKillTray ) + { + // delete taskbar icon + NOTIFYICONDATAW nid; + nid.cbSize=sizeof(nid); + nid.hWnd = hWnd; + nid.uID = ID_QUICKSTART; + Shell_NotifyIconW(NIM_DELETE, &nid); + + bool const ret = PostMessageW(aExecuterWindow, WM_COMMAND, IDM_EXIT, reinterpret_cast<LPARAM>(hWnd)); + SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!"); + } + else + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + return 0; +} + + +static LRESULT CALLBACK executerWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_NCCREATE: + return TRUE; + case WM_CREATE: + return 0; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDM_OPEN: + if ( !ShutdownIcon::bModalMode ) + ShutdownIcon::FileOpen(); + break; + case IDM_WRITER: + ShutdownIcon::OpenURL( WRITER_URL, "_default" ); + break; + case IDM_CALC: + ShutdownIcon::OpenURL( CALC_URL, "_default" ); + break; + case IDM_IMPRESS: + ShutdownIcon::OpenURL( IMPRESS_WIZARD_URL, "_default" ); + break; + case IDM_DRAW: + ShutdownIcon::OpenURL( DRAW_URL, "_default" ); + break; + case IDM_BASE: + ShutdownIcon::OpenURL( BASE_URL, "_default" ); + break; + case IDM_MATH: + ShutdownIcon::OpenURL( MATH_URL, "_default" ); + break; + case IDM_STARTCENTER: + ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); + break; + case IDM_TEMPLATE: + if ( !ShutdownIcon::bModalMode ) + ShutdownIcon::FromTemplate(); + break; + case IDM_INSTALL: + ShutdownIcon::SetAutostart( !ShutdownIcon::GetAutostart() ); + break; + case IDM_EXIT: + // remove listener and + // terminate office if running in background + if ( !ShutdownIcon::bModalMode ) + ShutdownIcon::terminateDesktop(); + break; + } + break; + case WM_DESTROY: + default: + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + return 0; +} + + +static unsigned __stdcall SystrayThread(void* /*lpParam*/) +{ + osl_setThreadName("SystrayThread"); + + aListenerWindow = CreateWindowExW(0, + QUICKSTART_CLASSNAME, // registered class name + QUICKSTART_WINDOWNAME, // window name + 0, // window style + CW_USEDEFAULT, // horizontal position of window + CW_USEDEFAULT, // vertical position of window + CW_USEDEFAULT, // window width + CW_USEDEFAULT, // window height + nullptr, // handle to parent or owner window + nullptr, // menu handle or child identifier + GetModuleHandleW( nullptr ), // handle to application instance + nullptr // window-creation data + ); + + MSG msg; + + for (;;) + { + int const bRet = GetMessageW(&msg, nullptr, 0, 0); + if (bRet == 0) + { + break; + } + if (-1 == bRet) + { + SAL_WARN("sfx.appl", "GetMessageW failed: " << WindowsErrorString(GetLastError())); + return 1; + } + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + + return msg.wParam; // Exit code of WM_QUIT +} + + +void win32_init_sys_tray() +{ + if ( ShutdownIcon::IsQuickstarterInstalled() ) + { + WNDCLASSEXW listenerClass; + listenerClass.cbSize = sizeof(listenerClass); + listenerClass.style = 0; + listenerClass.lpfnWndProc = listenerWndProc; + listenerClass.cbClsExtra = 0; + listenerClass.cbWndExtra = 0; + listenerClass.hInstance = GetModuleHandleW( nullptr ); + listenerClass.hIcon = nullptr; + listenerClass.hCursor = nullptr; + listenerClass.hbrBackground = nullptr; + listenerClass.lpszMenuName = nullptr; + listenerClass.lpszClassName = QUICKSTART_CLASSNAME; + listenerClass.hIconSm = nullptr; + + RegisterClassExW(&listenerClass); + + WNDCLASSEXW executerClass; + executerClass.cbSize = sizeof(executerClass); + executerClass.style = 0; + executerClass.lpfnWndProc = executerWndProc; + executerClass.cbClsExtra = 0; + executerClass.cbWndExtra = 0; + executerClass.hInstance = GetModuleHandleW( nullptr ); + executerClass.hIcon = nullptr; + executerClass.hCursor = nullptr; + executerClass.hbrBackground = nullptr; + executerClass.lpszMenuName = nullptr; + executerClass.lpszClassName = EXECUTER_WINDOWCLASS; + executerClass.hIconSm = nullptr; + + RegisterClassExW( &executerClass ); + + aExecuterWindow = CreateWindowExW(0, + EXECUTER_WINDOWCLASS, // registered class name + EXECUTER_WINDOWNAME, // window name + 0, // window style + CW_USEDEFAULT, // horizontal position of window + CW_USEDEFAULT, // vertical position of window + CW_USEDEFAULT, // window width + CW_USEDEFAULT, // window height + nullptr, // handle to parent or owner window + nullptr, // menu handle or child identifier + GetModuleHandleW( nullptr ), // handle to application instance + nullptr // window-creation data + ); + + CloseHandle(reinterpret_cast<HANDLE>( + _beginthreadex(nullptr, 0, SystrayThread, nullptr, 0, nullptr))); + } +} + + +void win32_shutdown_sys_tray() +{ + if ( ShutdownIcon::IsQuickstarterInstalled() ) + { + if( IsWindow( aListenerWindow ) ) + { + DestroyWindow( aListenerWindow ); + aListenerWindow = nullptr; + DestroyWindow( aExecuterWindow ); + aExecuterWindow = nullptr; + } + UnregisterClassW( QUICKSTART_CLASSNAME, GetModuleHandleW( nullptr ) ); + UnregisterClassW( EXECUTER_WINDOWCLASS, GetModuleHandleW( nullptr ) ); + } +} + + +void OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis) +{ + MYITEM *pMyItem = reinterpret_cast<MYITEM *>(lpmis->itemData); + HDC hdc = GetDC(hwnd); + SIZE size; + + NONCLIENTMETRICSW ncm = {}; + ncm.cbSize = sizeof(ncm); + + SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); + + // Assume every menu item can be default and printed bold + ncm.lfMenuFont.lfWeight = FW_BOLD; + + HFONT hfntOld = static_cast<HFONT>(SelectObject(hdc, CreateFontIndirectW( &ncm.lfMenuFont ))); + + GetTextExtentPoint32W(hdc, o3tl::toW(pMyItem->text.getStr()), + pMyItem->text.getLength(), &size); + + lpmis->itemWidth = size.cx + 4 + GetSystemMetrics( SM_CXSMICON ); + lpmis->itemHeight = std::max<int>(size.cy, GetSystemMetrics( SM_CYSMICON )); + lpmis->itemHeight += 4; + + DeleteObject( SelectObject(hdc, hfntOld) ); + ReleaseDC(hwnd, hdc); +} + +void OnDrawItem(HWND /*hwnd*/, LPDRAWITEMSTRUCT lpdis) +{ + MYITEM *pMyItem = reinterpret_cast<MYITEM *>(lpdis->itemData); + COLORREF clrPrevText, clrPrevBkgnd; + HFONT hfntOld; + HBRUSH hbrOld; + int x, y; + bool fSelected = lpdis->itemState & ODS_SELECTED; + bool fDisabled = lpdis->itemState & (ODS_DISABLED | ODS_GRAYED); + + // Set the appropriate foreground and background colors. + + RECT aRect = lpdis->rcItem; + + clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_MENU) ); + + if ( fDisabled ) + clrPrevText = SetTextColor( lpdis->hDC, GetSysColor( COLOR_GRAYTEXT ) ); + else + clrPrevText = SetTextColor( lpdis->hDC, GetSysColor( fSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT ) ); + + if ( fSelected ) + clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT) ); + else + clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_MENU) ); + + hbrOld = static_cast<HBRUSH>(SelectObject( lpdis->hDC, CreateSolidBrush( GetBkColor( lpdis->hDC ) ) )); + + // Fill background + PatBlt(lpdis->hDC, aRect.left, aRect.top, aRect.right-aRect.left, aRect.bottom-aRect.top, PATCOPY); + + int height = aRect.bottom-aRect.top; + + x = aRect.left; + y = aRect.top; + + int cx = GetSystemMetrics( SM_CXSMICON ); + int cy = GetSystemMetrics( SM_CYSMICON ); + HICON hIcon( nullptr ); + HMODULE hModule( GetModuleHandleW( nullptr ) ); + + if ( pMyItem->module.getLength() > 0 ) + { + LPCWSTR pModuleName = o3tl::toW( pMyItem->module.getStr() ); + hModule = GetModuleHandleW( pModuleName ); + if ( hModule == nullptr ) + { + hModule = LoadLibraryW(pModuleName); + } + } + + hIcon = static_cast<HICON>(LoadImageW( hModule, MAKEINTRESOURCEW( pMyItem->iconId ), + IMAGE_ICON, cx, cy, + LR_DEFAULTCOLOR | LR_SHARED )); + + + HBRUSH hbrIcon = CreateSolidBrush( GetSysColor( COLOR_GRAYTEXT ) ); + + DrawStateW( lpdis->hDC, hbrIcon, nullptr, reinterpret_cast<LPARAM>(hIcon), WPARAM(0), x, y+(height-cy)/2, 0, 0, DST_ICON | (fDisabled ? (fSelected ? DSS_MONO : DSS_DISABLED) : DSS_NORMAL) ); + + DeleteObject( hbrIcon ); + + x += cx + 4; // space for icon + aRect.left = x; + + NONCLIENTMETRICSW ncm = {}; + ncm.cbSize = sizeof(ncm); + + SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); + + // Print default menu entry with bold font + if ( lpdis->itemState & ODS_DEFAULT ) + ncm.lfMenuFont.lfWeight = FW_BOLD; + + hfntOld = static_cast<HFONT>(SelectObject(lpdis->hDC, CreateFontIndirectW( &ncm.lfMenuFont ))); + + + SIZE size; + GetTextExtentPointW( lpdis->hDC, o3tl::toW(pMyItem->text.getStr()), pMyItem->text.getLength(), &size ); + + DrawStateW( lpdis->hDC, nullptr, nullptr, reinterpret_cast<LPARAM>(pMyItem->text.getStr()), WPARAM(0), aRect.left, aRect.top + (height - size.cy)/2, 0, 0, DST_TEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ); + + // Restore the original font and colors. + DeleteObject( SelectObject( lpdis->hDC, hbrOld ) ); + DeleteObject( SelectObject( lpdis->hDC, hfntOld) ); + SetTextColor(lpdis->hDC, clrPrevText); + SetBkColor(lpdis->hDC, clrPrevBkgnd); +} + + +// code from setup2 project + + +static void SHFree_( void *pv ) +{ + IMalloc *pMalloc; + if( NOERROR == SHGetMalloc(&pMalloc) ) + { + pMalloc->Free( pv ); + pMalloc->Release(); + } +} + +#define ALLOC(type, n) static_cast<type *>(HeapAlloc(GetProcessHeap(), 0, sizeof(type) * n )) +#define FREE(p) HeapFree(GetProcessHeap(), 0, p) + +static OUString SHGetSpecialFolder( int nFolderID ) +{ + + LPITEMIDLIST pidl; + HRESULT hHdl = SHGetSpecialFolderLocation( nullptr, nFolderID, &pidl ); + OUString aFolder; + + if( hHdl == NOERROR ) + { + WCHAR *lpFolderA; + lpFolderA = ALLOC( WCHAR, 16000 ); + + SHGetPathFromIDListW( pidl, lpFolderA ); + aFolder = o3tl::toU( lpFolderA ); + + FREE( lpFolderA ); + SHFree_( pidl ); + } + return aFolder; +} + +OUString ShutdownIcon::GetAutostartFolderNameW32() +{ + return SHGetSpecialFolder(CSIDL_STARTUP); +} + +static HRESULT WINAPI SHCoCreateInstance( LPVOID lpszReserved, REFCLSID clsid, LPUNKNOWN pUnkUnknown, REFIID iid, LPVOID *ppv ) +{ + HRESULT hResult = E_NOTIMPL; + HMODULE hModShell = GetModuleHandleW( L"SHELL32" ); + + if ( hModShell != nullptr ) + { + typedef HRESULT (WINAPI *SHCoCreateInstance_PROC)( LPVOID lpszReserved, REFCLSID clsid, LPUNKNOWN pUnkUnknown, REFIID iid, LPVOID *ppv ); + + SHCoCreateInstance_PROC lpfnSHCoCreateInstance = reinterpret_cast<SHCoCreateInstance_PROC>(GetProcAddress( hModShell, MAKEINTRESOURCEA(102) )); + + if ( lpfnSHCoCreateInstance ) + hResult = lpfnSHCoCreateInstance( lpszReserved, clsid, pUnkUnknown, iid, ppv ); + } + return hResult; +} + +static bool CreateShortcut( const OUString& rAbsObject, const OUString& rAbsObjectPath, + const OUString& rAbsShortcut, const OUString& rDescription, const OUString& rParameter ) +{ + HRESULT hres; + IShellLinkW* psl; + CLSID clsid_ShellLink = CLSID_ShellLink; + CLSID clsid_IShellLink = IID_IShellLinkW; + + hres = CoCreateInstance( clsid_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + clsid_IShellLink, reinterpret_cast<void**>(&psl) ); + if( FAILED(hres) ) + hres = SHCoCreateInstance( nullptr, clsid_ShellLink, nullptr, clsid_IShellLink, reinterpret_cast<void**>(&psl) ); + + if( SUCCEEDED(hres) ) + { + IPersistFile* ppf; + psl->SetPath( o3tl::toW(rAbsObject.getStr()) ); + psl->SetWorkingDirectory( o3tl::toW(rAbsObjectPath.getStr()) ); + psl->SetDescription( o3tl::toW(rDescription.getStr()) ); + if( rParameter.getLength() ) + psl->SetArguments( o3tl::toW(rParameter.getStr()) ); + + CLSID clsid_IPersistFile = IID_IPersistFile; + hres = psl->QueryInterface( clsid_IPersistFile, reinterpret_cast<void**>(&ppf) ); + + if( SUCCEEDED(hres) ) + { + hres = ppf->Save( o3tl::toW(rAbsShortcut.getStr()), TRUE ); + ppf->Release(); + } else return false; + psl->Release(); + } else return false; + return true; +} + + +// install/uninstall + +static bool FileExistsW( LPCWSTR lpPath ) +{ + bool bExists = false; + WIN32_FIND_DATAW aFindData; + + HANDLE hFind = FindFirstFileW( lpPath, &aFindData ); + + if ( INVALID_HANDLE_VALUE != hFind ) + { + bExists = true; + FindClose( hFind ); + } + + return bExists; +} + +bool ShutdownIcon::IsQuickstarterInstalled() +{ + wchar_t aPath[_MAX_PATH]; + GetModuleFileNameW( nullptr, aPath, _MAX_PATH-1); + + OUString aOfficepath( o3tl::toU(aPath) ); + int i = aOfficepath.lastIndexOf('\\'); + if( i != -1 ) + aOfficepath = aOfficepath.copy(0, i); + + OUString quickstartExe(aOfficepath + "\\quickstart.exe"); + + return FileExistsW( o3tl::toW(quickstartExe.getStr()) ); +} + +void ShutdownIcon::EnableAutostartW32( const OUString &aShortcut ) +{ + wchar_t aPath[_MAX_PATH]; + GetModuleFileNameW( nullptr, aPath, _MAX_PATH-1); + + OUString aOfficepath( o3tl::toU(aPath) ); + int i = aOfficepath.lastIndexOf('\\'); + if( i != -1 ) + aOfficepath = aOfficepath.copy(0, i); + + OUString quickstartExe(aOfficepath + "\\quickstart.exe"); + + CreateShortcut( quickstartExe, aOfficepath, aShortcut, OUString(), OUString() ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/workwin.cxx b/sfx2/source/appl/workwin.cxx new file mode 100644 index 0000000000..88b7cc016e --- /dev/null +++ b/sfx2/source/appl/workwin.cxx @@ -0,0 +1,2356 @@ +/* -*- 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_features.h> +#include <config_feature_desktop.h> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/app.hxx> +#include <workwin.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/module.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/dockwin.hxx> +#include <sfx2/viewsh.hxx> +#include <splitwin.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/toolbarids.hxx> +#include <vcl/taskpanelist.hxx> +#include <vcl/svapp.hxx> +#include <svl/eitem.hxx> +#include <tools/svborder.hxx> +#include <tools/debug.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/moduleoptions.hxx> +#include <com/sun/star/ui/XUIElement.hpp> +#include <com/sun/star/frame/LayoutManagerEvents.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/XLayoutManagerEventBroadcaster.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <type_traits> +#include <unordered_map> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +struct ResIdToResName +{ + ToolbarId eId; + const char* pName; +}; + +} + +const ResIdToResName pToolBarResToName[] = +{ + { ToolbarId::FullScreenToolbox, "fullscreenbar" }, + { ToolbarId::EnvToolbox, "standardbar", }, + { ToolbarId::SvxTbx_Form_Navigation, "formsnavigationbar" }, + { ToolbarId::SvxTbx_Form_Filter, "formsfilterbar" }, + { ToolbarId::SvxTbx_Text_Control_Attributes, "formtextobjectbar" }, + { ToolbarId::SvxTbx_Controls, "formcontrols" }, + { ToolbarId::SvxTbx_FormDesign, "formdesign" }, + { ToolbarId::Math_Toolbox, "toolbar" }, //math + { ToolbarId::Text_Toolbox_Sc, "textobjectbar" }, //calc + { ToolbarId::Draw_Objectbar, "drawobjectbar" }, + { ToolbarId::Graphic_Objectbar, "graphicobjectbar" }, + { ToolbarId::Objectbar_Format, "formatobjectbar" }, + { ToolbarId::Objectbar_Preview, "previewbar" }, + { ToolbarId::Objectbar_Tools, "toolbar" }, //calc + { ToolbarId::Bezier_Toolbox_Sd, "bezierobjectbar" }, //draw/impress + { ToolbarId::Gluepoints_Toolbox, "gluepointsobjectbar" }, + { ToolbarId::Draw_Graf_Toolbox, "graphicobjectbar" }, + { ToolbarId::Draw_Obj_Toolbox, "drawingobjectbar" }, //impress + { ToolbarId::Draw_Text_Toolbox_Sd, "textobjectbar" }, //impress + { ToolbarId::Draw_Toolbox_Sd, "toolbar" }, //impress + { ToolbarId::Draw_Options_Toolbox, "optionsbar" }, + { ToolbarId::Draw_CommonTask_Toolbox, "commontaskbar" }, + { ToolbarId::Graphic_Obj_Toolbox, "drawingobjectbar" }, //draw + { ToolbarId::Outline_Toolbox, "outlinetoolbar" }, //impress + { ToolbarId::Slide_Toolbox, "slideviewtoolbar" }, + { ToolbarId::Slide_Obj_Toolbox, "slideviewobjectbar" }, + { ToolbarId::Bezier_Toolbox_Sw, "bezierobjectbar" }, + { ToolbarId::Draw_Toolbox_Sw, "drawingobjectbar" }, + { ToolbarId::Draw_Text_Toolbox_Sw, "drawtextobjectbar" }, + { ToolbarId::Frame_Toolbox, "frameobjectbar" }, + { ToolbarId::Grafik_Toolbox, "graphicobjectbar" }, + { ToolbarId::Num_Toolbox, "numobjectbar" }, + { ToolbarId::Ole_Toolbox, "oleobjectbar" }, + { ToolbarId::Table_Toolbox, "tableobjectbar" }, + { ToolbarId::Text_Toolbox_Sw, "textobjectbar" }, + { ToolbarId::PView_Toolbox, "previewobjectbar" }, //writer + { ToolbarId::Webtools_Toolbox, "toolbar" }, //web + { ToolbarId::Webtext_Toolbox, "textobjectbar" }, + { ToolbarId::Tools_Toolbox, "toolbar" }, //writer + { ToolbarId::Webframe_Toolbox, "frameobjectbar" }, //web + { ToolbarId::Webgraphic_Toolbox, "graphicobjectbar" }, + { ToolbarId::Webole_Toolbox, "oleobjectbar" }, + { ToolbarId::Basicide_Objectbar, "macrobar" }, + { ToolbarId::Svx_Fontwork_Bar, "fontworkobjectbar" }, //global + { ToolbarId::Svx_Extrusion_Bar, "extrusionobjectbar" }, + { ToolbarId::FormLayer_Toolbox, "formsobjectbar" }, + { ToolbarId::Module_Toolbox, "viewerbar" }, //writer (plugin) + { ToolbarId::Objectbar_App, "viewerbar" }, //calc (plugin) + { ToolbarId::Draw_Viewer_Toolbox, "viewerbar" }, //impress(plugin) + { ToolbarId::Draw_Media_Toolbox, "mediaobjectbar" }, //draw/impress + { ToolbarId::Media_Objectbar, "mediaobjectbar" }, //calc + { ToolbarId::Media_Toolbox, "mediaobjectbar" }, //writer + { ToolbarId::None, "" } +}; + +// Sort the Children according their alignment +// The order corresponds to the enum SfxChildAlignment (->CHILDWIN.HXX). + +constexpr OUString g_aLayoutManagerPropName = u"LayoutManager"_ustr; + +// Help to make changes to the alignment compatible! +LayoutManagerListener::LayoutManagerListener( + SfxWorkWindow* pWrkWin ) : + m_bHasFrame( false ), + m_pWrkWin( pWrkWin ) +{ +} + +LayoutManagerListener::~LayoutManagerListener() +{ +} + +void LayoutManagerListener::setFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + SolarMutexGuard aGuard; + if ( !m_pWrkWin || m_bHasFrame ) + return; + + m_xFrame = xFrame; + m_bHasFrame = true; + + if ( !xFrame.is() ) + return; + + css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY ); + css::uno::Reference< css::frame::XLayoutManagerEventBroadcaster > xLayoutManager; + if ( !xPropSet.is() ) + return; + + try + { + Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName ); + aValue >>= xLayoutManager; + + if ( xLayoutManager.is() ) + xLayoutManager->addLayoutManagerEventListener( + css::uno::Reference< css::frame::XLayoutManagerListener >(this) ); + + xPropSet.set( xLayoutManager, UNO_QUERY ); + if ( xPropSet.is() ) + { + aValue = xPropSet->getPropertyValue( "LockCount" ); + aValue >>= m_pWrkWin->m_nLock; + } + } + catch ( css::lang::DisposedException& ) + { + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + { + } +} + + +// XComponent + +void SAL_CALL LayoutManagerListener::addEventListener( + const css::uno::Reference< css::lang::XEventListener >& ) +{ + // do nothing, only internal class +} + +void SAL_CALL LayoutManagerListener::removeEventListener( + const css::uno::Reference< css::lang::XEventListener >& ) +{ + // do nothing, only internal class +} + +void SAL_CALL LayoutManagerListener::dispose() +{ + SolarMutexGuard aGuard; + + // reset member + m_pWrkWin = nullptr; + + css::uno::Reference< css::frame::XFrame > xFrame( m_xFrame.get(), css::uno::UNO_QUERY ); + if ( !xFrame.is() ) + return; + + m_xFrame.clear(); + m_bHasFrame = false; + + css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY ); + css::uno::Reference< css::frame::XLayoutManagerEventBroadcaster > xLayoutManager; + if ( !xPropSet.is() ) + return; + + try + { + css::uno::Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName ); + aValue >>= xLayoutManager; + + // remove as listener from layout manager + if ( xLayoutManager.is() ) + xLayoutManager->removeLayoutManagerEventListener( + css::uno::Reference< css::frame::XLayoutManagerListener >(this) ); + } + catch ( css::lang::DisposedException& ) + { + } + catch ( const css::uno::RuntimeException& ) + { + throw; + } + catch ( css::uno::Exception& ) + { + } +} + + +// XEventListener + +void SAL_CALL LayoutManagerListener::disposing( + const css::lang::EventObject& ) +{ + SolarMutexGuard aGuard; + m_pWrkWin = nullptr; + m_bHasFrame = false; + m_xFrame.clear(); +} + + +// XLayoutManagerEventListener + +void SAL_CALL LayoutManagerListener::layoutEvent( + const css::lang::EventObject&, + ::sal_Int16 eLayoutEvent, + const css::uno::Any& ) +{ + SolarMutexGuard aGuard; + if ( !m_pWrkWin ) + return; + + if ( eLayoutEvent == css::frame::LayoutManagerEvents::VISIBLE ) + { + m_pWrkWin->MakeVisible_Impl( true ); + m_pWrkWin->ShowChildren_Impl(); + m_pWrkWin->ArrangeChildren_Impl(); + } + else if ( eLayoutEvent == css::frame::LayoutManagerEvents::INVISIBLE ) + { + m_pWrkWin->MakeVisible_Impl( false ); + m_pWrkWin->HideChildren_Impl(); + m_pWrkWin->ArrangeChildren_Impl(); + } + else if ( eLayoutEvent == css::frame::LayoutManagerEvents::LOCK ) + { + m_pWrkWin->Lock_Impl( true ); + } + else if ( eLayoutEvent == css::frame::LayoutManagerEvents::UNLOCK ) + { + m_pWrkWin->Lock_Impl( false ); + } +} + +namespace +{ + struct ToolbarIdHash + { + size_t operator()(ToolbarId t) const + { + typedef std::underlying_type<ToolbarId>::type underlying_type; + return std::hash<underlying_type>()(static_cast<underlying_type>(t)); + } + }; + + class FilledToolBarResIdToResourceURLMap + { + private: + typedef std::unordered_map<ToolbarId, OUString, ToolbarIdHash> ToolBarResIdToResourceURLMap; + ToolBarResIdToResourceURLMap m_aResIdToResourceURLMap; + public: + FilledToolBarResIdToResourceURLMap() + { + sal_Int32 nIndex( 0 ); + while (pToolBarResToName[nIndex].eId != ToolbarId::None) + { + OUString aResourceURL( OUString::createFromAscii( pToolBarResToName[nIndex].pName )); + m_aResIdToResourceURLMap.emplace(pToolBarResToName[nIndex].eId, aResourceURL); + ++nIndex; + } + } + + OUString findURL(ToolbarId eId) const + { + ToolBarResIdToResourceURLMap::const_iterator aIter = m_aResIdToResourceURLMap.find(eId); + if ( aIter != m_aResIdToResourceURLMap.end() ) + return aIter->second; + return OUString(); + } + }; +} + +static OUString GetResourceURLFromToolbarId(ToolbarId eId) +{ + static FilledToolBarResIdToResourceURLMap theFilledToolBarResIdToResourceURLMap; + return theFilledToolBarResIdToResourceURLMap.findURL(eId); +} + +static sal_uInt16 TbxMatch( sal_uInt16 nPos ) +{ + switch ( nPos ) + { + case SFX_OBJECTBAR_APPLICATION : + return 0; + case SFX_OBJECTBAR_OPTIONS: + return 1; + case SFX_OBJECTBAR_MACRO: + return 2; + case SFX_OBJECTBAR_OBJECT: + return 3; + case SFX_OBJECTBAR_TOOLS: + return 4; + case SFX_OBJECTBAR_FULLSCREEN: + case SFX_OBJECTBAR_COMMONTASK: + case SFX_OBJECTBAR_RECORDING: + return nPos+1; + default: + return nPos; + } +} + +static sal_uInt16 ChildAlignValue(SfxChildAlignment eAlign) +{ + sal_uInt16 ret = 17; + + switch (eAlign) + { + case SfxChildAlignment::HIGHESTTOP: + ret = 1; + break; + case SfxChildAlignment::LOWESTBOTTOM: + ret = 2; + break; + case SfxChildAlignment::FIRSTLEFT: + ret = 3; + break; + case SfxChildAlignment::LASTRIGHT: + ret = 4; + break; + case SfxChildAlignment::LEFT: + ret = 5; + break; + case SfxChildAlignment::RIGHT: + ret = 6; + break; + case SfxChildAlignment::FIRSTRIGHT: + ret = 7; + break; + case SfxChildAlignment::LASTLEFT: + ret = 8; + break; + case SfxChildAlignment::TOP: + ret = 9; + break; + case SfxChildAlignment::BOTTOM: + ret = 10; + break; + case SfxChildAlignment::TOOLBOXTOP: + ret = 11; + break; + case SfxChildAlignment::TOOLBOXBOTTOM: + ret = 12; + break; + case SfxChildAlignment::LOWESTTOP: + ret = 13; + break; + case SfxChildAlignment::HIGHESTBOTTOM: + ret = 14; + break; + case SfxChildAlignment::TOOLBOXLEFT: + ret = 15; + break; + case SfxChildAlignment::TOOLBOXRIGHT: + ret = 16; + break; + case SfxChildAlignment::NOALIGNMENT: + break; // -Wall not handled... + } + + return ret; +} + +void SfxWorkWindow::Sort_Impl() +{ + aSortedList.clear(); + for (size_t i = 0; i < aChildren.size(); ++i) + { + SfxChild_Impl *pCli = aChildren[i].get(); + if (pCli) + { + decltype(aSortedList)::size_type k; + for (k=0; k<aSortedList.size(); k++) + if (ChildAlignValue( aChildren[aSortedList[k]]->eAlign ) > + ChildAlignValue(pCli->eAlign)) + break; + aSortedList.insert( aSortedList.begin() + k, i ); + } + } + + bSorted = true; +} + +constexpr OUString g_aStatusBarResName( u"private:resource/statusbar/statusbar"_ustr ); +constexpr OUString g_aTbxTypeName( u"private:resource/toolbar/"_ustr ); +constexpr OUString g_aProgressBarResName( u"private:resource/progressbar/progressbar"_ustr ); + +// constructor for workwin of a Frame + +SfxWorkWindow::SfxWorkWindow( vcl::Window *pWin, SfxFrame *pFrm, SfxFrame* pMaster ) : + pBindings(&pFrm->GetCurrentViewFrame()->GetBindings()), + pWorkWin (pWin), + pActiveChild( nullptr ), + nUpdateMode(SfxVisibilityFlags::Standard), + nChildren( 0 ), + nOrigMode( SfxVisibilityFlags::Invisible ), + bSorted( true ), + bDockingAllowed(true), + bInternalDockingAllowed(true), + bAllChildrenVisible(true), +#if !defined(ANDROID) || HAVE_FEATURE_ANDROID_LOK + bIsFullScreen( false ), +#else // Fennec-based Android Viewer + bIsFullScreen( true ), +#endif +#if HAVE_FEATURE_DESKTOP + bShowStatusBar( true ), +#else + bShowStatusBar( sal_False ), +#endif + m_nLock( 0 ), + pMasterFrame( pMaster ), + pFrame( pFrm ) +{ + DBG_ASSERT (pBindings, "No Bindings!"); + + pBindings->SetWorkWindow_Impl( std::unique_ptr<SfxWorkWindow>(this) ); + + // For the ObjectBars an integral place in the Childlist is reserved, + // so that they always come in a defined order. + for (int i=0; i<SFX_OBJECTBAR_MAX; ++i) + aChildren.push_back( nullptr ); + + // create and initialize layout manager listener + Reference< css::frame::XFrame > xFrame = GetFrameInterface(); + rtl::Reference<LayoutManagerListener> pLayoutManagerListener = new LayoutManagerListener( this ); + m_xLayoutManagerListener = pLayoutManagerListener; + pLayoutManagerListener->setFrame( xFrame ); + + SfxShell* pConfigShell = pFrm->GetCurrentViewFrame(); + if ( pConfigShell && pConfigShell->GetObjectShell() ) + { + bShowStatusBar = ( !pConfigShell->GetObjectShell()->IsInPlaceActive() ); + bDockingAllowed = true; + bInternalDockingAllowed = true; + } + + // The required split windows (one for each side) can be created + for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ ) + { + // The SplitWindows excludes direct ChildWindows of the WorkWindows + // and receives the docked window. + + SfxChildAlignment eAlign = + ( n == SFX_SPLITWINDOWS_LEFT ? SfxChildAlignment::LEFT : + n == SFX_SPLITWINDOWS_RIGHT ? SfxChildAlignment::RIGHT : + n == SFX_SPLITWINDOWS_TOP ? SfxChildAlignment::TOP : + SfxChildAlignment::BOTTOM ); + VclPtr<SfxSplitWindow> pSplitWin = VclPtr<SfxSplitWindow>::Create(pWorkWin, eAlign, this, true ); + pSplit[n] = pSplitWin; + } + + nOrigMode = SfxVisibilityFlags::Standard; + nUpdateMode = SfxVisibilityFlags::Standard; +} + + +// Destructor + +SfxWorkWindow::~SfxWorkWindow() +{ + + // Delete SplitWindows + for (VclPtr<SfxSplitWindow> & p : pSplit) + { + if (p->GetWindowCount()) + ReleaseChild_Impl(*p); + p.disposeAndClear(); + } + + // Delete help structure for Child-Windows + DBG_ASSERT( aChildren.empty(), "dangling children" ); + + if ( m_xLayoutManagerListener.is() ) + m_xLayoutManagerListener->dispose(); +} + +void SfxWorkWindow::Lock_Impl( bool bLock ) +{ + if ( bLock ) + m_nLock++; + else + --m_nLock; + if ( m_nLock<0 ) + { + OSL_FAIL("Lock count underflow!"); + assert(m_nLock >= 0); + m_nLock = 0; + } + + if ( !m_nLock ) + ArrangeChildren_Impl(); +} + + +// Helper method to release the child lists. Should the destructor not be +// called after this, instead work continues, then space for the object bars +// and split windows has to be reserved in the same way as in the constructor +// of SfxWorkWindow. + +void SfxWorkWindow::DeleteControllers_Impl() +{ + + // Lock SplitWindows (which means suppressing the Resize-Reaction of the + // DockingWindows) + for (size_t n=0; n<SFX_SPLITWINDOWS_MAX; n++ ) + { + VclPtr<SfxSplitWindow> const &p = pSplit[n]; + if (p->GetWindowCount()) + p->Lock(); + } + + // Delete Child-Windows + while(!aChildWins.empty()) + { + std::unique_ptr<SfxChildWin_Impl> pCW = std::move(*aChildWins.begin()); + aChildWins.erase(aChildWins.begin()); + SfxChildWindow *pChild = pCW->pWin; + if (pChild) + { + if (comphelper::LibreOfficeKit::isActive()) + { + vcl::Window* pWindow = pChild->GetWindow(); + if (pWindow) + { + pWindow->ReleaseLOKNotifier(); + } + } + pChild->Hide(); + + // If the child window is a direct child window and not in a + // SplitWindow, cancel it at the workwindow. + // After TH a cancellation on the SplitWindow is not necessary + // since this window is also destroyed (see below). + if (pCW->pCli) + { + if (pChild->GetController()) + ReleaseChild_Impl(*pChild->GetController()); + else + ReleaseChild_Impl(*pChild->GetWindow()); + } + + pCW->pWin = nullptr; + pWorkWin->GetSystemWindow()->GetTaskPaneList()->RemoveWindow( pChild->GetWindow() ); + pChild->Destroy(); + } + + // ATTENTION: The array itself is cleared after this loop!! + // Therefore we have to set every array entry to zero as it could be + // accessed by calling pChild->Destroy(). + // Window::NotifyAllChildren() calls SfxWorkWindow::DataChanged_Impl for + // 8-bit displays (WM_QUERYPALETTECHANGED message due to focus change)!! + } + + Reference< css::frame::XFrame > xFrame = GetFrameInterface(); + Reference< css::beans::XPropertySet > xPropSet( xFrame, UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + if ( xPropSet.is() ) + { + try + { + Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName ); + aValue >>= xLayoutManager; + } + catch ( Exception& ) + { + } + } + + if ( xLayoutManager.is() ) + { + xLayoutManager->reset(); + + // Delete StatusBar + ResetStatusBar_Impl(); + + // Delete ObjectBars (this is done last, so that aChildren does not + // receive dead Pointers) + for (SfxObjectBar_Impl & i : aObjBarList) + { + // Not every position must be occupied + ToolbarId eId = i.eId; + if (eId != ToolbarId::None) + i.eId = ToolbarId::None; + } + } + + // ObjectBars are all released at once, since they occupy a + // fixed contiguous area in the array pChild + aChildren.clear(); + bSorted = false; + + nChildren = 0; +} + + +// for placing the child window. + +void SfxWorkWindow::ArrangeChildren_Impl( bool bForce ) +{ + if ( pFrame->IsClosing_Impl() || ( m_nLock && !bForce )) + return; + + SfxInPlaceClient *pClient = nullptr; + SfxViewFrame *pF = pFrame->GetCurrentViewFrame(); + if ( pF && pF->GetViewShell() ) + pClient = pF->GetViewShell()->GetIPClient(); + + if ( pClient ) + return; + + aClientArea = GetTopRect_Impl(); + if ( aClientArea.IsEmpty() ) + return; + + SvBorder aBorder; + if ( nChildren && IsVisible_Impl() ) + aBorder = Arrange_Impl(); + // If the current application document contains an IPClient, then the + // object through SetTopToolFramePixel has to be assigned the available + // space. The object will then point to its UITools and sets the app border + // (-> SfxInPlaceEnv_Impl:: ArrangeChildren_Impl ()). Otherwise the + // app border is set here directly to possibly overwrite the Border that + // was set by an object from another document. The object does not set + // the SetAppBorder when it removes its UI tools so that no-dithering + // ObjectBar arises. + // (->SfxInPlaceEnv_Impl::ArrangeChildren_Impl()) + + pMasterFrame->SetToolSpaceBorderPixel_Impl( aBorder ); + + ArrangeAutoHideWindows( nullptr ); +} + +void SfxWorkWindow::FlushPendingChildSizes() +{ + // tdf#116865, if any windows are being resized, i.e. their + // resize timer is active, then calling GetSizePixel on + // them forces the timer to fire and sets the final + // size to which they are getting resized towards. + for (size_t i = 0; i < aChildren.size(); ++i) + { + SfxChild_Impl *pCli = aChildren[i].get(); + if (!pCli || !pCli->pWin) + continue; + (void)pCli->pWin->GetSizePixel(); + } +} + +SvBorder SfxWorkWindow::Arrange_Impl() + +/* [Description] + + This method organizes all visible child windows so that the docked window + sorted in order from the outside to the inside are placed after one + another. If a visible window does not fit anymore into the free + ClientArea, it is set to "not visible". +*/ +{ + //tdf#116865 trigger pending sizing timers now so we arrange + //with the final size of the client area. + // + //Otherwise calling GetSizePixel in the following loop will trigger the + //timers, causing reentry into Arrange_Impl again where the inner + //Arrange_Impl arranges with the final size, and then returns to this outer + //Arrange_Impl which would rearrange with the old client area size + FlushPendingChildSizes(); + aClientArea = GetTopRect_Impl(); + aUpperClientArea = aClientArea; + + SvBorder aBorder; + if ( !nChildren ) + return aBorder; + + if (!bSorted) + Sort_Impl(); + + Point aPos; + Size aSize; + tools::Rectangle aTmp( aClientArea ); + + for (sal_uInt16 n : aSortedList) + { + SfxChild_Impl* pCli = aChildren[n].get(); + if ( !pCli->pWin ) + continue; + + // First, we assume that there is room for the window. + pCli->nVisible |= SfxChildVisibility::FITS_IN; + + // Skip invisible windows + if (pCli->nVisible != SfxChildVisibility::VISIBLE) + continue; + + if ( pCli->bResize ) + aSize = pCli->aSize; + else + aSize = pCli->pWin->GetSizePixel(); + + SvBorder aTemp = aBorder; + bool bAllowHiding = true; + switch ( pCli->eAlign ) + { + case SfxChildAlignment::HIGHESTTOP: + case SfxChildAlignment::TOP: + case SfxChildAlignment::TOOLBOXTOP: + case SfxChildAlignment::LOWESTTOP: + aSize.setWidth( aTmp.GetWidth() ); + if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW ) + aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize ); + bAllowHiding = false; + aBorder.Top() += aSize.Height(); + aPos = aTmp.TopLeft(); + aTmp.AdjustTop(aSize.Height() ); + if ( pCli->eAlign == SfxChildAlignment::HIGHESTTOP ) + aUpperClientArea.AdjustTop(aSize.Height() ); + break; + + case SfxChildAlignment::LOWESTBOTTOM: + case SfxChildAlignment::BOTTOM: + case SfxChildAlignment::TOOLBOXBOTTOM: + case SfxChildAlignment::HIGHESTBOTTOM: + aSize.setWidth( aTmp.GetWidth() ); + if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW ) + aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize ); + aBorder.Bottom() += aSize.Height(); + aPos = aTmp.BottomLeft(); + aPos.AdjustY( -(aSize.Height()-1) ); + aTmp.AdjustBottom( -(aSize.Height()) ); + if ( pCli->eAlign == SfxChildAlignment::LOWESTBOTTOM ) + aUpperClientArea.AdjustBottom( -(aSize.Height()) ); + break; + + case SfxChildAlignment::FIRSTLEFT: + case SfxChildAlignment::LEFT: + case SfxChildAlignment::LASTLEFT: + case SfxChildAlignment::TOOLBOXLEFT: + aSize.setHeight( aTmp.GetHeight() ); + if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW ) + aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize ); + bAllowHiding = false; + aBorder.Left() += aSize.Width(); + aPos = aTmp.TopLeft(); + aTmp.AdjustLeft(aSize.Width() ); + if ( pCli->eAlign != SfxChildAlignment::TOOLBOXLEFT ) + aUpperClientArea.AdjustLeft(aSize.Width() ); + break; + + case SfxChildAlignment::FIRSTRIGHT: + case SfxChildAlignment::RIGHT: + case SfxChildAlignment::LASTRIGHT: + case SfxChildAlignment::TOOLBOXRIGHT: + aSize.setHeight( aTmp.GetHeight() ); + if ( pCli->pWin->GetType() == WindowType::SPLITWINDOW ) + aSize = static_cast<SplitWindow *>(pCli->pWin.get())->CalcLayoutSizePixel( aSize ); + aBorder.Right() += aSize.Width(); + aPos = aTmp.TopRight(); + aPos.AdjustX( -(aSize.Width()-1) ); + aTmp.AdjustRight( -(aSize.Width()) ); + if ( pCli->eAlign != SfxChildAlignment::TOOLBOXRIGHT ) + aUpperClientArea.AdjustRight( -(aSize.Width()) ); + break; + + default: + pCli->aSize = pCli->pWin->GetSizePixel(); + pCli->bResize = false; + continue; + } + + pCli->pWin->SetPosSizePixel( aPos, aSize ); + pCli->bResize = false; + pCli->aSize = aSize; + if( bAllowHiding && !RequestTopToolSpacePixel_Impl( aBorder ) ) + { + pCli->nVisible ^= SfxChildVisibility::FITS_IN; + aBorder = aTemp; + } + } + + if ( aClientArea.GetWidth() >= aBorder.Left() + aBorder.Right() ) + { + aClientArea.AdjustLeft(aBorder.Left() ); + aClientArea.AdjustRight( -(aBorder.Right()) ); + } + else + { + aBorder.Left() = aClientArea.Left(); + aBorder.Right() = aClientArea.Right(); + aClientArea.SetRight( aTmp.Left() ); + aClientArea.SetLeft( aTmp.Left() ); + } + + if ( aClientArea.GetHeight() >= aBorder.Top() + aBorder.Bottom() ) + { + aClientArea.AdjustTop(aBorder.Top() ); + aClientArea.AdjustBottom( -(aBorder.Bottom()) ); + } + else + { + aBorder.Top() = aClientArea.Top(); + aBorder.Bottom() = aClientArea.Bottom(); + aClientArea.SetTop(aTmp.Top()); + aClientArea.SetBottom(aTmp.Top()); + } + + return IsDockingAllowed() ? aBorder : SvBorder(); +} + +bool SfxWorkWindow::PrepareClose_Impl() +{ + for (const std::unique_ptr<SfxChildWin_Impl> &pCW : aChildWins) + { + SfxChildWindow *pChild = pCW->pWin; + if ( pChild && !pChild->QueryClose() ) + return false; + } + + return true; +} + +SfxChild_Impl* SfxWorkWindow::RegisterChild_Impl( vcl::Window& rWindow, + SfxChildAlignment eAlign ) +{ + DBG_ASSERT( aChildren.size() < 255, "too many children" ); + DBG_ASSERT( SfxChildAlignValid(eAlign), "invalid align" ); + DBG_ASSERT( !FindChild_Impl(&rWindow), "child registered more than once" ); + + + if ( rWindow.GetParent() != pWorkWin ) + rWindow.SetParent( pWorkWin ); + + auto pChild = std::make_unique<SfxChild_Impl>(rWindow, rWindow.GetSizePixel(), + eAlign, rWindow.IsVisible()); + + aChildren.push_back(std::move(pChild)); + bSorted = false; + nChildren++; + return aChildren.back().get(); +} + +SfxChild_Impl* SfxWorkWindow::RegisterChild_Impl(std::shared_ptr<SfxDialogController>& rController, + SfxChildAlignment eAlign ) +{ + DBG_ASSERT( aChildren.size() < 255, "too many children" ); + DBG_ASSERT( SfxChildAlignValid(eAlign), "invalid align" ); + + auto pChild = std::make_unique<SfxChild_Impl>(rController, eAlign); + + aChildren.push_back(std::move(pChild)); + bSorted = false; + nChildren++; + return aChildren.back().get(); +} + +void SfxWorkWindow::ReleaseChild_Impl( vcl::Window& rWindow ) +{ + + SfxChild_Impl *pChild = nullptr; + decltype(aChildren)::size_type nPos; + for ( nPos = 0; nPos < aChildren.size(); ++nPos ) + { + pChild = aChildren[nPos].get(); + if ( pChild && pChild->pWin == &rWindow ) + { + bSorted = false; + nChildren--; + aChildren.erase(aChildren.begin() + nPos); + return; + } + } + OSL_FAIL( "releasing unregistered child" ); +} + +void SfxWorkWindow::ReleaseChild_Impl(const SfxDialogController& rController) +{ + + SfxChild_Impl *pChild = nullptr; + decltype(aChildren)::size_type nPos; + for ( nPos = 0; nPos < aChildren.size(); ++nPos ) + { + pChild = aChildren[nPos].get(); + if (pChild && pChild->xController.get() == &rController) + { + bSorted = false; + nChildren--; + aChildren.erase(aChildren.begin() + nPos); + return; + } + } + OSL_FAIL( "releasing unregistered child" ); +} + +SfxChild_Impl* SfxWorkWindow::FindChild_Impl( const vcl::Window* rWindow ) const +{ + + sal_uInt16 nCount = aChildren.size(); + for ( sal_uInt16 nPos = 0; nPos < nCount; ++nPos ) + { + SfxChild_Impl *pChild = aChildren[nPos].get(); + if ( pChild && pChild->pWin == rWindow ) + return pChild; + } + + return nullptr; +} + +void SfxWorkWindow::ShowChildren_Impl() +{ + bool bInvisible = ( !IsVisible_Impl() || ( !pWorkWin->IsReallyVisible() && !pWorkWin->IsReallyShown() )); + + for (std::unique_ptr<SfxChild_Impl>& pCli : aChildren) + { + if (!pCli) + continue; + SfxChildWin_Impl* pCW = nullptr; + if (pCli->pWin || pCli->xController) + { + // We have to find the SfxChildWin_Impl to retrieve the + // SFX_CHILDWIN flags that can influence visibility. + for (const std::unique_ptr<SfxChildWin_Impl>& pCWin : aChildWins) + { + SfxChild_Impl* pChild = pCWin->pCli; + if ( pChild == pCli.get() ) + { + pCW = pCWin.get(); + break; + } + } + + bool bVisible( !bInvisible ); + if ( pCW ) + { + // Check flag SFX_CHILDWIN_NEVERHIDE that forces us to show + // the child window even in situations where no child window is + // visible. + SfxChildWindowFlags nFlags = pCW->aInfo.nFlags; + bVisible = !bInvisible || ( nFlags & SfxChildWindowFlags::NEVERHIDE ); + } + + if ( SfxChildVisibility::VISIBLE == (pCli->nVisible & SfxChildVisibility::VISIBLE) && bVisible ) + { + if (pCli->xController) + { + if (!pCli->xController->getDialog()->get_visible()) + { + auto xController = pCli->xController; + weld::DialogController::runAsync(xController, + [=](sal_Int32 nResult){ + if (nResult == nCloseResponseToJustHide) + return; + xController->Close(); + }); + } + } + else + { + ShowFlags nFlags = pCli->bSetFocus ? ShowFlags::NONE : ShowFlags::NoFocusChange | ShowFlags::NoActivate; + pCli->pWin->Show(true, nFlags); + } + pCli->bSetFocus = false; + } + else + { + if (pCli->xController) + { + if (pCli->xController->getDialog()->get_visible()) + pCli->xController->response(RET_CLOSE); + } + else + pCli->pWin->Hide(); + } + } + } +} + + +void SfxWorkWindow::HideChildren_Impl() +{ + for ( sal_uInt16 nPos = aChildren.size(); nPos > 0; --nPos ) + { + SfxChild_Impl *pChild = aChildren[nPos-1].get(); + if (!pChild) + continue; + if (pChild->xController) + pChild->xController->response(RET_CLOSE); + else if (pChild->pWin) + pChild->pWin->Hide(); + } +} + +void SfxWorkWindow::ResetObjectBars_Impl() +{ + for ( auto & n: aObjBarList ) + n.bDestroy = true; + + for ( auto & n: aChildWins ) + n->nId = 0; +} + +void SfxWorkWindow::SetObjectBar_Impl(sal_uInt16 nPos, SfxVisibilityFlags nFlags, ToolbarId eId) +{ + DBG_ASSERT( nPos < SFX_OBJECTBAR_MAX, "object bar position overflow" ); + + SfxObjectBar_Impl aObjBar; + aObjBar.eId = eId; + aObjBar.nMode = nFlags; + + for (SfxObjectBar_Impl & rBar : aObjBarList) + { + if ( rBar.eId == aObjBar.eId ) + { + rBar = aObjBar; + return; + } + } + + aObjBarList.push_back( aObjBar ); +} + +bool SfxWorkWindow::IsVisible_Impl( SfxVisibilityFlags nMode ) const +{ + switch( nUpdateMode ) + { + case SfxVisibilityFlags::Standard: + return true; + case SfxVisibilityFlags::Invisible: + return false; + case SfxVisibilityFlags::Client: + case SfxVisibilityFlags::Server: + return bool(nMode & nUpdateMode); + default: + return (nMode & nOrigMode ) || + nOrigMode == SfxVisibilityFlags::Standard; + } +} + +void SfxWorkWindow::UpdateObjectBars_Impl() +{ + if ( pFrame->IsClosing_Impl() ) + return; + + UpdateObjectBars_Impl2(); + + { + ArrangeChildren_Impl( false ); + + ShowChildren_Impl(); + } + + ShowChildren_Impl(); +} + +Reference< css::task::XStatusIndicator > SfxWorkWindow::GetStatusIndicator() +{ + Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + Reference< css::task::XStatusIndicator > xStatusIndicator; + + if ( xPropSet.is() ) + { + Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName ); + aValue >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + xLayoutManager->createElement( g_aProgressBarResName ); + xLayoutManager->showElement( g_aProgressBarResName ); + + Reference< css::ui::XUIElement > xProgressBar = + xLayoutManager->getElement( g_aProgressBarResName ); + if ( xProgressBar.is() ) + { + xStatusIndicator.set( xProgressBar->getRealInterface(), UNO_QUERY ); + } + } + } + + return xStatusIndicator; +} + + +bool SfxWorkWindow::IsPluginMode( SfxObjectShell const * pObjShell ) +{ + if ( pObjShell && pObjShell->GetMedium() ) + { + const SfxBoolItem* pViewOnlyItem = pObjShell->GetMedium()->GetItemSet().GetItem(SID_VIEWONLY, false); + if ( pViewOnlyItem && pViewOnlyItem->GetValue() ) + return true; + } + + return false; +} + + +css::uno::Reference< css::frame::XFrame > SfxWorkWindow::GetFrameInterface() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + + SfxDispatcher* pDispatcher( GetBindings().GetDispatcher() ); + if ( pDispatcher ) + { + SfxViewFrame* pViewFrame = pDispatcher->GetFrame(); + if ( pViewFrame ) + xFrame = pViewFrame->GetFrame().GetFrameInterface(); + } + + return xFrame; +} + + +void SfxWorkWindow::UpdateObjectBars_Impl2() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + // Lock SplitWindows (which means suppressing the Resize-Reaction of the + // DockingWindows) + for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ ) + { + VclPtr<SfxSplitWindow> const & p = pSplit[n]; + if (p->GetWindowCount()) + p->Lock(); + } + + Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + + if ( xPropSet.is() ) + { + Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName ); + aValue >>= xLayoutManager; + } + + if ( !xLayoutManager.is() ) + return; + + bool bPluginMode( false ); + SfxDispatcher* pDispatcher( GetBindings().GetDispatcher() ); + + if ( pDispatcher ) + { + SfxViewFrame* pViewFrame = pDispatcher->GetFrame(); + if ( pViewFrame ) + bPluginMode = IsPluginMode( pViewFrame->GetObjectShell() ); + } + + // Iterate over all Toolboxes + xLayoutManager->lock(); + const bool bForceDestroyToolbars = + comphelper::LibreOfficeKit::isActive() ? false : sfx2::SfxNotebookBar::IsActive(true); + for ( auto const & n: aObjBarList ) + { + ToolbarId eId = n.eId; + bool bDestroy = n.bDestroy; + + // Determine the valid mode for the ToolBox + SfxVisibilityFlags nTbxMode = n.nMode; + bool bFullScreenTbx( nTbxMode & SfxVisibilityFlags::FullScreen ); + nTbxMode &= ~SfxVisibilityFlags::FullScreen; + nTbxMode &= ~SfxVisibilityFlags::Viewer; + + // Is a ToolBox required in this context ? + bool bModesMatching = (nUpdateMode != SfxVisibilityFlags::Invisible) && ((nTbxMode & nUpdateMode) == nUpdateMode); + if ( bDestroy || bForceDestroyToolbars) + { + OUString aTbxId = g_aTbxTypeName + GetResourceURLFromToolbarId(eId); + xLayoutManager->destroyElement( aTbxId ); + } + else if ( eId != ToolbarId::None && ( ( bModesMatching && !bIsFullScreen ) || + ( bIsFullScreen && bFullScreenTbx ) ) ) + { + OUString aTbxId = g_aTbxTypeName + GetResourceURLFromToolbarId(eId); + if ( !IsDockingAllowed() && !xLayoutManager->isElementFloating( aTbxId )) + xLayoutManager->destroyElement( aTbxId ); + else + { + xLayoutManager->requestElement( aTbxId ); + if ( bPluginMode ) + xLayoutManager->lockWindow( aTbxId ); + } + } + else if ( eId != ToolbarId::None ) + { + // Delete the Toolbox at this Position if possible + OUString aTbxId = g_aTbxTypeName + GetResourceURLFromToolbarId(eId); + xLayoutManager->destroyElement( aTbxId ); + } + } + + UpdateStatusBar_Impl(); + + // unlocking automatically forces Layout + xLayoutManager->unlock(); + + UpdateChildWindows_Impl(); + + // Unlock the SplitWindows again + for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ ) + { + VclPtr<SfxSplitWindow> const & p = pSplit[n]; + if (p->GetWindowCount()) + p->Lock(false); + } +} + +void SfxWorkWindow::UpdateChildWindows_Impl() +{ + // tdf#100870, tdf#101320: don't use range-based for loop when + // container is modified + for ( size_t n=0; n<aChildWins.size(); n++ ) + { + // any current or in the context available Childwindows + SfxChildWin_Impl *pCW = aChildWins[n].get(); + SfxChildWindow *pChildWin = pCW->pWin; + bool bCreate = false; + if ( pCW->nId && (pCW->aInfo.nFlags & SfxChildWindowFlags::ALWAYSAVAILABLE || IsVisible_Impl( pCW->nVisibility ) ) ) + { + // In the context is an appropriate ChildWindow allowed; + // it is also turned on? + if ( pChildWin == nullptr && pCW->bCreate ) + { + // Internal docking is only used for embedding into another + // container. We force the floating state of all floatable + // child windows. + if ( !bInternalDockingAllowed ) + { + // Special case for all non-floatable child windows. We have + // to prevent the creation here! + bCreate = !( pCW->aInfo.nFlags & SfxChildWindowFlags::FORCEDOCK ); + } + else if ( !IsDockingAllowed() || bIsFullScreen ) // || !bInternalDocking ) + { + // In Presentation mode or FullScreen only FloatingWindows + SfxChildAlignment eAlign; + if ( pCW->aInfo.GetExtraData_Impl( &eAlign ) ) + bCreate = ( eAlign == SfxChildAlignment::NOALIGNMENT ); + } + else + bCreate = true; + + if (pCW->aInfo.nFlags & SfxChildWindowFlags::NEVERCLONE) + pCW->bCreate = bCreate = false; // Don't create and remember that we haven't created. + + // Currently, no window here, but it is enabled; windows + // Create window and if possible theContext + if ( bCreate ) + CreateChildWin_Impl( pCW, false ); + + if ( !bAllChildrenVisible && pCW->pCli ) + pCW->pCli->nVisible &= ~SfxChildVisibility::ACTIVE; + } + else if ( pChildWin ) + { + // Window already exists, it should also be visible? + if ( ( !bIsFullScreen || pChildWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT ) && bAllChildrenVisible ) + { + // Update Mode is compatible; definitely enable it + bCreate = true; + if ( pCW->pCli ) + { + // The window is a direct Child + if ((IsDockingAllowed() && bInternalDockingAllowed) + || pCW->pCli->eAlign == SfxChildAlignment::NOALIGNMENT) + pCW->pCli->nVisible |= SfxChildVisibility::NOT_HIDDEN; + } + else + { + if ( pCW->bCreate && IsDockingAllowed() && bInternalDockingAllowed ) + // The window ia within a SplitWindow + static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Reappear_Impl(); + } + } + } + } + + if ( pChildWin && !bCreate ) + { + if ( !pChildWin->QueryClose() || pChildWin->IsHideNotDelete() || Application::IsUICaptured() ) + { + if ( pCW->pCli ) + { + if ( pCW->pCli->nVisible & SfxChildVisibility::NOT_HIDDEN ) + pCW->pCli->nVisible ^= SfxChildVisibility::NOT_HIDDEN; + } + else + static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Disappear_Impl(); + } + else + RemoveChildWin_Impl( pCW ); + } + } +} + +void SfxWorkWindow::CreateChildWin_Impl( SfxChildWin_Impl *pCW, bool bSetFocus ) +{ + pCW->aInfo.bVisible = true; + + SfxChildWindow *pChildWin = SfxChildWindow::CreateChildWindow( pCW->nId, pWorkWin, &GetBindings(), pCW->aInfo).release(); + if (!pChildWin) + return; + + if ( bSetFocus ) + bSetFocus = pChildWin->WantsFocus(); + pChildWin->SetWorkWindow_Impl( this ); + + // At least the extra string is changed during the evaluation, + // also get it anewed + SfxChildWinInfo aInfo = pChildWin->GetInfo(); + pCW->aInfo.aExtraString = aInfo.aExtraString; + pCW->aInfo.bVisible = aInfo.bVisible; + pCW->aInfo.nFlags |= aInfo.nFlags; + + // The creation was successful + GetBindings().Invalidate(pCW->nId); + + sal_uInt16 nPos = pChildWin->GetPosition(); + if (nPos != CHILDWIN_NOPOS) + { + DBG_ASSERT(nPos < SFX_OBJECTBAR_MAX, "Illegal objectbar position!"); + if ( aChildren[TbxMatch(nPos)] )// && + { + // ChildWindow replaces ObjectBar + aChildren[TbxMatch(nPos)]->nVisible ^= SfxChildVisibility::NOT_HIDDEN; + } + } + + // make childwin keyboard accessible + pWorkWin->GetSystemWindow()->GetTaskPaneList()->AddWindow( pChildWin->GetWindow() ); + + pCW->pWin = pChildWin; + + if ( pChildWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT || pChildWin->GetWindow()->GetParent() == pWorkWin) + { + // The window is not docked or docked outside of one split windows + // and must therefore be registered explicitly as a Child + if (pChildWin->GetController()) + pCW->pCli = RegisterChild_Impl(pChildWin->GetController(), pChildWin->GetAlignment()); + else + pCW->pCli = RegisterChild_Impl(*(pChildWin->GetWindow()), pChildWin->GetAlignment()); + pCW->pCli->nVisible = SfxChildVisibility::VISIBLE; + if ( pChildWin->GetAlignment() != SfxChildAlignment::NOALIGNMENT && bIsFullScreen ) + pCW->pCli->nVisible ^= SfxChildVisibility::ACTIVE; + pCW->pCli->bSetFocus = bSetFocus; + } + else + { + // A docked window which parent is not a WorkingWindow, must lie + // in a SplitWindow and thus not be explicitly registered. + // This happens already in the initialization of SfxDockingWindows! + } + + // Save the information in the INI file + SaveStatus_Impl(pChildWin, pCW->aInfo); +} + +void SfxWorkWindow::RemoveChildWin_Impl( SfxChildWin_Impl *pCW ) +{ + sal_uInt16 nId = pCW->nSaveId; + SfxChildWindow *pChildWin = pCW->pWin; + + // Save the information in the INI file + SfxChildWindowFlags nFlags = pCW->aInfo.nFlags; + pCW->aInfo = pChildWin->GetInfo(); + pCW->aInfo.nFlags |= nFlags; + SaveStatus_Impl(pChildWin, pCW->aInfo); + + pChildWin->Hide(); + + if ( pCW->pCli ) + { + // Child window is a direct child window and must therefore unregister + // itself from the WorkWindow + pCW->pCli = nullptr; + if (pChildWin->GetController()) + ReleaseChild_Impl(*pChildWin->GetController()); + else + ReleaseChild_Impl(*pChildWin->GetWindow()); + } + else + { + // ChildWindow is within a SplitWindow and unregister itself in + // the destructor. + } + + pWorkWin->GetSystemWindow()->GetTaskPaneList()->RemoveWindow( pChildWin->GetWindow() ); + pCW->pWin = nullptr; + pChildWin->Destroy(); + + GetBindings().Invalidate( nId ); +} + +void SfxWorkWindow::ResetStatusBar_Impl() +{ + aStatBar.eId = StatusBarId::None; +} + +void SfxWorkWindow::SetStatusBar_Impl(StatusBarId eId) +{ + if (eId != StatusBarId::None && bShowStatusBar && IsVisible_Impl()) + aStatBar.eId = eId; +} + +void SfxWorkWindow::UpdateStatusBar_Impl() +{ + Reference< css::beans::XPropertySet > xPropSet( GetFrameInterface(), UNO_QUERY ); + Reference< css::frame::XLayoutManager > xLayoutManager; + + Any aValue = xPropSet->getPropertyValue( g_aLayoutManagerPropName ); + aValue >>= xLayoutManager; + + // No status bar, if no ID is required or when in FullScreenView or + // if disabled + if (aStatBar.eId != StatusBarId::None && IsDockingAllowed() && bInternalDockingAllowed && bShowStatusBar && + !bIsFullScreen) + { + // Id has changed, thus create a suitable Statusbarmanager, this takes + // over the current status bar; + if ( xLayoutManager.is() ) + xLayoutManager->requestElement( g_aStatusBarResName ); + } + else + { + // Destroy the current StatusBar + // The Manager only creates the Status bar, does not destroy it. + if ( xLayoutManager.is() ) + xLayoutManager->destroyElement( g_aStatusBarResName ); + } +} + +void SfxWorkWindow::MakeVisible_Impl( bool bVis ) +{ + if ( bVis ) + nOrigMode = SfxVisibilityFlags::Standard; + else + nOrigMode = SfxVisibilityFlags::Invisible; + + if ( nOrigMode != nUpdateMode) + nUpdateMode = nOrigMode; +} + +bool SfxWorkWindow::IsVisible_Impl() const +{ + return nOrigMode != SfxVisibilityFlags::Invisible; +} + + +void SfxWorkWindow::HidePopups_Impl(bool bHide, sal_uInt16 nId ) +{ + if (comphelper::LibreOfficeKit::isActive() && bHide) + return; + + for (const std::unique_ptr<SfxChildWin_Impl>& i : aChildWins) + { + SfxChildWindow *pCW = i->pWin; + if (pCW && pCW->GetAlignment() == SfxChildAlignment::NOALIGNMENT && pCW->GetType() != nId) + { + vcl::Window *pWin = pCW->GetWindow(); + SfxChild_Impl *pChild = FindChild_Impl(pWin); + if (!pChild) + { + SAL_WARN("sfx.appl", "missing SfxChild_Impl child!"); + continue; + } + if (bHide) + { + pChild->nVisible &= ~SfxChildVisibility::ACTIVE; + pCW->Hide(); + } + else if ( !comphelper::LibreOfficeKit::isActive() || + SfxChildVisibility::ACTIVE != (pChild->nVisible & SfxChildVisibility::ACTIVE) ) + { + pChild->nVisible |= SfxChildVisibility::ACTIVE; + if ( SfxChildVisibility::VISIBLE == (pChild->nVisible & SfxChildVisibility::VISIBLE) ) + pCW->Show( ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + } + } +} + + +void SfxWorkWindow::ConfigChild_Impl(SfxChildIdentifier eChild, + SfxDockingConfig eConfig, sal_uInt16 nId) +{ + SfxDockingWindow* pDockWin=nullptr; + sal_uInt16 nPos = USHRT_MAX; + vcl::Window *pWin=nullptr; + SfxChildWin_Impl *pCW = nullptr; + + // configure direct childwindow + for (const std::unique_ptr<SfxChildWin_Impl>& i : aChildWins) + { + pCW = i.get(); + SfxChildWindow *pChild = pCW->pWin; + if ( pChild && (pChild->GetType() == nId )) + { + if (SfxDockingWindow* pSfxDockingWindow = dynamic_cast<SfxDockingWindow*>(pChild->GetWindow())) + { + // it's a DockingWindow + pDockWin = pSfxDockingWindow; + } + else + { + // FloatingWindow or ModelessDialog + pWin = pChild->GetWindow(); + } + break; + } + } + + if ( pDockWin ) + { + if ( eChild == SfxChildIdentifier::DOCKINGWINDOW || pDockWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT ) + { + if ( eChild == SfxChildIdentifier::SPLITWINDOW && eConfig == SfxDockingConfig::TOGGLEFLOATMODE) + { + // DockingWindow was dragged out of a SplitWindow + pCW->pCli = RegisterChild_Impl(*pDockWin, pDockWin->GetAlignment()); + pCW->pCli->nVisible = SfxChildVisibility::VISIBLE; + } + + pWin = pDockWin; + } + else + { + SfxSplitWindow *pSplitWin = GetSplitWindow_Impl(pDockWin->GetAlignment()); + + // configure DockingWindow inside a SplitWindow + if ( eConfig == SfxDockingConfig::TOGGLEFLOATMODE) + { + // DockingWindow was dragged into a SplitWindow + pCW->pCli = nullptr; + ReleaseChild_Impl(*pDockWin); + } + + pWin = pSplitWin->GetSplitWindow(); + if ( pSplitWin->GetWindowCount() == 1 ) + static_cast<SplitWindow*>(pWin)->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + } + + DBG_ASSERT( pCW, "Unknown window!" ); + + if ( !bSorted ) + // windows may have been registered and released without an update until now + Sort_Impl(); + + decltype(aSortedList)::size_type n; + for ( n=0; n<aSortedList.size(); ++n ) + { + SfxChild_Impl *pChild = aChildren[aSortedList[n]].get(); + if ( pChild && pChild->pWin == pWin ) + break; + } + + if ( n < aSortedList.size() ) + // sometimes called while toggling float mode + nPos = aSortedList[n]; + + switch ( eConfig ) + { + case SfxDockingConfig::SETDOCKINGRECTS : + { + if (nPos == USHRT_MAX || !pDockWin) + return; + + tools::Rectangle aOuterRect( GetTopRect_Impl() ); + aOuterRect.SetPos( pWorkWin->OutputToScreenPixel( aOuterRect.TopLeft() )); + tools::Rectangle aInnerRect( aOuterRect ); + + // The current affected window is included in the calculation of + // the inner rectangle! + for (sal_uInt16 i : aSortedList) + { + SfxChild_Impl* pCli = aChildren[i].get(); + + if ( pCli && pCli->nVisible == SfxChildVisibility::VISIBLE && pCli->pWin ) + { + switch ( pCli->eAlign ) + { + case SfxChildAlignment::TOP: + // Object-Toolboxes come always last + aInnerRect.AdjustTop(pCli->aSize.Height() ); + break; + + case SfxChildAlignment::HIGHESTTOP: + // Always performed first + aInnerRect.AdjustTop(pCli->aSize.Height() ); + break; + + case SfxChildAlignment::LOWESTTOP: + // Is only counted if it is the current window + if ( i == nPos ) + aInnerRect.AdjustTop(pCli->aSize.Height() ); + break; + + case SfxChildAlignment::BOTTOM: + // Object-Toolboxes come always last + aInnerRect.AdjustBottom( -(pCli->aSize.Height()) ); + break; + + case SfxChildAlignment::LOWESTBOTTOM: + // Always performed first + aInnerRect.AdjustBottom( -(pCli->aSize.Height()) ); + break; + + case SfxChildAlignment::HIGHESTBOTTOM: + // Is only counted if it is the current window + if ( i == nPos ) + aInnerRect.AdjustBottom( -(pCli->aSize.Height()) ); + break; + + case SfxChildAlignment::LEFT: + // Toolboxes come always last + aInnerRect.AdjustLeft(pCli->aSize.Width() ); + break; + + case SfxChildAlignment::FIRSTLEFT: + // Always performed first + aInnerRect.AdjustLeft(pCli->aSize.Width() ); + break; + + case SfxChildAlignment::LASTLEFT: + // Is only counted if it is the current window + if (i == nPos) + aInnerRect.AdjustLeft(pCli->aSize.Width() ); + break; + + case SfxChildAlignment::RIGHT: + // Toolboxes come always last + aInnerRect.AdjustRight( -(pCli->aSize.Width()) ); + break; + + case SfxChildAlignment::FIRSTRIGHT: + // Is only counted if it is the current window + if (i == nPos) + aInnerRect.AdjustRight( -(pCli->aSize.Width()) ); + break; + + case SfxChildAlignment::LASTRIGHT: + // Always performed first + aInnerRect.AdjustRight( -(pCli->aSize.Width()) ); + break; + + default: + break; + } + } + } + + pDockWin->SetDockingRects(aOuterRect, aInnerRect); + break; + } + + case SfxDockingConfig::ALIGNDOCKINGWINDOW : + case SfxDockingConfig::TOGGLEFLOATMODE: + { + if ( nPos == USHRT_MAX && !pCW ) + return; + + SfxChildAlignment eAlign = SfxChildAlignment::NOALIGNMENT; + SfxChild_Impl *pCli = ( nPos != USHRT_MAX ) ? aChildren[nPos].get() : nullptr; + if ( pCli && pDockWin ) + { + eAlign = pDockWin->GetAlignment(); + if ( eChild == SfxChildIdentifier::DOCKINGWINDOW || eAlign == SfxChildAlignment::NOALIGNMENT) + { + // configuration inside the SplitWindow, no change for the SplitWindows' configuration + pCli->bResize = true; + pCli->aSize = pDockWin->GetSizePixel(); + } + } + + if ( pCli ) + { + if( pCli->eAlign != eAlign ) + { + bSorted = false; + pCli->eAlign = eAlign; + } + + ArrangeChildren_Impl(); + ShowChildren_Impl(); + } + + if ( pCW && pCW->pWin ) + { + // store changed configuration + SfxChildWindowFlags nFlags = pCW->aInfo.nFlags; + pCW->aInfo = pCW->pWin->GetInfo(); + pCW->aInfo.nFlags |= nFlags; + SaveStatus_Impl( pCW->pWin, pCW->aInfo); + } + + break; + } + } +} + +namespace +{ +template <sal_uInt16 SfxChildWin_Impl::*Member> +SfxChildWin_Impl* Get_Impl(const std::vector<std::unique_ptr<SfxChildWin_Impl>>& rChildWins, + sal_uInt16 nId) +{ + for (auto& pChildWin : rChildWins) + if (pChildWin.get()->*Member == nId) + return pChildWin.get(); + return nullptr; +} +auto Get_ById = Get_Impl<&SfxChildWin_Impl::nId>; +auto Get_BySaveId = Get_Impl<&SfxChildWin_Impl::nSaveId>; +} + +void SfxWorkWindow::SetChildWindowVisible_Impl( sal_uInt32 lId, bool bEnabled, SfxVisibilityFlags nMode ) +{ + sal_uInt16 nId = static_cast<sal_uInt16>( lId & 0xFFFF ); + + SfxChildWin_Impl* pCW = Get_BySaveId(aChildWins, nId); + + if ( !pCW ) + { + // If new, then initialize, add this here depending on the flag or + // the Parent + pCW = new SfxChildWin_Impl( lId ); + pCW->nId = nId; + InitializeChild_Impl( pCW ); + aChildWins.push_back( std::unique_ptr<SfxChildWin_Impl>(pCW) ); + } + + pCW->nId = nId; + pCW->nVisibility = nMode; + pCW->bEnable = bEnabled; +} + + +// The on/off status of a ChildWindow is switched + +void SfxWorkWindow::ToggleChildWindow_Impl(sal_uInt16 nId, bool bSetFocus) +{ + if (SfxChildWin_Impl* pCW = Get_ById(aChildWins, nId)) + { + // The Window is already known + SfxChildWindow *pChild = pCW->pWin; + + bool bCreationAllowed( true ); + if ( !bInternalDockingAllowed ) + { + // Special case for all non-floatable child windows. We have + // to prevent the creation here! + bCreationAllowed = !( pCW->aInfo.nFlags & SfxChildWindowFlags::FORCEDOCK ); + } + + if ( bCreationAllowed ) + { + if ( pCW->bCreate ) + { + if ( pChild ) + { + if ( pChild->QueryClose() ) + { + pCW->bCreate = false; + // The Window should be switched off + pChild->SetVisible_Impl( false ); + RemoveChildWin_Impl( pCW ); + } + } + else + { + // no actual Window exists, yet => just remember the "switched off" state + pCW->bCreate = false; + } + } + else + { + pCW->bCreate = true; + if ( pChild ) + { + ShowChildWindow_Impl( nId, true, bSetFocus ); + } + else + { + // create actual Window + CreateChildWin_Impl( pCW, bSetFocus ); + if ( !pCW->pWin ) + // no success + pCW->bCreate = false; + } + } + } + + ArrangeChildren_Impl(); + ShowChildren_Impl(); + + if ( pCW->bCreate && bCreationAllowed ) + { + if ( !pCW->pCli ) + { + SfxDockingWindow *pDock = + static_cast<SfxDockingWindow*>( pCW->pWin->GetWindow() ); + if ( pDock->IsAutoHide_Impl() ) + pDock->AutoShow_Impl(); + } + } + + return; + } + +#ifdef DBG_UTIL + if (Get_BySaveId(aChildWins, nId)) + { + OSL_FAIL("The ChildWindow is not in context!"); + } + else + { + OSL_FAIL("The ChildWindow is not registered!"); + } +#endif +} + + +bool SfxWorkWindow::HasChildWindow_Impl(sal_uInt16 nId) +{ + if (const SfxChildWin_Impl* pCW = Get_BySaveId(aChildWins, nId)) + return ( pCW->pWin && pCW->bCreate ); + + return false; +} + +bool SfxWorkWindow::IsFloating( sal_uInt16 nId ) +{ + SfxChildWin_Impl* pCW = Get_BySaveId(aChildWins, nId); + + if ( !pCW ) + { + // If new, then initialize, add this here depending on the flag or + // the Parent + pCW = new SfxChildWin_Impl( nId ); + pCW->bEnable = false; + pCW->nId = 0; + pCW->nVisibility = SfxVisibilityFlags::Invisible; + InitializeChild_Impl( pCW ); + aChildWins.push_back( std::unique_ptr<SfxChildWin_Impl>(pCW) ); + } + + SfxChildAlignment eAlign; + if ( pCW->aInfo.GetExtraData_Impl( &eAlign ) ) + return( eAlign == SfxChildAlignment::NOALIGNMENT ); + else + return true; +} + + +bool SfxWorkWindow::KnowsChildWindow_Impl(sal_uInt16 nId) +{ + if (SfxChildWin_Impl* pCW = Get_BySaveId(aChildWins, nId)) + { + if ( !(pCW->aInfo.nFlags & SfxChildWindowFlags::ALWAYSAVAILABLE) && !IsVisible_Impl( pCW->nVisibility ) ) + return false; + return pCW->bEnable; + } + else + return false; +} + + +void SfxWorkWindow::SetChildWindow_Impl(sal_uInt16 nId, bool bOn, bool bSetFocus) +{ + SfxChildWin_Impl* pCW = Get_BySaveId(aChildWins, nId); + + if ( !pCW ) + { + // If new, then initialize, add this here depending on the flag or + // the Parent + pCW = new SfxChildWin_Impl( nId ); + InitializeChild_Impl( pCW ); + aChildWins.push_back( std::unique_ptr<SfxChildWin_Impl>(pCW) ); + } + + if ( pCW->bCreate != bOn ) + ToggleChildWindow_Impl(nId,bSetFocus); +} + + +void SfxWorkWindow::ShowChildWindow_Impl(sal_uInt16 nId, bool bVisible, bool bSetFocus) +{ + if (SfxChildWin_Impl* pCW = Get_ById(aChildWins, nId)) + { + SfxChildWindow *pChildWin = pCW->pWin; + if ( pChildWin ) + { + if ( bVisible ) + { + if ( pCW->pCli ) + { + pCW->pCli->bSetFocus = bSetFocus; + pCW->pCli->nVisible = SfxChildVisibility::VISIBLE; + pChildWin->Show( bSetFocus && pChildWin->WantsFocus() ? ShowFlags::NONE : ShowFlags::NoFocusChange | ShowFlags::NoActivate ); + } + else + static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Reappear_Impl(); + + } + else + { + if ( pCW->pCli ) + { + pCW->pCli->nVisible = SfxChildVisibility::VISIBLE ^ SfxChildVisibility::NOT_HIDDEN; + pCW->pWin->Hide(); + } + else + static_cast<SfxDockingWindow*>(pChildWin->GetWindow())->Disappear_Impl(); + + } + + ArrangeChildren_Impl(); + ShowChildren_Impl(); + } + else if ( bVisible ) + { + SetChildWindow_Impl( nId, true, bSetFocus ); + pChildWin = pCW->pWin; + } + + if ( pChildWin ) + { + pChildWin->SetVisible_Impl( bVisible ); + SfxChildWindowFlags nFlags = pCW->aInfo.nFlags; + pCW->aInfo = pChildWin->GetInfo(); + pCW->aInfo.nFlags |= nFlags; + if ( !pCW->bCreate ) + SaveStatus_Impl( pChildWin, pCW->aInfo ); + } + + return; + } + +#ifdef DBG_UTIL + if (Get_BySaveId(aChildWins, nId)) + { + OSL_FAIL("The ChildWindow is not in context!"); + } + else + { + OSL_FAIL("The ChildWindow is not registered"); + } +#endif +} + + +SfxChildWindow* SfxWorkWindow::GetChildWindow_Impl(sal_uInt16 nId) +{ + if (SfxChildWin_Impl* pCW = Get_BySaveId(aChildWins, nId)) + return pCW->pWin; + return nullptr; +} + + +void SfxWorkWindow::ResetChildWindows_Impl() +{ + for (std::unique_ptr<SfxChildWin_Impl>& pChildWin : aChildWins) + { + pChildWin->nId = 0; + pChildWin->bEnable = false; + } +} + +// returns the size of the area (client area) of the +// parent windows, in which the ChildWindow can be fitted. + +tools::Rectangle SfxWorkWindow::GetTopRect_Impl() const +{ + return pMasterFrame->GetTopOuterRectPixel_Impl(); +} + + +// Virtual method to find out if there is room for a ChildWindow in the +// client area of the parent. + +bool SfxWorkWindow::RequestTopToolSpacePixel_Impl( SvBorder aBorder ) +{ + return !(!IsDockingAllowed() || + aClientArea.GetWidth() < aBorder.Left() + aBorder.Right() || + aClientArea.GetHeight() < aBorder.Top() + aBorder.Bottom()); +} + +void SfxWorkWindow::SaveStatus_Impl(SfxChildWindow *pChild, const SfxChildWinInfo &rInfo) +{ + // The Status of the Presentation mode is not saved + if ( IsDockingAllowed() && bInternalDockingAllowed ) + pChild->SaveStatus(rInfo); +} + +void SfxWorkWindow::InitializeChild_Impl(SfxChildWin_Impl *pCW) +{ + SfxDispatcher *pDisp = pBindings->GetDispatcher_Impl(); + SfxViewFrame *pViewFrame = pDisp ? pDisp->GetFrame() :nullptr; + SfxModule *pMod = pViewFrame ? SfxModule::GetActiveModule(pViewFrame) :nullptr; + + OUString sModule; + if (pViewFrame) + { + try + { + uno::Reference< frame::XModuleManager2 > xModuleManager( + frame::ModuleManager::create(::comphelper::getProcessComponentContext())); + sModule = xModuleManager->identify(pViewFrame->GetFrame().GetFrameInterface()); + SvtModuleOptions::EFactory eFac = SvtModuleOptions::ClassifyFactoryByServiceName(sModule); + sModule = SvtModuleOptions::GetFactoryShortName(eFac); + } + catch (...) + { + } + } + + SfxChildWinFactory* pFact=nullptr; + SfxApplication *pApp = SfxGetpApp(); + { + pFact = pApp->GetChildWinFactoryById(pCW->nSaveId); + if ( pFact ) + { + pCW->aInfo = pFact->aInfo; + pCW->aInfo.aModule = sModule; + SfxChildWindow::InitializeChildWinFactory_Impl( + pCW->nSaveId, pCW->aInfo); + pCW->bCreate = pCW->aInfo.bVisible; + SfxChildWindowFlags nFlags = pFact->aInfo.nFlags; + if ( nFlags & SfxChildWindowFlags::TASK ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::TASK; + if ( nFlags & SfxChildWindowFlags::CANTGETFOCUS ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::CANTGETFOCUS; + if ( nFlags & SfxChildWindowFlags::FORCEDOCK ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::FORCEDOCK; + pFact->aInfo = pCW->aInfo; + return; + } + } + + if ( !pMod ) + return; + + pFact = pMod->GetChildWinFactoryById(pCW->nSaveId); + if ( !pFact ) + return; + + pCW->aInfo = pFact->aInfo; + pCW->aInfo.aModule = sModule; + SfxChildWindow::InitializeChildWinFactory_Impl( + pCW->nSaveId, pCW->aInfo); + pCW->bCreate = pCW->aInfo.bVisible; + SfxChildWindowFlags nFlags = pFact->aInfo.nFlags; + if ( nFlags & SfxChildWindowFlags::TASK ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::TASK; + if ( nFlags & SfxChildWindowFlags::CANTGETFOCUS ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::CANTGETFOCUS; + if ( nFlags & SfxChildWindowFlags::FORCEDOCK ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::FORCEDOCK; + if ( nFlags & SfxChildWindowFlags::ALWAYSAVAILABLE ) + pCW->aInfo.nFlags |= SfxChildWindowFlags::ALWAYSAVAILABLE; + pFact->aInfo = pCW->aInfo; +} + +SfxSplitWindow* SfxWorkWindow::GetSplitWindow_Impl( SfxChildAlignment eAlign ) +{ + switch ( eAlign ) + { + case SfxChildAlignment::TOP: + return pSplit[2]; + + case SfxChildAlignment::BOTTOM: + return pSplit[3]; + + case SfxChildAlignment::LEFT: + return pSplit[0]; + + case SfxChildAlignment::RIGHT: + return pSplit[1]; + + default: + return nullptr; + } +} + +void SfxWorkWindow::MakeChildrenVisible_Impl( bool bVis ) +{ + bAllChildrenVisible = bVis; + if ( bVis ) + { + if ( !bSorted ) + Sort_Impl(); + for (sal_uInt16 n : aSortedList) + { + SfxChild_Impl* pCli = aChildren[n].get(); + if ( (pCli->eAlign == SfxChildAlignment::NOALIGNMENT) || (IsDockingAllowed() && bInternalDockingAllowed) ) + pCli->nVisible |= SfxChildVisibility::ACTIVE; + } + } + else + { + if ( !bSorted ) + Sort_Impl(); + for (sal_uInt16 n : aSortedList) + { + SfxChild_Impl* pCli = aChildren[n].get(); + pCli->nVisible &= ~SfxChildVisibility::ACTIVE; + } + } +} + +bool SfxWorkWindow::IsAutoHideMode( const SfxSplitWindow *pSplitWin ) +{ + for (const VclPtr<SfxSplitWindow> & pWin : pSplit) + { + if ( pWin.get() != pSplitWin && pWin->IsAutoHide( true ) ) + return true; + } + return false; +} + + +void SfxWorkWindow::EndAutoShow_Impl( Point aPos ) +{ + for (VclPtr<SfxSplitWindow> & p : pSplit) + { + if ( p && p->IsAutoHide(false) ) + { + Point aLocalPos = p->ScreenToOutputPixel( aPos ); + tools::Rectangle aRect( Point(), p->GetSizePixel() ); + if ( !aRect.Contains( aLocalPos ) ) + p->FadeOut(); + } + } +} + +void SfxWorkWindow::ArrangeAutoHideWindows( SfxSplitWindow *pActSplitWin ) +{ + if ( m_nLock ) + return; + + tools::Rectangle aArea( aUpperClientArea ); + for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ ) + { + // Either dummy window or window in the auto-show-mode are processed + // (not pinned, FadeIn). + // Only the abandoned window may be invisible, because perhaps its + // size is just being calculated before it is displayed. + VclPtr<SfxSplitWindow> const & pSplitWin = pSplit[n]; + bool bDummyWindow = !pSplitWin->IsFadeIn(); + vcl::Window *pDummy = pSplitWin->GetSplitWindow(); + vcl::Window *pWin = bDummyWindow ? pDummy : pSplitWin; + if ( (pSplitWin->IsPinned() && !bDummyWindow) || (!pWin->IsVisible() && pActSplitWin != pSplitWin) ) + continue; + + // Width and position of the dummy window as a starting point + Size aSize = pDummy->GetSizePixel(); + Point aPos = pDummy->GetPosPixel(); + + switch ( n ) + { + case 0 : + { + // Left SplitWindow + // Get the width of the Window yourself, if no DummyWindow + if ( !bDummyWindow ) + aSize.setWidth( pSplitWin->GetSizePixel().Width() ); + + // If a Window is visible to the left, then the free region + // starts to the right from it, for example at the Client area + tools::Long nLeft = aPos.X() + aSize.Width(); + if ( nLeft > aArea.Left() ) + aArea.SetLeft( nLeft ); + break; + } + case 1 : + { + // Right SplitWindow + // Position to correct the difference of the widths + aPos.AdjustX(aSize.Width() ); + + // Get the width of the Window yourself, if no DummyWindow + if ( !bDummyWindow ) + aSize.setWidth( pSplitWin->GetSizePixel().Width() ); + + aPos.AdjustX( -(aSize.Width()) ); + + // If already a window is opened at the left side, then the + // right is not allowed to overlap this one. + if ( aPos.X() < aArea.Left() ) + { + aPos.setX( aArea.Left() ); + aSize.setWidth( aArea.GetWidth() ); + } + + // If a Window is visible to the right, then the free region + // starts to the left from it, for example at the Client area + tools::Long nRight = aPos.X(); + if ( !aArea.IsWidthEmpty() && nRight < aArea.Right() ) + aArea.SetRight( nRight ); + break; + } + case 2 : + { + // Top SplitWindow + // Get the height of the Window yourself, if no DummyWindow + if ( !bDummyWindow ) + aSize.setHeight( pSplitWin->GetSizePixel().Height() ); + + + // Adjust width with regard to if a Window is already open + // to the left or right + aPos.setX( aArea.Left() ); + aSize.setWidth( aArea.GetWidth() ); + + // If a Window is visible at the top, then the free region + // starts beneath it, for example at the Client area + tools::Long nTop = aPos.Y() + aSize.Height(); + if ( nTop > aArea.Top() ) + aArea.SetTop( nTop ); + break; + } + case 3 : + { + // The bottom SplitWindow + // Position to correct the difference of the heights + aPos.AdjustY(aSize.Height() ); + + // Get the height of the Window yourself, if no DummyWindow + if ( !bDummyWindow ) + aSize.setHeight( pSplitWin->GetSizePixel().Height() ); + + aPos.AdjustY( -(aSize.Height()) ); + + // Adjust width with regard to if a Window is already open + // to the left or right. + aPos.setX( aArea.Left() ); + aSize.setWidth( aArea.GetWidth() ); + + // If already a window is opened at the top, then the + // bottom one is not allowed to overlap this one. + if ( aPos.Y() < aArea.Top() ) + { + aPos.setY( aArea.Top() ); + aSize.setHeight( aArea.GetHeight() ); + } + + break; + } + } + + if ( !bDummyWindow ) + // the FadeIn-Window is a Floating window, which coordinates are + // set in Screen coordinates. + pSplitWin->SetPosSizePixel( pWorkWin->OutputToScreenPixel(aPos), aSize ); + else + // the docked DummyWindow + pDummy->SetPosSizePixel( aPos, aSize ); + } +} + +tools::Rectangle SfxWorkWindow::GetFreeArea( bool bAutoHide ) const +{ + if ( bAutoHide ) + { + tools::Rectangle aArea( aClientArea ); + for ( sal_uInt16 n=0; n<SFX_SPLITWINDOWS_MAX; n++ ) + { + if ( pSplit[n]->IsPinned() || !pSplit[n]->IsVisible() ) + continue; + + Size aSize = pSplit[n]->GetSizePixel(); + switch ( n ) + { + case 0 : + aArea.AdjustLeft(aSize.Width() ); + break; + case 1 : + aArea.AdjustRight( -(aSize.Width()) ); + break; + case 2 : + aArea.AdjustTop(aSize.Height() ); + break; + case 3 : + aArea.AdjustBottom( -(aSize.Height()) ); + break; + } + } + + return aArea; + } + else + return aClientArea; +} + +void SfxWorkWindow::SetActiveChild_Impl( vcl::Window *pChild ) +{ + pActiveChild = pChild; +} + +void SfxWorkWindow::DataChanged_Impl() +{ + ArrangeChildren_Impl(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/appl/xpackcreator.cxx b/sfx2/source/appl/xpackcreator.cxx new file mode 100644 index 0000000000..29ebcc6155 --- /dev/null +++ b/sfx2/source/appl/xpackcreator.cxx @@ -0,0 +1,164 @@ +/* -*- 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 <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/embed/XPackageStructureCreator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sot/stg.hxx> +#include <sot/storage.hxx> +#include <tools/stream.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/ucbhelper.hxx> +#include <ucbhelper/content.hxx> + +using namespace css; + +namespace { + +class OPackageStructureCreator : public ::cppu::WeakImplHelper< embed::XPackageStructureCreator, + lang::XServiceInfo > +{ +public: + OPackageStructureCreator() {} + + // XPackageStructureCreator + virtual void SAL_CALL convertToPackage( const OUString& aFolderUrl, const uno::Reference< io::XOutputStream >& xTargetStream ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + + +void SAL_CALL OPackageStructureCreator::convertToPackage( const OUString& aFolderUrl, + const uno::Reference< io::XOutputStream >& xTargetStream ) +{ + uno::Reference< ucb::XCommandEnvironment > xComEnv; + + if ( !xTargetStream.is() ) + throw io::IOException(); // TODO/LATER + + bool bSuccess = false; + ::ucbhelper::Content aContent; + if( ::ucbhelper::Content::create( aFolderUrl, xComEnv, comphelper::getProcessComponentContext(), aContent ) ) + { + OUString aTempURL = ::utl::CreateTempURL(); + try { + if ( aContent.isFolder() ) + { + UCBStorage* pUCBStorage = new UCBStorage( aContent, + aFolderUrl, + StreamMode::READ, + false, + true ); + tools::SvRef<SotStorage> aStorage = new SotStorage( pUCBStorage ); + + if ( !aTempURL.isEmpty() ) + { + SvFileStream aTempStream( aTempURL, StreamMode::STD_READWRITE ); + tools::SvRef<SotStorage> aTargetStorage = new SotStorage( true, aTempStream ); + aStorage->CopyTo( aTargetStorage.get() ); + aTargetStorage->Commit(); + + if ( aStorage->GetError() || aTargetStorage->GetError() || aTempStream.GetError() ) + throw io::IOException(); + + aTargetStorage = nullptr; + aStorage = nullptr; + + aTempStream.Seek( 0 ); + + uno::Sequence< sal_Int8 > aSeq( 32000 ); + sal_uInt32 nRead = 0; + do { + if ( aSeq.getLength() < 32000 ) + aSeq.realloc( 32000 ); + + nRead = aTempStream.ReadBytes(aSeq.getArray(), 32000); + if ( nRead < 32000 ) + aSeq.realloc( nRead ); + xTargetStream->writeBytes( aSeq ); + } while (aTempStream.good() && nRead); + + if ( aTempStream.GetError() ) + throw io::IOException(); + + bSuccess = true; + } + } + } + catch (const uno::RuntimeException&) + { + if ( !aTempURL.isEmpty() ) + ::utl::UCBContentHelper::Kill( aTempURL ); + + throw; + } + catch (const io::IOException&) + { + if ( !aTempURL.isEmpty() ) + ::utl::UCBContentHelper::Kill( aTempURL ); + + throw; + } + catch (const uno::Exception&) + { + } + + if ( !aTempURL.isEmpty() ) + ::utl::UCBContentHelper::Kill( aTempURL ); + } + + if ( !bSuccess ) + throw io::IOException(); // TODO/LATER: can't proceed with creation +} + +OUString SAL_CALL OPackageStructureCreator::getImplementationName() +{ + return "com.sun.star.comp.embed.PackageStructureCreator"; +} + +sal_Bool SAL_CALL OPackageStructureCreator::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OPackageStructureCreator::getSupportedServiceNames() +{ + return { "com.sun.star.embed.PackageStructureCreator", "com.sun.star.comp.embed.PackageStructureCreator" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_embed_PackageStructureCreator_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OPackageStructureCreator()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |