1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <helper/statusindicatorfactory.hxx>
23 #include <helper/statusindicator.hxx>
24 #include <helper/vclstatusindicator.hxx>
25 #include <properties.h>
27 #include <com/sun/star/awt/XWindow2.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/frame/XLayoutManager2.hpp>
31 #include <toolkit/helper/vclunohelper.hxx>
33 #include <comphelper/sequenceashashmap.hxx>
34 #include <unotools/mediadescriptor.hxx>
35 #include <vcl/svapp.hxx>
37 #include <rtl/ref.hxx>
39 #include <officecfg/Office/Common.hxx>
43 sal_Int32
StatusIndicatorFactory::m_nInReschedule
= 0; ///< static counter for rescheduling
45 constexpr OUString PROGRESS_RESOURCE
= u
"private:resource/progressbar/progressbar"_ustr
;
47 StatusIndicatorFactory::StatusIndicatorFactory(css::uno::Reference
< css::uno::XComponentContext
> xContext
)
48 : m_xContext (std::move(xContext
))
49 , m_bAllowReschedule (false)
50 , m_bAllowParentShow (false)
51 , m_bDisableReschedule(false)
55 StatusIndicatorFactory::~StatusIndicatorFactory()
57 impl_stopWakeUpThread();
60 void SAL_CALL
StatusIndicatorFactory::initialize(const css::uno::Sequence
< css::uno::Any
>& lArguments
)
62 if (lArguments
.hasElements()) {
63 std::scoped_lock
g(m_mutex
);
65 css::uno::Reference
< css::frame::XFrame
> xTmpFrame
;
66 css::uno::Reference
< css::awt::XWindow
> xTmpWindow
;
67 bool b1
= lArguments
[0] >>= xTmpFrame
;
68 bool b2
= lArguments
[0] >>= xTmpWindow
;
69 if (lArguments
.getLength() == 3 && b1
) {
70 // it's the first service constructor "createWithFrame"
72 lArguments
[1] >>= m_bDisableReschedule
;
73 lArguments
[2] >>= m_bAllowParentShow
;
74 } else if (lArguments
.getLength() == 3 && b2
) {
75 // it's the second service constructor "createWithWindow"
76 m_xPluggWindow
= xTmpWindow
;
77 lArguments
[1] >>= m_bDisableReschedule
;
78 lArguments
[2] >>= m_bAllowParentShow
;
80 // it's an old-style initialisation using properties
81 ::comphelper::SequenceAsHashMap
lArgs(lArguments
);
83 m_xFrame
= lArgs
.getUnpackedValueOrDefault(u
"Frame"_ustr
, css::uno::Reference
< css::frame::XFrame
>());
84 m_xPluggWindow
= lArgs
.getUnpackedValueOrDefault(u
"Window"_ustr
, css::uno::Reference
< css::awt::XWindow
>() );
85 m_bAllowParentShow
= lArgs
.getUnpackedValueOrDefault(u
"AllowParentShow"_ustr
, false );
86 m_bDisableReschedule
= lArgs
.getUnpackedValueOrDefault(u
"DisableReschedule"_ustr
, false );
91 m_bDisableReschedule
= true;
93 impl_createProgress();
96 css::uno::Reference
< css::task::XStatusIndicator
> SAL_CALL
StatusIndicatorFactory::createStatusIndicator()
98 return new StatusIndicator(this);
101 void SAL_CALL
StatusIndicatorFactory::update()
103 std::scoped_lock
g(m_mutex
);
104 m_bAllowReschedule
= true;
107 void StatusIndicatorFactory::start(const css::uno::Reference
< css::task::XStatusIndicator
>& xChild
,
108 const OUString
& sText
,
111 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
112 // SAFE -> ----------------------------------
114 std::scoped_lock
aWriteLock(m_mutex
);
116 // create new info structure for this child or move it to the front of our stack
117 IndicatorStack::iterator pItem
= ::std::find(m_aStack
.begin(), m_aStack
.end(), xChild
);
118 if (pItem
!= m_aStack
.end())
119 m_aStack
.erase(pItem
);
120 IndicatorInfo
aInfo(xChild
, sText
);
121 m_aStack
.push_back (aInfo
);
123 m_xActiveChild
= xChild
;
124 xProgress
= m_xProgress
;
126 // <- SAFE ----------------------------------
128 implts_makeParentVisibleIfAllowed();
131 xProgress
->start(sText
, nRange
);
133 impl_startWakeUpThread();
134 impl_reschedule(true);
137 void StatusIndicatorFactory::reset(const css::uno::Reference
< css::task::XStatusIndicator
>& xChild
)
139 css::uno::Reference
< css::task::XStatusIndicator
> xActive
;
140 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
141 // SAFE -> ----------------------------------
143 std::scoped_lock
aReadLock(m_mutex
);
145 // reset the internal info structure related to this child
146 IndicatorStack::iterator pItem
= ::std::find(m_aStack
.begin(), m_aStack
.end(), xChild
);
147 if (pItem
!= m_aStack
.end())
150 pItem
->m_sText
.clear();
153 xActive
= m_xActiveChild
;
154 xProgress
= m_xProgress
;
156 // <- SAFE ----------------------------------
158 // not the top most child => don't change UI
159 // But don't forget Reschedule!
161 (xChild
== xActive
) &&
166 impl_reschedule(true);
169 void StatusIndicatorFactory::end(const css::uno::Reference
< css::task::XStatusIndicator
>& xChild
)
171 css::uno::Reference
< css::task::XStatusIndicator
> xActive
;
172 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
174 sal_Int32 nValue
= 0;
175 // SAFE -> ----------------------------------
177 std::scoped_lock
aWriteLock(m_mutex
);
179 // remove this child from our stack
180 IndicatorStack::iterator pItem
= ::std::find(m_aStack
.begin(), m_aStack
.end(), xChild
);
181 if (pItem
!= m_aStack
.end())
182 m_aStack
.erase(pItem
);
184 // activate next child ... or finish the progress if there is no further one.
185 m_xActiveChild
.clear();
186 IndicatorStack::reverse_iterator pNext
= m_aStack
.rbegin();
187 if (pNext
!= m_aStack
.rend())
189 m_xActiveChild
= pNext
->m_xIndicator
;
190 sText
= pNext
->m_sText
;
191 nValue
= pNext
->m_nValue
;
194 xActive
= m_xActiveChild
;
195 xProgress
= m_xProgress
;
197 // <- SAFE ----------------------------------
201 // There is at least one further child indicator.
202 // Actualize our progress, so it shows these values from now.
205 xProgress
->setText (sText
);
206 xProgress
->setValue(nValue
);
211 // Our stack is empty. No further child exists.
212 // Se we must "end" our progress really
215 // Now hide the progress bar again.
218 impl_stopWakeUpThread();
221 impl_reschedule(true);
224 void StatusIndicatorFactory::setText(const css::uno::Reference
< css::task::XStatusIndicator
>& xChild
,
225 const OUString
& sText
)
227 css::uno::Reference
< css::task::XStatusIndicator
> xActive
;
228 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
229 // SAFE -> ----------------------------------
231 std::scoped_lock
aWriteLock(m_mutex
);
233 IndicatorStack::iterator pItem
= ::std::find(m_aStack
.begin(), m_aStack
.end(), xChild
);
234 if (pItem
!= m_aStack
.end())
235 pItem
->m_sText
= sText
;
237 xActive
= m_xActiveChild
;
238 xProgress
= m_xProgress
;
240 // SAFE -> ----------------------------------
242 // paint only the top most indicator
243 // but don't forget to Reschedule!
245 (xChild
== xActive
) &&
249 xProgress
->setText(sText
);
252 impl_reschedule(true);
255 void StatusIndicatorFactory::setValue( const css::uno::Reference
< css::task::XStatusIndicator
>& xChild
,
258 sal_Int32 nOldValue
= 0;
259 css::uno::Reference
< css::task::XStatusIndicator
> xActive
;
260 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
261 // SAFE -> ----------------------------------
263 std::scoped_lock
aWriteLock(m_mutex
);
265 IndicatorStack::iterator pItem
= ::std::find(m_aStack
.begin(), m_aStack
.end(), xChild
);
266 if (pItem
!= m_aStack
.end())
268 nOldValue
= pItem
->m_nValue
;
269 pItem
->m_nValue
= nValue
;
272 xActive
= m_xActiveChild
;
273 xProgress
= m_xProgress
;
275 // SAFE -> ----------------------------------
278 (xChild
== xActive
) &&
279 (nOldValue
!= nValue
) &&
283 xProgress
->setValue(nValue
);
286 impl_reschedule(false);
289 bool StatusIndicatorFactory::joinThreads()
291 WakeUpThread::joinThread();
295 void StatusIndicatorFactory::startThreads()
297 WakeUpThread::startThread();
300 void StatusIndicatorFactory::implts_makeParentVisibleIfAllowed()
302 css::uno::Reference
< css::frame::XFrame
> xFrame
;
303 css::uno::Reference
< css::awt::XWindow
> xPluggWindow
;
304 css::uno::Reference
< css::uno::XComponentContext
> xContext
;
305 // SAFE -> ----------------------------------
307 std::scoped_lock
aReadLock(m_mutex
);
309 if (!m_bAllowParentShow
)
313 xPluggWindow
= m_xPluggWindow
;
314 xContext
= m_xContext
;
316 // <- SAFE ----------------------------------
318 css::uno::Reference
< css::awt::XWindow
> xParentWindow
;
320 xParentWindow
= xFrame
->getContainerWindow();
322 xParentWindow
= std::move(xPluggWindow
);
324 // don't disturb user in case he put the loading document into the background!
325 // Suppress any setVisible() or toFront() call in case the initial show was
327 css::uno::Reference
< css::awt::XWindow2
> xVisibleCheck(xParentWindow
, css::uno::UNO_QUERY
);
328 bool bIsVisible
= false;
329 if (xVisibleCheck
.is())
330 bIsVisible
= xVisibleCheck
->isVisible();
338 // Check if the layout manager has been set to invisible state. It this case we are also
339 // not allowed to set the frame visible!
340 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
343 css::uno::Reference
< css::frame::XLayoutManager2
> xLayoutManager
;
344 xPropSet
->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER
) >>= xLayoutManager
;
345 if (xLayoutManager
.is())
347 if ( !xLayoutManager
->isVisible() )
352 // Ok the window should be made visible... because it is not currently visible.
354 // We need a Hack for our applications: They get her progress from the frame directly
355 // on saving documents. Because there is no progress set on the MediaDescriptor.
356 // But that's wrong. In case the document was opened hidden, they should not use any progress .-(
357 // They only possible workaround: don't show the parent window here, if the document was opened hidden.
358 bool bHiddenDoc
= false;
361 css::uno::Reference
< css::frame::XController
> xController
;
362 css::uno::Reference
< css::frame::XModel
> xModel
;
363 xController
= xFrame
->getController();
364 if (xController
.is())
365 xModel
= xController
->getModel();
368 utl::MediaDescriptor
lDocArgs(xModel
->getArgs());
369 bHiddenDoc
= lDocArgs
.getUnpackedValueOrDefault(
370 utl::MediaDescriptor::PROP_HIDDEN
,
378 // OK: The document was not opened in hidden mode ...
379 // and the window isn't already visible.
380 // Show it and bring it to front.
381 // But before we have to be sure, that our internal used helper progress
385 SolarMutexGuard aSolarGuard
;
386 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow(xParentWindow
);
389 bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
390 pWindow
->Show(true, bForceFrontAndFocus
? ShowFlags::ForegroundTask
: ShowFlags::NONE
);
395 void StatusIndicatorFactory::impl_createProgress()
397 css::uno::Reference
< css::frame::XFrame
> xFrame
;
398 css::uno::Reference
< css::awt::XWindow
> xWindow
;
399 // SAFE -> ----------------------------------
401 std::scoped_lock
aReadLock(m_mutex
);
404 xWindow
= m_xPluggWindow
;
406 // <- SAFE ----------------------------------
408 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
412 // use vcl based progress implementation in plugged mode
413 xProgress
= new VCLStatusIndicator(xWindow
);
415 else if (xFrame
.is())
417 // use frame layouted progress implementation
418 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
421 css::uno::Reference
< css::frame::XLayoutManager2
> xLayoutManager
;
422 xPropSet
->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER
) >>= xLayoutManager
;
423 if (xLayoutManager
.is())
425 xLayoutManager
->lock();
426 OUString
sPROGRESS_RESOURCE(PROGRESS_RESOURCE
);
427 xLayoutManager
->createElement( sPROGRESS_RESOURCE
);
428 xLayoutManager
->hideElement( sPROGRESS_RESOURCE
);
430 css::uno::Reference
< css::ui::XUIElement
> xProgressBar
= xLayoutManager
->getElement(sPROGRESS_RESOURCE
);
431 if (xProgressBar
.is())
432 xProgress
.set(xProgressBar
->getRealInterface(), css::uno::UNO_QUERY
);
433 xLayoutManager
->unlock();
438 std::scoped_lock
g(m_mutex
);
439 m_xProgress
= std::move(xProgress
);
442 void StatusIndicatorFactory::impl_showProgress()
444 css::uno::Reference
< css::frame::XFrame
> xFrame
;
445 // SAFE -> ----------------------------------
447 std::scoped_lock
aReadLock(m_mutex
);
451 // <- SAFE ----------------------------------
453 css::uno::Reference
< css::task::XStatusIndicator
> xProgress
;
458 // use frame layouted progress implementation
459 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
462 css::uno::Reference
< css::frame::XLayoutManager2
> xLayoutManager
;
463 xPropSet
->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER
) >>= xLayoutManager
;
464 if (xLayoutManager
.is())
466 // Be sure that we have always a progress. It can be that our frame
467 // was recycled and therefore the progress was destroyed!
468 // CreateElement does nothing if there is already a valid progress.
469 OUString
sPROGRESS_RESOURCE(PROGRESS_RESOURCE
);
470 xLayoutManager
->createElement( sPROGRESS_RESOURCE
);
471 xLayoutManager
->showElement( sPROGRESS_RESOURCE
);
473 css::uno::Reference
< css::ui::XUIElement
> xProgressBar
= xLayoutManager
->getElement(sPROGRESS_RESOURCE
);
474 if (xProgressBar
.is())
475 xProgress
.set(xProgressBar
->getRealInterface(), css::uno::UNO_QUERY
);
479 std::scoped_lock
g(m_mutex
);
480 m_xProgress
= std::move(xProgress
);
483 void StatusIndicatorFactory::impl_hideProgress()
485 css::uno::Reference
< css::frame::XFrame
> xFrame
;
486 // SAFE -> ----------------------------------
488 std::scoped_lock
aReadLock(m_mutex
);
492 // <- SAFE ----------------------------------
496 // use frame layouted progress implementation
497 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
500 css::uno::Reference
< css::frame::XLayoutManager2
> xLayoutManager
;
501 xPropSet
->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER
) >>= xLayoutManager
;
502 if (xLayoutManager
.is())
503 xLayoutManager
->hideElement( PROGRESS_RESOURCE
);
508 void StatusIndicatorFactory::impl_reschedule(bool bForce
)
512 std::scoped_lock
aReadLock(m_mutex
);
513 if (m_bDisableReschedule
)
518 bool bReschedule
= bForce
;
521 std::scoped_lock
g(m_mutex
);
522 bReschedule
= m_bAllowReschedule
;
523 m_bAllowReschedule
= false;
529 static std::mutex rescheduleLock
;
531 std::unique_lock
aRescheduleGuard(rescheduleLock
);
533 if (m_nInReschedule
!= 0)
536 // coverity[missing_lock: FALSE] - coverity fails to see the aRescheduleGuard ctor as taking a lock
538 aRescheduleGuard
.unlock();
543 Application::Reschedule(true);
547 aRescheduleGuard
.lock();
551 void StatusIndicatorFactory::impl_startWakeUpThread()
553 std::scoped_lock
g(m_mutex
);
555 if (m_bDisableReschedule
)
559 m_pWakeUp
.reset(new WakeUpThread(this));
562 void StatusIndicatorFactory::impl_stopWakeUpThread()
564 std::unique_ptr
<WakeUpThread
> wakeUp
;
566 std::scoped_lock
g(m_mutex
);
567 std::swap(wakeUp
, m_pWakeUp
);
573 } // namespace framework
575 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
576 com_sun_star_comp_framework_StatusIndicatorFactory_get_implementation(
577 css::uno::XComponentContext
*context
,
578 css::uno::Sequence
<css::uno::Any
> const &)
580 return cppu::acquire(new framework::StatusIndicatorFactory(context
));
583 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */