1
0
Fork 0
libreoffice/framework/source/helper/statusindicatorfactory.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

583 lines
19 KiB
C++

/* -*- 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 OUString PROGRESS_RESOURCE = u"private:resource/progressbar/progressbar"_ustr;
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(u"Frame"_ustr , css::uno::Reference< css::frame::XFrame >());
m_xPluggWindow = lArgs.getUnpackedValueOrDefault(u"Window"_ustr , css::uno::Reference< css::awt::XWindow >() );
m_bAllowParentShow = lArgs.getUnpackedValueOrDefault(u"AllowParentShow"_ustr , false );
m_bDisableReschedule = lArgs.getUnpackedValueOrDefault(u"DisableReschedule"_ustr, 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);
}
bool StatusIndicatorFactory::joinThreads()
{
WakeUpThread::joinThread();
return true;
}
void StatusIndicatorFactory::startThreads()
{
WakeUpThread::startThread();
}
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 = std::move(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 = std::move(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 = std::move(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)
m_pWakeUp.reset(new WakeUpThread(this));
}
void StatusIndicatorFactory::impl_stopWakeUpThread()
{
std::unique_ptr<WakeUpThread> wakeUp;
{
std::scoped_lock g(m_mutex);
std::swap(wakeUp, m_pWakeUp);
}
if (wakeUp)
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: */