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