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 //-----------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
128 void SAL_CALL HelpAgentDispatcher::windowResized(const css::awt::WindowEvent&)
129 	throw(css::uno::RuntimeException)
130 {
131 	implts_positionAgentWindow();
132 }
133 
134 //--------------------------------------------------------------------
135 void SAL_CALL HelpAgentDispatcher::windowMoved(const css::awt::WindowEvent&)
136 	throw(css::uno::RuntimeException)
137 {
138 	implts_positionAgentWindow();
139 }
140 
141 //--------------------------------------------------------------------
142 void SAL_CALL HelpAgentDispatcher::windowShown(const css::lang::EventObject&)
143 	throw(css::uno::RuntimeException)
144 {
145 	implts_showAgentWindow();
146 }
147 
148 //--------------------------------------------------------------------
149 void SAL_CALL HelpAgentDispatcher::windowHidden(const css::lang::EventObject&)
150 	throw(css::uno::RuntimeException)
151 {
152 	implts_hideAgentWindow();
153 }
154 
155 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
193 void HelpAgentDispatcher::helpRequested()
194 {
195 	implts_stopTimer();
196 	implts_hideAgentWindow();
197 	implts_acceptCurrentURL();
198 }
199 
200 //-----------------------------------------------
201 void HelpAgentDispatcher::closeAgent()
202 {
203 	implts_stopTimer();
204 	implts_hideAgentWindow();
205 	implts_ignoreCurrentURL();
206 }
207 
208 //--------------------------------------------------------------------
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 isn't 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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 normally we are ref counted. So we must make sure that this
288 	// dispatcher isn't 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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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 //--------------------------------------------------------------------
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