1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <algorithm>
21 #include <utility>
22 #include <helper/statusindicatorfactory.hxx>
23 #include <helper/statusindicator.hxx>
24 #include <helper/vclstatusindicator.hxx>
25 #include <properties.h>
26
27 #include <com/sun/star/awt/XWindow2.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/frame/XLayoutManager2.hpp>
30
31 #include <toolkit/helper/vclunohelper.hxx>
32
33 #include <comphelper/sequenceashashmap.hxx>
34 #include <unotools/mediadescriptor.hxx>
35 #include <vcl/svapp.hxx>
36 #include <mutex>
37 #include <rtl/ref.hxx>
38
39 #include <officecfg/Office/Common.hxx>
40
41 namespace framework{
42
43 sal_Int32 StatusIndicatorFactory::m_nInReschedule = 0; ///< static counter for rescheduling
44
45 constexpr OUString PROGRESS_RESOURCE = u"private:resource/progressbar/progressbar"_ustr;
46
StatusIndicatorFactory(css::uno::Reference<css::uno::XComponentContext> xContext)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)
52 {
53 }
54
~StatusIndicatorFactory()55 StatusIndicatorFactory::~StatusIndicatorFactory()
56 {
57 impl_stopWakeUpThread();
58 }
59
initialize(const css::uno::Sequence<css::uno::Any> & lArguments)60 void SAL_CALL StatusIndicatorFactory::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
61 {
62 if (lArguments.hasElements()) {
63 std::scoped_lock g(m_mutex);
64
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"
71 m_xFrame = xTmpFrame;
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;
79 } else {
80 // it's an old-style initialisation using properties
81 ::comphelper::SequenceAsHashMap lArgs(lArguments);
82
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 );
87 }
88 }
89
90 #ifdef EMSCRIPTEN
91 m_bDisableReschedule = true;
92 #endif
93 impl_createProgress();
94 }
95
createStatusIndicator()96 css::uno::Reference< css::task::XStatusIndicator > SAL_CALL StatusIndicatorFactory::createStatusIndicator()
97 {
98 return new StatusIndicator(this);
99 }
100
update()101 void SAL_CALL StatusIndicatorFactory::update()
102 {
103 std::scoped_lock g(m_mutex);
104 m_bAllowReschedule = true;
105 }
106
start(const css::uno::Reference<css::task::XStatusIndicator> & xChild,const OUString & sText,sal_Int32 nRange)107 void StatusIndicatorFactory::start(const css::uno::Reference< css::task::XStatusIndicator >& xChild,
108 const OUString& sText ,
109 sal_Int32 nRange)
110 {
111 css::uno::Reference< css::task::XStatusIndicator > xProgress;
112 // SAFE -> ----------------------------------
113 {
114 std::scoped_lock aWriteLock(m_mutex);
115
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 );
122
123 m_xActiveChild = xChild;
124 xProgress = m_xProgress;
125 }
126 // <- SAFE ----------------------------------
127
128 implts_makeParentVisibleIfAllowed();
129
130 if (xProgress.is())
131 xProgress->start(sText, nRange);
132
133 impl_startWakeUpThread();
134 impl_reschedule(true);
135 }
136
reset(const css::uno::Reference<css::task::XStatusIndicator> & xChild)137 void StatusIndicatorFactory::reset(const css::uno::Reference< css::task::XStatusIndicator >& xChild)
138 {
139 css::uno::Reference< css::task::XStatusIndicator > xActive;
140 css::uno::Reference< css::task::XStatusIndicator > xProgress;
141 // SAFE -> ----------------------------------
142 {
143 std::scoped_lock aReadLock(m_mutex);
144
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())
148 {
149 pItem->m_nValue = 0;
150 pItem->m_sText.clear();
151 }
152
153 xActive = m_xActiveChild;
154 xProgress = m_xProgress;
155 }
156 // <- SAFE ----------------------------------
157
158 // not the top most child => don't change UI
159 // But don't forget Reschedule!
160 if (
161 (xChild == xActive) &&
162 (xProgress.is() )
163 )
164 xProgress->reset();
165
166 impl_reschedule(true);
167 }
168
end(const css::uno::Reference<css::task::XStatusIndicator> & xChild)169 void StatusIndicatorFactory::end(const css::uno::Reference< css::task::XStatusIndicator >& xChild)
170 {
171 css::uno::Reference< css::task::XStatusIndicator > xActive;
172 css::uno::Reference< css::task::XStatusIndicator > xProgress;
173 OUString sText;
174 sal_Int32 nValue = 0;
175 // SAFE -> ----------------------------------
176 {
177 std::scoped_lock aWriteLock(m_mutex);
178
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);
183
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())
188 {
189 m_xActiveChild = pNext->m_xIndicator;
190 sText = pNext->m_sText;
191 nValue = pNext->m_nValue;
192 }
193
194 xActive = m_xActiveChild;
195 xProgress = m_xProgress;
196 }
197 // <- SAFE ----------------------------------
198
199 if (xActive.is())
200 {
201 // There is at least one further child indicator.
202 // Actualize our progress, so it shows these values from now.
203 if (xProgress.is())
204 {
205 xProgress->setText (sText );
206 xProgress->setValue(nValue);
207 }
208 }
209 else
210 {
211 // Our stack is empty. No further child exists.
212 // Se we must "end" our progress really
213 if (xProgress.is())
214 xProgress->end();
215 // Now hide the progress bar again.
216 impl_hideProgress();
217
218 impl_stopWakeUpThread();
219 }
220
221 impl_reschedule(true);
222 }
223
setText(const css::uno::Reference<css::task::XStatusIndicator> & xChild,const OUString & sText)224 void StatusIndicatorFactory::setText(const css::uno::Reference< css::task::XStatusIndicator >& xChild,
225 const OUString& sText )
226 {
227 css::uno::Reference< css::task::XStatusIndicator > xActive;
228 css::uno::Reference< css::task::XStatusIndicator > xProgress;
229 // SAFE -> ----------------------------------
230 {
231 std::scoped_lock aWriteLock(m_mutex);
232
233 IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
234 if (pItem != m_aStack.end())
235 pItem->m_sText = sText;
236
237 xActive = m_xActiveChild;
238 xProgress = m_xProgress;
239 }
240 // SAFE -> ----------------------------------
241
242 // paint only the top most indicator
243 // but don't forget to Reschedule!
244 if (
245 (xChild == xActive) &&
246 (xProgress.is() )
247 )
248 {
249 xProgress->setText(sText);
250 }
251
252 impl_reschedule(true);
253 }
254
setValue(const css::uno::Reference<css::task::XStatusIndicator> & xChild,sal_Int32 nValue)255 void StatusIndicatorFactory::setValue( const css::uno::Reference< css::task::XStatusIndicator >& xChild ,
256 sal_Int32 nValue )
257 {
258 sal_Int32 nOldValue = 0;
259 css::uno::Reference< css::task::XStatusIndicator > xActive;
260 css::uno::Reference< css::task::XStatusIndicator > xProgress;
261 // SAFE -> ----------------------------------
262 {
263 std::scoped_lock aWriteLock(m_mutex);
264
265 IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
266 if (pItem != m_aStack.end())
267 {
268 nOldValue = pItem->m_nValue;
269 pItem->m_nValue = nValue;
270 }
271
272 xActive = m_xActiveChild;
273 xProgress = m_xProgress;
274 }
275 // SAFE -> ----------------------------------
276
277 if (
278 (xChild == xActive) &&
279 (nOldValue != nValue ) &&
280 (xProgress.is() )
281 )
282 {
283 xProgress->setValue(nValue);
284 }
285
286 impl_reschedule(false);
287 }
288
joinThreads()289 bool StatusIndicatorFactory::joinThreads()
290 {
291 WakeUpThread::joinThread();
292 return true;
293 }
294
startThreads()295 void StatusIndicatorFactory::startThreads()
296 {
297 WakeUpThread::startThread();
298 }
299
implts_makeParentVisibleIfAllowed()300 void StatusIndicatorFactory::implts_makeParentVisibleIfAllowed()
301 {
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 -> ----------------------------------
306 {
307 std::scoped_lock aReadLock(m_mutex);
308
309 if (!m_bAllowParentShow)
310 return;
311
312 xFrame = m_xFrame;
313 xPluggWindow = m_xPluggWindow;
314 xContext = m_xContext;
315 }
316 // <- SAFE ----------------------------------
317
318 css::uno::Reference< css::awt::XWindow > xParentWindow;
319 if (xFrame.is())
320 xParentWindow = xFrame->getContainerWindow();
321 else
322 xParentWindow = std::move(xPluggWindow);
323
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
326 // already made.
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();
331
332 if (bIsVisible)
333 {
334 impl_showProgress();
335 return;
336 }
337
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);
341 if (xPropSet.is())
342 {
343 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
344 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
345 if (xLayoutManager.is())
346 {
347 if ( !xLayoutManager->isVisible() )
348 return;
349 }
350 }
351
352 // Ok the window should be made visible... because it is not currently visible.
353 // BUT..!
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;
359 if (xFrame.is())
360 {
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();
366 if (xModel.is())
367 {
368 utl::MediaDescriptor lDocArgs(xModel->getArgs());
369 bHiddenDoc = lDocArgs.getUnpackedValueOrDefault(
370 utl::MediaDescriptor::PROP_HIDDEN,
371 false);
372 }
373 }
374
375 if (bHiddenDoc)
376 return;
377
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
382 // is visible too.
383 impl_showProgress();
384
385 SolarMutexGuard aSolarGuard;
386 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xParentWindow);
387 if ( pWindow )
388 {
389 bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
390 pWindow->Show(true, bForceFrontAndFocus ? ShowFlags::ForegroundTask : ShowFlags::NONE );
391 }
392
393 }
394
impl_createProgress()395 void StatusIndicatorFactory::impl_createProgress()
396 {
397 css::uno::Reference< css::frame::XFrame > xFrame;
398 css::uno::Reference< css::awt::XWindow > xWindow;
399 // SAFE -> ----------------------------------
400 {
401 std::scoped_lock aReadLock(m_mutex);
402
403 xFrame = m_xFrame;
404 xWindow = m_xPluggWindow;
405 }
406 // <- SAFE ----------------------------------
407
408 css::uno::Reference< css::task::XStatusIndicator > xProgress;
409
410 if (xWindow.is())
411 {
412 // use vcl based progress implementation in plugged mode
413 xProgress = new VCLStatusIndicator(xWindow);
414 }
415 else if (xFrame.is())
416 {
417 // use frame layouted progress implementation
418 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
419 if (xPropSet.is())
420 {
421 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
422 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
423 if (xLayoutManager.is())
424 {
425 xLayoutManager->lock();
426 OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
427 xLayoutManager->createElement( sPROGRESS_RESOURCE );
428 xLayoutManager->hideElement( sPROGRESS_RESOURCE );
429
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();
434 }
435 }
436 }
437
438 std::scoped_lock g(m_mutex);
439 m_xProgress = std::move(xProgress);
440 }
441
impl_showProgress()442 void StatusIndicatorFactory::impl_showProgress()
443 {
444 css::uno::Reference< css::frame::XFrame > xFrame;
445 // SAFE -> ----------------------------------
446 {
447 std::scoped_lock aReadLock(m_mutex);
448
449 xFrame = m_xFrame;
450 }
451 // <- SAFE ----------------------------------
452
453 css::uno::Reference< css::task::XStatusIndicator > xProgress;
454
455 if (!xFrame.is())
456 return;
457
458 // use frame layouted progress implementation
459 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
460 if (xPropSet.is())
461 {
462 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
463 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
464 if (xLayoutManager.is())
465 {
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 );
472
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);
476 }
477 }
478
479 std::scoped_lock g(m_mutex);
480 m_xProgress = std::move(xProgress);
481 }
482
impl_hideProgress()483 void StatusIndicatorFactory::impl_hideProgress()
484 {
485 css::uno::Reference< css::frame::XFrame > xFrame;
486 // SAFE -> ----------------------------------
487 {
488 std::scoped_lock aReadLock(m_mutex);
489
490 xFrame = m_xFrame;
491 }
492 // <- SAFE ----------------------------------
493
494 if (xFrame.is())
495 {
496 // use frame layouted progress implementation
497 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
498 if (xPropSet.is())
499 {
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 );
504 }
505 }
506 }
507
impl_reschedule(bool bForce)508 void StatusIndicatorFactory::impl_reschedule(bool bForce)
509 {
510 // SAFE ->
511 {
512 std::scoped_lock aReadLock(m_mutex);
513 if (m_bDisableReschedule)
514 return;
515 }
516 // <- SAFE
517
518 bool bReschedule = bForce;
519 if (!bReschedule)
520 {
521 std::scoped_lock g(m_mutex);
522 bReschedule = m_bAllowReschedule;
523 m_bAllowReschedule = false;
524 }
525
526 if (!bReschedule)
527 return;
528
529 static std::mutex rescheduleLock;
530 // SAFE ->
531 std::unique_lock aRescheduleGuard(rescheduleLock);
532
533 if (m_nInReschedule != 0)
534 return;
535
536 // coverity[missing_lock: FALSE] - coverity fails to see the aRescheduleGuard ctor as taking a lock
537 ++m_nInReschedule;
538 aRescheduleGuard.unlock();
539 // <- SAFE
540
541 {
542 SolarMutexGuard g;
543 Application::Reschedule(true);
544 }
545
546 // SAFE ->
547 aRescheduleGuard.lock();
548 --m_nInReschedule;
549 }
550
impl_startWakeUpThread()551 void StatusIndicatorFactory::impl_startWakeUpThread()
552 {
553 std::scoped_lock g(m_mutex);
554
555 if (m_bDisableReschedule)
556 return;
557
558 if (!m_pWakeUp)
559 m_pWakeUp.reset(new WakeUpThread(this));
560 }
561
impl_stopWakeUpThread()562 void StatusIndicatorFactory::impl_stopWakeUpThread()
563 {
564 std::unique_ptr<WakeUpThread> wakeUp;
565 {
566 std::scoped_lock g(m_mutex);
567 std::swap(wakeUp, m_pWakeUp);
568 }
569 if (wakeUp)
570 wakeUp->stop();
571 }
572
573 } // namespace framework
574
575 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 &)576 com_sun_star_comp_framework_StatusIndicatorFactory_get_implementation(
577 css::uno::XComponentContext *context,
578 css::uno::Sequence<css::uno::Any> const &)
579 {
580 return cppu::acquire(new framework::StatusIndicatorFactory(context));
581 }
582
583 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
584