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 <services.h>
26 #include <properties.h>
27
28 #include <com/sun/star/awt/Rectangle.hpp>
29
30 #include <com/sun/star/awt/XControl.hpp>
31 #include <com/sun/star/awt/XLayoutConstrains.hpp>
32 #include <com/sun/star/awt/DeviceInfo.hpp>
33 #include <com/sun/star/awt/PosSize.hpp>
34 #include <com/sun/star/awt/WindowAttribute.hpp>
35 #include <com/sun/star/awt/XTopWindow.hpp>
36 #include <com/sun/star/awt/XWindow2.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/frame/XLayoutManager2.hpp>
39
40 #include <toolkit/helper/vclunohelper.hxx>
41
42 #include <comphelper/sequenceashashmap.hxx>
43 #include <unotools/mediadescriptor.hxx>
44 #include <vcl/svapp.hxx>
45 #include <osl/mutex.hxx>
46 #include <rtl/ref.hxx>
47
48 #include <officecfg/Office/Common.hxx>
49
50 namespace framework{
51
52 sal_Int32 StatusIndicatorFactory::m_nInReschedule = 0; ///< static counter for rescheduling
53 struct RescheduleLock: public rtl::Static<osl::Mutex, RescheduleLock> {}; ///< mutex to guard the m_nInReschedule
54
55 const char PROGRESS_RESOURCE[] = "private:resource/progressbar/progressbar";
56
StatusIndicatorFactory(const css::uno::Reference<css::uno::XComponentContext> & xContext)57 StatusIndicatorFactory::StatusIndicatorFactory(const css::uno::Reference< css::uno::XComponentContext >& xContext)
58 : m_xContext (xContext )
59 , m_bAllowReschedule (false)
60 , m_bAllowParentShow (false)
61 , m_bDisableReschedule(false)
62 {
63 }
64
~StatusIndicatorFactory()65 StatusIndicatorFactory::~StatusIndicatorFactory()
66 {
67 impl_stopWakeUpThread();
68 }
69
initialize(const css::uno::Sequence<css::uno::Any> & lArguments)70 void SAL_CALL StatusIndicatorFactory::initialize(const css::uno::Sequence< css::uno::Any >& lArguments)
71 {
72 if (lArguments.hasElements()) {
73 osl::MutexGuard g(m_mutex);
74
75 css::uno::Reference< css::frame::XFrame > xTmpFrame;
76 css::uno::Reference< css::awt::XWindow > xTmpWindow;
77 bool b1 = lArguments[0] >>= xTmpFrame;
78 bool b2 = lArguments[0] >>= xTmpWindow;
79 if (lArguments.getLength() == 3 && b1) {
80 // it's the first service constructor "createWithFrame"
81 m_xFrame = xTmpFrame;
82 lArguments[1] >>= m_bDisableReschedule;
83 lArguments[2] >>= m_bAllowParentShow;
84 } else if (lArguments.getLength() == 3 && b2) {
85 // it's the second service constructor "createWithWindow"
86 m_xPluggWindow = xTmpWindow;
87 lArguments[1] >>= m_bDisableReschedule;
88 lArguments[2] >>= m_bAllowParentShow;
89 } else {
90 // it's an old-style initialisation using properties
91 ::comphelper::SequenceAsHashMap lArgs(lArguments);
92
93 m_xFrame = lArgs.getUnpackedValueOrDefault("Frame" , css::uno::Reference< css::frame::XFrame >());
94 m_xPluggWindow = lArgs.getUnpackedValueOrDefault("Window" , css::uno::Reference< css::awt::XWindow >() );
95 m_bAllowParentShow = lArgs.getUnpackedValueOrDefault("AllowParentShow" , false );
96 m_bDisableReschedule = lArgs.getUnpackedValueOrDefault("DisableReschedule", false );
97 }
98 }
99
100 impl_createProgress();
101 }
102
createStatusIndicator()103 css::uno::Reference< css::task::XStatusIndicator > SAL_CALL StatusIndicatorFactory::createStatusIndicator()
104 {
105 StatusIndicator* pIndicator = new StatusIndicator(this);
106 css::uno::Reference< css::task::XStatusIndicator > xIndicator(static_cast< ::cppu::OWeakObject* >(pIndicator), css::uno::UNO_QUERY_THROW);
107
108 return xIndicator;
109 }
110
update()111 void SAL_CALL StatusIndicatorFactory::update()
112 {
113 osl::MutexGuard g(m_mutex);
114 m_bAllowReschedule = true;
115 }
116
start(const css::uno::Reference<css::task::XStatusIndicator> & xChild,const OUString & sText,sal_Int32 nRange)117 void StatusIndicatorFactory::start(const css::uno::Reference< css::task::XStatusIndicator >& xChild,
118 const OUString& sText ,
119 sal_Int32 nRange)
120 {
121 // SAFE -> ----------------------------------
122 osl::ClearableMutexGuard aWriteLock(m_mutex);
123
124 // create new info structure for this child or move it to the front of our stack
125 IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
126 if (pItem != m_aStack.end())
127 m_aStack.erase(pItem);
128 IndicatorInfo aInfo(xChild, sText);
129 m_aStack.push_back (aInfo );
130
131 m_xActiveChild = xChild;
132 css::uno::Reference< css::task::XStatusIndicator > xProgress = m_xProgress;
133
134 aWriteLock.clear();
135 // <- SAFE ----------------------------------
136
137 implts_makeParentVisibleIfAllowed();
138
139 if (xProgress.is())
140 xProgress->start(sText, nRange);
141
142 impl_startWakeUpThread();
143 impl_reschedule(true);
144 }
145
reset(const css::uno::Reference<css::task::XStatusIndicator> & xChild)146 void StatusIndicatorFactory::reset(const css::uno::Reference< css::task::XStatusIndicator >& xChild)
147 {
148 // SAFE -> ----------------------------------
149 osl::ClearableMutexGuard aReadLock(m_mutex);
150
151 // reset the internal info structure related to this child
152 IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
153 if (pItem != m_aStack.end())
154 {
155 pItem->m_nValue = 0;
156 pItem->m_sText.clear();
157 }
158
159 css::uno::Reference< css::task::XStatusIndicator > xActive = m_xActiveChild;
160 css::uno::Reference< css::task::XStatusIndicator > xProgress = m_xProgress;
161
162 aReadLock.clear();
163 // <- SAFE ----------------------------------
164
165 // not the top most child => don't change UI
166 // But don't forget Reschedule!
167 if (
168 (xChild == xActive) &&
169 (xProgress.is() )
170 )
171 xProgress->reset();
172
173 impl_reschedule(true);
174 }
175
end(const css::uno::Reference<css::task::XStatusIndicator> & xChild)176 void StatusIndicatorFactory::end(const css::uno::Reference< css::task::XStatusIndicator >& xChild)
177 {
178 // SAFE -> ----------------------------------
179 osl::ClearableMutexGuard aWriteLock(m_mutex);
180
181 // remove this child from our stack
182 IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
183 if (pItem != m_aStack.end())
184 m_aStack.erase(pItem);
185
186 // activate next child ... or finish the progress if there is no further one.
187 m_xActiveChild.clear();
188 OUString sText;
189 sal_Int32 nValue = 0;
190 IndicatorStack::reverse_iterator pNext = m_aStack.rbegin();
191 if (pNext != m_aStack.rend())
192 {
193 m_xActiveChild = pNext->m_xIndicator;
194 sText = pNext->m_sText;
195 nValue = pNext->m_nValue;
196 }
197
198 css::uno::Reference< css::task::XStatusIndicator > xActive = m_xActiveChild;
199 css::uno::Reference< css::task::XStatusIndicator > xProgress = m_xProgress;
200
201 aWriteLock.clear();
202 // <- SAFE ----------------------------------
203
204 if (xActive.is())
205 {
206 // There is at least one further child indicator.
207 // Actualize our progress, so it shows these values from now.
208 if (xProgress.is())
209 {
210 xProgress->setText (sText );
211 xProgress->setValue(nValue);
212 }
213 }
214 else
215 {
216 // Our stack is empty. No further child exists.
217 // Se we must "end" our progress really
218 if (xProgress.is())
219 xProgress->end();
220 // Now hide the progress bar again.
221 impl_hideProgress();
222
223 impl_stopWakeUpThread();
224 }
225
226 impl_reschedule(true);
227 }
228
setText(const css::uno::Reference<css::task::XStatusIndicator> & xChild,const OUString & sText)229 void StatusIndicatorFactory::setText(const css::uno::Reference< css::task::XStatusIndicator >& xChild,
230 const OUString& sText )
231 {
232 // SAFE -> ----------------------------------
233 osl::ClearableMutexGuard aWriteLock(m_mutex);
234
235 IndicatorStack::iterator pItem = ::std::find(m_aStack.begin(), m_aStack.end(), xChild);
236 if (pItem != m_aStack.end())
237 pItem->m_sText = sText;
238
239 css::uno::Reference< css::task::XStatusIndicator > xActive = m_xActiveChild;
240 css::uno::Reference< css::task::XStatusIndicator > xProgress = m_xProgress;
241
242 aWriteLock.clear();
243 // SAFE -> ----------------------------------
244
245 // paint only the top most indicator
246 // but don't forget to Reschedule!
247 if (
248 (xChild == xActive) &&
249 (xProgress.is() )
250 )
251 {
252 xProgress->setText(sText);
253 }
254
255 impl_reschedule(true);
256 }
257
setValue(const css::uno::Reference<css::task::XStatusIndicator> & xChild,sal_Int32 nValue)258 void StatusIndicatorFactory::setValue( const css::uno::Reference< css::task::XStatusIndicator >& xChild ,
259 sal_Int32 nValue )
260 {
261 // SAFE -> ----------------------------------
262 osl::ClearableMutexGuard aWriteLock(m_mutex);
263
264 sal_Int32 nOldValue = 0;
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 css::uno::Reference< css::task::XStatusIndicator > xActive = m_xActiveChild;
273 css::uno::Reference< css::task::XStatusIndicator > xProgress = m_xProgress;
274
275 aWriteLock.clear();
276 // SAFE -> ----------------------------------
277
278 if (
279 (xChild == xActive) &&
280 (nOldValue != nValue ) &&
281 (xProgress.is() )
282 )
283 {
284 xProgress->setValue(nValue);
285 }
286
287 impl_reschedule(false);
288 }
289
implts_makeParentVisibleIfAllowed()290 void StatusIndicatorFactory::implts_makeParentVisibleIfAllowed()
291 {
292 // SAFE -> ----------------------------------
293 osl::ClearableMutexGuard aReadLock(m_mutex);
294
295 if (!m_bAllowParentShow)
296 return;
297
298 css::uno::Reference< css::frame::XFrame > xFrame (m_xFrame.get() , css::uno::UNO_QUERY);
299 css::uno::Reference< css::awt::XWindow > xPluggWindow(m_xPluggWindow.get(), css::uno::UNO_QUERY);
300 css::uno::Reference< css::uno::XComponentContext > xContext( m_xContext);
301
302 aReadLock.clear();
303 // <- SAFE ----------------------------------
304
305 css::uno::Reference< css::awt::XWindow > xParentWindow;
306 if (xFrame.is())
307 xParentWindow = xFrame->getContainerWindow();
308 else
309 xParentWindow = xPluggWindow;
310
311 // don't disturb user in case he put the loading document into the background!
312 // Suppress any setVisible() or toFront() call in case the initial show was
313 // already made.
314 css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(xParentWindow, css::uno::UNO_QUERY);
315 bool bIsVisible = false;
316 if (xVisibleCheck.is())
317 bIsVisible = xVisibleCheck->isVisible();
318
319 if (bIsVisible)
320 {
321 impl_showProgress();
322 return;
323 }
324
325 // Check if the layout manager has been set to invisible state. It this case we are also
326 // not allowed to set the frame visible!
327 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
328 if (xPropSet.is())
329 {
330 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
331 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
332 if (xLayoutManager.is())
333 {
334 if ( !xLayoutManager->isVisible() )
335 return;
336 }
337 }
338
339 // Ok the window should be made visible... because it is not currently visible.
340 // BUT..!
341 // We need a Hack for our applications: They get her progress from the frame directly
342 // on saving documents. Because there is no progress set on the MediaDescriptor.
343 // But that's wrong. In case the document was opened hidden, they should not use any progress .-(
344 // They only possible workaround: don't show the parent window here, if the document was opened hidden.
345 bool bHiddenDoc = false;
346 if (xFrame.is())
347 {
348 css::uno::Reference< css::frame::XController > xController;
349 css::uno::Reference< css::frame::XModel > xModel;
350 xController = xFrame->getController();
351 if (xController.is())
352 xModel = xController->getModel();
353 if (xModel.is())
354 {
355 utl::MediaDescriptor lDocArgs(xModel->getArgs());
356 bHiddenDoc = lDocArgs.getUnpackedValueOrDefault(
357 utl::MediaDescriptor::PROP_HIDDEN(),
358 false);
359 }
360 }
361
362 if (bHiddenDoc)
363 return;
364
365 // OK: The document was not opened in hidden mode ...
366 // and the window isn't already visible.
367 // Show it and bring it to front.
368 // But before we have to be sure, that our internal used helper progress
369 // is visible too.
370 impl_showProgress();
371
372 SolarMutexGuard aSolarGuard;
373 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xParentWindow);
374 if ( pWindow )
375 {
376 bool bForceFrontAndFocus(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get(xContext));
377 pWindow->Show(true, bForceFrontAndFocus ? ShowFlags::ForegroundTask : ShowFlags::NONE );
378 }
379
380 }
381
impl_createProgress()382 void StatusIndicatorFactory::impl_createProgress()
383 {
384 // SAFE -> ----------------------------------
385 osl::ClearableMutexGuard aReadLock(m_mutex);
386
387 css::uno::Reference< css::frame::XFrame > xFrame (m_xFrame.get() , css::uno::UNO_QUERY);
388 css::uno::Reference< css::awt::XWindow > xWindow(m_xPluggWindow.get(), css::uno::UNO_QUERY);
389
390 aReadLock.clear();
391 // <- SAFE ----------------------------------
392
393 css::uno::Reference< css::task::XStatusIndicator > xProgress;
394
395 if (xWindow.is())
396 {
397 // use vcl based progress implementation in plugged mode
398 VCLStatusIndicator* pVCLProgress = new VCLStatusIndicator(xWindow);
399 xProgress.set(static_cast< css::task::XStatusIndicator* >(pVCLProgress), css::uno::UNO_QUERY);
400 }
401 else if (xFrame.is())
402 {
403 // use frame layouted progress implementation
404 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
405 if (xPropSet.is())
406 {
407 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
408 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
409 if (xLayoutManager.is())
410 {
411 xLayoutManager->lock();
412 OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
413 xLayoutManager->createElement( sPROGRESS_RESOURCE );
414 xLayoutManager->hideElement( sPROGRESS_RESOURCE );
415
416 css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE);
417 if (xProgressBar.is())
418 xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY);
419 xLayoutManager->unlock();
420 }
421 }
422 }
423
424 osl::MutexGuard g(m_mutex);
425 m_xProgress = xProgress;
426 }
427
impl_showProgress()428 void StatusIndicatorFactory::impl_showProgress()
429 {
430 // SAFE -> ----------------------------------
431 osl::ClearableMutexGuard aReadLock(m_mutex);
432
433 css::uno::Reference< css::frame::XFrame > xFrame (m_xFrame.get() , css::uno::UNO_QUERY);
434
435 aReadLock.clear();
436 // <- SAFE ----------------------------------
437
438 css::uno::Reference< css::task::XStatusIndicator > xProgress;
439
440 if (xFrame.is())
441 {
442 // use frame layouted progress implementation
443 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
444 if (xPropSet.is())
445 {
446 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
447 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
448 if (xLayoutManager.is())
449 {
450 // Be sure that we have always a progress. It can be that our frame
451 // was recycled and therefore the progress was destroyed!
452 // CreateElement does nothing if there is already a valid progress.
453 OUString sPROGRESS_RESOURCE(PROGRESS_RESOURCE);
454 xLayoutManager->createElement( sPROGRESS_RESOURCE );
455 xLayoutManager->showElement( sPROGRESS_RESOURCE );
456
457 css::uno::Reference< css::ui::XUIElement > xProgressBar = xLayoutManager->getElement(sPROGRESS_RESOURCE);
458 if (xProgressBar.is())
459 xProgress.set(xProgressBar->getRealInterface(), css::uno::UNO_QUERY);
460 }
461 }
462
463 osl::MutexGuard g(m_mutex);
464 m_xProgress = xProgress;
465 }
466 }
467
impl_hideProgress()468 void StatusIndicatorFactory::impl_hideProgress()
469 {
470 // SAFE -> ----------------------------------
471 osl::ClearableMutexGuard aReadLock(m_mutex);
472
473 css::uno::Reference< css::frame::XFrame > xFrame (m_xFrame.get() , css::uno::UNO_QUERY);
474
475 aReadLock.clear();
476 // <- SAFE ----------------------------------
477
478 if (xFrame.is())
479 {
480 // use frame layouted progress implementation
481 css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
482 if (xPropSet.is())
483 {
484 css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
485 xPropSet->getPropertyValue(FRAME_PROPNAME_ASCII_LAYOUTMANAGER) >>= xLayoutManager;
486 if (xLayoutManager.is())
487 xLayoutManager->hideElement( PROGRESS_RESOURCE );
488 }
489 }
490 }
491
impl_reschedule(bool bForce)492 void StatusIndicatorFactory::impl_reschedule(bool bForce)
493 {
494 // SAFE ->
495 {
496 osl::MutexGuard aReadLock(m_mutex);
497 if (m_bDisableReschedule)
498 return;
499 }
500 // <- SAFE
501
502 bool bReschedule = bForce;
503 if (!bReschedule)
504 {
505 osl::MutexGuard g(m_mutex);
506 bReschedule = m_bAllowReschedule;
507 m_bAllowReschedule = false;
508 }
509
510 if (!bReschedule)
511 return;
512
513 // SAFE ->
514 osl::ResettableMutexGuard aRescheduleGuard(RescheduleLock::get());
515
516 if (m_nInReschedule == 0)
517 {
518 ++m_nInReschedule;
519 aRescheduleGuard.clear();
520 // <- SAFE
521
522 {
523 SolarMutexGuard g;
524 Application::Reschedule(true);
525 }
526
527 // SAFE ->
528 aRescheduleGuard.reset();
529 --m_nInReschedule;
530 }
531 }
532
impl_startWakeUpThread()533 void StatusIndicatorFactory::impl_startWakeUpThread()
534 {
535 osl::MutexGuard g(m_mutex);
536
537 if (m_bDisableReschedule)
538 return;
539
540 if (!m_pWakeUp.is())
541 {
542 m_pWakeUp = new WakeUpThread(this);
543 m_pWakeUp->launch();
544 }
545 }
546
impl_stopWakeUpThread()547 void StatusIndicatorFactory::impl_stopWakeUpThread()
548 {
549 rtl::Reference<WakeUpThread> wakeUp;
550 {
551 osl::MutexGuard g(m_mutex);
552 std::swap(wakeUp, m_pWakeUp);
553 }
554 if (wakeUp.is())
555 {
556 wakeUp->stop();
557 }
558 }
559
560 } // namespace framework
561
562 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 &)563 com_sun_star_comp_framework_StatusIndicatorFactory_get_implementation(
564 css::uno::XComponentContext *context,
565 css::uno::Sequence<css::uno::Any> const &)
566 {
567 return cppu::acquire(new framework::StatusIndicatorFactory(context));
568 }
569
570 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
571