1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 21 #include "commonpicker.hxx" 22 #include "fpdialogbase.hxx" 23 #include "OfficeControlAccess.hxx" 24 #include <com/sun/star/beans/PropertyAttribute.hpp> 25 #include <com/sun/star/beans/PropertyValue.hpp> 26 #include <com/sun/star/beans/NamedValue.hpp> 27 #include <vcl/svapp.hxx> 28 #include <vcl/window.hxx> 29 #include <osl/mutex.hxx> 30 #include <sal/log.hxx> 31 #include <tools/debug.hxx> 32 #include <toolkit/helper/vclunohelper.hxx> 33 #include <comphelper/weakeventlistener.hxx> 34 #include <comphelper/types.hxx> 35 36 37 namespace svt 38 { 39 40 41 #define PROPERTY_ID_HELPURL 1 42 #define PROPERTY_ID_WINDOW 2 43 44 // using -------------------------------------------------------------- 45 46 using namespace ::com::sun::star::lang; 47 using namespace ::com::sun::star::ui::dialogs; 48 using namespace ::com::sun::star::uno; 49 using namespace ::com::sun::star::beans; 50 using namespace ::comphelper; 51 52 OCommonPicker()53 OCommonPicker::OCommonPicker() 54 :OCommonPicker_Base( m_aMutex ) 55 ,OPropertyContainer( GetBroadcastHelper() ) 56 ,m_nCancelEvent( nullptr ) 57 ,m_bExecuting( false ) 58 { 59 // the two properties we have 60 registerProperty( 61 "HelpURL", PROPERTY_ID_HELPURL, 62 PropertyAttribute::TRANSIENT, 63 &m_sHelpURL, cppu::UnoType<decltype(m_sHelpURL)>::get() 64 ); 65 66 registerProperty( 67 "Window", PROPERTY_ID_WINDOW, 68 PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY, 69 &m_xWindow, cppu::UnoType<decltype(m_xWindow)>::get() 70 ); 71 } 72 73 ~OCommonPicker()74 OCommonPicker::~OCommonPicker() 75 { 76 if ( !GetBroadcastHelper().bDisposed ) 77 { 78 acquire(); 79 dispose(); 80 } 81 } 82 83 84 // disambiguate XInterface 85 IMPLEMENT_FORWARD_XINTERFACE2(OCommonPicker,OCommonPicker_Base,OPropertyContainer)86 IMPLEMENT_FORWARD_XINTERFACE2( OCommonPicker, OCommonPicker_Base, OPropertyContainer ) 87 88 89 // disambiguate XTypeProvider 90 91 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCommonPicker, OCommonPicker_Base, OPropertyContainer ) 92 93 94 // XComponent related methods 95 96 void OCommonPicker::checkAlive() const 97 { 98 if ( GetBroadcastHelper().bInDispose || GetBroadcastHelper().bDisposed ) 99 throw DisposedException(); 100 } 101 prepareDialog()102 void OCommonPicker::prepareDialog() 103 { 104 if(createPicker()) 105 { 106 // set the title 107 if ( !m_aTitle.isEmpty() ) 108 m_xDlg->set_title(m_aTitle); 109 } 110 } 111 112 disposing()113 void SAL_CALL OCommonPicker::disposing() 114 { 115 SolarMutexGuard aGuard; 116 117 stopWindowListening(); 118 119 if ( m_nCancelEvent ) 120 Application::RemoveUserEvent( m_nCancelEvent ); 121 122 { 123 ::osl::MutexGuard aOwnGuard( m_aMutex ); 124 if ( m_bExecuting && m_xDlg ) 125 m_xDlg->response(RET_CANCEL); 126 } 127 128 m_xDlg.reset(); 129 m_xWindow = nullptr; 130 m_xDialogParent = nullptr; 131 } 132 133 stopWindowListening()134 void OCommonPicker::stopWindowListening() 135 { 136 disposeComponent( m_xWindowListenerAdapter ); 137 disposeComponent( m_xParentListenerAdapter ); 138 } 139 140 // XEventListener disposing(const EventObject & _rSource)141 void SAL_CALL OCommonPicker::disposing( const EventObject& _rSource ) 142 { 143 SolarMutexGuard aGuard; 144 bool bDialogDying = _rSource.Source == m_xWindow; 145 bool bParentDying = _rSource.Source == m_xDialogParent; 146 147 if ( bDialogDying || bParentDying ) 148 { 149 stopWindowListening(); 150 151 SAL_WARN_IF(bDialogDying && m_bExecuting, "fpicker.office", "unexpected disposing before response" ); 152 153 // it's the parent which is dying -> delete the dialog 154 { 155 ::osl::MutexGuard aOwnGuard(m_aMutex); 156 if (m_bExecuting && m_xDlg) 157 m_xDlg->response(RET_CANCEL); 158 } 159 160 m_xDlg.reset(); 161 m_xWindow = nullptr; 162 m_xDialogParent = nullptr; 163 } 164 else 165 { 166 OSL_FAIL( "OCommonPicker::disposing: where did this come from?" ); 167 } 168 } 169 170 // property set related methods createArrayHelper() const171 ::cppu::IPropertyArrayHelper* OCommonPicker::createArrayHelper( ) const 172 { 173 Sequence< Property > aProps; 174 describeProperties( aProps ); 175 return new cppu::OPropertyArrayHelper( aProps ); 176 } 177 getInfoHelper()178 ::cppu::IPropertyArrayHelper& SAL_CALL OCommonPicker::getInfoHelper() 179 { 180 return *getArrayHelper(); 181 } 182 getPropertySetInfo()183 Reference< XPropertySetInfo > SAL_CALL OCommonPicker::getPropertySetInfo( ) 184 { 185 return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() ); 186 } 187 setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any & rValue)188 void SAL_CALL OCommonPicker::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) 189 { 190 OPropertyContainer::setFastPropertyValue_NoBroadcast(nHandle, rValue); 191 192 // if the HelpURL changed, forward this to the dialog 193 if (PROPERTY_ID_HELPURL == nHandle && m_xDlg) 194 { 195 ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView()); 196 aAccess.setHelpURL(m_xDlg->getDialog(), m_sHelpURL); 197 } 198 } 199 createPicker()200 bool OCommonPicker::createPicker() 201 { 202 if ( !m_xDlg ) 203 { 204 m_xDlg = implCreateDialog(Application::GetFrameWeld(m_xDialogParent)); 205 SAL_WARN_IF( !m_xDlg, "fpicker.office", "OCommonPicker::createPicker: invalid dialog returned!" ); 206 207 if ( m_xDlg ) 208 { 209 weld::Dialog* pDlg = m_xDlg->getDialog(); 210 211 ::svt::OControlAccess aAccess(m_xDlg.get(), m_xDlg->GetView()); 212 // synchronize the help id of the dialog without help URL property 213 if ( !m_sHelpURL.isEmpty() ) 214 { // somebody already set the help URL while we had no dialog yet 215 aAccess.setHelpURL(pDlg, m_sHelpURL); 216 } 217 else 218 { 219 m_sHelpURL = aAccess.getHelpURL(pDlg); 220 } 221 222 m_xWindow = pDlg->GetXWindow(); 223 224 // add as event listener to the window 225 OSL_ENSURE( m_xWindow.is(), "OCommonPicker::createFileDialog: invalid window component!" ); 226 if ( m_xWindow.is() ) 227 { 228 m_xWindowListenerAdapter = new OWeakEventListenerAdapter( this, m_xWindow ); 229 // the adapter will add itself as listener, and forward notifications 230 } 231 232 VclPtr<vcl::Window> xVclDialog(VCLUnoHelper::GetWindow(m_xWindow)); 233 if (xVclDialog) // this block is quite possibly unnecessary by now 234 { 235 // _and_ add as event listener to the parent - in case the parent is destroyed 236 // before we are disposed, our disposal would access dead VCL windows then... 237 m_xDialogParent = VCLUnoHelper::GetInterface(xVclDialog->GetParent()); 238 OSL_ENSURE(m_xDialogParent.is() || !xVclDialog->GetParent(), "OCommonPicker::createFileDialog: invalid window component (the parent this time)!"); 239 } 240 if ( m_xDialogParent.is() ) 241 { 242 m_xParentListenerAdapter = new OWeakEventListenerAdapter( this, m_xDialogParent ); 243 // the adapter will add itself as listener, and forward notifications 244 } 245 } 246 } 247 248 return nullptr != m_xDlg; 249 } 250 251 // XControlAccess functions setControlProperty(const OUString & aControlName,const OUString & aControlProperty,const Any & aValue)252 void SAL_CALL OCommonPicker::setControlProperty( const OUString& aControlName, const OUString& aControlProperty, const Any& aValue ) 253 { 254 checkAlive(); 255 256 SolarMutexGuard aGuard; 257 if ( createPicker() ) 258 { 259 ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() ); 260 aAccess.setControlProperty( aControlName, aControlProperty, aValue ); 261 } 262 } 263 getControlProperty(const OUString & aControlName,const OUString & aControlProperty)264 Any SAL_CALL OCommonPicker::getControlProperty( const OUString& aControlName, const OUString& aControlProperty ) 265 { 266 checkAlive(); 267 268 SolarMutexGuard aGuard; 269 if ( createPicker() ) 270 { 271 ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() ); 272 return aAccess.getControlProperty( aControlName, aControlProperty ); 273 } 274 275 return Any(); 276 } 277 278 // XControlInformation functions getSupportedControls()279 Sequence< OUString > SAL_CALL OCommonPicker::getSupportedControls( ) 280 { 281 checkAlive(); 282 283 SolarMutexGuard aGuard; 284 if ( createPicker() ) 285 { 286 ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() ); 287 return aAccess.getSupportedControls( ); 288 } 289 290 return Sequence< OUString >(); 291 } 292 isControlSupported(const OUString & aControlName)293 sal_Bool SAL_CALL OCommonPicker::isControlSupported( const OUString& aControlName ) 294 { 295 checkAlive(); 296 297 SolarMutexGuard aGuard; 298 if ( createPicker() ) 299 { 300 return svt::OControlAccess::isControlSupported( aControlName ); 301 } 302 303 return false; 304 } 305 getSupportedControlProperties(const OUString & aControlName)306 Sequence< OUString > SAL_CALL OCommonPicker::getSupportedControlProperties( const OUString& aControlName ) 307 { 308 checkAlive(); 309 310 SolarMutexGuard aGuard; 311 if ( createPicker() ) 312 { 313 ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() ); 314 return aAccess.getSupportedControlProperties( aControlName ); 315 } 316 317 return Sequence< OUString >(); 318 } 319 isControlPropertySupported(const OUString & aControlName,const OUString & aControlProperty)320 sal_Bool SAL_CALL OCommonPicker::isControlPropertySupported( const OUString& aControlName, const OUString& aControlProperty ) 321 { 322 checkAlive(); 323 324 SolarMutexGuard aGuard; 325 if ( createPicker() ) 326 { 327 ::svt::OControlAccess aAccess( m_xDlg.get(), m_xDlg->GetView() ); 328 return aAccess.isControlPropertySupported( aControlName, aControlProperty ); 329 } 330 331 return false; 332 } 333 334 335 // XExecutableDialog functions 336 setTitle(const OUString & _rTitle)337 void OCommonPicker::setTitle( const OUString& _rTitle ) 338 { 339 SolarMutexGuard aGuard; 340 m_aTitle = _rTitle; 341 } 342 343 execute()344 sal_Int16 OCommonPicker::execute() 345 { 346 SolarMutexGuard aGuard; 347 348 prepareDialog(); 349 350 { 351 ::osl::MutexGuard aOwnGuard( m_aMutex ); 352 m_bExecuting = true; 353 } 354 sal_Int16 nResult = implExecutePicker(); 355 { 356 ::osl::MutexGuard aOwnGuard( m_aMutex ); 357 m_bExecuting = false; 358 } 359 360 return nResult; 361 } 362 363 364 // XCancellable functions 365 cancel()366 void SAL_CALL OCommonPicker::cancel( ) 367 { 368 { 369 ::osl::MutexGuard aGuard( m_aMutex ); 370 if ( m_nCancelEvent ) 371 // nothing to do - the event for cancelling the dialog is already on the way 372 return; 373 } 374 375 // The thread which executes our dialog has locked the solar mutex for 376 // sure. Cancelling the dialog should be done with a locked solar mutex, too. 377 // Thus we post ourself a message for cancelling the dialog. This way, the message 378 // is either handled in the thread which opened the dialog (which may even be 379 // this thread here), or, if no dialog is open, in the thread doing scheduling 380 // currently. Both is okay for us... 381 382 // Note that we could do check if we are really executing the dialog currently. 383 // but the information would be potentially obsolete at the moment our event 384 // arrives, so we need to check it there, anyway... 385 m_nCancelEvent = Application::PostUserEvent( LINK( this, OCommonPicker, OnCancelPicker ) ); 386 } 387 IMPL_LINK_NOARG(OCommonPicker,OnCancelPicker,void *,void)388 IMPL_LINK_NOARG(OCommonPicker, OnCancelPicker, void*, void) 389 { 390 // By definition, the solar mutex is locked when we arrive here. Note that this 391 // is important, as for instance the consistency of m_xDlg depends on this mutex. 392 ::osl::MutexGuard aGuard( m_aMutex ); 393 m_nCancelEvent = nullptr; 394 395 if ( !m_bExecuting ) 396 // nothing to do. This may be because the dialog was canceled after our cancel method 397 // posted this async event, or because somebody called cancel without the dialog 398 // being executed at this time. 399 return; 400 401 OSL_ENSURE( m_xDlg, "OCommonPicker::OnCancelPicker: executing, but no dialog!" ); 402 if (m_xDlg) 403 m_xDlg->response(RET_CANCEL); 404 } 405 406 // XInitialization functions initialize(const Sequence<Any> & _rArguments)407 void SAL_CALL OCommonPicker::initialize( const Sequence< Any >& _rArguments ) 408 { 409 checkAlive(); 410 411 OUString sSettingName; 412 Any aSettingValue; 413 414 PropertyValue aPropArg; 415 NamedValue aPairArg; 416 417 418 const Any* pArguments = _rArguments.getConstArray(); 419 const Any* pArgumentsEnd = _rArguments.getConstArray() + _rArguments.getLength(); 420 for ( const Any* pArgument = pArguments; 421 pArgument != pArgumentsEnd; 422 ++pArgument 423 ) 424 { 425 if ( *pArgument >>= aPropArg ) 426 { 427 if ( aPropArg.Name.isEmpty()) 428 continue; 429 430 sSettingName = aPropArg.Name; 431 aSettingValue = aPropArg.Value; 432 } 433 else if ( *pArgument >>= aPairArg ) 434 { 435 if ( aPairArg.Name.isEmpty()) 436 continue; 437 438 sSettingName = aPairArg.Name; 439 aSettingValue = aPairArg.Value; 440 441 442 } 443 else 444 { 445 SAL_WARN( "fpicker", "OCommonPicker::initialize: unknown argument type at position " 446 << (pArguments - _rArguments.getConstArray())); 447 continue; 448 } 449 450 bool bKnownSetting = 451 implHandleInitializationArgument( sSettingName, aSettingValue ); 452 DBG_ASSERT( bKnownSetting, 453 OString( 454 "OCommonPicker::initialize: unknown argument \"" 455 + OString(sSettingName.getStr(), sSettingName.getLength(), osl_getThreadTextEncoding()) 456 + "\"!").getStr() ); 457 } 458 } 459 implHandleInitializationArgument(const OUString & _rName,const Any & _rValue)460 bool OCommonPicker::implHandleInitializationArgument( const OUString& _rName, const Any& _rValue ) 461 { 462 bool bKnown = true; 463 if ( _rName == "ParentWindow" ) 464 { 465 m_xDialogParent.clear(); 466 OSL_VERIFY( _rValue >>= m_xDialogParent ); 467 OSL_ENSURE( m_xDialogParent.is(), "OCommonPicker::implHandleInitializationArgument: invalid parent window given!" ); 468 } 469 else 470 bKnown = false; 471 return bKnown; 472 } 473 474 } // namespace svt 475 476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 477