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