xref: /trunk/main/framework/source/dispatch/helpagentdispatcher.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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 constructed 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 {
87     // silently drop the request if the new URL was marked to be ignored next time.
88     sal_Int32 nAllowedToIgnore = SvtHelpOptions().getAgentIgnoreURLCounter(aURL.Complete);
89     if (nAllowedToIgnore < 1)
90         return;
91 
92     // stop the expiration timer for the old URL
93     // The timer will add the old URL to the list of ignorable URLs.
94     // So m_sCurrentURL must be set AFTER the timer was stopped !!!
95     implts_stopTimer();
96 
97     // SAFE ->
98     WriteGuard aWriteLock(m_aLock);
99     m_sCurrentURL = aURL.Complete;
100     aWriteLock.unlock();
101     // <- SAFE
102 
103     // start the expiration timer for the new URL
104     implts_startTimer();
105 
106     // make sure the agent window is shown
107     implts_showAgentWindow();
108 }
109 
110 //--------------------------------------------------------------------
addStatusListener(const css::uno::Reference<css::frame::XStatusListener> &,const css::util::URL &)111 void SAL_CALL HelpAgentDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >&,
112                                                      const css::util::URL&)
113 {
114     // no status available
115 }
116 
117 //--------------------------------------------------------------------
removeStatusListener(const css::uno::Reference<css::frame::XStatusListener> &,const css::util::URL &)118 void SAL_CALL HelpAgentDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >&,
119                                                         const css::util::URL&)
120 {
121     // no status available
122 }
123 
124 //--------------------------------------------------------------------
windowResized(const css::awt::WindowEvent &)125 void SAL_CALL HelpAgentDispatcher::windowResized(const css::awt::WindowEvent&)
126 {
127     implts_positionAgentWindow();
128 }
129 
130 //--------------------------------------------------------------------
windowMoved(const css::awt::WindowEvent &)131 void SAL_CALL HelpAgentDispatcher::windowMoved(const css::awt::WindowEvent&)
132 {
133     implts_positionAgentWindow();
134 }
135 
136 //--------------------------------------------------------------------
windowShown(const css::lang::EventObject &)137 void SAL_CALL HelpAgentDispatcher::windowShown(const css::lang::EventObject&)
138 {
139     implts_showAgentWindow();
140 }
141 
142 //--------------------------------------------------------------------
windowHidden(const css::lang::EventObject &)143 void SAL_CALL HelpAgentDispatcher::windowHidden(const css::lang::EventObject&)
144 {
145     implts_hideAgentWindow();
146 }
147 
148 //--------------------------------------------------------------------
disposing(const css::lang::EventObject & aEvent)149 void SAL_CALL HelpAgentDispatcher::disposing(const css::lang::EventObject& aEvent)
150 {
151     // SAFE ->
152     WriteGuard aWriteLock(m_aLock);
153 
154     // Already disposed ?!
155     if (! m_xContainerWindow.is())
156         return;
157     // Wrong broadcaster ?!
158     if (aEvent.Source != m_xContainerWindow)
159         return;
160 
161     css::uno::Reference< css::uno::XInterface > xSelfHoldUntilMethodEnds(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
162     m_xSelfHold.clear();
163 
164     aWriteLock.unlock();
165     // <- SAFE
166 
167     implts_stopTimer();
168     implts_hideAgentWindow();
169     implts_ignoreCurrentURL();
170 
171     // SAFE ->
172     aWriteLock.lock();
173     m_xContainerWindow.clear();
174     css::uno::Reference< css::lang::XComponent > xAgentWindow(m_xAgentWindow, css::uno::UNO_QUERY);
175     m_xAgentWindow.clear();
176     aWriteLock.unlock();
177     // <- SAFE
178 
179     // Needed ... because it was create as "new VCLWindow()" ! Such windows must be disposed explicitly.
180     if (xAgentWindow.is())
181         xAgentWindow->dispose();
182 }
183 
184 //--------------------------------------------------------------------
helpRequested()185 void HelpAgentDispatcher::helpRequested()
186 {
187     implts_stopTimer();
188     implts_hideAgentWindow();
189     implts_acceptCurrentURL();
190 }
191 
192 //-----------------------------------------------
closeAgent()193 void HelpAgentDispatcher::closeAgent()
194 {
195     implts_stopTimer();
196     implts_hideAgentWindow();
197     implts_ignoreCurrentURL();
198 }
199 
200 //--------------------------------------------------------------------
implts_acceptCurrentURL()201 void HelpAgentDispatcher::implts_acceptCurrentURL()
202 {
203     // SAFE ->
204     WriteGuard aWriteLock(m_aLock);
205 
206     ::rtl::OUString sAcceptedURL  = m_sCurrentURL;
207                     m_sCurrentURL = ::rtl::OUString();
208 
209     aWriteLock.unlock();
210     // <- SAFE
211 
212     // We must make sure that this URL isn't marked as ignored by the user.
213     // Otherwise the user won't see the corresponding help content in the future.
214     SvtHelpOptions().resetAgentIgnoreURLCounter(sAcceptedURL);
215 
216     // show the right help content
217     // SOLAR SAFE ->
218     {
219         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
220         Help* pHelp = Application::GetHelp();
221         if (pHelp)
222             pHelp->Start(sAcceptedURL, NULL);
223     }
224     // <- SOLAR SAFE
225 }
226 
227 //--------------------------------------------------------------------
implts_ignoreCurrentURL()228 void HelpAgentDispatcher::implts_ignoreCurrentURL()
229 {
230     // SAFE ->
231     WriteGuard aWriteLock(m_aLock);
232 
233     ::rtl::OUString sIgnoredURL   = m_sCurrentURL;
234                     m_sCurrentURL = ::rtl::OUString();
235 
236     aWriteLock.unlock();
237     // <- SAFE
238 
239     if (sIgnoredURL.getLength())
240         SvtHelpOptions().decAgentIgnoreURLCounter(sIgnoredURL);
241 }
242 
243 //--------------------------------------------------------------------
implts_stopTimer()244 void HelpAgentDispatcher::implts_stopTimer()
245 {
246     // SAFE ->
247     WriteGuard aWriteLock(m_aLock);
248     m_xSelfHold.clear();
249     aWriteLock.unlock();
250     // <- SAFE
251 
252     // SOLAR SAFE ->
253     // Timer access needs no "own lock" ! It lives if we live ...
254     // But it requires locking of the solar mutex ... because it's a vcl based timer.
255     {
256         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
257         if (! m_aTimer.IsActive())
258             return;
259         m_aTimer.Stop();
260     }
261     // <- SOLAR SAFE
262 }
263 
264 //--------------------------------------------------------------------
implts_startTimer()265 void HelpAgentDispatcher::implts_startTimer()
266 {
267     // SOLAR SAFE ->
268     // Timer access needs no "own lock" ! It lives if we live ...
269     // But it requires locking of the solar mutex ... because it's a vcl based timer.
270     {
271         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
272         if (m_aTimer.IsActive())
273             return;
274     }
275     // <- SOLAR SAFE
276 
277     // SAFE ->
278     // Timer uses pointer to this help agent dispatcher ...
279     // But normally we are ref counted. So we must make sure that this
280     // dispatcher isn't killed during the timer runs .-)
281     WriteGuard aWriteLock(m_aLock);
282     m_xSelfHold = css::uno::Reference< css::uno::XInterface >(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
283     aWriteLock.unlock();
284     // <- SAFE
285 
286     sal_Int32 nTime = SvtHelpOptions().GetHelpAgentTimeoutPeriod();
287 
288     // SOLAR SAFE ->
289     // Timer access needs no "own lock" ! It lives if we live ...
290     // But it requires locking of the solar mutex ... because it's a vcl based timer.
291     {
292         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
293         m_aTimer.SetTimeout(nTime*1000); // sec => ms !
294         m_aTimer.Start();
295     }
296 }
297 
298 //-----------------------------------------------
299 IMPL_LINK(HelpAgentDispatcher, implts_timerExpired, void*,)
300 {
301     // This method is called by using a pointer to us.
302     // But we must be aware that we can be destroyed hardly
303     // if our uno reference will be gone!
304     // => Hold this object alive till this method finish its work.
305     // SAFE ->
306     WriteGuard aWriteLock(m_aLock);
307     css::uno::Reference< css::uno::XInterface > xSelfHoldUntilMethodEnds(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
308     m_xSelfHold.clear();
309     aWriteLock.unlock();
310     // <- SAFE
311 
312     implts_hideAgentWindow();
313     implts_ignoreCurrentURL();
314 
315     return 0;
316 }
317 
318 //--------------------------------------------------------------------
implts_showAgentWindow()319 void HelpAgentDispatcher::implts_showAgentWindow()
320 {
321     // SAFE ->
322     ReadGuard aReadLock(m_aLock);
323     css::uno::Reference< css::awt::XWindow2 > xContainerWindow(m_xContainerWindow, css::uno::UNO_QUERY_THROW);
324     aReadLock.unlock();
325     // <- SAFE
326 
327     css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
328 
329     if (
330         (xContainerWindow.is()        ) &&
331         (xAgentWindow.is()            ) &&
332         (xContainerWindow->isVisible())
333        )
334     {
335         // make sure that agent window resists at the right place .-)
336         implts_positionAgentWindow();
337         xAgentWindow->setVisible(sal_True);
338     }
339 }
340 
341 //--------------------------------------------------------------------
implts_hideAgentWindow()342 void HelpAgentDispatcher::implts_hideAgentWindow()
343 {
344     css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
345     if (xAgentWindow.is())
346         xAgentWindow->setVisible(sal_False);
347 }
348 
349 //--------------------------------------------------------------------
implts_positionAgentWindow()350 void HelpAgentDispatcher::implts_positionAgentWindow()
351 {
352     // SAFE ->
353     ReadGuard aReadLock(m_aLock);
354     css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
355     aReadLock.unlock();
356     // <- SAFE
357 
358     css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
359     if (
360         (! xContainerWindow.is())  ||
361         (! xAgentWindow.is()    )
362        )
363         return;
364 
365           ::svt::HelpAgentWindow* pAgentWindow   = (::svt::HelpAgentWindow*)VCLUnoHelper::GetWindow(xAgentWindow);
366     const css::awt::Rectangle     aContainerSize = xContainerWindow->getPosSize();
367     const Size                    aAgentSize     = pAgentWindow->getPreferredSizePixel();
368 
369     sal_Int32 nW = aAgentSize.Width() ;
370     sal_Int32 nH = aAgentSize.Height();
371 
372     if (nW < 1)
373         nW = 100;
374     if (nH < 1)
375         nH = 100;
376 
377     sal_Int32 nX = aContainerSize.Width  - nW;
378     sal_Int32 nY = aContainerSize.Height - nH;
379 
380     // TODO: use a surrogate if the container window is too small to contain the full-sized agent window
381     xAgentWindow->setPosSize(nX, nY, nW, nH, css::awt::PosSize::POSSIZE);
382 }
383 
384 //--------------------------------------------------------------------
implts_ensureAgentWindow()385 css::uno::Reference< css::awt::XWindow > HelpAgentDispatcher::implts_ensureAgentWindow()
386 {
387     // SAFE ->
388     ReadGuard aReadLock(m_aLock);
389     if (m_xAgentWindow.is())
390         return m_xAgentWindow;
391     css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
392     aReadLock.unlock();
393     // <- SAFE
394 
395     if (!xContainerWindow.is())
396         return css::uno::Reference< css::awt::XWindow >();
397 
398     ::svt::HelpAgentWindow* pAgentWindow = 0;
399     // SOLAR SAFE ->
400     {
401         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
402         // create the agent window
403         Window* pContainerWindow = VCLUnoHelper::GetWindow(xContainerWindow);
404                 pAgentWindow     = new ::svt::HelpAgentWindow(pContainerWindow);
405         pAgentWindow->setCallback(this);
406     }
407     // <- SOLAR SAFE
408 
409     // SAFE ->
410     WriteGuard aWriteLock(m_aLock);
411     m_xAgentWindow = VCLUnoHelper::GetInterface(pAgentWindow);
412     css::uno::Reference< css::awt::XWindow > xAgentWindow = m_xAgentWindow;
413     aWriteLock.unlock();
414     // <- SAFE
415 
416     // add as window listener to the container window so we can maintain the property position of the agent window
417     xContainerWindow->addWindowListener(this);
418 
419     // SOLAR SAFE ->
420     {
421         ::vos::OGuard aSolarLock(Application::GetSolarMutex());
422         // establish callback for our internal used timer.
423         // Note: Its only active, if the timer will be started ...
424         m_aTimer.SetTimeoutHdl(LINK(this, HelpAgentDispatcher, implts_timerExpired));
425     }
426     // <- SOLAR SAFE
427 
428     return xAgentWindow;
429 }
430 
431 } // namespace framework
432