LibreOffice Module framework (master) 1
statusindicatorfactory.cxx
Go to the documentation of this file.
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>
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
32
35#include <vcl/svapp.hxx>
36#include <mutex>
37#include <rtl/ref.hxx>
38
39#include <officecfg/Office/Common.hxx>
40
41namespace framework{
42
44
45constexpr OUStringLiteral PROGRESS_RESOURCE = u"private:resource/progressbar/progressbar";
46
47StatusIndicatorFactory::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
56{
58}
59
60void 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("Frame" , css::uno::Reference< css::frame::XFrame >());
84 m_xPluggWindow = lArgs.getUnpackedValueOrDefault("Window" , css::uno::Reference< css::awt::XWindow >() );
85 m_bAllowParentShow = lArgs.getUnpackedValueOrDefault("AllowParentShow" , false );
86 m_bDisableReschedule = lArgs.getUnpackedValueOrDefault("DisableReschedule", false );
87 }
88 }
89
90#ifdef EMSCRIPTEN
92#endif
94}
95
96css::uno::Reference< css::task::XStatusIndicator > SAL_CALL StatusIndicatorFactory::createStatusIndicator()
97{
98 return new StatusIndicator(this);
99}
100
102{
103 std::scoped_lock g(m_mutex);
104 m_bAllowReschedule = true;
105}
106
107void 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
129
130 if (xProgress.is())
131 xProgress->start(sText, nRange);
132
134 impl_reschedule(true);
135}
136
137void 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
169void 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.
217
219 }
220
221 impl_reschedule(true);
222}
223
224void 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
255void 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
290{
291 css::uno::Reference< css::frame::XFrame > xFrame;
292 css::uno::Reference< css::awt::XWindow > xPluggWindow;
293 css::uno::Reference< css::uno::XComponentContext > xContext;
294 // SAFE -> ----------------------------------
295 {
296 std::scoped_lock aReadLock(m_mutex);
297
299 return;
300
302 xPluggWindow = m_xPluggWindow;
303 xContext = m_xContext;
304 }
305 // <- SAFE ----------------------------------
306
307 css::uno::Reference< css::awt::XWindow > xParentWindow;
308 if (xFrame.is())
309 xParentWindow = xFrame->getContainerWindow();
310 else
311 xParentWindow = xPluggWindow;
312
313 // don't disturb user in case he put the loading document into the background!
314 // Suppress any setVisible() or toFront() call in case the initial show was
315 // already made.
316 css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xParentWindow, css::uno::UNO_QUERY);
317 bool bIsVisible = false;
318 if (xVisibleCheck.is())
319 bIsVisible = xVisibleCheck->isVisible();
320
321 if (bIsVisible)
322 {
324 return;
325 }
326
327 // Check if the layout manager has been set to invisible state. It this case we are also
328 // not allowed to set the frame visible!
329 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
330 if (xPropSet.is())
331 {
332 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
333 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
334 if (xLayoutManager.is())
335 {
336 if ( !xLayoutManager->isVisible() )
337 return;
338 }
339 }
340
341 // Ok the window should be made visible... because it is not currently visible.
342 // BUT..!
343 // We need a Hack for our applications: They get her progress from the frame directly
344 // on saving documents. Because there is no progress set on the MediaDescriptor.
345 // But that's wrong. In case the document was opened hidden, they should not use any progress .-(
346 // They only possible workaround: don't show the parent window here, if the document was opened hidden.
347 bool bHiddenDoc = false;
348 if (xFrame.is())
349 {
350 css::uno::Reference< css::frame::XController > xController;
351 css::uno::Reference< css::frame::XModel > xModel;
352 xController = xFrame->getController();
353 if (xController.is())
354 xModel = xController->getModel();
355 if (xModel.is())
356 {
357 utl::MediaDescriptor lDocArgs(xModel->getArgs());
358 bHiddenDoc = lDocArgs.getUnpackedValueOrDefault(
360 false);
361 }
362 }
363
364 if (bHiddenDoc)
365 return;
366
367 // OK: The document was not opened in hidden mode ...
368 // and the window isn't already visible.
369 // Show it and bring it to front.
370 // But before we have to be sure, that our internal used helper progress
371 // is visible too.
373
374 SolarMutexGuard aSolarGuard;
375 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xParentWindow);
376 if ( pWindow )
377 {
378 bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
379 pWindow->Show(true, bForceFrontAndFocus ? ShowFlags::ForegroundTask : ShowFlags::NONE );
380 }
381
382}
383
385{
386 css::uno::Reference< css::frame::XFrame > xFrame;
387 css::uno::Reference< css::awt::XWindow > xWindow;
388 // SAFE -> ----------------------------------
389 {
390 std::scoped_lock aReadLock(m_mutex);
391
393 xWindow = m_xPluggWindow;
394 }
395 // <- SAFE ----------------------------------
396
397 css::uno::Reference< css::task::XStatusIndicator > xProgress;
398
399 if (xWindow.is())
400 {
401 // use vcl based progress implementation in plugged mode
402 xProgress = new VCLStatusIndicator(xWindow);
403 }
404 else if (xFrame.is())
405 {
406 // use frame layouted progress implementation
407 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
408 if (xPropSet.is())
409 {
410 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
411 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
412 if (xLayoutManager.is())
413 {
414 xLayoutManager->lock();
415 OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
416 xLayoutManager->createElement( sPROGRESS_RESOURCE );
417 xLayoutManager->hideElement( sPROGRESS_RESOURCE );
418
419 css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE);
420 if (xProgressBar.is())
421 xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY);
422 xLayoutManager->unlock();
423 }
424 }
425 }
426
427 std::scoped_lock g(m_mutex);
428 m_xProgress = xProgress;
429}
430
432{
433 css::uno::Reference< css::frame::XFrame > xFrame;
434 // SAFE -> ----------------------------------
435 {
436 std::scoped_lock aReadLock(m_mutex);
437
439 }
440 // <- SAFE ----------------------------------
441
442 css::uno::Reference< css::task::XStatusIndicator > xProgress;
443
444 if (!xFrame.is())
445 return;
446
447 // use frame layouted progress implementation
448 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
449 if (xPropSet.is())
450 {
451 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
452 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
453 if (xLayoutManager.is())
454 {
455 // Be sure that we have always a progress. It can be that our frame
456 // was recycled and therefore the progress was destroyed!
457 // CreateElement does nothing if there is already a valid progress.
458 OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
459 xLayoutManager->createElement( sPROGRESS_RESOURCE );
460 xLayoutManager->showElement( sPROGRESS_RESOURCE );
461
462 css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE);
463 if (xProgressBar.is())
464 xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY);
465 }
466 }
467
468 std::scoped_lock g(m_mutex);
469 m_xProgress = xProgress;
470}
471
473{
474 css::uno::Reference< css::frame::XFrame > xFrame;
475 // SAFE -> ----------------------------------
476 {
477 std::scoped_lock aReadLock(m_mutex);
478
480 }
481 // <- SAFE ----------------------------------
482
483 if (xFrame.is())
484 {
485 // use frame layouted progress implementation
486 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
487 if (xPropSet.is())
488 {
489 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
490 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
491 if (xLayoutManager.is())
492 xLayoutManager->hideElement( PROGRESS_RESOURCE );
493 }
494 }
495}
496
498{
499 // SAFE ->
500 {
501 std::scoped_lock aReadLock(m_mutex);
503 return;
504 }
505 // <- SAFE
506
507 bool bReschedule = bForce;
508 if (!bReschedule)
509 {
510 std::scoped_lock g(m_mutex);
511 bReschedule = m_bAllowReschedule;
512 m_bAllowReschedule = false;
513 }
514
515 if (!bReschedule)
516 return;
517
518 static std::mutex rescheduleLock;
519 // SAFE ->
520 std::unique_lock aRescheduleGuard(rescheduleLock);
521
522 if (m_nInReschedule != 0)
523 return;
524
525 // coverity[missing_lock: FALSE] - coverity fails to see the aRescheduleGuard ctor as taking a lock
527 aRescheduleGuard.unlock();
528 // <- SAFE
529
530 {
533 }
534
535 // SAFE ->
536 aRescheduleGuard.lock();
538}
539
541{
542 std::scoped_lock g(m_mutex);
543
545 return;
546
547 if (!m_pWakeUp.is())
548 {
549 m_pWakeUp = new WakeUpThread(this);
550 m_pWakeUp->launch();
551 }
552}
553
555{
557 {
558 std::scoped_lock g(m_mutex);
559 std::swap(wakeUp, m_pWakeUp);
560 }
561 if (wakeUp.is())
562 {
563 wakeUp->stop();
564 }
565}
566
567} // namespace framework
568
569extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
571 css::uno::XComponentContext *context,
572 css::uno::Sequence<css::uno::Any> const &)
573{
574 return cppu::acquire(new framework::StatusIndicatorFactory(context));
575}
576
577/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static bool Reschedule(bool bHandleAllCurrentEvents=false)
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
TValueType getUnpackedValueOrDefault(const OUString &sKey, const TValueType &aDefault) const
implement a factory service to create new status indicator objects
virtual void SAL_CALL update() override
void setText(const css::uno::Reference< css::task::XStatusIndicator > &xChild, const OUString &sText)
bool m_bDisableReschedule
enable/disable rescheduling.
bool m_bAllowReschedule
Our WakeUpThread calls us in our interface method "XUpdatable::update().
void end(const css::uno::Reference< css::task::XStatusIndicator > &xChild)
static sal_Int32 m_nInReschedule
prevent recursive calling of Application::Reschedule().
css::uno::Reference< css::task::XStatusIndicator > m_xActiveChild
most active indicator child, which could work with our shared indicator window only.
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &lArguments) override
IndicatorStack m_aStack
stack with all current indicator children.
void setValue(const css::uno::Reference< css::task::XStatusIndicator > &xChild, sal_Int32 nValue)
void start(const css::uno::Reference< css::task::XStatusIndicator > &xChild, const OUString &sText, sal_Int32 nRange)
StatusIndicatorFactory(css::uno::Reference< css::uno::XComponentContext > xContext)
void impl_reschedule(bool bForceUpdate)
try to "share the current thread in an intelligent manner" :-)
void impl_showProgress()
shows the internal used progress.
css::uno::WeakReference< css::frame::XFrame > m_xFrame
points to the frame, where we show the progress (in case m_xProgress points to a frame progress.
css::uno::Reference< css::task::XStatusIndicator > m_xProgress
used to show the progress on the frame (layouted!) or as a plugged vcl window.
virtual css::uno::Reference< css::task::XStatusIndicator > SAL_CALL createStatusIndicator() override
rtl::Reference< WakeUpThread > m_pWakeUp
Notify us if a fix time is over.
void implts_makeParentVisibleIfAllowed()
show the parent window of this progress ... if it's allowed to do so.
void impl_createProgress()
creates a new internal used progress.
css::uno::WeakReference< css::awt::XWindow > m_xPluggWindow
points to an outside window, where we show the progress (in case we are plugged into such window).
void impl_hideProgress()
hides the internal used progress.
void reset(const css::uno::Reference< css::task::XStatusIndicator > &xChild)
bool m_bAllowParentShow
enable/disable automatic showing of our parent window.
css::uno::Reference< css::uno::XComponentContext > m_xContext
uno service manager to create own needed uno resources.
implement a status indicator object
static constexpr OUStringLiteral PROP_HIDDEN
float u
sal_Int16 nValue
css::uno::Reference< css::uno::XComponentContext > m_xContext
constexpr OUStringLiteral PROGRESS_RESOURCE
constexpr OUStringLiteral FRAME_PROPNAME_ASCII_LAYOUTMANAGER
Definition: properties.h:30
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 &)
@descr This struct hold some information about all currently running progress processes.
Reference< XController > xController
Reference< XFrame > xFrame
Reference< XModel > xModel