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