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