1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "ImageControl.hxx"
22 #include <strings.hrc>
23 #include <frm_resource.hxx>
24 #include <property.hxx>
25 #include <services.hxx>
26 #include <componenttools.hxx>
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/beans/PropertyAttribute.hpp>
34 #include <com/sun/star/form/FormComponentType.hpp>
35 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
36 #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
37 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
38 #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
39 #include <com/sun/star/sdbc/DataType.hpp>
40 #include <com/sun/star/awt/MouseButton.hpp>
41 #include <com/sun/star/awt/XWindow.hpp>
42 #include <com/sun/star/graphic/XGraphic.hpp>
43 #include <com/sun/star/graphic/GraphicObject.hpp>
44 #include <tools/urlobj.hxx>
45 #include <tools/stream.hxx>
46 #include <tools/debug.hxx>
47 #include <comphelper/diagnose_ex.hxx>
48 #include <vcl/graph.hxx>
49 #include <vcl/svapp.hxx>
50 #include <comphelper/guarding.hxx>
51 #include <comphelper/property.hxx>
52 #include <comphelper/types.hxx>
53 #include <cppuhelper/queryinterface.hxx>
54 #include <unotools/securityoptions.hxx>
55 #include <unotools/streamwrap.hxx>
56 #include <unotools/ucbstreamhelper.hxx>
57 #include <svl/urihelper.hxx>
61 #define ID_OPEN_GRAPHICS 1
62 #define ID_CLEAR_GRAPHICS 2
67 using namespace ::com::sun::star
;
68 using namespace ::com::sun::star::uno
;
69 using namespace ::com::sun::star::sdb
;
70 using namespace ::com::sun::star::sdbc
;
71 using namespace ::com::sun::star::beans
;
72 using namespace ::com::sun::star::container
;
73 using namespace ::com::sun::star::form
;
74 using namespace ::com::sun::star::awt
;
75 using namespace ::com::sun::star::io
;
76 using namespace ::com::sun::star::ui::dialogs
;
77 using namespace ::com::sun::star::lang
;
78 using namespace ::com::sun::star::util
;
79 using namespace ::com::sun::star::graphic
;
80 using namespace ::com::sun::star::frame
;
83 //= OImageControlModel
95 ImageStoreType
lcl_getImageStoreType( const sal_Int32 _nFieldType
)
97 // binary/longvarchar types could be used to store images in binary representation
98 if ( ( _nFieldType
== DataType::BINARY
)
99 || ( _nFieldType
== DataType::VARBINARY
)
100 || ( _nFieldType
== DataType::LONGVARBINARY
)
101 || ( _nFieldType
== DataType::OTHER
)
102 || ( _nFieldType
== DataType::OBJECT
)
103 || ( _nFieldType
== DataType::BLOB
)
104 || ( _nFieldType
== DataType::LONGVARCHAR
)
105 || ( _nFieldType
== DataType::CLOB
)
107 return ImageStoreBinary
;
109 // char types could be used to store links to images
110 if ( ( _nFieldType
== DataType::CHAR
)
111 || ( _nFieldType
== DataType::VARCHAR
)
113 return ImageStoreLink
;
115 return ImageStoreInvalid
;
120 // OImageControlModel
122 Sequence
<Type
> OImageControlModel::_getTypes()
124 return concatSequences(
125 OBoundControlModel::_getTypes(),
126 OImageControlModel_Base::getTypes()
131 OImageControlModel::OImageControlModel(const Reference
<XComponentContext
>& _rxFactory
)
132 :OBoundControlModel( _rxFactory
, VCL_CONTROLMODEL_IMAGECONTROL
, FRM_SUN_CONTROL_IMAGECONTROL
, false, false, false )
133 // use the old control name for compatibility reasons
134 ,m_bExternalGraphic( true )
135 ,m_bReadOnly( false )
137 m_nClassId
= FormComponentType::IMAGECONTROL
;
138 initOwnValueProperty( PROPERTY_IMAGE_URL
);
144 OImageControlModel::OImageControlModel( const OImageControlModel
* _pOriginal
, const Reference
< XComponentContext
>& _rxFactory
)
145 :OBoundControlModel( _pOriginal
, _rxFactory
)
146 // use the old control name for compatibility reasons
147 ,m_bExternalGraphic( true )
148 ,m_bReadOnly( _pOriginal
->m_bReadOnly
)
149 ,m_sImageURL( _pOriginal
->m_sImageURL
)
150 ,m_xGraphicObject( _pOriginal
->m_xGraphicObject
)
154 osl_atomic_increment( &m_refCount
);
156 // simulate a propertyChanged event for the ImageURL
157 ::osl::MutexGuard
aGuard( m_aMutex
);
158 impl_handleNewImageURL_lck( eOther
);
160 osl_atomic_decrement( &m_refCount
);
164 void OImageControlModel::implConstruct()
166 m_xImageProducer
= new ImageProducer
;
167 m_xImageProducer
->SetDoneHdl( LINK( this, OImageControlModel
, OnImageImportDone
) );
171 OImageControlModel::~OImageControlModel()
173 if (!OComponentHelper::rBHelper
.bDisposed
)
183 css::uno::Reference
< css::util::XCloneable
> SAL_CALL
OImageControlModel::createClone()
185 rtl::Reference
<OImageControlModel
> pClone
= new OImageControlModel(this, getContext());
186 pClone
->clonedFrom(this);
192 css::uno::Sequence
<OUString
> OImageControlModel::getSupportedServiceNames()
194 css::uno::Sequence
<OUString
> aSupported
= OBoundControlModel::getSupportedServiceNames();
195 aSupported
.realloc(aSupported
.getLength() + 2);
197 OUString
*pArray
= aSupported
.getArray();
198 pArray
[aSupported
.getLength()-2] = FRM_SUN_COMPONENT_IMAGECONTROL
;
199 pArray
[aSupported
.getLength()-1] = FRM_COMPONENT_IMAGECONTROL
;
204 Any SAL_CALL
OImageControlModel::queryAggregation(const Type
& _rType
)
206 // Order matters: we want to "override" the XImageProducer interface of the aggregate without
207 // own XImageProducer interface, thus we need to query OImageControlModel_Base first
208 Any aReturn
= OImageControlModel_Base::queryInterface( _rType
);
210 // BUT: _don't_ let it feel responsible for the XTypeProvider interface
211 // (as this is implemented by our base class in the proper way)
212 if ( _rType
.equals( cppu::UnoType
<XTypeProvider
>::get() )
213 || !aReturn
.hasValue()
215 aReturn
= OBoundControlModel::queryAggregation( _rType
);
221 bool OImageControlModel::approveDbColumnType( sal_Int32 _nColumnType
)
223 return ImageStoreInvalid
!= lcl_getImageStoreType( _nColumnType
);
227 void OImageControlModel::getFastPropertyValue(Any
& rValue
, sal_Int32 nHandle
) const
231 case PROPERTY_ID_READONLY
:
232 rValue
<<= m_bReadOnly
;
234 case PROPERTY_ID_IMAGE_URL
:
235 rValue
<<= m_sImageURL
;
237 case PROPERTY_ID_GRAPHIC
:
238 rValue
<<= m_xGraphicObject
.is() ? m_xGraphicObject
->getGraphic() : Reference
< XGraphic
>();
241 OBoundControlModel::getFastPropertyValue(rValue
, nHandle
);
246 void OImageControlModel::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle
, const Any
& rValue
)
250 case PROPERTY_ID_READONLY
:
251 DBG_ASSERT(rValue
.getValueTypeClass() == TypeClass_BOOLEAN
, "OImageControlModel::setFastPropertyValue_NoBroadcast : invalid type !" );
252 m_bReadOnly
= getBOOL(rValue
);
255 case PROPERTY_ID_IMAGE_URL
:
256 OSL_VERIFY( rValue
>>= m_sImageURL
);
257 impl_handleNewImageURL_lck( eOther
);
259 ControlModelLock
aLock( *this );
260 // that's a fake ... onValuePropertyChange expects to receive the only lock to our instance,
261 // but we're already called with our mutex locked ...
262 onValuePropertyChange( aLock
);
266 case PROPERTY_ID_GRAPHIC
:
268 Reference
< XGraphic
> xGraphic
;
269 OSL_VERIFY( rValue
>>= xGraphic
);
270 if ( !xGraphic
.is() )
271 m_xGraphicObject
.clear();
274 m_xGraphicObject
= graphic::GraphicObject::create( m_xContext
);
275 m_xGraphicObject
->setGraphic( xGraphic
);
278 if ( m_bExternalGraphic
)
280 m_sImageURL
= OUString();
281 // TODO: speaking strictly, this would need to be notified, since ImageURL is a bound property. However,
282 // this method here is called with a locked mutex, so we cannot simply call listeners ...
283 // I think the missing notification (and thus clients which potentially cannot observe the change)
284 // is less severe than the potential deadlock ...
290 OBoundControlModel::setFastPropertyValue_NoBroadcast(nHandle
, rValue
);
296 sal_Bool
OImageControlModel::convertFastPropertyValue(Any
& rConvertedValue
, Any
& rOldValue
, sal_Int32 nHandle
, const Any
& rValue
)
300 case PROPERTY_ID_READONLY
:
301 return tryPropertyValue(rConvertedValue
, rOldValue
, rValue
, m_bReadOnly
);
303 case PROPERTY_ID_IMAGE_URL
:
304 return tryPropertyValue( rConvertedValue
, rOldValue
, rValue
, m_sImageURL
);
306 case PROPERTY_ID_GRAPHIC
:
308 const Reference
< XGraphic
> xGraphic( getFastPropertyValue( PROPERTY_ID_GRAPHIC
), UNO_QUERY
);
309 return tryPropertyValue( rConvertedValue
, rOldValue
, rValue
, xGraphic
);
313 return OBoundControlModel::convertFastPropertyValue(rConvertedValue
, rOldValue
, nHandle
, rValue
);
318 void OImageControlModel::describeFixedProperties( Sequence
< Property
>& _rProps
) const
320 OBoundControlModel::describeFixedProperties( _rProps
);
321 sal_Int32 nOldCount
= _rProps
.getLength();
322 _rProps
.realloc( nOldCount
+ 4);
323 css::beans::Property
* pProperties
= _rProps
.getArray() + nOldCount
;
324 *pProperties
++ = css::beans::Property(PROPERTY_GRAPHIC
, PROPERTY_ID_GRAPHIC
, cppu::UnoType
<XGraphic
>::get(),
325 css::beans::PropertyAttribute::BOUND
| css::beans::PropertyAttribute::TRANSIENT
);
326 *pProperties
++ = css::beans::Property(PROPERTY_IMAGE_URL
, PROPERTY_ID_IMAGE_URL
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
);
327 *pProperties
++ = css::beans::Property(PROPERTY_READONLY
, PROPERTY_ID_READONLY
, cppu::UnoType
<bool>::get(),
328 css::beans::PropertyAttribute::BOUND
);
329 *pProperties
++ = css::beans::Property(PROPERTY_TABINDEX
, PROPERTY_ID_TABINDEX
, cppu::UnoType
<sal_Int16
>::get(), css::beans::PropertyAttribute::BOUND
);
330 DBG_ASSERT( pProperties
== _rProps
.getArray() + _rProps
.getLength(), "<...>::describeFixedProperties/getInfoHelper: forgot to adjust the count ?");
334 void OImageControlModel::describeAggregateProperties( Sequence
< Property
>& /* [out] */ o_rAggregateProperties
) const
336 OBoundControlModel::describeAggregateProperties( o_rAggregateProperties
);
337 // remove ImageURL and Graphic properties, we "override" them.
338 // This is because our aggregate synchronizes those
339 // two, but we have an own synchronization mechanism.
340 RemoveProperty( o_rAggregateProperties
, PROPERTY_IMAGE_URL
);
341 RemoveProperty( o_rAggregateProperties
, PROPERTY_GRAPHIC
);
345 OUString
OImageControlModel::getServiceName()
347 return FRM_COMPONENT_IMAGECONTROL
; // old (non-sun) name for compatibility !
351 void OImageControlModel::write(const Reference
<XObjectOutputStream
>& _rxOutStream
)
354 OBoundControlModel::write(_rxOutStream
);
356 _rxOutStream
->writeShort(0x0003);
358 _rxOutStream
->writeBoolean(m_bReadOnly
);
359 writeHelpTextCompatibly(_rxOutStream
);
360 // from version 0x0003 : common properties
361 writeCommonProperties(_rxOutStream
);
365 void OImageControlModel::read(const Reference
<XObjectInputStream
>& _rxInStream
)
367 OBoundControlModel::read(_rxInStream
);
370 sal_uInt16 nVersion
= _rxInStream
->readShort();
374 m_bReadOnly
= _rxInStream
->readBoolean();
377 m_bReadOnly
= _rxInStream
->readBoolean();
378 readHelpTextCompatibly(_rxInStream
);
381 m_bReadOnly
= _rxInStream
->readBoolean();
382 readHelpTextCompatibly(_rxInStream
);
383 readCommonProperties(_rxInStream
);
386 OSL_FAIL("OImageControlModel::read : unknown version !");
388 defaultCommonProperties();
391 // Display default values after read
392 if ( !getControlSource().isEmpty() )
393 { // (not if we don't have a control source - the "State" property acts like it is persistent, then
394 ::osl::MutexGuard
aGuard(m_aMutex
); // resetNoBroadcast expects this mutex guarding
400 bool OImageControlModel::impl_updateStreamForURL_lck( const OUString
& _rURL
, ValueChangeInstigator _eInstigator
)
403 getPropertyValue(u
"Referer"_ustr
) >>= referer
;
404 if (SvtSecurityOptions::isUntrustedReferer(referer
) || INetURLObject(_rURL
).IsExoticProtocol()) {
408 // create a stream for the image specified by the URL
409 Reference
< XInputStream
> xImageStream
;
411 if ( ::svt::GraphicAccess::isSupportedURL( _rURL
) )
413 xImageStream
= ::svt::GraphicAccess::getImageXStream( getContext(), _rURL
);
417 std::unique_ptr
< SvStream
> pImageStream
= ::utl::UcbStreamHelper::CreateStream( _rURL
, StreamMode::READ
);
418 bool bSetNull
= (pImageStream
== nullptr) || (ERRCODE_NONE
!= pImageStream
->GetErrorCode());
421 xImageStream
= new ::utl::OInputStreamWrapper( std::move(pImageStream
) );
424 if ( xImageStream
.is() )
426 if ( m_xColumnUpdate
.is() )
427 m_xColumnUpdate
->updateBinaryStream( xImageStream
, xImageStream
->available() );
429 setControlValue( Any( xImageStream
), _eInstigator
);
430 xImageStream
->closeInput();
438 void OImageControlModel::impl_handleNewImageURL_lck( ValueChangeInstigator _eInstigator
)
440 switch ( lcl_getImageStoreType( getFieldType() ) )
442 case ImageStoreBinary
:
443 if ( impl_updateStreamForURL_lck( m_sImageURL
, _eInstigator
) )
449 OUString
sCommitURL( m_sImageURL
);
450 if ( !m_sDocumentURL
.isEmpty() )
451 sCommitURL
= URIHelper::simpleNormalizedMakeRelative( m_sDocumentURL
, sCommitURL
);
452 OSL_ENSURE( m_xColumnUpdate
.is(), "OImageControlModel::impl_handleNewImageURL_lck: no bound field, but ImageStoreLink?!" );
453 if ( m_xColumnUpdate
.is() )
455 m_xColumnUpdate
->updateString( sCommitURL
);
461 case ImageStoreInvalid
:
462 OSL_FAIL( "OImageControlModel::impl_handleNewImageURL_lck: image storage type type!" );
466 // if we're here, then the above code was unable to update our field/control from the given URL
467 // => fall back to NULL/VOID
468 if ( m_xColumnUpdate
.is() )
469 m_xColumnUpdate
->updateNull();
471 setControlValue( Any(), _eInstigator
);
475 bool OImageControlModel::commitControlValueToDbColumn( bool _bPostReset
)
479 // since this is a "commit after reset", we can simply update the column
480 // with null - this is our "default" which we were just reset to
481 if ( m_xColumnUpdate
.is() )
482 m_xColumnUpdate
->updateNull();
486 ::osl::MutexGuard
aGuard(m_aMutex
);
487 impl_handleNewImageURL_lck( eDbColumnBinding
);
496 bool lcl_isValidDocumentURL( std::u16string_view _rDocURL
)
498 return ( !_rDocURL
.empty() && _rDocURL
!= u
"private:object" );
503 void OImageControlModel::onConnectedDbColumn( const Reference
< XInterface
>& _rxForm
)
505 OBoundControlModel::onConnectedDbColumn( _rxForm
);
509 Reference
< XModel
> xDocument( getXModel( *this ) );
510 if ( xDocument
.is() )
512 m_sDocumentURL
= xDocument
->getURL();
513 if ( !lcl_isValidDocumentURL( m_sDocumentURL
) )
515 Reference
< XChild
> xAsChild( xDocument
, UNO_QUERY
);
516 while ( xAsChild
.is() && !lcl_isValidDocumentURL( m_sDocumentURL
) )
518 xDocument
.set( xAsChild
->getParent(), UNO_QUERY
);
519 if ( xDocument
.is() )
520 m_sDocumentURL
= xDocument
->getURL();
521 xAsChild
.set( xDocument
, UNO_QUERY
);
526 catch( const Exception
& )
528 DBG_UNHANDLED_EXCEPTION("forms.component");
533 void OImageControlModel::onDisconnectedDbColumn()
535 OBoundControlModel::onDisconnectedDbColumn();
537 m_sDocumentURL
.clear();
541 Any
OImageControlModel::translateDbColumnToControlValue()
543 switch ( lcl_getImageStoreType( getFieldType() ) )
545 case ImageStoreBinary
:
547 Reference
< XInputStream
> xImageStream( m_xColumn
->getBinaryStream() );
548 if ( m_xColumn
->wasNull() )
549 xImageStream
.clear();
550 return Any( xImageStream
);
554 OUString
sImageLink( m_xColumn
->getString() );
555 if ( !m_sDocumentURL
.isEmpty() )
556 sImageLink
= INetURLObject::GetAbsURL( m_sDocumentURL
, sImageLink
);
557 return Any( sImageLink
);
559 case ImageStoreInvalid
:
560 OSL_FAIL( "OImageControlModel::translateDbColumnToControlValue: invalid field type!" );
567 Any
OImageControlModel::getControlValue( ) const
569 return Any( m_sImageURL
);
573 void OImageControlModel::doSetControlValue( const Any
& _rValue
)
575 DBG_ASSERT( GetImageProducer() && m_xImageProducer
.is(), "OImageControlModel::doSetControlValue: no image producer!" );
576 if ( !GetImageProducer() || !m_xImageProducer
.is() )
579 bool bStartProduction
= false;
580 switch ( lcl_getImageStoreType( getFieldType() ) )
582 case ImageStoreBinary
:
584 // give the image producer the stream
585 Reference
< XInputStream
> xInStream
;
586 _rValue
>>= xInStream
;
587 GetImageProducer()->setImage( xInStream
);
588 bStartProduction
= true;
595 _rValue
>>= sImageURL
;
596 GetImageProducer()->SetImage( sImageURL
);
597 bStartProduction
= true;
601 case ImageStoreInvalid
:
602 OSL_FAIL( "OImageControlModel::doSetControlValue: invalid field type!" );
605 } // switch ( lcl_getImageStoreType( getFieldType() ) )
607 if ( bStartProduction
)
610 rtl::Reference
< ImageProducer
> xProducer
= m_xImageProducer
;
612 // release our mutex once (it's acquired in the calling method!), as starting the image production may
613 // result in the locking of the solar mutex (unfortunately the default implementation of our aggregate,
614 // VCLXImageControl, does this locking)
615 MutexRelease
aRelease(m_aMutex
);
616 xProducer
->startProduction();
621 void OImageControlModel::resetNoBroadcast()
623 if ( hasField() ) // only reset when we are connected to a column
624 OBoundControlModel::resetNoBroadcast( );
628 Reference
< XImageProducer
> SAL_CALL
OImageControlModel::getImageProducer()
634 void SAL_CALL
OImageControlModel::addConsumer( const Reference
< XImageConsumer
>& _rxConsumer
)
636 GetImageProducer()->addConsumer( _rxConsumer
);
640 void SAL_CALL
OImageControlModel::removeConsumer( const Reference
< XImageConsumer
>& _rxConsumer
)
642 GetImageProducer()->removeConsumer( _rxConsumer
);
646 void SAL_CALL
OImageControlModel::startProduction( )
648 GetImageProducer()->startProduction();
652 IMPL_LINK( OImageControlModel
, OnImageImportDone
, ::Graphic
*, i_pGraphic
, void )
654 const Reference
< XGraphic
> xGraphic(i_pGraphic
!= nullptr ? i_pGraphic
->GetXGraphic() : nullptr);
655 m_bExternalGraphic
= false;
658 setPropertyValue( PROPERTY_GRAPHIC
, Any( xGraphic
) );
660 catch ( const Exception
& )
662 DBG_UNHANDLED_EXCEPTION("forms.component");
664 m_bExternalGraphic
= true;
668 // OImageControlControl
670 Sequence
<Type
> OImageControlControl::_getTypes()
672 return concatSequences(
673 OBoundControl::_getTypes(),
674 OImageControlControl_Base::getTypes()
679 OImageControlControl::OImageControlControl(const Reference
<XComponentContext
>& _rxFactory
)
680 :OBoundControl(_rxFactory
, VCL_CONTROL_IMAGECONTROL
)
681 ,m_aModifyListeners( m_aMutex
)
683 osl_atomic_increment(&m_refCount
);
685 // Add as Focus- and MouseListener
686 if (auto xComp
= query_aggregation
<XWindow
>(m_xAggregate
))
687 xComp
->addMouseListener( this );
689 osl_atomic_decrement(&m_refCount
);
693 Any SAL_CALL
OImageControlControl::queryAggregation(const Type
& _rType
)
695 Any aReturn
= OBoundControl::queryAggregation( _rType
);
696 if ( !aReturn
.hasValue() )
697 aReturn
= ::cppu::queryInterface(
699 static_cast< XMouseListener
* >( this ),
700 static_cast< XModifyBroadcaster
* >( this )
707 css::uno::Sequence
<OUString
> OImageControlControl::getSupportedServiceNames()
709 css::uno::Sequence
<OUString
> aSupported
= OBoundControl::getSupportedServiceNames();
710 aSupported
.realloc(aSupported
.getLength() + 2);
712 OUString
*pArray
= aSupported
.getArray();
713 pArray
[aSupported
.getLength()-2] = FRM_SUN_CONTROL_IMAGECONTROL
;
714 pArray
[aSupported
.getLength()-1] = STARDIV_ONE_FORM_CONTROL_IMAGECONTROL
;
719 void SAL_CALL
OImageControlControl::addModifyListener( const Reference
< XModifyListener
>& Listener
)
721 m_aModifyListeners
.addInterface( Listener
);
725 void SAL_CALL
OImageControlControl::removeModifyListener( const Reference
< XModifyListener
>& Listener
)
727 m_aModifyListeners
.removeInterface( Listener
);
731 void SAL_CALL
OImageControlControl::disposing()
733 EventObject
aEvent( *this );
734 m_aModifyListeners
.disposeAndClear( aEvent
);
736 OBoundControl::disposing();
740 void SAL_CALL
OImageControlControl::disposing( const EventObject
& Event
)
742 OBoundControl::disposing( Event
);
746 void OImageControlControl::implClearGraphics( bool _bForce
)
748 Reference
< XPropertySet
> xSet( getModel(), UNO_QUERY
);
754 OUString sOldImageURL
;
755 xSet
->getPropertyValue( PROPERTY_IMAGE_URL
) >>= sOldImageURL
;
757 if ( sOldImageURL
.isEmpty() )
758 // the ImageURL is already empty, so simply setting a new empty one would not suffice
759 // (since it would be ignored)
760 xSet
->setPropertyValue( PROPERTY_IMAGE_URL
, Any( u
"private:emptyImage"_ustr
) );
761 // (the concrete URL we're passing here doesn't matter. It's important that
762 // the model cannot resolve it to a valid resource describing an image stream
765 xSet
->setPropertyValue( PROPERTY_IMAGE_URL
, Any( OUString() ) );
769 bool OImageControlControl::implInsertGraphics()
771 Reference
< XPropertySet
> xSet( getModel(), UNO_QUERY
);
775 OUString sTitle
= ResourceManager::loadString(RID_STR_IMPORT_GRAPHIC
);
776 // build some arguments for the upcoming dialog
779 Reference
< XWindow
> xWindow( static_cast< ::cppu::OWeakObject
* >( this ), UNO_QUERY
);
780 ::sfx2::FileDialogHelper
aDialog(TemplateDescription::FILEOPEN_LINK_PREVIEW
, FileDialogFlags::Graphic
,
781 Application::GetFrameWeld(xWindow
));
782 aDialog
.SetContext(sfx2::FileDialogHelper::FormsInsertImage
);
783 aDialog
.SetTitle( sTitle
);
785 Reference
< XFilePickerControlAccess
> xController( aDialog
.GetFilePicker(), UNO_QUERY_THROW
);
786 xController
->setValue(ExtendedFilePickerElementIds::CHECKBOX_PREVIEW
, 0, css::uno::Any(true));
788 Reference
<XPropertySet
> xBoundField
;
789 if ( hasProperty( PROPERTY_BOUNDFIELD
, xSet
) )
790 xSet
->getPropertyValue( PROPERTY_BOUNDFIELD
) >>= xBoundField
;
791 bool bHasField
= xBoundField
.is();
793 // if the control is bound to a DB field, then it's not possible to decide whether or not to link
794 xController
->enableControl(ExtendedFilePickerElementIds::CHECKBOX_LINK
, !bHasField
);
796 // if the control is bound to a DB field, then linking of the image depends on the type of the field
797 bool bImageIsLinked
= true;
800 sal_Int32 nFieldType
= DataType::OTHER
;
801 OSL_VERIFY( xBoundField
->getPropertyValue( PROPERTY_FIELDTYPE
) >>= nFieldType
);
802 bImageIsLinked
= ( lcl_getImageStoreType( nFieldType
) == ImageStoreLink
);
804 xController
->setValue(ExtendedFilePickerElementIds::CHECKBOX_LINK
, 0, Any( bImageIsLinked
) );
806 if ( ERRCODE_NONE
== aDialog
.Execute() )
808 // reset the url property in case it already has the value we're about to set - in this case
809 // our propertyChanged would not get called without this.
810 implClearGraphics( false );
811 bool bIsLink
= false;
812 xController
->getValue(ExtendedFilePickerElementIds::CHECKBOX_LINK
, 0) >>= bIsLink
;
813 // Force bIsLink to be sal_True if we're bound to a field. Though we initialized the file picker with IsLink=TRUE
814 // in this case, and disabled the respective control, there might be picker implementations which do not
815 // respect this, and return IsLink=FALSE here. In this case, "normalize" the flag.
817 bIsLink
|= bHasField
;
821 aDialog
.GetGraphic( aGraphic
);
822 xSet
->setPropertyValue( PROPERTY_GRAPHIC
, Any( aGraphic
.GetXGraphic() ) );
825 xSet
->setPropertyValue( PROPERTY_IMAGE_URL
, Any( aDialog
.GetPath() ) );
830 catch(const Exception
&)
832 TOOLS_WARN_EXCEPTION( "forms.component", "OImageControlControl::implInsertGraphics: caught an exception while attempting to execute the FilePicker!");
838 bool OImageControlControl::impl_isEmptyGraphics_nothrow() const
840 bool bIsEmpty
= true;
844 Reference
< XPropertySet
> xModelProps( const_cast< OImageControlControl
* >( this )->getModel(), UNO_QUERY_THROW
);
845 Reference
< XGraphic
> xGraphic
;
846 OSL_VERIFY( xModelProps
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
);
847 bIsEmpty
= !xGraphic
.is();
849 catch( const Exception
& )
851 DBG_UNHANDLED_EXCEPTION("forms.component");
859 void OImageControlControl::mousePressed(const css::awt::MouseEvent
& e
)
861 SolarMutexGuard aGuard
;
863 if (e
.Buttons
!= MouseButton::LEFT
)
866 bool bModified
= false;
867 // is this a request for a context menu?
868 if ( e
.PopupTrigger
)
870 Reference
< XPopupMenu
> xMenu( awt::PopupMenu::create( m_xContext
) );
871 DBG_ASSERT( xMenu
.is(), "OImageControlControl::mousePressed: could not create a popup menu!" );
873 Reference
< XWindowPeer
> xWindowPeer
= getPeer();
874 DBG_ASSERT( xWindowPeer
.is(), "OImageControlControl::mousePressed: no window!" );
876 if ( xMenu
.is() && xWindowPeer
.is() )
878 xMenu
->insertItem( ID_OPEN_GRAPHICS
, ResourceManager::loadString(RID_STR_OPEN_GRAPHICS
), 0, 0 );
879 xMenu
->insertItem( ID_CLEAR_GRAPHICS
, ResourceManager::loadString(RID_STR_CLEAR_GRAPHICS
), 0, 1 );
881 // check if the ImageURL is empty
882 if ( impl_isEmptyGraphics_nothrow() )
883 xMenu
->enableItem( ID_CLEAR_GRAPHICS
, false );
885 awt::Rectangle
aRect( e
.X
, e
.Y
, 0, 0 );
886 if ( ( e
.X
< 0 ) || ( e
.Y
< 0 ) )
887 { // context menu triggered by keyboard
888 // position it in the center of the control
889 Reference
< XWindow
> xWindow( static_cast< ::cppu::OWeakObject
* >( this ), UNO_QUERY
);
890 OSL_ENSURE( xWindow
.is(), "OImageControlControl::mousePressed: me not a window? How this?" );
893 awt::Rectangle aPosSize
= xWindow
->getPosSize();
894 aRect
.X
= aPosSize
.Width
/ 2;
895 aRect
.Y
= aPosSize
.Height
/ 2;
899 const sal_Int16 nResult
= xMenu
->execute( xWindowPeer
, aRect
, PopupMenuDirection::EXECUTE_DEFAULT
);
903 case ID_OPEN_GRAPHICS
:
904 implInsertGraphics();
908 case ID_CLEAR_GRAPHICS
:
909 implClearGraphics( true );
919 if (e
.ClickCount
== 2)
922 Reference
<XPropertySet
> xSet(getModel(), UNO_QUERY
);
926 // If the Control is not bound, do not display a dialog (because the to-be-sent URL would be invalid anyway)
927 Reference
<XPropertySet
> xBoundField
;
928 if (hasProperty(PROPERTY_BOUNDFIELD
, xSet
))
930 xSet
->getPropertyValue(PROPERTY_BOUNDFIELD
),
931 css::uno::UNO_QUERY
);
932 if (!xBoundField
.is())
934 // but only if our IMAGE_URL property is handled as if it is transient, which is equivalent to
935 // an empty control source
936 if ( !hasProperty(PROPERTY_CONTROLSOURCE
, xSet
) || !::comphelper::getString(xSet
->getPropertyValue(PROPERTY_CONTROLSOURCE
)).isEmpty() )
940 bool bReadOnly
= false;
941 xSet
->getPropertyValue(PROPERTY_READONLY
) >>= bReadOnly
;
945 if ( implInsertGraphics() )
952 EventObject
aEvent( *this );
953 m_aModifyListeners
.notifyEach( &XModifyListener::modified
, aEvent
);
958 void SAL_CALL
OImageControlControl::mouseReleased(const awt::MouseEvent
& /*e*/)
963 void SAL_CALL
OImageControlControl::mouseEntered(const awt::MouseEvent
& /*e*/)
968 void SAL_CALL
OImageControlControl::mouseExited(const awt::MouseEvent
& /*e*/)
974 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
975 com_sun_star_form_OImageControlModel_get_implementation(css::uno::XComponentContext
* component
,
976 css::uno::Sequence
<css::uno::Any
> const &)
978 return cppu::acquire(new frm::OImageControlModel(component
));
981 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
982 com_sun_star_form_OImageControlControl_get_implementation(css::uno::XComponentContext
* component
,
983 css::uno::Sequence
<css::uno::Any
> const &)
985 return cppu::acquire(new frm::OImageControlControl(component
));
988 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */