1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_dbaccess.hxx"
26 
27 #include "browserids.hxx"
28 #include "commontypes.hxx"
29 #include "dataview.hxx"
30 #include "dbu_misc.hrc"
31 #include "dbustrings.hrc"
32 #include "moduledbu.hxx"
33 #include "dbsubcomponentcontroller.hxx"
34 
35 /** === begin UNO includes === **/
36 #include <com/sun/star/frame/XUntitledNumbers.hpp>
37 #include <com/sun/star/beans/PropertyAttribute.hpp>
38 #include <com/sun/star/container/XChild.hpp>
39 #include <com/sun/star/container/XNameAccess.hpp>
40 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
41 #include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp>
42 #include <com/sun/star/sdbc/XDataSource.hpp>
43 #include <com/sun/star/lang/IllegalArgumentException.hpp>
44 #include <com/sun/star/frame/XUntitledNumbers.hpp>
45 /** === end UNO includes === **/
46 
47 #include <comphelper/sequence.hxx>
48 #include <comphelper/types.hxx>
49 #include <connectivity/dbexception.hxx>
50 #include <connectivity/dbtools.hxx>
51 #include <cppuhelper/typeprovider.hxx>
52 #include <rtl/ustrbuf.hxx>
53 #include <toolkit/unohlp.hxx>
54 #include <tools/debug.hxx>
55 #include <tools/diagnose_ex.h>
56 #include <vcl/msgbox.hxx>
57 
58 //........................................................................
59 namespace dbaui
60 {
61 //........................................................................
62 
63     /** === begin UNO using === **/
64     using ::com::sun::star::uno::Any;
65     using ::com::sun::star::uno::Reference;
66     using ::com::sun::star::beans::XPropertySet;
67     using ::com::sun::star::util::XNumberFormatter;
68     using ::com::sun::star::lang::XMultiServiceFactory;
69     using ::com::sun::star::uno::RuntimeException;
70     using ::com::sun::star::uno::Sequence;
71     using ::com::sun::star::uno::Type;
72     using ::com::sun::star::sdbc::XConnection;
73     using ::com::sun::star::uno::UNO_QUERY;
74     using ::com::sun::star::container::XChild;
75     using ::com::sun::star::sdbc::XDataSource;
76     using ::com::sun::star::util::XNumberFormatter;
77     using ::com::sun::star::util::XNumberFormatsSupplier;
78     using ::com::sun::star::frame::XFrame;
79     using ::com::sun::star::uno::Exception;
80     using ::com::sun::star::sdbc::SQLException;
81     using ::com::sun::star::lang::EventObject;
82     using ::com::sun::star::beans::PropertyValue;
83     using ::com::sun::star::frame::XModel;
84     using ::com::sun::star::sdb::XOfficeDatabaseDocument;
85     using ::com::sun::star::awt::XWindow;
86     using ::com::sun::star::sdbc::XDatabaseMetaData;
87     using ::com::sun::star::sdb::XDocumentDataSource;
88     using ::com::sun::star::document::XEmbeddedScripts;
89     using ::com::sun::star::lang::IllegalArgumentException;
90     using ::com::sun::star::uno::UNO_SET_THROW;
91     using ::com::sun::star::uno::UNO_QUERY_THROW;
92     using ::com::sun::star::frame::XUntitledNumbers;
93     using ::com::sun::star::beans::PropertyVetoException;
94     /** === end UNO using === **/
95 
96     class DataSourceHolder
97     {
98     public:
99         DataSourceHolder()
100         {
101         }
102 
103         DataSourceHolder( const Reference< XDataSource >& _rxDataSource )
104         {
105             m_xDataSource = _rxDataSource;
106             Reference< XDocumentDataSource > xDocDS( m_xDataSource, UNO_QUERY );
107             if ( xDocDS.is() )
108                 m_xDocument = xDocDS->getDatabaseDocument();
109 
110             m_xDataSourceProps.set( m_xDataSource, UNO_QUERY );
111         }
112 
113         const Reference< XDataSource >&             getDataSource() const { return m_xDataSource; }
114         const Reference< XPropertySet >&            getDataSourceProps() const { return m_xDataSourceProps; }
115         const Reference< XOfficeDatabaseDocument >  getDatabaseDocument() const { return m_xDocument; }
116 
117         bool is() const { return m_xDataSource.is(); }
118 
119         void clear()
120         {
121             m_xDataSource.clear();
122             m_xDocument.clear();
123         }
124 
125     private:
126         Reference< XDataSource >                m_xDataSource;
127         Reference< XPropertySet >               m_xDataSourceProps;
128         Reference< XOfficeDatabaseDocument >    m_xDocument;
129     };
130 
131     struct DBSubComponentController_Impl
132     {
133     private:
134         ::boost::optional< bool >       m_aDocScriptSupport;
135 
136     public:
137         OModuleClient                   m_aModuleClient;
138         ::dbtools::SQLExceptionInfo     m_aCurrentError;
139 
140         ::cppu::OInterfaceContainerHelper
141                                         m_aModifyListeners;
142 
143 		// <properties>
144         SharedConnection                m_xConnection;
145         ::dbtools::DatabaseMetaData     m_aSdbMetaData;
146 		// </properties>
147 		::rtl::OUString	                m_sDataSourceName;		// the data source we're working for
148 		DataSourceHolder                m_aDataSource;
149         Reference< XModel >             m_xDocument;
150 		Reference< XNumberFormatter > 	m_xFormatter;	// a number formatter working with the connection's NumberFormatsSupplier
151         sal_Int32                       m_nDocStartNumber;
152 		sal_Bool		                m_bSuspended;	// is true when the controller was already suspended
153 		sal_Bool		                m_bEditable;	// is the control readonly or not
154 		sal_Bool		                m_bModified;	// is the data modified
155         bool                            m_bNotAttached;
156 
157         DBSubComponentController_Impl( ::osl::Mutex& i_rMutex )
158             :m_aDocScriptSupport()
159             ,m_aModifyListeners( i_rMutex )
160             ,m_nDocStartNumber(0)
161             ,m_bSuspended( sal_False )
162 		    ,m_bEditable(sal_True)
163 		    ,m_bModified(sal_False)
164             ,m_bNotAttached(true)
165         {
166         }
167 
168         bool    documentHasScriptSupport() const
169         {
170             OSL_PRECOND( !!m_aDocScriptSupport,
171                 "DBSubComponentController_Impl::documentHasScriptSupport: not completely initialized, yet - don't know!?" );
172             return !!m_aDocScriptSupport && *m_aDocScriptSupport;
173         }
174 
175         void    setDocumentScriptSupport( const bool _bSupport )
176         {
177             OSL_PRECOND( !m_aDocScriptSupport,
178                 "DBSubComponentController_Impl::setDocumentScriptSupport: already initialized!" );
179             m_aDocScriptSupport = ::boost::optional< bool >( _bSupport );
180         }
181     };
182 
183 	//====================================================================
184 	//= DBSubComponentController
185 	//====================================================================
186 	//--------------------------------------------------------------------
187 	DBSubComponentController::DBSubComponentController(const Reference< XMultiServiceFactory >& _rxORB)
188         :DBSubComponentController_Base( _rxORB )
189         ,m_pImpl( new DBSubComponentController_Impl( getMutex() ) )
190 	{
191 	}
192 
193     //--------------------------------------------------------------------
194     DBSubComponentController::~DBSubComponentController()
195     {
196     }
197 
198 	//--------------------------------------------------------------------
199     void DBSubComponentController::impl_initialize()
200     {
201         OGenericUnoController::impl_initialize();
202 
203         const ::comphelper::NamedValueCollection& rArguments( getInitParams() );
204 
205 		Reference< XConnection > xConnection;
206         xConnection = rArguments.getOrDefault( (::rtl::OUString)PROPERTY_ACTIVE_CONNECTION, xConnection );
207 
208         if ( !xConnection.is() )
209             ::dbtools::isEmbeddedInDatabase( getModel(), xConnection );
210 
211 		if ( xConnection.is() )
212             initializeConnection( xConnection );
213 
214 		bool bShowError = true;
215 		if ( !isConnected() )
216         {
217 			reconnect( sal_False );
218 			bShowError = false;
219 		}
220 		if ( !isConnected() )
221 		{
222 			if ( bShowError )
223 				connectionLostMessage();
224 			throw IllegalArgumentException();
225 		}
226     }
227 
228     //--------------------------------------------------------------------
229     Any SAL_CALL DBSubComponentController::queryInterface(const Type& _rType) throw (RuntimeException)
230     {
231         if ( _rType.equals( XScriptInvocationContext::static_type() ) )
232         {
233             if ( m_pImpl->documentHasScriptSupport() )
234                 return makeAny( Reference< XScriptInvocationContext >( this ) );
235             return Any();
236         }
237 
238         return DBSubComponentController_Base::queryInterface( _rType );
239     }
240 
241     //--------------------------------------------------------------------
242     Sequence< Type > SAL_CALL DBSubComponentController::getTypes(  ) throw (RuntimeException)
243     {
244         Sequence< Type > aTypes( DBSubComponentController_Base::getTypes() );
245         if ( !m_pImpl->documentHasScriptSupport() )
246         {
247             Sequence< Type > aStrippedTypes( aTypes.getLength() - 1 );
248             ::std::remove_copy_if(
249                 aTypes.getConstArray(),
250                 aTypes.getConstArray() + aTypes.getLength(),
251                 aStrippedTypes.getArray(),
252                 ::std::bind2nd( ::std::equal_to< Type >(), XScriptInvocationContext::static_type() )
253             );
254             aTypes = aStrippedTypes;
255         }
256         return aTypes;
257     }
258 
259 	//--------------------------------------------------------------------
260 	void DBSubComponentController::initializeConnection( const Reference< XConnection >& _rxForeignConn )
261 	{
262 		DBG_ASSERT( !isConnected(), "DBSubComponentController::initializeConnection: not to be called when already connected!" );
263 			// usually this gets called from within initialize of derived classes ...
264 		if ( isConnected() )
265 			disconnect();
266 
267         m_pImpl->m_xConnection.reset( _rxForeignConn, SharedConnection::NoTakeOwnership );
268         m_pImpl->m_aSdbMetaData.reset( m_pImpl->m_xConnection );
269 		startConnectionListening( m_pImpl->m_xConnection );
270 
271 		// get the data source the connection belongs to
272 		try
273 		{
274             // determine our data source
275             OSL_PRECOND( !m_pImpl->m_aDataSource.is(), "DBSubComponentController::initializeConnection: already a data source in this phase?" );
276             {
277 			    Reference< XChild > xConnAsChild( m_pImpl->m_xConnection, UNO_QUERY );
278 			    Reference< XDataSource > xDS;
279 			    if ( xConnAsChild.is() )
280 				    xDS = Reference< XDataSource >( xConnAsChild->getParent(), UNO_QUERY );
281 
282 			    // (take the indirection through XDataSource to ensure we have a correct object ....)
283 			    m_pImpl->m_aDataSource = xDS;
284             }
285             OSL_POSTCOND( m_pImpl->m_aDataSource.is(), "DBSubComponentController::initializeConnection: unable to obtain the data source object!" );
286 
287             if ( m_pImpl->m_bNotAttached )
288             {
289                 Reference< XUntitledNumbers > xUntitledProvider( getDatabaseDocument(), UNO_QUERY );
290                 m_pImpl->m_nDocStartNumber = 1;
291                 if ( xUntitledProvider.is() )
292                     m_pImpl->m_nDocStartNumber = xUntitledProvider->leaseNumber( static_cast< XWeak* >( this ) );
293             }
294 
295             // determine the availability of script support in our document. Our own XScriptInvocationContext
296             // interface depends on this
297             m_pImpl->setDocumentScriptSupport( Reference< XEmbeddedScripts >( getDatabaseDocument(), UNO_QUERY ).is() );
298 
299             // get a number formatter
300             Reference< XPropertySet > xDataSourceProps( m_pImpl->m_aDataSource.getDataSourceProps(), UNO_SET_THROW );
301 			xDataSourceProps->getPropertyValue( PROPERTY_NAME ) >>= m_pImpl->m_sDataSourceName;
302 			DBG_ASSERT( m_pImpl->m_sDataSourceName.getLength(), "DBSubComponentController::initializeConnection: invalid data source name!" );
303 			Reference< XNumberFormatsSupplier> xSupplier = ::dbtools::getNumberFormats(m_pImpl->m_xConnection);
304 			if(xSupplier.is())
305 			{
306 				m_pImpl->m_xFormatter = Reference< XNumberFormatter >(getORB()
307 					->createInstance(::rtl::OUString::createFromAscii("com.sun.star.util.NumberFormatter")), UNO_QUERY);
308 				m_pImpl->m_xFormatter->attachNumberFormatsSupplier(xSupplier);
309 			}
310 			OSL_ENSURE(m_pImpl->m_xFormatter.is(),"No NumberFormatter!");
311 		}
312         catch( const Exception& )
313         {
314             DBG_UNHANDLED_EXCEPTION();
315         }
316 	}
317 
318 	//--------------------------------------------------------------------
319 	void DBSubComponentController::reconnect( sal_Bool _bUI )
320 	{
321 		OSL_ENSURE(!m_pImpl->m_bSuspended, "Cannot reconnect while suspended!");
322 
323 		stopConnectionListening( m_pImpl->m_xConnection );
324         m_pImpl->m_aSdbMetaData.reset( NULL );
325         m_pImpl->m_xConnection.clear();
326 
327 		// reconnect
328 		sal_Bool bReConnect = sal_True;
329 		if ( _bUI )
330 		{
331 			QueryBox aQuery( getView(), ModuleRes(QUERY_CONNECTION_LOST) );
332 			bReConnect = ( RET_YES == aQuery.Execute() );
333 		}
334 
335 		// now really reconnect ...
336 		if ( bReConnect )
337         {
338             m_pImpl->m_xConnection.reset( connect( m_pImpl->m_aDataSource.getDataSource(), NULL ), SharedConnection::TakeOwnership );
339             m_pImpl->m_aSdbMetaData.reset( m_pImpl->m_xConnection );
340         }
341 
342 		// invalidate all slots
343 		InvalidateAll();
344 	}
345 
346 	//--------------------------------------------------------------------
347 	void DBSubComponentController::disconnect()
348 	{
349 		stopConnectionListening(m_pImpl->m_xConnection);
350         m_pImpl->m_aSdbMetaData.reset( NULL );
351         m_pImpl->m_xConnection.clear();
352 
353 		InvalidateAll();
354 	}
355 
356 	//--------------------------------------------------------------------
357 	void DBSubComponentController::losingConnection()
358 	{
359 		// our connection was disposed so we need a new one
360 		reconnect( sal_True );
361 	    InvalidateAll();
362 	}
363 
364 	//--------------------------------------------------------------------
365 	void SAL_CALL DBSubComponentController::disposing()
366 	{
367 		DBSubComponentController_Base::disposing();
368 
369         disconnect();
370 
371 		attachFrame( Reference < XFrame >() );
372 
373 		m_pImpl->m_aDataSource.clear();
374 	}
375 
376 	//--------------------------------------------------------------------
377 	void SAL_CALL DBSubComponentController::disposing(const EventObject& _rSource) throw( RuntimeException )
378 	{
379         if ( _rSource.Source == getConnection() )
380         {
381 		    if (    !m_pImpl->m_bSuspended // when already suspended then we don't have to reconnect
382 			    &&	!getBroadcastHelper().bInDispose
383 			    &&	!getBroadcastHelper().bDisposed
384 			    &&	isConnected()
385 			    )
386 		    {
387 			    losingConnection();
388 		    }
389             else
390             {
391                 m_pImpl->m_xConnection.reset( m_pImpl->m_xConnection, SharedConnection::NoTakeOwnership );
392                     // this prevents the "disposeComponent" call in disconnect
393                 disconnect();
394             }
395         }
396 		else
397             DBSubComponentController_Base::disposing( _rSource );
398 	}
399 
400     //--------------------------------------------------------------------
401     void DBSubComponentController::appendError( const ::rtl::OUString& _rErrorMessage, const ::dbtools::StandardSQLState _eSQLState,
402             const sal_Int32 _nErrorCode )
403 	{
404         m_pImpl->m_aCurrentError.append( ::dbtools::SQLExceptionInfo::SQL_EXCEPTION, _rErrorMessage, getStandardSQLStateAscii( _eSQLState ),
405             _nErrorCode );
406 	}
407 	//--------------------------------------------------------------------
408 	void DBSubComponentController::clearError()
409 	{
410         m_pImpl->m_aCurrentError = ::dbtools::SQLExceptionInfo();
411 	}
412 
413 	//--------------------------------------------------------------------
414 	sal_Bool DBSubComponentController::hasError() const
415 	{
416 		return m_pImpl->m_aCurrentError.isValid();
417 	}
418 
419 	//--------------------------------------------------------------------
420     const ::dbtools::SQLExceptionInfo& DBSubComponentController::getError() const
421     {
422         return m_pImpl->m_aCurrentError;
423     }
424 
425 	//--------------------------------------------------------------------
426     void DBSubComponentController::displayError()
427     {
428         showError( m_pImpl->m_aCurrentError );
429     }
430 
431 	//--------------------------------------------------------------------
432 	sal_Bool SAL_CALL DBSubComponentController::suspend(sal_Bool bSuspend) throw( RuntimeException )
433 	{
434 		m_pImpl->m_bSuspended = bSuspend;
435 		if ( !bSuspend && !isConnected() )
436 			reconnect(sal_True);
437 
438 
439 		return sal_True;
440 	}
441 
442     // -----------------------------------------------------------------------------
443     sal_Bool SAL_CALL DBSubComponentController::attachModel( const Reference< XModel > & _rxModel) throw( RuntimeException )
444     {
445         if ( !_rxModel.is() )
446             return sal_False;
447         if ( !DBSubComponentController_Base::attachModel( _rxModel ) )
448             return sal_False;
449 
450         m_pImpl->m_bNotAttached = false;
451         if ( m_pImpl->m_nDocStartNumber == 1 )
452             releaseNumberForComponent();
453 
454         Reference< XUntitledNumbers > xUntitledProvider( _rxModel, UNO_QUERY );
455         m_pImpl->m_nDocStartNumber = 1;
456         if ( xUntitledProvider.is() )
457             m_pImpl->m_nDocStartNumber = xUntitledProvider->leaseNumber( static_cast< XWeak* >( this ) );
458 
459 		return sal_True;
460     }
461 
462 	// -----------------------------------------------------------------------------
463 	void DBSubComponentController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& _rArgs)
464 	{
465         if ( _nId == ID_BROWSER_CLOSE )
466         {
467             closeTask();
468             return;
469         }
470 
471         DBSubComponentController_Base::Execute( _nId, _rArgs );
472 		InvalidateFeature( _nId );
473 	}
474 
475 	// -----------------------------------------------------------------------------
476 	::rtl::OUString DBSubComponentController::getDataSourceName() const
477 	{
478 		::rtl::OUString sName;
479         Reference< XPropertySet > xDataSourceProps( m_pImpl->m_aDataSource.getDataSourceProps() );
480 		if ( xDataSourceProps.is() )
481 			xDataSourceProps->getPropertyValue(PROPERTY_NAME) >>= sName;
482 		return sName;
483 	}
484 	// -----------------------------------------------------------------------------
485 	void DBSubComponentController::connectionLostMessage() const
486 	{
487 		String aMessage(ModuleRes(RID_STR_CONNECTION_LOST));
488 		Reference< XWindow > xWindow = getTopMostContainerWindow();
489 		Window* pWin = NULL;
490 		if ( xWindow.is() )
491 			pWin = VCLUnoHelper::GetWindow(xWindow);
492 		if ( !pWin )
493 			pWin = getView()->Window::GetParent();
494 
495 		InfoBox(pWin, aMessage).Execute();
496 	}
497 	// -----------------------------------------------------------------------------
498 	const Reference< XConnection >& DBSubComponentController::getConnection() const
499 	{
500 		return m_pImpl->m_xConnection;
501 	}
502 
503     // -----------------------------------------------------------------------------
504     sal_Bool DBSubComponentController::isReadOnly() const
505     {
506         return !m_pImpl->m_bEditable;
507     }
508 
509 	// -----------------------------------------------------------------------------
510 	sal_Bool DBSubComponentController::isEditable() const
511     {
512         return m_pImpl->m_bEditable;
513     }
514 
515 	// -----------------------------------------------------------------------------
516     void DBSubComponentController::setEditable(sal_Bool _bEditable)
517     {
518         m_pImpl->m_bEditable = _bEditable;
519     }
520 
521 	// -----------------------------------------------------------------------------
522     const ::dbtools::DatabaseMetaData& DBSubComponentController::getSdbMetaData() const
523     {
524         return m_pImpl->m_aSdbMetaData;
525     }
526 
527 	// -----------------------------------------------------------------------------
528     sal_Bool DBSubComponentController::isConnected() const
529     {
530         return m_pImpl->m_xConnection.is();
531     }
532 
533 	// -----------------------------------------------------------------------------
534     Reference< XDatabaseMetaData > DBSubComponentController::getMetaData( ) const
535     {
536         Reference< XDatabaseMetaData > xMeta;
537         try
538         {
539             if ( isConnected() )
540                 xMeta.set( m_pImpl->m_xConnection->getMetaData(), UNO_SET_THROW );
541         }
542         catch( const Exception& )
543         {
544         	DBG_UNHANDLED_EXCEPTION();
545         }
546         return xMeta;
547     }
548 
549 	// -----------------------------------------------------------------------------
550     const Reference< XPropertySet >& DBSubComponentController::getDataSource() const
551     {
552         return m_pImpl->m_aDataSource.getDataSourceProps();
553     }
554 
555 	// -----------------------------------------------------------------------------
556     sal_Bool DBSubComponentController::haveDataSource() const
557     {
558         return m_pImpl->m_aDataSource.is();
559     }
560 
561 	// -----------------------------------------------------------------------------
562     Reference< XModel > DBSubComponentController::getDatabaseDocument() const
563     {
564         return Reference< XModel >( m_pImpl->m_aDataSource.getDatabaseDocument(), UNO_QUERY );
565     }
566 
567 	// -----------------------------------------------------------------------------
568     Reference< XNumberFormatter > DBSubComponentController::getNumberFormatter() const
569     {
570         return m_pImpl->m_xFormatter;
571     }
572 
573     // -----------------------------------------------------------------------------
574     Reference< XModel > DBSubComponentController::getPrivateModel() const
575     {
576 		return getDatabaseDocument();
577     }
578     // -----------------------------------------------------------------------------
579     // XTitle
580     ::rtl::OUString SAL_CALL DBSubComponentController::getTitle()
581         throw (RuntimeException)
582     {
583         ::osl::MutexGuard aGuard( getMutex() );
584         if ( m_bExternalTitle )
585             return impl_getTitleHelper_throw()->getTitle ();
586 
587         ::rtl::OUStringBuffer sTitle;
588         Reference< XTitle > xTitle(getPrivateModel(),UNO_QUERY);
589         if ( xTitle.is() )
590         {
591             sTitle.append( xTitle->getTitle() );
592             sTitle.appendAscii(" : ");
593         }
594         sTitle.append( getPrivateTitle() );
595         return sTitle.makeStringAndClear();
596     }
597 
598     // -----------------------------------------------------------------------------
599     sal_Int32 DBSubComponentController::getCurrentStartNumber() const
600     {
601         return m_pImpl->m_nDocStartNumber;
602     }
603 
604     // -----------------------------------------------------------------------------
605     Reference< XEmbeddedScripts > SAL_CALL DBSubComponentController::getScriptContainer() throw (RuntimeException)
606     {
607         ::osl::MutexGuard aGuard( getMutex() );
608         if ( !m_pImpl->documentHasScriptSupport() )
609             return NULL;
610 
611         return Reference< XEmbeddedScripts >( getDatabaseDocument(), UNO_QUERY_THROW );
612     }
613 
614     // -----------------------------------------------------------------------------
615     void SAL_CALL DBSubComponentController::addModifyListener( const Reference< XModifyListener >& i_Listener ) throw (RuntimeException)
616     {
617         ::osl::MutexGuard aGuard( getMutex() );
618         m_pImpl->m_aModifyListeners.addInterface( i_Listener );
619     }
620 
621     // -----------------------------------------------------------------------------
622     void SAL_CALL DBSubComponentController::removeModifyListener( const Reference< XModifyListener >& i_Listener ) throw (RuntimeException)
623     {
624         ::osl::MutexGuard aGuard( getMutex() );
625         m_pImpl->m_aModifyListeners.removeInterface( i_Listener );
626     }
627 
628     // -----------------------------------------------------------------------------
629     ::sal_Bool SAL_CALL DBSubComponentController::isModified(  ) throw (RuntimeException)
630     {
631         ::osl::MutexGuard aGuard( getMutex() );
632         return impl_isModified();
633     }
634 
635     // -----------------------------------------------------------------------------
636     void SAL_CALL DBSubComponentController::setModified( ::sal_Bool i_bModified ) throw (PropertyVetoException, RuntimeException)
637     {
638         ::osl::ClearableMutexGuard aGuard( getMutex() );
639 
640         if ( m_pImpl->m_bModified == i_bModified )
641             return;
642 
643 		m_pImpl->m_bModified = i_bModified;
644         impl_onModifyChanged();
645 
646         EventObject aEvent( *this );
647         aGuard.clear();
648         m_pImpl->m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
649     }
650 
651     // -----------------------------------------------------------------------------
652     sal_Bool DBSubComponentController::impl_isModified() const
653     {
654         return m_pImpl->m_bModified;
655     }
656 
657     // -----------------------------------------------------------------------------
658     void DBSubComponentController::impl_onModifyChanged()
659     {
660         InvalidateFeature( ID_BROWSER_SAVEDOC );
661         if ( isFeatureSupported( ID_BROWSER_SAVEASDOC ) )
662             InvalidateFeature( ID_BROWSER_SAVEASDOC );
663     }
664 
665 //........................................................................
666 }	// namespace dbaui
667 //........................................................................
668 
669