LibreOffice Module dbaccess (master) 1
dbsubcomponentcontroller.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <browserids.hxx>
21#include <commontypes.hxx>
22#include <core_resource.hxx>
23#include <dbaccess/dataview.hxx>
24#include <strings.hrc>
25#include <strings.hxx>
27
28#include <com/sun/star/frame/XUntitledNumbers.hpp>
29#include <com/sun/star/container/XChild.hpp>
30#include <com/sun/star/sdb/XDocumentDataSource.hpp>
31#include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp>
32#include <com/sun/star/sdbc/XDataSource.hpp>
33#include <com/sun/star/util/NumberFormatter.hpp>
34#include <com/sun/star/lang/IllegalArgumentException.hpp>
35
36#include <comphelper/types.hxx>
41#include <rtl/ustrbuf.hxx>
42#include <sal/log.hxx>
44#include <tools/debug.hxx>
46#include <vcl/svapp.hxx>
47#include <vcl/weld.hxx>
48
49namespace dbaui
50{
51
52 using ::com::sun::star::uno::Any;
53 using ::com::sun::star::uno::Reference;
54 using ::com::sun::star::beans::XPropertySet;
55 using ::com::sun::star::uno::Sequence;
56 using ::com::sun::star::uno::Type;
57 using ::com::sun::star::uno::XComponentContext;
58 using ::com::sun::star::sdbc::XConnection;
59 using ::com::sun::star::uno::UNO_QUERY;
60 using ::com::sun::star::container::XChild;
61 using ::com::sun::star::sdbc::XDataSource;
62 using ::com::sun::star::util::NumberFormatter;
63 using ::com::sun::star::util::XNumberFormatter;
64 using ::com::sun::star::util::XNumberFormatsSupplier;
65 using ::com::sun::star::frame::XFrame;
66 using ::com::sun::star::uno::Exception;
67 using ::com::sun::star::lang::EventObject;
68 using ::com::sun::star::beans::PropertyValue;
69 using ::com::sun::star::frame::XModel;
70 using ::com::sun::star::sdb::XOfficeDatabaseDocument;
71 using ::com::sun::star::awt::XWindow;
72 using ::com::sun::star::sdbc::XDatabaseMetaData;
73 using ::com::sun::star::sdb::XDocumentDataSource;
74 using ::com::sun::star::document::XEmbeddedScripts;
75 using ::com::sun::star::lang::IllegalArgumentException;
76 using ::com::sun::star::uno::UNO_SET_THROW;
77 using ::com::sun::star::uno::UNO_QUERY_THROW;
78 using ::com::sun::star::frame::XUntitledNumbers;
79
80 namespace {
81
82 class DataSourceHolder
83 {
84 public:
85 DataSourceHolder()
86 {
87 }
88
89 explicit DataSourceHolder(const Reference< XDataSource >& _rxDataSource)
90 : m_xDataSource(_rxDataSource)
91 {
92 Reference< XDocumentDataSource > xDocDS( m_xDataSource, UNO_QUERY );
93 if ( xDocDS.is() )
94 m_xDocument = xDocDS->getDatabaseDocument();
95
96 m_xDataSourceProps.set( m_xDataSource, UNO_QUERY );
97 }
98
99 const Reference< XDataSource >& getDataSource() const { return m_xDataSource; }
100 const Reference< XPropertySet >& getDataSourceProps() const { return m_xDataSourceProps; }
101 const Reference< XOfficeDatabaseDocument >& getDatabaseDocument() const { return m_xDocument; }
102
103 bool is() const { return m_xDataSource.is(); }
104
105 void clear()
106 {
107 m_xDataSource.clear();
108 m_xDocument.clear();
109 }
110
111 private:
112 Reference< XDataSource > m_xDataSource;
113 Reference< XPropertySet > m_xDataSourceProps;
114 Reference< XOfficeDatabaseDocument > m_xDocument;
115 };
116
117 }
118
120 {
121 private:
122 ::std::optional< bool > m_aDocScriptSupport;
123
124 public:
126
129
130 // <properties>
133 // </properties>
134 OUString m_sDataSourceName; // the data source we're working for
135 DataSourceHolder m_aDataSource;
136 Reference< XNumberFormatter > m_xFormatter; // a number formatter working with the connection's NumberFormatsSupplier
138 bool m_bSuspended; // is true when the controller was already suspended
139 bool m_bEditable; // is the control readonly or not
140 bool m_bModified; // is the data modified
142
143 explicit DBSubComponentController_Impl(osl::Mutex& i_rMutex)
144 :m_aModifyListeners( i_rMutex )
146 ,m_bSuspended( false )
147 ,m_bEditable(true)
148 ,m_bModified(false)
149 ,m_bNotAttached(true)
150 {
151 }
152
154 {
155 OSL_PRECOND( !!m_aDocScriptSupport,
156 "DBSubComponentController_Impl::documentHasScriptSupport: not completely initialized, yet - don't know!?" );
158 }
159
160 void setDocumentScriptSupport( const bool _bSupport )
161 {
162 OSL_PRECOND( !m_aDocScriptSupport,
163 "DBSubComponentController_Impl::setDocumentScriptSupport: already initialized!" );
164 m_aDocScriptSupport = ::std::optional< bool >( _bSupport );
165 }
166 };
167
168 // DBSubComponentController
172 {
173 }
174
176 {
177 }
178
180 {
182
183 const ::comphelper::NamedValueCollection& rArguments( getInitParams() );
184
185 Reference< XConnection > xConnection;
186 xConnection = rArguments.getOrDefault( PROPERTY_ACTIVE_CONNECTION, xConnection );
187
188 if ( !xConnection.is() )
189 ::dbtools::isEmbeddedInDatabase( getModel(), xConnection );
190
191 if ( xConnection.is() )
192 initializeConnection( xConnection );
193
194 bool bShowError = true;
195 if ( !isConnected() )
196 {
197 reconnect( false );
198 bShowError = false;
199 }
200 if ( !isConnected() )
201 {
202 if ( bShowError )
204 throw IllegalArgumentException();
205 }
206 }
207
209 {
210 if ( _rType.equals( cppu::UnoType<XScriptInvocationContext>::get() ) )
211 {
212 if ( m_pImpl->documentHasScriptSupport() )
214 return Any();
215 }
216
217 return DBSubComponentController_Base::queryInterface( _rType );
218 }
219
221 {
222 Sequence< Type > aTypes( DBSubComponentController_Base::getTypes() );
223 if ( !m_pImpl->documentHasScriptSupport() )
224 {
225 auto [begin, end] = asNonConstRange(aTypes);
226 auto newEnd = std::remove_if( begin, end,
227 [](const Type& type)
229 aTypes.realloc( std::distance(begin, newEnd) );
230 }
231 return aTypes;
232 }
233
235 {
236 DBG_ASSERT( !isConnected(), "DBSubComponentController::initializeConnection: not to be called when already connected!" );
237 // usually this gets called from within initialize of derived classes ...
238 if ( isConnected() )
239 disconnect();
240
241 m_pImpl->m_xConnection.reset( _rxForeignConn, SharedConnection::NoTakeOwnership );
242 m_pImpl->m_aSdbMetaData.reset( m_pImpl->m_xConnection );
243 startConnectionListening( m_pImpl->m_xConnection );
244
245 // get the data source the connection belongs to
246 try
247 {
248 // determine our data source
249 OSL_PRECOND( !m_pImpl->m_aDataSource.is(), "DBSubComponentController::initializeConnection: already a data source in this phase?" );
250 {
251 Reference< XChild > xConnAsChild( m_pImpl->m_xConnection, UNO_QUERY );
253 if ( xConnAsChild.is() )
254 xDS.set( xConnAsChild->getParent(), UNO_QUERY );
255
256 // (take the indirection through XDataSource to ensure we have a correct object...)
257 m_pImpl->m_aDataSource = DataSourceHolder(xDS);
258 }
259 SAL_WARN_IF( !m_pImpl->m_aDataSource.is(), "dbaccess.ui", "DBSubComponentController::initializeConnection: unable to obtain the data source object!" );
260
261 if ( m_pImpl->m_bNotAttached )
262 {
263 Reference< XUntitledNumbers > xUntitledProvider( getDatabaseDocument(), UNO_QUERY );
264 m_pImpl->m_nDocStartNumber = 1;
265 if ( xUntitledProvider.is() )
266 m_pImpl->m_nDocStartNumber = xUntitledProvider->leaseNumber( static_cast< XWeak* >( this ) );
267 }
268
269 // determine the availability of script support in our document. Our own XScriptInvocationContext
270 // interface depends on this
271 m_pImpl->setDocumentScriptSupport( Reference< XEmbeddedScripts >( getDatabaseDocument(), UNO_QUERY ).is() );
272
273 // get a number formatter
274 Reference< XPropertySet > xDataSourceProps( m_pImpl->m_aDataSource.getDataSourceProps(), UNO_SET_THROW );
275 xDataSourceProps->getPropertyValue( PROPERTY_NAME ) >>= m_pImpl->m_sDataSourceName;
276 DBG_ASSERT( !m_pImpl->m_sDataSourceName.isEmpty(), "DBSubComponentController::initializeConnection: invalid data source name!" );
277 Reference< XNumberFormatsSupplier> xSupplier = ::dbtools::getNumberFormats(m_pImpl->m_xConnection);
278 if(xSupplier.is())
279 {
280 m_pImpl->m_xFormatter.set(NumberFormatter::create(getORB()), UNO_QUERY_THROW);
281 m_pImpl->m_xFormatter->attachNumberFormatsSupplier(xSupplier);
282 }
283 OSL_ENSURE(m_pImpl->m_xFormatter.is(),"No NumberFormatter!");
284 }
285 catch( const Exception& )
286 {
287 DBG_UNHANDLED_EXCEPTION("dbaccess");
288 }
289 }
290
292 {
293 OSL_ENSURE(!m_pImpl->m_bSuspended, "Cannot reconnect while suspended!");
294
295 stopConnectionListening( m_pImpl->m_xConnection );
296 m_pImpl->m_aSdbMetaData.reset( nullptr );
297 m_pImpl->m_xConnection.clear();
298
299 // reconnect
300 bool bReConnect = true;
301 if ( _bUI )
302 {
303 std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(getFrameWeld(),
304 VclMessageType::Question, VclButtonsType::YesNo,
305 DBA_RES(STR_QUERY_CONNECTION_LOST)));
306 bReConnect = (RET_YES == xQuery->run());
307 }
308
309 // now really reconnect ...
310 if ( bReConnect )
311 {
312 m_pImpl->m_xConnection.reset( connect( m_pImpl->m_aDataSource.getDataSource() ), SharedConnection::TakeOwnership );
313 m_pImpl->m_aSdbMetaData.reset( m_pImpl->m_xConnection );
314 }
315
316 // invalidate all slots
317 InvalidateAll();
318 }
319
321 {
322 stopConnectionListening(m_pImpl->m_xConnection);
323 m_pImpl->m_aSdbMetaData.reset( nullptr );
324 m_pImpl->m_xConnection.clear();
325
326 InvalidateAll();
327 }
328
330 {
331 // our connection was disposed so we need a new one
332 reconnect( true );
333 InvalidateAll();
334 }
335
337 {
338 DBSubComponentController_Base::disposing();
339
340 disconnect();
341
342 attachFrame( Reference < XFrame >() );
343
344 m_pImpl->m_aDataSource.clear();
345 }
346
347 void SAL_CALL DBSubComponentController::disposing(const EventObject& _rSource)
348 {
349 if ( _rSource.Source == getConnection() )
350 {
351 if ( !m_pImpl->m_bSuspended // when already suspended then we don't have to reconnect
352 && !getBroadcastHelper().bInDispose
353 && !getBroadcastHelper().bDisposed
354 && isConnected()
355 )
356 {
358 }
359 else
360 {
361 m_pImpl->m_xConnection.reset( m_pImpl->m_xConnection, SharedConnection::NoTakeOwnership );
362 // this prevents the "disposeComponent" call in disconnect
363 disconnect();
364 }
365 }
366 else
367 DBSubComponentController_Base::disposing( _rSource );
368 }
369
370 void DBSubComponentController::appendError( const OUString& _rErrorMessage )
371 {
372 m_pImpl->m_aCurrentError.append( ::dbtools::SQLExceptionInfo::TYPE::SQLException, _rErrorMessage,
373 getStandardSQLState( ::dbtools::StandardSQLState::GENERAL_ERROR ),
374 1000 );
375 }
377 {
378 m_pImpl->m_aCurrentError = ::dbtools::SQLExceptionInfo();
379 }
380
382 {
383 return m_pImpl->m_aCurrentError.isValid();
384 }
385
386 const ::dbtools::SQLExceptionInfo& DBSubComponentController::getError() const
387 {
388 return m_pImpl->m_aCurrentError;
389 }
390
392 {
393 showError( m_pImpl->m_aCurrentError );
394 }
395
397 {
398 m_pImpl->m_bSuspended = bSuspend;
399 if ( !bSuspend && !isConnected() )
400 reconnect(true);
401
402 return true;
403 }
404
406 {
407 if ( !_rxModel.is() )
408 return false;
409 if ( !DBSubComponentController_Base::attachModel( _rxModel ) )
410 return false;
411
412 m_pImpl->m_bNotAttached = false;
413 if ( m_pImpl->m_nDocStartNumber == 1 )
414 releaseNumberForComponent();
415
416 Reference< XUntitledNumbers > xUntitledProvider( _rxModel, UNO_QUERY );
417 m_pImpl->m_nDocStartNumber = 1;
418 if ( xUntitledProvider.is() )
419 m_pImpl->m_nDocStartNumber = xUntitledProvider->leaseNumber( static_cast< XWeak* >( this ) );
420
421 return true;
422 }
423
425 {
426 if ( _nId == ID_BROWSER_CLOSE )
427 {
428 closeTask();
429 return;
430 }
431
432 DBSubComponentController_Base::Execute( _nId, _rArgs );
433 InvalidateFeature( _nId );
434 }
435
437 {
438 OUString sName;
439 Reference< XPropertySet > xDataSourceProps( m_pImpl->m_aDataSource.getDataSourceProps() );
440 if ( xDataSourceProps.is() )
441 xDataSourceProps->getPropertyValue(PROPERTY_NAME) >>= sName;
442 return sName;
443 }
445 {
446 OUString aMessage(DBA_RES(RID_STR_CONNECTION_LOST));
447 Reference< XWindow > xWindow = getTopMostContainerWindow();
448 vcl::Window* pWin = nullptr;
449 if ( xWindow.is() )
450 pWin = VCLUnoHelper::GetWindow(xWindow);
451 if ( !pWin )
452 pWin = getView()->Window::GetParent();
453
454 std::unique_ptr<weld::MessageDialog> xInfo(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
455 VclMessageType::Info, VclButtonsType::Ok, aMessage));
456 xInfo->run();
457 }
459 {
460 return m_pImpl->m_xConnection;
461 }
462
464 {
465 return !m_pImpl->m_bEditable;
466 }
467
469 {
470 return m_pImpl->m_bEditable;
471 }
472
474 {
475 m_pImpl->m_bEditable = _bEditable;
476 }
477
478 const ::dbtools::DatabaseMetaData& DBSubComponentController::getSdbMetaData() const
479 {
480 return m_pImpl->m_aSdbMetaData;
481 }
482
484 {
485 return m_pImpl->m_xConnection.is();
486 }
487
489 {
491 try
492 {
493 if ( isConnected() )
494 xMeta.set( m_pImpl->m_xConnection->getMetaData(), UNO_SET_THROW );
495 }
496 catch( const Exception& )
497 {
498 DBG_UNHANDLED_EXCEPTION("dbaccess");
499 }
500 return xMeta;
501 }
502
504 {
505 return m_pImpl->m_aDataSource.getDataSourceProps();
506 }
507
509 {
510 return m_pImpl->m_aDataSource.is();
511 }
512
514 {
515 return Reference< XModel >( m_pImpl->m_aDataSource.getDatabaseDocument(), UNO_QUERY );
516 }
517
519 {
520 return m_pImpl->m_xFormatter;
521 }
522
524 {
525 return getDatabaseDocument();
526 }
527 // XTitle
529 {
530 ::osl::MutexGuard aGuard( getMutex() );
531 if ( m_bExternalTitle )
532 return impl_getTitleHelper_throw()->getTitle ();
533
534 OUStringBuffer sTitle;
535 Reference< XTitle > xTitle(getPrivateModel(),UNO_QUERY);
536 if ( xTitle.is() )
537 {
538 sTitle.append( xTitle->getTitle() + " : ");
539 }
540 sTitle.append( getPrivateTitle() );
541 return sTitle.makeStringAndClear();
542 }
543
545 {
546 return m_pImpl->m_nDocStartNumber;
547 }
548
550 {
551 ::osl::MutexGuard aGuard( getMutex() );
552 if ( !m_pImpl->documentHasScriptSupport() )
553 return nullptr;
554
555 return Reference< XEmbeddedScripts >( getDatabaseDocument(), UNO_QUERY_THROW );
556 }
557
559 {
560 ::osl::MutexGuard aGuard( getMutex() );
561 m_pImpl->m_aModifyListeners.addInterface( i_Listener );
562 }
563
565 {
566 ::osl::MutexGuard aGuard( getMutex() );
567 m_pImpl->m_aModifyListeners.removeInterface( i_Listener );
568 }
569
571 {
572 ::osl::MutexGuard aGuard( getMutex() );
573 return impl_isModified();
574 }
575
577 {
578 ::osl::ClearableMutexGuard aGuard( getMutex() );
579
580 if ( m_pImpl->m_bModified == bool(i_bModified) )
581 return;
582
583 m_pImpl->m_bModified = i_bModified;
585
586 EventObject aEvent( *this );
587 aGuard.clear();
588 m_pImpl->m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent );
589 }
590
592 {
593 return m_pImpl->m_bModified;
594 }
595
597 {
598 InvalidateFeature( ID_BROWSER_SAVEDOC );
599 if ( isFeatureSupported( ID_BROWSER_SAVEASDOC ) )
600 InvalidateFeature( ID_BROWSER_SAVEASDOC );
601 }
602
603} // namespace dbaui
604
605/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sName
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
AnyEventRef aEvent
#define ID_BROWSER_CLOSE
Definition: browserids.hxx:92
#define ID_BROWSER_SAVEDOC
Definition: browserids.hxx:32
#define ID_BROWSER_SAVEASDOC
Definition: browserids.hxx:33
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, const ILibreOfficeKitNotifier *pNotifier=nullptr)
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
css::uno::Type const & get()
css::uno::Reference< css::sdbc::XDatabaseMetaData > getMetaData() const
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &_rType) override
void connectionLostMessage() const
shows an info box with the string conntection lost.
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
virtual sal_Bool SAL_CALL attachModel(const css::uno::Reference< css::frame::XModel > &xModel) override
css::uno::Reference< css::util::XNumberFormatter > const & getNumberFormatter() const
returns the number formatter
virtual sal_Bool SAL_CALL suspend(sal_Bool bSuspend) override
void appendError(const OUString &_rErrorMessage)
appends an error in the current environment.
virtual void SAL_CALL removeModifyListener(const css::uno::Reference< css::util::XModifyListener > &aListener) override
virtual void losingConnection()
called when our connection is being disposed
DBSubComponentController(const css::uno::Reference< css::uno::XComponentContext > &_rxORB)
const css::uno::Reference< css::sdbc::XConnection > & getConnection() const
gives access to the currently used connection
virtual void SAL_CALL setModified(sal_Bool bModified) override
const ::dbtools::SQLExceptionInfo & getError() const
returns the current error
virtual void SAL_CALL disposing() override
virtual sal_Bool SAL_CALL isModified() override
void displayError()
displays the current error, or does nothing if there is no current error
::std::unique_ptr< DBSubComponentController_Impl > m_pImpl
virtual css::uno::Reference< css::frame::XModel > getPrivateModel() const override
virtual void Execute(sal_uInt16 nId, const css::uno::Sequence< css::beans::PropertyValue > &aArgs) override
css::uno::Reference< css::frame::XModel > getDatabaseDocument() const
void clearError()
clears the error state.
virtual void SAL_CALL addModifyListener(const css::uno::Reference< css::util::XModifyListener > &aListener) override
virtual OUString SAL_CALL getTitle() override
void initializeConnection(const css::uno::Reference< css::sdbc::XConnection > &_rxForeignConn)
forces usage of a connection which we do not own
const css::uno::Reference< css::beans::XPropertySet > & getDataSource() const
virtual css::uno::Reference< css::document::XEmbeddedScripts > SAL_CALL getScriptContainer() override
const ::dbtools::DatabaseMetaData & getSdbMetaData() const
provides access to the SDB-level database meta data of the current connection
weld::Window * GetFrameWeld() const
#define DBA_RES(id)
Reference< XPropertySet > m_xDataSourceProps
Reference< XDataSource > m_xDataSource
Reference< XOfficeDatabaseDocument > m_xDocument
#define DBG_ASSERT(sCon, aError)
#define DBG_UNHANDLED_EXCEPTION(...)
#define SAL_WARN_IF(condition, area, stream)
@ Exception
Type
css::uno::Reference< css::uno::XInterface > getDataSource(const css::uno::Reference< css::uno::XInterface > &_rxDependentObject)
::cppu::ImplInheritanceHelper< OGenericUnoController, css::document::XScriptInvocationContext, css::util::XModifiable > DBSubComponentController_Base
void showError(const SQLExceptionInfo &_rInfo, const Reference< XWindow > &_xParent, const Reference< XComponentContext > &_rxContext)
OUString getStandardSQLState(StandardSQLState _eState)
::osl::Mutex & getMutex()
enumrange< T >::Iterator begin(enumrange< T >)
SwView * getView(const uno::Reference< frame::XModel > &xModel)
end
constexpr OUStringLiteral PROPERTY_ACTIVE_CONNECTION(u"ActiveConnection")
constexpr OUStringLiteral PROPERTY_NAME(u"Name")
::comphelper::OInterfaceContainerHelper3< css::util::XModifyListener > m_aModifyListeners
void setDocumentScriptSupport(const bool _bSupport)
unsigned char sal_Bool
ResultType type
RET_YES
const SvXMLTokenMapEntry aTypes[]