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 #include "ImageControl.hxx" 21 22 #include <strings.hrc> 23 #include <frm_resource.hxx> 24 #include <property.hxx> 25 #include <services.hxx> 26 #include <componenttools.hxx> 27 28 #include <svtools/imageresourceaccess.hxx> 29 #include <sfx2/filedlghelper.hxx> 30 #include <com/sun/star/awt/PopupMenu.hpp> 31 #include <com/sun/star/awt/XPopupMenu.hpp> 32 #include <com/sun/star/awt/PopupMenuDirection.hpp> 33 #include <com/sun/star/form/FormComponentType.hpp> 34 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp> 35 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> 36 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp> 37 #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> 38 #include <com/sun/star/sdbc/DataType.hpp> 39 #include <com/sun/star/awt/MouseButton.hpp> 40 #include <com/sun/star/awt/XWindow.hpp> 41 #include <com/sun/star/graphic/XGraphic.hpp> 42 #include <com/sun/star/graphic/GraphicObject.hpp> 43 #include <tools/urlobj.hxx> 44 #include <tools/stream.hxx> 45 #include <tools/debug.hxx> 46 #include <tools/diagnose_ex.h> 47 #include <vcl/graph.hxx> 48 #include <vcl/svapp.hxx> 49 #include <unotools/streamhelper.hxx> 50 #include <comphelper/guarding.hxx> 51 #include <comphelper/property.hxx> 52 #include <comphelper/types.hxx> 53 #include <cppuhelper/queryinterface.hxx> 54 #include <unotools/ucbstreamhelper.hxx> 55 #include <svl/urihelper.hxx> 56 57 #include <memory> 58 59 #define ID_OPEN_GRAPHICS 1 60 #define ID_CLEAR_GRAPHICS 2 61 62 namespace frm 63 { 64 65 using namespace ::com::sun::star; 66 using namespace ::com::sun::star::uno; 67 using namespace ::com::sun::star::sdb; 68 using namespace ::com::sun::star::sdbc; 69 using namespace ::com::sun::star::beans; 70 using namespace ::com::sun::star::container; 71 using namespace ::com::sun::star::form; 72 using namespace ::com::sun::star::awt; 73 using namespace ::com::sun::star::io; 74 using namespace ::com::sun::star::ui::dialogs; 75 using namespace ::com::sun::star::lang; 76 using namespace ::com::sun::star::util; 77 using namespace ::com::sun::star::graphic; 78 using namespace ::com::sun::star::frame; 79 80 81 //= OImageControlModel 82 83 namespace 84 { 85 enum ImageStoreType 86 { 87 ImageStoreBinary, 88 ImageStoreLink, 89 90 ImageStoreInvalid 91 }; 92 93 ImageStoreType lcl_getImageStoreType( const sal_Int32 _nFieldType ) 94 { 95 // binary/longvarchar types could be used to store images in binary representation 96 if ( ( _nFieldType == DataType::BINARY ) 97 || ( _nFieldType == DataType::VARBINARY ) 98 || ( _nFieldType == DataType::LONGVARBINARY ) 99 || ( _nFieldType == DataType::OTHER ) 100 || ( _nFieldType == DataType::OBJECT ) 101 || ( _nFieldType == DataType::BLOB ) 102 || ( _nFieldType == DataType::LONGVARCHAR ) 103 || ( _nFieldType == DataType::CLOB ) 104 ) 105 return ImageStoreBinary; 106 107 // char types could be used to store links to images 108 if ( ( _nFieldType == DataType::CHAR ) 109 || ( _nFieldType == DataType::VARCHAR ) 110 ) 111 return ImageStoreLink; 112 113 return ImageStoreInvalid; 114 } 115 } 116 117 118 // OImageControlModel 119 120 Sequence<Type> OImageControlModel::_getTypes() 121 { 122 return concatSequences( 123 OBoundControlModel::_getTypes(), 124 OImageControlModel_Base::getTypes() 125 ); 126 } 127 128 129 OImageControlModel::OImageControlModel(const Reference<XComponentContext>& _rxFactory) 130 :OBoundControlModel( _rxFactory, VCL_CONTROLMODEL_IMAGECONTROL, FRM_SUN_CONTROL_IMAGECONTROL, false, false, false ) 131 // use the old control name for compytibility reasons 132 ,m_bExternalGraphic( true ) 133 ,m_bReadOnly( false ) 134 { 135 m_nClassId = FormComponentType::IMAGECONTROL; 136 initOwnValueProperty( PROPERTY_IMAGE_URL ); 137 138 implConstruct(); 139 } 140 141 142 OImageControlModel::OImageControlModel( const OImageControlModel* _pOriginal, const Reference< XComponentContext >& _rxFactory ) 143 :OBoundControlModel( _pOriginal, _rxFactory ) 144 // use the old control name for compytibility reasons 145 ,m_bExternalGraphic( true ) 146 ,m_bReadOnly( _pOriginal->m_bReadOnly ) 147 ,m_sImageURL( _pOriginal->m_sImageURL ) 148 ,m_xGraphicObject( _pOriginal->m_xGraphicObject ) 149 { 150 implConstruct(); 151 152 osl_atomic_increment( &m_refCount ); 153 { 154 // simulate a propertyChanged event for the ImageURL 155 ::osl::MutexGuard aGuard( m_aMutex ); 156 impl_handleNewImageURL_lck( eOther ); 157 } 158 osl_atomic_decrement( &m_refCount ); 159 } 160 161 162 void OImageControlModel::implConstruct() 163 { 164 m_xImageProducer = new ImageProducer; 165 m_xImageProducer->SetDoneHdl( LINK( this, OImageControlModel, OnImageImportDone ) ); 166 } 167 168 169 OImageControlModel::~OImageControlModel() 170 { 171 if (!OComponentHelper::rBHelper.bDisposed) 172 { 173 acquire(); 174 dispose(); 175 } 176 177 } 178 179 // XCloneable 180 181 IMPLEMENT_DEFAULT_CLONING( OImageControlModel ) 182 183 // XServiceInfo 184 185 css::uno::Sequence<OUString> OImageControlModel::getSupportedServiceNames() 186 { 187 css::uno::Sequence<OUString> aSupported = OBoundControlModel::getSupportedServiceNames(); 188 aSupported.realloc(aSupported.getLength() + 2); 189 190 OUString*pArray = aSupported.getArray(); 191 pArray[aSupported.getLength()-2] = FRM_SUN_COMPONENT_IMAGECONTROL; 192 pArray[aSupported.getLength()-1] = FRM_COMPONENT_IMAGECONTROL; 193 return aSupported; 194 } 195 196 197 Any SAL_CALL OImageControlModel::queryAggregation(const Type& _rType) 198 { 199 // Order matters: we want to "override" the XImageProducer interface of the aggregate without 200 // own XImageProducer interface, thus we need to query OImageControlModel_Base first 201 Any aReturn = OImageControlModel_Base::queryInterface( _rType ); 202 203 // BUT: _don't_ let it feel responsible for the XTypeProvider interface 204 // (as this is implemented by our base class in the proper way) 205 if ( _rType.equals( cppu::UnoType<XTypeProvider>::get() ) 206 || !aReturn.hasValue() 207 ) 208 aReturn = OBoundControlModel::queryAggregation( _rType ); 209 210 return aReturn; 211 } 212 213 214 bool OImageControlModel::approveDbColumnType( sal_Int32 _nColumnType ) 215 { 216 return ImageStoreInvalid != lcl_getImageStoreType( _nColumnType ); 217 } 218 219 220 void OImageControlModel::getFastPropertyValue(Any& rValue, sal_Int32 nHandle) const 221 { 222 switch (nHandle) 223 { 224 case PROPERTY_ID_READONLY: 225 rValue <<= m_bReadOnly; 226 break; 227 case PROPERTY_ID_IMAGE_URL: 228 rValue <<= m_sImageURL; 229 break; 230 case PROPERTY_ID_GRAPHIC: 231 rValue <<= m_xGraphicObject.is() ? m_xGraphicObject->getGraphic() : Reference< XGraphic >(); 232 break; 233 default: 234 OBoundControlModel::getFastPropertyValue(rValue, nHandle); 235 } 236 } 237 238 239 void OImageControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle, const Any& rValue) 240 { 241 switch (nHandle) 242 { 243 case PROPERTY_ID_READONLY : 244 DBG_ASSERT(rValue.getValueType().getTypeClass() == TypeClass_BOOLEAN, "OImageControlModel::setFastPropertyValue_NoBroadcast : invalid type !" ); 245 m_bReadOnly = getBOOL(rValue); 246 break; 247 248 case PROPERTY_ID_IMAGE_URL: 249 OSL_VERIFY( rValue >>= m_sImageURL ); 250 impl_handleNewImageURL_lck( eOther ); 251 { 252 ControlModelLock aLock( *this ); 253 // that's a fake ... onValuePropertyChange expects to receive the only lock to our instance, 254 // but we're already called with our mutex locked ... 255 onValuePropertyChange( aLock ); 256 } 257 break; 258 259 case PROPERTY_ID_GRAPHIC: 260 { 261 Reference< XGraphic > xGraphic; 262 OSL_VERIFY( rValue >>= xGraphic ); 263 if ( !xGraphic.is() ) 264 m_xGraphicObject.clear(); 265 else 266 { 267 m_xGraphicObject = graphic::GraphicObject::create( m_xContext ); 268 m_xGraphicObject->setGraphic( xGraphic ); 269 } 270 271 if ( m_bExternalGraphic ) 272 { 273 m_sImageURL = OUString(); 274 // TODO: speaking strictly, this would need to be notified, since ImageURL is a bound property. However, 275 // this method here is called with a locked mutex, so we cannot simply call listeners ... 276 // I think the missing notification (and thus clients which potentially cannot observe the change) 277 // is less severe than the potential deadlock ... 278 } 279 } 280 break; 281 282 default: 283 OBoundControlModel::setFastPropertyValue_NoBroadcast(nHandle, rValue); 284 break; 285 } 286 } 287 288 289 sal_Bool OImageControlModel::convertFastPropertyValue(Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue) 290 { 291 switch (nHandle) 292 { 293 case PROPERTY_ID_READONLY : 294 return tryPropertyValue(rConvertedValue, rOldValue, rValue, m_bReadOnly); 295 296 case PROPERTY_ID_IMAGE_URL: 297 return tryPropertyValue( rConvertedValue, rOldValue, rValue, m_sImageURL ); 298 299 case PROPERTY_ID_GRAPHIC: 300 { 301 const Reference< XGraphic > xGraphic( getFastPropertyValue( PROPERTY_ID_GRAPHIC ), UNO_QUERY ); 302 return tryPropertyValue( rConvertedValue, rOldValue, rValue, xGraphic ); 303 } 304 305 default: 306 return OBoundControlModel::convertFastPropertyValue(rConvertedValue, rOldValue, nHandle, rValue); 307 } 308 } 309 310 311 void OImageControlModel::describeFixedProperties( Sequence< Property >& _rProps ) const 312 { 313 BEGIN_DESCRIBE_PROPERTIES( 4, OBoundControlModel ) 314 DECL_IFACE_PROP2( GRAPHIC, XGraphic, BOUND, TRANSIENT ); 315 DECL_PROP1 ( IMAGE_URL, OUString, BOUND ); 316 DECL_BOOL_PROP1 ( READONLY, BOUND ); 317 DECL_PROP1 ( TABINDEX, sal_Int16, BOUND ); 318 END_DESCRIBE_PROPERTIES(); 319 } 320 321 322 void OImageControlModel::describeAggregateProperties( Sequence< Property >& /* [out] */ o_rAggregateProperties ) const 323 { 324 OBoundControlModel::describeAggregateProperties( o_rAggregateProperties ); 325 // remove ImageURL and Graphic properties, we "override" them. 326 // This is because our aggregate synchronizes those 327 // two, but we have an own synchronization mechanism. 328 RemoveProperty( o_rAggregateProperties, PROPERTY_IMAGE_URL ); 329 RemoveProperty( o_rAggregateProperties, PROPERTY_GRAPHIC ); 330 } 331 332 333 OUString OImageControlModel::getServiceName() 334 { 335 return FRM_COMPONENT_IMAGECONTROL; // old (non-sun) name for compatibility ! 336 } 337 338 339 void OImageControlModel::write(const Reference<XObjectOutputStream>& _rxOutStream) 340 { 341 // Base class 342 OBoundControlModel::write(_rxOutStream); 343 // Version 344 _rxOutStream->writeShort(0x0003); 345 // Name 346 _rxOutStream->writeBoolean(m_bReadOnly); 347 writeHelpTextCompatibly(_rxOutStream); 348 // from version 0x0003 : common properties 349 writeCommonProperties(_rxOutStream); 350 } 351 352 353 void OImageControlModel::read(const Reference<XObjectInputStream>& _rxInStream) 354 { 355 OBoundControlModel::read(_rxInStream); 356 357 // Version 358 sal_uInt16 nVersion = _rxInStream->readShort(); 359 switch (nVersion) 360 { 361 case 0x0001: 362 m_bReadOnly = _rxInStream->readBoolean(); 363 break; 364 case 0x0002: 365 m_bReadOnly = _rxInStream->readBoolean(); 366 readHelpTextCompatibly(_rxInStream); 367 break; 368 case 0x0003: 369 m_bReadOnly = _rxInStream->readBoolean(); 370 readHelpTextCompatibly(_rxInStream); 371 readCommonProperties(_rxInStream); 372 break; 373 default : 374 OSL_FAIL("OImageControlModel::read : unknown version !"); 375 m_bReadOnly = false; 376 defaultCommonProperties(); 377 break; 378 } 379 // Display default values after read 380 if ( !getControlSource().isEmpty() ) 381 { // (not if we don't have a control source - the "State" property acts like it is persistent, then 382 ::osl::MutexGuard aGuard(m_aMutex); // resetNoBroadcast expects this mutex guarding 383 resetNoBroadcast(); 384 } 385 } 386 387 388 bool OImageControlModel::impl_updateStreamForURL_lck( const OUString& _rURL, ValueChangeInstigator _eInstigator ) 389 { 390 // create a stream for the image specified by the URL 391 std::unique_ptr< SvStream > pImageStream; 392 Reference< XInputStream > xImageStream; 393 394 if ( ::svt::GraphicAccess::isSupportedURL( _rURL ) ) 395 { 396 xImageStream = ::svt::GraphicAccess::getImageXStream( getContext(), _rURL ); 397 } 398 else 399 { 400 pImageStream = ::utl::UcbStreamHelper::CreateStream( _rURL, StreamMode::READ ); 401 bool bSetNull = (pImageStream == nullptr) || (ERRCODE_NONE != pImageStream->GetErrorCode()); 402 403 if ( !bSetNull ) 404 { 405 // get the size of the stream 406 sal_uInt64 const nSize = pImageStream->remainingSize(); 407 if (pImageStream->GetBufferSize() < 8192) 408 pImageStream->SetBufferSize(8192); 409 pImageStream->Seek(STREAM_SEEK_TO_BEGIN); 410 411 xImageStream = new ::utl::OInputStreamHelper( new SvLockBytes( pImageStream.get(), false ), nSize ); 412 } 413 } 414 415 if ( xImageStream.is() ) 416 { 417 if ( m_xColumnUpdate.is() ) 418 m_xColumnUpdate->updateBinaryStream( xImageStream, xImageStream->available() ); 419 else 420 setControlValue( makeAny( xImageStream ), _eInstigator ); 421 xImageStream->closeInput(); 422 return true; 423 } 424 425 return false; 426 } 427 428 429 void OImageControlModel::impl_handleNewImageURL_lck( ValueChangeInstigator _eInstigator ) 430 { 431 switch ( lcl_getImageStoreType( getFieldType() ) ) 432 { 433 case ImageStoreBinary: 434 if ( impl_updateStreamForURL_lck( m_sImageURL, _eInstigator ) ) 435 return; 436 break; 437 438 case ImageStoreLink: 439 { 440 OUString sCommitURL( m_sImageURL ); 441 if ( !m_sDocumentURL.isEmpty() ) 442 sCommitURL = URIHelper::simpleNormalizedMakeRelative( m_sDocumentURL, sCommitURL ); 443 OSL_ENSURE( m_xColumnUpdate.is(), "OImageControlModel::impl_handleNewImageURL_lck: no bound field, but ImageStoreLink?!" ); 444 if ( m_xColumnUpdate.is() ) 445 { 446 m_xColumnUpdate->updateString( sCommitURL ); 447 return; 448 } 449 } 450 break; 451 452 case ImageStoreInvalid: 453 OSL_FAIL( "OImageControlModel::impl_handleNewImageURL_lck: image storage type type!" ); 454 break; 455 } 456 457 // if we're here, then the above code was unable to update our field/control from the given URL 458 // => fall back to NULL/VOID 459 if ( m_xColumnUpdate.is() ) 460 m_xColumnUpdate->updateNull(); 461 else 462 setControlValue( Any(), _eInstigator ); 463 } 464 465 466 bool OImageControlModel::commitControlValueToDbColumn( bool _bPostReset ) 467 { 468 if ( _bPostReset ) 469 { 470 // since this is a "commit after reset", we can simply update the column 471 // with null - this is our "default" which we were just reset to 472 if ( m_xColumnUpdate.is() ) 473 m_xColumnUpdate->updateNull(); 474 } 475 else 476 { 477 ::osl::MutexGuard aGuard(m_aMutex); 478 impl_handleNewImageURL_lck( eDbColumnBinding ); 479 } 480 481 return true; 482 } 483 484 485 namespace 486 { 487 bool lcl_isValidDocumentURL( std::u16string_view _rDocURL ) 488 { 489 return ( !_rDocURL.empty() && _rDocURL != u"private:object" ); 490 } 491 } 492 493 494 void OImageControlModel::onConnectedDbColumn( const Reference< XInterface >& _rxForm ) 495 { 496 OBoundControlModel::onConnectedDbColumn( _rxForm ); 497 498 try 499 { 500 Reference< XModel > xDocument( getXModel( *this ) ); 501 if ( xDocument.is() ) 502 { 503 m_sDocumentURL = xDocument->getURL(); 504 if ( !lcl_isValidDocumentURL( m_sDocumentURL ) ) 505 { 506 Reference< XChild > xAsChild( xDocument, UNO_QUERY ); 507 while ( xAsChild.is() && !lcl_isValidDocumentURL( m_sDocumentURL ) ) 508 { 509 xDocument.set( xAsChild->getParent(), UNO_QUERY ); 510 if ( xDocument.is() ) 511 m_sDocumentURL = xDocument->getURL(); 512 xAsChild.set( xDocument, UNO_QUERY ); 513 } 514 } 515 } 516 } 517 catch( const Exception& ) 518 { 519 DBG_UNHANDLED_EXCEPTION("forms.component"); 520 } 521 } 522 523 524 void OImageControlModel::onDisconnectedDbColumn() 525 { 526 OBoundControlModel::onDisconnectedDbColumn(); 527 528 m_sDocumentURL.clear(); 529 } 530 531 532 Any OImageControlModel::translateDbColumnToControlValue() 533 { 534 switch ( lcl_getImageStoreType( getFieldType() ) ) 535 { 536 case ImageStoreBinary: 537 { 538 Reference< XInputStream > xImageStream( m_xColumn->getBinaryStream() ); 539 if ( m_xColumn->wasNull() ) 540 xImageStream.clear(); 541 return makeAny( xImageStream ); 542 } 543 case ImageStoreLink: 544 { 545 OUString sImageLink( m_xColumn->getString() ); 546 if ( !m_sDocumentURL.isEmpty() ) 547 sImageLink = INetURLObject::GetAbsURL( m_sDocumentURL, sImageLink ); 548 return makeAny( sImageLink ); 549 } 550 case ImageStoreInvalid: 551 OSL_FAIL( "OImageControlModel::translateDbColumnToControlValue: invalid field type!" ); 552 break; 553 } 554 return Any(); 555 } 556 557 558 Any OImageControlModel::getControlValue( ) const 559 { 560 return makeAny( m_sImageURL ); 561 } 562 563 564 void OImageControlModel::doSetControlValue( const Any& _rValue ) 565 { 566 DBG_ASSERT( GetImageProducer() && m_xImageProducer.is(), "OImageControlModel::doSetControlValue: no image producer!" ); 567 if ( !GetImageProducer() || !m_xImageProducer.is() ) 568 return; 569 570 bool bStartProduction = false; 571 switch ( lcl_getImageStoreType( getFieldType() ) ) 572 { 573 case ImageStoreBinary: 574 { 575 // give the image producer the stream 576 Reference< XInputStream > xInStream; 577 _rValue >>= xInStream; 578 GetImageProducer()->setImage( xInStream ); 579 bStartProduction = true; 580 } 581 break; 582 583 case ImageStoreLink: 584 { 585 OUString sImageURL; 586 _rValue >>= sImageURL; 587 GetImageProducer()->SetImage( sImageURL ); 588 bStartProduction = true; 589 } 590 break; 591 592 case ImageStoreInvalid: 593 OSL_FAIL( "OImageControlModel::doSetControlValue: invalid field type!" ); 594 break; 595 596 } // switch ( lcl_getImageStoreType( getFieldType() ) ) 597 598 if ( bStartProduction ) 599 { 600 // start production 601 rtl::Reference< ImageProducer > xProducer = m_xImageProducer; 602 { 603 // release our mutex once (it's acquired in the calling method!), as starting the image production may 604 // result in the locking of the solar mutex (unfortunately the default implementation of our aggregate, 605 // VCLXImageControl, does this locking) 606 MutexRelease aRelease(m_aMutex); 607 xProducer->startProduction(); 608 } 609 } 610 } 611 612 void OImageControlModel::resetNoBroadcast() 613 { 614 if ( hasField() ) // only reset when we are connected to a column 615 OBoundControlModel::resetNoBroadcast( ); 616 } 617 618 619 Reference< XImageProducer > SAL_CALL OImageControlModel::getImageProducer() 620 { 621 return this; 622 } 623 624 625 void SAL_CALL OImageControlModel::addConsumer( const Reference< XImageConsumer >& _rxConsumer ) 626 { 627 GetImageProducer()->addConsumer( _rxConsumer ); 628 } 629 630 631 void SAL_CALL OImageControlModel::removeConsumer( const Reference< XImageConsumer >& _rxConsumer ) 632 { 633 GetImageProducer()->removeConsumer( _rxConsumer ); 634 } 635 636 637 void SAL_CALL OImageControlModel::startProduction( ) 638 { 639 GetImageProducer()->startProduction(); 640 } 641 642 643 IMPL_LINK( OImageControlModel, OnImageImportDone, ::Graphic*, i_pGraphic, void ) 644 { 645 const Reference< XGraphic > xGraphic(i_pGraphic != nullptr ? i_pGraphic->GetXGraphic() : nullptr); 646 m_bExternalGraphic = false; 647 try 648 { 649 setPropertyValue( PROPERTY_GRAPHIC, makeAny( xGraphic ) ); 650 } 651 catch ( const Exception& ) 652 { 653 DBG_UNHANDLED_EXCEPTION("forms.component"); 654 } 655 m_bExternalGraphic = true; 656 } 657 658 659 // OImageControlControl 660 661 Sequence<Type> OImageControlControl::_getTypes() 662 { 663 return concatSequences( 664 OBoundControl::_getTypes(), 665 OImageControlControl_Base::getTypes() 666 ); 667 } 668 669 670 OImageControlControl::OImageControlControl(const Reference<XComponentContext>& _rxFactory) 671 :OBoundControl(_rxFactory, VCL_CONTROL_IMAGECONTROL) 672 ,m_aModifyListeners( m_aMutex ) 673 { 674 osl_atomic_increment(&m_refCount); 675 { 676 // Add as Focus- and MouseListener 677 Reference< XWindow > xComp; 678 query_aggregation( m_xAggregate, xComp ); 679 if ( xComp.is() ) 680 xComp->addMouseListener( this ); 681 } 682 osl_atomic_decrement(&m_refCount); 683 } 684 685 686 Any SAL_CALL OImageControlControl::queryAggregation(const Type& _rType) 687 { 688 Any aReturn = OBoundControl::queryAggregation( _rType ); 689 if ( !aReturn.hasValue() ) 690 aReturn = ::cppu::queryInterface( 691 _rType, 692 static_cast< XMouseListener* >( this ), 693 static_cast< XModifyBroadcaster* >( this ) 694 ); 695 696 return aReturn; 697 } 698 699 700 css::uno::Sequence<OUString> OImageControlControl::getSupportedServiceNames() 701 { 702 css::uno::Sequence<OUString> aSupported = OBoundControl::getSupportedServiceNames(); 703 aSupported.realloc(aSupported.getLength() + 2); 704 705 OUString*pArray = aSupported.getArray(); 706 pArray[aSupported.getLength()-2] = FRM_SUN_CONTROL_IMAGECONTROL; 707 pArray[aSupported.getLength()-1] = STARDIV_ONE_FORM_CONTROL_IMAGECONTROL; 708 return aSupported; 709 } 710 711 712 void SAL_CALL OImageControlControl::addModifyListener( const Reference< XModifyListener >& Listener ) 713 { 714 m_aModifyListeners.addInterface( Listener ); 715 } 716 717 718 void SAL_CALL OImageControlControl::removeModifyListener( const Reference< XModifyListener >& Listener ) 719 { 720 m_aModifyListeners.removeInterface( Listener ); 721 } 722 723 724 void SAL_CALL OImageControlControl::disposing() 725 { 726 EventObject aEvent( *this ); 727 m_aModifyListeners.disposeAndClear( aEvent ); 728 729 OBoundControl::disposing(); 730 } 731 732 733 void SAL_CALL OImageControlControl::disposing( const EventObject& Event ) 734 { 735 OBoundControl::disposing( Event ); 736 } 737 738 739 void OImageControlControl::implClearGraphics( bool _bForce ) 740 { 741 Reference< XPropertySet > xSet( getModel(), UNO_QUERY ); 742 if ( !xSet.is() ) 743 return; 744 745 if ( _bForce ) 746 { 747 OUString sOldImageURL; 748 xSet->getPropertyValue( PROPERTY_IMAGE_URL ) >>= sOldImageURL; 749 750 if ( sOldImageURL.isEmpty() ) 751 // the ImageURL is already empty, so simply setting a new empty one would not suffice 752 // (since it would be ignored) 753 xSet->setPropertyValue( PROPERTY_IMAGE_URL, makeAny( OUString( "private:emptyImage" ) ) ); 754 // (the concrete URL we're passing here doesn't matter. It's important that 755 // the model cannot resolve it to a valid resource describing an image stream 756 } 757 758 xSet->setPropertyValue( PROPERTY_IMAGE_URL, makeAny( OUString() ) ); 759 } 760 761 762 bool OImageControlControl::implInsertGraphics() 763 { 764 Reference< XPropertySet > xSet( getModel(), UNO_QUERY ); 765 if ( !xSet.is() ) 766 return false; 767 768 OUString sTitle = FRM_RES_STRING(RID_STR_IMPORT_GRAPHIC); 769 // build some arguments for the upcoming dialog 770 try 771 { 772 Reference< XWindow > xWindow( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY ); 773 ::sfx2::FileDialogHelper aDialog(TemplateDescription::FILEOPEN_LINK_PREVIEW, FileDialogFlags::Graphic, 774 Application::GetFrameWeld(xWindow)); 775 aDialog.SetContext(sfx2::FileDialogHelper::FormsInsertImage); 776 aDialog.SetTitle( sTitle ); 777 778 Reference< XFilePickerControlAccess > xController( aDialog.GetFilePicker(), UNO_QUERY_THROW ); 779 xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, css::uno::Any(true)); 780 781 Reference<XPropertySet> xBoundField; 782 if ( hasProperty( PROPERTY_BOUNDFIELD, xSet ) ) 783 xSet->getPropertyValue( PROPERTY_BOUNDFIELD ) >>= xBoundField; 784 bool bHasField = xBoundField.is(); 785 786 // if the control is bound to a DB field, then it's not possible to decide whether or not to link 787 xController->enableControl(ExtendedFilePickerElementIds::CHECKBOX_LINK, !bHasField ); 788 789 // if the control is bound to a DB field, then linking of the image depends on the type of the field 790 bool bImageIsLinked = true; 791 if ( bHasField ) 792 { 793 sal_Int32 nFieldType = DataType::OTHER; 794 OSL_VERIFY( xBoundField->getPropertyValue( PROPERTY_FIELDTYPE ) >>= nFieldType ); 795 bImageIsLinked = ( lcl_getImageStoreType( nFieldType ) == ImageStoreLink ); 796 } 797 xController->setValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, makeAny( bImageIsLinked ) ); 798 799 if ( ERRCODE_NONE == aDialog.Execute() ) 800 { 801 // reset the url property in case it already has the value we're about to set - in this case 802 // our propertyChanged would not get called without this. 803 implClearGraphics( false ); 804 bool bIsLink = false; 805 xController->getValue(ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bIsLink; 806 // Force bIsLink to be sal_True if we're bound to a field. Though we initialized the file picker with IsLink=TRUE 807 // in this case, and disabled the respective control, there might be picker implementations which do not 808 // respect this, and return IsLink=FALSE here. In this case, "normalize" the flag. 809 // #i112659# 810 bIsLink |= bHasField; 811 if ( !bIsLink ) 812 { 813 Graphic aGraphic; 814 aDialog.GetGraphic( aGraphic ); 815 xSet->setPropertyValue( PROPERTY_GRAPHIC, makeAny( aGraphic.GetXGraphic() ) ); 816 } 817 else 818 xSet->setPropertyValue( PROPERTY_IMAGE_URL, makeAny( aDialog.GetPath() ) ); 819 820 return true; 821 } 822 } 823 catch(const Exception&) 824 { 825 TOOLS_WARN_EXCEPTION( "forms.component", "OImageControlControl::implInsertGraphics: caught an exception while attempting to execute the FilePicker!"); 826 } 827 return false; 828 } 829 830 831 bool OImageControlControl::impl_isEmptyGraphics_nothrow() const 832 { 833 bool bIsEmpty = true; 834 835 try 836 { 837 Reference< XPropertySet > xModelProps( const_cast< OImageControlControl* >( this )->getModel(), UNO_QUERY_THROW ); 838 Reference< XGraphic > xGraphic; 839 OSL_VERIFY( xModelProps->getPropertyValue("Graphic") >>= xGraphic ); 840 bIsEmpty = !xGraphic.is(); 841 } 842 catch( const Exception& ) 843 { 844 DBG_UNHANDLED_EXCEPTION("forms.component"); 845 } 846 847 return bIsEmpty; 848 } 849 850 // MouseListener 851 852 void OImageControlControl::mousePressed(const css::awt::MouseEvent& e) 853 { 854 SolarMutexGuard aGuard; 855 856 if (e.Buttons != MouseButton::LEFT) 857 return; 858 859 bool bModified = false; 860 // is this a request for a context menu? 861 if ( e.PopupTrigger ) 862 { 863 Reference< XPopupMenu > xMenu( awt::PopupMenu::create( m_xContext ) ); 864 DBG_ASSERT( xMenu.is(), "OImageControlControl::mousePressed: could not create a popup menu!" ); 865 866 Reference< XWindowPeer > xWindowPeer = getPeer(); 867 DBG_ASSERT( xWindowPeer.is(), "OImageControlControl::mousePressed: no window!" ); 868 869 if ( xMenu.is() && xWindowPeer.is() ) 870 { 871 xMenu->insertItem( ID_OPEN_GRAPHICS, FRM_RES_STRING( RID_STR_OPEN_GRAPHICS ), 0, 0 ); 872 xMenu->insertItem( ID_CLEAR_GRAPHICS, FRM_RES_STRING( RID_STR_CLEAR_GRAPHICS ), 0, 1 ); 873 874 // check if the ImageURL is empty 875 if ( impl_isEmptyGraphics_nothrow() ) 876 xMenu->enableItem( ID_CLEAR_GRAPHICS, false ); 877 878 awt::Rectangle aRect( e.X, e.Y, 0, 0 ); 879 if ( ( e.X < 0 ) || ( e.Y < 0 ) ) 880 { // context menu triggered by keyboard 881 // position it in the center of the control 882 Reference< XWindow > xWindow( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY ); 883 OSL_ENSURE( xWindow.is(), "OImageControlControl::mousePressed: me not a window? How this?" ); 884 if ( xWindow.is() ) 885 { 886 awt::Rectangle aPosSize = xWindow->getPosSize(); 887 aRect.X = aPosSize.Width / 2; 888 aRect.Y = aPosSize.Height / 2; 889 } 890 } 891 892 const sal_Int16 nResult = xMenu->execute( xWindowPeer, aRect, PopupMenuDirection::EXECUTE_DEFAULT ); 893 894 switch ( nResult ) 895 { 896 case ID_OPEN_GRAPHICS: 897 implInsertGraphics(); 898 bModified = true; 899 break; 900 901 case ID_CLEAR_GRAPHICS: 902 implClearGraphics( true ); 903 bModified = true; 904 break; 905 } 906 } 907 } 908 else 909 { 910 911 // Double click 912 if (e.ClickCount == 2) 913 { 914 915 Reference<XPropertySet> xSet(getModel(), UNO_QUERY); 916 if (!xSet.is()) 917 return; 918 919 // If the Control is not bound, do not display a dialog (because the to-be-sent URL would be invalid anyway) 920 Reference<XPropertySet> xBoundField; 921 if (hasProperty(PROPERTY_BOUNDFIELD, xSet)) 922 xBoundField.set( 923 xSet->getPropertyValue(PROPERTY_BOUNDFIELD), 924 css::uno::UNO_QUERY); 925 if (!xBoundField.is()) 926 { 927 // but only if our IMAGE_URL property is handled as if it is transient, which is equivalent to 928 // an empty control source 929 if ( !hasProperty(PROPERTY_CONTROLSOURCE, xSet) || !::comphelper::getString(xSet->getPropertyValue(PROPERTY_CONTROLSOURCE)).isEmpty() ) 930 return; 931 } 932 933 bool bReadOnly = false; 934 xSet->getPropertyValue(PROPERTY_READONLY) >>= bReadOnly; 935 if (bReadOnly) 936 return; 937 938 if ( implInsertGraphics() ) 939 bModified = true; 940 } 941 } 942 943 if ( bModified ) 944 { 945 EventObject aEvent( *this ); 946 m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent ); 947 } 948 } 949 950 951 void SAL_CALL OImageControlControl::mouseReleased(const awt::MouseEvent& /*e*/) 952 { 953 } 954 955 956 void SAL_CALL OImageControlControl::mouseEntered(const awt::MouseEvent& /*e*/) 957 { 958 } 959 960 961 void SAL_CALL OImageControlControl::mouseExited(const awt::MouseEvent& /*e*/) 962 { 963 } 964 965 } // namespace frm 966 967 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* 968 com_sun_star_form_OImageControlModel_get_implementation(css::uno::XComponentContext* component, 969 css::uno::Sequence<css::uno::Any> const &) 970 { 971 return cppu::acquire(new frm::OImageControlModel(component)); 972 } 973 974 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* 975 com_sun_star_form_OImageControlControl_get_implementation(css::uno::XComponentContext* component, 976 css::uno::Sequence<css::uno::Any> const &) 977 { 978 return cppu::acquire(new frm::OImageControlControl(component)); 979 } 980 981 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 982
