xref: /AOO41X/main/framework/source/dispatch/helpagentdispatcher.cxx (revision 6d739b60ff8f4ed2134ae1442e284f9da90334b4)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_framework.hxx"
26 #include <dispatch/helpagentdispatcher.hxx>
27 #include <threadhelp/readguard.hxx>
28 #include <threadhelp/writeguard.hxx>
29 #include <com/sun/star/awt/XWindow2.hpp>
30 #include <com/sun/star/awt/PosSize.hpp>
31 #include <com/sun/star/awt/Size.hpp>
32 #include <com/sun/star/awt/Rectangle.hpp>
33 #include <toolkit/helper/vclunohelper.hxx>
34 #include <svtools/helpopt.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/help.hxx>
37 
38 namespace css = ::com::sun::star;
39 
40 //........................................................................
41 namespace framework
42 {
43 
44 //-----------------------------------------------
DEFINE_XINTERFACE_4(HelpAgentDispatcher,OWeakObject,DIRECT_INTERFACE (css::lang::XTypeProvider),DIRECT_INTERFACE (css::frame::XDispatch),DIRECT_INTERFACE (css::awt::XWindowListener),DIRECT_INTERFACE (css::lang::XEventListener))45 DEFINE_XINTERFACE_4(HelpAgentDispatcher                         ,
46                     OWeakObject                                 ,
47                     DIRECT_INTERFACE (css::lang::XTypeProvider ),
48                     DIRECT_INTERFACE (css::frame::XDispatch    ),
49                     DIRECT_INTERFACE (css::awt::XWindowListener),
50                     DIRECT_INTERFACE (css::lang::XEventListener))
51 
52 //-----------------------------------------------
53 DEFINE_XTYPEPROVIDER_2(HelpAgentDispatcher     ,
54                        css::lang::XTypeProvider,
55                        css::frame::XDispatch   )
56 
57 //--------------------------------------------------------------------
58 HelpAgentDispatcher::HelpAgentDispatcher( const css::uno::Reference< css::frame::XFrame >& xParentFrame)
59     : ThreadHelpBase    (&Application::GetSolarMutex())
60     , m_sCurrentURL     (                             )
61     , m_xContainerWindow(                             )
62     , m_xAgentWindow    (                             )
63     , m_aTimer          (                             )
64     , m_xSelfHold       (                             )
65 {
66     // It's required that this class has to be contructed with a valid frame.
67     // And "valid" means: the frame must already bound to a valid container window.
68     m_xContainerWindow = xParentFrame->getContainerWindow();
69 }
70 
71 //--------------------------------------------------------------------
~HelpAgentDispatcher()72 HelpAgentDispatcher::~HelpAgentDispatcher()
73 {
74     implts_stopTimer();
75     implts_ignoreCurrentURL();
76 
77     // Needed ... because it was create as "new VCLWindow()" ! Such windows must be disposed explicitly.
78     css::uno::Reference< css::lang::XComponent > xAgentWindow(m_xAgentWindow, css::uno::UNO_QUERY);
79     if (xAgentWindow.is())
80         xAgentWindow->dispose();
81 }
82 
83 //--------------------------------------------------------------------
dispatch(const css::util::URL & aURL,const css::uno::Sequence<css::beans::PropertyValue> &)84 void SAL_CALL HelpAgentDispatcher::dispatch(const css::util::URL&                                  aURL ,
85                                             const css::uno::Sequence< css::beans::PropertyValue >&)
86     throw(css::uno::RuntimeException)
87 {
88     // silently drop the request if the new URL was marked to be ignored next time.
89     sal_Int32 nAllowedToIgnore = SvtHelpOptions().getAgentIgnoreURLCounter(aURL.Complete);
90     if (nAllowedToIgnore < 1)
91         return;
92 
93     // stop the expiration timer for the old URL
94     // The timer will add the old URL to the list of ignorable URLs.
95     // So m_sCurrentURL must be set AFTER the timer was stopped !!!
96     implts_stopTimer();
97 
98     // SAFE ->
99     WriteGuard aWriteLock(m_aLock);
100     m_sCurrentURL = aURL.Complete;
101     aWriteLock.unlock();
102     // <- SAFE
103 
104     // start the expiration timer for the new URL
105     implts_startTimer();
106 
107     // make sure the agent window is shown
108     implts_showAgentWindow();
109 }
110 
111 //--------------------------------------------------------------------
addStatusListener(const css::uno::Reference<css::frame::XStatusListener> &,const css::util::URL &)112 void SAL_CALL HelpAgentDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >&,
113                                                      const css::util::URL&)
114     throw(css::uno::RuntimeException)
115 {
116     // no status available
117 }
118 
119 //--------------------------------------------------------------------
removeStatusListener(const css::uno::Reference<css::frame::XStatusListener> &,const css::util::URL &)120 void SAL_CALL HelpAgentDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >&,
121                                                         const css::util::URL&)
122     throw(css::uno::RuntimeException)
123 {
124     // no status available
125 }
126 
127 //--------------------------------------------------------------------
windowResized(const css::awt::WindowEvent &)128 void SAL_CALL HelpAgentDispatcher::windowResized(const css::awt::WindowEvent&)
129     throw(css::uno::RuntimeException)
130 {
131     implts_positionAgentWindow();
132 }
133 
134 //--------------------------------------------------------------------
windowMoved(const css::awt::WindowEvent &)135 void SAL_CALL HelpAgentDispatcher::windowMoved(const css::awt::WindowEvent&)
136     throw(css::uno::RuntimeException)
137 {
138     implts_positionAgentWindow();
139 }
140 
141 //--------------------------------------------------------------------
windowShown(const css::lang::EventObject &)142 void SAL_CALL HelpAgentDispatcher::windowShown(const css::lang::EventObject&)
143     throw(css::uno::RuntimeException)
144 {
145     implts_showAgentWindow();
146 }
147 
148 //--------------------------------------------------------------------
windowHidden(const css::lang::EventObject &)149 void SAL_CALL HelpAgentDispatcher::windowHidden(const css::lang::EventObject&)
150     throw(css::uno::RuntimeException)
151 {
152     implts_hideAgentWindow();
153 }
154 
155 //--------------------------------------------------------------------
disposing(const css::lang::EventObject & aEvent)156 void SAL_CALL HelpAgentDispatcher::disposing(const css::lang::EventObject& aEvent)
157     throw(css::uno::RuntimeException)
158 {
159     // SAFE ->
160     WriteGuard aWriteLock(m_aLock);
161 
162     // Already disposed ?!
163     if (! m_xContainerWindow.is())
164         return;
165     // Wrong broadcaster ?!
166     if (aEvent.Source != m_xContainerWindow)
167         return;
168 
169     css::uno::Reference< css::uno::XInterface > xSelfHoldUntilMethodEnds(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
170     m_xSelfHold.clear();
171 
172     aWriteLock.unlock();
173     // <- SAFE
174 
175     implts_stopTimer();
176     implts_hideAgentWindow();
177     implts_ignoreCurrentURL();
178 
179     // SAFE ->
180     aWriteLock.lock();
181     m_xContainerWindow.clear();
182     css::uno::Reference< css::lang::XComponent > xAgentWindow(m_xAgentWindow, css::uno::UNO_QUERY);
183     m_xAgentWindow.clear();
184     aWriteLock.unlock();
185     // <- SAFE
186 
187     // Needed ... because it was create as "new VCLWindow()" ! Such windows must be disposed explicitly.
188     if (xAgentWindow.is())
189         xAgentWindow->dispose();
190 }
191 
192 //--------------------------------------------------------------------
helpRequested()193 void HelpAgentDispatcher::helpRequested()
194 {
195     implts_stopTimer();
196     implts_hideAgentWindow();
197     implts_acceptCurrentURL();
198 }
199 
200 //-----------------------------------------------
closeAgent()201 void HelpAgentDispatcher::closeAgent()
202 {
203     implts_stopTimer();
204     implts_hideAgentWindow();
205     implts_ignoreCurrentURL();
206 }
207 
208 //--------------------------------------------------------------------
implts_acceptCurrentURL()209 void HelpAgentDispatcher::implts_acceptCurrentURL()
210 {
211     // SAFE ->
212     WriteGuard aWriteLock(m_aLock);
213 
214     ::rtl::OUString sAcceptedURL  = m_sCurrentURL;
215                     m_sCurrentURL = ::rtl::OUString();
216 
217     aWriteLock.unlock();
218     // <- SAFE
219 
220     // We must make sure that this URL isnt marked as ignored by the user.
221     // Otherwhise the user wont see the corresponding help content in the future.
222     SvtHelpOptions().resetAgentIgnoreURLCounter(sAcceptedURL);
223 
224     // show the right help content
225     // SOLAR SAFE ->
226     {
227         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
228         Help* pHelp = Application::GetHelp();
229         if (pHelp)
230             pHelp->Start(sAcceptedURL, NULL);
231     }
232     // <- SOLAR SAFE
233 }
234 
235 //--------------------------------------------------------------------
implts_ignoreCurrentURL()236 void HelpAgentDispatcher::implts_ignoreCurrentURL()
237 {
238     // SAFE ->
239     WriteGuard aWriteLock(m_aLock);
240 
241     ::rtl::OUString sIgnoredURL   = m_sCurrentURL;
242                     m_sCurrentURL = ::rtl::OUString();
243 
244     aWriteLock.unlock();
245     // <- SAFE
246 
247     if (sIgnoredURL.getLength())
248         SvtHelpOptions().decAgentIgnoreURLCounter(sIgnoredURL);
249 }
250 
251 //--------------------------------------------------------------------
implts_stopTimer()252 void HelpAgentDispatcher::implts_stopTimer()
253 {
254     // SAFE ->
255     WriteGuard aWriteLock(m_aLock);
256     m_xSelfHold.clear();
257     aWriteLock.unlock();
258     // <- SAFE
259 
260     // SOLAR SAFE ->
261     // Timer access needs no "own lock" ! It lives if we live ...
262     // But it requires locking of the solar mutex ... because it's a vcl based timer.
263     {
264         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
265         if (! m_aTimer.IsActive())
266             return;
267         m_aTimer.Stop();
268     }
269     // <- SOLAR SAFE
270 }
271 
272 //--------------------------------------------------------------------
implts_startTimer()273 void HelpAgentDispatcher::implts_startTimer()
274 {
275     // SOLAR SAFE ->
276     // Timer access needs no "own lock" ! It lives if we live ...
277     // But it requires locking of the solar mutex ... because it's a vcl based timer.
278     {
279         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
280         if (m_aTimer.IsActive())
281             return;
282     }
283     // <- SOLAR SAFE
284 
285     // SAFE ->
286     // Timer uses pointer to this help agent dispatcher ...
287     // But normaly we are ref counted. So we must make sure that this
288     // dispatcher isnt killed during the timer runs .-)
289     WriteGuard aWriteLock(m_aLock);
290     m_xSelfHold = css::uno::Reference< css::uno::XInterface >(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
291     aWriteLock.unlock();
292     // <- SAFE
293 
294     sal_Int32 nTime = SvtHelpOptions().GetHelpAgentTimeoutPeriod();
295 
296     // SOLAR SAFE ->
297     // Timer access needs no "own lock" ! It lives if we live ...
298     // But it requires locking of the solar mutex ... because it's a vcl based timer.
299     {
300         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
301         m_aTimer.SetTimeout(nTime*1000); // sec => ms !
302         m_aTimer.Start();
303     }
304 }
305 
306 //-----------------------------------------------
307 IMPL_LINK(HelpAgentDispatcher, implts_timerExpired, void*,)
308 {
309     // This method is called by using a pointer to us.
310     // But we must be aware that we can be destroyed hardly
311     // if our uno reference will be gone!
312     // => Hold this object alive till this method finish its work.
313     // SAFE ->
314     WriteGuard aWriteLock(m_aLock);
315     css::uno::Reference< css::uno::XInterface > xSelfHoldUntilMethodEnds(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
316     m_xSelfHold.clear();
317     aWriteLock.unlock();
318     // <- SAFE
319 
320     implts_hideAgentWindow();
321     implts_ignoreCurrentURL();
322 
323     return 0;
324 }
325 
326 //--------------------------------------------------------------------
implts_showAgentWindow()327 void HelpAgentDispatcher::implts_showAgentWindow()
328 {
329     // SAFE ->
330     ReadGuard aReadLock(m_aLock);
331     css::uno::Reference< css::awt::XWindow2 > xContainerWindow(m_xContainerWindow, css::uno::UNO_QUERY_THROW);
332     aReadLock.unlock();
333     // <- SAFE
334 
335     css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
336 
337     if (
338         (xContainerWindow.is()        ) &&
339         (xAgentWindow.is()            ) &&
340         (xContainerWindow->isVisible())
341        )
342     {
343         // make sure that agent window resists at the right place .-)
344         implts_positionAgentWindow();
345         xAgentWindow->setVisible(sal_True);
346     }
347 }
348 
349 //--------------------------------------------------------------------
implts_hideAgentWindow()350 void HelpAgentDispatcher::implts_hideAgentWindow()
351 {
352     css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
353     if (xAgentWindow.is())
354         xAgentWindow->setVisible(sal_False);
355 }
356 
357 //--------------------------------------------------------------------
implts_positionAgentWindow()358 void HelpAgentDispatcher::implts_positionAgentWindow()
359 {
360     // SAFE ->
361     ReadGuard aReadLock(m_aLock);
362     css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
363     aReadLock.unlock();
364     // <- SAFE
365 
366     css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
367     if (
368         (! xContainerWindow.is())  ||
369         (! xAgentWindow.is()    )
370        )
371         return;
372 
373           ::svt::HelpAgentWindow* pAgentWindow   = (::svt::HelpAgentWindow*)VCLUnoHelper::GetWindow(xAgentWindow);
374     const css::awt::Rectangle     aContainerSize = xContainerWindow->getPosSize();
375     const Size                    aAgentSize     = pAgentWindow->getPreferredSizePixel();
376 
377     sal_Int32 nW = aAgentSize.Width() ;
378     sal_Int32 nH = aAgentSize.Height();
379 
380     if (nW < 1)
381         nW = 100;
382     if (nH < 1)
383         nH = 100;
384 
385     sal_Int32 nX = aContainerSize.Width  - nW;
386     sal_Int32 nY = aContainerSize.Height - nH;
387 
388     // TODO: use a surrogate if the container window is too small to contain the full-sized agent window
389     xAgentWindow->setPosSize(nX, nY, nW, nH, css::awt::PosSize::POSSIZE);
390 }
391 
392 //--------------------------------------------------------------------
implts_ensureAgentWindow()393 css::uno::Reference< css::awt::XWindow > HelpAgentDispatcher::implts_ensureAgentWindow()
394 {
395     // SAFE ->
396     ReadGuard aReadLock(m_aLock);
397     if (m_xAgentWindow.is())
398         return m_xAgentWindow;
399     css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
400     aReadLock.unlock();
401     // <- SAFE
402 
403     if (!xContainerWindow.is())
404         return css::uno::Reference< css::awt::XWindow >();
405 
406     ::svt::HelpAgentWindow* pAgentWindow = 0;
407     // SOLAR SAFE ->
408     {
409         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
410         // create the agent window
411         Window* pContainerWindow = VCLUnoHelper::GetWindow(xContainerWindow);
412                 pAgentWindow     = new ::svt::HelpAgentWindow(pContainerWindow);
413         pAgentWindow->setCallback(this);
414     }
415     // <- SOLAR SAFE
416 
417     // SAFE ->
418     WriteGuard aWriteLock(m_aLock);
419     m_xAgentWindow = VCLUnoHelper::GetInterface(pAgentWindow);
420     css::uno::Reference< css::awt::XWindow > xAgentWindow = m_xAgentWindow;
421     aWriteLock.unlock();
422     // <- SAFE
423 
424     // add as window listener to the container window so we can maintain the property position of the agent window
425     xContainerWindow->addWindowListener(this);
426 
427     // SOLAR SAFE ->
428     {
429         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
430         // establish callback for our internal used timer.
431         // Note: Its only active, if the timer will be started ...
432         m_aTimer.SetTimeoutHdl(LINK(this, HelpAgentDispatcher, implts_timerExpired));
433     }
434     // <- SOLAR SAFE
435 
436     return xAgentWindow;
437 }
438 
439 } // namespace framework
440 
441