diff options
Diffstat (limited to 'framework/source/helper/statusindicatorfactory.cxx')
-rw-r--r-- | framework/source/helper/statusindicatorfactory.cxx | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/framework/source/helper/statusindicatorfactory.cxx b/framework/source/helper/statusindicatorfactory.cxx new file mode 100644 index 000000000..e9e54df6b --- /dev/null +++ b/framework/source/helper/statusindicatorfactory.cxx @@ -0,0 +1,577 @@ +/* -*- 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 <algorithm> +#include <utility> +#include <helper/statusindicatorfactory.hxx> +#include <helper/statusindicator.hxx> +#include <helper/vclstatusindicator.hxx> +#include <properties.h> + +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLayoutManager2.hpp> + +#include <toolkit/helper/vclunohelper.hxx> + +#include <comphelper/sequenceashashmap.hxx> +#include <unotools/mediadescriptor.hxx> +#include <vcl/svapp.hxx> +#include <mutex> +#include <rtl/ref.hxx> + +#include <officecfg/Office/Common.hxx> + +namespace framework{ + +sal_Int32 StatusIndicatorFactory::m_nInReschedule = 0; ///< static counter for rescheduling + +constexpr OUStringLiteral PROGRESS_RESOURCE = u"private:resource/progressbar/progressbar"; + +StatusIndicatorFactory::StatusIndicatorFactory(css::uno::Reference< css::uno::XComponentContext > xContext) + : m_xContext (std::move(xContext )) + , m_bAllowReschedule (false) + , m_bAllowParentShow (false) + , m_bDisableReschedule(false) +{ +} + +StatusIndicatorFactory::~StatusIndicatorFactory() +{ + impl_stopWakeUpThread(); +} + +void SAL_CALL StatusIndicatorFactory::initialize(const css::uno::Sequence< css::uno::Any >& lArguments) +{ + if (lArguments.hasElements()) { + std::scoped_lock g(m_mutex); + + css::uno::Reference< css::frame::XFrame > xTmpFrame; + css::uno::Reference< css::awt::XWindow > xTmpWindow; + bool b1 = lArguments[0] >>= xTmpFrame; + bool b2 = lArguments[0] >>= xTmpWindow; + if (lArguments.getLength() == 3 && b1) { + // it's the first service constructor "createWithFrame" + m_xFrame = xTmpFrame; + lArguments[1] >>= m_bDisableReschedule; + lArguments[2] >>= m_bAllowParentShow; + } else if (lArguments.getLength() == 3 && b2) { + // it's the second service constructor "createWithWindow" + m_xPluggWindow = xTmpWindow; + lArguments[1] >>= m_bDisableReschedule; + lArguments[2] >>= m_bAllowParentShow; + } else { + // it's an old-style initialisation using properties + ::comphelper::SequenceAsHashMap lArgs(lArguments); + + m_xFrame = lArgs.getUnpackedValueOrDefault("Frame" , css::uno::Reference< css::frame::XFrame >()); + m_xPluggWindow = lArgs.getUnpackedValueOrDefault("Window" , css::uno::Reference< css::awt::XWindow >() ); + m_bAllowParentShow = lArgs.getUnpackedValueOrDefault("AllowParentShow" , false ); + m_bDisableReschedule = lArgs.getUnpackedValueOrDefault("DisableReschedule", false ); + } + } + +#ifdef EMSCRIPTEN + m_bDisableReschedule = true; +#endif + impl_createProgress(); +} + +css::uno::Reference< css::task::XStatusIndicator > SAL_CALL StatusIndicatorFactory::createStatusIndicator() +{ + return new StatusIndicator(this); +} + +void SAL_CALL StatusIndicatorFactory::update() +{ + std::scoped_lock g(m_mutex); + m_bAllowReschedule = true; +} + +void StatusIndicatorFactory::start(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + const OUString& sText , + sal_Int32 nRange) +{ + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + // create new info structure for this child or move it to the front of our stack + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + m_aStack.erase(pItem); + IndicatorInfo aInfo(xChild, sText); + m_aStack.push_back (aInfo ); + + m_xActiveChild = xChild; + xProgress = m_xProgress; + } + // <- SAFE ---------------------------------- + + implts_makeParentVisibleIfAllowed(); + + if (xProgress.is()) + xProgress->start(sText, nRange); + + impl_startWakeUpThread(); + impl_reschedule(true); +} + +void StatusIndicatorFactory::reset(const css::uno::Reference< css::task::XStatusIndicator >& xChild) +{ + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + // reset the internal info structure related to this child + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + { + pItem->m_nValue = 0; + pItem->m_sText.clear(); + } + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // <- SAFE ---------------------------------- + + // not the top most child => don't change UI + // But don't forget Reschedule! + if ( + (xChild == xActive) && + (xProgress.is() ) + ) + xProgress->reset(); + + impl_reschedule(true); +} + +void StatusIndicatorFactory::end(const css::uno::Reference< css::task::XStatusIndicator >& xChild) +{ + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + OUString sText; + sal_Int32 nValue = 0; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + // remove this child from our stack + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + m_aStack.erase(pItem); + + // activate next child ... or finish the progress if there is no further one. + m_xActiveChild.clear(); + IndicatorStack::reverse_iterator pNext = m_aStack.rbegin(); + if (pNext != m_aStack.rend()) + { + m_xActiveChild = pNext->m_xIndicator; + sText = pNext->m_sText; + nValue = pNext->m_nValue; + } + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // <- SAFE ---------------------------------- + + if (xActive.is()) + { + // There is at least one further child indicator. + // Actualize our progress, so it shows these values from now. + if (xProgress.is()) + { + xProgress->setText (sText ); + xProgress->setValue(nValue); + } + } + else + { + // Our stack is empty. No further child exists. + // Se we must "end" our progress really + if (xProgress.is()) + xProgress->end(); + // Now hide the progress bar again. + impl_hideProgress(); + + impl_stopWakeUpThread(); + } + + impl_reschedule(true); +} + +void StatusIndicatorFactory::setText(const css::uno::Reference< css::task::XStatusIndicator >& xChild, + const OUString& sText ) +{ + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + pItem->m_sText = sText; + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // SAFE -> ---------------------------------- + + // paint only the top most indicator + // but don't forget to Reschedule! + if ( + (xChild == xActive) && + (xProgress.is() ) + ) + { + xProgress->setText(sText); + } + + impl_reschedule(true); +} + +void StatusIndicatorFactory::setValue( const css::uno::Reference< css::task::XStatusIndicator >& xChild , + sal_Int32 nValue ) +{ + sal_Int32 nOldValue = 0; + css::uno::Reference< css::task::XStatusIndicator > xActive; + css::uno::Reference< css::task::XStatusIndicator > xProgress; + // SAFE -> ---------------------------------- + { + std::scoped_lock aWriteLock(m_mutex); + + IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild); + if (pItem != m_aStack.end()) + { + nOldValue = pItem->m_nValue; + pItem->m_nValue = nValue; + } + + xActive = m_xActiveChild; + xProgress = m_xProgress; + } + // SAFE -> ---------------------------------- + + if ( + (xChild == xActive) && + (nOldValue != nValue ) && + (xProgress.is() ) + ) + { + xProgress->setValue(nValue); + } + + impl_reschedule(false); +} + +void StatusIndicatorFactory::implts_makeParentVisibleIfAllowed() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + css::uno::Reference< css::awt::XWindow > xPluggWindow; + css::uno::Reference< css::uno::XComponentContext > xContext; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + if (!m_bAllowParentShow) + return; + + xFrame = m_xFrame; + xPluggWindow = m_xPluggWindow; + xContext = m_xContext; + } + // <- SAFE ---------------------------------- + + css::uno::Reference< css::awt::XWindow > xParentWindow; + if (xFrame.is()) + xParentWindow = xFrame->getContainerWindow(); + else + xParentWindow = xPluggWindow; + + // don't disturb user in case he put the loading document into the background! + // Suppress any setVisible() or toFront() call in case the initial show was + // already made. + css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xParentWindow, css::uno::UNO_QUERY); + bool bIsVisible = false; + if (xVisibleCheck.is()) + bIsVisible = xVisibleCheck->isVisible(); + + if (bIsVisible) + { + impl_showProgress(); + return; + } + + // Check if the layout manager has been set to invisible state. It this case we are also + // not allowed to set the frame visible! + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + if ( !xLayoutManager->isVisible() ) + return; + } + } + + // Ok the window should be made visible... because it is not currently visible. + // BUT..! + // We need a Hack for our applications: They get her progress from the frame directly + // on saving documents. Because there is no progress set on the MediaDescriptor. + // But that's wrong. In case the document was opened hidden, they should not use any progress .-( + // They only possible workaround: don't show the parent window here, if the document was opened hidden. + bool bHiddenDoc = false; + if (xFrame.is()) + { + css::uno::Reference< css::frame::XController > xController; + css::uno::Reference< css::frame::XModel > xModel; + xController = xFrame->getController(); + if (xController.is()) + xModel = xController->getModel(); + if (xModel.is()) + { + utl::MediaDescriptor lDocArgs(xModel->getArgs()); + bHiddenDoc = lDocArgs.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_HIDDEN, + false); + } + } + + if (bHiddenDoc) + return; + + // OK: The document was not opened in hidden mode ... + // and the window isn't already visible. + // Show it and bring it to front. + // But before we have to be sure, that our internal used helper progress + // is visible too. + impl_showProgress(); + + SolarMutexGuard aSolarGuard; + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xParentWindow); + if ( pWindow ) + { + bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get()); + pWindow->Show(true, bForceFrontAndFocus ? ShowFlags::ForegroundTask : ShowFlags::NONE ); + } + +} + +void StatusIndicatorFactory::impl_createProgress() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + css::uno::Reference< css::awt::XWindow > xWindow; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + xFrame = m_xFrame; + xWindow = m_xPluggWindow; + } + // <- SAFE ---------------------------------- + + css::uno::Reference< css::task::XStatusIndicator > xProgress; + + if (xWindow.is()) + { + // use vcl based progress implementation in plugged mode + xProgress = new VCLStatusIndicator(xWindow); + } + else if (xFrame.is()) + { + // use frame layouted progress implementation + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + xLayoutManager->lock(); + OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE); + xLayoutManager->createElement( sPROGRESS_RESOURCE ); + xLayoutManager->hideElement( sPROGRESS_RESOURCE ); + + css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE); + if (xProgressBar.is()) + xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY); + xLayoutManager->unlock(); + } + } + } + + std::scoped_lock g(m_mutex); + m_xProgress = xProgress; +} + +void StatusIndicatorFactory::impl_showProgress() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + xFrame = m_xFrame; + } + // <- SAFE ---------------------------------- + + css::uno::Reference< css::task::XStatusIndicator > xProgress; + + if (!xFrame.is()) + return; + + // use frame layouted progress implementation + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + // Be sure that we have always a progress. It can be that our frame + // was recycled and therefore the progress was destroyed! + // CreateElement does nothing if there is already a valid progress. + OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE); + xLayoutManager->createElement( sPROGRESS_RESOURCE ); + xLayoutManager->showElement( sPROGRESS_RESOURCE ); + + css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE); + if (xProgressBar.is()) + xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY); + } + } + + std::scoped_lock g(m_mutex); + m_xProgress = xProgress; +} + +void StatusIndicatorFactory::impl_hideProgress() +{ + css::uno::Reference< css::frame::XFrame > xFrame; + // SAFE -> ---------------------------------- + { + std::scoped_lock aReadLock(m_mutex); + + xFrame = m_xFrame; + } + // <- SAFE ---------------------------------- + + if (xFrame.is()) + { + // use frame layouted progress implementation + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + xLayoutManager->hideElement( PROGRESS_RESOURCE ); + } + } +} + +void StatusIndicatorFactory::impl_reschedule(bool bForce) +{ + // SAFE -> + { + std::scoped_lock aReadLock(m_mutex); + if (m_bDisableReschedule) + return; + } + // <- SAFE + + bool bReschedule = bForce; + if (!bReschedule) + { + std::scoped_lock g(m_mutex); + bReschedule = m_bAllowReschedule; + m_bAllowReschedule = false; + } + + if (!bReschedule) + return; + + static std::mutex rescheduleLock; + // SAFE -> + std::unique_lock aRescheduleGuard(rescheduleLock); + + if (m_nInReschedule != 0) + return; + + // coverity[missing_lock: FALSE] - coverity fails to see the aRescheduleGuard ctor as taking a lock + ++m_nInReschedule; + aRescheduleGuard.unlock(); + // <- SAFE + + { + SolarMutexGuard g; + Application::Reschedule(true); + } + + // SAFE -> + aRescheduleGuard.lock(); + --m_nInReschedule; +} + +void StatusIndicatorFactory::impl_startWakeUpThread() +{ + std::scoped_lock g(m_mutex); + + if (m_bDisableReschedule) + return; + + if (!m_pWakeUp.is()) + { + m_pWakeUp = new WakeUpThread(this); + m_pWakeUp->launch(); + } +} + +void StatusIndicatorFactory::impl_stopWakeUpThread() +{ + rtl::Reference<WakeUpThread> wakeUp; + { + std::scoped_lock g(m_mutex); + std::swap(wakeUp, m_pWakeUp); + } + if (wakeUp.is()) + { + wakeUp->stop(); + } +} + +} // namespace framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_framework_StatusIndicatorFactory_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new framework::StatusIndicatorFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |