blob: 136279c215d5e38a660f9ea43f1cd8420a416310 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_framework.hxx"
#include <dispatch/helpagentdispatcher.hxx>
#include <threadhelp/readguard.hxx>
#include <threadhelp/writeguard.hxx>
#include <com/sun/star/awt/XWindow2.hpp>
#include <com/sun/star/awt/PosSize.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <toolkit/helper/vclunohelper.hxx>
#include <svtools/helpopt.hxx>
#include <vcl/svapp.hxx>
#include <vcl/help.hxx>
namespace css = ::com::sun::star;
//........................................................................
namespace framework
{
//-----------------------------------------------
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))
//-----------------------------------------------
DEFINE_XTYPEPROVIDER_2(HelpAgentDispatcher ,
css::lang::XTypeProvider,
css::frame::XDispatch )
//--------------------------------------------------------------------
HelpAgentDispatcher::HelpAgentDispatcher( const css::uno::Reference< css::frame::XFrame >& xParentFrame)
: ThreadHelpBase (&Application::GetSolarMutex())
, m_sCurrentURL ( )
, m_xContainerWindow( )
, m_xAgentWindow ( )
, m_aTimer ( )
, m_xSelfHold ( )
{
// It's required that this class has to be contructed with a valid frame.
// And "valid" means: the frame must already bound to a valid container window.
m_xContainerWindow = xParentFrame->getContainerWindow();
}
//--------------------------------------------------------------------
HelpAgentDispatcher::~HelpAgentDispatcher()
{
implts_stopTimer();
implts_ignoreCurrentURL();
// Needed ... because it was create as "new VCLWindow()" ! Such windows must be disposed explicitly.
css::uno::Reference< css::lang::XComponent > xAgentWindow(m_xAgentWindow, css::uno::UNO_QUERY);
if (xAgentWindow.is())
xAgentWindow->dispose();
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::dispatch(const css::util::URL& aURL ,
const css::uno::Sequence< css::beans::PropertyValue >&)
throw(css::uno::RuntimeException)
{
// silently drop the request if the new URL was marked to be ignored next time.
sal_Int32 nAllowedToIgnore = SvtHelpOptions().getAgentIgnoreURLCounter(aURL.Complete);
if (nAllowedToIgnore < 1)
return;
// stop the expiration timer for the old URL
// The timer will add the old URL to the list of ignorable URLs.
// So m_sCurrentURL must be set AFTER the timer was stopped !!!
implts_stopTimer();
// SAFE ->
WriteGuard aWriteLock(m_aLock);
m_sCurrentURL = aURL.Complete;
aWriteLock.unlock();
// <- SAFE
// start the expiration timer for the new URL
implts_startTimer();
// make sure the agent window is shown
implts_showAgentWindow();
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >&,
const css::util::URL&)
throw(css::uno::RuntimeException)
{
// no status available
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >&,
const css::util::URL&)
throw(css::uno::RuntimeException)
{
// no status available
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::windowResized(const css::awt::WindowEvent&)
throw(css::uno::RuntimeException)
{
implts_positionAgentWindow();
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::windowMoved(const css::awt::WindowEvent&)
throw(css::uno::RuntimeException)
{
implts_positionAgentWindow();
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::windowShown(const css::lang::EventObject&)
throw(css::uno::RuntimeException)
{
implts_showAgentWindow();
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::windowHidden(const css::lang::EventObject&)
throw(css::uno::RuntimeException)
{
implts_hideAgentWindow();
}
//--------------------------------------------------------------------
void SAL_CALL HelpAgentDispatcher::disposing(const css::lang::EventObject& aEvent)
throw(css::uno::RuntimeException)
{
// SAFE ->
WriteGuard aWriteLock(m_aLock);
// Already disposed ?!
if (! m_xContainerWindow.is())
return;
// Wrong broadcaster ?!
if (aEvent.Source != m_xContainerWindow)
return;
css::uno::Reference< css::uno::XInterface > xSelfHoldUntilMethodEnds(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
m_xSelfHold.clear();
aWriteLock.unlock();
// <- SAFE
implts_stopTimer();
implts_hideAgentWindow();
implts_ignoreCurrentURL();
// SAFE ->
aWriteLock.lock();
m_xContainerWindow.clear();
css::uno::Reference< css::lang::XComponent > xAgentWindow(m_xAgentWindow, css::uno::UNO_QUERY);
m_xAgentWindow.clear();
aWriteLock.unlock();
// <- SAFE
// Needed ... because it was create as "new VCLWindow()" ! Such windows must be disposed explicitly.
if (xAgentWindow.is())
xAgentWindow->dispose();
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::helpRequested()
{
implts_stopTimer();
implts_hideAgentWindow();
implts_acceptCurrentURL();
}
//-----------------------------------------------
void HelpAgentDispatcher::closeAgent()
{
implts_stopTimer();
implts_hideAgentWindow();
implts_ignoreCurrentURL();
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_acceptCurrentURL()
{
// SAFE ->
WriteGuard aWriteLock(m_aLock);
::rtl::OUString sAcceptedURL = m_sCurrentURL;
m_sCurrentURL = ::rtl::OUString();
aWriteLock.unlock();
// <- SAFE
// We must make sure that this URL isnt marked as ignored by the user.
// Otherwhise the user wont see the corresponding help content in the future.
SvtHelpOptions().resetAgentIgnoreURLCounter(sAcceptedURL);
// show the right help content
// SOLAR SAFE ->
{
::vos::OGuard aSolarLock(Application::GetSolarMutex());
Help* pHelp = Application::GetHelp();
if (pHelp)
pHelp->Start(sAcceptedURL, NULL);
}
// <- SOLAR SAFE
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_ignoreCurrentURL()
{
// SAFE ->
WriteGuard aWriteLock(m_aLock);
::rtl::OUString sIgnoredURL = m_sCurrentURL;
m_sCurrentURL = ::rtl::OUString();
aWriteLock.unlock();
// <- SAFE
if (sIgnoredURL.getLength())
SvtHelpOptions().decAgentIgnoreURLCounter(sIgnoredURL);
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_stopTimer()
{
// SAFE ->
WriteGuard aWriteLock(m_aLock);
m_xSelfHold.clear();
aWriteLock.unlock();
// <- SAFE
// SOLAR SAFE ->
// Timer access needs no "own lock" ! It lives if we live ...
// But it requires locking of the solar mutex ... because it's a vcl based timer.
{
::vos::OGuard aSolarLock(Application::GetSolarMutex());
if (! m_aTimer.IsActive())
return;
m_aTimer.Stop();
}
// <- SOLAR SAFE
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_startTimer()
{
// SOLAR SAFE ->
// Timer access needs no "own lock" ! It lives if we live ...
// But it requires locking of the solar mutex ... because it's a vcl based timer.
{
::vos::OGuard aSolarLock(Application::GetSolarMutex());
if (m_aTimer.IsActive())
return;
}
// <- SOLAR SAFE
// SAFE ->
// Timer uses pointer to this help agent dispatcher ...
// But normaly we are ref counted. So we must make sure that this
// dispatcher isnt killed during the timer runs .-)
WriteGuard aWriteLock(m_aLock);
m_xSelfHold = css::uno::Reference< css::uno::XInterface >(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
aWriteLock.unlock();
// <- SAFE
sal_Int32 nTime = SvtHelpOptions().GetHelpAgentTimeoutPeriod();
// SOLAR SAFE ->
// Timer access needs no "own lock" ! It lives if we live ...
// But it requires locking of the solar mutex ... because it's a vcl based timer.
{
::vos::OGuard aSolarLock(Application::GetSolarMutex());
m_aTimer.SetTimeout(nTime*1000); // sec => ms !
m_aTimer.Start();
}
}
//-----------------------------------------------
IMPL_LINK(HelpAgentDispatcher, implts_timerExpired, void*,)
{
// This method is called by using a pointer to us.
// But we must be aware that we can be destroyed hardly
// if our uno reference will be gone!
// => Hold this object alive till this method finish its work.
// SAFE ->
WriteGuard aWriteLock(m_aLock);
css::uno::Reference< css::uno::XInterface > xSelfHoldUntilMethodEnds(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY_THROW);
m_xSelfHold.clear();
aWriteLock.unlock();
// <- SAFE
implts_hideAgentWindow();
implts_ignoreCurrentURL();
return 0;
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_showAgentWindow()
{
// SAFE ->
ReadGuard aReadLock(m_aLock);
css::uno::Reference< css::awt::XWindow2 > xContainerWindow(m_xContainerWindow, css::uno::UNO_QUERY_THROW);
aReadLock.unlock();
// <- SAFE
css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
if (
(xContainerWindow.is() ) &&
(xAgentWindow.is() ) &&
(xContainerWindow->isVisible())
)
{
// make sure that agent window resists at the right place .-)
implts_positionAgentWindow();
xAgentWindow->setVisible(sal_True);
}
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_hideAgentWindow()
{
css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
if (xAgentWindow.is())
xAgentWindow->setVisible(sal_False);
}
//--------------------------------------------------------------------
void HelpAgentDispatcher::implts_positionAgentWindow()
{
// SAFE ->
ReadGuard aReadLock(m_aLock);
css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
aReadLock.unlock();
// <- SAFE
css::uno::Reference< css::awt::XWindow > xAgentWindow = implts_ensureAgentWindow();
if (
(! xContainerWindow.is()) ||
(! xAgentWindow.is() )
)
return;
::svt::HelpAgentWindow* pAgentWindow = (::svt::HelpAgentWindow*)VCLUnoHelper::GetWindow(xAgentWindow);
const css::awt::Rectangle aContainerSize = xContainerWindow->getPosSize();
const Size aAgentSize = pAgentWindow->getPreferredSizePixel();
sal_Int32 nW = aAgentSize.Width() ;
sal_Int32 nH = aAgentSize.Height();
if (nW < 1)
nW = 100;
if (nH < 1)
nH = 100;
sal_Int32 nX = aContainerSize.Width - nW;
sal_Int32 nY = aContainerSize.Height - nH;
// TODO: use a surrogate if the container window is too small to contain the full-sized agent window
xAgentWindow->setPosSize(nX, nY, nW, nH, css::awt::PosSize::POSSIZE);
}
//--------------------------------------------------------------------
css::uno::Reference< css::awt::XWindow > HelpAgentDispatcher::implts_ensureAgentWindow()
{
// SAFE ->
ReadGuard aReadLock(m_aLock);
if (m_xAgentWindow.is())
return m_xAgentWindow;
css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow;
aReadLock.unlock();
// <- SAFE
if (!xContainerWindow.is())
return css::uno::Reference< css::awt::XWindow >();
::svt::HelpAgentWindow* pAgentWindow = 0;
// SOLAR SAFE ->
{
::vos::OGuard aSolarLock(Application::GetSolarMutex());
// create the agent window
Window* pContainerWindow = VCLUnoHelper::GetWindow(xContainerWindow);
pAgentWindow = new ::svt::HelpAgentWindow(pContainerWindow);
pAgentWindow->setCallback(this);
}
// <- SOLAR SAFE
// SAFE ->
WriteGuard aWriteLock(m_aLock);
m_xAgentWindow = VCLUnoHelper::GetInterface(pAgentWindow);
css::uno::Reference< css::awt::XWindow > xAgentWindow = m_xAgentWindow;
aWriteLock.unlock();
// <- SAFE
// add as window listener to the container window so we can maintain the property position of the agent window
xContainerWindow->addWindowListener(this);
// SOLAR SAFE ->
{
::vos::OGuard aSolarLock(Application::GetSolarMutex());
// establish callback for our internal used timer.
// Note: Its only active, if the timer will be started ...
m_aTimer.SetTimeoutHdl(LINK(this, HelpAgentDispatcher, implts_timerExpired));
}
// <- SOLAR SAFE
return xAgentWindow;
}
} // namespace framework