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