diff options
Diffstat (limited to 'dbaccess/source/ui/browser')
-rw-r--r-- | dbaccess/source/ui/browser/AsynchronousLink.cxx | 83 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/brwctrlr.cxx | 2596 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/brwview.cxx | 340 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dataview.cxx | 183 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dbexchange.cxx | 240 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dbloader.cxx | 298 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dbtreemodel.cxx | 33 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dbtreemodel.hxx | 60 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dbtreeview.cxx | 90 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dbtreeview.hxx | 66 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dsEntriesNoExp.cxx | 256 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/dsbrowserDnD.cxx | 266 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/exsrcbrw.cxx | 434 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/formadapter.cxx | 1651 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/genericcontroller.cxx | 1235 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/sbagrid.cxx | 1419 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/sbamultiplex.cxx | 96 | ||||
-rw-r--r-- | dbaccess/source/ui/browser/unodatbr.cxx | 3723 |
18 files changed, 13069 insertions, 0 deletions
diff --git a/dbaccess/source/ui/browser/AsynchronousLink.cxx b/dbaccess/source/ui/browser/AsynchronousLink.cxx new file mode 100644 index 000000000..c8a7e500f --- /dev/null +++ b/dbaccess/source/ui/browser/AsynchronousLink.cxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbaccess/AsynchronousLink.hxx> +#include <vcl/svapp.hxx> + +// OAsynchronousLink +using namespace dbaui; +OAsynchronousLink::OAsynchronousLink( const Link<void*,void>& _rHandler ) + :m_aHandler(_rHandler) + ,m_aEventSafety() + ,m_aDestructionSafety() + ,m_nEventId(nullptr) +{ +} + +OAsynchronousLink::~OAsynchronousLink() +{ + { + ::osl::MutexGuard aEventGuard( m_aEventSafety ); + if ( m_nEventId ) + Application::RemoveUserEvent(m_nEventId); + m_nEventId = nullptr; + } + + { + ::osl::MutexGuard aDestructionGuard( m_aDestructionSafety ); + // this is just for the case we're deleted while another thread just handled the event : + // if this other thread called our link while we were deleting the event here, the + // link handler blocked. With leaving the above block it continued, but now we are prevented + // to leave this destructor 'til the link handler recognizes that nEvent == 0 and leaves. + } +} + +void OAsynchronousLink::Call( void* _pArgument ) +{ + ::osl::MutexGuard aEventGuard( m_aEventSafety ); + if (m_nEventId) + Application::RemoveUserEvent(m_nEventId); + m_nEventId = Application::PostUserEvent( LINK( this, OAsynchronousLink, OnAsyncCall ), _pArgument ); +} + +void OAsynchronousLink::CancelCall() +{ + ::osl::MutexGuard aEventGuard( m_aEventSafety ); + if ( m_nEventId ) + Application::RemoveUserEvent( m_nEventId ); + m_nEventId = nullptr; +} + +IMPL_LINK(OAsynchronousLink, OnAsyncCall, void*, _pArg, void) +{ + { + ::osl::MutexGuard aDestructionGuard( m_aDestructionSafety ); + { + ::osl::MutexGuard aEventGuard( m_aEventSafety ); + if (!m_nEventId) + // our destructor deleted the event just while we are waiting for m_aEventSafety + // -> get outta here + return; + m_nEventId = nullptr; + } + } + m_aHandler.Call(_pArg); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/brwctrlr.cxx b/dbaccess/source/ui/browser/brwctrlr.cxx new file mode 100644 index 000000000..4fb847750 --- /dev/null +++ b/dbaccess/source/ui/browser/brwctrlr.cxx @@ -0,0 +1,2596 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <browserids.hxx> +#include <brwctrlr.hxx> +#include <brwview.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <queryfilter.hxx> +#include <queryorder.hxx> +#include <sqlmessage.hxx> + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/form/XBoundControl.hpp> +#include <com/sun/star/form/XDatabaseParameterBroadcaster.hpp> +#include <com/sun/star/form/XLoadable.hpp> +#include <com/sun/star/form/XReset.hpp> +#include <com/sun/star/form/XResetListener.hpp> +#include <com/sun/star/form/runtime/XFormController.hpp> +#include <com/sun/star/form/runtime/FormOperations.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <com/sun/star/sdb/ParametersRequest.hpp> +#include <com/sun/star/sdb/XInteractionSupplyParameters.hpp> +#include <com/sun/star/sdb/XSQLErrorBroadcaster.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdb/SQLFilterOperator.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> + +#include <comphelper/enumhelper.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/interaction.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/string.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sqlerror.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase2.hxx> +#include <osl/mutex.hxx> +#include <sal/log.hxx> +#include <svx/fmsearch.hxx> +#include <svx/svxdlg.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::form::runtime; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::dbtools; +using namespace ::comphelper; +using namespace ::svt; + +#define HANDLE_SQL_ERRORS( action, successflag, context, message ) \ + try \ + { \ + successflag = false; \ + action; \ + successflag = true; \ + } \ + catch(SQLException& e) \ + { \ + SQLException aError = ::dbtools::prependErrorInfo(e, *this, context); \ + css::sdb::SQLErrorEvent aEvent; \ + aEvent.Reason <<= aError; \ + errorOccured(aEvent); \ + } \ + catch(Exception&) \ + { \ + DBG_UNHANDLED_EXCEPTION("dbaccess"); \ + } \ + +#define DO_SAFE( action, message ) try { action; } catch(Exception&) { SAL_WARN("dbaccess.ui",message); } ; + +namespace dbaui +{ + +namespace { + +// OParameterContinuation +class OParameterContinuation : public OInteraction< XInteractionSupplyParameters > +{ + Sequence< PropertyValue > m_aValues; + +public: + OParameterContinuation() { } + + const Sequence< PropertyValue >& getValues() const { return m_aValues; } + +// XInteractionSupplyParameters + virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override; +}; + +} + +void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues ) +{ + m_aValues = _rValues; +} + +// a helper class implementing a runtime::XFormController, will be aggregated by SbaXDataBrowserController +// (we can't derive from XFormController as it's base class is XTabController and the XTabController::getModel collides +// with the XController::getModel implemented in our base class SbaXDataBrowserController) +class SbaXDataBrowserController::FormControllerImpl + : public ::cppu::WeakAggImplHelper2< css::form::runtime::XFormController, + css::frame::XFrameActionListener > +{ + friend class SbaXDataBrowserController; + ::comphelper::OInterfaceContainerHelper2 m_aActivateListeners; + SbaXDataBrowserController* m_pOwner; + +public: + explicit FormControllerImpl(SbaXDataBrowserController* pOwner); + + // XFormController + virtual css::uno::Reference< css::form::runtime::XFormOperations > SAL_CALL getFormOperations() override; + virtual css::uno::Reference< css::awt::XControl > SAL_CALL getCurrentControl() override; + virtual void SAL_CALL addActivateListener(const css::uno::Reference< css::form::XFormControllerListener > & l) override; + virtual void SAL_CALL removeActivateListener(const css::uno::Reference< css::form::XFormControllerListener > & l) override; + virtual void SAL_CALL addChildController( const css::uno::Reference< css::form::runtime::XFormController >& ChildController ) override; + virtual css::uno::Reference< css::form::runtime::XFormControllerContext > SAL_CALL getContext() override; + virtual void SAL_CALL setContext( const css::uno::Reference< css::form::runtime::XFormControllerContext >& _context ) override; + virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override; + virtual void SAL_CALL setInteractionHandler( const css::uno::Reference< css::task::XInteractionHandler >& _interactionHandler ) override; + + // XChild, base of XFormController + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + + // XComponent, base of XFormController + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XIndexAccess, base of XFormController + virtual ::sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override; + + // XElementAccess, base of XIndexAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // XEnumerationAccess, base of XElementAccess + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override; + + // XModifyBroadcaster, base of XFormController + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XConfirmDeleteBroadcaster, base of XFormController + virtual void SAL_CALL addConfirmDeleteListener( const css::uno::Reference< css::form::XConfirmDeleteListener >& aListener ) override; + virtual void SAL_CALL removeConfirmDeleteListener( const css::uno::Reference< css::form::XConfirmDeleteListener >& aListener ) override; + + // XSQLErrorBroadcaster, base of XFormController + virtual void SAL_CALL addSQLErrorListener( const css::uno::Reference< css::sdb::XSQLErrorListener >& Listener ) override; + virtual void SAL_CALL removeSQLErrorListener( const css::uno::Reference< css::sdb::XSQLErrorListener >& Listener ) override; + + // XRowSetApproveBroadcaster, base of XFormController + virtual void SAL_CALL addRowSetApproveListener( const css::uno::Reference< css::sdb::XRowSetApproveListener >& listener ) override; + virtual void SAL_CALL removeRowSetApproveListener( const css::uno::Reference< css::sdb::XRowSetApproveListener >& listener ) override; + + // XDatabaseParameterBroadcaster2, base of XFormController + virtual void SAL_CALL addDatabaseParameterListener( const css::uno::Reference< css::form::XDatabaseParameterListener >& aListener ) override; + virtual void SAL_CALL removeDatabaseParameterListener( const css::uno::Reference< css::form::XDatabaseParameterListener >& aListener ) override; + + // XDatabaseParameterBroadcaster, base of XDatabaseParameterBroadcaster2 + virtual void SAL_CALL addParameterListener( const css::uno::Reference< css::form::XDatabaseParameterListener >& aListener ) override; + virtual void SAL_CALL removeParameterListener( const css::uno::Reference< css::form::XDatabaseParameterListener >& aListener ) override; + + // XModeSelector, base of XFormController + virtual void SAL_CALL setMode( const OUString& aMode ) override; + virtual OUString SAL_CALL getMode( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedModes( ) override; + virtual sal_Bool SAL_CALL supportsMode( const OUString& aMode ) override; + + // XTabController, base of XFormController + virtual void SAL_CALL setModel(const css::uno::Reference< css::awt::XTabControllerModel > & Model) override; + virtual css::uno::Reference< css::awt::XTabControllerModel > SAL_CALL getModel() override; + virtual void SAL_CALL setContainer(const css::uno::Reference< css::awt::XControlContainer > & Container) override; + virtual css::uno::Reference< css::awt::XControlContainer > SAL_CALL getContainer() override; + virtual css::uno::Sequence< css::uno::Reference< css::awt::XControl > > SAL_CALL getControls() override; + virtual void SAL_CALL autoTabOrder() override; + virtual void SAL_CALL activateTabOrder() override; + virtual void SAL_CALL activateFirst() override; + virtual void SAL_CALL activateLast() override; + + // XFrameActionListener + virtual void SAL_CALL frameAction(const css::frame::FrameActionEvent& aEvent) override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + +protected: + virtual ~FormControllerImpl() override; +}; + +SbaXDataBrowserController::FormControllerImpl::FormControllerImpl(SbaXDataBrowserController* _pOwner) + :m_aActivateListeners(_pOwner->getMutex()) + ,m_pOwner(_pOwner) +{ + + OSL_ENSURE(m_pOwner, "SbaXDataBrowserController::FormControllerImpl::FormControllerImpl : invalid Owner !"); +} + +SbaXDataBrowserController::FormControllerImpl::~FormControllerImpl() +{ + +} + +Reference< runtime::XFormOperations > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getFormOperations() +{ + return FormOperations::createWithFormController( m_pOwner->m_xContext, this ); +} + +Reference< css::awt::XControl > SbaXDataBrowserController::FormControllerImpl::getCurrentControl() +{ + return m_pOwner->getBrowserView() ? m_pOwner->getBrowserView()->getGridControl() : Reference< css::awt::XControl > (); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addActivateListener(const Reference< css::form::XFormControllerListener > & l) +{ + m_aActivateListeners.addInterface(l); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeActivateListener(const Reference< css::form::XFormControllerListener > & l) +{ + m_aActivateListeners.removeInterface(l); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addChildController( const Reference< runtime::XFormController >& ) +{ + // not supported + throw IllegalArgumentException( OUString(), *this, 1 ); +} + +Reference< runtime::XFormControllerContext > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getContext() +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::getContext: no support!!" ); + return nullptr; +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::setContext( const Reference< runtime::XFormControllerContext >& /*_context*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::setContext: no support!!" ); +} + +Reference< XInteractionHandler > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getInteractionHandler() +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::getInteractionHandler: no support!!" ); + return nullptr; +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::setInteractionHandler( const Reference< XInteractionHandler >& /*_interactionHandler*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::setInteractionHandler: no support!!" ); +} + +Reference< XInterface > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getParent( ) +{ + // don't have any parent form controllers + return nullptr; +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::setParent( const Reference< XInterface >& /*Parent*/ ) +{ + throw NoSupportException( OUString(), *this ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::dispose( ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::dispose: no, you do *not* want to do this!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addEventListener( const Reference< XEventListener >& /*xListener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addEventListener: no support!!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeEventListener( const Reference< XEventListener >& /*aListener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeEventListener: no support!!" ); +} + +::sal_Int32 SAL_CALL SbaXDataBrowserController::FormControllerImpl::getCount( ) +{ + // no sub controllers, never + return 0; +} + +Any SAL_CALL SbaXDataBrowserController::FormControllerImpl::getByIndex( ::sal_Int32 /*Index*/ ) +{ + // no sub controllers, never + throw IndexOutOfBoundsException( OUString(), *this ); +} + +Type SAL_CALL SbaXDataBrowserController::FormControllerImpl::getElementType( ) +{ + return ::cppu::UnoType< runtime::XFormController >::get(); +} + +sal_Bool SAL_CALL SbaXDataBrowserController::FormControllerImpl::hasElements( ) +{ + // no sub controllers, never + return false; +} + +Reference< XEnumeration > SAL_CALL SbaXDataBrowserController::FormControllerImpl::createEnumeration( ) +{ + return new ::comphelper::OEnumerationByIndex( this ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addModifyListener( const Reference< XModifyListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addModifyListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeModifyListener( const Reference< XModifyListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeModifyListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addConfirmDeleteListener( const Reference< XConfirmDeleteListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addConfirmDeleteListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeConfirmDeleteListener( const Reference< XConfirmDeleteListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeConfirmDeleteListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addSQLErrorListener( const Reference< XSQLErrorListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addSQLErrorListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeSQLErrorListener( const Reference< XSQLErrorListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeSQLErrorListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addRowSetApproveListener( const Reference< XRowSetApproveListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addRowSetApproveListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeRowSetApproveListener( const Reference< XRowSetApproveListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeRowSetApproveListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addDatabaseParameterListener( const Reference< XDatabaseParameterListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addDatabaseParameterListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeDatabaseParameterListener( const Reference< XDatabaseParameterListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeDatabaseParameterListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::addParameterListener( const Reference< XDatabaseParameterListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::addParameterListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::removeParameterListener( const Reference< XDatabaseParameterListener >& /*_Listener*/ ) +{ + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::FormControllerImpl::removeParameterListener: no support!" ); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::setMode( const OUString& _rMode ) +{ + if ( !supportsMode( _rMode ) ) + throw NoSupportException(); +} + +OUString SAL_CALL SbaXDataBrowserController::FormControllerImpl::getMode( ) +{ + return "DataMode"; +} + +Sequence< OUString > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getSupportedModes( ) +{ + Sequence< OUString > aModes { "DataMode" }; + return aModes; +} + +sal_Bool SAL_CALL SbaXDataBrowserController::FormControllerImpl::supportsMode( const OUString& aMode ) +{ + return aMode == "DataMode"; +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::setModel(const Reference< css::awt::XTabControllerModel > & /*Model*/) +{ + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::FormControllerImpl::setModel : invalid call, can't change my model !"); +} + +Reference< css::awt::XTabControllerModel > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getModel() +{ + return Reference< XTabControllerModel >(m_pOwner->getRowSet(), UNO_QUERY); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::setContainer(const Reference< css::awt::XControlContainer > &) +{ + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::FormControllerImpl::setContainer : invalid call, can't change my container !"); +} + +Reference< css::awt::XControlContainer > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getContainer() +{ + if (m_pOwner->getBrowserView()) + return m_pOwner->getBrowserView()->getContainer(); + return Reference< css::awt::XControlContainer > (); +} + +Sequence< Reference< css::awt::XControl > > SAL_CALL SbaXDataBrowserController::FormControllerImpl::getControls() +{ + if (m_pOwner->getBrowserView()) + { + Reference< css::awt::XControl > xGrid = m_pOwner->getBrowserView()->getGridControl(); + return Sequence< Reference< css::awt::XControl > >(&xGrid, 1); + } + return Sequence< Reference< css::awt::XControl > >(); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::autoTabOrder() +{ + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::FormControllerImpl::autoTabOrder : nothing to do (always have only one control) !"); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::activateTabOrder() +{ + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::FormControllerImpl::activateTabOrder : nothing to do (always have only one control) !"); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::activateFirst() +{ + if (m_pOwner->getBrowserView()) + m_pOwner->getBrowserView()->getVclControl()->ActivateCell(); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::activateLast() +{ + if (m_pOwner->getBrowserView()) + m_pOwner->getBrowserView()->getVclControl()->ActivateCell(); +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::frameAction(const css::frame::FrameActionEvent& /*aEvent*/) +{ +} + +void SAL_CALL SbaXDataBrowserController::FormControllerImpl::disposing(const css::lang::EventObject& /*Source*/) +{ + // nothing to do + // we don't add ourself as listener to any broadcasters, so we are not responsible for removing us +} + +// SbaXDataBrowserController +Sequence< Type > SAL_CALL SbaXDataBrowserController::getTypes( ) +{ + return ::comphelper::concatSequences( + SbaXDataBrowserController_Base::getTypes(), + m_pFormControllerImpl->getTypes() + ); +} + +Sequence< sal_Int8 > SAL_CALL SbaXDataBrowserController::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any SAL_CALL SbaXDataBrowserController::queryInterface(const Type& _rType) +{ + // check for our additional interfaces + Any aRet = SbaXDataBrowserController_Base::queryInterface(_rType); + + // check for our aggregate (implementing the XFormController) + if (!aRet.hasValue()) + aRet = m_xFormControllerImpl->queryAggregation(_rType); + + // no more to offer + return aRet; +} + +SbaXDataBrowserController::SbaXDataBrowserController(const Reference< css::uno::XComponentContext >& _rM) + :SbaXDataBrowserController_Base(_rM) + ,m_nRowSetPrivileges(0) + ,m_aAsyncGetCellFocus(LINK(this, SbaXDataBrowserController, OnAsyncGetCellFocus)) + ,m_aAsyncDisplayError( LINK( this, SbaXDataBrowserController, OnAsyncDisplayError ) ) + ,m_sStateSaveRecord(DBA_RES(RID_STR_SAVE_CURRENT_RECORD)) + ,m_sStateUndoRecord(DBA_RES(RID_STR_UNDO_MODIFY_RECORD)) + ,m_sModuleIdentifier( OUString( "com.sun.star.sdb.DataSourceBrowser" ) ) + ,m_pFormControllerImpl(nullptr) + ,m_nFormActionNestingLevel(0) + ,m_bLoadCanceled( false ) + ,m_bCannotSelectUnfiltered( true ) +{ + // create the form controller aggregate + osl_atomic_increment(&m_refCount); + { + m_pFormControllerImpl = new FormControllerImpl(this); + m_xFormControllerImpl = m_pFormControllerImpl; + m_xFormControllerImpl->setDelegator(*this); + } + osl_atomic_decrement(&m_refCount); + + m_aInvalidateClipboard.SetDebugName("dbaui::SbaXDataBrowserController m_aInvalidateClipboard"); + m_aInvalidateClipboard.SetInvokeHandler(LINK(this, SbaXDataBrowserController, OnInvalidateClipboard)); + m_aInvalidateClipboard.SetTimeout(300); +} + +SbaXDataBrowserController::~SbaXDataBrowserController() +{ + // deleteView(); + // release the aggregated form controller + if (m_xFormControllerImpl.is()) + { + Reference< XInterface > xEmpty; + m_xFormControllerImpl->setDelegator(xEmpty); + } + +} + +void SbaXDataBrowserController::startFrameListening( const Reference< XFrame >& _rxFrame ) +{ + SbaXDataBrowserController_Base::startFrameListening( _rxFrame ); + + Reference< XFrameActionListener > xAggListener; + if ( m_xFormControllerImpl.is() ) + m_xFormControllerImpl->queryAggregation( cppu::UnoType<XFrameActionListener>::get() ) >>= xAggListener; + + if ( _rxFrame.is() && xAggListener.is() ) + _rxFrame->addFrameActionListener( xAggListener ); +} + +void SbaXDataBrowserController::stopFrameListening( const Reference< XFrame >& _rxFrame ) +{ + SbaXDataBrowserController_Base::stopFrameListening( _rxFrame ); + + Reference< XFrameActionListener > xAggListener; + if ( m_xFormControllerImpl.is() ) + m_xFormControllerImpl->queryAggregation( cppu::UnoType<XFrameActionListener>::get() ) >>= xAggListener; + + if ( _rxFrame.is() && xAggListener.is() ) + _rxFrame->removeFrameActionListener( xAggListener ); +} + +void SbaXDataBrowserController::onStartLoading( const Reference< XLoadable >& _rxLoadable ) +{ + m_bLoadCanceled = false; + m_bCannotSelectUnfiltered = false; + + Reference< XWarningsSupplier > xWarnings( _rxLoadable, UNO_QUERY ); + if ( xWarnings.is() ) + { + try + { + xWarnings->clearWarnings(); + } + catch(const SQLException& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void SbaXDataBrowserController::impl_checkForCannotSelectUnfiltered( const SQLExceptionInfo& _rError ) +{ + ::connectivity::ErrorCode nErrorCode( connectivity::SQLError::getErrorCode( sdb::ErrorCondition::DATA_CANNOT_SELECT_UNFILTERED ) ); + if ( static_cast<const SQLException*>(_rError)->ErrorCode == nErrorCode ) + { + m_bCannotSelectUnfiltered = true; + InvalidateFeature( ID_BROWSER_FILTERCRIT ); + } +} + +bool SbaXDataBrowserController::reloadForm( const Reference< XLoadable >& _rxLoadable ) +{ + weld::WaitObject aWO(getFrameWeld()); + + onStartLoading( _rxLoadable ); + + FormErrorHelper aReportError(this); + if (_rxLoadable->isLoaded()) + _rxLoadable->reload(); + else + _rxLoadable->load(); + + m_xParser.clear(); + const Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ESCAPE_PROCESSING))) + xFormSet->getPropertyValue(PROPERTY_SINGLESELECTQUERYCOMPOSER) >>= m_xParser; +#if 0 + { + const Reference< XPropertySet > xRowSetProps( getRowSet(), UNO_QUERY ); + const Reference< XSingleSelectQueryAnalyzer > xAnalyzer( xRowSetProps->getPropertyValue( PROPERTY_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY ); + if ( xAnalyzer.is() ) + { + const Reference< XIndexAccess > xOrderColumns( xAnalyzer->getOrderColumns(), UNO_SET_THROW ); + const sal_Int32 nOrderColumns( xOrderColumns->getCount() ); + for ( sal_Int32 c=0; c<nOrderColumns; ++c ) + { + const Reference< XPropertySet > xOrderColumn( xOrderColumns->getByIndex(c), UNO_QUERY_THROW ); + OUString sColumnName; + OSL_VERIFY( xOrderColumn->getPropertyValue( PROPERTY_NAME ) >>= sColumnName); + OUString sTableName; + OSL_VERIFY( xOrderColumn->getPropertyValue( PROPERTY_TABLENAME ) >>= sTableName); + (void)sColumnName; + (void)sTableName; + } + } + } +#endif + + Reference< XWarningsSupplier > xWarnings( _rxLoadable, UNO_QUERY ); + if ( xWarnings.is() ) + { + try + { + SQLExceptionInfo aInfo( xWarnings->getWarnings() ); + if ( aInfo.isValid() ) + { + showError( aInfo ); + impl_checkForCannotSelectUnfiltered( aInfo ); + } + } + catch(const SQLException& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + return _rxLoadable->isLoaded(); +} + +void SbaXDataBrowserController::initFormatter() +{ + // create a formatter working with the connections format supplier + Reference< css::util::XNumberFormatsSupplier > xSupplier(::dbtools::getNumberFormats(::dbtools::getConnection(m_xRowSet), true, getORB())); + + if(xSupplier.is()) + { + // create a new formatter + m_xFormatter.set(util::NumberFormatter::create(getORB()), UNO_QUERY_THROW); + m_xFormatter->attachNumberFormatsSupplier(xSupplier); + } + else // clear the formatter + m_xFormatter = nullptr; +} + +void SbaXDataBrowserController::describeSupportedFeatures() +{ + SbaXDataBrowserController_Base::describeSupportedFeatures(); + implDescribeSupportedFeature( ".uno:FormSlots/undoRecord", ID_BROWSER_UNDORECORD, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:FormController/undoRecord", ID_BROWSER_UNDORECORD, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:RecUndo", ID_BROWSER_UNDORECORD, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:FormSlots/saveRecord", ID_BROWSER_SAVERECORD, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:FormController/saveRecord", ID_BROWSER_SAVERECORD, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:RecSave", ID_BROWSER_SAVERECORD, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:Save", ID_BROWSER_SAVERECORD, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:RecSearch", SID_FM_SEARCH, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:AutoFilter", SID_FM_AUTOFILTER, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:Refresh", SID_FM_REFRESH, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:OrderCrit", SID_FM_ORDERCRIT, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:RemoveFilterSort", SID_FM_REMOVE_FILTER_SORT,CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:FormFiltered", SID_FM_FORM_FILTERED, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:FilterCrit", SID_FM_FILTERCRIT, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:Sortup", ID_BROWSER_SORTUP, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:SortDown", ID_BROWSER_SORTDOWN, CommandGroup::CONTROLS ); + implDescribeSupportedFeature( ".uno:FormSlots/deleteRecord", SID_FM_DELETEROWS, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:FormSlots/insertRecord", ID_BROWSER_INSERT_ROW, CommandGroup::INSERT ); +} + +bool SbaXDataBrowserController::Construct(vcl::Window* pParent) +{ + // create/initialize the form and the grid model + m_xRowSet = CreateForm(); + if (!m_xRowSet.is()) + return false; + + m_xColumnsSupplier.set(m_xRowSet,UNO_QUERY); + m_xLoadable.set(m_xRowSet,UNO_QUERY); + + Reference< XPropertySet > xFormProperties( m_xRowSet, UNO_QUERY ); + if ( !InitializeForm( xFormProperties ) ) + return false; + + m_xGridModel = CreateGridModel(); + if (!m_xGridModel.is()) + return false; + + // set the formatter if available + initFormatter(); + + // we want to have a grid with a "flat" border + Reference< XPropertySet > xGridSet(m_xGridModel, UNO_QUERY); + if ( xGridSet.is() ) + xGridSet->setPropertyValue(PROPERTY_BORDER, makeAny(sal_Int16(2))); + + + // marry them + Reference< css::container::XNameContainer > xNameCont(m_xRowSet, UNO_QUERY); + { + OUString sText(DBA_RES(STR_DATASOURCE_GRIDCONTROL_NAME)); + xNameCont->insertByName(sText, makeAny(m_xGridModel)); + } + + // create the view + setView( VclPtr<UnoDataBrowserView>::Create( pParent, *this, getORB() ) ); + if (!getBrowserView()) + return false; + + // late construction + bool bSuccess = false; + try + { + getBrowserView()->Construct(getControlModel()); + bSuccess = true; + } + catch(SQLException&) + { + } + catch(Exception&) + { + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::Construct : the construction of UnoDataBrowserView failed !"); + } + + if (!bSuccess) + { + // deleteView(); + return false; + } + + // now that we have a view we can create the clipboard listener + m_aSystemClipboard = TransferableDataHelper::CreateFromSystemClipboard( getView() ); + m_aSystemClipboard.StartClipboardListening( ); + + m_pClipboardNotifier = new TransferableClipboardListener( LINK( this, SbaXDataBrowserController, OnClipboardChanged ) ); + m_pClipboardNotifier->AddListener( getView() ); + + // this call create the toolbox + SbaXDataBrowserController_Base::Construct(pParent); + + getBrowserView()->Show(); + + // set the callbacks for the grid control + SbaGridControl* pVclGrid = getBrowserView()->getVclControl(); + OSL_ENSURE(pVclGrid, "SbaXDataBrowserController::Construct : have no VCL control !"); + pVclGrid->SetMasterListener(this); + + // add listeners... + + // ... to the form model + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (xFormSet.is()) + { + xFormSet->addPropertyChangeListener(PROPERTY_ISNEW, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_ISMODIFIED, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_ROWCOUNT, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_ACTIVECOMMAND, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_ORDER, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_FILTER, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_HAVING_CLAUSE, static_cast<XPropertyChangeListener*>(this)); + xFormSet->addPropertyChangeListener(PROPERTY_APPLYFILTER, static_cast<XPropertyChangeListener*>(this)); + } + Reference< css::sdb::XSQLErrorBroadcaster > xFormError(getRowSet(), UNO_QUERY); + if (xFormError.is()) + xFormError->addSQLErrorListener(static_cast<css::sdb::XSQLErrorListener*>(this)); + + if (m_xLoadable.is()) + m_xLoadable->addLoadListener(this); + + Reference< css::form::XDatabaseParameterBroadcaster > xFormParameter(getRowSet(), UNO_QUERY); + if (xFormParameter.is()) + xFormParameter->addParameterListener(static_cast<css::form::XDatabaseParameterListener*>(this)); + + addModelListeners(getControlModel()); + addControlListeners(getBrowserView()->getGridControl()); + + // load the form + return LoadForm(); +} + +bool SbaXDataBrowserController::LoadForm() +{ + reloadForm( m_xLoadable ); + return true; +} + +void SbaXDataBrowserController::AddColumnListener(const Reference< XPropertySet > & /*xCol*/) +{ + // we're not interested in any column properties... +} + +void SbaXDataBrowserController::RemoveColumnListener(const Reference< XPropertySet > & /*xCol*/) +{ +} + +Reference< XRowSet > SbaXDataBrowserController::CreateForm() +{ + return Reference< XRowSet > ( + getORB()->getServiceManager()->createInstanceWithContext("com.sun.star.form.component.Form", getORB()), + UNO_QUERY); +} + +Reference< css::form::XFormComponent > SbaXDataBrowserController::CreateGridModel() +{ + return Reference< css::form::XFormComponent > ( + getORB()->getServiceManager()->createInstanceWithContext("com.sun.star.form.component.GridControl", getORB()), + UNO_QUERY); +} + +void SbaXDataBrowserController::addModelListeners(const Reference< css::awt::XControlModel > & _xGridControlModel) +{ + // ... all the grid columns + addColumnListeners(_xGridControlModel); + + // (we are interested in all columns the grid has (and only in these) so we have to listen to the container, too) + Reference< css::container::XContainer > xColContainer(_xGridControlModel, UNO_QUERY); + if (xColContainer.is()) + xColContainer->addContainerListener(static_cast<css::container::XContainerListener*>(this)); + + Reference< css::form::XReset > xReset(_xGridControlModel, UNO_QUERY); + if (xReset.is()) + xReset->addResetListener(static_cast<css::form::XResetListener*>(this)); +} + +void SbaXDataBrowserController::removeModelListeners(const Reference< XControlModel > & _xGridControlModel) +{ + // every single column model + Reference< XIndexContainer > xColumns(_xGridControlModel, UNO_QUERY); + if (xColumns.is()) + { + sal_Int32 nCount = xColumns->getCount(); + for (sal_Int32 i=0; i < nCount; ++i) + { + Reference< XPropertySet > xCol(xColumns->getByIndex(i),UNO_QUERY); + RemoveColumnListener(xCol); + } + } + + Reference< XContainer > xColContainer(_xGridControlModel, UNO_QUERY); + if (xColContainer.is()) + xColContainer->removeContainerListener( this ); + + Reference< XReset > xReset(_xGridControlModel, UNO_QUERY); + if (xReset.is()) + xReset->removeResetListener( this ); +} + +void SbaXDataBrowserController::addControlListeners(const Reference< css::awt::XControl > & _xGridControl) +{ + // to ge the 'modified' for the current cell + Reference< XModifyBroadcaster > xBroadcaster(getBrowserView()->getGridControl(), UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->addModifyListener(static_cast<XModifyListener*>(this)); + + // introduce ourself as dispatch provider for the grid + Reference< XDispatchProviderInterception > xInterception(getBrowserView()->getGridControl(), UNO_QUERY); + if (xInterception.is()) + xInterception->registerDispatchProviderInterceptor(static_cast<XDispatchProviderInterceptor*>(this)); + + // add as focus listener to the control (needed for the form controller functionality) + Reference< XWindow > xWindow(_xGridControl, UNO_QUERY); + if (xWindow.is()) + xWindow->addFocusListener(this); +} + +void SbaXDataBrowserController::removeControlListeners(const Reference< css::awt::XControl > & _xGridControl) +{ + Reference< XModifyBroadcaster > xBroadcaster(_xGridControl, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removeModifyListener(static_cast<XModifyListener*>(this)); + + Reference< XDispatchProviderInterception > xInterception(_xGridControl, UNO_QUERY); + if (xInterception.is()) + xInterception->releaseDispatchProviderInterceptor(static_cast<XDispatchProviderInterceptor*>(this)); + + Reference< XWindow > xWindow(_xGridControl, UNO_QUERY); + if (xWindow.is()) + xWindow->removeFocusListener(this); +} + +void SAL_CALL SbaXDataBrowserController::focusGained(const FocusEvent& /*e*/) +{ + // notify our activate listeners (registered on the form controller aggregate) + EventObject aEvt(*this); + ::comphelper::OInterfaceIteratorHelper2 aIter(m_pFormControllerImpl->m_aActivateListeners); + while (aIter.hasMoreElements()) + static_cast<XFormControllerListener*>(aIter.next())->formActivated(aEvt); +} + +void SAL_CALL SbaXDataBrowserController::focusLost(const FocusEvent& e) +{ + // some general checks + if (!getBrowserView() || !getBrowserView()->getGridControl().is()) + return; + Reference< XVclWindowPeer > xMyGridPeer(getBrowserView()->getGridControl()->getPeer(), UNO_QUERY); + if (!xMyGridPeer.is()) + return; + Reference< XWindowPeer > xNextControlPeer(e.NextFocus, UNO_QUERY); + if (!xNextControlPeer.is()) + return; + + // don't do a notification if it remains in the family (i.e. a child of the grid control gets the focus) + if (xMyGridPeer->isChild(xNextControlPeer)) + return; + + if (xMyGridPeer == xNextControlPeer) + return; + + // notify the listeners that the "form" we represent has been deactivated + EventObject aEvt(*this); + ::comphelper::OInterfaceIteratorHelper2 aIter(m_pFormControllerImpl->m_aActivateListeners); + while (aIter.hasMoreElements()) + static_cast<XFormControllerListener*>(aIter.next())->formDeactivated(aEvt); + + // commit the changes of the grid control (as we're deactivated) + Reference< XBoundComponent > xCommitable(getBrowserView()->getGridControl(), UNO_QUERY); + if (xCommitable.is()) + xCommitable->commit(); + else + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::focusLost : why is my control not committable?"); +} + +void SbaXDataBrowserController::disposingFormModel(const css::lang::EventObject& Source) +{ + Reference< XPropertySet > xSourceSet(Source.Source, UNO_QUERY); + if (xSourceSet.is()) + { + xSourceSet->removePropertyChangeListener(PROPERTY_ISNEW, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_ISMODIFIED, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_ROWCOUNT, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_ACTIVECOMMAND, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_ORDER, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_FILTER, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_HAVING_CLAUSE, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_APPLYFILTER, static_cast<XPropertyChangeListener*>(this)); + } + + Reference< css::sdb::XSQLErrorBroadcaster > xFormError(Source.Source, UNO_QUERY); + if (xFormError.is()) + xFormError->removeSQLErrorListener(static_cast<css::sdb::XSQLErrorListener*>(this)); + + if (m_xLoadable.is()) + m_xLoadable->removeLoadListener(this); + + Reference< css::form::XDatabaseParameterBroadcaster > xFormParameter(Source.Source, UNO_QUERY); + if (xFormParameter.is()) + xFormParameter->removeParameterListener(static_cast<css::form::XDatabaseParameterListener*>(this)); +} + +void SbaXDataBrowserController::disposingColumnModel(const css::lang::EventObject& Source) +{ + RemoveColumnListener(Reference< XPropertySet > (Source.Source, UNO_QUERY)); +} + +void SbaXDataBrowserController::disposing(const EventObject& Source) +{ + // if it's a component other than our aggregate, forward it to the aggregate + if ( m_xFormControllerImpl != Source.Source ) + { + Reference< XEventListener > xAggListener; + m_xFormControllerImpl->queryAggregation( cppu::UnoType<decltype(xAggListener)>::get() ) >>= xAggListener; + if ( xAggListener.is( )) + xAggListener->disposing( Source ); + } + + // is it the grid control ? + if (getBrowserView()) + { + Reference< css::awt::XControl > xSourceControl(Source.Source, UNO_QUERY); + if (xSourceControl == getBrowserView()->getGridControl()) + removeControlListeners(getBrowserView()->getGridControl()); + } + + // its model (the container of the columns) ? + if (getControlModel() == Source.Source) + removeModelListeners(getControlModel()); + + // the form's model ? + if (getRowSet() == Source.Source) + disposingFormModel(Source); + + // from a single column model ? + Reference< XPropertySet > xSourceSet(Source.Source, UNO_QUERY); + if (xSourceSet.is()) + { + Reference< XPropertySetInfo > xInfo = xSourceSet->getPropertySetInfo(); + // we assume that columns have a Width property and all other sets we are listening to don't have + if (xInfo->hasPropertyByName(PROPERTY_WIDTH)) + disposingColumnModel(Source); + } + SbaXDataBrowserController_Base::OGenericUnoController::disposing( Source ); +} + +void SAL_CALL SbaXDataBrowserController::setIdentifier( const OUString& Identifier ) +{ + ::osl::MutexGuard aGuard( getMutex() ); + m_sModuleIdentifier = Identifier; +} + +OUString SAL_CALL SbaXDataBrowserController::getIdentifier( ) +{ + ::osl::MutexGuard aGuard( getMutex() ); + return m_sModuleIdentifier; +} + +void SbaXDataBrowserController::propertyChange(const PropertyChangeEvent& evt) +{ + Reference< XPropertySet > xSource(evt.Source, UNO_QUERY); + if (!xSource.is()) + return; + + SolarMutexGuard aGuard; + // the IsModified changed to sal_False ? + if ( evt.PropertyName == PROPERTY_ISMODIFIED + && !::comphelper::getBOOL(evt.NewValue) + ) + { // -> the current field isn't modified anymore, too + setCurrentModified( false ); + } + + // switching to a new record ? + if ( evt.PropertyName == PROPERTY_ISNEW + && ::comphelper::getBOOL(evt.NewValue) + ) + { + if (::comphelper::getINT32(xSource->getPropertyValue(PROPERTY_ROWCOUNT)) == 0) + // if we're switching to a new record and didn't have any records before we need to invalidate + // all slots (as the cursor was invalid before the mode change and so the slots were disabled) + InvalidateAll(); + } + + if (evt.PropertyName == PROPERTY_FILTER) + { + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + } + else if (evt.PropertyName == PROPERTY_HAVING_CLAUSE) + { + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + } + else if (evt.PropertyName == PROPERTY_ORDER) + { + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + } + + // a new record count ? -> may be our search availability has changed + if (evt.PropertyName == PROPERTY_ROWCOUNT) + { + sal_Int32 nNewValue = 0, nOldValue = 0; + evt.NewValue >>= nNewValue; + evt.OldValue >>= nOldValue; + if((nOldValue == 0 && nNewValue != 0) || (nOldValue != 0 && nNewValue == 0)) + InvalidateAll(); + } +} + +void SbaXDataBrowserController::modified(const css::lang::EventObject& /*aEvent*/) +{ + setCurrentModified( true ); +} + +void SbaXDataBrowserController::elementInserted(const css::container::ContainerEvent& evt) +{ + OSL_ENSURE(Reference< XInterface >(evt.Source, UNO_QUERY).get() == Reference< XInterface >(getControlModel(), UNO_QUERY).get(), + "SbaXDataBrowserController::elementInserted: where did this come from (not from the grid model)?!"); + Reference< XPropertySet > xNewColumn(evt.Element,UNO_QUERY); + if ( xNewColumn.is() ) + AddColumnListener(xNewColumn); +} + +void SbaXDataBrowserController::elementRemoved(const css::container::ContainerEvent& evt) +{ + OSL_ENSURE(Reference< XInterface >(evt.Source, UNO_QUERY).get() == Reference< XInterface >(getControlModel(), UNO_QUERY).get(), + "SbaXDataBrowserController::elementRemoved: where did this come from (not from the grid model)?!"); + Reference< XPropertySet > xOldColumn(evt.Element,UNO_QUERY); + if ( xOldColumn.is() ) + RemoveColumnListener(xOldColumn); +} + +void SbaXDataBrowserController::elementReplaced(const css::container::ContainerEvent& evt) +{ + OSL_ENSURE(Reference< XInterface >(evt.Source, UNO_QUERY).get() == Reference< XInterface >(getControlModel(), UNO_QUERY).get(), + "SbaXDataBrowserController::elementReplaced: where did this come from (not from the grid model)?!"); + Reference< XPropertySet > xOldColumn(evt.ReplacedElement,UNO_QUERY); + if ( xOldColumn.is() ) + RemoveColumnListener(xOldColumn); + + Reference< XPropertySet > xNewColumn(evt.Element,UNO_QUERY); + if ( xNewColumn.is() ) + AddColumnListener(xNewColumn); +} + +sal_Bool SbaXDataBrowserController::suspend(sal_Bool /*bSuspend*/) +{ + m_aAsyncGetCellFocus.CancelCall(); + m_aAsyncDisplayError.CancelCall(); + m_aAsyncInvalidateAll.CancelCall(); + + bool bSuccess = SaveModified(); + return bSuccess; +} + +void SbaXDataBrowserController::disposing() +{ + // the base class + SbaXDataBrowserController_Base::OGenericUnoController::disposing(); + + // the data source + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (xFormSet.is()) + { + xFormSet->removePropertyChangeListener(PROPERTY_ISNEW, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_ISMODIFIED, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_ROWCOUNT, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_ACTIVECOMMAND, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_ORDER, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_FILTER, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_HAVING_CLAUSE, static_cast<XPropertyChangeListener*>(this)); + xFormSet->removePropertyChangeListener(PROPERTY_APPLYFILTER, static_cast<XPropertyChangeListener*>(this)); + } + + Reference< css::sdb::XSQLErrorBroadcaster > xFormError(getRowSet(), UNO_QUERY); + if (xFormError.is()) + xFormError->removeSQLErrorListener(static_cast<css::sdb::XSQLErrorListener*>(this)); + + if (m_xLoadable.is()) + m_xLoadable->removeLoadListener(this); + + Reference< css::form::XDatabaseParameterBroadcaster > xFormParameter(getRowSet(), UNO_QUERY); + if (xFormParameter.is()) + xFormParameter->removeParameterListener(static_cast<css::form::XDatabaseParameterListener*>(this)); + + removeModelListeners(getControlModel()); + + if ( getView() && m_pClipboardNotifier.is() ) + { + m_pClipboardNotifier->ClearCallbackLink(); + m_pClipboardNotifier->RemoveListener( getView() ); + m_pClipboardNotifier.clear(); + } + + if (getBrowserView()) + { + removeControlListeners(getBrowserView()->getGridControl()); + // don't delete explicitly, this is done by the owner (and user) of this controller (me hopes ...) + clearView(); + } + + if(m_aInvalidateClipboard.IsActive()) + m_aInvalidateClipboard.Stop(); + + // dispose the row set + try + { + ::comphelper::disposeComponent(m_xRowSet); + + m_xRowSet = nullptr; + m_xColumnsSupplier = nullptr; + m_xLoadable = nullptr; + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_xParser.clear(); + // don't dispose, just reset - it's owned by the RowSet +} + +void SbaXDataBrowserController::frameAction(const css::frame::FrameActionEvent& aEvent) +{ + ::osl::MutexGuard aGuard( getMutex() ); + + SbaXDataBrowserController_Base::frameAction( aEvent ); + + if ( aEvent.Source != getFrame() ) + return; + + switch ( aEvent.Action ) + { + case FrameAction_FRAME_ACTIVATED: + case FrameAction_FRAME_UI_ACTIVATED: + // ensure that the active cell (if any) has the focus + m_aAsyncGetCellFocus.Call(); + // start the clipboard timer + if (getBrowserView() && getBrowserView()->getVclControl() && !m_aInvalidateClipboard.IsActive()) + { + m_aInvalidateClipboard.Start(); + OnInvalidateClipboard( nullptr ); + } + break; + case FrameAction_FRAME_DEACTIVATING: + case FrameAction_FRAME_UI_DEACTIVATING: + // stop the clipboard invalidator + if (getBrowserView() && getBrowserView()->getVclControl() && m_aInvalidateClipboard.IsActive()) + { + m_aInvalidateClipboard.Stop(); + OnInvalidateClipboard( nullptr ); + } + // remove the "get cell focus"-event + m_aAsyncGetCellFocus.CancelCall(); + break; + default: + break; + } +} + +IMPL_LINK_NOARG( SbaXDataBrowserController, OnAsyncDisplayError, void*, void ) +{ + if ( m_aCurrentError.isValid() ) + { + OSQLMessageBox aDlg(getFrameWeld(), m_aCurrentError); + aDlg.run(); + } +} + +void SbaXDataBrowserController::errorOccured(const css::sdb::SQLErrorEvent& aEvent) +{ + ::osl::MutexGuard aGuard( getMutex() ); + + SQLExceptionInfo aInfo( aEvent.Reason ); + if ( !aInfo.isValid() ) + return; + + if ( m_nFormActionNestingLevel ) + { + OSL_ENSURE( !m_aCurrentError.isValid(), "SbaXDataBrowserController::errorOccurred: can handle one error per transaction only!" ); + m_aCurrentError = aInfo; + } + else + { + m_aCurrentError = aInfo; + m_aAsyncDisplayError.Call(); + } +} + +sal_Bool SbaXDataBrowserController::approveParameter(const css::form::DatabaseParameterEvent& aEvent) +{ + if (aEvent.Source != getRowSet()) + { + // not my data source -> allow anything + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::approveParameter : invalid event source !"); + return true; + } + + Reference< css::container::XIndexAccess > xParameters = aEvent.Parameters; + SolarMutexGuard aSolarGuard; + + // default handling: instantiate an interaction handler and let it handle the parameter request + try + { + // two continuations allowed: OK and Cancel + OParameterContinuation* pParamValues = new OParameterContinuation; + OInteractionAbort* pAbort = new OInteractionAbort; + // the request + ParametersRequest aRequest; + aRequest.Parameters = xParameters; + aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY)); + OInteractionRequest* pParamRequest = new OInteractionRequest(makeAny(aRequest)); + Reference< XInteractionRequest > xParamRequest(pParamRequest); + // some knittings + pParamRequest->addContinuation(pParamValues); + pParamRequest->addContinuation(pAbort); + + // create the handler, let it handle the request + Reference< XInteractionHandler2 > xHandler(InteractionHandler::createWithParent(getORB(), getComponentWindow())); + xHandler->handle(xParamRequest); + + if (!pParamValues->wasSelected()) + { // canceled + setLoadingCancelled(); + return false; + } + + // transfer the values into the parameter supplier + Sequence< PropertyValue > aFinalValues = pParamValues->getValues(); + if (aFinalValues.getLength() != aRequest.Parameters->getCount()) + { + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::approveParameter: the InteractionHandler returned nonsense!"); + setLoadingCancelled(); + return false; + } + const PropertyValue* pFinalValues = aFinalValues.getConstArray(); + for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues) + { + Reference< XPropertySet > xParam( + aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY); + OSL_ENSURE(xParam.is(), "SbaXDataBrowserController::approveParameter: one of the parameters is no property set!"); + if (xParam.is()) + { +#ifdef DBG_UTIL + OUString sName; + xParam->getPropertyValue(PROPERTY_NAME) >>= sName; + OSL_ENSURE(sName == pFinalValues->Name, "SbaXDataBrowserController::approveParameter: suspicious value names!"); +#endif + try { xParam->setPropertyValue(PROPERTY_VALUE, pFinalValues->Value); } + catch(Exception&) + { + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::approveParameter: setting one of the properties failed!"); + } + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return true; +} + +sal_Bool SbaXDataBrowserController::approveReset(const css::lang::EventObject& /*rEvent*/) +{ + return true; +} + +void SbaXDataBrowserController::resetted(const css::lang::EventObject& rEvent) +{ + OSL_ENSURE(rEvent.Source == getControlModel(), "SbaXDataBrowserController::resetted : where did this come from ?"); + setCurrentModified( false ); +} + +sal_Bool SbaXDataBrowserController::confirmDelete(const css::sdb::RowChangeEvent& /*aEvent*/) +{ + std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(getFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + DBA_RES(STR_QUERY_BRW_DELETE_ROWS))); + if (xQuery->run() != RET_YES) + return false; + return true; +} + +FeatureState SbaXDataBrowserController::GetState(sal_uInt16 nId) const +{ + FeatureState aReturn; + // (disabled automatically) + + try + { + // no chance without a view + if (!getBrowserView() || !getBrowserView()->getVclControl()) + return aReturn; + + switch (nId) + { + case ID_BROWSER_REMOVEFILTER: + if (!m_xParser.is()) + { + aReturn.bEnabled = false; + return aReturn; + } + // any filter or sort order set ? + aReturn.bEnabled = m_xParser->getFilter().getLength() || m_xParser->getHavingClause().getLength() || m_xParser->getOrder().getLength(); + return aReturn; + } + // no chance without valid models + if (isValid() && !isValidCursor()) + return aReturn; + + switch (nId) + { + case ID_BROWSER_SEARCH: + { + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + sal_Int32 nCount = ::comphelper::getINT32(xFormSet->getPropertyValue(PROPERTY_ROWCOUNT)); + aReturn.bEnabled = nCount != 0; + } + break; + case ID_BROWSER_INSERT_ROW: + { + // check if it is available + bool bInsertPrivilege = ( m_nRowSetPrivileges & Privilege::INSERT) != 0; + bool bAllowInsertions = true; + try + { + Reference< XPropertySet > xRowSetProps( getRowSet(), UNO_QUERY_THROW ); + OSL_VERIFY( xRowSetProps->getPropertyValue("AllowInserts") >>= bAllowInsertions ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + aReturn.bEnabled = bInsertPrivilege && bAllowInsertions; + } + break; + case SID_FM_DELETEROWS: + { + // check if it is available + bool bDeletePrivilege = ( m_nRowSetPrivileges & Privilege::INSERT) != 0; + bool bAllowDeletions = true; + sal_Int32 nRowCount = 0; + bool bInsertionRow = false; + try + { + Reference< XPropertySet > xRowSetProps( getRowSet(), UNO_QUERY_THROW ); + OSL_VERIFY( xRowSetProps->getPropertyValue("AllowDeletes") >>= bAllowDeletions ); + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_ROWCOUNT ) >>= nRowCount ); + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_ISNEW ) >>= bInsertionRow ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + aReturn.bEnabled = bDeletePrivilege && bAllowDeletions && ( nRowCount != 0 ) && !bInsertionRow; + } + break; + + case ID_BROWSER_COPY: + if ( getBrowserView()->getVclControl()->GetSelectRowCount() ) + { + aReturn.bEnabled = m_aCurrentFrame.isActive(); + break; + } + [[fallthrough]]; + case ID_BROWSER_PASTE: + case ID_BROWSER_CUT: + { + CellControllerRef xCurrentController = getBrowserView()->getVclControl()->Controller(); + if (xCurrentController.is() && nullptr != dynamic_cast< const EditCellController* >(xCurrentController.get())) + { + Edit& rEdit = static_cast<Edit&>(xCurrentController->GetWindow()); + bool bHasLen = (rEdit.GetSelection().Len() != 0); + bool bIsReadOnly = rEdit.IsReadOnly(); + switch (nId) + { + case ID_BROWSER_CUT: aReturn.bEnabled = m_aCurrentFrame.isActive() && bHasLen && !bIsReadOnly; break; + case SID_COPY : aReturn.bEnabled = m_aCurrentFrame.isActive() && bHasLen; break; + case ID_BROWSER_PASTE: + aReturn.bEnabled = m_aCurrentFrame.isActive() && !bIsReadOnly; + if(aReturn.bEnabled) + { + aReturn.bEnabled = IsFormatSupported( m_aSystemClipboard.GetDataFlavorExVector(), SotClipboardFormatId::STRING ); + } + break; + } + } + } + break; + + case ID_BROWSER_SORTUP: + case ID_BROWSER_SORTDOWN: + case ID_BROWSER_AUTOFILTER: + { + // a native statement can't be filtered or sorted + const Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if ( !::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ESCAPE_PROCESSING)) || !m_xParser.is() ) + break; + + Reference< XPropertySet > xCurrentField = getBoundField(); + if (!xCurrentField.is()) + break; + + aReturn.bEnabled = ::comphelper::getBOOL(xCurrentField->getPropertyValue(PROPERTY_ISSEARCHABLE)); + const Reference< XRowSet > xRow = getRowSet(); + aReturn.bEnabled = aReturn.bEnabled + && xRow.is() + && !xRow->isBeforeFirst() + && !xRow->isAfterLast() + && !xRow->rowDeleted() + && ( ::comphelper::getINT32( xFormSet->getPropertyValue( PROPERTY_ROWCOUNT ) ) != 0 ); + } + break; + + case ID_BROWSER_FILTERCRIT: + if ( m_bCannotSelectUnfiltered && m_xParser.is() ) + { + aReturn.bEnabled = true; + break; + } + [[fallthrough]]; + case ID_BROWSER_ORDERCRIT: + { + const Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if ( !::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ESCAPE_PROCESSING)) || !m_xParser.is() ) + break; + + aReturn.bEnabled = getRowSet().is() + && ( ::comphelper::getINT32( xFormSet->getPropertyValue( PROPERTY_ROWCOUNT ) ) != 0 ); + } + break; + + case ID_BROWSER_REFRESH: + aReturn.bEnabled = true; + break; + + case ID_BROWSER_REDO: + aReturn.bEnabled = false; // simply forget it ;). no redo possible. + break; + + case ID_BROWSER_UNDORECORD: + case ID_BROWSER_SAVERECORD: + { + if (!m_bCurrentlyModified) + { + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (xFormSet.is()) + aReturn.bEnabled = ::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ISMODIFIED)); + } + else + aReturn.bEnabled = true; + + aReturn.sTitle = (ID_BROWSER_UNDORECORD == nId) ? m_sStateUndoRecord : m_sStateSaveRecord; + } + break; + case ID_BROWSER_EDITDOC: + { + // check if it is available + Reference< XPropertySet > xDataSourceSet(getRowSet(), UNO_QUERY); + if (!xDataSourceSet.is()) + break; // no datasource -> no edit mode + + sal_Int32 nDataSourcePrivileges = ::comphelper::getINT32(xDataSourceSet->getPropertyValue(PROPERTY_PRIVILEGES)); + bool bInsertAllowedAndPossible = ((nDataSourcePrivileges & css::sdbcx::Privilege::INSERT) != 0) && ::comphelper::getBOOL(xDataSourceSet->getPropertyValue("AllowInserts")); + bool bUpdateAllowedAndPossible = ((nDataSourcePrivileges & css::sdbcx::Privilege::UPDATE) != 0) && ::comphelper::getBOOL(xDataSourceSet->getPropertyValue("AllowUpdates")); + bool bDeleteAllowedAndPossible = ((nDataSourcePrivileges & css::sdbcx::Privilege::DELETE) != 0) && ::comphelper::getBOOL(xDataSourceSet->getPropertyValue("AllowDeletes")); + if (!bInsertAllowedAndPossible && !bUpdateAllowedAndPossible && !bDeleteAllowedAndPossible) + break; // no insert/update/delete -> no edit mode + + if (!isValidCursor() || !isLoaded()) + break; // no cursor -> no edit mode + + aReturn.bEnabled = true; + + DbGridControlOptions nGridMode = getBrowserView()->getVclControl()->GetOptions(); + aReturn.bChecked = nGridMode > DbGridControlOptions::Readonly; + } + break; + case ID_BROWSER_FILTERED: + { + aReturn.bEnabled = false; + Reference< XPropertySet > xActiveSet(getRowSet(), UNO_QUERY); + OUString aFilter = ::comphelper::getString(xActiveSet->getPropertyValue(PROPERTY_FILTER)); + OUString aHaving = ::comphelper::getString(xActiveSet->getPropertyValue(PROPERTY_HAVING_CLAUSE)); + if ( !(aFilter.isEmpty() && aHaving.isEmpty()) ) + { + xActiveSet->getPropertyValue( PROPERTY_APPLYFILTER ) >>= aReturn.bChecked; + aReturn.bEnabled = true; + } + else + { + aReturn.bChecked = false; + aReturn.bEnabled = false; + } + } + break; + default: + return SbaXDataBrowserController_Base::GetState(nId); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return aReturn; +} + +void SbaXDataBrowserController::applyParserOrder(const OUString& _rOldOrder,const Reference< XSingleSelectQueryComposer >& _xParser) +{ + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (!m_xLoadable.is()) + { + SAL_WARN("dbaccess.ui","SbaXDataBrowserController::applyParserOrder: invalid row set!"); + return; + } + + sal_uInt16 nPos = getCurrentColumnPosition(); + bool bSuccess = false; + try + { + xFormSet->setPropertyValue(PROPERTY_ORDER, makeAny(_xParser->getOrder())); + bSuccess = reloadForm(m_xLoadable); + } + catch(Exception&) + { + } + + if (!bSuccess) + { + xFormSet->setPropertyValue(PROPERTY_ORDER, makeAny(_rOldOrder)); + + try + { + if (loadingCancelled() || !reloadForm(m_xLoadable)) + criticalFail(); + } + catch(Exception&) + { + criticalFail(); + } + InvalidateAll(); + } + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + + setCurrentColumnPosition(nPos); +} + +void SbaXDataBrowserController::applyParserFilter(const OUString& _rOldFilter, bool _bOldFilterApplied,const ::OUString& _sOldHaving,const Reference< XSingleSelectQueryComposer >& _xParser) +{ + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (!m_xLoadable.is()) + { + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::applyParserFilter: invalid row set!"); + return; + } + + sal_uInt16 nPos = getCurrentColumnPosition(); + + bool bSuccess = false; + try + { + FormErrorHelper aError(this); + xFormSet->setPropertyValue(PROPERTY_FILTER, makeAny(_xParser->getFilter())); + xFormSet->setPropertyValue(PROPERTY_HAVING_CLAUSE, makeAny(_xParser->getHavingClause())); + xFormSet->setPropertyValue(PROPERTY_APPLYFILTER, css::uno::Any(true)); + + bSuccess = reloadForm(m_xLoadable); + } + catch(Exception&) + { + } + + if (!bSuccess) + { + xFormSet->setPropertyValue(PROPERTY_FILTER, makeAny(_rOldFilter)); + xFormSet->setPropertyValue(PROPERTY_HAVING_CLAUSE, makeAny(_sOldHaving)); + xFormSet->setPropertyValue(PROPERTY_APPLYFILTER, css::uno::Any(_bOldFilterApplied)); + + try + { + if (loadingCancelled() || !reloadForm(m_xLoadable)) + criticalFail(); + } + catch(Exception&) + { + criticalFail(); + } + InvalidateAll(); + } + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + + setCurrentColumnPosition(nPos); +} + +Reference< XSingleSelectQueryComposer > SbaXDataBrowserController::createParser_nothrow() +{ + Reference< XSingleSelectQueryComposer > xComposer; + try + { + const Reference< XPropertySet > xRowSetProps( getRowSet(), UNO_QUERY_THROW ); + const Reference< XMultiServiceFactory > xFactory( + xRowSetProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ), UNO_QUERY_THROW ); + xComposer.set( xFactory->createInstance( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER ), UNO_QUERY_THROW ); + + OUString sActiveCommand; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_ACTIVECOMMAND ) >>= sActiveCommand ); + if ( !sActiveCommand.isEmpty() ) + { + xComposer->setElementaryQuery( sActiveCommand ); + } + else + { + OUString sCommand; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand ); + sal_Int32 nCommandType = CommandType::COMMAND; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_COMMAND_TYPE ) >>= nCommandType ); + xComposer->setCommand( sCommand, nCommandType ); + } + + OUString sFilter; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_FILTER ) >>= sFilter ); + xComposer->setFilter( sFilter ); + + OUString sHavingClause; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_HAVING_CLAUSE ) >>= sHavingClause ); + xComposer->setHavingClause( sHavingClause ); + + OUString sOrder; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_ORDER ) >>= sOrder ); + xComposer->setOrder( sOrder ); + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return xComposer; +} + +void SbaXDataBrowserController::ExecuteFilterSortCrit(bool bFilter) +{ + if (!SaveModified()) + return; + + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + + const OUString sOldVal = bFilter ? m_xParser->getFilter() : m_xParser->getOrder(); + const OUString sOldHaving = m_xParser->getHavingClause(); + Reference< XSingleSelectQueryComposer > xParser = createParser_nothrow(); + try + { + Reference< XConnection> xCon(xFormSet->getPropertyValue(PROPERTY_ACTIVE_CONNECTION),UNO_QUERY); + if(bFilter) + { + DlgFilterCrit aDlg(getFrameWeld(), getORB(), xCon, xParser, m_xColumnsSupplier->getColumns()); + if (!aDlg.run()) + return; // if so we don't need to update the grid + aDlg.BuildWherePart(); + } + else + { + DlgOrderCrit aDlg(getFrameWeld(), xCon, xParser, m_xColumnsSupplier->getColumns()); + if (!aDlg.run()) + { + return; // if so we don't need to actualize the grid + } + aDlg.BuildOrderPart(); + } + } + catch(const SQLException& ) + { + SQLExceptionInfo aError( ::cppu::getCaughtException() ); + showError( aError ); + return; + } + catch(Exception&) + { + return; + } + + OUString sNewVal = bFilter ? xParser->getFilter() : xParser->getOrder(); + bool bOldFilterApplied(false); + if (bFilter) + { + try { bOldFilterApplied = ::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_APPLYFILTER)); } catch(Exception&) { } ; + } + + OUString sNewHaving = xParser->getHavingClause(); + if ( sOldVal == sNewVal && (!bFilter || sOldHaving == sNewHaving) ) + // nothing to be done + return; + + if (bFilter) + applyParserFilter(sOldVal, bOldFilterApplied,sOldHaving,xParser); + else + applyParserOrder(sOldVal,xParser); + + ::comphelper::disposeComponent(xParser); +} + +void SbaXDataBrowserController::ExecuteSearch() +{ + // calculate the control source of the active field + Reference< css::form::XGrid > xGrid(getBrowserView()->getGridControl(), UNO_QUERY); + OSL_ENSURE(xGrid.is(), "SbaXDataBrowserController::ExecuteSearch : the control should have a css::form::XGrid interface !"); + + Reference< css::form::XGridPeer > xGridPeer(getBrowserView()->getGridControl()->getPeer(), UNO_QUERY); + Reference< css::container::XIndexContainer > xColumns = xGridPeer->getColumns(); + OSL_ENSURE(xGridPeer.is() && xColumns.is(), "SbaXDataBrowserController::ExecuteSearch : invalid peer !"); + + sal_Int16 nViewCol = xGrid->getCurrentColumnPosition(); + sal_Int16 nModelCol = getBrowserView()->View2ModelPos(nViewCol); + + Reference< XPropertySet > xCurrentCol(xColumns->getByIndex(nModelCol),UNO_QUERY); + OUString sActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(PROPERTY_CONTROLSOURCE)); + + // the text within the current cell + OUString sInitialText; + Reference< css::container::XIndexAccess > xColControls(xGridPeer, UNO_QUERY); + Reference< XInterface > xCurControl(xColControls->getByIndex(nViewCol),UNO_QUERY); + OUString aInitialText; + if (IsSearchableControl(xCurControl, &aInitialText)) + sInitialText = aInitialText; + + // prohibit the synchronization of the grid's display with the cursor's position + Reference< XPropertySet > xModelSet(getControlModel(), UNO_QUERY); + OSL_ENSURE(xModelSet.is(), "SbaXDataBrowserController::ExecuteSearch : no model set ?!"); + xModelSet->setPropertyValue("DisplayIsSynchron", css::uno::Any(false)); + xModelSet->setPropertyValue("AlwaysShowCursor", css::uno::Any(true)); + xModelSet->setPropertyValue("CursorColor", makeAny(COL_LIGHTRED)); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<AbstractFmSearchDialog> pDialog; + std::vector< OUString > aContextNames; + aContextNames.emplace_back("Standard" ); + pDialog = pFact->CreateFmSearchDialog(getFrameWeld(), sInitialText, aContextNames, 0, LINK(this, SbaXDataBrowserController, OnSearchContextRequest)); + pDialog->SetActiveField( sActiveField ); + pDialog->SetFoundHandler( LINK( this, SbaXDataBrowserController, OnFoundData ) ); + pDialog->SetCanceledNotFoundHdl( LINK( this, SbaXDataBrowserController, OnCanceledNotFound ) ); + pDialog->Execute(); + pDialog.disposeAndClear(); + + // restore the grid's normal operating state + xModelSet->setPropertyValue("DisplayIsSynchron", css::uno::Any(true)); + xModelSet->setPropertyValue("AlwaysShowCursor", css::uno::Any(false)); + xModelSet->setPropertyValue("CursorColor", Any()); +} + +void SbaXDataBrowserController::Execute(sal_uInt16 nId, const Sequence< PropertyValue >& _rArgs) +{ + bool bSortUp = true; + + switch (nId) + { + default: + SbaXDataBrowserController_Base::Execute( nId, _rArgs ); + return; + + case ID_BROWSER_INSERT_ROW: + try + { + if (SaveModified()) + { + getRowSet()->afterLast(); + // check if it is available + Reference< XResultSetUpdate > xUpdateCursor(getRowSet(), UNO_QUERY_THROW); + xUpdateCursor->moveToInsertRow(); + } + } + catch(Exception&) + { + SAL_WARN("dbaccess.ui", "Exception caught!"); + } + break; + case SID_FM_DELETEROWS: + + if (SaveModified()) + { + SbaGridControl* pVclGrid = getBrowserView()->getVclControl(); + if ( pVclGrid ) + { + if( !pVclGrid->GetSelectRowCount() ) + { + pVclGrid->DeactivateCell(); + pVclGrid->SelectRow(pVclGrid->GetCurRow()); + } + pVclGrid->DeleteSelectedRows(); + } + } + break; + + case ID_BROWSER_FILTERED: + if (SaveModified()) + { + Reference< XPropertySet > xActiveSet(getRowSet(), UNO_QUERY); + bool bApplied = ::comphelper::getBOOL(xActiveSet->getPropertyValue(PROPERTY_APPLYFILTER)); + xActiveSet->setPropertyValue(PROPERTY_APPLYFILTER, css::uno::Any(!bApplied)); + reloadForm(m_xLoadable); + } + InvalidateFeature(ID_BROWSER_FILTERED); + break; + case ID_BROWSER_EDITDOC: + { + DbGridControlOptions nGridMode = getBrowserView()->getVclControl()->GetOptions(); + if (nGridMode == DbGridControlOptions::Readonly) + getBrowserView()->getVclControl()->SetOptions(DbGridControlOptions::Update | DbGridControlOptions::Insert | DbGridControlOptions::Delete); + // the options not supported by the data source will be removed automatically + else + { + if ( !SaveModified( ) ) + // give the user a chance to save the current record (if necessary) + break; + + // maybe the user wanted to reject the modified record ? + if (GetState(ID_BROWSER_UNDORECORD).bEnabled) + Execute(ID_BROWSER_UNDORECORD,Sequence<PropertyValue>()); + + getBrowserView()->getVclControl()->SetOptions(DbGridControlOptions::Readonly); + } + InvalidateFeature(ID_BROWSER_EDITDOC); + } + break; + + case ID_BROWSER_SEARCH: + if ( SaveModified( ) ) + ExecuteSearch(); + break; + + case ID_BROWSER_COPY: + if ( getBrowserView()->getVclControl()->GetSelectRowCount() > 0 ) + { + getBrowserView()->getVclControl()->CopySelectedRowsToClipboard(); + break; + } + [[fallthrough]]; + case ID_BROWSER_CUT: + case ID_BROWSER_PASTE: + { + CellControllerRef xCurrentController = getBrowserView()->getVclControl()->Controller(); + if (!xCurrentController.is()) + // should be intercepted by GetState. Normally. + // Unfortunately ID_BROWSER_PASTE is a 'fast call' slot, which means it may be executed without checking if it is + // enabled. This would be really deadly herein if the current cell has no controller ... + return; + + Edit& rEdit = static_cast<Edit&>(xCurrentController->GetWindow()); + switch (nId) + { + case ID_BROWSER_CUT : rEdit.Cut(); break; + case SID_COPY : rEdit.Copy(); break; + case ID_BROWSER_PASTE : rEdit.Paste(); break; + } + if (ID_BROWSER_CUT == nId || ID_BROWSER_PASTE == nId) + { + xCurrentController->SetModified(); + rEdit.Modify(); + } + } + break; + + case ID_BROWSER_SORTDOWN: + bSortUp = false; + [[fallthrough]]; + case ID_BROWSER_SORTUP: + { + if (!SaveModified()) + break; + + if (!isValidCursor()) + break; + + // only one sort order + Reference< XPropertySet > xField = getBoundField(); + if (!xField.is()) + break; + + Reference< XSingleSelectQueryComposer > xParser = createParser_nothrow(); + const OUString sOldSort = xParser->getOrder(); + bool bParserSuccess = false; + HANDLE_SQL_ERRORS( + xParser->setOrder(OUString()); xParser->appendOrderByColumn(xField, bSortUp), + bParserSuccess, + DBA_RES(SBA_BROWSER_SETTING_ORDER), + "SbaXDataBrowserController::Execute : caught an exception while composing the new filter !" + ) + + if (bParserSuccess) + applyParserOrder(sOldSort,xParser); + } + break; + + case ID_BROWSER_AUTOFILTER: + { + if (!SaveModified()) + break; + + if (!isValidCursor()) + break; + + Reference< XPropertySet > xField = getBoundField(); + if (!xField.is()) + break; + + // check if the column is an aggregate function + const bool bHaving(isAggregateColumn(m_xParser, xField)); + + Reference< XSingleSelectQueryComposer > xParser = createParser_nothrow(); + const OUString sOldFilter = xParser->getFilter(); + const OUString sOldHaving = xParser->getHavingClause(); + + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + bool bApplied = ::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_APPLYFILTER)); + // do we have a filter but it's not applied ? + // -> completely overwrite it, else append one + if (!bApplied) + { + DO_SAFE( xParser->setFilter( OUString()), "SbaXDataBrowserController::Execute : caught an exception while resetting unapplied filter !" ); + DO_SAFE( xParser->setHavingClause(OUString()), "SbaXDataBrowserController::Execute : caught an exception while resetting unapplied HAVING clause !" ); + } + + bool bParserSuccess = false; + + const sal_Int32 nOp = SQLFilterOperator::EQUAL; + + if ( bHaving ) + { + HANDLE_SQL_ERRORS( + xParser->appendHavingClauseByColumn(xField,true,nOp), + bParserSuccess, + DBA_RES(SBA_BROWSER_SETTING_FILTER), + "SbaXDataBrowserController::Execute : caught an exception while composing the new filter !" + ) + } + else + { + HANDLE_SQL_ERRORS( + xParser->appendFilterByColumn(xField,true,nOp), + bParserSuccess, + DBA_RES(SBA_BROWSER_SETTING_FILTER), + "SbaXDataBrowserController::Execute : caught an exception while composing the new filter !" + ) + } + + if (bParserSuccess) + applyParserFilter(sOldFilter, bApplied,sOldHaving,xParser); + + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + InvalidateFeature(ID_BROWSER_FILTERED); + } + break; + + case ID_BROWSER_ORDERCRIT: + ExecuteFilterSortCrit(false); + break; + + case ID_BROWSER_FILTERCRIT: + ExecuteFilterSortCrit(true); + InvalidateFeature(ID_BROWSER_FILTERED); + break; + + case ID_BROWSER_REMOVEFILTER: + { + if (!SaveModified()) + break; + + bool bNeedPostReload = preReloadForm(); + // reset the filter and the sort property simultaneously so only _one_ new statement has to be + // sent + Reference< XPropertySet > xSet(getRowSet(), UNO_QUERY); + if ( xSet.is() ) + { + xSet->setPropertyValue(PROPERTY_FILTER,makeAny(OUString())); + xSet->setPropertyValue(PROPERTY_HAVING_CLAUSE,makeAny(OUString())); + xSet->setPropertyValue(PROPERTY_ORDER,makeAny(OUString())); + } + try + { + reloadForm(m_xLoadable); + if ( bNeedPostReload ) + postReloadForm(); + } + catch(Exception&) + { + } + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + InvalidateFeature(ID_BROWSER_FILTERED); + } + break; + + case ID_BROWSER_REFRESH: + if ( SaveModified( ) ) + { + if (!reloadForm(m_xLoadable)) + criticalFail(); + } + break; + + case ID_BROWSER_SAVERECORD: + if ( SaveModified( false ) ) + setCurrentModified( false ); + break; + + case ID_BROWSER_UNDORECORD: + { + try + { + // restore the cursor state + Reference< XResultSetUpdate > xCursor(getRowSet(), UNO_QUERY); + Reference< XPropertySet > xSet(xCursor, UNO_QUERY); + Any aVal = xSet->getPropertyValue(PROPERTY_ISNEW); + if (aVal.hasValue() && ::comphelper::getBOOL(aVal)) + { + xCursor->moveToInsertRow(); + // no need to reset the grid model after we moved to the insert row, this is done implicitly by the + // form + // (and in some cases it may be deadly to do the reset explicitly after the form did it implicitly, + // cause the form's reset may be async, and this leads to some nice deadlock scenarios...) + } + else + { + xCursor->cancelRowUpdates(); + + // restore the grids state + Reference< css::form::XReset > xReset(getControlModel(), UNO_QUERY); + if (xReset.is()) + xReset->reset(); + } + } + catch(SQLException&) + { + } + + setCurrentModified( false ); + } + } +} + +bool SbaXDataBrowserController::SaveModified(bool bAskFor) +{ + if ( bAskFor && GetState(ID_BROWSER_SAVERECORD).bEnabled ) + { + getBrowserView()->getVclControl()->GrabFocus(); + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/savemodifieddialog.ui")); + std::unique_ptr<weld::MessageDialog> xQry(xBuilder->weld_message_dialog("SaveModifiedDialog")); + switch (xQry->run()) + { + case RET_NO: + Execute(ID_BROWSER_UNDORECORD,Sequence<PropertyValue>()); + return true; + case RET_CANCEL: + return false; + } + } + + if ( !CommitCurrent() ) // Commit the current control + return false; + + Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + bool bResult = false; + try + { + if (::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ISMODIFIED))) + { + Reference< XResultSetUpdate > xCursor(getRowSet(), UNO_QUERY); + if (::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ISNEW))) + xCursor->insertRow(); + else + xCursor->updateRow(); + } + bResult = true; + } + catch(SQLException&) + { + } + catch(Exception&) + { + SAL_WARN("dbaccess.ui", "SbaXDataBrowserController::SaveModified : could not save the current record !"); + bResult = false; + } + + InvalidateFeature(ID_BROWSER_SAVERECORD); + InvalidateFeature(ID_BROWSER_UNDORECORD); + return bResult; +} + +bool SbaXDataBrowserController::CommitCurrent() +{ + if (!getBrowserView()) + return true; + + Reference< css::awt::XControl > xActiveControl(getBrowserView()->getGridControl()); + Reference< css::form::XBoundControl > xLockingTest(xActiveControl, UNO_QUERY); + bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock(); + if (xActiveControl.is() && !bControlIsLocked) + { + // At first check Control if it supports the IFace + Reference< css::form::XBoundComponent > xBoundControl(xActiveControl, UNO_QUERY); + if (!xBoundControl.is()) + xBoundControl.set(xActiveControl->getModel(), UNO_QUERY); + if (xBoundControl.is() && !xBoundControl->commit()) + return false; + } + return true; +} + +void SbaXDataBrowserController::setCurrentModified( bool _bSet ) +{ + m_bCurrentlyModified = _bSet; + InvalidateFeature( ID_BROWSER_SAVERECORD ); + InvalidateFeature( ID_BROWSER_UNDORECORD ); +} + +void SbaXDataBrowserController::RowChanged() +{ + setCurrentModified( false ); +} + +void SbaXDataBrowserController::ColumnChanged() +{ + InvalidateFeature(ID_BROWSER_SORTUP); + InvalidateFeature(ID_BROWSER_SORTDOWN); + InvalidateFeature(ID_BROWSER_ORDERCRIT); + InvalidateFeature(ID_BROWSER_FILTERCRIT); + InvalidateFeature(ID_BROWSER_AUTOFILTER); + InvalidateFeature(ID_BROWSER_REMOVEFILTER); + + setCurrentModified( false ); +} + +void SbaXDataBrowserController::SelectionChanged() +{ + // not interested in +} + +void SbaXDataBrowserController::CellActivated() +{ + m_aInvalidateClipboard.Start(); + OnInvalidateClipboard( nullptr ); +} + +void SbaXDataBrowserController::CellDeactivated() +{ + m_aInvalidateClipboard.Stop(); + OnInvalidateClipboard( nullptr ); +} + +IMPL_LINK_NOARG(SbaXDataBrowserController, OnClipboardChanged, TransferableDataHelper*, void) +{ + SolarMutexGuard aGuard; + OnInvalidateClipboard( nullptr ); +} + +IMPL_LINK(SbaXDataBrowserController, OnInvalidateClipboard, Timer*, _pTimer, void) +{ + InvalidateFeature(ID_BROWSER_CUT); + InvalidateFeature(ID_BROWSER_COPY); + + // if the invalidation was triggered by the timer, we do not need to invalidate PASTE. + // The timer is only for checking the CUT/COPY slots regularly, which depend on the + // selection state of the active cell + // TODO: get a callback at the Edit which allows to be notified when the selection + // changes. This would be much better than this cycle-eating polling mechanism here... + if ( _pTimer != &m_aInvalidateClipboard ) + InvalidateFeature(ID_BROWSER_PASTE); +} + +Reference< XPropertySet > SbaXDataBrowserController::getBoundField() const +{ + Reference< XPropertySet > xEmptyReturn; + + // get the current column from the grid + Reference< css::form::XGrid > xGrid(getBrowserView()->getGridControl(), UNO_QUERY); + if (!xGrid.is()) + return xEmptyReturn; + sal_uInt16 nViewPos = xGrid->getCurrentColumnPosition(); + sal_uInt16 nCurrentCol = getBrowserView()->View2ModelPos(nViewPos); + if (nCurrentCol == sal_uInt16(-1)) + return xEmptyReturn; + + // get the according column from the model + Reference< css::container::XIndexContainer > xCols(getControlModel(), UNO_QUERY); + Reference< XPropertySet > xCurrentCol(xCols->getByIndex(nCurrentCol),UNO_QUERY); + if (!xCurrentCol.is()) + return xEmptyReturn; + + xEmptyReturn.set(xCurrentCol->getPropertyValue(PROPERTY_BOUNDFIELD) ,UNO_QUERY); + return xEmptyReturn; +} + +IMPL_LINK(SbaXDataBrowserController, OnSearchContextRequest, FmSearchContext&, rContext, sal_uInt32) +{ + Reference< css::container::XIndexAccess > xPeerContainer(getBrowserView()->getGridControl(), UNO_QUERY); + + // check all grid columns for their control source + Reference< css::container::XIndexAccess > xModelColumns(getFormComponent(), UNO_QUERY); + OSL_ENSURE(xModelColumns.is(), "SbaXDataBrowserController::OnSearchContextRequest : there is a grid control without columns !"); + // the case 'no columns' should be indicated with an empty container, I think ... + OSL_ENSURE(xModelColumns->getCount() >= xPeerContainer->getCount(), "SbaXDataBrowserController::OnSearchContextRequest : impossible : have more view than model columns !"); + + OUString sFieldList; + for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos) + { + Reference< XInterface > xCurrentColumn(xPeerContainer->getByIndex(nViewPos),UNO_QUERY); + if (!xCurrentColumn.is()) + continue; + + // can we use this column control for searching ? + if (!IsSearchableControl(xCurrentColumn)) + continue; + + sal_uInt16 nModelPos = getBrowserView()->View2ModelPos(static_cast<sal_uInt16>(nViewPos)); + Reference< XPropertySet > xCurrentColModel(xModelColumns->getByIndex(nModelPos),UNO_QUERY); + OUString aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(PROPERTY_CONTROLSOURCE)); + + sFieldList += aName + ";"; + + rContext.arrFields.push_back(xCurrentColumn); + } + sFieldList = comphelper::string::stripEnd(sFieldList, ';'); + + rContext.xCursor = getRowSet(); + rContext.strUsedFields = sFieldList; + + // if the cursor is in a mode other than STANDARD -> reset + Reference< XPropertySet > xCursorSet(rContext.xCursor, UNO_QUERY); + OSL_ENSURE(xCursorSet.is() && !::comphelper::getBOOL(xCursorSet->getPropertyValue(PROPERTY_ISMODIFIED)), + "SbaXDataBrowserController::OnSearchContextRequest : please do not call for cursors with modified rows !"); + if (xCursorSet.is() && ::comphelper::getBOOL(xCursorSet->getPropertyValue(PROPERTY_ISNEW))) + { + Reference< XResultSetUpdate > xUpdateCursor(rContext.xCursor, UNO_QUERY); + xUpdateCursor->moveToCurrentRow(); + } + return rContext.arrFields.size(); +} + +IMPL_LINK(SbaXDataBrowserController, OnFoundData, FmFoundRecordInformation&, rInfo, void) +{ + Reference< css::sdbcx::XRowLocate > xCursor(getRowSet(), UNO_QUERY); + OSL_ENSURE(xCursor.is(), "SbaXDataBrowserController::OnFoundData : xCursor is empty"); + + // move the cursor + xCursor->moveToBookmark(rInfo.aPosition); + + // let the grid sync its display with the cursor + Reference< XPropertySet > xModelSet(getControlModel(), UNO_QUERY); + OSL_ENSURE(xModelSet.is(), "SbaXDataBrowserController::OnFoundData : no model set ?!"); + Any aOld = xModelSet->getPropertyValue("DisplayIsSynchron"); + xModelSet->setPropertyValue("DisplayIsSynchron", css::uno::Any(true)); + xModelSet->setPropertyValue("DisplayIsSynchron", aOld); + + // and move to the field + Reference< css::container::XIndexAccess > aColumnControls(getBrowserView()->getGridControl()->getPeer(), UNO_QUERY); + sal_Int32 nViewPos; + + for ( nViewPos = 0; nViewPos < aColumnControls->getCount(); ++nViewPos ) + { + Reference< XInterface > xCurrent(aColumnControls->getByIndex(nViewPos),UNO_QUERY); + if (IsSearchableControl(xCurrent)) + { + if (rInfo.nFieldPos) + --rInfo.nFieldPos; + else + break; + } + } + + Reference< css::form::XGrid > xGrid(getBrowserView()->getGridControl(), UNO_QUERY); + xGrid->setCurrentColumnPosition(nViewPos); //TODO: sal_Int32 -> sal_Int16! +} + +IMPL_LINK(SbaXDataBrowserController, OnCanceledNotFound, FmFoundRecordInformation&, rInfo, void) +{ + Reference< css::sdbcx::XRowLocate > xCursor(getRowSet(), UNO_QUERY); + + try + { + OSL_ENSURE(xCursor.is(), "SbaXDataBrowserController::OnCanceledNotFound : xCursor is empty"); + // move the cursor + xCursor->moveToBookmark(rInfo.aPosition); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + try + { + // let the grid sync its display with the cursor + Reference< XPropertySet > xModelSet(getControlModel(), UNO_QUERY); + OSL_ENSURE(xModelSet.is(), "SbaXDataBrowserController::OnCanceledNotFound : no model set ?!"); + Any aOld = xModelSet->getPropertyValue("DisplayIsSynchron"); + xModelSet->setPropertyValue("DisplayIsSynchron", css::uno::Any(true)); + xModelSet->setPropertyValue("DisplayIsSynchron", aOld); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +IMPL_LINK_NOARG(SbaXDataBrowserController, OnAsyncGetCellFocus, void*, void) +{ + SbaGridControl* pVclGrid = getBrowserView() ? getBrowserView()->getVclControl() : nullptr; + // if we have a controller, but the window for the controller doesn't have the focus, we correct this + if (pVclGrid && pVclGrid->IsEditing() && pVclGrid->HasChildPathFocus()) + pVclGrid->Controller()->GetWindow().GrabFocus(); +} + +void SbaXDataBrowserController::criticalFail() +{ + InvalidateAll(); + m_nRowSetPrivileges = 0; +} + +void SbaXDataBrowserController::LoadFinished(bool /*bWasSynch*/) +{ + m_nRowSetPrivileges = 0; + + if (!(isValid() && !loadingCancelled())) + return; + + // obtain cached values + try + { + Reference< XPropertySet > xFormProps( m_xLoadable, UNO_QUERY_THROW ); + OSL_VERIFY( xFormProps->getPropertyValue( PROPERTY_PRIVILEGES ) >>= m_nRowSetPrivileges ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // switch the control to alive mode + getBrowserView()->getGridControl()->setDesignMode(false); + + initializeParser(); + + InvalidateAll(); + + m_aAsyncGetCellFocus.Call(); +} + +void SbaXDataBrowserController::initializeParser() const +{ + if ( m_xParser.is() ) + return; + + // create a parser (needed for filtering/sorting) + try + { + const Reference< XPropertySet > xFormSet(getRowSet(), UNO_QUERY); + if (::comphelper::getBOOL(xFormSet->getPropertyValue(PROPERTY_ESCAPE_PROCESSING))) + { // (only if the statement isn't native) + // (it is allowed to use the PROPERTY_ISPASSTHROUGH : _after_ loading a form it is valid) + xFormSet->getPropertyValue(PROPERTY_SINGLESELECTQUERYCOMPOSER) >>= m_xParser; + } + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + m_xParser = nullptr; + // no further handling, we ignore the error + } +} + +void SbaXDataBrowserController::loaded(const EventObject& /*aEvent*/) +{ + // not interested in + // we're loading within a separated thread and have a handling for its "finished event" +} + +void SbaXDataBrowserController::unloading(const EventObject& /*aEvent*/) +{ + // not interested in +} + +void SbaXDataBrowserController::unloaded(const EventObject& /*aEvent*/) +{ + m_xParser.clear(); + InvalidateAll(); + // do this asynchronously, there are other listeners reacting on this message ... + // (it's a little hack : the grid columns are listening to this event, too, and their bound field may + // change as a reaction on that event. as we have no chance to be notified of this change (which is + // the one we're interested in) we give them time to do what they want to before invalidating our + // bound-field-dependent slots... +} + +void SbaXDataBrowserController::reloading(const EventObject& /*aEvent*/) +{ + // not interested in +} + +void SbaXDataBrowserController::reloaded(const EventObject& /*aEvent*/) +{ + InvalidateAll(); + // do this asynchronously, there are other listeners reacting on this message ... + // (it's a little hack : the grid columns are listening to this event, too, and their bound field may + // change as a reaction on that event. as we have no chance to be notified of this change (which is + // the one we're interested in) we give them time to do what they want to before invalidating our + // bound-field-dependent slots... +} + +void SbaXDataBrowserController::enterFormAction() +{ + if ( !m_nFormActionNestingLevel ) + // first action -> reset + m_aCurrentError.clear(); + + ++m_nFormActionNestingLevel; +} + +void SbaXDataBrowserController::leaveFormAction() +{ + OSL_ENSURE( m_nFormActionNestingLevel > 0, "SbaXDataBrowserController::leaveFormAction : invalid call !" ); + if ( --m_nFormActionNestingLevel > 0 ) + return; + + if ( !m_aCurrentError.isValid() ) + return; + + m_aAsyncDisplayError.Call(); +} + +bool SbaXDataBrowserController::isLoaded() const +{ + return m_xLoadable.is() && m_xLoadable->isLoaded(); +} + +bool SbaXDataBrowserController::isValidCursor() const +{ + if (!m_xColumnsSupplier.is()) + return false; + Reference< css::container::XNameAccess > xCols = m_xColumnsSupplier->getColumns(); + if (!xCols.is() || !xCols->hasElements()) + return false; + + bool bIsValid = !(m_xRowSet->isBeforeFirst() || m_xRowSet->isAfterLast()); + if ( !bIsValid ) + { + Reference<XPropertySet> xProp(m_xRowSet,UNO_QUERY); + bIsValid = ::cppu::any2bool(xProp->getPropertyValue(PROPERTY_ISNEW)); + if ( !bIsValid ) + { + bIsValid = m_xParser.is(); + } + } + return bIsValid; +} + +sal_Int16 SbaXDataBrowserController::getCurrentColumnPosition() const +{ + Reference< css::form::XGrid > xGrid(getBrowserView()->getGridControl(), UNO_QUERY); + sal_Int16 nViewPos = -1; + try + { + if ( xGrid.is() ) + nViewPos = xGrid->getCurrentColumnPosition(); + } + catch(Exception&) {} + return nViewPos; +} + +void SbaXDataBrowserController::setCurrentColumnPosition( sal_Int16 _nPos ) +{ + Reference< css::form::XGrid > xGrid(getBrowserView()->getGridControl(), UNO_QUERY); + try + { + if ( -1 != _nPos ) + xGrid->setCurrentColumnPosition(_nPos); + } + catch(Exception&) {} +} + +void SbaXDataBrowserController::BeforeDrop() +{ + Reference< css::sdb::XSQLErrorBroadcaster > xFormError(getRowSet(), UNO_QUERY); + if (xFormError.is()) + xFormError->removeSQLErrorListener(static_cast<css::sdb::XSQLErrorListener*>(this)); +} + +void SbaXDataBrowserController::AfterDrop() +{ + Reference< css::sdb::XSQLErrorBroadcaster > xFormError(getRowSet(), UNO_QUERY); + if (xFormError.is()) + xFormError->addSQLErrorListener(static_cast<css::sdb::XSQLErrorListener*>(this)); +} + +void SbaXDataBrowserController::addColumnListeners(const Reference< css::awt::XControlModel > & _xGridControlModel) +{ +// ... all the grid columns + Reference< css::container::XIndexContainer > xColumns(_xGridControlModel, UNO_QUERY); + if (xColumns.is()) + { + sal_Int32 nCount = xColumns->getCount(); + for (sal_Int32 i=0; i < nCount; ++i) + { + Reference< XPropertySet > xCol(xColumns->getByIndex(i),UNO_QUERY); + AddColumnListener(xCol); + } + } +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/brwview.cxx b/dbaccess/source/ui/browser/brwview.cxx new file mode 100644 index 000000000..01b17fd7c --- /dev/null +++ b/dbaccess/source/ui/browser/brwview.cxx @@ -0,0 +1,340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <brwview.hxx> +#include <sbagrid.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/types.hxx> +#include <vcl/fixed.hxx> +#include <vcl/split.hxx> +#include "dbtreeview.hxx" +#include <strings.hxx> +#include <com/sun/star/form/XLoadable.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <osl/diagnose.h> + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +namespace +{ + bool isGrabVclControlFocusAllowed(const UnoDataBrowserView* _pView) + { + bool bGrabFocus = false; + SbaGridControl* pVclControl = _pView->getVclControl(); + const Reference< css::awt::XControl >& xGrid = _pView->getGridControl(); + if (pVclControl && xGrid.is()) + { + bGrabFocus = true; + if(!pVclControl->HasChildPathFocus()) + { + Reference<XChild> xChild(xGrid->getModel(),UNO_QUERY); + Reference<XLoadable> xLoad; + if(xChild.is()) + xLoad.set(xChild->getParent(),UNO_QUERY); + bGrabFocus = xLoad.is() && xLoad->isLoaded(); + } + } + return bGrabFocus; + } +} + +// UnoDataBrowserView + +UnoDataBrowserView::UnoDataBrowserView( vcl::Window* pParent, + IController& _rController, + const Reference< css::uno::XComponentContext >& _rxContext) + :ODataView(pParent,_rController,_rxContext) + ,m_pTreeView(nullptr) + ,m_pSplitter(nullptr) + ,m_pVclControl(nullptr) + ,m_pStatus(nullptr) +{ + +} + +void UnoDataBrowserView::Construct(const Reference< css::awt::XControlModel >& xModel) +{ + try + { + ODataView::Construct(); + + // our UNO representation + m_xMe = VCLUnoHelper::CreateControlContainer(this); + + // create the (UNO-) control + m_xGrid = new SbaXGridControl( getORB() ); + OSL_ENSURE(m_xGrid.is(), "UnoDataBrowserView::Construct : could not create a grid control !"); + // in design mode (for the moment) + m_xGrid->setDesignMode(true); + + Reference< css::awt::XWindow > xGridWindow(m_xGrid, UNO_QUERY); + xGridWindow->setVisible(true); + xGridWindow->setEnable(true); + + // introduce the model to the grid + m_xGrid->setModel(xModel); + // introduce the container (me) to the grid + Reference< css::beans::XPropertySet > xModelSet(xModel, UNO_QUERY); + getContainer()->addControl(::comphelper::getString(xModelSet->getPropertyValue(PROPERTY_NAME)), m_xGrid); + + // get the VCL-control + m_pVclControl = nullptr; + getVclControl(); + + OSL_ENSURE(m_pVclControl != nullptr, "UnoDataBrowserView::Construct : no real grid control !"); + } + catch(const Exception&) + { + ::comphelper::disposeComponent(m_xGrid); + throw; + } +} + +UnoDataBrowserView::~UnoDataBrowserView() +{ + disposeOnce(); +} + +void UnoDataBrowserView::dispose() +{ + m_pSplitter.disposeAndClear(); + setTreeView(nullptr); + + m_pStatus.disposeAndClear(); + + try + { + ::comphelper::disposeComponent(m_xGrid); + ::comphelper::disposeComponent(m_xMe); + } + catch(const Exception&) + {} + m_pTreeView.clear(); + m_pVclControl.clear(); + ODataView::dispose(); +} + +IMPL_LINK_NOARG( UnoDataBrowserView, SplitHdl, Splitter*, void ) +{ + long nYPos = m_pSplitter->GetPosPixel().Y(); + m_pSplitter->SetPosPixel( Point( m_pSplitter->GetSplitPosPixel(), nYPos ) ); + Resize(); +} + +void UnoDataBrowserView::setSplitter(Splitter* _pSplitter) +{ + m_pSplitter = _pSplitter; + m_pSplitter->SetSplitHdl( LINK( this, UnoDataBrowserView, SplitHdl ) ); + LINK( this, UnoDataBrowserView, SplitHdl ).Call(m_pSplitter); +} + +void UnoDataBrowserView::setTreeView(DBTreeView* _pTreeView) +{ + if (m_pTreeView.get() != _pTreeView) + { + m_pTreeView.disposeAndClear(); + m_pTreeView = _pTreeView; + } +} + +void UnoDataBrowserView::showStatus( const OUString& _rStatus ) +{ + if (_rStatus.isEmpty()) + hideStatus(); + else + { + if (!m_pStatus) + m_pStatus = VclPtr<FixedText>::Create(this); + m_pStatus->SetText(_rStatus); + m_pStatus->Show(); + Resize(); + PaintImmediately(); + } +} + +void UnoDataBrowserView::hideStatus() +{ + if (!m_pStatus || !m_pStatus->IsVisible()) + // nothing to do + return; + m_pStatus->Hide(); + Resize(); + PaintImmediately(); +} + +void UnoDataBrowserView::resizeDocumentView(tools::Rectangle& _rPlayground) +{ + Point aSplitPos; + Size aSplitSize; + Point aPlaygroundPos( _rPlayground.TopLeft() ); + Size aPlaygroundSize( _rPlayground.GetSize() ); + + if (m_pTreeView && m_pTreeView->IsVisible() && m_pSplitter) + { + // calculate the splitter pos and size + aSplitPos = m_pSplitter->GetPosPixel(); + aSplitPos.setY( aPlaygroundPos.Y() ); + aSplitSize = m_pSplitter->GetOutputSizePixel(); + aSplitSize.setHeight( aPlaygroundSize.Height() ); + + if( ( aSplitPos.X() + aSplitSize.Width() ) > ( aPlaygroundSize.Width() )) + aSplitPos.setX( aPlaygroundSize.Width() - aSplitSize.Width() ); + + if( aSplitPos.X() <= aPlaygroundPos.X() ) + aSplitPos.setX( aPlaygroundPos.X() + sal_Int32(aPlaygroundSize.Width() * 0.2) ); + + // the tree pos and size + Point aTreeViewPos( aPlaygroundPos ); + Size aTreeViewSize( aSplitPos.X(), aPlaygroundSize.Height() ); + + // the status pos and size + if (m_pStatus && m_pStatus->IsVisible()) + { + Size aStatusSize(aPlaygroundPos.X(), GetTextHeight() + 2); + aStatusSize = LogicToPixel(aStatusSize, MapMode(MapUnit::MapAppFont)); + aStatusSize.setWidth( aTreeViewSize.Width() - 2 - 2 ); + + Point aStatusPos( aPlaygroundPos.X() + 2, aTreeViewPos.Y() + aTreeViewSize.Height() - aStatusSize.Height() ); + m_pStatus->SetPosSizePixel( aStatusPos, aStatusSize ); + aTreeViewSize.AdjustHeight( -(aStatusSize.Height()) ); + } + + // set the size of treelistbox + m_pTreeView->SetPosSizePixel( aTreeViewPos, aTreeViewSize ); + + //set the size of the splitter + m_pSplitter->SetPosSizePixel( aSplitPos, Size( aSplitSize.Width(), aPlaygroundSize.Height() ) ); + m_pSplitter->SetDragRectPixel( _rPlayground ); + } + + // set the size of grid control + Reference< css::awt::XWindow > xGridAsWindow(m_xGrid, UNO_QUERY); + if (xGridAsWindow.is()) + xGridAsWindow->setPosSize( aSplitPos.X() + aSplitSize.Width(), aPlaygroundPos.Y(), + aPlaygroundSize.Width() - aSplitSize.Width() - aSplitPos.X(), aPlaygroundSize.Height(), css::awt::PosSize::POSSIZE); + + // just for completeness: there is no space left, we occupied it all ... + _rPlayground.SetPos( _rPlayground.BottomRight() ); + _rPlayground.SetSize( Size( 0, 0 ) ); +} + +sal_uInt16 UnoDataBrowserView::View2ModelPos(sal_uInt16 nPos) const +{ + return m_pVclControl ? m_pVclControl->GetModelColumnPos(m_pVclControl->GetColumnIdFromViewPos(nPos)) : -1; +} + +SbaGridControl* UnoDataBrowserView::getVclControl() const +{ + if ( !m_pVclControl ) + { + OSL_ENSURE(m_xGrid.is(),"Grid not set!"); + if ( m_xGrid.is() ) + { + Reference< css::awt::XWindowPeer > xPeer = m_xGrid->getPeer(); + if ( xPeer.is() ) + { + SbaXGridPeer* pPeer = comphelper::getUnoTunnelImplementation<SbaXGridPeer>(xPeer); + UnoDataBrowserView* pTHIS = const_cast<UnoDataBrowserView*>(this); + if ( pPeer ) + { + m_pVclControl = static_cast<SbaGridControl*>(pPeer->GetWindow().get()); + pTHIS->startComponentListening(VCLUnoHelper::GetInterface(m_pVclControl)); + } + } + } + } + return m_pVclControl; +} + +void UnoDataBrowserView::GetFocus() +{ + ODataView::GetFocus(); + if( m_pTreeView && m_pTreeView->IsVisible() && !m_pTreeView->HasChildPathFocus()) + m_pTreeView->GrabFocus(); + else if (m_pVclControl && m_xGrid.is()) + { + bool bGrabFocus = false; + if(!m_pVclControl->HasChildPathFocus()) + { + bGrabFocus = isGrabVclControlFocusAllowed(this); + if( bGrabFocus ) + m_pVclControl->GrabFocus(); + } + if(!bGrabFocus && m_pTreeView && m_pTreeView->IsVisible() ) + m_pTreeView->GrabFocus(); + } +} + +void UnoDataBrowserView::_disposing( const css::lang::EventObject& /*_rSource*/ ) +{ + stopComponentListening(VCLUnoHelper::GetInterface(m_pVclControl)); + m_pVclControl = nullptr; +} + +bool UnoDataBrowserView::PreNotify( NotifyEvent& rNEvt ) +{ + bool bDone = false; + if(rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) + { + bool bGrabAllowed = isGrabVclControlFocusAllowed(this); + if ( bGrabAllowed ) + { + const KeyEvent* pKeyEvt = rNEvt.GetKeyEvent(); + const vcl::KeyCode& rKeyCode = pKeyEvt->GetKeyCode(); + if ( ( rKeyCode == vcl::KeyCode( KEY_E, true, true, false, false ) ) + || ( rKeyCode == vcl::KeyCode( KEY_TAB, true, false, false, false ) ) + ) + { + if ( m_pTreeView && m_pVclControl && m_pTreeView->HasChildPathFocus() ) + m_pVclControl->GrabFocus(); + else if ( m_pTreeView && m_pVclControl && m_pVclControl->HasChildPathFocus() ) + m_pTreeView->GrabFocus(); + + bDone = true; + } + } + } + return bDone || ODataView::PreNotify(rNEvt); +} + +BrowserViewStatusDisplay::BrowserViewStatusDisplay( UnoDataBrowserView* _pView, const OUString& _rStatus ) + :m_pView(_pView) +{ + + if (m_pView) + m_pView->showStatus(_rStatus); +} + +BrowserViewStatusDisplay::~BrowserViewStatusDisplay( ) +{ + if (m_pView) + m_pView->showStatus(OUString()); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dataview.cxx b/dbaccess/source/ui/browser/dataview.cxx new file mode 100644 index 000000000..099199bc2 --- /dev/null +++ b/dbaccess/source/ui/browser/dataview.cxx @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <dbaccess/dataview.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <dbaccess/IController.hxx> +#include <svtools/acceleratorexecute.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/event.hxx> +#include <vcl/fixed.hxx> +#include <vcl/settings.hxx> + +namespace dbaui +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::frame; + + ODataView::ODataView( vcl::Window* pParent, + IController& _rController, + const Reference< XComponentContext >& _rxContext, + WinBits nStyle) + :Window(pParent,nStyle) + ,m_xContext(_rxContext) + ,m_xController( &_rController ) + ,m_aSeparator( VclPtr<FixedLine>::Create(this) ) + { + m_pAccel = ::svt::AcceleratorExecute::createAcceleratorHelper(); + m_aSeparator->Show(); + } + + void ODataView::Construct() + { + } + + ODataView::~ODataView() + { + disposeOnce(); + } + + void ODataView::dispose() + { + m_xController.clear(); + m_aSeparator.disposeAndClear(); + m_pAccel.reset(); + vcl::Window::dispose(); + } + + void ODataView::resizeDocumentView( tools::Rectangle& /*_rPlayground*/ ) + { + } + + void ODataView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rRect) + { + // draw the background + { + rRenderContext.Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + rRenderContext.SetLineColor(COL_TRANSPARENT); + rRenderContext.SetFillColor(GetSettings().GetStyleSettings().GetFaceColor()); + rRenderContext.DrawRect(_rRect); + rRenderContext.Pop(); + } + + // let the base class do anything it needs + Window::Paint(rRenderContext, _rRect); + } + + void ODataView::resizeAll( const tools::Rectangle& _rPlayground ) + { + tools::Rectangle aPlayground( _rPlayground ); + + // position the separator + const Size aSeparatorSize( aPlayground.GetWidth(), 2 ); + m_aSeparator->SetPosSizePixel( aPlayground.TopLeft(), aSeparatorSize ); + aPlayground.AdjustTop(aSeparatorSize.Height() + 1 ); + + // position the controls of the document's view + resizeDocumentView( aPlayground ); + } + + void ODataView::Resize() + { + Window::Resize(); + resizeAll( tools::Rectangle( Point( 0, 0), GetSizePixel() ) ); + } + bool ODataView::PreNotify( NotifyEvent& _rNEvt ) + { + bool bHandled = false; + switch ( _rNEvt.GetType() ) + { + case MouseNotifyEvent::KEYINPUT: + { + const KeyEvent* pKeyEvent = _rNEvt.GetKeyEvent(); + const vcl::KeyCode& aKeyCode = pKeyEvent->GetKeyCode(); + if ( m_pAccel && m_pAccel->execute( aKeyCode ) ) + // the accelerator consumed the event + return true; + [[fallthrough]]; + } + case MouseNotifyEvent::KEYUP: + case MouseNotifyEvent::MOUSEBUTTONDOWN: + case MouseNotifyEvent::MOUSEBUTTONUP: + bHandled = m_xController->interceptUserInput( _rNEvt ); + break; + default: + break; + } + return bHandled || Window::PreNotify( _rNEvt ); + } + void ODataView::StateChanged( StateChangedType nType ) + { + Window::StateChanged( nType ); + + if ( nType == StateChangedType::ControlBackground ) + { + // Check if we need to get new images for normal/high contrast mode + m_xController->notifyHiContrastChanged(); + } + + if ( nType != StateChangedType::InitShow ) + return; + + // now that there's a view which is finally visible, remove the "Hidden" value from the + // model's arguments. + try + { + Reference< XController > xController( m_xController->getXController(), UNO_SET_THROW ); + Reference< XModel > xModel = xController->getModel(); + if ( xModel.is() ) + { + ::comphelper::NamedValueCollection aArgs( xModel->getArgs() ); + aArgs.remove( "Hidden" ); + xModel->attachResource( xModel->getURL(), aArgs.getPropertyValues() ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + void ODataView::DataChanged( const DataChangedEvent& rDCEvt ) + { + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + // Check if we need to get new images for normal/high contrast mode + m_xController->notifyHiContrastChanged(); + } + } + void ODataView::attachFrame(const Reference< XFrame >& _xFrame) + { + m_pAccel->init(m_xContext, _xFrame); + } +} + +// namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dbexchange.cxx b/dbaccess/source/ui/browser/dbexchange.cxx new file mode 100644 index 000000000..2fe0a3b21 --- /dev/null +++ b/dbaccess/source/ui/browser/dbexchange.cxx @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbexchange.hxx> +#include <sot/formats.hxx> +#include <sot/storage.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/XResultSetAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <TokenWriter.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <UITools.hxx> + +namespace dbaui +{ + constexpr sal_uInt32 FORMAT_OBJECT_ID_RTF = 1; + constexpr sal_uInt32 FORMAT_OBJECT_ID_HTML = 2; + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::datatransfer; + using namespace ::svx; + + namespace + { + template<class T > void lcl_setListener(const Reference<T>& _xComponent, const Reference< XEventListener >& i_rListener, const bool i_bAdd ) + { + if ( !_xComponent.is() ) + return; + + Reference< XComponent> xCom( _xComponent, UNO_QUERY ); + OSL_ENSURE( xCom.is(), "lcl_setListener: no component!" ); + if ( !xCom.is() ) + return; + + i_bAdd ? xCom->addEventListener( i_rListener ) : xCom->removeEventListener( i_rListener ); + } + } + + ODataClipboard::ODataClipboard( + const OUString& _rDatasource, + const sal_Int32 _nCommandType, + const OUString& _rCommand, + const Reference< XConnection >& _rxConnection, + const Reference< XNumberFormatter >& _rxFormatter, + const Reference< XComponentContext >& _rxORB) + :ODataAccessObjectTransferable( _rDatasource, _nCommandType, _rCommand, _rxConnection ) + { + osl_atomic_increment( &m_refCount ); + lcl_setListener( _rxConnection, this, true ); + + m_pHtml.set( new OHTMLImportExport( getDescriptor(), _rxORB, _rxFormatter ) ); + m_pRtf.set( new ORTFImportExport( getDescriptor(), _rxORB, _rxFormatter ) ); + + osl_atomic_decrement( &m_refCount ); + } + + ODataClipboard::ODataClipboard( + const OUString& _rDatasource, + const sal_Int32 _nCommandType, + const OUString& _rCommand, + const Reference< XNumberFormatter >& _rxFormatter, + const Reference< XComponentContext >& _rxORB) + :ODataAccessObjectTransferable( _rDatasource, _nCommandType, _rCommand) + { + m_pHtml.set( new OHTMLImportExport( getDescriptor(),_rxORB, _rxFormatter ) ); + m_pRtf.set( new ORTFImportExport( getDescriptor(),_rxORB, _rxFormatter ) ); + } + + ODataClipboard::ODataClipboard( const Reference< XPropertySet >& i_rAliveForm, + const Sequence< Any >& i_rSelectedRows, + const bool i_bBookmarkSelection, + const Reference< XComponentContext >& i_rORB ) + :ODataAccessObjectTransferable( i_rAliveForm ) + { + OSL_PRECOND( i_rORB.is(), "ODataClipboard::ODataClipboard: having no factory is not good ..." ); + + osl_atomic_increment( &m_refCount ); + + Reference<XConnection> xConnection; + getDescriptor()[ DataAccessDescriptorProperty::Connection ] >>= xConnection; + lcl_setListener( xConnection, this, true ); + + // do not pass the form itself as source result set, since the client might operate on the form, which + // might lead to undesired effects. Instead, use a clone. + Reference< XResultSet > xResultSetClone; + Reference< XResultSetAccess > xResultSetAccess( i_rAliveForm, UNO_QUERY ); + if ( xResultSetAccess.is() ) + xResultSetClone = xResultSetAccess->createResultSet(); + OSL_ENSURE( xResultSetClone.is(), "ODataClipboard::ODataClipboard: could not clone the form's result set" ); + lcl_setListener( xResultSetClone, this, true ); + + getDescriptor()[DataAccessDescriptorProperty::Cursor] <<= xResultSetClone; + getDescriptor()[DataAccessDescriptorProperty::Selection] <<= i_rSelectedRows; + getDescriptor()[DataAccessDescriptorProperty::BookmarkSelection]<<= i_bBookmarkSelection; + addCompatibleSelectionDescription( i_rSelectedRows ); + + if ( xConnection.is() && i_rORB.is() ) + { + Reference< XNumberFormatter > xFormatter( getNumberFormatter( xConnection, i_rORB ) ); + if ( xFormatter.is() ) + { + m_pHtml.set( new OHTMLImportExport( getDescriptor(), i_rORB, xFormatter ) ); + m_pRtf.set( new ORTFImportExport( getDescriptor(), i_rORB, xFormatter ) ); + } + } + + osl_atomic_decrement( &m_refCount ); + } + + bool ODataClipboard::WriteObject( ::tools::SvRef<SotStorageStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, const css::datatransfer::DataFlavor& /*rFlavor*/ ) + { + if (nUserObjectId == FORMAT_OBJECT_ID_RTF || nUserObjectId == FORMAT_OBJECT_ID_HTML ) + { + ODatabaseImportExport* pExport = static_cast<ODatabaseImportExport*>(pUserObject); + if ( pExport && rxOStm.is() ) + { + pExport->setStream(rxOStm.get()); + return pExport->Write(); + } + } + return false; + } + + void ODataClipboard::AddSupportedFormats() + { + if ( m_pRtf.is() ) + AddFormat( SotClipboardFormatId::RTF ); + + if ( m_pHtml.is() ) + AddFormat( SotClipboardFormatId::HTML ); + + ODataAccessObjectTransferable::AddSupportedFormats(); + } + + bool ODataClipboard::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) + { + const SotClipboardFormatId nFormat = SotExchange::GetFormat(rFlavor); + switch (nFormat) + { + case SotClipboardFormatId::RTF: + if ( m_pRtf.is() ) + m_pRtf->initialize(getDescriptor()); + return m_pRtf.is() && SetObject( m_pRtf.get(), FORMAT_OBJECT_ID_RTF, rFlavor ); + + case SotClipboardFormatId::HTML: + if ( m_pHtml.is() ) + m_pHtml->initialize(getDescriptor()); + return m_pHtml.is() && SetObject( m_pHtml.get(), FORMAT_OBJECT_ID_HTML, rFlavor ); + + default: break; + } + + return ODataAccessObjectTransferable::GetData(rFlavor, rDestDoc); + } + + void ODataClipboard::ObjectReleased() + { + if ( m_pHtml.is() ) + { + m_pHtml->dispose(); + m_pHtml.clear(); + } + + if ( m_pRtf.is() ) + { + m_pRtf->dispose(); + m_pRtf.clear(); + } + + if ( getDescriptor().has( DataAccessDescriptorProperty::Connection ) ) + { + Reference<XConnection> xConnection( getDescriptor()[DataAccessDescriptorProperty::Connection], UNO_QUERY ); + lcl_setListener( xConnection, this, false ); + } + + if ( getDescriptor().has( DataAccessDescriptorProperty::Cursor ) ) + { + Reference< XResultSet > xResultSet( getDescriptor()[ DataAccessDescriptorProperty::Cursor ], UNO_QUERY ); + lcl_setListener( xResultSet, this, false ); + } + + ODataAccessObjectTransferable::ObjectReleased( ); + } + + void SAL_CALL ODataClipboard::disposing( const css::lang::EventObject& i_rSource ) + { + ODataAccessDescriptor& rDescriptor( getDescriptor() ); + + if ( rDescriptor.has( DataAccessDescriptorProperty::Connection ) ) + { + Reference< XConnection > xConnection( rDescriptor[DataAccessDescriptorProperty::Connection], UNO_QUERY ); + if ( xConnection == i_rSource.Source ) + { + rDescriptor.erase( DataAccessDescriptorProperty::Connection ); + } + } + + if ( rDescriptor.has( DataAccessDescriptorProperty::Cursor ) ) + { + Reference< XResultSet > xResultSet( rDescriptor[ DataAccessDescriptorProperty::Cursor ], UNO_QUERY ); + if ( xResultSet == i_rSource.Source ) + { + rDescriptor.erase( DataAccessDescriptorProperty::Cursor ); + // Selection and BookmarkSelection are meaningless without a result set + if ( rDescriptor.has( DataAccessDescriptorProperty::Selection ) ) + rDescriptor.erase( DataAccessDescriptorProperty::Selection ); + if ( rDescriptor.has( DataAccessDescriptorProperty::BookmarkSelection ) ) + rDescriptor.erase( DataAccessDescriptorProperty::BookmarkSelection ); + } + } + + // no matter whether it was the source connection or the source result set which died, + // we cannot provide the data anymore. + ClearFormats(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dbloader.cxx b/dbaccess/source/ui/browser/dbloader.cxx new file mode 100644 index 000000000..62a3c14b1 --- /dev/null +++ b/dbaccess/source/ui/browser/dbloader.cxx @@ -0,0 +1,298 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbu_reghelper.hxx> +#include <strings.hxx> +#include <uiservices.hxx> +#include <UITools.hxx> + +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/frame/XController2.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XFrameLoader.hpp> +#include <com/sun/star/frame/XLoadEventListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/sdb/ReportDesign.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/frame/XModule.hpp> + +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/types.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::registry; +using namespace dbaui; + +namespace { + +class DBContentLoader : public ::cppu::WeakImplHelper< XFrameLoader, XServiceInfo> +{ +private: + Sequence< PropertyValue> m_aArgs; + Reference< XLoadEventListener > m_xListener; + Reference< XComponentContext > m_xContext; +public: + explicit DBContentLoader(const Reference< XComponentContext >&); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // static methods + static OUString getImplementationName_Static() throw( ) + { + return "org.openoffice.comp.dbu.DBContentLoader"; + } + static Sequence< OUString> getSupportedServiceNames_Static() throw( ); + static css::uno::Reference< css::uno::XInterface > + Create(const css::uno::Reference< css::lang::XMultiServiceFactory >&); + + // XLoader + virtual void SAL_CALL load( const Reference< XFrame > & _rFrame, const OUString& _rURL, + const Sequence< PropertyValue >& _rArgs, + const Reference< XLoadEventListener > & _rListener) override; + virtual void SAL_CALL cancel() override; +}; + +} + +DBContentLoader::DBContentLoader(const Reference< XComponentContext >& _rxContext) + :m_xContext(_rxContext) +{ + +} + +extern "C" void createRegistryInfo_DBContentLoader() +{ + static ::dbaui::OMultiInstanceAutoRegistration< DBContentLoader > aAutoRegistration; +} + +Reference< XInterface > DBContentLoader::Create( const Reference< XMultiServiceFactory > & rSMgr ) +{ + return *(new DBContentLoader(comphelper::getComponentContext(rSMgr))); +} + +// XServiceInfo +OUString SAL_CALL DBContentLoader::getImplementationName() +{ + return getImplementationName_Static(); +} + +// XServiceInfo +sal_Bool SAL_CALL DBContentLoader::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > SAL_CALL DBContentLoader::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +// ORegistryServiceManager_Static +Sequence< OUString > DBContentLoader::getSupportedServiceNames_Static() throw( ) +{ + return { "com.sun.star.frame.FrameLoader", "com.sun.star.sdb.ContentLoader" }; +} + +void SAL_CALL DBContentLoader::load(const Reference< XFrame > & rFrame, const OUString& rURL, + const Sequence< PropertyValue >& rArgs, + const Reference< XLoadEventListener > & rListener) +{ + m_xListener = rListener; + m_aArgs = rArgs; + + static const struct ServiceNameToImplName + { + const char* pAsciiServiceName; + const char* pAsciiImplementationName; + ServiceNameToImplName( const char* _pService, const char* _pImpl ) + :pAsciiServiceName( _pService ) + ,pAsciiImplementationName( _pImpl ) + { + } + } aImplementations[] = { + ServiceNameToImplName( URL_COMPONENT_FORMGRIDVIEW, "org.openoffice.comp.dbu.OFormGridView" ), + ServiceNameToImplName( URL_COMPONENT_DATASOURCEBROWSER, "org.openoffice.comp.dbu.ODatasourceBrowser" ), + ServiceNameToImplName( URL_COMPONENT_QUERYDESIGN, "org.openoffice.comp.dbu.OQueryDesign" ), + ServiceNameToImplName( URL_COMPONENT_TABLEDESIGN, "org.openoffice.comp.dbu.OTableDesign" ), + ServiceNameToImplName( URL_COMPONENT_RELATIONDESIGN, "org.openoffice.comp.dbu.ORelationDesign" ), + ServiceNameToImplName( URL_COMPONENT_VIEWDESIGN, "org.openoffice.comp.dbu.OViewDesign" ) + }; + + INetURLObject aParser( rURL ); + Reference< XController2 > xController; + + const OUString sComponentURL( aParser.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) ); + for (const ServiceNameToImplName& aImplementation : aImplementations) + { + if ( sComponentURL.equalsAscii( aImplementation.pAsciiServiceName ) ) + { + xController.set( m_xContext->getServiceManager()-> + createInstanceWithContext( OUString::createFromAscii( aImplementation.pAsciiImplementationName ), m_xContext), UNO_QUERY_THROW ); + break; + } + } + + // if a data source browser is loaded without its tree pane, then we assume it to be a + // table data view, effectively. In this case, we need to adjust the module identifier. + // #i85879# + ::comphelper::NamedValueCollection aLoadArgs( rArgs ); + + if ( sComponentURL == URL_COMPONENT_DATASOURCEBROWSER ) + { + bool bDisableBrowser = !aLoadArgs.getOrDefault( "ShowTreeViewButton", true ) // compatibility name + || !aLoadArgs.getOrDefault( PROPERTY_ENABLE_BROWSER, true ); + + if ( bDisableBrowser ) + { + try + { + Reference< XModule > xModule( xController, UNO_QUERY_THROW ); + xModule->setIdentifier( "com.sun.star.sdb.TableDataView" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + if ( sComponentURL == URL_COMPONENT_REPORTDESIGN ) + { + bool bPreview = aLoadArgs.getOrDefault( "Preview", false ); + if ( bPreview ) + { // report designs cannot be previewed + if ( rListener.is() ) + rListener->loadCancelled( this ); + return; + } + Reference< XModel > xReportModel( aLoadArgs.getOrDefault( "Model", Reference< XModel >() ) ); + if ( xReportModel.is() ) + { + xController.set( ReportDesign::create( m_xContext ) ); + xController->attachModel( xReportModel ); + xReportModel->connectController( xController.get() ); + xReportModel->setCurrentController( xController.get() ); + } + } + + bool bSuccess = xController.is(); + Reference< XModel > xDatabaseDocument; + if ( bSuccess ) + { + Reference< XDataSource > xDataSource ( aLoadArgs.getOrDefault( "DataSource", Reference< XDataSource >() ) ); + OUString sDataSourceName( aLoadArgs.getOrDefault( "DataSourceName", OUString() ) ); + Reference< XConnection > xConnection ( aLoadArgs.getOrDefault( "ActiveConnection", Reference< XConnection >() ) ); + if ( xDataSource.is() ) + { + xDatabaseDocument.set( getDataSourceOrModel( xDataSource ), UNO_QUERY ); + } + else if ( !sDataSourceName.isEmpty() ) + { + ::dbtools::SQLExceptionInfo aError; + xDataSource.set( getDataSourceByName( sDataSourceName, nullptr, m_xContext, &aError ) ); + xDatabaseDocument.set( getDataSourceOrModel( xDataSource ), UNO_QUERY ); + } + else if ( xConnection.is() ) + { + Reference< XChild > xAsChild( xConnection, UNO_QUERY ); + if ( xAsChild.is() ) + { + OSL_ENSURE( Reference< XDataSource >( xAsChild->getParent(), UNO_QUERY ).is(), + "DBContentLoader::load: a connection whose parent is no data source?" ); + xDatabaseDocument.set( getDataSourceOrModel( xAsChild->getParent() ), UNO_QUERY ); + } + } + + // init controller + SolarMutexGuard aGuard; + try + { + Reference<XInitialization > xIni(xController,UNO_QUERY); + PropertyValue aFrame("Frame",0,makeAny(rFrame),PropertyState_DIRECT_VALUE); + Sequence< Any > aInitArgs(m_aArgs.getLength()+1); + + Any* pBegin = aInitArgs.getArray(); + Any* pEnd = pBegin + aInitArgs.getLength(); + *pBegin <<= aFrame; + const PropertyValue* pIter = m_aArgs.getConstArray(); + for(++pBegin;pBegin != pEnd;++pBegin,++pIter) + { + *pBegin <<= *pIter; + } + + xIni->initialize(aInitArgs); + } + catch(const Exception&) + { + // Does this need to be shown to the user? + bSuccess = false; + try + { + ::comphelper::disposeComponent( xController ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + // assign controller and frame + if ( bSuccess ) + { + if ( xController.is() && rFrame.is() ) + { + rFrame->setComponent( xController->getComponentWindow(), xController.get() ); + xController->attachFrame(rFrame); + } + + if ( rListener.is() ) + rListener->loadFinished( this ); + } + else + if ( rListener.is() ) + rListener->loadCancelled( this ); +} + +void DBContentLoader::cancel() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dbtreemodel.cxx b/dbaccess/source/ui/browser/dbtreemodel.cxx new file mode 100644 index 000000000..88a9faeb0 --- /dev/null +++ b/dbaccess/source/ui/browser/dbtreemodel.cxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "dbtreemodel.hxx" + +namespace dbaui +{ + DBTreeListUserData::DBTreeListUserData() + :eType(SbaTableQueryBrowser::etQuery) + { + } + DBTreeListUserData::~DBTreeListUserData() + { + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dbtreemodel.hxx b/dbaccess/source/ui/browser/dbtreemodel.hxx new file mode 100644 index 000000000..42887fd00 --- /dev/null +++ b/dbaccess/source/ui/browser/dbtreemodel.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_DBACCESS_SOURCE_UI_BROWSER_DBTREEMODEL_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_BROWSER_DBTREEMODEL_HXX + +#include <unodatbr.hxx> +#include <commontypes.hxx> + +// syntax of the tree userdata +// datasource holds the connection +// queries holds the nameaccess for the queries +// query holds the query +// tables holds the nameaccess for the tables +// table holds the table + +#define CONTAINER_QUERIES sal_uLong( 0 ) +#define CONTAINER_TABLES sal_uLong( 1 ) + +namespace com::sun::star::lang { class XMultiServiceFactory; } + +namespace dbaui +{ + struct DBTreeListUserData + { + /// if the entry denotes a table or query, this is the respective UNO object + css::uno::Reference< css::beans::XPropertySet > + xObjectProperties; + /// if the entry denotes an object container, this is the UNO interface for this container + css::uno::Reference< css::uno::XInterface > + xContainer; + /// if the entry denotes a data source, this is the connection for this data source (if already connection) + SharedConnection xConnection; + SbaTableQueryBrowser::EntryType eType; + OUString sAccessor; + + DBTreeListUserData(); + ~DBTreeListUserData(); + }; +} + +#endif // INCLUDED_DBACCESS_SOURCE_UI_BROWSER_DBTREEMODEL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dbtreeview.cxx b/dbaccess/source/ui/browser/dbtreeview.cxx new file mode 100644 index 000000000..38464068b --- /dev/null +++ b/dbaccess/source/ui/browser/dbtreeview.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "dbtreeview.hxx" +#include <vcl/treelistbox.hxx> +#include <dbtreelistbox.hxx> +#include <helpids.h> + +namespace dbaui +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +DBTreeView::DBTreeView( vcl::Window* pParent, WinBits nBits) + : Window( pParent, nBits ) + , m_pTreeListBox(nullptr) +{ + + m_pTreeListBox = VclPtr<DBTreeListBox>::Create(this, WB_BORDER | WB_HASLINES | WB_HASLINESATROOT | WB_HASBUTTONS | WB_HSCROLL |WB_HASBUTTONSATROOT); + m_pTreeListBox->EnableCheckButton(nullptr); + m_pTreeListBox->SetDragDropMode( DragDropMode::NONE ); + m_pTreeListBox->EnableInplaceEditing( true ); + m_pTreeListBox->SetHelpId(HID_TLB_TREELISTBOX); + m_pTreeListBox->Show(); +} + +DBTreeView::~DBTreeView() +{ + disposeOnce(); +} + +void DBTreeView::dispose() +{ + m_pTreeListBox.disposeAndClear(); + vcl::Window::dispose(); +} + +SvTreeList* DBTreeView::GetTreeModel() +{ + return m_pTreeListBox->GetModel(); +} + +void DBTreeView::SetPreExpandHandler(const Link<SvTreeListEntry*,bool>& _rHdl) +{ + m_pTreeListBox->SetPreExpandHandler(_rHdl); +} + +void DBTreeView::setCopyHandler(const Link<LinkParamNone*,void>& _rHdl) +{ + m_pTreeListBox->setCopyHandler(_rHdl); +} + +void DBTreeView::Resize() +{ + Window::Resize(); + m_pTreeListBox->SetPosSizePixel(Point(0,0),GetOutputSizePixel()); +} + +void DBTreeView::setSelChangeHdl( const Link<LinkParamNone*,void>& _rHdl ) +{ + m_pTreeListBox->SetSelChangeHdl( _rHdl ); +} + +void DBTreeView::GetFocus() +{ + Window::GetFocus(); + if ( m_pTreeListBox )//&& !m_pTreeListBox->HasChildPathFocus()) + m_pTreeListBox->GrabFocus(); +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dbtreeview.hxx b/dbaccess/source/ui/browser/dbtreeview.hxx new file mode 100644 index 000000000..892a6beab --- /dev/null +++ b/dbaccess/source/ui/browser/dbtreeview.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_DBACCESS_SOURCE_UI_BROWSER_DBTREEVIEW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_BROWSER_DBTREEVIEW_HXX + +#include <vcl/window.hxx> + +class SvTreeList; +class SvTreeListEntry; + +namespace dbaui +{ + class DBTreeListBox; + // - DBTreeView - + + class DBTreeView : public vcl::Window + { + private: + VclPtr<DBTreeListBox> m_pTreeListBox; + protected: + // window overridables + virtual void Resize() override; + public: + + DBTreeView( vcl::Window* pParent, + WinBits nBits ); + virtual ~DBTreeView() override; + virtual void dispose() override; + + /** sets a handler which is called when a list box entry is to be expanded. + <p>When calling the link, the parameter is an SvTreeListEntry marking the entry to be expanded. + </p> + */ + void SetPreExpandHandler(const Link<SvTreeListEntry*,bool>& _rHdl); + + void setCopyHandler(const Link<LinkParamNone*,void>& _rHdl); + + SvTreeList* GetTreeModel(); + void setSelChangeHdl(const Link<LinkParamNone*,void>& _rHdl); + + DBTreeListBox& getListBox() const { return *m_pTreeListBox; } + + virtual void GetFocus() override; + }; +} + +#endif // INCLUDED_DBACCESS_SOURCE_UI_BROWSER_DBTREEVIEW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dsEntriesNoExp.cxx b/dbaccess/source/ui/browser/dsEntriesNoExp.cxx new file mode 100644 index 000000000..7e9bfc851 --- /dev/null +++ b/dbaccess/source/ui/browser/dsEntriesNoExp.cxx @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <memory> + +#include <unodatbr.hxx> +#include <browserids.hxx> +#include <listviewitems.hxx> +#include <imageprovider.hxx> +#include <osl/diagnose.h> +#include "dbtreeview.hxx" +#include <dbtreelistbox.hxx> +#include "dbtreemodel.hxx" +#include <vcl/treelistentry.hxx> + +using namespace ::com::sun::star::frame; +using namespace ::dbtools; +using namespace ::svx; + +namespace dbaui +{ +SbaTableQueryBrowser::EntryType SbaTableQueryBrowser::getChildType( SvTreeListEntry const * _pEntry ) const +{ + OSL_ENSURE(isContainer(_pEntry), "SbaTableQueryBrowser::getChildType: invalid entry!"); + switch (getEntryType(_pEntry)) + { + case etTableContainer: + return etTableOrView; + case etQueryContainer: + return etQuery; + default: + break; + } + return etUnknown; +} + +OUString SbaTableQueryBrowser::GetEntryText( SvTreeListEntry* _pEntry ) const +{ + return m_pTreeView->getListBox().GetEntryText(_pEntry); +} + +SbaTableQueryBrowser::EntryType SbaTableQueryBrowser::getEntryType( const SvTreeListEntry* _pEntry ) const +{ + if (!_pEntry) + return etUnknown; + + SvTreeListEntry* pRootEntry = m_pTreeView->getListBox().GetRootLevelParent(const_cast<SvTreeListEntry*>(_pEntry)); + SvTreeListEntry* pEntryParent = m_pTreeView->getListBox().GetParent(const_cast<SvTreeListEntry*>(_pEntry)); + SvTreeListEntry* pTables = m_pTreeView->getListBox().GetEntry(pRootEntry, CONTAINER_TABLES); + SvTreeListEntry* pQueries = m_pTreeView->getListBox().GetEntry(pRootEntry, CONTAINER_QUERIES); + +#ifdef DBG_UTIL + OUString sTest; + if (pTables) sTest = m_pTreeView->getListBox().GetEntryText(pTables); + if (pQueries) sTest = m_pTreeView->getListBox().GetEntryText(pQueries); +#endif + + if (pRootEntry == _pEntry) + return etDatasource; + + if (pTables == _pEntry) + return etTableContainer; + + if (pQueries == _pEntry) + return etQueryContainer; + + if (pTables == pEntryParent) + return etTableOrView; + + if (pQueries == pEntryParent) + { + DBTreeListUserData* pEntryData = static_cast<DBTreeListUserData*>(_pEntry->GetUserData()); + if ( pEntryData ) + return pEntryData->eType; + + return etQuery; + } + while( pEntryParent != pQueries ) + { + pEntryParent = m_pTreeView->getListBox().GetParent(pEntryParent); + if ( !pEntryParent ) + return etUnknown; + } + + return etQueryContainer; +} + +void SbaTableQueryBrowser::select(SvTreeListEntry* _pEntry, bool _bSelect) +{ + SvLBoxItem* pTextItem = _pEntry ? _pEntry->GetFirstItem(SvLBoxItemType::String) : nullptr; + if (pTextItem) + { + static_cast<OBoldListboxString*>(pTextItem)->emphasize(_bSelect); + m_pTreeView->GetTreeModel()->InvalidateEntry(_pEntry); + } + else { + OSL_FAIL("SbaTableQueryBrowser::select: invalid entry!"); + } +} + +void SbaTableQueryBrowser::selectPath(SvTreeListEntry* _pEntry, bool _bSelect) +{ + while (_pEntry) + { + select(_pEntry, _bSelect); + _pEntry = m_pTreeView->GetTreeModel()->GetParent(_pEntry); + } +} + +bool SbaTableQueryBrowser::isSelected(SvTreeListEntry* _pEntry) +{ + SvLBoxItem* pTextItem = _pEntry ? _pEntry->GetFirstItem(SvLBoxItemType::String) : nullptr; + if (pTextItem) + return static_cast<OBoldListboxString*>(pTextItem)->isEmphasized(); + else { + OSL_FAIL("SbaTableQueryBrowser::isSelected: invalid entry!"); + } + return false; +} + +void SbaTableQueryBrowser::SelectionChanged() +{ + if ( !m_bShowMenu ) + { + InvalidateFeature(ID_BROWSER_INSERTCOLUMNS); + InvalidateFeature(ID_BROWSER_INSERTCONTENT); + InvalidateFeature(ID_BROWSER_FORMLETTER); + } + InvalidateFeature(ID_BROWSER_COPY); + InvalidateFeature(ID_BROWSER_CUT); +} + +void SbaTableQueryBrowser::describeSupportedFeatures() +{ + SbaXDataBrowserController::describeSupportedFeatures(); + + implDescribeSupportedFeature( ".uno:Title", ID_BROWSER_TITLE ); + if ( !m_bShowMenu ) + { + implDescribeSupportedFeature( ".uno:DSBEditDB", ID_TREE_EDIT_DATABASE ); + implDescribeSupportedFeature( ".uno:DSBCloseConnection", ID_TREE_CLOSE_CONN ); + implDescribeSupportedFeature( ".uno:DSBAdministrate", ID_TREE_ADMINISTRATE ); + + implDescribeSupportedFeature( ".uno:DSBrowserExplorer", ID_BROWSER_EXPLORER, CommandGroup::VIEW ); + + implDescribeSupportedFeature( ".uno:DSBFormLetter", ID_BROWSER_FORMLETTER, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:DSBInsertColumns", ID_BROWSER_INSERTCOLUMNS, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DSBInsertContent", ID_BROWSER_INSERTCONTENT, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DSBDocumentDataSource", ID_BROWSER_DOCUMENT_DATASOURCE, CommandGroup::VIEW ); + + implDescribeSupportedFeature( ".uno:DataSourceBrowser/FormLetter", ID_BROWSER_FORMLETTER ); + implDescribeSupportedFeature( ".uno:DataSourceBrowser/InsertColumns", ID_BROWSER_INSERTCOLUMNS ); + implDescribeSupportedFeature( ".uno:DataSourceBrowser/InsertContent", ID_BROWSER_INSERTCONTENT ); + implDescribeSupportedFeature( ".uno:DataSourceBrowser/DocumentDataSource", ID_BROWSER_DOCUMENT_DATASOURCE ); + } + + implDescribeSupportedFeature( ".uno:CloseWin", ID_BROWSER_CLOSE, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:DBRebuildData", ID_BROWSER_REFRESH_REBUILD, CommandGroup::DATA ); +} + +sal_Int32 SbaTableQueryBrowser::getDatabaseObjectType( EntryType _eType ) +{ + switch ( _eType ) + { + case etQuery: + case etQueryContainer: + return css::sdb::application::DatabaseObject::QUERY; + case etTableOrView: + case etTableContainer: + return css::sdb::application::DatabaseObject::TABLE; + default: + break; + } + OSL_FAIL( "SbaTableQueryBrowser::getDatabaseObjectType: folder types and 'Unknown' not allowed here!" ); + return css::sdb::application::DatabaseObject::TABLE; +} + +void SbaTableQueryBrowser::notifyHiContrastChanged() +{ + if ( !m_pTreeView ) + return; + + auto pTreeModel = m_pTreeView->GetTreeModel(); + // change all bitmap entries + SvTreeListEntry* pEntryLoop = pTreeModel->First(); + while ( pEntryLoop ) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pEntryLoop->GetUserData()); + if ( !pData ) + { + pEntryLoop = pTreeModel->Next(pEntryLoop); + continue; + } + + // the connection to which this entry belongs, if any + std::unique_ptr< ImageProvider > pImageProvider( getImageProviderFor( pEntryLoop ) ); + + // the images for this entry + Image aImage; + if ( pData->eType == etDatasource ) + aImage = ImageProvider::getDatabaseImage(); + else + { + bool bIsFolder = !isObject( pData->eType ); + if ( bIsFolder ) + { + sal_Int32 nObjectType( getDatabaseObjectType( pData->eType ) ); + aImage = ImageProvider::getFolderImage( nObjectType ); + } + else + { + sal_Int32 nObjectType( getDatabaseObjectType( pData->eType ) ); + pImageProvider->getImages( GetEntryText( pEntryLoop ), nObjectType, aImage ); + } + } + + // find the proper item, and set its icons + sal_uInt16 nCount = pEntryLoop->ItemCount(); + for (sal_uInt16 i=0;i<nCount;++i) + { + SvLBoxItem& rItem = pEntryLoop->GetItem(i); + if (rItem.GetType() != SvLBoxItemType::ContextBmp) + continue; + + SvLBoxContextBmp& rContextBitmapItem = static_cast< SvLBoxContextBmp& >( rItem ); + + rContextBitmapItem.SetBitmap1( aImage ); + rContextBitmapItem.SetBitmap2( aImage ); + break; + } + + pEntryLoop = pTreeModel->Next(pEntryLoop); + } +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/dsbrowserDnD.cxx b/dbaccess/source/ui/browser/dsbrowserDnD.cxx new file mode 100644 index 000000000..cd8c124f0 --- /dev/null +++ b/dbaccess/source/ui/browser/dsbrowserDnD.cxx @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbexchange.hxx> +#include <dbtreelistbox.hxx> +#include "dbtreemodel.hxx" +#include "dbtreeview.hxx" +#include <UITools.hxx> +#include <unodatbr.hxx> + +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> + +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <vcl/treelistentry.hxx> +#include <vcl/svapp.hxx> + +#include <algorithm> + +namespace dbaui +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::sdb; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::sdbcx; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::container; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::form; + using namespace ::com::sun::star::io; + using namespace ::com::sun::star::i18n; + using namespace ::com::sun::star::task; + using namespace ::com::sun::star::datatransfer; + using namespace ::dbtools; + using namespace ::svx; + + TransferableHelper* SbaTableQueryBrowser::implCopyObject( SvTreeListEntry* _pApplyTo, sal_Int32 _nCommandType ) + { + try + { + OUString aName = GetEntryText( _pApplyTo ); + OUString aDSName = getDataSourceAccessor( m_pTreeView->getListBox().GetRootLevelParent( _pApplyTo ) ); + + ODataClipboard* pData = nullptr; + SharedConnection xConnection; + if ( CommandType::QUERY != _nCommandType ) + { + if ( !ensureConnection( _pApplyTo, xConnection) ) + return nullptr; + pData = new ODataClipboard(aDSName, _nCommandType, aName, xConnection, getNumberFormatter(), getORB()); + } + else + pData = new ODataClipboard(aDSName, _nCommandType, aName, getNumberFormatter(), getORB()); + + // the ownership goes to ODataClipboards + return pData; + } + catch(const SQLException& ) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return nullptr; + } + sal_Int8 SbaTableQueryBrowser::queryDrop( const AcceptDropEvent& _rEvt, const DataFlavorExVector& _rFlavors ) + { + // check if we're a table or query container + SvTreeListEntry* pHitEntry = m_pTreeView->getListBox().GetEntry( _rEvt.maPosPixel ); + + if ( pHitEntry ) // no drop if no entry was hit... + { + // it must be a container + EntryType eEntryType = getEntryType( pHitEntry ); + SharedConnection xConnection; + if ( eEntryType == etTableContainer && ensureConnection( pHitEntry, xConnection ) && xConnection.is() ) + { + Reference<XChild> xChild(xConnection,UNO_QUERY); + Reference<XStorable> xStore; + if ( xChild.is() ) + xStore.set( getDataSourceOrModel(xChild->getParent()), UNO_QUERY ); + // check for the concrete type + if ( xStore.is() && !xStore->isReadonly() && std::any_of(_rFlavors.begin(),_rFlavors.end(),TAppSupportedSotFunctor(E_TABLE)) ) + return DND_ACTION_COPY; + } + } + + return DND_ACTION_NONE; + } + sal_Int8 SbaTableQueryBrowser::executeDrop( const ExecuteDropEvent& _rEvt ) + { + SvTreeListEntry* pHitEntry = m_pTreeView->getListBox().GetEntry( _rEvt.maPosPixel ); + EntryType eEntryType = getEntryType( pHitEntry ); + if (!isContainer(eEntryType)) + { + OSL_FAIL("SbaTableQueryBrowser::executeDrop: what the hell did queryDrop do?"); + // queryDrop should not have allowed us to reach this situation... + return DND_ACTION_NONE; + } + // a TransferableDataHelper for accessing the dropped data + TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable); + + // reset the data of the previous async drop (if any) + if ( m_nAsyncDrop ) + Application::RemoveUserEvent(m_nAsyncDrop); + + m_nAsyncDrop = nullptr; + m_aAsyncDrop.aDroppedData.clear(); + m_aAsyncDrop.nType = E_TABLE; + m_aAsyncDrop.nAction = _rEvt.mnAction; + m_aAsyncDrop.bError = false; + m_aAsyncDrop.bHtml = false; + m_aAsyncDrop.pDroppedAt = nullptr; + m_aAsyncDrop.aUrl.clear(); + + // loop through the available formats and see what we can do ... + // first we have to check if it is our own format, if not we have to copy the stream :-( + if ( ODataAccessObjectTransferable::canExtractObjectDescriptor(aDroppedData.GetDataFlavorExVector()) ) + { + m_aAsyncDrop.aDroppedData = ODataAccessObjectTransferable::extractObjectDescriptor(aDroppedData); + m_aAsyncDrop.pDroppedAt = pHitEntry; + + // asynchron because we some dialogs and we aren't allowed to show them while in D&D + m_nAsyncDrop = Application::PostUserEvent(LINK(this, SbaTableQueryBrowser, OnAsyncDrop)); + return DND_ACTION_COPY; + } + else + { + SharedConnection xDestConnection; + if ( ensureConnection( pHitEntry, xDestConnection ) + && xDestConnection.is() + && m_aTableCopyHelper.copyTagTable( aDroppedData, m_aAsyncDrop, xDestConnection ) + ) + { + m_aAsyncDrop.pDroppedAt = pHitEntry; + + // asynchron because we some dialogs and we aren't allowed to show them while in D&D + m_nAsyncDrop = Application::PostUserEvent(LINK(this, SbaTableQueryBrowser, OnAsyncDrop)); + return DND_ACTION_COPY; + } + } + + return DND_ACTION_NONE; + } + + bool SbaTableQueryBrowser::requestDrag( const Point& _rPosPixel ) + { + // get the affected list entry + // ensure that the entry which the user clicked at is selected + SvTreeListEntry* pHitEntry = m_pTreeView->getListBox().GetEntry( _rPosPixel ); + if (!pHitEntry) + // no drag of no entry was hit... + return false; + + // it must be a query/table + EntryType eEntryType = getEntryType( pHitEntry ); + if (!isObject(eEntryType)) + return false; + + rtl::Reference<TransferableHelper> pTransfer = implCopyObject( pHitEntry, ( etTableOrView == eEntryType ) ? CommandType::TABLE : CommandType::QUERY); + + if (pTransfer) + pTransfer->StartDrag( &m_pTreeView->getListBox(), DND_ACTION_COPY ); + + return pTransfer.is(); + } + IMPL_LINK_NOARG(SbaTableQueryBrowser, OnCopyEntry, LinkParamNone*, void) + { + SvTreeListEntry* pSelected = m_pTreeView->getListBox().FirstSelected(); + if( isEntryCopyAllowed( pSelected ) ) + copyEntry( pSelected ); + } + bool SbaTableQueryBrowser::isEntryCopyAllowed(SvTreeListEntry const * _pEntry) const + { + EntryType eType = getEntryType(_pEntry); + return ( eType == etTableOrView || eType == etQuery ); + } + void SbaTableQueryBrowser::copyEntry(SvTreeListEntry* _pEntry) + { + TransferableHelper* pTransfer = nullptr; + Reference< XTransferable> aEnsureDelete; + EntryType eType = getEntryType(_pEntry); + pTransfer = implCopyObject( _pEntry, eType == etQuery ? CommandType::QUERY : CommandType::TABLE); + aEnsureDelete = pTransfer; + if (pTransfer) + pTransfer->CopyToClipboard(getView()); + } + IMPL_LINK_NOARG( SbaTableQueryBrowser, OnAsyncDrop, void*, void ) + { + m_nAsyncDrop = nullptr; + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( m_aAsyncDrop.nType == E_TABLE ) + { + SharedConnection xDestConnection; + if ( ensureConnection( m_aAsyncDrop.pDroppedAt, xDestConnection ) && xDestConnection.is() ) + { + SvTreeListEntry* pDataSourceEntry = m_pTreeView->getListBox().GetRootLevelParent(m_aAsyncDrop.pDroppedAt); + m_aTableCopyHelper.asyncCopyTagTable( m_aAsyncDrop, getDataSourceAccessor( pDataSourceEntry ), xDestConnection ); + } + } + + m_aAsyncDrop.aDroppedData.clear(); + } + void SbaTableQueryBrowser::clearTreeModel() + { + if (m_pTreeView) + { + auto pTreeModel = m_pTreeView->GetTreeModel(); + // clear the user data of the tree model + SvTreeListEntry* pEntryLoop = pTreeModel->First(); + while (pEntryLoop) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pEntryLoop->GetUserData()); + if(pData) + { + pEntryLoop->SetUserData(nullptr); + Reference< XContainer > xContainer(pData->xContainer, UNO_QUERY); + if (xContainer.is()) + xContainer->removeContainerListener(this); + + if ( pData->xConnection.is() ) + { + OSL_ENSURE( impl_isDataSourceEntry( pEntryLoop ), "SbaTableQueryBrowser::clearTreeModel: no data source entry, but a connection?" ); + // connections are to be stored *only* at the data source entries + impl_releaseConnection( pData->xConnection ); + } + + delete pData; + } + pEntryLoop = pTreeModel->Next(pEntryLoop); + } + } + m_pCurrentlyDisplayed = nullptr; + } +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/exsrcbrw.cxx b/dbaccess/source/ui/browser/exsrcbrw.cxx new file mode 100644 index 000000000..e468f9a5a --- /dev/null +++ b/dbaccess/source/ui/browser/exsrcbrw.cxx @@ -0,0 +1,434 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <exsrcbrw.hxx> +#include <uiservices.hxx> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> +#include <com/sun/star/form/XLoadable.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <formadapter.hxx> +#include <comphelper/processfactory.hxx> +#include <strings.hxx> +#include <dbu_reghelper.hxx> +#include <o3tl/any.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::frame; +using namespace dbaui; + +// SbaExternalSourceBrowser +extern "C" void createRegistryInfo_OFormGridView() +{ + static OMultiInstanceAutoRegistration< SbaExternalSourceBrowser > aAutoRegistration; +} + +Any SAL_CALL SbaExternalSourceBrowser::queryInterface(const Type& _rType) +{ + Any aRet = SbaXDataBrowserController::queryInterface(_rType); + if(!aRet.hasValue()) + aRet = ::cppu::queryInterface(_rType, + static_cast<css::util::XModifyBroadcaster*>(this), + static_cast<css::form::XLoadListener*>(this)); + + return aRet; +} + +SbaExternalSourceBrowser::SbaExternalSourceBrowser(const Reference< css::uno::XComponentContext >& _rM) + :SbaXDataBrowserController(_rM) + ,m_aModifyListeners(getMutex()) + ,m_pDataSourceImpl(nullptr) + ,m_bInQueryDispatch( false ) +{ + +} + +SbaExternalSourceBrowser::~SbaExternalSourceBrowser() +{ + +} + +css::uno::Sequence<OUString> SAL_CALL SbaExternalSourceBrowser::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +OUString SbaExternalSourceBrowser::getImplementationName_Static() +{ + return "org.openoffice.comp.dbu.OFormGridView"; +} + +css::uno::Sequence<OUString> SbaExternalSourceBrowser::getSupportedServiceNames_Static() +{ + css::uno::Sequence<OUString> aSupported { "com.sun.star.sdb.FormGridView" }; + return aSupported; +} + +Reference< XInterface > SbaExternalSourceBrowser::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + return *(new SbaExternalSourceBrowser( comphelper::getComponentContext(_rxFactory))); +} + +OUString SAL_CALL SbaExternalSourceBrowser::getImplementationName() +{ + return getImplementationName_Static(); +} + +Reference< XRowSet > SbaExternalSourceBrowser::CreateForm() +{ + m_pDataSourceImpl = new SbaXFormAdapter(); + return m_pDataSourceImpl; +} + +bool SbaExternalSourceBrowser::InitializeForm(const Reference< XPropertySet > & /*i_formProperties*/) +{ + return true; +} + +bool SbaExternalSourceBrowser::LoadForm() +{ + // as we don't have a main form (yet), we have nothing to do + // we don't call FormLoaded, because this expects a working data source + return true; +} + +void SbaExternalSourceBrowser::modified(const css::lang::EventObject& aEvent) +{ + SbaXDataBrowserController::modified(aEvent); + + // multiplex this event to all my listeners + css::lang::EventObject aEvt(*this); + ::comphelper::OInterfaceIteratorHelper2 aIt(m_aModifyListeners); + while (aIt.hasMoreElements()) + static_cast< css::util::XModifyListener*>(aIt.next())->modified(aEvt); +} + +void SAL_CALL SbaExternalSourceBrowser::dispatch(const css::util::URL& aURL, const Sequence< css::beans::PropertyValue>& aArgs) +{ + if ( aURL.Complete == ".uno:FormSlots/AddGridColumn" ) + { + // search the argument describing the column to create + OUString sControlType; + sal_Int32 nControlPos = -1; + Sequence< css::beans::PropertyValue> aControlProps; + for ( const css::beans::PropertyValue& rArgument : aArgs ) + { + if ( rArgument.Name == "ColumnType" ) + { + auto s = o3tl::tryAccess<OUString>(rArgument.Value); + OSL_ENSURE(s, "invalid type for argument \"ColumnType\" !"); + if (s) + sControlType = *s; + } + else if ( rArgument.Name == "ColumnPosition" ) + { + auto n = o3tl::tryAccess<sal_Int16>(rArgument.Value); + OSL_ENSURE(n, "invalid type for argument \"ColumnPosition\" !"); + if (n) + nControlPos = *n; + } + else if ( rArgument.Name == "ColumnProperties" ) + { + auto s = o3tl::tryAccess<Sequence<css::beans::PropertyValue>>( + rArgument.Value); + OSL_ENSURE(s, "invalid type for argument \"ColumnProperties\" !"); + if (s) + aControlProps = *s; + } + else + SAL_WARN("dbaccess.ui", "SbaExternalSourceBrowser::dispatch(AddGridColumn) : unknown argument (" << rArgument.Name << ") !"); + } + if (sControlType.isEmpty()) + { + SAL_WARN("dbaccess.ui", "SbaExternalSourceBrowser::dispatch(AddGridColumn) : missing argument (ColumnType) !"); + sControlType = "TextField"; + } + OSL_ENSURE(aControlProps.hasElements(), "SbaExternalSourceBrowser::dispatch(AddGridColumn) : missing argument (ColumnProperties) !"); + + // create the col + Reference< css::form::XGridColumnFactory > xColFactory(getControlModel(), UNO_QUERY); + Reference< css::beans::XPropertySet > xNewCol = xColFactory->createColumn(sControlType); + Reference< XPropertySetInfo > xNewColProperties; + if (xNewCol.is()) + xNewColProperties = xNewCol->getPropertySetInfo(); + // set its properties + if (xNewColProperties.is()) + { + for (const css::beans::PropertyValue& rControlProp : std::as_const(aControlProps)) + { + try + { + if (xNewColProperties->hasPropertyByName(rControlProp.Name)) + xNewCol->setPropertyValue(rControlProp.Name, rControlProp.Value); + } + catch (const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaExternalSourceBrowser::dispatch : could not set a column property (" << rControlProp.Name << ")!"); + } + } + } + + // correct the position + Reference< css::container::XIndexContainer > xColContainer(getControlModel(), UNO_QUERY); + + if (nControlPos > xColContainer->getCount()) + nControlPos = xColContainer->getCount(); + if (nControlPos < 0) + nControlPos = 0; + + // append the column + xColContainer->insertByIndex(nControlPos, makeAny(xNewCol)); + } + else if ( aURL.Complete == ".uno:FormSlots/ClearView" ) + { + ClearView(); + } + else if ( aURL.Complete == ".uno:FormSlots/AttachToForm" ) + { + if (!m_pDataSourceImpl) + return; + + Reference< XRowSet > xMasterForm; + // search the arguments for the master form + for (const css::beans::PropertyValue& rArgument : aArgs) + { + if ( (rArgument.Name == "MasterForm") && (rArgument.Value.getValueTypeClass() == TypeClass_INTERFACE) ) + { + xMasterForm.set(rArgument.Value, UNO_QUERY); + break; + } + } + if (!xMasterForm.is()) + { + SAL_WARN("dbaccess.ui", "SbaExternalSourceBrowser::dispatch(FormSlots/AttachToForm) : please specify a form to attach to as argument !"); + return; + } + + Attach(xMasterForm); + } + else + SbaXDataBrowserController::dispatch(aURL, aArgs); +} + +Reference< css::frame::XDispatch > SAL_CALL SbaExternalSourceBrowser::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags) +{ + Reference< css::frame::XDispatch > xReturn; + if (m_bInQueryDispatch) + return xReturn; + + m_bInQueryDispatch = true; + + if ( ( aURL.Complete == ".uno:FormSlots/AttachToForm" ) + // attach a new external form + || ( aURL.Complete == ".uno:FormSlots/AddGridColumn" ) + // add a column to the grid + || ( aURL.Complete == ".uno:FormSlots/ClearView" ) + // clear the grid + ) + xReturn = static_cast<css::frame::XDispatch*>(this); + + if ( !xReturn.is() + && ( (aURL.Complete == ".uno:FormSlots/moveToFirst" ) || (aURL.Complete == ".uno:FormSlots/moveToPrev" ) + || (aURL.Complete == ".uno:FormSlots/moveToNext" ) || (aURL.Complete == ".uno:FormSlots/moveToLast" ) + || (aURL.Complete == ".uno:FormSlots/moveToNew" ) || (aURL.Complete == ".uno:FormSlots/undoRecord" ) + ) + ) + { + OSL_ENSURE(aURL.Mark.isEmpty(), "SbaExternalSourceBrowser::queryDispatch : the css::util::URL shouldn't have a mark !"); + css::util::URL aNewUrl = aURL; + + // split the css::util::URL + OSL_ENSURE( m_xUrlTransformer.is(), "SbaExternalSourceBrowser::queryDispatch : could not create a URLTransformer !" ); + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aNewUrl ); + + // set a new mark + aNewUrl.Mark = "DB/FormGridView"; + // this controller is instantiated when somebody dispatches the ".component:DB/FormGridView" in any + // frame, so we use "FormGridView" as mark that a dispatch request came from this view + + if (m_xUrlTransformer.is()) + m_xUrlTransformer->assemble(aNewUrl); + + Reference< XDispatchProvider > xFrameDispatcher( getFrame(), UNO_QUERY ); + if (xFrameDispatcher.is()) + xReturn = xFrameDispatcher->queryDispatch(aNewUrl, aTargetFrameName, FrameSearchFlag::PARENT); + + } + + if (!xReturn.is()) + xReturn = SbaXDataBrowserController::queryDispatch(aURL, aTargetFrameName, nSearchFlags); + + m_bInQueryDispatch = false; + return xReturn; +} + +void SAL_CALL SbaExternalSourceBrowser::disposing() +{ + // say our modify listeners goodbye + css::lang::EventObject aEvt; + aEvt.Source = static_cast<XWeak*>(this); + m_aModifyListeners.disposeAndClear(aEvt); + + stopListening(); + + SbaXDataBrowserController::disposing(); +} + +void SAL_CALL SbaExternalSourceBrowser::addModifyListener(const Reference< css::util::XModifyListener > & aListener) +{ + m_aModifyListeners.addInterface(aListener); +} + +void SAL_CALL SbaExternalSourceBrowser::removeModifyListener(const Reference< css::util::XModifyListener > & aListener) +{ + m_aModifyListeners.removeInterface(aListener); +} + +void SAL_CALL SbaExternalSourceBrowser::unloading(const css::lang::EventObject& aEvent) +{ + if (m_pDataSourceImpl && (m_pDataSourceImpl->getAttachedForm() == aEvent.Source)) + { + ClearView(); + } + + SbaXDataBrowserController::unloading(aEvent); +} + +void SbaExternalSourceBrowser::Attach(const Reference< XRowSet > & xMaster) +{ + Any aOldPos; + bool bWasInsertRow = false; + bool bBeforeFirst = true; + bool bAfterLast = true; + Reference< XRowLocate > xCursor(xMaster, UNO_QUERY); + Reference< XPropertySet > xMasterProps(xMaster, UNO_QUERY); + + try + { + // switch the control to design mode + if (getBrowserView() && getBrowserView()->getGridControl().is()) + getBrowserView()->getGridControl()->setDesignMode(true); + + // the grid will move the form's cursor to the first record, but we want the form to remain unchanged + // restore the old position + if (xCursor.is() && xMaster.is()) + { + bBeforeFirst = xMaster->isBeforeFirst(); + bAfterLast = xMaster->isAfterLast(); + if(!bBeforeFirst && !bAfterLast) + aOldPos = xCursor->getBookmark(); + } + + if (xMasterProps.is()) + xMasterProps->getPropertyValue(PROPERTY_ISNEW) >>= bWasInsertRow; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + onStartLoading( Reference< XLoadable >( xMaster, UNO_QUERY ) ); + + stopListening(); + m_pDataSourceImpl->AttachForm(xMaster); + startListening(); + + if (!xMaster.is()) + return; + + // at this point we have to reset the formatter for the new form + initFormatter(); + // assume that the master form is already loaded +#if OSL_DEBUG_LEVEL > 0 + { + Reference< XLoadable > xLoadable( xMaster, UNO_QUERY ); + OSL_ENSURE( xLoadable.is() && xLoadable->isLoaded(), "SbaExternalSourceBrowser::Attach: master is not loaded!" ); + } +#endif + + LoadFinished(true); + + Reference< XResultSetUpdate > xUpdate(xMaster, UNO_QUERY); + try + { + if (bWasInsertRow && xUpdate.is()) + xUpdate->moveToInsertRow(); + else if (xCursor.is() && aOldPos.hasValue()) + xCursor->moveToBookmark(aOldPos); + else if(bBeforeFirst && xMaster.is()) + xMaster->beforeFirst(); + else if(bAfterLast && xMaster.is()) + xMaster->afterLast(); + } + catch(Exception&) + { + SAL_WARN("dbaccess.ui", "SbaExternalSourceBrowser::Attach : couldn't restore the cursor position !"); + } +} + +void SbaExternalSourceBrowser::ClearView() +{ + // set a new (empty) datasource + Attach(Reference< XRowSet > ()); + + // clear all cols in the grid + Reference< css::container::XIndexContainer > xColContainer(getControlModel(), UNO_QUERY); + while (xColContainer->getCount() > 0) + xColContainer->removeByIndex(0); +} + +void SAL_CALL SbaExternalSourceBrowser::disposing(const css::lang::EventObject& Source) +{ + if (m_pDataSourceImpl && (m_pDataSourceImpl->getAttachedForm() == Source.Source)) + { + ClearView(); + } + + SbaXDataBrowserController::disposing(Source); +} + +void SbaExternalSourceBrowser::startListening() +{ + if (m_pDataSourceImpl && m_pDataSourceImpl->getAttachedForm().is()) + { + Reference< css::form::XLoadable > xLoadable(m_pDataSourceImpl->getAttachedForm(), UNO_QUERY); + xLoadable->addLoadListener(static_cast<css::form::XLoadListener*>(this)); + } +} + +void SbaExternalSourceBrowser::stopListening() +{ + if (m_pDataSourceImpl && m_pDataSourceImpl->getAttachedForm().is()) + { + Reference< css::form::XLoadable > xLoadable(m_pDataSourceImpl->getAttachedForm(), UNO_QUERY); + xLoadable->removeLoadListener(static_cast<css::form::XLoadListener*>(this)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/formadapter.cxx b/dbaccess/source/ui/browser/formadapter.cxx new file mode 100644 index 000000000..9620d5840 --- /dev/null +++ b/dbaccess/source/ui/browser/formadapter.cxx @@ -0,0 +1,1651 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <formadapter.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <comphelper/types.hxx> +#include <comphelper/enumhelper.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <strings.hxx> +#include <connectivity/dbexception.hxx> +#include <comphelper/sequence.hxx> + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +// SbaXFormAdapter + +SbaXFormAdapter::SbaXFormAdapter() + :m_aLoadListeners(*this, m_aMutex) + ,m_aRowSetListeners(*this, m_aMutex) + ,m_aRowSetApproveListeners(*this, m_aMutex) + ,m_aErrorListeners(*this, m_aMutex) + ,m_aParameterListeners(*this, m_aMutex) + ,m_aSubmitListeners(*this, m_aMutex) + ,m_aResetListeners(*this, m_aMutex) + ,m_aPropertyChangeListeners(*this, m_aMutex) + ,m_aVetoablePropertyChangeListeners(*this, m_aMutex) + ,m_aPropertiesChangeListeners(*this, m_aMutex) + ,m_aDisposeListeners(m_aMutex) + ,m_aContainerListeners(m_aMutex) + ,m_nNamePropHandle(-1) +{ + +} + +SbaXFormAdapter::~SbaXFormAdapter() +{ + +} + +Sequence< Type > SAL_CALL SbaXFormAdapter::getTypes( ) +{ + return ::comphelper::concatSequences( + SbaXFormAdapter_BASE1::getTypes(), + SbaXFormAdapter_BASE2::getTypes(), + SbaXFormAdapter_BASE3::getTypes() + ); +} + +Sequence< sal_Int8 > SAL_CALL SbaXFormAdapter::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +Any SAL_CALL SbaXFormAdapter::queryInterface(const Type& _rType) +{ + Any aReturn = SbaXFormAdapter_BASE1::queryInterface( _rType ); + + if (!aReturn.hasValue()) + aReturn = SbaXFormAdapter_BASE2::queryInterface( _rType ); + + if (!aReturn.hasValue()) + aReturn = SbaXFormAdapter_BASE3::queryInterface( _rType ); + + return aReturn; +} + +void SbaXFormAdapter::StopListening() +{ + // log off all our multiplexers + STOP_MULTIPLEXER_LISTENING(LoadListener, m_aLoadListeners, css::form::XLoadable, m_xMainForm); + STOP_MULTIPLEXER_LISTENING(RowSetListener, m_aRowSetListeners, css::sdbc::XRowSet, m_xMainForm); + STOP_MULTIPLEXER_LISTENING(RowSetApproveListener, m_aRowSetApproveListeners, css::sdb::XRowSetApproveBroadcaster, m_xMainForm); + STOP_MULTIPLEXER_LISTENING(SQLErrorListener, m_aErrorListeners, css::sdb::XSQLErrorBroadcaster, m_xMainForm); + STOP_MULTIPLEXER_LISTENING(SubmitListener, m_aSubmitListeners, css::form::XSubmit, m_xMainForm); + STOP_MULTIPLEXER_LISTENING(ResetListener, m_aResetListeners, css::form::XReset, m_xMainForm); + + if (m_aParameterListeners.getLength()) + { + Reference< css::form::XDatabaseParameterBroadcaster > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removeParameterListener(&m_aParameterListeners); + } + + STOP_PROPERTY_MULTIPLEXER_LISTENING(PropertyChangeListener, m_aPropertyChangeListeners, css::beans::XPropertySet, m_xMainForm); + STOP_PROPERTY_MULTIPLEXER_LISTENING(VetoableChangeListener, m_aVetoablePropertyChangeListeners, css::beans::XPropertySet, m_xMainForm); + if (m_aPropertiesChangeListeners.getLength()) + { + Reference< css::beans::XMultiPropertySet > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removePropertiesChangeListener(&m_aPropertiesChangeListeners); + } + + // log off ourself + Reference< css::lang::XComponent > xComp(m_xMainForm, UNO_QUERY); + if (xComp.is()) + xComp->removeEventListener(static_cast<css::lang::XEventListener*>(static_cast<css::beans::XPropertyChangeListener*>(this))); +} + +void SbaXFormAdapter::StartListening() +{ + // log off all our multiplexers + START_MULTIPLEXER_LISTENING(LoadListener, m_aLoadListeners, css::form::XLoadable, m_xMainForm); + START_MULTIPLEXER_LISTENING(RowSetListener, m_aRowSetListeners, css::sdbc::XRowSet, m_xMainForm); + START_MULTIPLEXER_LISTENING(RowSetApproveListener, m_aRowSetApproveListeners, css::sdb::XRowSetApproveBroadcaster, m_xMainForm); + START_MULTIPLEXER_LISTENING(SQLErrorListener, m_aErrorListeners, css::sdb::XSQLErrorBroadcaster, m_xMainForm); + START_MULTIPLEXER_LISTENING(SubmitListener, m_aSubmitListeners, css::form::XSubmit, m_xMainForm); + START_MULTIPLEXER_LISTENING(ResetListener, m_aResetListeners, css::form::XReset, m_xMainForm); + + if (m_aParameterListeners.getLength()) + { + Reference< css::form::XDatabaseParameterBroadcaster > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->addParameterListener(&m_aParameterListeners); + } + + START_PROPERTY_MULTIPLEXER_LISTENING(PropertyChangeListener, m_aPropertyChangeListeners, css::beans::XPropertySet, m_xMainForm); + START_PROPERTY_MULTIPLEXER_LISTENING(VetoableChangeListener, m_aVetoablePropertyChangeListeners, css::beans::XPropertySet, m_xMainForm); + if (m_aPropertiesChangeListeners.getLength()) + { + Reference< css::beans::XMultiPropertySet > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->addPropertiesChangeListener(css::uno::Sequence<OUString>{""}, &m_aPropertiesChangeListeners); + } + + // log off ourself + Reference< css::lang::XComponent > xComp(m_xMainForm, UNO_QUERY); + if (xComp.is()) + xComp->addEventListener(static_cast<css::lang::XEventListener*>(static_cast<css::beans::XPropertyChangeListener*>(this))); +} + +void SbaXFormAdapter::AttachForm(const Reference< css::sdbc::XRowSet >& xNewMaster) +{ + if (xNewMaster == m_xMainForm) + return; + + OSL_ENSURE(xNewMaster.get() != static_cast< css::sdbc::XRowSet* >(this), "SbaXFormAdapter::AttachForm : invalid argument !"); + + if (m_xMainForm.is()) + { + StopListening(); + + // if our old master is loaded we have to send an 'unloaded' event + Reference< css::form::XLoadable > xLoadable(m_xMainForm, UNO_QUERY); + if (xLoadable->isLoaded()) + { + css::lang::EventObject aEvt(*this); + ::comphelper::OInterfaceIteratorHelper2 aIt(m_aLoadListeners); + while (aIt.hasMoreElements()) + static_cast< css::form::XLoadListener*>(aIt.next())->unloaded(aEvt); + } + } + + m_xMainForm = xNewMaster; + + if (!m_xMainForm.is()) + return; + + StartListening(); + + // if our new master is loaded we have to send an 'loaded' event + Reference< css::form::XLoadable > xLoadable(m_xMainForm, UNO_QUERY); + if (xLoadable->isLoaded()) + { + css::lang::EventObject aEvt(*this); + ::comphelper::OInterfaceIteratorHelper2 aIt(m_aLoadListeners); + while (aIt.hasMoreElements()) + static_cast< css::form::XLoadListener*>(aIt.next())->loaded(aEvt); + } + + // TODO : perhaps _all_ of our listeners should be notified about our new state + // (nearly every aspect of us may have changed with new master form) +} + +// css::sdbc::XCloseable +void SAL_CALL SbaXFormAdapter::close() +{ + Reference< css::sdbc::XCloseable > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->close(); +} + +// css::sdbc::XResultSetMetaDataSupplier +Reference< css::sdbc::XResultSetMetaData > SAL_CALL SbaXFormAdapter::getMetaData() +{ + Reference< css::sdbc::XResultSetMetaDataSupplier > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getMetaData(); + return Reference< css::sdbc::XResultSetMetaData > (); +} + +// css::sdbc::XColumnLocate +sal_Int32 SAL_CALL SbaXFormAdapter::findColumn(const OUString& columnName) +{ + Reference< css::sdbc::XColumnLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->findColumn(columnName); + + ::dbtools::throwInvalidColumnException( columnName, *this ); + assert(false); + return 0; // Never reached +} + +// css::sdbcx::XColumnsSupplier +Reference< css::container::XNameAccess > SAL_CALL SbaXFormAdapter::getColumns() +{ + Reference< css::sdbcx::XColumnsSupplier > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getColumns(); + return Reference< css::container::XNameAccess > (); +} + +// css::sdbc::XRow +sal_Bool SAL_CALL SbaXFormAdapter::wasNull() +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->wasNull(); + return true; +} + +OUString SAL_CALL SbaXFormAdapter::getString(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getString(columnIndex); + return OUString(); +} + +sal_Bool SAL_CALL SbaXFormAdapter::getBoolean(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getBoolean(columnIndex); + return false; +} + +sal_Int8 SAL_CALL SbaXFormAdapter::getByte(sal_Int32 columnIndex) + +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getByte(columnIndex); + return 0; +} + +sal_Int16 SAL_CALL SbaXFormAdapter::getShort(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getShort(columnIndex); + return 0; +} + +sal_Int32 SAL_CALL SbaXFormAdapter::getInt(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getInt(columnIndex); + return 0; +} + +sal_Int64 SAL_CALL SbaXFormAdapter::getLong(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getLong(columnIndex); + return 0; +} + +float SAL_CALL SbaXFormAdapter::getFloat(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getFloat(columnIndex); + return 0.0; +} + +double SAL_CALL SbaXFormAdapter::getDouble(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getDouble(columnIndex); + return 0.0; +} + +Sequence< sal_Int8 > SAL_CALL SbaXFormAdapter::getBytes(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getBytes(columnIndex); + return Sequence <sal_Int8> (); +} + +css::util::Date SAL_CALL SbaXFormAdapter::getDate(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getDate(columnIndex); + return css::util::Date(); +} + +css::util::Time SAL_CALL SbaXFormAdapter::getTime(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getTime(columnIndex); + return css::util::Time(); +} + +css::util::DateTime SAL_CALL SbaXFormAdapter::getTimestamp(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getTimestamp(columnIndex); + return css::util::DateTime(); +} + +Reference< css::io::XInputStream > SAL_CALL SbaXFormAdapter::getBinaryStream(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getBinaryStream(columnIndex); + return Reference< css::io::XInputStream > (); +} + +Reference< css::io::XInputStream > SAL_CALL SbaXFormAdapter::getCharacterStream(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getCharacterStream(columnIndex); + return Reference< css::io::XInputStream > (); +} + +Any SAL_CALL SbaXFormAdapter::getObject(sal_Int32 columnIndex, const Reference< css::container::XNameAccess >& typeMap) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getObject(columnIndex, typeMap); + return Any(); +} + +Reference< css::sdbc::XRef > SAL_CALL SbaXFormAdapter::getRef(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getRef(columnIndex); + return Reference< css::sdbc::XRef > (); +} + +Reference< css::sdbc::XBlob > SAL_CALL SbaXFormAdapter::getBlob(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getBlob(columnIndex); + return Reference< css::sdbc::XBlob > (); +} + +Reference< css::sdbc::XClob > SAL_CALL SbaXFormAdapter::getClob(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getClob(columnIndex); + return Reference< css::sdbc::XClob > (); +} + +Reference< css::sdbc::XArray > SAL_CALL SbaXFormAdapter::getArray(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRow > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getArray(columnIndex); + return Reference< css::sdbc::XArray > (); +} + +// css::sdbcx::XRowLocate +Any SAL_CALL SbaXFormAdapter::getBookmark() +{ + Reference< css::sdbcx::XRowLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getBookmark(); + return Any(); +} + +sal_Bool SAL_CALL SbaXFormAdapter::moveToBookmark(const Any& bookmark) +{ + Reference< css::sdbcx::XRowLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->moveToBookmark(bookmark); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::moveRelativeToBookmark(const Any& bookmark, sal_Int32 rows) +{ + Reference< css::sdbcx::XRowLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->moveRelativeToBookmark(bookmark,rows); + return false; +} + +sal_Int32 SAL_CALL SbaXFormAdapter::compareBookmarks(const Any& _first, const Any& _second) +{ + Reference< css::sdbcx::XRowLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->compareBookmarks(_first, _second); + return 0; +} + +sal_Bool SAL_CALL SbaXFormAdapter::hasOrderedBookmarks() +{ + Reference< css::sdbcx::XRowLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->hasOrderedBookmarks(); + return false; +} + +sal_Int32 SAL_CALL SbaXFormAdapter::hashBookmark(const Any& bookmark) +{ + Reference< css::sdbcx::XRowLocate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->hashBookmark(bookmark); + return 0; +} + +// css::sdbc::XRowUpdate +void SAL_CALL SbaXFormAdapter::updateNull(sal_Int32 columnIndex) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateNull(columnIndex); +} + +void SAL_CALL SbaXFormAdapter::updateBoolean(sal_Int32 columnIndex, sal_Bool x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateBoolean(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateByte(sal_Int32 columnIndex, sal_Int8 x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateByte(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateShort(sal_Int32 columnIndex, sal_Int16 x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateShort(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateInt(sal_Int32 columnIndex, sal_Int32 x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateInt(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateLong(sal_Int32 columnIndex, sal_Int64 x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateLong(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateFloat(sal_Int32 columnIndex, float x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateFloat(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateDouble(sal_Int32 columnIndex, double x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateDouble(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateString(sal_Int32 columnIndex, const OUString& x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateString(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateBytes(sal_Int32 columnIndex, const Sequence< sal_Int8 >& x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateBytes(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateDate(sal_Int32 columnIndex, const css::util::Date& x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateDate(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateTime(sal_Int32 columnIndex, const css::util::Time& x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateTime(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateTimestamp(sal_Int32 columnIndex, const css::util::DateTime& x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateTimestamp(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateBinaryStream(sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateBinaryStream(columnIndex, x, length); +} + +void SAL_CALL SbaXFormAdapter::updateCharacterStream(sal_Int32 columnIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateCharacterStream(columnIndex, x, length); +} + +void SAL_CALL SbaXFormAdapter::updateObject(sal_Int32 columnIndex, const Any& x) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateObject(columnIndex, x); +} + +void SAL_CALL SbaXFormAdapter::updateNumericObject(sal_Int32 columnIndex, const Any& x, sal_Int32 scale) +{ + Reference< css::sdbc::XRowUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateNumericObject(columnIndex, x, scale); +} + +// css::sdbc::XResultSet +sal_Bool SAL_CALL SbaXFormAdapter::next() +{ + if (m_xMainForm.is()) + return m_xMainForm->next(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::isBeforeFirst() +{ + if (m_xMainForm.is()) + return m_xMainForm->isBeforeFirst(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::isAfterLast() +{ + if (m_xMainForm.is()) + return m_xMainForm->isAfterLast(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::isFirst() +{ + if (m_xMainForm.is()) + return m_xMainForm->isFirst(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::isLast() +{ + if (m_xMainForm.is()) + return m_xMainForm->isLast(); + return false; +} + +void SAL_CALL SbaXFormAdapter::beforeFirst() +{ + if (m_xMainForm.is()) + m_xMainForm->beforeFirst(); +} + +void SAL_CALL SbaXFormAdapter::afterLast() +{ + if (m_xMainForm.is()) + m_xMainForm->afterLast(); +} + +sal_Bool SAL_CALL SbaXFormAdapter::first() +{ + if (m_xMainForm.is()) + return m_xMainForm->first(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::last() +{ + if (m_xMainForm.is()) + return m_xMainForm->last(); + return false; +} + +sal_Int32 SAL_CALL SbaXFormAdapter::getRow() +{ + if (m_xMainForm.is()) + return m_xMainForm->getRow(); + return 0; +} + +sal_Bool SAL_CALL SbaXFormAdapter::absolute(sal_Int32 row) +{ + if (m_xMainForm.is()) + return m_xMainForm->absolute(row); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::relative(sal_Int32 rows) +{ + if (m_xMainForm.is()) + return m_xMainForm->relative(rows); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::previous() +{ + if (m_xMainForm.is()) + return m_xMainForm->previous(); + return false; +} + +void SAL_CALL SbaXFormAdapter::refreshRow() +{ + if (m_xMainForm.is()) + m_xMainForm->refreshRow(); +} + +sal_Bool SAL_CALL SbaXFormAdapter::rowUpdated() +{ + if (m_xMainForm.is()) + return m_xMainForm->rowUpdated(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::rowInserted() +{ + if (m_xMainForm.is()) + return m_xMainForm->rowInserted(); + return false; +} + +sal_Bool SAL_CALL SbaXFormAdapter::rowDeleted() +{ + if (m_xMainForm.is()) + return m_xMainForm->rowDeleted(); + return false; +} + +Reference< XInterface > SAL_CALL SbaXFormAdapter::getStatement() +{ + if (m_xMainForm.is()) + return m_xMainForm->getStatement(); + return nullptr; +} + +// css::sdbc::XResultSetUpdate +void SAL_CALL SbaXFormAdapter::insertRow() +{ + Reference< css::sdbc::XResultSetUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->insertRow(); +} + +void SAL_CALL SbaXFormAdapter::updateRow() +{ + Reference< css::sdbc::XResultSetUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->updateRow(); +} + +void SAL_CALL SbaXFormAdapter::deleteRow() +{ + Reference< css::sdbc::XResultSetUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->deleteRow(); +} + +void SAL_CALL SbaXFormAdapter::cancelRowUpdates() +{ + Reference< css::sdbc::XResultSetUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->cancelRowUpdates(); +} + +void SAL_CALL SbaXFormAdapter::moveToInsertRow() +{ + Reference< css::sdbc::XResultSetUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->moveToInsertRow(); +} + +void SAL_CALL SbaXFormAdapter::moveToCurrentRow() +{ + Reference< css::sdbc::XResultSetUpdate > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->moveToCurrentRow(); +} + +// css::sdbc::XRowSet +void SAL_CALL SbaXFormAdapter::execute() +{ + if (m_xMainForm.is()) + m_xMainForm->execute(); +} + +IMPLEMENT_LISTENER_ADMINISTRATION(SbaXFormAdapter, sdbc, RowSetListener, m_aRowSetListeners, css::sdbc::XRowSet, m_xMainForm) + +// css::sdbcx::XDeleteRows +Sequence<sal_Int32> SAL_CALL SbaXFormAdapter::deleteRows(const Sequence< Any >& rows) +{ + Reference< css::sdbcx::XDeleteRows > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->deleteRows(rows); + return Sequence<sal_Int32>(); +} + +// css::sdbc::XWarningsSupplier +Any SAL_CALL SbaXFormAdapter::getWarnings() +{ + Reference< css::sdbc::XWarningsSupplier > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->getWarnings(); + return Any(); +} + +void SAL_CALL SbaXFormAdapter::clearWarnings() +{ + Reference< css::sdbc::XWarningsSupplier > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->clearWarnings(); +} + +// css::sdb::XRowSetApproveBroadcaster +IMPLEMENT_LISTENER_ADMINISTRATION(SbaXFormAdapter, sdb, RowSetApproveListener, m_aRowSetApproveListeners, css::sdb::XRowSetApproveBroadcaster, m_xMainForm) + +// css::sdbc::XSQLErrorBroadcaster +IMPLEMENT_LISTENER_ADMINISTRATION(SbaXFormAdapter, sdb, SQLErrorListener, m_aErrorListeners, css::sdb::XSQLErrorBroadcaster, m_xMainForm) + +// css::sdb::XResultSetAccess +Reference< css::sdbc::XResultSet > SAL_CALL SbaXFormAdapter::createResultSet() +{ + Reference< css::sdb::XResultSetAccess > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->createResultSet(); + return Reference< css::sdbc::XResultSet > (); +} + +// css::form::XLoadable +void SAL_CALL SbaXFormAdapter::load() +{ + Reference< css::form::XLoadable > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->load(); +} + +void SAL_CALL SbaXFormAdapter::unload() +{ + Reference< css::form::XLoadable > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->unload(); +} + +void SAL_CALL SbaXFormAdapter::reload() +{ + Reference< css::form::XLoadable > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->reload(); +} + +sal_Bool SAL_CALL SbaXFormAdapter::isLoaded() +{ + Reference< css::form::XLoadable > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + return xIface->isLoaded(); + return false; +} + +IMPLEMENT_LISTENER_ADMINISTRATION(SbaXFormAdapter, form, LoadListener, m_aLoadListeners, css::form::XLoadable, m_xMainForm) + +// css::sdbc::XParameters +void SAL_CALL SbaXFormAdapter::setNull(sal_Int32 parameterIndex, sal_Int32 sqlType) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setNull(parameterIndex, sqlType); +} + +void SAL_CALL SbaXFormAdapter::setObjectNull(sal_Int32 parameterIndex, sal_Int32 sqlType, const OUString& typeName) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setObjectNull(parameterIndex, sqlType, typeName); +} + +void SAL_CALL SbaXFormAdapter::setBoolean(sal_Int32 parameterIndex, sal_Bool x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setBoolean(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setByte(sal_Int32 parameterIndex, sal_Int8 x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setByte(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setShort(sal_Int32 parameterIndex, sal_Int16 x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setShort(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setInt(sal_Int32 parameterIndex, sal_Int32 x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setInt(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setLong(sal_Int32 parameterIndex, sal_Int64 x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setLong(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setFloat(sal_Int32 parameterIndex, float x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setFloat(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setDouble(sal_Int32 parameterIndex, double x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setDouble(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setString(sal_Int32 parameterIndex, const OUString& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setString(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setBytes(sal_Int32 parameterIndex, const Sequence< sal_Int8 >& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setBytes(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setDate(sal_Int32 parameterIndex, const css::util::Date& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setDate(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setTime(sal_Int32 parameterIndex, const css::util::Time& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setTime(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setTimestamp(sal_Int32 parameterIndex, const css::util::DateTime& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setTimestamp(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setBinaryStream(sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setBinaryStream(parameterIndex, x, length); +} + +void SAL_CALL SbaXFormAdapter::setCharacterStream(sal_Int32 parameterIndex, const Reference< css::io::XInputStream >& x, sal_Int32 length) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setCharacterStream(parameterIndex, x, length); +} + +void SAL_CALL SbaXFormAdapter::setObject(sal_Int32 parameterIndex, const Any& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setObject(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setObjectWithInfo(sal_Int32 parameterIndex, const Any& x, sal_Int32 targetSqlType, sal_Int32 scale) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setObjectWithInfo(parameterIndex, x, targetSqlType, scale); +} + +void SAL_CALL SbaXFormAdapter::setRef(sal_Int32 parameterIndex, const Reference< css::sdbc::XRef >& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setRef(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setBlob(sal_Int32 parameterIndex, const Reference< css::sdbc::XBlob >& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setBlob(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setClob(sal_Int32 parameterIndex, const Reference< css::sdbc::XClob >& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setClob(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::setArray(sal_Int32 parameterIndex, const Reference< css::sdbc::XArray >& x) +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->setArray(parameterIndex, x); +} + +void SAL_CALL SbaXFormAdapter::clearParameters() +{ + Reference< css::sdbc::XParameters > xIface(m_xMainForm, UNO_QUERY); + if (xIface.is()) + xIface->clearParameters(); +} + +// css::form::XDatabaseParameterBroadcaster +void SAL_CALL SbaXFormAdapter::addParameterListener(const Reference< css::form::XDatabaseParameterListener >& aListener) +{ + m_aParameterListeners.addInterface(aListener); + if (m_aParameterListeners.getLength() == 1) + { + Reference< css::form::XDatabaseParameterBroadcaster > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->addParameterListener(&m_aParameterListeners); + } +} + +void SAL_CALL SbaXFormAdapter::removeParameterListener(const Reference< css::form::XDatabaseParameterListener >& aListener) +{ + if (m_aParameterListeners.getLength() == 1) + { + Reference< css::form::XDatabaseParameterBroadcaster > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removeParameterListener(&m_aParameterListeners); + } + m_aParameterListeners.removeInterface(aListener); +} + +// css::container::XChild +Reference< XInterface > SAL_CALL SbaXFormAdapter::getParent() +{ + return m_xParent; +} + +void SAL_CALL SbaXFormAdapter::setParent(const Reference< XInterface >& Parent) +{ + m_xParent = Parent; +} + +// css::form::XSubmit +void SAL_CALL SbaXFormAdapter::submit(const Reference< css::awt::XControl >& aControl, const css::awt::MouseEvent& aMouseEvt) +{ + Reference< css::form::XSubmit > xSubmit(m_xMainForm, UNO_QUERY); + if (xSubmit.is()) + xSubmit->submit(aControl, aMouseEvt); +} + +IMPLEMENT_LISTENER_ADMINISTRATION(SbaXFormAdapter, form, SubmitListener, m_aSubmitListeners, css::form::XSubmit, m_xMainForm) + +// css::awt::XTabControllerModel +sal_Bool SAL_CALL SbaXFormAdapter::getGroupControl() +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::getGroupControl : not supported !"); + return false; +} + +void SAL_CALL SbaXFormAdapter::setGroupControl(sal_Bool /*GroupControl*/) +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::setGroupControl : not supported !"); +} + +void SAL_CALL SbaXFormAdapter::setControlModels(const Sequence< Reference< css::awt::XControlModel > >& /*Controls*/) +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::setControlModels : not supported !"); +} + +Sequence< Reference< css::awt::XControlModel > > SAL_CALL SbaXFormAdapter::getControlModels() +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::getControlModels : not supported !"); + return Sequence< Reference< css::awt::XControlModel > >(); +} + +void SAL_CALL SbaXFormAdapter::setGroup(const Sequence< Reference< css::awt::XControlModel > >& /*_rGroup*/, const OUString& /*GroupName*/) +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::setGroup : not supported !"); +} + +sal_Int32 SAL_CALL SbaXFormAdapter::getGroupCount() +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::getGroupCount : not supported !"); + return 0; +} + +void SAL_CALL SbaXFormAdapter::getGroup(sal_Int32 /*nGroup*/, Sequence< Reference< css::awt::XControlModel > >& /*_rGroup*/, OUString& /*Name*/) +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::getGroup : not supported !"); +} + +void SAL_CALL SbaXFormAdapter::getGroupByName(const OUString& /*Name*/, Sequence< Reference< css::awt::XControlModel > >& /*_rGroup*/) +{ + OSL_FAIL("SAL_CALL SbaXFormAdapter::getGroupByName : not supported !"); +} + +// css::lang::XComponent +void SAL_CALL SbaXFormAdapter::dispose() +{ + // log off all multiplexers + if (m_xMainForm.is()) + StopListening(); + + css::lang::EventObject aEvt(*this); + m_aLoadListeners.disposeAndClear(aEvt); + m_aRowSetListeners.disposeAndClear(aEvt); + m_aRowSetApproveListeners.disposeAndClear(aEvt); + m_aErrorListeners.disposeAndClear(aEvt); + m_aParameterListeners.disposeAndClear(aEvt); + m_aSubmitListeners.disposeAndClear(aEvt); + m_aResetListeners.disposeAndClear(aEvt); + + m_aVetoablePropertyChangeListeners.disposeAndClear(); + m_aPropertyChangeListeners.disposeAndClear(); + m_aPropertiesChangeListeners.disposeAndClear(aEvt); + + m_aDisposeListeners.disposeAndClear(aEvt); + m_aContainerListeners.disposeAndClear(aEvt); + + // dispose all children + for (auto const& child : m_aChildren) + { + Reference< css::beans::XPropertySet > xSet(child, UNO_QUERY); + if (xSet.is()) + xSet->removePropertyChangeListener(PROPERTY_NAME, static_cast<css::beans::XPropertyChangeListener*>(this)); + + Reference< css::container::XChild > xChild(child, UNO_QUERY); + if (xChild.is()) + xChild->setParent(Reference< XInterface > ()); + + Reference< css::lang::XComponent > xComp(child, UNO_QUERY); + if (xComp.is()) + xComp->dispose(); + } + m_aChildren.clear(); +} + +void SAL_CALL SbaXFormAdapter::addEventListener(const Reference< css::lang::XEventListener >& xListener) +{ + m_aDisposeListeners.addInterface(xListener); +} + +void SAL_CALL SbaXFormAdapter::removeEventListener(const Reference< css::lang::XEventListener >& aListener) +{ + m_aDisposeListeners.removeInterface(aListener); +} + +// css::beans::XFastPropertySet +void SAL_CALL SbaXFormAdapter::setFastPropertyValue(sal_Int32 nHandle, const Any& aValue) +{ + Reference< css::beans::XFastPropertySet > xSet(m_xMainForm, UNO_QUERY); + OSL_ENSURE(xSet.is(), "SAL_CALL SbaXFormAdapter::setFastPropertyValue : have no master form !"); + + if (m_nNamePropHandle == nHandle) + { + if (aValue.getValueType().getTypeClass() != TypeClass_STRING) + { + throw css::lang::IllegalArgumentException(); + } + + // for notifying property listeners + css::beans::PropertyChangeEvent aEvt; + aEvt.Source = *this; + aEvt.PropertyName = PROPERTY_NAME; + aEvt.PropertyHandle = m_nNamePropHandle; + aEvt.OldValue <<= m_sName; + aEvt.NewValue = aValue; + + aValue >>= m_sName; + + ::cppu::OInterfaceIteratorHelper aIt(*m_aPropertyChangeListeners.getContainer(PROPERTY_NAME)); + while (aIt.hasMoreElements()) + static_cast< css::beans::XPropertyChangeListener*>(aIt.next())->propertyChange(aEvt); + + return; + } + + xSet->setFastPropertyValue(nHandle, aValue); +} + +Any SAL_CALL SbaXFormAdapter::getFastPropertyValue(sal_Int32 nHandle) +{ + Reference< css::beans::XFastPropertySet > xSet(m_xMainForm, UNO_QUERY); + OSL_ENSURE(xSet.is(), "SAL_CALL SbaXFormAdapter::getFastPropertyValue : have no master form !"); + + if (m_nNamePropHandle == nHandle) + return makeAny(m_sName); + + return xSet->getFastPropertyValue(nHandle); +} + +// css::container::XNamed +OUString SAL_CALL SbaXFormAdapter::getName() +{ + return ::comphelper::getString(getPropertyValue(PROPERTY_NAME)); +} + +void SAL_CALL SbaXFormAdapter::setName(const OUString& aName) +{ + setPropertyValue(PROPERTY_NAME, makeAny(aName)); +} + +// css::io::XPersistObject +OUString SAL_CALL SbaXFormAdapter::getServiceName() +{ + Reference< css::io::XPersistObject > xPersist(m_xMainForm, UNO_QUERY); + if (xPersist.is()) + return xPersist->getServiceName(); + return OUString(); +} + +void SAL_CALL SbaXFormAdapter::write(const Reference< css::io::XObjectOutputStream >& _rxOutStream) +{ + Reference< css::io::XPersistObject > xPersist(m_xMainForm, UNO_QUERY); + if (xPersist.is()) + xPersist->write(_rxOutStream); +} + +void SAL_CALL SbaXFormAdapter::read(const Reference< css::io::XObjectInputStream >& _rxInStream) +{ + Reference< css::io::XPersistObject > xPersist(m_xMainForm, UNO_QUERY); + if (xPersist.is()) + xPersist->read(_rxInStream); +} + +// css::beans::XMultiPropertySet +Reference< css::beans::XPropertySetInfo > SAL_CALL SbaXFormAdapter::getPropertySetInfo() +{ + Reference< css::beans::XMultiPropertySet > xSet(m_xMainForm, UNO_QUERY); + if (!xSet.is()) + return Reference< css::beans::XPropertySetInfo > (); + + Reference< css::beans::XPropertySetInfo > xReturn = xSet->getPropertySetInfo(); + if (-1 == m_nNamePropHandle) + { + // we need to determine the handle for the NAME property + const Sequence<css::beans::Property> aProps = xReturn->getProperties(); + for (const css::beans::Property& rProp : aProps) + { + if (rProp.Name == PROPERTY_NAME) + { + m_nNamePropHandle = rProp.Handle; + break; + } + } + } + return xReturn; +} + +void SAL_CALL SbaXFormAdapter::setPropertyValues(const Sequence< OUString >& PropertyNames, const Sequence< Any >& Values) +{ + Reference< css::beans::XMultiPropertySet > xSet(m_xMainForm, UNO_QUERY); + if (xSet.is()) + xSet->setPropertyValues(PropertyNames, Values); +} + +Sequence< Any > SAL_CALL SbaXFormAdapter::getPropertyValues(const Sequence< OUString >& aPropertyNames) +{ + Reference< css::beans::XMultiPropertySet > xSet(m_xMainForm, UNO_QUERY); + if (!xSet.is()) + return Sequence< Any>(aPropertyNames.getLength()); + + Sequence< Any> aReturn = xSet->getPropertyValues(aPropertyNames); + + // search for (and fake) the NAME property + OSL_ENSURE(aReturn.getLength() == aPropertyNames.getLength(), "SAL_CALL SbaXFormAdapter::getPropertyValues : the main form returned an invalid-length sequence !"); + for (sal_Int32 i=0; i<aPropertyNames.getLength(); ++i) + if (aPropertyNames[i] == PROPERTY_NAME) + { + aReturn[i] <<= m_sName; + break; + } + + return aReturn; +} + +void SAL_CALL SbaXFormAdapter::addPropertiesChangeListener(const Sequence< OUString>& /*aPropertyNames*/, const Reference< css::beans::XPropertiesChangeListener >& xListener) +{ + // we completely ignore the property names, _all_ changes of _all_ properties will be forwarded to _all_ listeners + m_aPropertiesChangeListeners.addInterface(xListener); + if (m_aPropertiesChangeListeners.getLength() == 1) + { + Reference< css::beans::XMultiPropertySet > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->addPropertiesChangeListener(Sequence< OUString>{""}, &m_aPropertiesChangeListeners); + } +} + +void SAL_CALL SbaXFormAdapter::removePropertiesChangeListener(const Reference< css::beans::XPropertiesChangeListener >& Listener) +{ + if (m_aPropertiesChangeListeners.getLength() == 1) + { + Reference< css::beans::XMultiPropertySet > xBroadcaster(m_xMainForm, UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removePropertiesChangeListener(&m_aPropertiesChangeListeners); + } + m_aPropertiesChangeListeners.removeInterface(Listener); +} + +void SAL_CALL SbaXFormAdapter::firePropertiesChangeEvent(const Sequence< OUString >& aPropertyNames, const Reference< css::beans::XPropertiesChangeListener >& xListener) +{ + Reference< css::beans::XMultiPropertySet > xSet(m_xMainForm, UNO_QUERY); + if (xSet.is()) + xSet->firePropertiesChangeEvent(aPropertyNames, xListener); +} + +// css::beans::XPropertySet +void SAL_CALL SbaXFormAdapter::setPropertyValue(const OUString& aPropertyName, const Any& aValue) +{ + Reference< css::beans::XPropertySet > xSet(m_xMainForm, UNO_QUERY); + if (!xSet.is()) + return; + + // special handling for the "name" property + if (aPropertyName == PROPERTY_NAME) + setFastPropertyValue(m_nNamePropHandle, aValue); + + xSet->setPropertyValue(aPropertyName, aValue); +} + +Any SAL_CALL SbaXFormAdapter::getPropertyValue(const OUString& PropertyName) +{ + Reference< css::beans::XPropertySet > xSet(m_xMainForm, UNO_QUERY); + if (!xSet.is()) + return Any(); + + // special handling for the "name" property + if (PropertyName == PROPERTY_NAME) + return getFastPropertyValue(m_nNamePropHandle); + + return xSet->getPropertyValue(PropertyName); +} + +IMPLEMENT_PROPERTY_LISTENER_ADMINISTRATION(SbaXFormAdapter, PropertyChangeListener, m_aPropertyChangeListeners, css::beans::XPropertySet, m_xMainForm); +IMPLEMENT_PROPERTY_LISTENER_ADMINISTRATION(SbaXFormAdapter, VetoableChangeListener, m_aVetoablePropertyChangeListeners, css::beans::XPropertySet, m_xMainForm); + +// css::util::XCancellable +void SAL_CALL SbaXFormAdapter::cancel() +{ + Reference< css::util::XCancellable > xCancel(m_xMainForm, UNO_QUERY); + if (xCancel.is()) + return; + xCancel->cancel(); +} + +// css::beans::XPropertyState +css::beans::PropertyState SAL_CALL SbaXFormAdapter::getPropertyState(const OUString& PropertyName) +{ + Reference< css::beans::XPropertyState > xState(m_xMainForm, UNO_QUERY); + if (xState.is()) + return xState->getPropertyState(PropertyName); + return css::beans::PropertyState_DEFAULT_VALUE; +} + +Sequence< css::beans::PropertyState> SAL_CALL SbaXFormAdapter::getPropertyStates(const Sequence< OUString >& aPropertyName) +{ + Reference< css::beans::XPropertyState > xState(m_xMainForm, UNO_QUERY); + if (xState.is()) + return xState->getPropertyStates(aPropertyName); + + // set them all to DEFAULT + Sequence< css::beans::PropertyState> aReturn(aPropertyName.getLength()); + for (css::beans::PropertyState& rState : aReturn) + rState = css::beans::PropertyState_DEFAULT_VALUE; + return aReturn; +} + +void SAL_CALL SbaXFormAdapter::setPropertyToDefault(const OUString& PropertyName) +{ + Reference< css::beans::XPropertyState > xState(m_xMainForm, UNO_QUERY); + if (xState.is()) + xState->setPropertyToDefault(PropertyName); +} + +Any SAL_CALL SbaXFormAdapter::getPropertyDefault(const OUString& aPropertyName) +{ + Reference< css::beans::XPropertyState > xState(m_xMainForm, UNO_QUERY); + if (xState.is()) + return xState->getPropertyDefault(aPropertyName); + return Any(); +} + +// css::form::XReset +void SAL_CALL SbaXFormAdapter::reset() +{ + Reference< css::form::XReset > xReset(m_xMainForm, UNO_QUERY); + if (xReset.is()) + xReset->reset(); +} + +IMPLEMENT_LISTENER_ADMINISTRATION(SbaXFormAdapter, form, ResetListener, m_aResetListeners, css::form::XReset, m_xMainForm) + +// css::container::XNameContainer +void SbaXFormAdapter::implInsert(const Any& aElement, sal_Int32 nIndex, const OUString* pNewElName) +{ + // extract the form component + if (aElement.getValueType().getTypeClass() != TypeClass_INTERFACE) + { + throw css::lang::IllegalArgumentException(); + } + + Reference< css::form::XFormComponent > xElement(aElement, UNO_QUERY); + if (!xElement.is()) + { + throw css::lang::IllegalArgumentException(); + } + + // for the name we need the propset + Reference< css::beans::XPropertySet > xElementSet(xElement, UNO_QUERY); + if (!xElementSet.is()) + { + throw css::lang::IllegalArgumentException(); + } + OUString sName; + try + { + if (pNewElName) + xElementSet->setPropertyValue(PROPERTY_NAME, makeAny(*pNewElName)); + + xElementSet->getPropertyValue(PROPERTY_NAME) >>= sName; + } + catch(Exception&) + { + // the set didn't support the name prop + throw css::lang::IllegalArgumentException(); + } + + // check the index + OSL_ASSERT(nIndex >= 0); + if (sal::static_int_cast< sal_uInt32 >(nIndex) > m_aChildren.size()) + nIndex = m_aChildren.size(); + + OSL_ENSURE(m_aChildren.size() == m_aChildNames.size(), "SAL_CALL SbaXFormAdapter::implInsert : inconsistent container state !"); + m_aChildren.insert(m_aChildren.begin() + nIndex, xElement); + m_aChildNames.insert(m_aChildNames.begin() + nIndex, sName); + + // listen for a change of the name + xElementSet->addPropertyChangeListener(PROPERTY_NAME, static_cast<css::beans::XPropertyChangeListener*>(this)); + + // we are now the parent of the new element + xElement->setParent(static_cast<css::container::XContainer*>(this)); + + // notify the container listeners + css::container::ContainerEvent aEvt; + aEvt.Source = *this; + aEvt.Accessor <<= nIndex; + aEvt.Element <<= xElement; + ::comphelper::OInterfaceIteratorHelper2 aIt(m_aContainerListeners); + while (aIt.hasMoreElements()) + static_cast< css::container::XContainerListener*>(aIt.next())->elementInserted(aEvt); +} + +sal_Int32 SbaXFormAdapter::implGetPos(const OUString& rName) +{ + std::vector< OUString>::const_iterator aIter = std::find( m_aChildNames.begin(), + m_aChildNames.end(), + rName); + + if(aIter != m_aChildNames.end()) + return aIter - m_aChildNames.begin(); + + return -1; +} + +void SAL_CALL SbaXFormAdapter::insertByName(const OUString& aName, const Any& aElement) +{ + implInsert(aElement, m_aChildren.size(), &aName); +} + +void SAL_CALL SbaXFormAdapter::removeByName(const OUString& Name) +{ + sal_Int32 nPos = implGetPos(Name); + if (-1 == nPos) + { + throw css::container::NoSuchElementException(); + } + removeByIndex(nPos); +} + +// css::container::XNameReplace +void SAL_CALL SbaXFormAdapter::replaceByName(const OUString& aName, const Any& aElement) +{ + sal_Int32 nPos = implGetPos(aName); + if (-1 == nPos) + { + throw css::container::NoSuchElementException(); + } + replaceByIndex(nPos, aElement); +} + +// css::container::XNameAccess +Any SAL_CALL SbaXFormAdapter::getByName(const OUString& aName) +{ + sal_Int32 nPos = implGetPos(aName); + if (-1 == nPos) + { + throw css::container::NoSuchElementException(); + } + return makeAny(m_aChildren[nPos]); +} + +Sequence< OUString > SAL_CALL SbaXFormAdapter::getElementNames() +{ + return Sequence< OUString >(m_aChildNames.data(), m_aChildNames.size()); +} + +sal_Bool SAL_CALL SbaXFormAdapter::hasByName(const OUString& aName) +{ + return (-1 != implGetPos(aName)); +} + +// css::container::XElementAccess +Type SAL_CALL SbaXFormAdapter::getElementType() +{ + return cppu::UnoType<css::form::XFormComponent>::get(); +} + +sal_Bool SAL_CALL SbaXFormAdapter::hasElements() +{ + return !m_aChildren.empty(); +} + +// css::container::XIndexContainer +void SAL_CALL SbaXFormAdapter::insertByIndex(sal_Int32 _rIndex, const Any& Element) +{ + if ( ( _rIndex < 0 ) || ( o3tl::make_unsigned(_rIndex) >= m_aChildren.size() ) ) + throw css::lang::IndexOutOfBoundsException(); + implInsert(Element, _rIndex); +} + +void SAL_CALL SbaXFormAdapter::removeByIndex(sal_Int32 _rIndex) +{ + if ( ( _rIndex < 0 ) || ( o3tl::make_unsigned(_rIndex) >= m_aChildren.size() ) ) + throw css::lang::IndexOutOfBoundsException(); + + Reference< css::form::XFormComponent > xAffected = *(m_aChildren.begin() + _rIndex); + + OSL_ENSURE(m_aChildren.size() == m_aChildNames.size(), "SAL_CALL SbaXFormAdapter::removeByIndex : inconsistent container state !"); + m_aChildren.erase(m_aChildren.begin() + _rIndex); + m_aChildNames.erase(m_aChildNames.begin() + _rIndex); + + // no need to listen anymore + Reference< css::beans::XPropertySet > xAffectedSet(xAffected, UNO_QUERY); + xAffectedSet->removePropertyChangeListener(PROPERTY_NAME, static_cast<css::beans::XPropertyChangeListener*>(this)); + + // we are no longer the parent + xAffected->setParent(Reference< XInterface > ()); + + // notify container listeners + css::container::ContainerEvent aEvt; + aEvt.Source = *this; + aEvt.Element <<= xAffected; + ::comphelper::OInterfaceIteratorHelper2 aIt(m_aContainerListeners); + while (aIt.hasMoreElements()) + static_cast< css::container::XContainerListener*>(aIt.next())->elementRemoved(aEvt); + +} + +// css::container::XIndexReplace +void SAL_CALL SbaXFormAdapter::replaceByIndex(sal_Int32 _rIndex, const Any& Element) +{ + if ( ( _rIndex < 0 ) || ( o3tl::make_unsigned(_rIndex) >= m_aChildren.size() ) ) + throw css::lang::IndexOutOfBoundsException(); + + // extract the form component + if (Element.getValueType().getTypeClass() != TypeClass_INTERFACE) + { + throw css::lang::IllegalArgumentException(); + } + + Reference< css::form::XFormComponent > xElement(Element, UNO_QUERY); + if (!xElement.is()) + { + throw css::lang::IllegalArgumentException(); + } + + // for the name we need the propset + Reference< css::beans::XPropertySet > xElementSet(xElement, UNO_QUERY); + if (!xElementSet.is()) + { + throw css::lang::IllegalArgumentException(); + } + OUString sName; + try + { + xElementSet->getPropertyValue(PROPERTY_NAME) >>= sName; + } + catch(Exception&) + { + // the set didn't support the name prop + throw css::lang::IllegalArgumentException(); + } + + Reference< css::form::XFormComponent > xOld = *(m_aChildren.begin() + _rIndex); + + OSL_ENSURE(m_aChildren.size() == m_aChildNames.size(), "SAL_CALL SbaXFormAdapter::replaceByIndex : inconsistent container state !"); + *(m_aChildren.begin() + _rIndex) = xElement; + *(m_aChildNames.begin() + _rIndex) = sName; + + // correct property change listening + Reference< css::beans::XPropertySet > xOldSet(xOld, UNO_QUERY); + xOldSet->removePropertyChangeListener(PROPERTY_NAME, static_cast<css::beans::XPropertyChangeListener*>(this)); + xElementSet->addPropertyChangeListener(PROPERTY_NAME, static_cast<css::beans::XPropertyChangeListener*>(this)); + + // parent reset + xOld->setParent(Reference< XInterface > ()); + xElement->setParent(static_cast<css::container::XContainer*>(this)); + + // notify container listeners + css::container::ContainerEvent aEvt; + aEvt.Source = *this; + aEvt.Accessor <<= _rIndex; + aEvt.Element <<= xElement; + aEvt.ReplacedElement <<= xOld; + + ::comphelper::OInterfaceIteratorHelper2 aIt(m_aContainerListeners); + while (aIt.hasMoreElements()) + static_cast< css::container::XContainerListener*>(aIt.next())->elementReplaced(aEvt); +} + +// css::container::XIndexAccess +sal_Int32 SAL_CALL SbaXFormAdapter::getCount() +{ + return m_aChildren.size(); +} + +Any SAL_CALL SbaXFormAdapter::getByIndex(sal_Int32 _rIndex) +{ + if ( ( _rIndex < 0 ) || ( o3tl::make_unsigned(_rIndex) >= m_aChildren.size() ) ) + throw css::lang::IndexOutOfBoundsException(); + + Reference< css::form::XFormComponent > xElement = *(m_aChildren.begin() + _rIndex); + return makeAny(xElement); +} + +// css::container::XContainer +void SAL_CALL SbaXFormAdapter::addContainerListener(const Reference< css::container::XContainerListener >& xListener) +{ + m_aContainerListeners.addInterface(xListener); +} + +void SAL_CALL SbaXFormAdapter::removeContainerListener(const Reference< css::container::XContainerListener >& xListener) +{ + m_aContainerListeners.removeInterface(xListener); +} + +// css::container::XEnumerationAccess +Reference< css::container::XEnumeration > SAL_CALL SbaXFormAdapter::createEnumeration() +{ + return new ::comphelper::OEnumerationByName(this); +} + +// css::beans::XPropertyChangeListener +void SAL_CALL SbaXFormAdapter::propertyChange(const css::beans::PropertyChangeEvent& evt) +{ + if (evt.PropertyName != PROPERTY_NAME) + return; + + std::vector< css::uno::Reference< css::form::XFormComponent > >::const_iterator aIter = std::find_if( m_aChildren.begin(), + m_aChildren.end(), + [&evt](css::uno::Reference< css::uno::XInterface > const & x) { return x == evt.Source; }); + + if(aIter != m_aChildren.end()) + { + sal_Int32 nPos = aIter - m_aChildren.begin(); + OSL_ENSURE(*(m_aChildNames.begin() + nPos) == ::comphelper::getString(evt.OldValue), "SAL_CALL SbaXFormAdapter::propertyChange : object has a wrong name !"); + *(m_aChildNames.begin() + nPos) = ::comphelper::getString(evt.NewValue); + } +} + +// css::lang::XEventListener +void SAL_CALL SbaXFormAdapter::disposing(const css::lang::EventObject& Source) +{ + // was it our main form ? + if (Source.Source == m_xMainForm) + dispose(); + + std::vector< css::uno::Reference< css::form::XFormComponent > >::const_iterator aIter = std::find_if( m_aChildren.begin(), + m_aChildren.end(), + [&Source](css::uno::Reference< css::uno::XInterface > const & x) { return x == Source.Source; }); + if(aIter != m_aChildren.end()) + removeByIndex(aIter - m_aChildren.begin()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/genericcontroller.cxx b/dbaccess/source/ui/browser/genericcontroller.cxx new file mode 100644 index 000000000..58f5b55ea --- /dev/null +++ b/dbaccess/source/ui/browser/genericcontroller.cxx @@ -0,0 +1,1235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbaccess/genericcontroller.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <browserids.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <dbaccess/dataview.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <vcl/stdtext.hxx> +#include <framework/titlehelper.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +#include <com/sun/star/ui/XSidebarProvider.hpp> +#include <sfx2/userinputinterception.hxx> + +#include <datasourceconnector.hxx> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/status/Visibility.hpp> +#include <com/sun/star/frame/XUntitledNumbers.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <limits> +#include <unordered_map> +#include <set> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::frame::status; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::ui; +using namespace ::dbtools; +using namespace ::comphelper; + +#define ALL_FEATURES -1 +#define FIRST_USER_DEFINED_FEATURE ( std::numeric_limits< sal_uInt16 >::max() - 1000 ) +#define LAST_USER_DEFINED_FEATURE ( std::numeric_limits< sal_uInt16 >::max() ) + +typedef std::unordered_map< sal_Int16, sal_Int16 > CommandHashMap; + +namespace dbaui +{ + +namespace { + +// UserDefinedFeatures +class UserDefinedFeatures +{ +public: + explicit UserDefinedFeatures( const Reference< XController >& _rxController ); + + void execute( const URL& _rFeatureURL, const Sequence< PropertyValue>& _rArgs ); + +private: + css::uno::WeakReference< XController > m_aController; +}; + +} + +UserDefinedFeatures::UserDefinedFeatures( const Reference< XController >& _rxController ) + :m_aController( _rxController ) +{ +} + +void UserDefinedFeatures::execute( const URL& _rFeatureURL, const Sequence< PropertyValue>& _rArgs ) +{ + try + { + Reference< XController > xController( Reference< XController >(m_aController), UNO_SET_THROW ); + Reference< XDispatchProvider > xDispatchProvider( xController->getFrame(), UNO_QUERY_THROW ); + Reference< XDispatch > xDispatch( xDispatchProvider->queryDispatch( + _rFeatureURL, + "_self", + FrameSearchFlag::AUTO + ) ); + + if ( xDispatch == xController ) + { + SAL_WARN("dbaccess.ui", "UserDefinedFeatures::execute: the controller shouldn't be the dispatcher here!" ); + xDispatch.clear(); + } + + if ( xDispatch.is() ) + xDispatch->dispatch( _rFeatureURL, _rArgs ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +// OGenericUnoController_Data +struct OGenericUnoController_Data +{ + ::sfx2::UserInputInterception m_aUserInputInterception; + UserDefinedFeatures m_aUserDefinedFeatures; + + OGenericUnoController_Data( OGenericUnoController& _rController, ::osl::Mutex& _rMutex ) + :m_aUserInputInterception( _rController, _rMutex ) + ,m_aUserDefinedFeatures( _rController.getXController() ) + { + } +}; + +// OGenericUnoController +OGenericUnoController::OGenericUnoController(const Reference< XComponentContext >& _rM) + :OGenericUnoController_Base( getMutex() ) + ,m_pView(nullptr) +#ifdef DBG_UTIL + ,m_bDescribingSupportedFeatures( false ) +#endif + ,m_aAsyncInvalidateAll(LINK(this, OGenericUnoController, OnAsyncInvalidateAll)) + ,m_aAsyncCloseTask(LINK(this, OGenericUnoController, OnAsyncCloseTask)) + ,m_xContext(_rM) + ,m_aCurrentFrame( *this ) + ,m_bPreview(false) + ,m_bReadOnly(false) + ,m_bCurrentlyModified(false) + ,m_bExternalTitle(false) +{ + osl_atomic_increment( &m_refCount ); + { + m_pData.reset( new OGenericUnoController_Data( *this, getMutex() ) ); + } + osl_atomic_decrement( &m_refCount ); + + + try + { + m_xUrlTransformer = URLTransformer::create(_rM); + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +OGenericUnoController::~OGenericUnoController() +{ + +} + +bool OGenericUnoController::Construct(vcl::Window* /*pParent*/) +{ + OSL_ENSURE( getView(), "the view is NULL!" ); + + if ( getView() ) + { + getView()->Construct(); + getView()->Show(); + } + + m_aSupportedFeatures.clear(); + fillSupportedFeatures(); + + // create the database context + OSL_ENSURE(getORB().is(), "OGenericUnoController::Construct need a service factory!"); + try + { + m_xDatabaseContext = DatabaseContext::create(getORB()); + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui","OGenericUnoController::Construct: could not create (or start listening at) the database context!"); + // at least notify the user. Though the whole component does not make any sense without the database context ... + ShowServiceNotAvailableError(getFrameWeld(), "com.sun.star.sdb.DatabaseContext", true); + } + + return true; +} + +IMPL_LINK_NOARG(OGenericUnoController, OnAsyncInvalidateAll, void*, void) +{ + if ( !OGenericUnoController_Base::rBHelper.bInDispose && !OGenericUnoController_Base::rBHelper.bDisposed ) + InvalidateFeature_Impl(); +} + +void OGenericUnoController::impl_initialize() +{ +} + +void SAL_CALL OGenericUnoController::initialize( const Sequence< Any >& aArguments ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + Reference< XWindow > xParent; + Reference< XFrame > xFrame; + + PropertyValue aValue; + const Any* pIter = aArguments.getConstArray(); + const Any* pEnd = pIter + aArguments.getLength(); + + for ( ; pIter != pEnd; ++pIter ) + { + if ( ( *pIter >>= aValue ) && aValue.Name == "Frame" ) + { + xFrame.set(aValue.Value,UNO_QUERY_THROW); + } + else if ( ( *pIter >>= aValue ) && aValue.Name == "Preview" ) + { + aValue.Value >>= m_bPreview; + m_bReadOnly = true; + } + } + try + { + if ( !xFrame.is() ) + throw IllegalArgumentException("need a frame", *this, 1 ); + + xParent = xFrame->getContainerWindow(); + VCLXWindow* pParentComponent = comphelper::getUnoTunnelImplementation<VCLXWindow>(xParent); + VclPtr< vcl::Window > pParentWin = pParentComponent ? pParentComponent->GetWindow() : VclPtr< vcl::Window >(); + if (!pParentWin) + { + throw IllegalArgumentException("Parent window is null", *this, 1 ); + } + + m_aInitParameters.assign( aArguments ); + Construct( pParentWin ); + + ODataView* pView = getView(); + if ( !pView ) + throw RuntimeException("unable to create a view", *this ); + + if ( m_bReadOnly || m_bPreview ) + pView->EnableInput( false ); + + impl_initialize(); + } + catch(Exception&) + { + // no one clears my view if I won't + m_pView = nullptr; + throw; + } +} + +void SAL_CALL OGenericUnoController::acquire( ) throw () +{ + OGenericUnoController_Base::acquire(); +} + +void SAL_CALL OGenericUnoController::release( ) throw () +{ + OGenericUnoController_Base::release(); +} + +void OGenericUnoController::startFrameListening( const Reference< XFrame >& _rxFrame ) +{ + if ( _rxFrame.is() ) + _rxFrame->addFrameActionListener( this ); +} + +void OGenericUnoController::stopFrameListening( const Reference< XFrame >& _rxFrame ) +{ + if ( _rxFrame.is() ) + _rxFrame->removeFrameActionListener( this ); +} + +void OGenericUnoController::disposing(const EventObject& Source) +{ + // our frame ? + if ( Source.Source == getFrame() ) + stopFrameListening( getFrame() ); +} + +void OGenericUnoController::modified(const EventObject& aEvent) +{ + ::osl::MutexGuard aGuard( getMutex() ); + if ( !isDataSourceReadOnly() ) + { + Reference<XModifiable> xModi(aEvent.Source,UNO_QUERY); + if ( xModi.is() ) + m_bCurrentlyModified = xModi->isModified(); // can only be reset by save + else + m_bCurrentlyModified = true; + } + InvalidateFeature(ID_BROWSER_SAVEDOC); + InvalidateFeature(ID_BROWSER_UNDO); +} + +Reference< XWindow > SAL_CALL OGenericUnoController::getComponentWindow() +{ + SolarMutexGuard g; + return VCLUnoHelper::GetInterface( getView() ); +} + +Reference<XSidebarProvider> SAL_CALL OGenericUnoController::getSidebar() +{ + return nullptr; +} + +OUString SAL_CALL OGenericUnoController::getViewControllerName() +{ + return "Default"; +} + +Sequence< PropertyValue > SAL_CALL OGenericUnoController::getCreationArguments() +{ + // currently we do not support any creation args, so anything passed to XModel2::createViewController would be + // lost, so we can equally return an empty sequence here + return Sequence< PropertyValue >(); +} + +void OGenericUnoController::attachFrame( const Reference< XFrame >& _rxFrame ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + stopFrameListening( m_aCurrentFrame.getFrame() ); + Reference< XFrame > xFrame = m_aCurrentFrame.attachFrame( _rxFrame ); + startFrameListening( xFrame ); + + loadMenu( xFrame ); + + if ( getView() ) + getView()->attachFrame( xFrame ); +} + +namespace +{ + typedef std::vector< Any > States; + + void lcl_notifyMultipleStates( XStatusListener& _rListener, FeatureStateEvent& _rEvent, const States& _rStates ) + { + for (auto const& elem : _rStates) + { + _rEvent.State = elem; + _rListener.statusChanged( _rEvent ); + } + } + + void lcl_collectStates( const FeatureState& _rFeatureState, States& _out_rStates ) + { + // order matters, due to a bug in framework which resets the check state when any non-boolean event + // arrives + // #i68215# is the bug to (re-)introduce this "ordered" notification here + // #i67882# is the bug which was caused by the real fix which we did in framework + // #i68216# is the bug which requests to fix the code in Draw which relies on + // framework's implementation details + if ( !!_rFeatureState.sTitle ) + _out_rStates.push_back( makeAny( *_rFeatureState.sTitle ) ); + if ( !!_rFeatureState.bChecked ) + _out_rStates.push_back( makeAny( *_rFeatureState.bChecked ) ); + if ( !!_rFeatureState.bInvisible ) + _out_rStates.push_back( makeAny( Visibility( !*_rFeatureState.bInvisible ) ) ); + if ( _rFeatureState.aValue.hasValue() ) + _out_rStates.push_back( _rFeatureState.aValue ); + if ( _out_rStates.empty() ) + _out_rStates.emplace_back( ); + } +} + +void OGenericUnoController::ImplBroadcastFeatureState(const OUString& _rFeature, const Reference< XStatusListener > & xListener, bool _bIgnoreCache) +{ + sal_uInt16 nFeat = m_aSupportedFeatures[ _rFeature ].nFeatureId; + FeatureState aFeatState( GetState( nFeat ) ); + + FeatureState& rCachedState = m_aStateCache[nFeat]; // creates if necessary + if ( !_bIgnoreCache ) + { + // check if we really need to notify the listeners : this method may be called much more often than needed, so check + // the cached state of the feature + bool bAlreadyCached = ( m_aStateCache.find(nFeat) != m_aStateCache.end() ); + if ( bAlreadyCached ) + if ( ( rCachedState.bEnabled == aFeatState.bEnabled ) + && ( rCachedState.bChecked == aFeatState.bChecked ) + && ( rCachedState.bInvisible == aFeatState.bInvisible ) + && ( rCachedState.sTitle == aFeatState.sTitle ) + ) + return; + } + rCachedState = aFeatState; + + FeatureStateEvent aEvent; + aEvent.FeatureURL.Complete = _rFeature; + if (m_xUrlTransformer.is()) + m_xUrlTransformer->parseStrict(aEvent.FeatureURL); + aEvent.Source = static_cast<XDispatch*>(this); + aEvent.IsEnabled = aFeatState.bEnabled; + + // collect all states to be notified + States aStates; + lcl_collectStates( aFeatState, aStates ); + + // a special listener ? + if ( xListener.is() ) + lcl_notifyMultipleStates( *xListener, aEvent, aStates ); + else + { // no -> iterate through all listeners responsible for the URL + std::set<OUString> aFeatureCommands; + for( const auto& rFeature : m_aSupportedFeatures ) + { + if( rFeature.second.nFeatureId == nFeat ) + aFeatureCommands.insert( rFeature.first ); + } + + // it is possible that listeners are registered or revoked while + // we are notifying them, so we must use a copy of m_arrStatusListener, not + // m_arrStatusListener itself + Dispatch aNotifyLoop( m_arrStatusListener ); + + for (auto const& elem : aNotifyLoop) + { + if ( aFeatureCommands.find( elem.aURL.Complete ) != aFeatureCommands.end() ) + { + aEvent.FeatureURL = elem.aURL; + lcl_notifyMultipleStates( *elem.xListener, aEvent, aStates ); + } + } + } + +} + +bool OGenericUnoController::isFeatureSupported( sal_Int32 _nId ) +{ + SupportedFeatures::const_iterator aFeaturePos = std::find_if( + m_aSupportedFeatures.begin(), + m_aSupportedFeatures.end(), + CompareFeatureById(_nId) + ); + + return ( m_aSupportedFeatures.end() != aFeaturePos && !aFeaturePos->first.isEmpty()); +} + +void OGenericUnoController::InvalidateFeature_Impl() +{ +#ifdef DBG_UTIL + static sal_Int32 s_nRecursions = 0; + ++s_nRecursions; +#endif + + bool bEmpty = true; + FeatureListener aNextFeature; + { + ::osl::MutexGuard aGuard( m_aFeatureMutex); + bEmpty = m_aFeaturesToInvalidate.empty(); + if (!bEmpty) + aNextFeature = m_aFeaturesToInvalidate.front(); + } + while(!bEmpty) + { + if ( ALL_FEATURES == aNextFeature.nId ) + { + InvalidateAll_Impl(); + break; + } + else + { + SupportedFeatures::const_iterator aFeaturePos = std::find_if( + m_aSupportedFeatures.begin(), + m_aSupportedFeatures.end(), + CompareFeatureById( aNextFeature.nId ) + ); + +#if OSL_DEBUG_LEVEL > 0 + if ( m_aSupportedFeatures.end() == aFeaturePos ) + { + SAL_WARN( "dbaccess.ui", "OGenericUnoController::InvalidateFeature_Impl: feature id " + << aNextFeature.nId + << " has been invalidated, but is not supported!" ); + } +#endif + if ( m_aSupportedFeatures.end() != aFeaturePos ) + // we really know this feature + ImplBroadcastFeatureState( aFeaturePos->first, aNextFeature.xListener, aNextFeature.bForceBroadcast ); + } + + ::osl::MutexGuard aGuard( m_aFeatureMutex); + m_aFeaturesToInvalidate.pop_front(); + bEmpty = m_aFeaturesToInvalidate.empty(); + if (!bEmpty) + aNextFeature = m_aFeaturesToInvalidate.front(); + } + +#ifdef DBG_UTIL + --s_nRecursions; +#endif +} + +void OGenericUnoController::ImplInvalidateFeature( sal_Int32 _nId, const Reference< XStatusListener >& _xListener, bool _bForceBroadcast ) +{ +#if OSL_DEBUG_LEVEL > 0 + if ( _nId != -1 ) + { + auto isSupportedFeature = std::any_of( + m_aSupportedFeatures.begin(), + m_aSupportedFeatures.end(), + CompareFeatureById( _nId ) + ); + OSL_ENSURE( isSupportedFeature, "OGenericUnoController::ImplInvalidateFeature: invalidating an unsupported feature is suspicious, at least!" ); + } +#endif + + FeatureListener aListener; + aListener.nId = _nId; + aListener.xListener = _xListener; + aListener.bForceBroadcast = _bForceBroadcast; + + bool bWasEmpty; + { + ::osl::MutexGuard aGuard( m_aFeatureMutex ); + bWasEmpty = m_aFeaturesToInvalidate.empty(); + m_aFeaturesToInvalidate.push_back( aListener ); + } + + if ( bWasEmpty ) + m_aAsyncInvalidateAll.Call(); +} + +void OGenericUnoController::InvalidateFeature(sal_uInt16 _nId, const Reference< XStatusListener > & _xListener, bool _bForceBroadcast) +{ + ImplInvalidateFeature( _nId, _xListener, _bForceBroadcast ); +} + +void OGenericUnoController::InvalidateAll() +{ + ImplInvalidateFeature( ALL_FEATURES, nullptr, true ); +} + +void OGenericUnoController::InvalidateAll_Impl() +{ + // invalidate all supported features + for (auto const& supportedFeature : m_aSupportedFeatures) + ImplBroadcastFeatureState( supportedFeature.first, nullptr, true ); + + { + ::osl::MutexGuard aGuard( m_aFeatureMutex); + OSL_ENSURE(m_aFeaturesToInvalidate.size(), "OGenericUnoController::InvalidateAll_Impl: to be called from within InvalidateFeature_Impl only!"); + m_aFeaturesToInvalidate.pop_front(); + if(!m_aFeaturesToInvalidate.empty()) + m_aAsyncInvalidateAll.Call(); + } +} + +Reference< XDispatch > OGenericUnoController::queryDispatch(const URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags) +{ + Reference< XDispatch > xReturn; + + OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::queryDispatch: shouldn't this be filled at construction time?" ); + if ( m_aSupportedFeatures.empty() ) + fillSupportedFeatures(); + + // URL's we can handle ourself? + if ( aURL.Complete == ".uno:FormSlots/ConfirmDeletion" + || ( ( m_aSupportedFeatures.find( aURL.Complete ) != m_aSupportedFeatures.end() ) + && !isUserDefinedFeature( aURL.Complete ) + ) + ) + { + xReturn = this; + } + // no? -> ask the slave dispatcher + else if ( m_xSlaveDispatcher.is() ) + { + xReturn = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags); + } + + // outta here + return xReturn; +} + +Sequence< Reference< XDispatch > > OGenericUnoController::queryDispatches(const Sequence< DispatchDescriptor >& aDescripts) +{ + Sequence< Reference< XDispatch > > aReturn; + sal_Int32 nLen = aDescripts.getLength(); + if ( nLen ) + { + aReturn.realloc( nLen ); + Reference< XDispatch >* pReturn = aReturn.getArray(); + const Reference< XDispatch >* pReturnEnd = aReturn.getArray() + nLen; + const DispatchDescriptor* pDescripts = aDescripts.getConstArray(); + + for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts ) + { + *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags ); + } + } + + return aReturn; +} + +Reference< XDispatchProvider > OGenericUnoController::getSlaveDispatchProvider() +{ + return m_xSlaveDispatcher; +} + +void OGenericUnoController::setSlaveDispatchProvider(const Reference< XDispatchProvider > & _xNewProvider) +{ + m_xSlaveDispatcher = _xNewProvider; +} + +Reference< XDispatchProvider > OGenericUnoController::getMasterDispatchProvider() +{ + return m_xMasterDispatcher; +} + +void OGenericUnoController::setMasterDispatchProvider(const Reference< XDispatchProvider > & _xNewProvider) +{ + m_xMasterDispatcher = _xNewProvider; +} + +void OGenericUnoController::dispatch(const URL& _aURL, const Sequence< PropertyValue >& aArgs) +{ + SolarMutexGuard aSolarGuard; + // The SolarMutex is not locked anymore when the framework calls into + // here. So, lock it ourself. The real solution would be to lock it only in the places + // where it's needed, but a) this might turn out difficult, since we then also need to care + // for locking in the proper order (SolarMutex and m_aMutex), and b) this would be too many places + // for the time frame of the fix. + // #i52602# + executeChecked(_aURL,aArgs); +} + +void OGenericUnoController::addStatusListener(const Reference< XStatusListener > & aListener, const URL& _rURL) +{ + // parse the URL now and here, this saves later parsing in each notification round + URL aParsedURL( _rURL ); + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aParsedURL ); + + // remember the listener together with the URL + m_arrStatusListener.insert( m_arrStatusListener.end(), DispatchTarget( aParsedURL, aListener ) ); + + // initially broadcast the state + ImplBroadcastFeatureState( aParsedURL.Complete, aListener, true ); + // force the new state to be broadcast to the new listener +} + +void OGenericUnoController::removeStatusListener(const Reference< XStatusListener > & aListener, const URL& _rURL) +{ + if (_rURL.Complete.isEmpty()) + { + m_arrStatusListener.erase(std::remove_if(m_arrStatusListener.begin(), m_arrStatusListener.end(), + [&aListener](const DispatchTarget& rCurrent) { return rCurrent.xListener == aListener; }), + m_arrStatusListener.end()); + } + else + { + // remove the listener only for the given URL + Dispatch::iterator iterSearch = std::find_if(m_arrStatusListener.begin(), m_arrStatusListener.end(), + [&aListener, &_rURL](const DispatchTarget& rCurrent) { + return (rCurrent.xListener == aListener) && (rCurrent.aURL.Complete == _rURL.Complete); }); + if (iterSearch != m_arrStatusListener.end()) + m_arrStatusListener.erase(iterSearch); + } + + OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::removeStatusListener: shouldn't this be filled at construction time?" ); + if ( m_aSupportedFeatures.empty() ) + fillSupportedFeatures(); + + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find(_rURL.Complete); + if (aIter != m_aSupportedFeatures.end()) + { // clear the cache for that feature + StateCache::const_iterator aCachePos = m_aStateCache.find( aIter->second.nFeatureId ); + if ( aCachePos != m_aStateCache.end() ) + m_aStateCache.erase( aCachePos ); + } + + // now remove the listener from the deque + ::osl::MutexGuard aGuard( m_aFeatureMutex ); + m_aFeaturesToInvalidate.erase( + std::remove_if( m_aFeaturesToInvalidate.begin(), + m_aFeaturesToInvalidate.end(), + FindFeatureListener(aListener)) + ,m_aFeaturesToInvalidate.end()); +} + +void OGenericUnoController::releaseNumberForComponent() +{ + try + { + Reference< XUntitledNumbers > xUntitledProvider(getPrivateModel(), UNO_QUERY ); + if ( xUntitledProvider.is() ) + xUntitledProvider->releaseNumberForComponent(static_cast<XWeak*>(this)); + } + catch( const Exception& ) + { + // NII + } +} + +void OGenericUnoController::disposing() +{ + { + EventObject aDisposeEvent; + aDisposeEvent.Source = static_cast<XWeak*>(this); + Dispatch aStatusListener = m_arrStatusListener; + for (auto const& statusListener : aStatusListener) + { + statusListener.xListener->disposing(aDisposeEvent); + } + m_arrStatusListener.clear(); + } + + m_xDatabaseContext = nullptr; + { + ::osl::MutexGuard aGuard( m_aFeatureMutex); + m_aAsyncInvalidateAll.CancelCall(); + m_aFeaturesToInvalidate.clear(); + } + + releaseNumberForComponent(); + + // check out from all the objects we are listening + // the frame + stopFrameListening( m_aCurrentFrame.getFrame() ); + m_aCurrentFrame.attachFrame( nullptr ); + + m_xMasterDispatcher = nullptr; + m_xSlaveDispatcher = nullptr; + m_xTitleHelper.clear(); + m_xUrlTransformer.clear(); + m_aInitParameters.clear(); +} + +void SAL_CALL OGenericUnoController::addEventListener( const Reference< XEventListener >& xListener ) +{ + // disambiguate + OGenericUnoController_Base::WeakComponentImplHelperBase::addEventListener( xListener ); +} + +void SAL_CALL OGenericUnoController::removeEventListener( const Reference< XEventListener >& xListener ) +{ + // disambiguate + OGenericUnoController_Base::WeakComponentImplHelperBase::removeEventListener( xListener ); +} + +void OGenericUnoController::frameAction(const FrameActionEvent& aEvent) +{ + ::osl::MutexGuard aGuard( getMutex() ); + if ( aEvent.Frame == m_aCurrentFrame.getFrame() ) + m_aCurrentFrame.frameAction( aEvent.Action ); +} + +void OGenericUnoController::implDescribeSupportedFeature( const char* _pAsciiCommandURL, + sal_uInt16 _nFeatureId, sal_Int16 _nCommandGroup ) +{ +#ifdef DBG_UTIL + OSL_ENSURE( m_bDescribingSupportedFeatures, "OGenericUnoController::implDescribeSupportedFeature: bad timing for this call!" ); +#endif + OSL_PRECOND( _nFeatureId < FIRST_USER_DEFINED_FEATURE, "OGenericUnoController::implDescribeSupportedFeature: invalid feature id!" ); + + ControllerFeature aFeature; + aFeature.Command = OUString::createFromAscii( _pAsciiCommandURL ); + aFeature.nFeatureId = _nFeatureId; + aFeature.GroupId = _nCommandGroup; + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( m_aSupportedFeatures.find( aFeature.Command ) == m_aSupportedFeatures.end(), + "OGenericUnoController::implDescribeSupportedFeature: this feature is already there!" ); +#endif + m_aSupportedFeatures[ aFeature.Command ] = aFeature; +} + +void OGenericUnoController::describeSupportedFeatures() +{ + // add all supported features + implDescribeSupportedFeature( ".uno:Copy", ID_BROWSER_COPY, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Cut", ID_BROWSER_CUT, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Paste", ID_BROWSER_PASTE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:ClipboardFormatItems", ID_BROWSER_CLIPBOARD_FORMAT_ITEMS ); + implDescribeSupportedFeature( ".uno:DSBEditDoc", ID_BROWSER_EDITDOC, CommandGroup::DOCUMENT ); +} + +FeatureState OGenericUnoController::GetState( sal_uInt16 _nId ) const +{ + FeatureState aReturn; + // (disabled automatically) + + switch ( _nId ) + { + case ID_BROWSER_UNDO: + case ID_BROWSER_SAVEDOC: + aReturn.bEnabled = true; + break; + default: + // for now, enable all the time + // TODO: we should ask the dispatcher. However, this is laborious, since you cannot ask a dispatcher + // directly, but need to add a status listener. + aReturn.bEnabled = true; + break; + } + + return aReturn; +} + +void OGenericUnoController::Execute( sal_uInt16 _nId, const Sequence< PropertyValue>& _rArgs ) +{ + OSL_ENSURE( isUserDefinedFeature( _nId ), + "OGenericUnoController::Execute: responsible for user defined features only!" ); + + // user defined features can be handled by dispatch interceptors resp. protocol handlers only. + // So, we need to do a queryDispatch, and dispatch the URL + m_pData->m_aUserDefinedFeatures.execute( getURLForId( _nId ), _rArgs ); +} + +URL OGenericUnoController::getURLForId(sal_Int32 _nId) const +{ + URL aReturn; + if ( m_xUrlTransformer.is() ) + { + SupportedFeatures::const_iterator aIter = std::find_if( + m_aSupportedFeatures.begin(), + m_aSupportedFeatures.end(), + CompareFeatureById( _nId ) + ); + + if ( m_aSupportedFeatures.end() != aIter && !aIter->first.isEmpty() ) + { + aReturn.Complete = aIter->first; + m_xUrlTransformer->parseStrict( aReturn ); + } + } + return aReturn; +} + +bool OGenericUnoController::isUserDefinedFeature( const sal_uInt16 _nFeatureId ) +{ + return ( _nFeatureId >= FIRST_USER_DEFINED_FEATURE ) && ( _nFeatureId < LAST_USER_DEFINED_FEATURE ); +} + +bool OGenericUnoController::isUserDefinedFeature( const OUString& _rFeatureURL ) const +{ + SupportedFeatures::const_iterator pos = m_aSupportedFeatures.find( _rFeatureURL ); + OSL_PRECOND( pos != m_aSupportedFeatures.end(), + "OGenericUnoController::isUserDefinedFeature: this is no supported feature at all!" ); + + return ( pos != m_aSupportedFeatures.end() ) && isUserDefinedFeature( pos->second.nFeatureId ); +} + +sal_Bool SAL_CALL OGenericUnoController::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +void OGenericUnoController::startConnectionListening(const Reference< XConnection >& _rxConnection) +{ + // we have to remove ourself before dispoing the connection + Reference< XComponent > xComponent(_rxConnection, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast<XFrameActionListener*>(this)); +} + +void OGenericUnoController::stopConnectionListening(const Reference< XConnection >& _rxConnection) +{ + // we have to remove ourself before dispoing the connection + Reference< XComponent > xComponent(_rxConnection, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast<XFrameActionListener*>(this)); +} + +Reference< XConnection > OGenericUnoController::connect( const Reference< XDataSource>& _xDataSource ) +{ + weld::WaitObject aWaitCursor(getFrameWeld()); + + ODatasourceConnector aConnector( getORB(), getFrameWeld(), OUString() ); + Reference< XConnection > xConnection = aConnector.connect( _xDataSource, nullptr ); + startConnectionListening( xConnection ); + + return xConnection; +} + +Reference< XConnection > OGenericUnoController::connect( const OUString& _rDataSourceName, + const OUString& _rContextInformation, ::dbtools::SQLExceptionInfo* _pErrorInfo ) +{ + weld::WaitObject aWaitCursor(getFrameWeld()); + + ODatasourceConnector aConnector( getORB(), getFrameWeld(), _rContextInformation ); + Reference<XConnection> xConnection = aConnector.connect( _rDataSourceName, _pErrorInfo ); + startConnectionListening( xConnection ); + + return xConnection; +} + +void OGenericUnoController::setView( const VclPtr<ODataView> &i_rView ) +{ + m_pView = i_rView; +} + +void OGenericUnoController::clearView() +{ + m_pView = nullptr; +} + +void OGenericUnoController::showError(const SQLExceptionInfo& _rInfo) +{ + ::dbtools::showError(_rInfo,VCLUnoHelper::GetInterface(getView()),getORB()); +} + +Reference< XLayoutManager > OGenericUnoController::getLayoutManager(const Reference< XFrame >& _xFrame) +{ + Reference< XPropertySet > xPropSet( _xFrame, UNO_QUERY ); + Reference< XLayoutManager > xLayoutManager; + if ( xPropSet.is() ) + { + try + { + xLayoutManager.set(xPropSet->getPropertyValue("LayoutManager"),UNO_QUERY); + } + catch ( Exception& ) + { + } + } + return xLayoutManager; +} + +void OGenericUnoController::loadMenu(const Reference< XFrame >& _xFrame) +{ + Reference< XLayoutManager > xLayoutManager = getLayoutManager(_xFrame); + if ( xLayoutManager.is() ) + { + xLayoutManager->lock(); + xLayoutManager->createElement( "private:resource/menubar/menubar" ); + xLayoutManager->createElement( "private:resource/toolbar/toolbar" ); + xLayoutManager->unlock(); + xLayoutManager->doLayout(); + } + + onLoadedMenu( xLayoutManager ); +} + +void OGenericUnoController::onLoadedMenu(const Reference< XLayoutManager >& /*_xLayoutManager*/) +{ + // not interested in +} + +void OGenericUnoController::closeTask() +{ + m_aAsyncCloseTask.Call(); +} + +IMPL_LINK_NOARG(OGenericUnoController, OnAsyncCloseTask, void*, void) +{ + if ( !OGenericUnoController_Base::rBHelper.bInDispose ) + { + try + { + Reference< util::XCloseable > xCloseable( m_aCurrentFrame.getFrame(), UNO_QUERY_THROW ); + xCloseable->close( false ); // false - holds the owner ship for this frame inside this object! + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +Any SAL_CALL OGenericUnoController::getViewData() +{ + return Any(); +} + +void SAL_CALL OGenericUnoController::restoreViewData(const Any& /*Data*/) +{ +} + +Reference< XModel > SAL_CALL OGenericUnoController::getModel() +{ + return Reference< XModel >(); +} + +Reference< XFrame > SAL_CALL OGenericUnoController::getFrame() +{ + ::osl::MutexGuard aGuard( getMutex() ); + return m_aCurrentFrame.getFrame(); +} + +sal_Bool SAL_CALL OGenericUnoController::attachModel(const Reference< XModel > & /*xModel*/) +{ + SAL_WARN("dbaccess.ui", "OGenericUnoController::attachModel: not supported!" ); + return false; +} + +void OGenericUnoController::executeUnChecked(sal_uInt16 _nCommandId, const Sequence< PropertyValue >& aArgs) +{ + Execute(_nCommandId, aArgs); +} + +void OGenericUnoController::executeUnChecked(const util::URL& _rCommand, const Sequence< PropertyValue >& aArgs) +{ + OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::executeUnChecked: shouldn't this be filled at construction time?" ); + if ( m_aSupportedFeatures.empty() ) + fillSupportedFeatures(); + + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCommand.Complete ); + if (aIter != m_aSupportedFeatures.end()) + Execute( aIter->second.nFeatureId, aArgs ); +} + +void OGenericUnoController::executeChecked(const util::URL& _rCommand, const Sequence< PropertyValue >& aArgs) +{ + OSL_PRECOND( !m_aSupportedFeatures.empty(), "OGenericUnoController::executeChecked: shouldn't this be filled at construction time?" ); + if ( m_aSupportedFeatures.empty() ) + fillSupportedFeatures(); + + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCommand.Complete ); + if ( aIter != m_aSupportedFeatures.end() ) + { + sal_uInt16 nFeatureId = aIter->second.nFeatureId; + if ( GetState( nFeatureId ).bEnabled ) + Execute( nFeatureId, aArgs ); + } +} + +Reference< awt::XWindow> OGenericUnoController::getTopMostContainerWindow() const +{ + Reference< css::awt::XWindow> xWindow; + + // get the top most window + Reference< XFrame > xFrame( m_aCurrentFrame.getFrame() ); + if ( xFrame.is() ) + { + xWindow = xFrame->getContainerWindow(); + + while ( xFrame.is() && !xFrame->isTop() ) + { + xFrame = xFrame->getCreator(); + } + if ( xFrame.is() ) + xWindow = xFrame->getContainerWindow(); + } + return xWindow; +} + +Reference< XTitle > OGenericUnoController::impl_getTitleHelper_throw() +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( ! m_xTitleHelper.is ()) + { + Reference< XUntitledNumbers > xUntitledProvider(getPrivateModel(), UNO_QUERY ); + Reference< XController > xThis(static_cast< XController* >(this), UNO_QUERY_THROW); + + ::framework::TitleHelper* pHelper = new ::framework::TitleHelper( m_xContext ); + m_xTitleHelper.set( static_cast< ::cppu::OWeakObject* >(pHelper), UNO_QUERY_THROW); + + pHelper->setOwner (xThis ); + pHelper->connectWithUntitledNumbers (xUntitledProvider); + } + + return m_xTitleHelper; +} + +// XTitle +OUString SAL_CALL OGenericUnoController::getTitle() +{ + ::osl::MutexGuard aGuard( getMutex() ); + if ( m_bExternalTitle ) + return impl_getTitleHelper_throw()->getTitle (); + return getPrivateTitle() + impl_getTitleHelper_throw()->getTitle (); +} + +// XTitle +void SAL_CALL OGenericUnoController::setTitle(const OUString& sTitle) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + m_bExternalTitle = true; + impl_getTitleHelper_throw()->setTitle (sTitle); +} + +// XTitleChangeBroadcaster +void SAL_CALL OGenericUnoController::addTitleChangeListener(const Reference< XTitleChangeListener >& xListener) +{ + Reference< XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper_throw(), UNO_QUERY); + if (xBroadcaster.is ()) + xBroadcaster->addTitleChangeListener (xListener); +} + +void SAL_CALL OGenericUnoController::removeTitleChangeListener(const Reference< XTitleChangeListener >& xListener) +{ + Reference< XTitleChangeBroadcaster > xBroadcaster(impl_getTitleHelper_throw(), UNO_QUERY); + if (xBroadcaster.is ()) + xBroadcaster->removeTitleChangeListener (xListener); +} + +// XUserInputInterception +void SAL_CALL OGenericUnoController::addKeyHandler( const Reference< XKeyHandler >& _rxHandler ) +{ + if ( _rxHandler.is() ) + m_pData->m_aUserInputInterception.addKeyHandler( _rxHandler ); +} + +void SAL_CALL OGenericUnoController::removeKeyHandler( const Reference< XKeyHandler >& _rxHandler ) +{ + m_pData->m_aUserInputInterception.removeKeyHandler( _rxHandler ); +} + +void SAL_CALL OGenericUnoController::addMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler ) +{ + if ( _rxHandler.is() ) + m_pData->m_aUserInputInterception.addMouseClickHandler( _rxHandler ); +} + +void SAL_CALL OGenericUnoController::removeMouseClickHandler( const Reference< XMouseClickHandler >& _rxHandler ) +{ + m_pData->m_aUserInputInterception.removeMouseClickHandler( _rxHandler ); +} + +void OGenericUnoController::executeChecked(sal_uInt16 _nCommandId, const Sequence< PropertyValue >& aArgs) +{ + if ( isCommandEnabled(_nCommandId) ) + Execute(_nCommandId, aArgs); +} + +bool OGenericUnoController::isCommandEnabled(sal_uInt16 _nCommandId) const +{ + return GetState( _nCommandId ).bEnabled; +} + +void OGenericUnoController::notifyHiContrastChanged() +{ +} + +bool OGenericUnoController::isDataSourceReadOnly() const +{ + return false; +} + +Reference< XController > OGenericUnoController::getXController() +{ + return this; +} + +bool OGenericUnoController::interceptUserInput( const NotifyEvent& _rEvent ) +{ + return m_pData->m_aUserInputInterception.handleNotifyEvent( _rEvent ); +} + +bool OGenericUnoController::isCommandChecked(sal_uInt16 _nCommandId) const +{ + FeatureState aState = GetState( _nCommandId ); + + return aState.bChecked && *aState.bChecked; +} + +bool OGenericUnoController::isCommandEnabled( const OUString& _rCompleteCommandURL ) const +{ + OSL_ENSURE( !_rCompleteCommandURL.isEmpty(), "OGenericUnoController::isCommandEnabled: Empty command url!" ); + + bool bIsEnabled = false; + SupportedFeatures::const_iterator aIter = m_aSupportedFeatures.find( _rCompleteCommandURL ); + if ( aIter != m_aSupportedFeatures.end() ) + bIsEnabled = isCommandEnabled( aIter->second.nFeatureId ); + + return bIsEnabled; +} + +Sequence< ::sal_Int16 > SAL_CALL OGenericUnoController::getSupportedCommandGroups() +{ + CommandHashMap aCmdHashMap; + for (auto const& supportedFeature : m_aSupportedFeatures) + if ( supportedFeature.second.GroupId != CommandGroup::INTERNAL ) + aCmdHashMap.emplace( supportedFeature.second.GroupId, 0 ); + + return comphelper::mapKeysToSequence( aCmdHashMap ); +} + +Sequence< DispatchInformation > SAL_CALL OGenericUnoController::getConfigurableDispatchInformation( ::sal_Int16 CommandGroup ) +{ + std::vector< DispatchInformation > aInformationVector; + for (auto const& supportedFeature : m_aSupportedFeatures) + { + if ( sal_Int16( supportedFeature.second.GroupId ) == CommandGroup ) + { + aInformationVector.push_back( supportedFeature.second ); + } + } + + return comphelper::containerToSequence( aInformationVector ); +} + +void OGenericUnoController::fillSupportedFeatures() +{ +#ifdef DBG_UTIL + m_bDescribingSupportedFeatures = true; +#endif + describeSupportedFeatures(); +#ifdef DBG_UTIL + m_bDescribingSupportedFeatures = false; +#endif +} + +void SAL_CALL OGenericUnoController::dispose() +{ + SolarMutexGuard aSolarGuard; + OGenericUnoController_Base::dispose(); +} + +weld::Window* OGenericUnoController::getFrameWeld() const +{ + return m_pView ? m_pView->GetFrameWeld() : nullptr; +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/sbagrid.cxx b/dbaccess/source/ui/browser/sbagrid.cxx new file mode 100644 index 000000000..b035a9eeb --- /dev/null +++ b/dbaccess/source/ui/browser/sbagrid.cxx @@ -0,0 +1,1419 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <core_resource.hxx> +#include <helpids.h> +#include <uiservices.hxx> + +#include <sot/exchange.hxx> + +#include <svx/dbaexchange.hxx> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> + +#include <sbagrid.hxx> +#include <dlgsize.hxx> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> + +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/awt/XTextComponent.hpp> +#include <com/sun/star/sdbc/XResultSetUpdate.hpp> +#include <tools/diagnose_ex.h> + +#include <svl/numuno.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <vcl/svapp.hxx> + +#include <svl/zforlist.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <browserids.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <dbu_reghelper.hxx> +#include <dbexchange.hxx> +#include <TableRowExchange.hxx> +#include <TableRow.hxx> +#include <svtools/stringtransfer.hxx> +#include <UITools.hxx> +#include <TokenWriter.hxx> +#include <osl/diagnose.h> +#include <algorithm> + +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; +using namespace ::dbaui; +using namespace ::dbtools; +using namespace ::svx; +using namespace ::svt; + +extern "C" void createRegistryInfo_SbaXGridControl() +{ + static OMultiInstanceAutoRegistration< SbaXGridControl > aAutoRegistration; +} + +css::uno::Sequence<OUString> SAL_CALL SbaXGridControl::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +Reference< XInterface > SbaXGridControl::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + return *(new SbaXGridControl( comphelper::getComponentContext(_rxFactory) )); +} + +// SbaXGridControl + +OUString SAL_CALL SbaXGridControl::getImplementationName() +{ + return getImplementationName_Static(); +} + +OUString SbaXGridControl::getImplementationName_Static() +{ + return "com.sun.star.comp.dbu.SbaXGridControl"; +} + +Sequence< OUString> SbaXGridControl::getSupportedServiceNames_Static() +{ + return { "com.sun.star.form.control.InteractionGridControl", "com.sun.star.form.control.GridControl", + "com.sun.star.awt.UnoControl" }; +} + +SbaXGridControl::SbaXGridControl(const Reference< XComponentContext >& _rM) + : FmXGridControl(_rM) +{ +} + +SbaXGridControl::~SbaXGridControl() +{ +} + +FmXGridPeer* SbaXGridControl::imp_CreatePeer(vcl::Window* pParent) +{ + FmXGridPeer* pReturn = new SbaXGridPeer(m_xContext); + + // translate properties into WinBits + WinBits nStyle = WB_TABSTOP; + Reference< XPropertySet > xModelSet(getModel(), UNO_QUERY); + if (xModelSet.is()) + { + try + { + if (::comphelper::getINT16(xModelSet->getPropertyValue(PROPERTY_BORDER))) + nStyle |= WB_BORDER; + } + catch(Exception&) + { + } + + } + + pReturn->Create(pParent, nStyle); + return pReturn; +} + +Any SAL_CALL SbaXGridControl::queryInterface(const Type& _rType) +{ + Any aRet = FmXGridControl::queryInterface(_rType); + return aRet.hasValue() ? aRet : ::cppu::queryInterface(_rType,static_cast<css::frame::XDispatch*>(this)); +} + +Sequence< Type > SAL_CALL SbaXGridControl::getTypes( ) +{ + return comphelper::concatSequences( + FmXGridControl::getTypes(), + Sequence { cppu::UnoType<css::frame::XDispatch>::get() }); +} + +Sequence< sal_Int8 > SAL_CALL SbaXGridControl::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +void SAL_CALL SbaXGridControl::createPeer(const Reference< css::awt::XToolkit > & rToolkit, const Reference< css::awt::XWindowPeer > & rParentPeer) +{ + FmXGridControl::createPeer(rToolkit, rParentPeer); + + OSL_ENSURE(!mbCreatingPeer, "FmXGridControl::createPeer : recursion!"); + // see the base class' createPeer for a comment on this + + // TODO: why the hell this whole class does not use any mutex? + + Reference< css::frame::XDispatch > xDisp(getPeer(), UNO_QUERY); + for (auto const& elem : m_aStatusMultiplexer) + { + if (elem.second.is() && elem.second->getLength()) + xDisp->addStatusListener(elem.second.get(), elem.first); + } +} + +void SAL_CALL SbaXGridControl::dispatch(const css::util::URL& aURL, const Sequence< PropertyValue >& aArgs) +{ + Reference< css::frame::XDispatch > xDisp(getPeer(), UNO_QUERY); + if (xDisp.is()) + xDisp->dispatch(aURL, aArgs); +} + +void SAL_CALL SbaXGridControl::addStatusListener( const Reference< XStatusListener > & _rxListener, const URL& _rURL ) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + if ( !_rxListener.is() ) + return; + + rtl::Reference<SbaXStatusMultiplexer>& xMultiplexer = m_aStatusMultiplexer[ _rURL ]; + if ( !xMultiplexer.is() ) + { + xMultiplexer = new SbaXStatusMultiplexer( *this, GetMutex() ); + } + + xMultiplexer->addInterface( _rxListener ); + if ( getPeer().is() ) + { + if ( 1 == xMultiplexer->getLength() ) + { // the first external listener for this URL + Reference< XDispatch > xDisp( getPeer(), UNO_QUERY ); + xDisp->addStatusListener( xMultiplexer.get(), _rURL ); + } + else + { // already have other listeners for this URL + _rxListener->statusChanged( xMultiplexer->getLastEvent() ); + } + } +} + +void SAL_CALL SbaXGridControl::removeStatusListener(const Reference< css::frame::XStatusListener > & _rxListener, const css::util::URL& _rURL) +{ + ::osl::MutexGuard aGuard( GetMutex() ); + + rtl::Reference<SbaXStatusMultiplexer>& xMultiplexer = m_aStatusMultiplexer[_rURL]; + if (!xMultiplexer.is()) + { + xMultiplexer = new SbaXStatusMultiplexer(*this,GetMutex()); + } + + if (getPeer().is() && xMultiplexer->getLength() == 1) + { + Reference< css::frame::XDispatch > xDisp(getPeer(), UNO_QUERY); + xDisp->removeStatusListener(xMultiplexer.get(), _rURL); + } + xMultiplexer->removeInterface( _rxListener ); +} + +void SAL_CALL SbaXGridControl::dispose() +{ + SolarMutexGuard aGuard; + + EventObject aEvt; + aEvt.Source = *this; + + for (auto & elem : m_aStatusMultiplexer) + { + if (elem.second.is()) + { + elem.second->disposeAndClear(aEvt); + elem.second.clear(); + } + } + StatusMultiplexerArray().swap(m_aStatusMultiplexer); + + FmXGridControl::dispose(); +} + +// SbaXGridPeer +SbaXGridPeer::SbaXGridPeer(const Reference< XComponentContext >& _rM) +: FmXGridPeer(_rM) +,m_aStatusListeners(m_aMutex) +{ +} + +SbaXGridPeer::~SbaXGridPeer() +{ +} + +void SAL_CALL SbaXGridPeer::dispose() +{ + EventObject aEvt(*this); + + m_aStatusListeners.disposeAndClear(aEvt); + + FmXGridPeer::dispose(); +} + +void SbaXGridPeer::NotifyStatusChanged(const css::util::URL& _rUrl, const Reference< css::frame::XStatusListener > & xControl) +{ + VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >(); + if (!pGrid) + return; + + css::frame::FeatureStateEvent aEvt; + aEvt.Source = *this; + aEvt.IsEnabled = !pGrid->IsReadOnlyDB(); + aEvt.FeatureURL = _rUrl; + + MapDispatchToBool::const_iterator aURLStatePos = m_aDispatchStates.find( classifyDispatchURL( _rUrl ) ); + if ( m_aDispatchStates.end() != aURLStatePos ) + aEvt.State <<= aURLStatePos->second; + else + aEvt.State <<= false; + + if (xControl.is()) + xControl->statusChanged(aEvt); + else + { + ::cppu::OInterfaceContainerHelper * pIter = m_aStatusListeners.getContainer(_rUrl); + + if (pIter) + { + ::cppu::OInterfaceIteratorHelper aListIter(*pIter); + while (aListIter.hasMoreElements()) + static_cast< css::frame::XStatusListener*>(aListIter.next())->statusChanged(aEvt); + } + } +} + +Any SAL_CALL SbaXGridPeer::queryInterface(const Type& _rType) +{ + Any aRet = ::cppu::queryInterface(_rType,static_cast<css::frame::XDispatch*>(this)); + if(aRet.hasValue()) + return aRet; + return FmXGridPeer::queryInterface(_rType); +} + +Reference< css::frame::XDispatch > SAL_CALL SbaXGridPeer::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags) +{ + if ( ( aURL.Complete == ".uno:GridSlots/BrowserAttribs" ) || ( aURL.Complete == ".uno:GridSlots/RowHeight" ) + || ( aURL.Complete == ".uno:GridSlots/ColumnAttribs" ) || ( aURL.Complete == ".uno:GridSlots/ColumnWidth" ) + ) + { + return static_cast<css::frame::XDispatch*>(this); + } + + return FmXGridPeer::queryDispatch(aURL, aTargetFrameName, nSearchFlags); +} + +IMPL_LINK_NOARG( SbaXGridPeer, OnDispatchEvent, void*, void ) +{ + VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >(); + if ( !pGrid ) // if this fails, we were disposing before arriving here + return; + + if ( !Application::IsMainThread() ) + { + // still not in the main thread (see SbaXGridPeer::dispatch). post an event, again + // without moving the special even to the back of the queue + pGrid->PostUserEvent( LINK( this, SbaXGridPeer, OnDispatchEvent ) ); + } + else + { + DispatchArgs aArgs = m_aDispatchArgs.front(); + m_aDispatchArgs.pop(); + + SbaXGridPeer::dispatch( aArgs.aURL, aArgs.aArgs ); + } +} + +SbaXGridPeer::DispatchType SbaXGridPeer::classifyDispatchURL( const URL& _rURL ) +{ + DispatchType eURLType = dtUnknown; + if ( _rURL.Complete == ".uno:GridSlots/BrowserAttribs" ) + eURLType = dtBrowserAttribs; + else if ( _rURL.Complete == ".uno:GridSlots/RowHeight" ) + eURLType = dtRowHeight; + else if ( _rURL.Complete == ".uno:GridSlots/ColumnAttribs" ) + eURLType = dtColumnAttribs; + else if ( _rURL.Complete == ".uno:GridSlots/ColumnWidth" ) + eURLType = dtColumnWidth; + return eURLType; +} + +void SAL_CALL SbaXGridPeer::dispatch(const URL& aURL, const Sequence< PropertyValue >& aArgs) +{ + VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >(); + if (!pGrid) + return; + + if ( !Application::IsMainThread() ) + { + // we're not in the main thread. This is bad, as we want to raise windows here, + // and VCL does not like windows to be opened in non-main threads (at least on Win32). + // Okay, do this async. No problem with this, as XDispatch::dispatch is defined to be + // a one-way method. + + // save the args + DispatchArgs aDispatchArgs; + aDispatchArgs.aURL = aURL; + aDispatchArgs.aArgs = aArgs; + m_aDispatchArgs.push( aDispatchArgs ); + + // post an event + // we use the Window::PostUserEvent here, instead of the application::PostUserEvent + // this saves us from keeping track of these events - as soon as the window dies, + // the events are deleted automatically. For the application way, we would need to + // do this ourself. + // As we use our grid as window, and the grid dies before we die, this should be no problem. + pGrid->PostUserEvent( LINK( this, SbaXGridPeer, OnDispatchEvent ) ); + return; + } + + SolarMutexGuard aGuard; + sal_Int16 nColId = -1; + for (const PropertyValue& rArg : aArgs) + { + if (rArg.Name == "ColumnViewPos") + { + nColId = pGrid->GetColumnIdFromViewPos(::comphelper::getINT16(rArg.Value)); + break; + } + if (rArg.Name == "ColumnModelPos") + { + nColId = pGrid->GetColumnIdFromModelPos(::comphelper::getINT16(rArg.Value)); + break; + } + if (rArg.Name == "ColumnId") + { + nColId = ::comphelper::getINT16(rArg.Value); + break; + } + } + + DispatchType eURLType = classifyDispatchURL( aURL ); + + if ( dtUnknown == eURLType ) + return; + + // notify any status listeners that the dialog is now active (well, about to be active) + MapDispatchToBool::const_iterator aThisURLState = m_aDispatchStates.emplace( eURLType, true ).first; + NotifyStatusChanged( aURL, nullptr ); + + // execute the dialog + switch ( eURLType ) + { + case dtBrowserAttribs: + pGrid->SetBrowserAttrs(); + break; + + case dtRowHeight: + pGrid->SetRowHeight(); + break; + + case dtColumnAttribs: + { + OSL_ENSURE(nColId != -1, "SbaXGridPeer::dispatch : invalid parameter !"); + if (nColId != -1) + break; + pGrid->SetColAttrs(nColId); + } + break; + + case dtColumnWidth: + { + OSL_ENSURE(nColId != -1, "SbaXGridPeer::dispatch : invalid parameter !"); + if (nColId != -1) + break; + pGrid->SetColWidth(nColId); + } + break; + + case dtUnknown: + break; + } + + // notify any status listeners that the dialog vanished + m_aDispatchStates.erase( aThisURLState ); + NotifyStatusChanged( aURL, nullptr ); +} + +void SAL_CALL SbaXGridPeer::addStatusListener(const Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) +{ + ::cppu::OInterfaceContainerHelper* pCont = m_aStatusListeners.getContainer(aURL); + if (!pCont) + m_aStatusListeners.addInterface(aURL,xControl); + else + pCont->addInterface(xControl); + NotifyStatusChanged(aURL, xControl); +} + +void SAL_CALL SbaXGridPeer::removeStatusListener(const Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) +{ + ::cppu::OInterfaceContainerHelper* pCont = m_aStatusListeners.getContainer(aURL); + if ( pCont ) + pCont->removeInterface(xControl); +} + +Sequence< Type > SAL_CALL SbaXGridPeer::getTypes() +{ + return comphelper::concatSequences( + FmXGridPeer::getTypes(), + Sequence { cppu::UnoType<css::frame::XDispatch>::get() }); +} + +UNO3_GETIMPLEMENTATION2_IMPL(SbaXGridPeer, FmXGridPeer); + +VclPtr<FmGridControl> SbaXGridPeer::imp_CreateControl(vcl::Window* pParent, WinBits nStyle) +{ + return VclPtr<SbaGridControl>::Create( m_xContext, pParent, this, nStyle); +} + +// SbaGridHeader + +SbaGridHeader::SbaGridHeader(BrowseBox* pParent) + :FmGridHeader(pParent, WB_STDHEADERBAR | WB_DRAG) + ,DragSourceHelper(this) +{ +} + +SbaGridHeader::~SbaGridHeader() +{ + disposeOnce(); +} + +void SbaGridHeader::dispose() +{ + DragSourceHelper::dispose(); + FmGridHeader::dispose(); +} + +void SbaGridHeader::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel ) +{ + SolarMutexGuard aGuard; + // in the new DnD API, the solar mutex is not locked when StartDrag is called + + ImplStartColumnDrag( _nAction, _rPosPixel ); +} + +void SbaGridHeader::MouseButtonDown( const MouseEvent& _rMEvt ) +{ + if (_rMEvt.IsLeft()) + if (_rMEvt.GetClicks() != 2) + { + // the base class will start a column move here, which we don't want to allow + // (at the moment. If we store relative positions with the columns, we can allow column moves...) + + } + + FmGridHeader::MouseButtonDown(_rMEvt); +} + +void SbaGridHeader::ImplStartColumnDrag(sal_Int8 _nAction, const Point& _rMousePos) +{ + sal_uInt16 nId = GetItemId(_rMousePos); + bool bResizingCol = false; + if (HEADERBAR_ITEM_NOTFOUND != nId) + { + tools::Rectangle aColRect = GetItemRect(nId); + aColRect.AdjustLeft(nId ? 3 : 0 ); // the handle col (nId == 0) does not have a left margin for resizing + aColRect.AdjustRight( -3 ); + bResizingCol = !aColRect.IsInside(_rMousePos); + } + if (bResizingCol) + return; + + // force the base class to end its drag mode + EndTracking(TrackingEventFlags::Cancel | TrackingEventFlags::End); + + // because we have 3d-buttons the select handler is called from MouseButtonUp, but StartDrag + // occurs earlier (while the mouse button is down) + // so for optical reasons we select the column before really starting the drag operation. + notifyColumnSelect(nId); + + static_cast<SbaGridControl*>(GetParent())->StartDrag(_nAction, + Point( + _rMousePos.X() + GetPosPixel().X(), // we aren't left-justified with our parent, in contrast to the data window + _rMousePos.Y() - GetSizePixel().Height() + ) + ); +} + +void SbaGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, PopupMenu& rMenu) +{ + FmGridHeader::PreExecuteColumnContextMenu(nColId, rMenu); + + // some items are valid only if the db isn't readonly + bool bDBIsReadOnly = static_cast<SbaGridControl*>(GetParent())->IsReadOnlyDB(); + + if (bDBIsReadOnly) + { + rMenu.EnableItem(rMenu.GetItemId("hide"), false); + PopupMenu* pShowColsMenu = rMenu.GetPopupMenu(rMenu.GetItemId("show")); + if (pShowColsMenu) + { + // at most 16 items which mean "show column <name>" + for (sal_uInt16 i=1; i<16; ++i) + pShowColsMenu->EnableItem(i, false); + // "show cols/more..." and "show cols/all" + pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("more"), false); + pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("all"), false); + } + } + + // prepend some new items + bool bColAttrs = (nColId != sal_uInt16(-1)) && (nColId != 0); + if ( !(bColAttrs && !bDBIsReadOnly)) + return; + + sal_uInt16 nPos = 0; + sal_uInt16 nModelPos = static_cast<SbaGridControl*>(GetParent())->GetModelColumnPos(nColId); + Reference< XPropertySet > xField = static_cast<SbaGridControl*>(GetParent())->getField(nModelPos); + + if ( xField.is() ) + { + switch( ::comphelper::getINT32(xField->getPropertyValue(PROPERTY_TYPE)) ) + { + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::SQLNULL: + case DataType::OBJECT: + case DataType::BLOB: + case DataType::CLOB: + case DataType::REF: + break; + default: + rMenu.InsertItem(ID_BROWSER_COLATTRSET, DBA_RES(RID_STR_COLUMN_FORMAT), MenuItemBits::NONE, OString(), nPos++); + rMenu.SetHelpId(ID_BROWSER_COLATTRSET, HID_BROWSER_COLUMNFORMAT); + rMenu.InsertSeparator(OString(), nPos++); + } + } + + rMenu.InsertItem(ID_BROWSER_COLWIDTH, DBA_RES(RID_STR_COLUMN_WIDTH), MenuItemBits::NONE, OString(), nPos++); + rMenu.SetHelpId(ID_BROWSER_COLWIDTH, HID_BROWSER_COLUMNWIDTH); + rMenu.InsertSeparator(OString(), nPos++); +} + +void SbaGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const PopupMenu& rMenu, sal_uInt16 nExecutionResult) +{ + switch (nExecutionResult) + { + case ID_BROWSER_COLWIDTH: + static_cast<SbaGridControl*>(GetParent())->SetColWidth(nColId); + break; + + case ID_BROWSER_COLATTRSET: + static_cast<SbaGridControl*>(GetParent())->SetColAttrs(nColId); + break; + case ID_BROWSER_COLUMNINFO: + { + sal_uInt16 nModelPos = static_cast<SbaGridControl*>(GetParent())->GetModelColumnPos(nColId); + Reference< XPropertySet > xField = static_cast<SbaGridControl*>(GetParent())->getField(nModelPos); + + if(!xField.is()) + break; + std::vector< std::shared_ptr<OTableRow> > vClipboardList; + // send it to the clipboard + vClipboardList.push_back(std::make_shared<OTableRow>(xField)); + rtl::Reference<OTableRowExchange> pData = new OTableRowExchange(vClipboardList); + pData->CopyToClipboard(GetParent()); + } + break; + + default: FmGridHeader::PostExecuteColumnContextMenu(nColId, rMenu, nExecutionResult); + } +} + +// SbaGridControl +SbaGridControl::SbaGridControl(Reference< XComponentContext > const & _rM, + vcl::Window* pParent, FmXGridPeer* _pPeer, WinBits nBits) + :FmGridControl(_rM,pParent, _pPeer, nBits) + ,m_pMasterListener(nullptr) + ,m_nAsyncDropEvent(nullptr) + ,m_bActivatingForDrop(false) +{ +} + +SbaGridControl::~SbaGridControl() +{ + disposeOnce(); +} + +void SbaGridControl::dispose() +{ + if (m_nAsyncDropEvent) + Application::RemoveUserEvent(m_nAsyncDropEvent); + m_nAsyncDropEvent = nullptr; + FmGridControl::dispose(); +} + +VclPtr<BrowserHeader> SbaGridControl::imp_CreateHeaderBar(BrowseBox* pParent) +{ + return VclPtr<SbaGridHeader>::Create(pParent); +} + +CellController* SbaGridControl::GetController(long nRow, sal_uInt16 nCol) +{ + if ( m_bActivatingForDrop ) + return nullptr; + + return FmGridControl::GetController(nRow, nCol); +} + +void SbaGridControl::PreExecuteRowContextMenu(sal_uInt16 nRow, PopupMenu& rMenu) +{ + FmGridControl::PreExecuteRowContextMenu(nRow, rMenu); + + sal_uInt16 nPos = 0; + + if (!IsReadOnlyDB()) + { + rMenu.InsertItem(ID_BROWSER_TABLEATTR, DBA_RES(RID_STR_TABLE_FORMAT), MenuItemBits::NONE, OString(), nPos++); + rMenu.SetHelpId(ID_BROWSER_TABLEATTR, HID_BROWSER_TABLEFORMAT); + + rMenu.InsertItem(ID_BROWSER_ROWHEIGHT, DBA_RES(RID_STR_ROW_HEIGHT), MenuItemBits::NONE, OString(), nPos++); + rMenu.SetHelpId(ID_BROWSER_ROWHEIGHT, HID_BROWSER_ROWHEIGHT); + rMenu.InsertSeparator(OString(), nPos++); + } + + if ( GetSelectRowCount() > 0 ) + { + rMenu.InsertItem(ID_BROWSER_COPY, DBA_RES(RID_STR_COPY), MenuItemBits::NONE, OString(), nPos++); + rMenu.InsertSeparator(OString(), nPos++); + } +} + +SvNumberFormatter* SbaGridControl::GetDatasourceFormatter() +{ + Reference< css::util::XNumberFormatsSupplier > xSupplier = ::dbtools::getNumberFormats(::dbtools::getConnection(Reference< XRowSet > (getDataSource(),UNO_QUERY)), true, getContext()); + + SvNumberFormatsSupplierObj* pSupplierImpl = comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( xSupplier ); + if ( !pSupplierImpl ) + return nullptr; + + SvNumberFormatter* pFormatter = pSupplierImpl->GetNumberFormatter(); + return pFormatter; +} + +void SbaGridControl::SetColWidth(sal_uInt16 nColId) +{ + // get the (UNO) column model + sal_uInt16 nModelPos = GetModelColumnPos(nColId); + Reference< XIndexAccess > xCols = GetPeer()->getColumns(); + Reference< XPropertySet > xAffectedCol; + if (xCols.is() && (nModelPos != sal_uInt16(-1))) + xAffectedCol.set(xCols->getByIndex(nModelPos), css::uno::UNO_QUERY); + + if (!xAffectedCol.is()) + return; + + Any aWidth = xAffectedCol->getPropertyValue(PROPERTY_WIDTH); + sal_Int32 nCurWidth = aWidth.hasValue() ? ::comphelper::getINT32(aWidth) : -1; + + DlgSize aDlgColWidth(GetFrameWeld(), nCurWidth, false); + if (aDlgColWidth.run() != RET_OK) + return; + + sal_Int32 nValue = aDlgColWidth.GetValue(); + Any aNewWidth; + if (-1 == nValue) + { // set to default + Reference< XPropertyState > xPropState(xAffectedCol, UNO_QUERY); + if (xPropState.is()) + { + try { aNewWidth = xPropState->getPropertyDefault(PROPERTY_WIDTH); } catch(Exception&) { } ; + } + } + else + aNewWidth <<= nValue; + try { xAffectedCol->setPropertyValue(PROPERTY_WIDTH, aNewWidth); } catch(Exception&) { } ; +} + +void SbaGridControl::SetRowHeight() +{ + Reference< XPropertySet > xCols(GetPeer()->getColumns(), UNO_QUERY); + if (!xCols.is()) + return; + + Any aHeight = xCols->getPropertyValue(PROPERTY_ROW_HEIGHT); + sal_Int32 nCurHeight = aHeight.hasValue() ? ::comphelper::getINT32(aHeight) : -1; + + DlgSize aDlgRowHeight(GetFrameWeld(), nCurHeight, true); + if (aDlgRowHeight.run() != RET_OK) + return; + + sal_Int32 nValue = aDlgRowHeight.GetValue(); + Any aNewHeight; + if (sal_Int16(-1) == nValue) + { // set to default + Reference< XPropertyState > xPropState(xCols, UNO_QUERY); + if (xPropState.is()) + { + try + { + aNewHeight = xPropState->getPropertyDefault(PROPERTY_ROW_HEIGHT); + } + catch(Exception&) + { } + } + } + else + aNewHeight <<= nValue; + try + { + xCols->setPropertyValue(PROPERTY_ROW_HEIGHT, aNewHeight); + } + catch(Exception&) + { + OSL_FAIL("setPropertyValue: PROPERTY_ROW_HEIGHT throws an exception"); + } +} + +void SbaGridControl::SetColAttrs(sal_uInt16 nColId) +{ + SvNumberFormatter* pFormatter = GetDatasourceFormatter(); + if (!pFormatter) + return; + + sal_uInt16 nModelPos = GetModelColumnPos(nColId); + + // get the (UNO) column model + Reference< XIndexAccess > xCols = GetPeer()->getColumns(); + Reference< XPropertySet > xAffectedCol; + if (xCols.is() && (nModelPos != sal_uInt16(-1))) + xAffectedCol.set(xCols->getByIndex(nModelPos), css::uno::UNO_QUERY); + + // get the field the column is bound to + Reference< XPropertySet > xField = getField(nModelPos); + ::dbaui::callColumnFormatDialog(xAffectedCol,xField,pFormatter,this);//(Window::GetSettings().GetLanguage()); +} + +void SbaGridControl::SetBrowserAttrs() +{ + Reference< XPropertySet > xGridModel(GetPeer()->getColumns(), UNO_QUERY); + if (!xGridModel.is()) + return; + + try + { + Reference< XComponentContext > xContext = getContext(); + css::beans::PropertyValue aArg; + css::uno::Sequence<css::uno::Any> aArguments(2); + aArg.Name = "IntrospectedObject"; + aArg.Value <<= xGridModel; + aArguments[0] <<= aArg; + aArg.Name = "ParentWindow"; + aArg.Value <<= VCLUnoHelper::GetInterface(this); + aArguments[1] <<= aArg; + Reference<XExecutableDialog> xExecute(xContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.form.ControlFontDialog", + aArguments, xContext), css::uno::UNO_QUERY_THROW); + xExecute->execute(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SbaGridControl::PostExecuteRowContextMenu(sal_uInt16 nRow, const PopupMenu& rMenu, sal_uInt16 nExecutionResult) +{ + switch (nExecutionResult) + { + case ID_BROWSER_TABLEATTR: + SetBrowserAttrs(); + break; + case ID_BROWSER_ROWHEIGHT: + SetRowHeight(); + break; + case ID_BROWSER_COPY: + CopySelectedRowsToClipboard(); + break; + + default: + FmGridControl::PostExecuteRowContextMenu(nRow, rMenu, nExecutionResult); + break; + } +} + +void SbaGridControl::Select() +{ + // Some selection has changed ... + FmGridControl::Select(); + + if (m_pMasterListener) + m_pMasterListener->SelectionChanged(); +} + +void SbaGridControl::ActivateCell(long nRow, sal_uInt16 nCol, bool bSetCellFocus /*= sal_True*/ ) +{ + FmGridControl::ActivateCell(nRow, nCol, bSetCellFocus); + if (m_pMasterListener) + m_pMasterListener->CellActivated(); +} + +void SbaGridControl::DeactivateCell(bool bUpdate /*= sal_True*/) +{ + FmGridControl::DeactivateCell(bUpdate); + if (m_pMasterListener) + m_pMasterListener->CellDeactivated(); +} + +void SbaGridControl::onRowChange() +{ + if ( m_pMasterListener ) + m_pMasterListener->RowChanged(); +} + +void SbaGridControl::onColumnChange() +{ + if ( m_pMasterListener ) + m_pMasterListener->ColumnChanged(); +} + +Reference< XPropertySet > SbaGridControl::getField(sal_uInt16 nModelPos) +{ + Reference< XPropertySet > xEmptyReturn; + try + { + // first get the name of the column + Reference< XIndexAccess > xCols = GetPeer()->getColumns(); + if ( xCols.is() && xCols->getCount() > nModelPos ) + { + Reference< XPropertySet > xCol(xCols->getByIndex(nModelPos),UNO_QUERY); + if ( xCol.is() ) + xEmptyReturn.set(xCol->getPropertyValue(PROPERTY_BOUNDFIELD),UNO_QUERY); + } + else + OSL_FAIL("SbaGridControl::getField getColumns returns NULL or ModelPos is > than count!"); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "SbaGridControl::getField Exception occurred"); + } + + return xEmptyReturn; +} + +bool SbaGridControl::IsReadOnlyDB() const +{ + // assume yes if anything fails + bool bDBIsReadOnly = true; + + try + { + // the db is the implemented by the parent of the grid control's model ... + Reference< XChild > xColumns(GetPeer()->getColumns(), UNO_QUERY); + if (xColumns.is()) + { + Reference< XRowSet > xDataSource(xColumns->getParent(), UNO_QUERY); + ::dbtools::ensureRowSetConnection( xDataSource, getContext(), nullptr ); + Reference< XChild > xConn(::dbtools::getConnection(xDataSource),UNO_QUERY); + if (xConn.is()) + { + // ... and the RO-flag simply is implemented by a property + Reference< XPropertySet > xDbProps(xConn->getParent(), UNO_QUERY); + if (xDbProps.is()) + { + Reference< XPropertySetInfo > xInfo = xDbProps->getPropertySetInfo(); + if (xInfo->hasPropertyByName(PROPERTY_ISREADONLY)) + bDBIsReadOnly = ::comphelper::getBOOL(xDbProps->getPropertyValue(PROPERTY_ISREADONLY)); + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "SbaGridControl::IsReadOnlyDB Exception occurred"); + } + + return bDBIsReadOnly; +} + +void SbaGridControl::MouseButtonDown( const BrowserMouseEvent& rMEvt) +{ + long nRow = GetRowAtYPosPixel(rMEvt.GetPosPixel().Y()); + sal_uInt16 nColPos = GetColumnAtXPosPixel(rMEvt.GetPosPixel().X()); + sal_uInt16 nViewPos = (nColPos == BROWSER_INVALIDID) ? sal_uInt16(-1) : nColPos-1; + // 'the handle column' and 'no valid column' will both result in a view position of -1 ! + + bool bHitEmptySpace = (nRow > GetRowCount()) || (nViewPos == sal_uInt16(-1)); + + if (bHitEmptySpace && (rMEvt.GetClicks() == 2) && rMEvt.IsMod1()) + Control::MouseButtonDown(rMEvt); + else + FmGridControl::MouseButtonDown(rMEvt); +} + +void SbaGridControl::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel ) +{ + SolarMutexGuard aGuard; + // in the new DnD API, the solar mutex is not locked when StartDrag is called + + bool bHandled = false; + + do + { + // determine if dragging is allowed + // (Yes, this is controller (not view) functionality. But collecting and evaluating all the + // information necessary via UNO would be quite difficult (if not impossible) so + // my laziness says 'do it here'...) + long nRow = GetRowAtYPosPixel(_rPosPixel.Y()); + sal_uInt16 nColPos = GetColumnAtXPosPixel(_rPosPixel.X()); + sal_uInt16 nViewPos = (nColPos == BROWSER_INVALIDID) ? sal_uInt16(-1) : nColPos-1; + // 'the handle column' and 'no valid column' will both result in a view position of -1 ! + + bool bCurrentRowVirtual = IsCurrentAppending() && IsModified(); + // the current row doesn't really exist: the user's appending a new one and already has entered some data, + // so the row contains data which has no counter part within the data source + + long nCorrectRowCount = GetRowCount(); + if (GetOptions() & DbGridControlOptions::Insert) + --nCorrectRowCount; // there is an empty row for inserting records + if (bCurrentRowVirtual) + --nCorrectRowCount; + + if ((nColPos == BROWSER_INVALIDID) || (nRow >= nCorrectRowCount)) + break; + + bool bHitHandle = (nColPos == 0); + + // check which kind of dragging has to be initiated + if ( bHitHandle // the handle column + // AND + && ( GetSelectRowCount() // at least one row is selected + // OR + || ( (nRow >= 0) // a row below the header + && !bCurrentRowVirtual // we aren't appending a new record + && (nRow != GetCurrentPos()) // a row which is not the current one + ) // OR + || ( (0 == GetSelectRowCount()) // no rows selected + && (-1 == nRow) // hit the header + ) + ) + ) + { // => start dragging the row + if (GetDataWindow().IsMouseCaptured()) + GetDataWindow().ReleaseMouse(); + + if (0 == GetSelectRowCount()) + // no rows selected, but here in this branch + // -> the user started dragging the upper left corner, which symbolizes the whole table + SelectAll(); + + getMouseEvent().Clear(); + implTransferSelectedRows(static_cast<sal_Int16>(nRow), false); + + bHandled = true; + } + else if ( (nRow < 0) // the header + && (!bHitHandle) // non-handle column + && (nViewPos < GetViewColCount()) // valid (existing) column + ) + { // => start dragging the column + if (GetDataWindow().IsMouseCaptured()) + GetDataWindow().ReleaseMouse(); + + getMouseEvent().Clear(); + DoColumnDrag(nViewPos); + + bHandled = true; + } + else if ( !bHitHandle // non-handle column + && (nRow >= 0) // non-header row + ) + { // => start dragging the field content + if (GetDataWindow().IsMouseCaptured()) + GetDataWindow().ReleaseMouse(); + + getMouseEvent().Clear(); + DoFieldDrag(nViewPos, static_cast<sal_Int16>(nRow)); + + bHandled = true; + } + } + while (false); + + if (!bHandled) + FmGridControl::StartDrag(_nAction, _rPosPixel); +} + +void SbaGridControl::DoColumnDrag(sal_uInt16 nColumnPos) +{ + Reference< XPropertySet > xDataSource = getDataSource(); + OSL_ENSURE(xDataSource.is(), "SbaGridControl::DoColumnDrag : invalid data source !"); + + Reference< XPropertySet > xAffectedCol; + Reference< XPropertySet > xAffectedField; + Reference< XConnection > xActiveConnection; + + // determine the field to drag + OUString sField; + try + { + xActiveConnection = ::dbtools::getConnection(Reference< XRowSet >(getDataSource(),UNO_QUERY)); + + sal_uInt16 nModelPos = GetModelColumnPos(GetColumnIdFromViewPos(nColumnPos)); + Reference< XIndexContainer > xCols = GetPeer()->getColumns(); + xAffectedCol.set(xCols->getByIndex(nModelPos),UNO_QUERY); + if (xAffectedCol.is()) + { + xAffectedCol->getPropertyValue(PROPERTY_CONTROLSOURCE) >>= sField; + xAffectedField.set(xAffectedCol->getPropertyValue(PROPERTY_BOUNDFIELD),UNO_QUERY); + } + } + catch(Exception&) + { + OSL_FAIL("SbaGridControl::DoColumnDrag : something went wrong while getting the column"); + } + if (sField.isEmpty()) + return; + + rtl::Reference<OColumnTransferable> pDataTransfer = new OColumnTransferable(xDataSource, sField, xAffectedField, xActiveConnection, ColumnTransferFormatFlags::FIELD_DESCRIPTOR | ColumnTransferFormatFlags::COLUMN_DESCRIPTOR); + pDataTransfer->StartDrag(this, DND_ACTION_COPY | DND_ACTION_LINK); +} + +void SbaGridControl::CopySelectedRowsToClipboard() +{ + OSL_ENSURE( GetSelectRowCount() > 0, "SbaGridControl::CopySelectedRowsToClipboard: invalid call!" ); + implTransferSelectedRows( static_cast<sal_Int16>(FirstSelectedRow()), true ); +} + +void SbaGridControl::implTransferSelectedRows( sal_Int16 nRowPos, bool _bTrueIfClipboardFalseIfDrag ) +{ + Reference< XPropertySet > xForm = getDataSource(); + OSL_ENSURE( xForm.is(), "SbaGridControl::implTransferSelectedRows: invalid form!" ); + + // build the sequence of numbers of selected rows + Sequence< Any > aSelectedRows; + bool bSelectionBookmarks = true; + + // collect the affected rows + if ((GetSelectRowCount() == 0) && (nRowPos >= 0)) + { + aSelectedRows.realloc( 1 ); + aSelectedRows[0] <<= static_cast<sal_Int32>(nRowPos + 1); + bSelectionBookmarks = false; + } + else if ( !IsAllSelected() && GetSelectRowCount() ) + { + aSelectedRows = getSelectionBookmarks(); + bSelectionBookmarks = true; + } + + try + { + rtl::Reference<ODataClipboard> pTransfer = new ODataClipboard( xForm, aSelectedRows, bSelectionBookmarks, getContext() ); + + if ( _bTrueIfClipboardFalseIfDrag ) + pTransfer->CopyToClipboard( this ); + else + pTransfer->StartDrag(this, DND_ACTION_COPY | DND_ACTION_LINK); + } + catch(Exception&) + { + } +} + +void SbaGridControl::DoFieldDrag(sal_uInt16 nColumnPos, sal_Int16 nRowPos) +{ + // the only thing to do here is dragging the pure cell text + // the old implementation copied a SBA_FIELDDATAEXCHANGE_FORMAT, too, (which was rather expensive to obtain), + // but we have no client for this DnD format anymore (the mail part of SO 5.2 was the only client) + + OUString sCellText; + try + { + Reference< XGridFieldDataSupplier > xFieldData(static_cast< XGridPeer* >(GetPeer()), UNO_QUERY); + Sequence<sal_Bool> aSupportingText = xFieldData->queryFieldDataType(cppu::UnoType<decltype(sCellText)>::get()); + if (aSupportingText.getConstArray()[nColumnPos]) + { + Sequence< Any> aCellContents = xFieldData->queryFieldData(nRowPos, cppu::UnoType<decltype(sCellText)>::get()); + sCellText = ::comphelper::getString(aCellContents.getConstArray()[nColumnPos]); + ::svt::OStringTransfer::StartStringDrag(sCellText, this, DND_ACTION_COPY); + } + } + catch(Exception&) + { + OSL_FAIL("SbaGridControl::DoFieldDrag : could not retrieve the cell's contents !"); + return; + } + +} + + namespace { + +/// unary_function Functor object for class ZZ returntype is void + struct SbaGridControlPrec + { + bool operator()(const DataFlavorExVector::value_type& _aType) + { + switch (_aType.mnSotId) + { + case SotClipboardFormatId::DBACCESS_TABLE: // table descriptor + case SotClipboardFormatId::DBACCESS_QUERY: // query descriptor + case SotClipboardFormatId::DBACCESS_COMMAND: // SQL command + return true; + default: break; + } + return false; + } + }; + + } + +sal_Int8 SbaGridControl::AcceptDrop( const BrowserAcceptDropEvent& rEvt ) +{ + sal_Int8 nAction = DND_ACTION_NONE; + + // we need a valid connection + if (!::dbtools::getConnection(Reference< XRowSet > (getDataSource(),UNO_QUERY)).is()) + return nAction; + + if ( IsDropFormatSupported( SotClipboardFormatId::STRING ) ) do + { // odd construction, but spares us a lot of (explicit ;) goto's + + if (!GetEmptyRow().is()) + // without an empty row we're not in update mode + break; + + const long nRow = GetRowAtYPosPixel(rEvt.maPosPixel.Y(), false); + const sal_uInt16 nCol = GetColumnId(GetColumnAtXPosPixel(rEvt.maPosPixel.X())); + + long nCorrectRowCount = GetRowCount(); + if (GetOptions() & DbGridControlOptions::Insert) + --nCorrectRowCount; // there is an empty row for inserting records + if (IsCurrentAppending()) + --nCorrectRowCount; // the current data record doesn't really exist, we are appending a new one + + if ( (nCol == BROWSER_INVALIDID) || (nRow >= nCorrectRowCount) || (nCol == 0) ) + // no valid cell under the mouse cursor + break; + + tools::Rectangle aRect = GetCellRect(nRow, nCol, false); + if (!aRect.IsInside(rEvt.maPosPixel)) + // not dropped within a cell (a cell isn't as wide as the column - the are small spaces) + break; + + if ((IsModified() || (GetCurrentRow().is() && GetCurrentRow()->IsModified())) && (GetCurrentPos() != nRow)) + // there is a current and modified row or cell and he text is to be dropped into another one + break; + + CellControllerRef xCurrentController = Controller(); + if (xCurrentController.is() && xCurrentController->IsModified() && ((nRow != GetCurRow()) || (nCol != GetCurColumnId()))) + // the current controller is modified and the user wants to drop in another cell -> no chance + // (when leaving the modified cell an error may occur - this is deadly while dragging) + break; + + Reference< XPropertySet > xField = getField(GetModelColumnPos(nCol)); + if (!xField.is()) + // the column is not valid bound (for instance a binary field) + break; + + try + { + if (::comphelper::getBOOL(xField->getPropertyValue(PROPERTY_ISREADONLY))) + break; + } + catch (const Exception& ) + { + // assume RO + break; + } + + try + { + // assume that text can be dropped into a field if the column has a css::awt::XTextComponent interface + Reference< XIndexAccess > xColumnControls(static_cast<css::form::XGridPeer*>(GetPeer()), UNO_QUERY); + if (xColumnControls.is()) + { + Reference< css::awt::XTextComponent > xColControl( + xColumnControls->getByIndex(GetViewColumnPos(nCol)), + css::uno::UNO_QUERY); + if (xColControl.is()) + { + m_bActivatingForDrop = true; + GoToRowColumnId(nRow, nCol); + m_bActivatingForDrop = false; + + nAction = DND_ACTION_COPY; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + } while (false); + + if(nAction != DND_ACTION_COPY && GetEmptyRow().is()) + { + const DataFlavorExVector& _rFlavors = GetDataFlavors(); + if(std::any_of(_rFlavors.begin(),_rFlavors.end(),SbaGridControlPrec())) + nAction = DND_ACTION_COPY; + } + + return (DND_ACTION_NONE != nAction) ? nAction : FmGridControl::AcceptDrop(rEvt); +} + +sal_Int8 SbaGridControl::ExecuteDrop( const BrowserExecuteDropEvent& rEvt ) +{ + // we need some properties of our data source + Reference< XPropertySet > xDataSource = getDataSource(); + if (!xDataSource.is()) + return DND_ACTION_NONE; + + // we need a valid connection + if (!::dbtools::getConnection(Reference< XRowSet > (xDataSource,UNO_QUERY)).is()) + return DND_ACTION_NONE; + + if ( IsDropFormatSupported( SotClipboardFormatId::STRING ) ) + { + long nRow = GetRowAtYPosPixel(rEvt.maPosPixel.Y(), false); + sal_uInt16 nCol = GetColumnAtXPosPixel(rEvt.maPosPixel.X()); + + long nCorrectRowCount = GetRowCount(); + if (GetOptions() & DbGridControlOptions::Insert) + --nCorrectRowCount; // there is an empty row for inserting records + if (IsCurrentAppending()) + --nCorrectRowCount; // the current data record doesn't really exist, we are appending a new one + + OSL_ENSURE((nCol != BROWSER_INVALIDID) && (nRow < nCorrectRowCount), "SbaGridControl::Drop : dropped on an invalid position !"); + // AcceptDrop should have caught this + + // from now we work with ids instead of positions + nCol = GetColumnId(nCol); + + GoToRowColumnId(nRow, nCol); + if (!IsEditing()) + ActivateCell(); + + CellControllerRef xCurrentController = Controller(); + if (!xCurrentController.is() || nullptr == dynamic_cast< const EditCellController* >(xCurrentController.get())) + return DND_ACTION_NONE; + Edit& rEdit = static_cast<Edit&>(xCurrentController->GetWindow()); + + // get the dropped string + TransferableDataHelper aDropped( rEvt.maDropEvent.Transferable ); + OUString sDropped; + if ( !aDropped.GetString( SotClipboardFormatId::STRING, sDropped ) ) + return DND_ACTION_NONE; + + rEdit.SetText( sDropped ); + xCurrentController->SetModified(); + rEdit.Modify(); + // SetText itself doesn't call a Modify as it isn't a user interaction + + return DND_ACTION_COPY; + } + + if(GetEmptyRow().is()) + { + const DataFlavorExVector& _rFlavors = GetDataFlavors(); + if( std::any_of(_rFlavors.begin(),_rFlavors.end(), SbaGridControlPrec()) ) + { + TransferableDataHelper aDropped( rEvt.maDropEvent.Transferable ); + m_aDataDescriptor = ODataAccessObjectTransferable::extractObjectDescriptor(aDropped); + if (m_nAsyncDropEvent) + Application::RemoveUserEvent(m_nAsyncDropEvent); + m_nAsyncDropEvent = Application::PostUserEvent(LINK(this, SbaGridControl, AsynchDropEvent), nullptr, true); + return DND_ACTION_COPY; + } + } + + return DND_ACTION_NONE; +} + +Reference< XPropertySet > SbaGridControl::getDataSource() const +{ + Reference< XPropertySet > xReturn; + + Reference< XChild > xColumns(GetPeer()->getColumns(), UNO_QUERY); + if (xColumns.is()) + xReturn.set(xColumns->getParent(), UNO_QUERY); + + return xReturn; +} + +IMPL_LINK_NOARG(SbaGridControl, AsynchDropEvent, void*, void) +{ + m_nAsyncDropEvent = nullptr; + + Reference< XPropertySet > xDataSource = getDataSource(); + if ( xDataSource.is() ) + { + bool bCountFinal = false; + xDataSource->getPropertyValue(PROPERTY_ISROWCOUNTFINAL) >>= bCountFinal; + if ( !bCountFinal ) + setDataSource(nullptr); // detach from grid control + Reference< XResultSetUpdate > xResultSetUpdate(xDataSource,UNO_QUERY); + rtl::Reference<ODatabaseImportExport> pImExport = new ORowSetImportExport(GetFrameWeld(),xResultSetUpdate,m_aDataDescriptor, getContext()); + Hide(); + try + { + pImExport->initialize(m_aDataDescriptor); + if (m_pMasterListener) + m_pMasterListener->BeforeDrop(); + if(!pImExport->Read()) + { + OUString sError = DBA_RES(STR_NO_COLUMNNAME_MATCHING); + throwGenericSQLException(sError,nullptr); + } + if (m_pMasterListener) + m_pMasterListener->AfterDrop(); + Show(); + } + catch(const SQLException& e) + { + if (m_pMasterListener) + m_pMasterListener->AfterDrop(); + Show(); + ::dbtools::showError( ::dbtools::SQLExceptionInfo(e), VCLUnoHelper::GetInterface(this), getContext() ); + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + if (m_pMasterListener) + m_pMasterListener->AfterDrop(); + Show(); + } + if ( !bCountFinal ) + setDataSource(Reference< XRowSet >(xDataSource,UNO_QUERY)); + } + m_aDataDescriptor.clear(); +} + +OUString SbaGridControl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType eObjType,sal_Int32 _nPosition) const +{ + OUString sRet; + if ( ::vcl::BBTYPE_BROWSEBOX == eObjType ) + { + SolarMutexGuard aGuard; + sRet = DBA_RES(STR_DATASOURCE_GRIDCONTROL_DESC); + } + else + sRet = FmGridControl::GetAccessibleObjectDescription( eObjType,_nPosition); + return sRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/sbamultiplex.cxx b/dbaccess/source/ui/browser/sbamultiplex.cxx new file mode 100644 index 000000000..68673fd06 --- /dev/null +++ b/dbaccess/source/ui/browser/sbamultiplex.cxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sbamultiplex.hxx> +using namespace dbaui; + +// the listener multiplexers + +// XStatusListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXStatusMultiplexer, css::frame::XStatusListener) + +void SAL_CALL SbaXStatusMultiplexer::statusChanged(const css::frame::FeatureStateEvent& e) +{ + m_aLastKnownStatus = e; + m_aLastKnownStatus.Source = &m_rParent; + ::comphelper::OInterfaceIteratorHelper2 aIt( *this ); + while ( aIt.hasMoreElements() ) + static_cast< css::frame::XStatusListener* >( aIt.next() )->statusChanged( m_aLastKnownStatus ); +} + +// LoadListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXLoadMultiplexer, css::form::XLoadListener) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXLoadMultiplexer, css::form::XLoadListener, loaded, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXLoadMultiplexer, css::form::XLoadListener, unloaded, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXLoadMultiplexer, css::form::XLoadListener, unloading, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXLoadMultiplexer, css::form::XLoadListener, reloading, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXLoadMultiplexer, css::form::XLoadListener, reloaded, css::lang::EventObject) + +// css::sdbc::XRowSetListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXRowSetMultiplexer, css::sdbc::XRowSetListener) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXRowSetMultiplexer, css::sdbc::XRowSetListener, cursorMoved, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXRowSetMultiplexer, css::sdbc::XRowSetListener, rowChanged, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXRowSetMultiplexer, css::sdbc::XRowSetListener, rowSetChanged, css::lang::EventObject) + +// css::sdb::XRowSetApproveListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXRowSetApproveMultiplexer, css::sdb::XRowSetApproveListener) +IMPLEMENT_LISTENER_MULTIPLEXER_BOOL_METHOD(SbaXRowSetApproveMultiplexer, css::sdb::XRowSetApproveListener, approveCursorMove, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_BOOL_METHOD(SbaXRowSetApproveMultiplexer, css::sdb::XRowSetApproveListener, approveRowChange, css::sdb::RowChangeEvent) +IMPLEMENT_LISTENER_MULTIPLEXER_BOOL_METHOD(SbaXRowSetApproveMultiplexer, css::sdb::XRowSetApproveListener, approveRowSetChange, css::lang::EventObject) + +// css::sdb::XSQLErrorListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXSQLErrorMultiplexer, css::sdb::XSQLErrorListener) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXSQLErrorMultiplexer, css::sdb::XSQLErrorListener, errorOccured, css::sdb::SQLErrorEvent) + +// css::form::XDatabaseParameterListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXParameterMultiplexer, css::form::XDatabaseParameterListener) +IMPLEMENT_LISTENER_MULTIPLEXER_BOOL_METHOD(SbaXParameterMultiplexer, css::form::XDatabaseParameterListener, approveParameter, css::form::DatabaseParameterEvent) + +// css::form::XSubmitListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXSubmitMultiplexer, css::form::XSubmitListener) +IMPLEMENT_LISTENER_MULTIPLEXER_BOOL_METHOD(SbaXSubmitMultiplexer, css::form::XSubmitListener, approveSubmit, css::lang::EventObject) + +// css::form::XResetListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXResetMultiplexer, css::form::XResetListener) +IMPLEMENT_LISTENER_MULTIPLEXER_BOOL_METHOD(SbaXResetMultiplexer, css::form::XResetListener, approveReset, css::lang::EventObject) +IMPLEMENT_LISTENER_MULTIPLEXER_VOID_METHOD(SbaXResetMultiplexer, css::form::XResetListener, resetted, css::lang::EventObject) + +// css::beans::XPropertyChangeListener +IMPLEMENT_PROPERTY_MULTIPLEXER(SbaXPropertyChangeMultiplexer, css::beans::XPropertyChangeListener, propertyChange, css::beans::PropertyChangeEvent) + +// css::beans::XVetoableChangeListener +IMPLEMENT_PROPERTY_MULTIPLEXER(SbaXVetoableChangeMultiplexer, css::beans::XVetoableChangeListener, vetoableChange, css::beans::PropertyChangeEvent) + +// css::beans::XPropertiesChangeListener +IMPLEMENT_LISTENER_MULTIPLEXER_CORE(SbaXPropertiesChangeMultiplexer, css::beans::XPropertiesChangeListener); +void SbaXPropertiesChangeMultiplexer::propertiesChange(const css::uno::Sequence< css::beans::PropertyChangeEvent>& aEvts) +{ + // the SbaXPropertiesChangeMultiplexer doesn't care about the property names a listener logs on for, it simply + // forwards _all_ changes to _all_ listeners + + css::uno::Sequence< css::beans::PropertyChangeEvent> aMulti(aEvts); + for (css::beans::PropertyChangeEvent & rEvent : aMulti) + rEvent.Source = &m_rParent; + + ::comphelper::OInterfaceIteratorHelper2 aIt(*this); + while (aIt.hasMoreElements()) + static_cast< css::beans::XPropertiesChangeListener*>(aIt.next())->propertiesChange(aMulti); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/browser/unodatbr.cxx b/dbaccess/source/ui/browser/unodatbr.cxx new file mode 100644 index 000000000..cd0aed665 --- /dev/null +++ b/dbaccess/source/ui/browser/unodatbr.cxx @@ -0,0 +1,3723 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <browserids.hxx> +#include <core_resource.hxx> +#include <helpids.h> +#include <dbtreelistbox.hxx> +#include "dbtreemodel.hxx" +#include "dbtreeview.hxx" +#include <dbu_reghelper.hxx> +#include <strings.hrc> +#include <uiservices.hxx> +#include <imageprovider.hxx> +#include <sbagrid.hxx> +#include <strings.hxx> +#include <UITools.hxx> +#include <unodatbr.hxx> + +#include <com/sun/star/awt/MouseWheelBehavior.hpp> +#include <com/sun/star/awt/TextAlign.hpp> +#include <com/sun/star/awt/VisualEffect.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> +#include <com/sun/star/form/XLoadable.hpp> +#include <com/sun/star/form/XReset.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/i18n/Collator.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdb/XDatabaseContext.hpp> +#include <com/sun/star/sdb/XDatabaseRegistrations.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> +#include <com/sun/star/sdb/XParametersSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp> +#include <com/sun/star/sdb/XResultSetAccess.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdb/application/NamedDatabaseObject.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/FetchDirection.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbc/XWarningsSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/ui/XContextMenuInterceptor.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/extract.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <svl/filenotation.hxx> +#include <vcl/svlbitm.hxx> +#include <vcl/treelistbox.hxx> +#include <vcl/treelistentry.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <svx/databaseregistrationui.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <tools/multisel.hxx> +#include <tools/urlobj.hxx> +#include <unotools/confignode.hxx> +#include <vcl/split.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/settings.hxx> +#include <vcl/weld.hxx> + +#include <memory> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdb::application; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::ui; +using namespace ::dbtools; +using namespace ::comphelper; +using namespace ::svx; + +// SbaTableQueryBrowser +extern "C" void createRegistryInfo_OBrowser() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::SbaTableQueryBrowser > aAutoRegistration; +} + +namespace dbaui +{ + +namespace DatabaseObject = css::sdb::application::DatabaseObject; +namespace DatabaseObjectContainer = css::sdb::application::DatabaseObjectContainer; + +static void SafeAddPropertyListener(const Reference< XPropertySet > & xSet, const OUString& rPropName, XPropertyChangeListener* pListener) +{ + Reference< XPropertySetInfo > xInfo = xSet->getPropertySetInfo(); + if (xInfo->hasPropertyByName(rPropName)) + xSet->addPropertyChangeListener(rPropName, pListener); +} + +static void SafeRemovePropertyListener(const Reference< XPropertySet > & xSet, const OUString& rPropName, XPropertyChangeListener* pListener) +{ + Reference< XPropertySetInfo > xInfo = xSet->getPropertySetInfo(); + if (xInfo->hasPropertyByName(rPropName)) + xSet->removePropertyChangeListener(rPropName, pListener); +} + +OUString SAL_CALL SbaTableQueryBrowser::getImplementationName() +{ + return getImplementationName_Static(); +} + +css::uno::Sequence<OUString> SAL_CALL SbaTableQueryBrowser::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +OUString SbaTableQueryBrowser::getImplementationName_Static() +{ + return "org.openoffice.comp.dbu.ODatasourceBrowser"; +} + +css::uno::Sequence<OUString> SbaTableQueryBrowser::getSupportedServiceNames_Static() +{ + return { "com.sun.star.sdb.DataSourceBrowser" }; +} + +Reference< XInterface > SbaTableQueryBrowser::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + SolarMutexGuard aGuard; + return *(new SbaTableQueryBrowser(comphelper::getComponentContext(_rxFactory))); +} + +SbaTableQueryBrowser::SbaTableQueryBrowser(const Reference< XComponentContext >& _rM) + :SbaXDataBrowserController(_rM) + ,m_aSelectionListeners( getMutex() ) + ,m_aContextMenuInterceptors( getMutex() ) + ,m_aTableCopyHelper(this) + ,m_pTreeView(nullptr) + ,m_pSplitter(nullptr) + ,m_pCurrentlyDisplayed(nullptr) + ,m_nAsyncDrop(nullptr) + ,m_bQueryEscapeProcessing( false ) + ,m_bShowMenu(false) + ,m_bInSuspend(false) + ,m_bEnableBrowser(true) +{ +} + +SbaTableQueryBrowser::~SbaTableQueryBrowser() +{ + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + SAL_WARN("dbaccess.ui", "Please check who doesn't dispose this component!"); + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + SolarMutexGuard g; + m_pTreeView.reset(); + m_pSplitter.reset(); +} + +Any SAL_CALL SbaTableQueryBrowser::queryInterface(const Type& _rType) +{ + if ( _rType.equals( cppu::UnoType<XScriptInvocationContext>::get() ) ) + { + OSL_PRECOND( !!m_aDocScriptSupport, "SbaTableQueryBrowser::queryInterface: did not initialize this, yet!" ); + if ( !!m_aDocScriptSupport && *m_aDocScriptSupport ) + return makeAny( Reference< XScriptInvocationContext >( this ) ); + return Any(); + } + + Any aReturn = SbaXDataBrowserController::queryInterface(_rType); + if (!aReturn.hasValue()) + aReturn = SbaTableQueryBrowser_Base::queryInterface(_rType); + return aReturn; +} + +Sequence< Type > SAL_CALL SbaTableQueryBrowser::getTypes( ) +{ + Sequence< Type > aTypes( ::comphelper::concatSequences( + SbaXDataBrowserController::getTypes(), + SbaTableQueryBrowser_Base::getTypes() + ) ); + + OSL_PRECOND( !!m_aDocScriptSupport, "SbaTableQueryBrowser::getTypes: did not initialize this, yet!" ); + if ( !m_aDocScriptSupport || !*m_aDocScriptSupport ) + { + auto newEnd = std::remove_if( aTypes.begin(), aTypes.end(), + [](const Type& type) + { return type == cppu::UnoType<XScriptInvocationContext>::get(); } ); + aTypes.realloc( std::distance(aTypes.begin(), newEnd) ); + } + return aTypes; +} + +Sequence< sal_Int8 > SAL_CALL SbaTableQueryBrowser::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +void SAL_CALL SbaTableQueryBrowser::disposing() +{ + SolarMutexGuard aGuard; + // doin' a lot of VCL stuff here -> lock the SolarMutex + + // kiss our listeners goodbye + css::lang::EventObject aEvt(*this); + m_aSelectionListeners.disposeAndClear(aEvt); + m_aContextMenuInterceptors.disposeAndClear(aEvt); + + if (getBrowserView()) + { + // Need to do some cleaup of the data pointed to the tree view entries before we remove the treeview + clearTreeModel(); + m_pTreeView = nullptr; + getBrowserView()->setTreeView(nullptr); + } + + // remove ourself as status listener + implRemoveStatusListeners(); + + // remove the container listener from the database context + try + { + Reference< XDatabaseRegistrations > xDatabaseRegistrations( m_xDatabaseContext, UNO_QUERY_THROW ); + xDatabaseRegistrations->removeDatabaseRegistrationsListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // check out from all the objects we are listening + // the frame + if (m_xCurrentFrameParent.is()) + m_xCurrentFrameParent->removeFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this)); + SbaXDataBrowserController::disposing(); +} + +bool SbaTableQueryBrowser::Construct(vcl::Window* pParent) +{ + if ( !SbaXDataBrowserController::Construct( pParent ) ) + return false; + + try + { + Reference< XDatabaseRegistrations > xDatabaseRegistrations( m_xDatabaseContext, UNO_QUERY_THROW ); + xDatabaseRegistrations->addDatabaseRegistrationsListener( this ); + + // the collator for the string compares + m_xCollator = Collator::create( getORB() ); + m_xCollator->loadDefaultCollator( Application::GetSettings().GetLanguageTag().getLocale(), 0 ); + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::Construct: could not create (or start listening at) the database context!"); + } + // some help ids + if (getBrowserView() && getBrowserView()->getVclControl()) + { + + // create controls and set sizes + const long nFrameWidth = getBrowserView()->LogicToPixel(::Size(3, 0), MapMode(MapUnit::MapAppFont)).Width(); + + m_pSplitter = VclPtr<Splitter>::Create(getBrowserView(),WB_HSCROLL); + m_pSplitter->SetPosSizePixel( ::Point(0,0), ::Size(nFrameWidth,0) ); + m_pSplitter->SetBackground( Wallpaper( Application::GetSettings().GetStyleSettings().GetDialogColor() ) ); + + m_pTreeView = VclPtr<DBTreeView>::Create(getBrowserView(), WB_TABSTOP | WB_BORDER); + m_pTreeView->SetPreExpandHandler(LINK(this, SbaTableQueryBrowser, OnExpandEntry)); + + m_pTreeView->setCopyHandler(LINK(this, SbaTableQueryBrowser, OnCopyEntry)); + + m_pTreeView->getListBox().setContextMenuProvider( this ); + m_pTreeView->getListBox().setControlActionListener( this ); + m_pTreeView->SetHelpId(HID_CTL_TREEVIEW); + + // a default pos for the splitter, so that the listbox is about 80 (logical) pixels wide + m_pSplitter->SetSplitPosPixel(getBrowserView()->LogicToPixel(::Size(80, 0), MapMode(MapUnit::MapAppFont)).Width()); + + getBrowserView()->setSplitter(m_pSplitter); + getBrowserView()->setTreeView(m_pTreeView); + + // fill view with data + auto pTreeModel = m_pTreeView->GetTreeModel(); + pTreeModel->SetSortMode(SortAscending); + pTreeModel->SetCompareHdl(LINK(this, SbaTableQueryBrowser, OnTreeEntryCompare)); + m_pTreeView->setSelChangeHdl( LINK( this, SbaTableQueryBrowser, OnSelectionChange ) ); + + // TODO + getBrowserView()->getVclControl()->SetHelpId(HID_CTL_TABBROWSER); + if (getBrowserView()->getVclControl()->GetHeaderBar()) + getBrowserView()->getVclControl()->GetHeaderBar()->SetHelpId(HID_DATABROWSE_HEADER); + InvalidateFeature(ID_BROWSER_EXPLORER); + } + + return true; +} + +namespace +{ + struct SelectValueByName + { + const Any& operator()( OUString const& i_name ) const + { + return m_rCollection.get( i_name ); + } + + explicit SelectValueByName( ::comphelper::NamedValueCollection const& i_collection ) + :m_rCollection( i_collection ) + { + } + + ::comphelper::NamedValueCollection const& m_rCollection; + }; +} + +void SbaTableQueryBrowser::impl_sanitizeRowSetClauses_nothrow() +{ + try + { + Reference< XPropertySet > xRowSetProps( getRowSet(), UNO_QUERY_THROW ); + bool bEscapeProcessing = false; + OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing ); + if ( !bEscapeProcessing ) + // don't touch or interpret anything if escape processing is disabled + return; + + Reference< XSingleSelectQueryComposer > xComposer( createParser_nothrow() ); + if ( !xComposer.is() ) + // can't do anything. Already reported via assertion in createParser_nothrow. + return; + + // the tables participating in the statement + const Reference< XTablesSupplier > xSuppTables( xComposer, UNO_QUERY_THROW ); + const Reference< XNameAccess > xTableNames( xSuppTables->getTables(), UNO_SET_THROW ); + + // the columns participating in the statement + const Reference< XColumnsSupplier > xSuppColumns( xComposer, UNO_QUERY_THROW ); + const Reference< XNameAccess > xColumnNames( xSuppColumns->getColumns(), UNO_SET_THROW ); + + // check if the order columns apply to tables which really exist in the statement + const Reference< XIndexAccess > xOrderColumns( xComposer->getOrderColumns(), UNO_SET_THROW ); + const sal_Int32 nOrderColumns( xOrderColumns->getCount() ); + bool invalidColumn = nOrderColumns == 0; + for ( sal_Int32 c=0; ( c < nOrderColumns ) && !invalidColumn; ++c ) + { + const Reference< XPropertySet > xOrderColumn( xOrderColumns->getByIndex(c), UNO_QUERY_THROW ); + OUString sTableName; + OSL_VERIFY( xOrderColumn->getPropertyValue( PROPERTY_TABLENAME ) >>= sTableName ); + OUString sColumnName; + OSL_VERIFY( xOrderColumn->getPropertyValue( PROPERTY_NAME ) >>= sColumnName ); + + if ( sTableName.isEmpty() ) + { + if ( !xColumnNames->hasByName( sColumnName ) ) + { + invalidColumn = true; + break; + } + } + else + { + if ( !xTableNames->hasByName( sTableName ) ) + { + invalidColumn = true; + break; + } + + const Reference< XColumnsSupplier > xSuppTableColumns( xTableNames->getByName( sTableName ), UNO_QUERY_THROW ); + const Reference< XNameAccess > xTableColumnNames( xSuppTableColumns->getColumns(), UNO_SET_THROW ); + if ( !xTableColumnNames->hasByName( sColumnName ) ) + { + invalidColumn = true; + break; + } + } + } + + if ( invalidColumn ) + { + // reset the complete order statement at both the row set and the parser + xRowSetProps->setPropertyValue( PROPERTY_ORDER, makeAny( OUString() ) ); + xComposer->setOrder( "" ); + } + + // check if the columns participating in the filter refer to existing tables + // TODO: there's no API at all for this. The method which comes nearest to what we need is + // "getStructuredFilter", but it returns pure column names only. That is, for a statement like + // "SELECT * FROM <table> WHERE <other_table>.<column> = <value>", it will return "<column>". But + // there's no API at all to retrieve the information about "<other_table>" - which is what would + // be needed here. + // That'd be a chance to replace getStructuredFilter with something more reasonable. + // So, what really would be handy, is some + // XNormalizedFilter getNormalizedFilter(); + // with + // interface XDisjunctiveFilterExpression + // { + // XConjunctiveFilterTerm getTerm( int index ); + // } + // interface XConjunctiveFilterTerm + // { + // ComparisonPredicate getPredicate( int index ); + // } + // struct ComparisonPredicate + // { + // XComparisonOperand Lhs; + // SQLFilterOperator Operator; + // XComparisonOperand Rhs; + // } + // interface XComparisonOperand + // { + // SQLFilterOperand Type; + // XPropertySet getColumn(); + // string getLiteral(); + // ... + // } + // enum SQLFilterOperand { Column, Literal, ... } + // ... or something like this... + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +bool SbaTableQueryBrowser::InitializeForm( const Reference< XPropertySet > & i_formProperties ) +{ + if(!m_pCurrentlyDisplayed) + return true; + + // this method set all format settings from the original table or query + try + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(m_pCurrentlyDisplayed->GetUserData()); + ENSURE_OR_RETURN_FALSE( pData, "SbaTableQueryBrowser::InitializeForm: No user data set at the currently displayed entry!" ); + ENSURE_OR_RETURN_FALSE( pData->xObjectProperties.is(), "SbaTableQueryBrowser::InitializeForm: No table available!" ); + + Reference< XPropertySetInfo > xPSI( pData->xObjectProperties->getPropertySetInfo(), UNO_SET_THROW ); + + ::comphelper::NamedValueCollection aPropertyValues; + + const OUString aTransferProperties[] = + { + OUString(PROPERTY_APPLYFILTER), + OUString(PROPERTY_FILTER), + OUString(PROPERTY_HAVING_CLAUSE), + OUString(PROPERTY_ORDER) + }; + for (const auto & aTransferPropertie : aTransferProperties) + { + if ( !xPSI->hasPropertyByName( aTransferPropertie ) ) + continue; + aPropertyValues.put( aTransferPropertie, pData->xObjectProperties->getPropertyValue( aTransferPropertie ) ); + } + + std::vector< OUString > aNames( aPropertyValues.getNames() ); + std::sort(aNames.begin(), aNames.end()); + Sequence< OUString > aPropNames( comphelper::containerToSequence(aNames) ); + + Sequence< Any > aPropValues( aNames.size() ); + std::transform( aNames.begin(), aNames.end(), aPropValues.getArray(), SelectValueByName( aPropertyValues ) ); + + Reference< XMultiPropertySet > xFormMultiSet( i_formProperties, UNO_QUERY_THROW ); + xFormMultiSet->setPropertyValues( aPropNames, aPropValues ); + + impl_sanitizeRowSetClauses_nothrow(); + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + return false; + } + + return true; +} + +void SbaTableQueryBrowser::initializePreviewMode() +{ + if ( getBrowserView() && getBrowserView()->getVclControl() ) + { + getBrowserView()->getVclControl()->AlwaysEnableInput( false ); + getBrowserView()->getVclControl()->EnableInput( false ); + getBrowserView()->getVclControl()->ForceHideScrollbars(); + } + Reference< XPropertySet > xDataSourceSet(getRowSet(), UNO_QUERY); + if ( xDataSourceSet.is() ) + { + xDataSourceSet->setPropertyValue("AllowInserts",makeAny(false)); + xDataSourceSet->setPropertyValue("AllowUpdates",makeAny(false)); + xDataSourceSet->setPropertyValue("AllowDeletes",makeAny(false)); + } +} + +void SbaTableQueryBrowser::InitializeGridModel(const Reference< css::form::XFormComponent > & xGrid) +{ + try + { + Reference< css::form::XGridColumnFactory > xColFactory(xGrid, UNO_QUERY); + Reference< XNameContainer > xColContainer(xGrid, UNO_QUERY); + clearGridColumns( xColContainer ); + + Reference< XLoadable > xFormAsLoadable; + if (xGrid.is()) + xFormAsLoadable.set(xGrid->getParent(), css::uno::UNO_QUERY); + if (xFormAsLoadable.is() && xFormAsLoadable->isLoaded()) + { + // set the formats from the table + if(m_pCurrentlyDisplayed) + { + Sequence< OUString> aProperties(6 + ( m_bPreview ? 5 : 0 )); + Sequence< Any> aValues(7 + ( m_bPreview ? 5 : 0 )); + + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(m_pCurrentlyDisplayed->GetUserData()); + OSL_ENSURE( pData->xObjectProperties.is(), "SbaTableQueryBrowser::InitializeGridModel: No table available!" ); + if ( !pData->xObjectProperties.is() ) + return; + + OUString* pStringIter = aProperties.getArray(); + Any* pValueIter = aValues.getArray(); + if ( m_bPreview ) + { + *pStringIter++ = "AlwaysShowCursor"; + *pValueIter++ <<= false; + *pStringIter++ = PROPERTY_BORDER; + *pValueIter++ <<= sal_Int16(0); + } + + *pStringIter++ = PROPERTY_FONT; + *pValueIter++ = pData->xObjectProperties->getPropertyValue(PROPERTY_FONT); + *pStringIter++ = PROPERTY_TEXTEMPHASIS; + *pValueIter++ = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTEMPHASIS); + *pStringIter++ = PROPERTY_TEXTRELIEF; + *pValueIter++ = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTRELIEF); + if ( m_bPreview ) + { + *pStringIter++ = "HasNavigationBar"; + *pValueIter++ <<= false; + *pStringIter++ = "HasRecordMarker"; + *pValueIter++ <<= false; + } + *pStringIter++ = PROPERTY_ROW_HEIGHT; + *pValueIter++ = pData->xObjectProperties->getPropertyValue(PROPERTY_ROW_HEIGHT); + if ( m_bPreview ) + { + *pStringIter++ = "Tabstop"; + *pValueIter++ <<= false; + } + *pStringIter++ = PROPERTY_TEXTCOLOR; + *pValueIter++ = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTCOLOR); + *pStringIter++ = PROPERTY_TEXTLINECOLOR; + *pValueIter++ = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTLINECOLOR); + + Reference< XMultiPropertySet > xFormMultiSet(xGrid, UNO_QUERY); + xFormMultiSet->setPropertyValues(aProperties, aValues); + } + + // get the formats supplier of the database we're working with + Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormatter()->getNumberFormatsSupplier(); + + Reference<XConnection> xConnection; + Reference<XPropertySet> xRowSetProps(getRowSet(),UNO_QUERY); + xRowSetProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection; + OSL_ENSURE(xConnection.is(),"A ActiveConnection should normally exists!"); + + Reference<XChild> xChild(xConnection,UNO_QUERY); + Reference<XPropertySet> xDataSourceProp(xChild->getParent(),UNO_QUERY); + bool bSuppressVersionCol = false; + OSL_VERIFY( xDataSourceProp->getPropertyValue( PROPERTY_SUPPRESSVERSIONCL ) >>= bSuppressVersionCol ); + + // insert the column into the gridcontrol so that we see something :-) + OUString aCurrentModelType; + Reference<XColumnsSupplier> xSupCols(getRowSet(),UNO_QUERY); + Reference<XNameAccess> xColumns = xSupCols->getColumns(); + + OUString sDefaultProperty; + Reference< XPropertySet > xColumn; + Reference< XPropertySetInfo > xColPSI; + const Sequence<OUString> aColNames = xColumns->getElementNames(); + for (const OUString& rName : aColNames) + { + xColumn.set( xColumns->getByName( rName ), UNO_QUERY_THROW ); + xColPSI.set( xColumn->getPropertySetInfo(), UNO_SET_THROW ); + + // ignore the column when it is a rowversion one + if ( bSuppressVersionCol + && xColPSI->hasPropertyByName( PROPERTY_ISROWVERSION ) + && ::cppu::any2bool( xColumn->getPropertyValue( PROPERTY_ISROWVERSION ) ) + ) + continue; + + // use the result set column's type to determine the type of grid column to create + bool bFormattedIsNumeric = true; + sal_Int32 nType = ::comphelper::getINT32( xColumn->getPropertyValue( PROPERTY_TYPE ) ); + + std::vector< NamedValue > aInitialValues; + std::vector< OUString > aCopyProperties; + Any aDefault; + + switch(nType) + { + case DataType::BIT: + case DataType::BOOLEAN: + { + aCurrentModelType = "CheckBox"; + aInitialValues.emplace_back( "VisualEffect", makeAny( VisualEffect::FLAT ) ); + sDefaultProperty = PROPERTY_DEFAULTSTATE; + + sal_Int32 nNullable = ColumnValue::NULLABLE_UNKNOWN; + OSL_VERIFY( xColumn->getPropertyValue( PROPERTY_ISNULLABLE ) >>= nNullable ); + aInitialValues.emplace_back( + "TriState", + makeAny( ColumnValue::NO_NULLS != nNullable ) + ); + if ( ColumnValue::NO_NULLS == nNullable ) + aDefault <<= sal_Int16(TRISTATE_FALSE); + } + break; + + case DataType::LONGVARCHAR: + case DataType::CLOB: + aInitialValues.emplace_back( "MultiLine", makeAny( true ) ); + [[fallthrough]]; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + aCurrentModelType = "TextField"; + sDefaultProperty = PROPERTY_DEFAULTTEXT; + break; + + case DataType::VARCHAR: + case DataType::CHAR: + bFormattedIsNumeric = false; + [[fallthrough]]; + default: + aCurrentModelType = "FormattedField"; + sDefaultProperty = PROPERTY_EFFECTIVEDEFAULT; + + if ( xSupplier.is() ) + aInitialValues.emplace_back( "FormatsSupplier", makeAny( xSupplier ) ); + aInitialValues.emplace_back( "TreatAsNumber", makeAny( bFormattedIsNumeric ) ); + aCopyProperties.emplace_back(PROPERTY_FORMATKEY ); + break; + } + + aInitialValues.emplace_back( PROPERTY_CONTROLSOURCE, makeAny( rName ) ); + OUString sLabel; + xColumn->getPropertyValue(PROPERTY_LABEL) >>= sLabel; + if ( !sLabel.isEmpty() ) + aInitialValues.emplace_back( PROPERTY_LABEL, makeAny( sLabel ) ); + else + aInitialValues.emplace_back( PROPERTY_LABEL, makeAny( rName ) ); + + Reference< XPropertySet > xGridCol( xColFactory->createColumn( aCurrentModelType ), UNO_SET_THROW ); + Reference< XPropertySetInfo > xGridColPSI( xGridCol->getPropertySetInfo(), UNO_SET_THROW ); + + // calculate the default + if ( xGridColPSI->hasPropertyByName( PROPERTY_CONTROLDEFAULT ) ) + { + aDefault = xColumn->getPropertyValue( PROPERTY_CONTROLDEFAULT ); + // default value + if ( nType == DataType::BIT || nType == DataType::BOOLEAN ) + { + if ( aDefault.hasValue() ) + aDefault <<= (comphelper::getString(aDefault).toInt32() == 0) ? sal_Int16(TRISTATE_FALSE) : sal_Int16(TRISTATE_TRUE); + else + aDefault <<= sal_Int16(TRISTATE_INDET); + } + } + + if ( aDefault.hasValue() ) + aInitialValues.emplace_back( sDefaultProperty, aDefault ); + + // transfer properties from the definition to the UNO-model : + aCopyProperties.emplace_back(PROPERTY_HIDDEN ); + aCopyProperties.emplace_back(PROPERTY_WIDTH ); + + // help text to display for the column + Any aDescription; + if ( xColPSI->hasPropertyByName( PROPERTY_HELPTEXT ) ) + aDescription = xColumn->getPropertyValue( PROPERTY_HELPTEXT ); + OUString sTemp; + aDescription >>= sTemp; + if ( sTemp.isEmpty() ) + xColumn->getPropertyValue( PROPERTY_DESCRIPTION ) >>= sTemp; + + aDescription <<= sTemp; + aInitialValues.emplace_back( PROPERTY_HELPTEXT, aDescription ); + + // ... horizontal justify + Any aAlign; aAlign <<= sal_Int16( 0 ); + Any aColAlign( xColumn->getPropertyValue( PROPERTY_ALIGN ) ); + if ( aColAlign.hasValue() ) + aAlign <<= sal_Int16( ::comphelper::getINT32( aColAlign ) ); + aInitialValues.emplace_back( PROPERTY_ALIGN, aAlign ); + + // don't allow the mouse to scroll in the cells + if ( xGridColPSI->hasPropertyByName( PROPERTY_MOUSE_WHEEL_BEHAVIOR ) ) + aInitialValues.emplace_back( PROPERTY_MOUSE_WHEEL_BEHAVIOR, makeAny( MouseWheelBehavior::SCROLL_DISABLED ) ); + + // now set all those values + for (auto const& property : aInitialValues) + { + xGridCol->setPropertyValue( property.Name, property.Value ); + } + for (auto const& copyPropertyName : aCopyProperties) + xGridCol->setPropertyValue( copyPropertyName, xColumn->getPropertyValue(copyPropertyName) ); + + xColContainer->insertByName(rName, makeAny(xGridCol)); + } + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +static Reference<XPropertySet> getColumnHelper(SvTreeListEntry const * _pCurrentlyDisplayed, const Reference<XPropertySet>& _rxSource) +{ + Reference<XPropertySet> xRet; + if(_pCurrentlyDisplayed) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(_pCurrentlyDisplayed->GetUserData()); + Reference<XColumnsSupplier> xColumnsSup(pData->xObjectProperties,UNO_QUERY); + Reference<XNameAccess> xNames = xColumnsSup->getColumns(); + OUString aName; + _rxSource->getPropertyValue(PROPERTY_NAME) >>= aName; + if(xNames.is() && xNames->hasByName(aName)) + xRet.set(xNames->getByName(aName),UNO_QUERY); + } + return xRet; +} + +void SbaTableQueryBrowser::transferChangedControlProperty(const OUString& _rProperty, const Any& _rNewValue) +{ + if(m_pCurrentlyDisplayed) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(m_pCurrentlyDisplayed->GetUserData()); + Reference< XPropertySet > xObjectProps = pData->xObjectProperties; + OSL_ENSURE(xObjectProps.is(),"SbaTableQueryBrowser::transferChangedControlProperty: no table/query object!"); + if (xObjectProps.is()) + xObjectProps->setPropertyValue(_rProperty, _rNewValue); + } +} + +void SbaTableQueryBrowser::propertyChange(const PropertyChangeEvent& evt) +{ + SbaXDataBrowserController::propertyChange(evt); + + try + { + Reference< XPropertySet > xSource(evt.Source, UNO_QUERY); + if (!xSource.is()) + return; + + // one of the many properties which require us to update the definition ? + // a column's width ? + else if (evt.PropertyName == PROPERTY_WIDTH) + { // a column width has changed -> update the model + // (the update of the view is done elsewhere) + Reference<XPropertySet> xProp = getColumnHelper(m_pCurrentlyDisplayed,xSource); + if(xProp.is()) + { + if(!evt.NewValue.hasValue()) + xProp->setPropertyValue(PROPERTY_WIDTH,makeAny(sal_Int32(227))); + else + xProp->setPropertyValue(PROPERTY_WIDTH,evt.NewValue); + } + } + + // a column's 'visible' state ? + else if (evt.PropertyName == PROPERTY_HIDDEN) + { + Reference<XPropertySet> xProp = getColumnHelper(m_pCurrentlyDisplayed,xSource); + if(xProp.is()) + xProp->setPropertyValue(PROPERTY_HIDDEN,evt.NewValue); + } + + // a columns alignment ? + else if (evt.PropertyName == PROPERTY_ALIGN) + { + Reference<XPropertySet> xProp = getColumnHelper(m_pCurrentlyDisplayed,xSource); + try + { + if(xProp.is()) + { + if(evt.NewValue.hasValue()) + { + sal_Int16 nAlign = 0; + if(evt.NewValue >>= nAlign) + xProp->setPropertyValue(PROPERTY_ALIGN,makeAny(sal_Int32(nAlign))); + else + xProp->setPropertyValue(PROPERTY_ALIGN,evt.NewValue); + } + else + xProp->setPropertyValue(PROPERTY_ALIGN,makeAny(css::awt::TextAlign::LEFT)); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + // a column's format ? + else if ( evt.PropertyName == PROPERTY_FORMATKEY + && (TypeClass_LONG == evt.NewValue.getValueTypeClass()) + ) + { + // update the model (means the definition object) + Reference<XPropertySet> xProp = getColumnHelper(m_pCurrentlyDisplayed,xSource); + if(xProp.is()) + xProp->setPropertyValue(PROPERTY_FORMATKEY,evt.NewValue); + } + + // some table definition properties ? + // the height of the rows in the grid ? + else if (evt.PropertyName == PROPERTY_ROW_HEIGHT) + { + if(m_pCurrentlyDisplayed) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(m_pCurrentlyDisplayed->GetUserData()); + OSL_ENSURE( pData->xObjectProperties.is(), "No table available!" ); + + bool bDefault = !evt.NewValue.hasValue(); + if (bDefault) + pData->xObjectProperties->setPropertyValue(PROPERTY_ROW_HEIGHT,makeAny(sal_Int32(45))); + else + pData->xObjectProperties->setPropertyValue(PROPERTY_ROW_HEIGHT,evt.NewValue); + } + } + + else if ( evt.PropertyName == PROPERTY_FONT // the font ? + || evt.PropertyName == PROPERTY_TEXTCOLOR // the text color ? + || evt.PropertyName == PROPERTY_FILTER // the filter ? + || evt.PropertyName == PROPERTY_HAVING_CLAUSE // the having clause ? + || evt.PropertyName == PROPERTY_ORDER // the sort ? + || evt.PropertyName == PROPERTY_APPLYFILTER // the appliance of the filter ? + || evt.PropertyName == PROPERTY_TEXTLINECOLOR // the text line color ? + || evt.PropertyName == PROPERTY_TEXTEMPHASIS // the text emphasis ? + || evt.PropertyName == PROPERTY_TEXTRELIEF // the text relief ? + ) + { + transferChangedControlProperty(evt.PropertyName, evt.NewValue); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +sal_Bool SbaTableQueryBrowser::suspend(sal_Bool bSuspend) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + if ( getView() && getView()->IsInModalMode() ) + return false; + bool bRet = false; + if ( !m_bInSuspend ) + { + m_bInSuspend = true; + if ( rBHelper.bDisposed ) + throw DisposedException( OUString(), *this ); + + bRet = SbaXDataBrowserController::suspend(bSuspend); + if ( bRet && getView() ) + getView()->Hide(); + + m_bInSuspend = false; + } + + return bRet; +} + +void SAL_CALL SbaTableQueryBrowser::statusChanged( const FeatureStateEvent& _rEvent ) +{ + // search the external dispatcher causing this call + Reference< XDispatch > xSource(_rEvent.Source, UNO_QUERY); + bool bFound = false; + for (auto & externalFeature : m_aExternalFeatures) + { + if ( _rEvent.FeatureURL.Complete == externalFeature.second.aURL.Complete) + { + bFound = true; + OSL_ENSURE( xSource.get() == externalFeature.second.xDispatcher.get(), "SbaTableQueryBrowser::statusChanged: inconsistent!" ); + // update the enabled state + externalFeature.second.bEnabled = _rEvent.IsEnabled; + + switch ( externalFeature.first ) + { + case ID_BROWSER_DOCUMENT_DATASOURCE: + { + // if it's the slot for the document data source, remember the state + Sequence< PropertyValue > aDescriptor; + bool bProperFormat = _rEvent.State >>= aDescriptor; + OSL_ENSURE(bProperFormat, "SbaTableQueryBrowser::statusChanged: need a data access descriptor here!"); + m_aDocumentDataSource.initializeFrom(aDescriptor); + + OSL_ENSURE( ( m_aDocumentDataSource.has(DataAccessDescriptorProperty::DataSource) + || m_aDocumentDataSource.has(DataAccessDescriptorProperty::DatabaseLocation) + ) + && m_aDocumentDataSource.has(DataAccessDescriptorProperty::Command) + && m_aDocumentDataSource.has(DataAccessDescriptorProperty::CommandType), + "SbaTableQueryBrowser::statusChanged: incomplete descriptor!"); + + // check if we know the object which is set as document data source + checkDocumentDataSource(); + } + break; + + default: + // update the toolbox + implCheckExternalSlot( externalFeature.first ); + break; + } + break; + } + } + + OSL_ENSURE(bFound, "SbaTableQueryBrowser::statusChanged: don't know who sent this!"); +} + +void SbaTableQueryBrowser::checkDocumentDataSource() +{ + SvTreeListEntry* pDataSourceEntry = nullptr; + SvTreeListEntry* pContainerEntry = nullptr; + SvTreeListEntry* pObjectEntry = getObjectEntry( m_aDocumentDataSource, &pDataSourceEntry, &pContainerEntry ); + bool bKnownDocDataSource = (nullptr != pObjectEntry); + if (!bKnownDocDataSource) + { + if (nullptr != pDataSourceEntry) + { // at least the data source is known + if (nullptr != pContainerEntry) + bKnownDocDataSource = true; // assume we know it. + // TODO: should we expand the object container? This may be too expensive just for checking... + else + { + if (m_aDocumentDataSource.has(DataAccessDescriptorProperty::CommandType) + && m_aDocumentDataSource.has(DataAccessDescriptorProperty::Command)) + { // maybe we have a command to be displayed ? + sal_Int32 nCommandType = CommandType::TABLE; + m_aDocumentDataSource[DataAccessDescriptorProperty::CommandType] >>= nCommandType; + + OUString sCommand; + m_aDocumentDataSource[DataAccessDescriptorProperty::Command] >>= sCommand; + + bKnownDocDataSource = (CommandType::COMMAND == nCommandType) && (!sCommand.isEmpty()); + } + } + } + } + + if ( !bKnownDocDataSource ) + m_aExternalFeatures[ ID_BROWSER_DOCUMENT_DATASOURCE ].bEnabled = false; + + // update the toolbox + implCheckExternalSlot(ID_BROWSER_DOCUMENT_DATASOURCE); +} + +void SbaTableQueryBrowser::extractDescriptorProps(const svx::ODataAccessDescriptor& _rDescriptor, OUString& _rDataSource, OUString& _rCommand, sal_Int32& _rCommandType, bool& _rEscapeProcessing) +{ + _rDataSource = _rDescriptor.getDataSource(); + if ( _rDescriptor.has(DataAccessDescriptorProperty::Command) ) + _rDescriptor[DataAccessDescriptorProperty::Command] >>= _rCommand; + if ( _rDescriptor.has(DataAccessDescriptorProperty::CommandType) ) + _rDescriptor[DataAccessDescriptorProperty::CommandType] >>= _rCommandType; + + // escape processing is the only one allowed not to be present + _rEscapeProcessing = true; + if (_rDescriptor.has(DataAccessDescriptorProperty::EscapeProcessing)) + _rEscapeProcessing = ::cppu::any2bool(_rDescriptor[DataAccessDescriptorProperty::EscapeProcessing]); +} + +namespace +{ + bool getDataSourceDisplayName_isURL( const OUString& _rDS, OUString& _rDisplayName, OUString& _rUniqueId ) + { + INetURLObject aURL( _rDS ); + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + { + _rDisplayName = aURL.getBase(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::WithCharset); + _rUniqueId = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + return true; + } + _rDisplayName = _rDS; + _rUniqueId.clear(); + return false; + } + + struct FilterByEntryDataId : public IEntryFilter + { + OUString sId; + explicit FilterByEntryDataId( const OUString& _rId ) : sId( _rId ) { } + + virtual ~FilterByEntryDataId() {} + + virtual bool includeEntry(const void* pEntry) const override; + }; + + bool FilterByEntryDataId::includeEntry(const void* pUserData) const + { + const DBTreeListUserData* pData = static_cast<const DBTreeListUserData*>(pUserData); + return ( !pData || ( pData->sAccessor == sId ) ); + } +} + +OUString SbaTableQueryBrowser::getDataSourceAccessor( SvTreeListEntry* _pDataSourceEntry ) const +{ + OSL_ENSURE( _pDataSourceEntry, "SbaTableQueryBrowser::getDataSourceAccessor: invalid entry!" ); + + DBTreeListUserData* pData = static_cast< DBTreeListUserData* >( _pDataSourceEntry->GetUserData() ); + OSL_ENSURE( pData, "SbaTableQueryBrowser::getDataSourceAccessor: invalid entry data!" ); + OSL_ENSURE( pData->eType == etDatasource, "SbaTableQueryBrowser::getDataSourceAccessor: entry does not denote a data source!" ); + return !pData->sAccessor.isEmpty() ? pData->sAccessor : GetEntryText( _pDataSourceEntry ); +} + +SvTreeListEntry* SbaTableQueryBrowser::getObjectEntry(const OUString& _rDataSource, const OUString& _rCommand, sal_Int32 _nCommandType, + SvTreeListEntry** _ppDataSourceEntry, SvTreeListEntry** _ppContainerEntry, bool _bExpandAncestors, + const SharedConnection& _rxConnection ) +{ + if (_ppDataSourceEntry) + *_ppDataSourceEntry = nullptr; + if (_ppContainerEntry) + *_ppContainerEntry = nullptr; + + SvTreeListEntry* pObject = nullptr; + if ( m_pTreeView ) + { + // look for the data source entry + OUString sDisplayName, sDataSourceId; + bool bIsDataSourceURL = getDataSourceDisplayName_isURL( _rDataSource, sDisplayName, sDataSourceId ); + // the display name may differ from the URL for readability reasons + // #i33699# + + FilterByEntryDataId aFilter( sDataSourceId ); + SvTreeListEntry* pDataSource = m_pTreeView->getListBox().GetEntryPosByName( sDisplayName, nullptr, &aFilter ); + if ( !pDataSource ) // check if the data source name is a file location + { + if ( bIsDataSourceURL ) + { + // special case, the data source is a URL + // add new entries to the list box model + implAddDatasource( _rDataSource, _rxConnection ); + pDataSource = m_pTreeView->getListBox().GetEntryPosByName( sDisplayName, nullptr, &aFilter ); + OSL_ENSURE( pDataSource, "SbaTableQueryBrowser::getObjectEntry: hmm - did not find it again!" ); + } + } + if (_ppDataSourceEntry) + // (caller wants to have it...) + *_ppDataSourceEntry = pDataSource; + + if (pDataSource) + { + // expand if required so + if (_bExpandAncestors) + m_pTreeView->getListBox().Expand(pDataSource); + + // look for the object container + SvTreeListEntry* pCommandType = nullptr; + switch (_nCommandType) + { + case CommandType::TABLE: + pCommandType = m_pTreeView->getListBox().GetModel()->GetEntry(pDataSource, CONTAINER_TABLES); + break; + + case CommandType::QUERY: + pCommandType = m_pTreeView->getListBox().GetModel()->GetEntry(pDataSource, CONTAINER_QUERIES); + break; + } + + if (_ppContainerEntry) + *_ppContainerEntry = pCommandType; + + if (pCommandType) + { + // expand if required so + if (_bExpandAncestors) + { + m_pTreeView->getListBox().Expand(pCommandType); + } + + // look for the object + sal_Int32 nIndex = 0; + do + { + OUString sPath; + switch (_nCommandType) + { + case CommandType::TABLE: + sPath = _rCommand; + nIndex = -1; + break; + + case CommandType::QUERY: + sPath = _rCommand.getToken( 0, '/', nIndex ); + break; + + default: + assert(false); + } + pObject = m_pTreeView->getListBox().GetEntryPosByName(sPath, pCommandType); + pCommandType = pObject; + if ( nIndex >= 0 ) + { + if (ensureEntryObject(pObject)) + { + DBTreeListUserData* pParentData = static_cast< DBTreeListUserData* >( pObject->GetUserData() ); + Reference< XNameAccess > xCollection( pParentData->xContainer, UNO_QUERY ); + sal_Int32 nIndex2 = nIndex; + sPath = _rCommand.getToken( 0, '/', nIndex2 ); + try + { + if ( xCollection->hasByName(sPath) ) + { + if(!m_pTreeView->getListBox().GetEntryPosByName(sPath,pObject)) + { + Reference<XNameAccess> xChild(xCollection->getByName(sPath),UNO_QUERY); + DBTreeListUserData* pEntryData = new DBTreeListUserData; + pEntryData->eType = etQuery; + if ( xChild.is() ) + { + pEntryData->eType = etQueryContainer; + } + implAppendEntry( pObject, sPath, pEntryData, pEntryData->eType ); + } + } + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::populateTree: could not fill the tree"); + } + } + } + } + while ( nIndex >= 0 ); + } + } + } + return pObject; +} + +SvTreeListEntry* SbaTableQueryBrowser::getObjectEntry(const svx::ODataAccessDescriptor& _rDescriptor, + SvTreeListEntry** _ppDataSourceEntry, SvTreeListEntry** _ppContainerEntry) +{ + // extract the props from the descriptor + OUString sDataSource; + OUString sCommand; + sal_Int32 nCommandType = CommandType::COMMAND; + bool bEscapeProcessing = true; + extractDescriptorProps(_rDescriptor, sDataSource, sCommand, nCommandType, bEscapeProcessing); + + return getObjectEntry( sDataSource, sCommand, nCommandType, _ppDataSourceEntry, _ppContainerEntry, false/*_bExpandAncestors*/ ); +} + +void SbaTableQueryBrowser::connectExternalDispatches() +{ + Reference< XDispatchProvider > xProvider( getFrame(), UNO_QUERY ); + OSL_ENSURE(xProvider.is(), "SbaTableQueryBrowser::connectExternalDispatches: no DispatchProvider !"); + if (!xProvider.is()) + return; + + if ( m_aExternalFeatures.empty() ) + { + const char* pURLs[] = { + ".uno:DataSourceBrowser/DocumentDataSource", + ".uno:DataSourceBrowser/FormLetter", + ".uno:DataSourceBrowser/InsertColumns", + ".uno:DataSourceBrowser/InsertContent", + }; + const sal_uInt16 nIds[] = { + ID_BROWSER_DOCUMENT_DATASOURCE, + ID_BROWSER_FORMLETTER, + ID_BROWSER_INSERTCOLUMNS, + ID_BROWSER_INSERTCONTENT + }; + + for ( size_t i=0; i < SAL_N_ELEMENTS( pURLs ); ++i ) + { + URL aURL; + aURL.Complete = OUString::createFromAscii( pURLs[i] ); + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aURL ); + m_aExternalFeatures[ nIds[ i ] ] = ExternalFeature( aURL ); + } + } + + for (auto & externalFeature : m_aExternalFeatures) + { + externalFeature.second.xDispatcher = xProvider->queryDispatch( + externalFeature.second.aURL, "_parent", FrameSearchFlag::PARENT + ); + + if ( externalFeature.second.xDispatcher.get() == static_cast< XDispatch* >( this ) ) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::connectExternalDispatches: this should not happen anymore!" ); + // (nowadays, the URLs aren't in our SupportedFeatures list anymore, so we should + // not supply a dispatcher for this) + externalFeature.second.xDispatcher.clear(); + } + + if ( externalFeature.second.xDispatcher.is() ) + { + try + { + externalFeature.second.xDispatcher->addStatusListener( this, externalFeature.second.aURL ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + implCheckExternalSlot( externalFeature.first ); + } +} + +void SbaTableQueryBrowser::implCheckExternalSlot( sal_uInt16 _nId ) +{ + if ( !m_xMainToolbar.is() ) + return; + + VclPtr<vcl::Window> pToolboxWindow = VCLUnoHelper::GetWindow( m_xMainToolbar ); + ToolBox* pToolbox = dynamic_cast< ToolBox* >( pToolboxWindow.get() ); + OSL_ENSURE( pToolbox, "SbaTableQueryBrowser::implCheckExternalSlot: cannot obtain the toolbox window!" ); + + // check if we have to hide this item from the toolbox + if ( pToolbox ) + { + bool bHaveDispatcher = m_aExternalFeatures[ _nId ].xDispatcher.is(); + if ( bHaveDispatcher != pToolbox->IsItemVisible( _nId ) ) + bHaveDispatcher ? pToolbox->ShowItem( _nId ) : pToolbox->HideItem( _nId ); + } + + // and invalidate this feature in general + InvalidateFeature( _nId ); +} + +void SAL_CALL SbaTableQueryBrowser::disposing( const css::lang::EventObject& _rSource ) +{ + // our frame ? + Reference< css::frame::XFrame > xSourceFrame(_rSource.Source, UNO_QUERY); + if (m_xCurrentFrameParent.is() && (xSourceFrame == m_xCurrentFrameParent)) + m_xCurrentFrameParent->removeFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this)); + else + { + // search the external dispatcher causing this call in our map + Reference< XDispatch > xSource(_rSource.Source, UNO_QUERY); + if(xSource.is()) + { + ExternalFeaturesMap::const_iterator aLoop = m_aExternalFeatures.begin(); + ExternalFeaturesMap::const_iterator aEnd = m_aExternalFeatures.end(); + while (aLoop != aEnd) + { + if ( aLoop->second.xDispatcher.get() == xSource.get() ) + { + sal_uInt16 nSlot = aLoop->first; + + // remove it + aLoop = m_aExternalFeatures.erase(aLoop); + + // maybe update the UI + implCheckExternalSlot(nSlot); + + // continue, the same XDispatch may be responsible for more than one URL + } + ++aLoop; + } + } + else + { + Reference<XConnection> xCon(_rSource.Source, UNO_QUERY); + if ( xCon.is() && m_pTreeView ) + { // our connection is in dispose so we have to find the entry equal with this connection + // and close it what means to collapse the entry + // get the top-level representing the removed data source + SvTreeListEntry* pDSLoop = m_pTreeView->getListBox().FirstChild(nullptr); + while (pDSLoop) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pDSLoop->GetUserData()); + if ( pData && pData->xConnection == xCon ) + { + // we set the connection to null to avoid a second disposing of the connection + pData->xConnection.clear(); + closeConnection(pDSLoop,false); + break; + } + + pDSLoop = pDSLoop->NextSibling(); + } + } + else + SbaXDataBrowserController::disposing(_rSource); + } + } +} + +void SbaTableQueryBrowser::implRemoveStatusListeners() +{ + // clear all old dispatches + for (auto const& externalFeature : m_aExternalFeatures) + { + if ( externalFeature.second.xDispatcher.is() ) + { + try + { + externalFeature.second.xDispatcher->removeStatusListener( this, externalFeature.second.aURL ); + } + catch (Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::implRemoveStatusListeners: could not remove a status listener!"); + } + } + } + m_aExternalFeatures.clear(); +} + +sal_Bool SAL_CALL SbaTableQueryBrowser::select( const Any& _rSelection ) +{ + SolarMutexGuard aGuard; + // doin' a lot of VCL stuff here -> lock the SolarMutex + + Sequence< PropertyValue > aDescriptorSequence; + if (!(_rSelection >>= aDescriptorSequence)) + throw IllegalArgumentException(OUString(), *this, 1); + // TODO: error message + + ODataAccessDescriptor aDescriptor; + try + { + aDescriptor = ODataAccessDescriptor(aDescriptorSequence); + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::select: could not extract the descriptor!"); + } + + // check the presence of the props we need + if ( !(aDescriptor.has(DataAccessDescriptorProperty::DataSource) || aDescriptor.has(DataAccessDescriptorProperty::DatabaseLocation)) || !aDescriptor.has(DataAccessDescriptorProperty::Command) || !aDescriptor.has(DataAccessDescriptorProperty::CommandType)) + throw IllegalArgumentException(OUString(), *this, 1); + // TODO: error message + + return implSelect(aDescriptor,true); +} + +Any SAL_CALL SbaTableQueryBrowser::getSelection( ) +{ + Any aReturn; + + try + { + Reference< XLoadable > xLoadable(getRowSet(), UNO_QUERY); + if (xLoadable.is() && xLoadable->isLoaded()) + { + Reference< XPropertySet > aFormProps(getRowSet(), UNO_QUERY); + ODataAccessDescriptor aDescriptor(aFormProps); + // remove properties which are not part of our "selection" + aDescriptor.erase(DataAccessDescriptorProperty::Connection); + aDescriptor.erase(DataAccessDescriptorProperty::Cursor); + + aReturn <<= aDescriptor.createPropertyValueSequence(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return aReturn; +} + +void SAL_CALL SbaTableQueryBrowser::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener ) +{ + m_aSelectionListeners.addInterface(_rxListener); +} + +void SAL_CALL SbaTableQueryBrowser::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener ) +{ + m_aSelectionListeners.removeInterface(_rxListener); +} + +void SbaTableQueryBrowser::attachFrame(const Reference< css::frame::XFrame > & _xFrame) +{ + implRemoveStatusListeners(); + + if (m_xCurrentFrameParent.is()) + m_xCurrentFrameParent->removeFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this)); + + SbaXDataBrowserController::attachFrame(_xFrame); + + Reference< XFrame > xCurrentFrame( getFrame() ); + if ( xCurrentFrame.is() ) + { + m_xCurrentFrameParent = xCurrentFrame->findFrame("_parent",FrameSearchFlag::PARENT); + if ( m_xCurrentFrameParent.is() ) + m_xCurrentFrameParent->addFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this)); + + // obtain our toolbox + try + { + Reference< XPropertySet > xFrameProps( m_aCurrentFrame.getFrame(), UNO_QUERY_THROW ); + Reference< XLayoutManager > xLayouter( + xFrameProps->getPropertyValue("LayoutManager"), + UNO_QUERY ); + + if ( xLayouter.is() ) + { + Reference< XUIElement > xUI( + xLayouter->getElement( "private:resource/toolbar/toolbar" ), + UNO_SET_THROW ); + m_xMainToolbar.set(xUI->getRealInterface(), css::uno::UNO_QUERY); + OSL_ENSURE( m_xMainToolbar.is(), "SbaTableQueryBrowser::attachFrame: where's my toolbox?" ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + // get the dispatchers for the external slots + connectExternalDispatches(); +} + +void SbaTableQueryBrowser::addModelListeners(const Reference< css::awt::XControlModel > & _xGridControlModel) +{ + SbaXDataBrowserController::addModelListeners(_xGridControlModel); + Reference< XPropertySet > xSourceSet(_xGridControlModel, UNO_QUERY); + if (xSourceSet.is()) + { + xSourceSet->addPropertyChangeListener(PROPERTY_ROW_HEIGHT, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->addPropertyChangeListener(PROPERTY_FONT, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->addPropertyChangeListener(PROPERTY_TEXTCOLOR, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->addPropertyChangeListener(PROPERTY_TEXTLINECOLOR, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->addPropertyChangeListener(PROPERTY_TEXTEMPHASIS, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->addPropertyChangeListener(PROPERTY_TEXTRELIEF, static_cast<XPropertyChangeListener*>(this)); + } + +} + +void SbaTableQueryBrowser::removeModelListeners(const Reference< css::awt::XControlModel > & _xGridControlModel) +{ + SbaXDataBrowserController::removeModelListeners(_xGridControlModel); + Reference< XPropertySet > xSourceSet(_xGridControlModel, UNO_QUERY); + if (xSourceSet.is()) + { + xSourceSet->removePropertyChangeListener(PROPERTY_ROW_HEIGHT, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_FONT, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_TEXTCOLOR, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_TEXTLINECOLOR, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_TEXTEMPHASIS, static_cast<XPropertyChangeListener*>(this)); + xSourceSet->removePropertyChangeListener(PROPERTY_TEXTRELIEF, static_cast<XPropertyChangeListener*>(this)); + } +} + +void SbaTableQueryBrowser::RowChanged() +{ + if(getBrowserView()) + { + SbaGridControl* pControl = getBrowserView()->getVclControl(); + if (!pControl->IsEditing()) + InvalidateFeature(ID_BROWSER_COPY); + } + SbaXDataBrowserController::RowChanged(); +} + +void SbaTableQueryBrowser::ColumnChanged() +{ + if(getBrowserView()) + { + SbaGridControl* pControl = getBrowserView()->getVclControl(); + if (!pControl->IsEditing()) + InvalidateFeature(ID_BROWSER_COPY); + } + SbaXDataBrowserController::ColumnChanged(); +} + +void SbaTableQueryBrowser::AddColumnListener(const Reference< XPropertySet > & xCol) +{ + SbaXDataBrowserController::AddColumnListener(xCol); + SafeAddPropertyListener(xCol, PROPERTY_WIDTH, static_cast<XPropertyChangeListener*>(this)); + SafeAddPropertyListener(xCol, PROPERTY_HIDDEN, static_cast<XPropertyChangeListener*>(this)); + SafeAddPropertyListener(xCol, PROPERTY_ALIGN, static_cast<XPropertyChangeListener*>(this)); + SafeAddPropertyListener(xCol, PROPERTY_FORMATKEY, static_cast<XPropertyChangeListener*>(this)); +} + +void SbaTableQueryBrowser::RemoveColumnListener(const Reference< XPropertySet > & xCol) +{ + SbaXDataBrowserController::RemoveColumnListener(xCol); + SafeRemovePropertyListener(xCol, PROPERTY_WIDTH, static_cast<XPropertyChangeListener*>(this)); + SafeRemovePropertyListener(xCol, PROPERTY_HIDDEN, static_cast<XPropertyChangeListener*>(this)); + SafeRemovePropertyListener(xCol, PROPERTY_ALIGN, static_cast<XPropertyChangeListener*>(this)); + SafeRemovePropertyListener(xCol, PROPERTY_FORMATKEY, static_cast<XPropertyChangeListener*>(this)); +} + +void SbaTableQueryBrowser::criticalFail() +{ + SbaXDataBrowserController::criticalFail(); + unloadAndCleanup( false ); +} + +void SbaTableQueryBrowser::LoadFinished(bool _bWasSynch) +{ + SbaXDataBrowserController::LoadFinished(_bWasSynch); + + m_sQueryCommand.clear(); + m_bQueryEscapeProcessing = false; + + if (isValid() && !loadingCancelled()) + { + // did we load a query? + bool bTemporary; // needed because we m_bQueryEscapeProcessing is only one bit wide (and we want to pass it by reference) + if ( implGetQuerySignature( m_sQueryCommand, bTemporary ) ) + m_bQueryEscapeProcessing = bTemporary; + } + + // if the form has been loaded, this means that our "selection" has changed + css::lang::EventObject aEvent( *this ); + m_aSelectionListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aEvent ); +} + +bool SbaTableQueryBrowser::getExternalSlotState( sal_uInt16 _nId ) const +{ + bool bEnabled = false; + ExternalFeaturesMap::const_iterator aPos = m_aExternalFeatures.find( _nId ); + if ( ( m_aExternalFeatures.end() != aPos ) && aPos->second.xDispatcher.is() ) + bEnabled = aPos->second.bEnabled; + return bEnabled; +} + +FeatureState SbaTableQueryBrowser::GetState(sal_uInt16 nId) const +{ + FeatureState aReturn; + // (disabled automatically) + + // no chance without a view + if (!getBrowserView() || !getBrowserView()->getVclControl()) + return aReturn; + + switch ( nId ) + { + case ID_TREE_ADMINISTRATE: + aReturn.bEnabled = true; + return aReturn; + + case ID_BROWSER_CLOSE: + // the close button should always be enabled + aReturn.bEnabled = !m_bEnableBrowser; + return aReturn; + + // "toggle explorer" is always enabled (if we have an explorer) + case ID_BROWSER_EXPLORER: + aReturn.bEnabled = m_bEnableBrowser; + aReturn.bChecked = haveExplorer(); + return aReturn; + + case ID_BROWSER_REMOVEFILTER: + return SbaXDataBrowserController::GetState( nId ); + + case ID_BROWSER_COPY: + if ( !m_pTreeView->HasChildPathFocus() ) + // handled below + break; + [[fallthrough]]; + case ID_TREE_CLOSE_CONN: + case ID_TREE_EDIT_DATABASE: + { + SvTreeListEntry* pCurrentEntry( m_pTreeView->getListBox().GetCurEntry() ); + EntryType eType = getEntryType( pCurrentEntry ); + if ( eType == etUnknown ) + return aReturn; + + SvTreeListEntry* pDataSourceEntry = m_pTreeView->getListBox().GetRootLevelParent( pCurrentEntry ); + DBTreeListUserData* pDSData + = pDataSourceEntry + ? static_cast< DBTreeListUserData* >( pDataSourceEntry->GetUserData() ) + : nullptr; + + if ( nId == ID_TREE_CLOSE_CONN ) + { + aReturn.bEnabled = ( pDSData != nullptr ) && pDSData->xConnection.is(); + } + else if ( nId == ID_TREE_EDIT_DATABASE ) + { + ::utl::OConfigurationTreeRoot aConfig( ::utl::OConfigurationTreeRoot::createWithComponentContext( getORB(), + "/org.openoffice.Office.DataAccess/Policies/Features/Common" ) ); + bool bHaveEditDatabase( true ); + OSL_VERIFY( aConfig.getNodeValue( "EditDatabaseFromDataSourceView" ) >>= bHaveEditDatabase ); + aReturn.bEnabled = getORB().is() && ( pDataSourceEntry != nullptr ) && bHaveEditDatabase; + } + else if ( nId == ID_BROWSER_COPY ) + { + aReturn.bEnabled = isEntryCopyAllowed( pCurrentEntry ); + } + + return aReturn; + } + } + + // all slots not handled above are not available if no form is loaded + if (!isLoaded()) + return aReturn; + + try + { + bool bHandled = false; + switch (nId) + { + case ID_BROWSER_DOCUMENT_DATASOURCE: + // the slot is enabled if we have an external dispatcher able to handle it, + // and the dispatcher must have enabled the slot in general + aReturn.bEnabled = getExternalSlotState( ID_BROWSER_DOCUMENT_DATASOURCE ); + bHandled = true; + break; + case ID_BROWSER_REFRESH: + aReturn.bEnabled = true; + bHandled = true; + break; + } + + if (bHandled) + return aReturn; + + // no chance without valid models + if (isValid() && !isValidCursor() && nId != ID_BROWSER_CLOSE) + return aReturn; + + switch (nId) + { + case ID_BROWSER_INSERTCOLUMNS: + case ID_BROWSER_INSERTCONTENT: + case ID_BROWSER_FORMLETTER: + { + // the slot is enabled if we have an external dispatcher able to handle it, + // and the dispatcher must have enabled the slot in general + aReturn.bEnabled = getExternalSlotState( nId ); + + // for the Insert* slots, we need at least one selected row + if (ID_BROWSER_FORMLETTER != nId) + aReturn.bEnabled = aReturn.bEnabled && getBrowserView()->getVclControl()->GetSelectRowCount(); + + // disabled for native queries which are not saved within the database + Reference< XPropertySet > xDataSource(getRowSet(), UNO_QUERY); + try + { + aReturn.bEnabled = aReturn.bEnabled && xDataSource.is(); + + if (xDataSource.is()) + { + sal_Int32 nType = ::comphelper::getINT32(xDataSource->getPropertyValue(PROPERTY_COMMAND_TYPE)); + aReturn.bEnabled = aReturn.bEnabled && + ( ::comphelper::getBOOL(xDataSource->getPropertyValue(PROPERTY_ESCAPE_PROCESSING)) || + (nType == css::sdb::CommandType::QUERY) ); + } + } + catch(DisposedException&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::GetState: object already disposed!"); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + break; + + case ID_BROWSER_TITLE: + { + Reference<XPropertySet> xProp(getRowSet(),UNO_QUERY); + sal_Int32 nCommandType = CommandType::TABLE; + xProp->getPropertyValue(PROPERTY_COMMAND_TYPE) >>= nCommandType; + OUString sTitle; + switch (nCommandType) + { + case CommandType::TABLE: + sTitle = DBA_RES(STR_TBL_TITLE); break; + case CommandType::QUERY: + case CommandType::COMMAND: + sTitle = DBA_RES(STR_QRY_TITLE); break; + default: + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::GetState: unknown command type!"); + } + OUString aName; + xProp->getPropertyValue(PROPERTY_COMMAND) >>= aName; + OUString sObject(aName); + + aReturn.sTitle = sTitle.replaceFirst("#", sObject); + aReturn.bEnabled = true; + } + break; + case ID_BROWSER_TABLEATTR: + case ID_BROWSER_ROWHEIGHT: + case ID_BROWSER_COLATTRSET: + case ID_BROWSER_COLWIDTH: + aReturn.bEnabled = getBrowserView()->getVclControl() && isValid() && isValidCursor(); + // aReturn.bEnabled &= getDefinition() && !getDefinition()->GetDatabase()->IsReadOnly(); + break; + + case ID_BROWSER_COPY: + OSL_ENSURE( !m_pTreeView->HasChildPathFocus(), "SbaTableQueryBrowser::GetState( ID_BROWSER_COPY ): this should have been handled above!" ); + if (getBrowserView()->getVclControl() && !getBrowserView()->getVclControl()->IsEditing()) + { + SbaGridControl* pControl = getBrowserView()->getVclControl(); + if ( pControl->GetSelectRowCount() ) + { + aReturn.bEnabled = m_aCurrentFrame.isActive(); + break; + } + else + aReturn.bEnabled = pControl->canCopyCellText(pControl->GetCurRow(), pControl->GetCurColumnId()); + break; + } + [[fallthrough]]; + default: + return SbaXDataBrowserController::GetState(nId); + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return aReturn; + +} + +void SbaTableQueryBrowser::Execute(sal_uInt16 nId, const Sequence< PropertyValue >& aArgs) +{ + switch (nId) + { + default: + SbaXDataBrowserController::Execute(nId,aArgs); + break; + + case ID_TREE_EDIT_DATABASE: + implAdministrate( m_pTreeView->getListBox().GetCurEntry() ); + break; + + case ID_TREE_CLOSE_CONN: + closeConnection( m_pTreeView->getListBox().GetRootLevelParent( m_pTreeView->getListBox().GetCurEntry() ) ); + break; + + case ID_TREE_ADMINISTRATE: + svx::administrateDatabaseRegistration( getFrameWeld() ); + break; + + case ID_BROWSER_REFRESH: + { + if ( !SaveModified( ) ) + // nothing to do + break; + + bool bFullReinit = false; + // check if the query signature (if the form is based on a query) has changed + if ( !m_sQueryCommand.isEmpty() ) + { + OUString sNewQueryCommand; + bool bNewQueryEP; + + bool bIsQuery = + implGetQuerySignature( sNewQueryCommand, bNewQueryEP ); + OSL_ENSURE( bIsQuery, "SbaTableQueryBrowser::Execute: was a query before, but is not anymore?" ); + + bFullReinit = ( sNewQueryCommand != m_sQueryCommand ) || ( m_bQueryEscapeProcessing != bNewQueryEP ); + } + if ( !bFullReinit ) + { + // let the base class do a simple reload + SbaXDataBrowserController::Execute(nId,aArgs); + break; + } + [[fallthrough]]; + } + + case ID_BROWSER_REFRESH_REBUILD: + { + if ( !SaveModified() ) + // nothing to do + break; + + SvTreeListEntry* pSelected = m_pCurrentlyDisplayed; + // unload + unloadAndCleanup( false ); + + // reselect the entry + if ( pSelected ) + { + implSelect( pSelected ); + } + else + { + Reference<XPropertySet> xProp(getRowSet(),UNO_QUERY); + implSelect(svx::ODataAccessDescriptor(xProp)); + } + } + break; + + case ID_BROWSER_EXPLORER: + toggleExplorer(); + break; + + case ID_BROWSER_DOCUMENT_DATASOURCE: + implSelect(m_aDocumentDataSource); + break; + + case ID_BROWSER_INSERTCOLUMNS: + case ID_BROWSER_INSERTCONTENT: + case ID_BROWSER_FORMLETTER: + if (getBrowserView() && isValidCursor()) + { + // the URL the slot id is assigned to + OSL_ENSURE( m_aExternalFeatures.find( nId ) != m_aExternalFeatures.end(), + "SbaTableQueryBrowser::Execute( ID_BROWSER_?): how could this ever be enabled?" ); + URL aParentUrl = m_aExternalFeatures[ nId ].aURL; + + // let the dispatcher execute the slot + Reference< XDispatch > xDispatch( m_aExternalFeatures[ nId ].xDispatcher ); + if (xDispatch.is()) + { + // set the properties for the dispatch + + // first fill the selection + SbaGridControl* pGrid = getBrowserView()->getVclControl(); + MultiSelection* pSelection = const_cast<MultiSelection*>(pGrid->GetSelection()); + Sequence< Any > aSelection; + if ( !pGrid->IsAllSelected() ) + { // transfer the selected rows only if not all rows are selected + // (all rows means the whole table) + // #i3832# + if (pSelection != nullptr) + { + aSelection.realloc(pSelection->GetSelectCount()); + long nIdx = pSelection->FirstSelected(); + Any* pSelectionNos = aSelection.getArray(); + while (nIdx != SFX_ENDOFSELECTION) + { + *pSelectionNos++ <<= static_cast<sal_Int32>(nIdx + 1); + nIdx = pSelection->NextSelected(); + } + } + } + + Reference< XResultSet > xCursorClone; + try + { + Reference< XResultSetAccess > xResultSetAccess(getRowSet(),UNO_QUERY); + if (xResultSetAccess.is()) + xCursorClone = xResultSetAccess->createResultSet(); + } + catch(DisposedException&) + { + SAL_WARN("dbaccess.ui", "Object already disposed!"); + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::Execute(ID_BROWSER_?): could not clone the cursor!"); + } + + Reference<XPropertySet> xProp(getRowSet(),UNO_QUERY); + + try + { + ODataAccessDescriptor aDescriptor; + OUString sDataSourceName; + xProp->getPropertyValue(PROPERTY_DATASOURCENAME) >>= sDataSourceName; + + aDescriptor.setDataSource(sDataSourceName); + aDescriptor[DataAccessDescriptorProperty::Command] = xProp->getPropertyValue(PROPERTY_COMMAND); + aDescriptor[DataAccessDescriptorProperty::CommandType] = xProp->getPropertyValue(PROPERTY_COMMAND_TYPE); + aDescriptor[DataAccessDescriptorProperty::Connection] = xProp->getPropertyValue(PROPERTY_ACTIVE_CONNECTION); + aDescriptor[DataAccessDescriptorProperty::Cursor] <<= xCursorClone; + if ( aSelection.hasElements() ) + { + aDescriptor[DataAccessDescriptorProperty::Selection] <<= aSelection; + aDescriptor[DataAccessDescriptorProperty::BookmarkSelection] <<= false; + // these are selection indices + // before we change this, all clients have to be adjusted + // so that they recognize the new BookmarkSelection property! + } + + xDispatch->dispatch(aParentUrl, aDescriptor.createPropertyValueSequence()); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + break; + + case ID_BROWSER_CLOSE: + closeTask(); + // if it's not 0, such an async close is already pending + break; + + case ID_BROWSER_COPY: + if(m_pTreeView->HasChildPathFocus()) + { + copyEntry(m_pTreeView->getListBox().GetCurEntry()); + } + else if (getBrowserView() && getBrowserView()->getVclControl() && !getBrowserView()->getVclControl()->IsEditing() && getBrowserView()->getVclControl()->GetSelectRowCount() < 1) + { + SbaGridControl* pControl = getBrowserView()->getVclControl(); + pControl->copyCellText(pControl->GetCurRow(), pControl->GetCurColumnId()); + } + else + SbaXDataBrowserController::Execute(nId,aArgs); + break; + } +} + +void SbaTableQueryBrowser::implAddDatasource( const OUString& _rDataSourceName, const SharedConnection& _rxConnection ) +{ + Image a, b, c; + OUString d, e; + implAddDatasource( _rDataSourceName, a, d, b, e, c, _rxConnection ); +} + +void SbaTableQueryBrowser::implAddDatasource(const OUString& _rDbName, Image& _rDbImage, + OUString& _rQueryName, Image& _rQueryImage, OUString& _rTableName, Image& _rTableImage, + const SharedConnection& _rxConnection) +{ + SolarMutexGuard aGuard; + // initialize the names/images if necessary + if (_rQueryName.isEmpty()) + _rQueryName = DBA_RES(RID_STR_QUERIES_CONTAINER); + if (_rTableName.isEmpty()) + _rTableName = DBA_RES(RID_STR_TABLES_CONTAINER); + + if (!_rQueryImage) + _rQueryImage = ImageProvider::getFolderImage( DatabaseObject::QUERY ); + if (!_rTableImage) + _rTableImage = ImageProvider::getFolderImage( DatabaseObject::TABLE ); + + if (!_rDbImage) + _rDbImage = ImageProvider::getDatabaseImage(); + + // add the entry for the data source + // special handling for data sources denoted by URLs - we do not want to display this ugly URL, do we? + // #i33699# + OUString sDSDisplayName, sDataSourceId; + getDataSourceDisplayName_isURL( _rDbName, sDSDisplayName, sDataSourceId ); + + SvTreeListEntry* pDatasourceEntry = m_pTreeView->getListBox().InsertEntry( sDSDisplayName, _rDbImage, _rDbImage ); + DBTreeListUserData* pDSData = new DBTreeListUserData; + pDSData->eType = etDatasource; + pDSData->sAccessor = sDataSourceId; + pDSData->xConnection = _rxConnection; + pDatasourceEntry->SetUserData(pDSData); + + // the child for the queries container + { + DBTreeListUserData* pQueriesData = new DBTreeListUserData; + pQueriesData->eType = etQueryContainer; + + m_pTreeView->getListBox().InsertEntry( + _rQueryName, _rQueryImage, _rQueryImage, pDatasourceEntry, + true /*ChildrenOnDemand*/, TREELIST_APPEND, pQueriesData ); + } + + // the child for the tables container + { + DBTreeListUserData* pTablesData = new DBTreeListUserData; + pTablesData->eType = etTableContainer; + + m_pTreeView->getListBox().InsertEntry( + _rTableName, _rTableImage, _rTableImage, pDatasourceEntry, + true /*ChildrenOnDemand*/, TREELIST_APPEND, pTablesData ); + } + +} + +void SbaTableQueryBrowser::initializeTreeModel() +{ + if (m_xDatabaseContext.is()) + { + Image aDBImage, aQueriesImage, aTablesImage; + OUString sQueriesName, sTablesName; + + // fill the model with the names of the registered datasources + const Sequence<OUString> aDatasourceNames = m_xDatabaseContext->getElementNames(); + for (const OUString& rDatasource : aDatasourceNames) + implAddDatasource( rDatasource, aDBImage, sQueriesName, aQueriesImage, sTablesName, aTablesImage, SharedConnection() ); + } +} + +void SbaTableQueryBrowser::populateTree(const Reference<XNameAccess>& _xNameAccess, + SvTreeListEntry* _pParent, + EntryType _eEntryType) +{ + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(_pParent->GetUserData()); + if(pData) // don't ask if the nameaccess is already set see OnExpandEntry views and tables + pData->xContainer = _xNameAccess; + + try + { + const Sequence<OUString> aNames = _xNameAccess->getElementNames(); + for (const OUString& rName : aNames) + { + if( !m_pTreeView->getListBox().GetEntryPosByName(rName,_pParent)) + { + DBTreeListUserData* pEntryData = new DBTreeListUserData; + pEntryData->eType = _eEntryType; + if ( _eEntryType == etQuery ) + { + Reference<XNameAccess> xChild(_xNameAccess->getByName(rName),UNO_QUERY); + if ( xChild.is() ) + pEntryData->eType = etQueryContainer; + } + implAppendEntry( _pParent, rName, pEntryData, pEntryData->eType ); + } + } + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::populateTree: could not fill the tree"); + } +} + +SvTreeListEntry* SbaTableQueryBrowser::implAppendEntry( SvTreeListEntry* _pParent, const OUString& _rName, void* _pUserData, EntryType _eEntryType ) +{ + std::unique_ptr< ImageProvider > pImageProvider( getImageProviderFor( _pParent ) ); + + Image aImage; + pImageProvider->getImages( _rName, getDatabaseObjectType( _eEntryType ), aImage ); + + SvTreeListEntry* pNewEntry = m_pTreeView->getListBox().InsertEntry( _rName, _pParent, _eEntryType == etQueryContainer , TREELIST_APPEND, _pUserData ); + + m_pTreeView->getListBox().SetExpandedEntryBmp( pNewEntry, aImage ); + m_pTreeView->getListBox().SetCollapsedEntryBmp( pNewEntry, aImage ); + + return pNewEntry; +} + +IMPL_LINK(SbaTableQueryBrowser, OnExpandEntry, SvTreeListEntry*, _pParent, bool) +{ + if (_pParent->HasChildren()) + // nothing to do... + return true; + + SvTreeListEntry* pFirstParent = m_pTreeView->getListBox().GetRootLevelParent(_pParent); + OSL_ENSURE(pFirstParent,"SbaTableQueryBrowser::OnExpandEntry: No rootlevelparent!"); + + DBTreeListUserData* pData = static_cast< DBTreeListUserData* >(_pParent->GetUserData()); + assert(pData && "SbaTableQueryBrowser::OnExpandEntry: No user data!"); +#if OSL_DEBUG_LEVEL > 0 + SvLBoxString* pString = static_cast<SvLBoxString*>(pFirstParent->GetFirstItem(SvLBoxItemType::String)); + OSL_ENSURE(pString,"SbaTableQueryBrowser::OnExpandEntry: No string item!"); +#endif + + if (etTableContainer == pData->eType) + { + weld::WaitObject aWaitCursor(getFrameWeld()); + + // it could be that we already have a connection + SharedConnection xConnection; + ensureConnection( pFirstParent, xConnection ); + + if ( xConnection.is() ) + { + SQLExceptionInfo aInfo; + try + { + Reference< XWarningsSupplier > xWarnings(xConnection, UNO_QUERY); + if (xWarnings.is()) + xWarnings->clearWarnings(); + + // first insert the views because the tables can also include + // views but that time the bitmap is the wrong one + // the nameaccess will be overwritten in populateTree + Reference<XViewsSupplier> xViewSup(xConnection,UNO_QUERY); + if(xViewSup.is()) + populateTree( xViewSup->getViews(), _pParent, etTableOrView ); + + Reference<XTablesSupplier> xTabSup(xConnection,UNO_QUERY); + if(xTabSup.is()) + { + populateTree( xTabSup->getTables(), _pParent, etTableOrView ); + Reference<XContainer> xCont(xTabSup->getTables(),UNO_QUERY); + if(xCont.is()) + // add as listener to know when elements are inserted or removed + xCont->addContainerListener(this); + } + + if (xWarnings.is()) + { +#if 0 + SQLExceptionInfo aWarnings(xWarnings->getWarnings()); +// Obviously this if test is always false. So to avoid a Clang warning +// "use of logical '&&' with constant operand" I put this in #if +// 0. Yeah, I know it is fairly likely nobody will ever read this +// comment and make a decision what to do here, so I could as well +// have just binned this... + if (aWarnings.isValid() && sal_False) + { + SQLContext aContext; + aContext.Message = DBA_RES(STR_OPENTABLES_WARNINGS); + aContext.Details = DBA_RES(STR_OPENTABLES_WARNINGS_DETAILS); + aContext.NextException = aWarnings.get(); + aWarnings = aContext; + showError(aWarnings); + } +#endif + // TODO: we need a better concept for these warnings: + // something like "don't show any warnings for this datasource, again" would be nice + // But this requires an extension of the InteractionHandler and an additional property on the data source + } + } + catch(const SQLContext& e) { aInfo = e; } + catch(const SQLWarning& e) { aInfo = e; } + catch(const SQLException& e) { aInfo = e; } + catch(const WrappedTargetException& e) + { + SQLException aSql; + if(e.TargetException >>= aSql) + aInfo = aSql; + else + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::OnExpandEntry: something strange happened!"); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + if (aInfo.isValid()) + showError(aInfo); + } + else + return false; + // 0 indicates that an error occurred + } + else + { // we have to expand the queries or bookmarks + if (ensureEntryObject(_pParent)) + { + DBTreeListUserData* pParentData = static_cast< DBTreeListUserData* >( _pParent->GetUserData() ); + Reference< XNameAccess > xCollection( pParentData->xContainer, UNO_QUERY ); + populateTree( xCollection, _pParent, etQuery ); + } + } + return true; +} + +bool SbaTableQueryBrowser::ensureEntryObject( SvTreeListEntry* _pEntry ) +{ + OSL_ENSURE(_pEntry, "SbaTableQueryBrowser::ensureEntryObject: invalid argument!"); + if (!_pEntry) + return false; + + EntryType eType = getEntryType( _pEntry ); + + // the user data of the entry + DBTreeListUserData* pEntryData = static_cast<DBTreeListUserData*>(_pEntry->GetUserData()); + OSL_ENSURE(pEntryData,"ensureEntryObject: user data should already be set!"); + + SvTreeListEntry* pDataSourceEntry = m_pTreeView->getListBox().GetRootLevelParent(_pEntry); + + bool bSuccess = false; + switch (eType) + { + case etQueryContainer: + if ( pEntryData->xContainer.is() ) + { + // nothing to do + bSuccess = true; + break; + } + + { + SvTreeListEntry* pParent = m_pTreeView->getListBox().GetParent(_pEntry); + if ( pParent != pDataSourceEntry ) + { + SvLBoxString* pString = static_cast<SvLBoxString*>(_pEntry->GetFirstItem(SvLBoxItemType::String)); + OSL_ENSURE(pString,"There must be a string item!"); + OUString aName(pString->GetText()); + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pParent->GetUserData()); + try + { + Reference< XNameAccess > xNameAccess(pData->xContainer,UNO_QUERY); + if ( xNameAccess.is() ) + pEntryData->xContainer.set(xNameAccess->getByName(aName),UNO_QUERY); + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + bSuccess = pEntryData->xContainer.is(); + } + else + { + try + { + Reference< XQueryDefinitionsSupplier > xQuerySup; + m_xDatabaseContext->getByName( getDataSourceAccessor( pDataSourceEntry ) ) >>= xQuerySup; + if (xQuerySup.is()) + { + Reference< XNameAccess > xQueryDefs = xQuerySup->getQueryDefinitions(); + Reference< XContainer > xCont(xQueryDefs, UNO_QUERY); + if (xCont.is()) + // add as listener to get notified if elements are inserted or removed + xCont->addContainerListener(this); + + pEntryData->xContainer = xQueryDefs; + bSuccess = pEntryData->xContainer.is(); + } + else { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::ensureEntryObject: no XQueryDefinitionsSupplier interface!"); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + break; + + default: + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::ensureEntryObject: ooops ... missing some implementation here!"); + // TODO ... + break; + } + + return bSuccess; +} + +bool SbaTableQueryBrowser::implSelect(const svx::ODataAccessDescriptor& _rDescriptor, bool _bSelectDirect) +{ + // extract the props + OUString sDataSource; + OUString sCommand; + sal_Int32 nCommandType = CommandType::COMMAND; + bool bEscapeProcessing = true; + extractDescriptorProps(_rDescriptor, sDataSource, sCommand, nCommandType, bEscapeProcessing); + + // select it + return implSelect( sDataSource, sCommand, nCommandType, bEscapeProcessing, SharedConnection(), _bSelectDirect ); +} + +bool SbaTableQueryBrowser::implLoadAnything(const OUString& _rDataSourceName, const OUString& _rCommand, + const sal_Int32 _nCommandType, const bool _bEscapeProcessing, const SharedConnection& _rxConnection) +{ + try + { + Reference<XPropertySet> xProp( getRowSet(), UNO_QUERY_THROW ); + Reference< XLoadable > xLoadable( xProp, UNO_QUERY_THROW ); + // the values allowing the RowSet to re-execute + xProp->setPropertyValue(PROPERTY_DATASOURCENAME, makeAny(_rDataSourceName)); + if(_rxConnection.is()) + xProp->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, makeAny( _rxConnection.getTyped() ) ); + + // set this _before_ setting the connection, else the rowset would rebuild it ... + xProp->setPropertyValue(PROPERTY_COMMAND_TYPE, makeAny(_nCommandType)); + xProp->setPropertyValue(PROPERTY_COMMAND, makeAny(_rCommand)); + xProp->setPropertyValue(PROPERTY_ESCAPE_PROCESSING, css::uno::makeAny(_bEscapeProcessing)); + if ( m_bPreview ) + { + xProp->setPropertyValue(PROPERTY_FETCHDIRECTION, makeAny(FetchDirection::FORWARD)); + } + + // the formatter depends on the data source we're working on, so rebuild it here ... + initFormatter(); + + // switch the grid to design mode while loading + getBrowserView()->getGridControl()->setDesignMode(true); + InitializeForm( xProp ); + + bool bSuccess = true; + + { + { + Reference< XNameContainer > xColContainer(getFormComponent(), UNO_QUERY); + // first we have to clear the grid + clearGridColumns(xColContainer); + } + FormErrorHelper aHelper(this); + // load the form + bSuccess = reloadForm(xLoadable); + + // initialize the model + InitializeGridModel(getFormComponent()); + + Any aVal = xProp->getPropertyValue(PROPERTY_ISNEW); + if (aVal.hasValue() && ::comphelper::getBOOL(aVal)) + { + // then set the default values and the parameters given from the parent + Reference< XReset> xReset(xProp, UNO_QUERY); + xReset->reset(); + } + + if ( m_bPreview ) + initializePreviewMode(); + + LoadFinished(true); + } + + InvalidateAll(); + return bSuccess; + } + catch( const SQLException& ) + { + Any aException( ::cppu::getCaughtException() ); + showError( SQLExceptionInfo( aException ) ); + } + catch( const WrappedTargetException& e ) + { + if ( e.TargetException.isExtractableTo( ::cppu::UnoType< SQLException >::get() ) ) + showError( SQLExceptionInfo( e.TargetException ) ); + else + { + TOOLS_WARN_EXCEPTION("dbaccess", ""); + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + InvalidateAll(); + return false; +} + +bool SbaTableQueryBrowser::implSelect(const OUString& _rDataSourceName, const OUString& _rCommand, + const sal_Int32 _nCommandType, const bool _bEscapeProcessing, + const SharedConnection& _rxConnection, + bool _bSelectDirect) +{ + if (_rDataSourceName.getLength() && _rCommand.getLength() && (-1 != _nCommandType)) + { + SvTreeListEntry* pDataSource = nullptr; + SvTreeListEntry* pCommandType = nullptr; + SvTreeListEntry* pCommand = getObjectEntry( _rDataSourceName, _rCommand, _nCommandType, &pDataSource, &pCommandType, true, _rxConnection ); + + if (pCommand) + { + bool bSuccess = true; + if ( _bSelectDirect ) + { + bSuccess = implSelect( pCommand ); + } + else + { + m_pTreeView->getListBox().Select( pCommand ); + } + + if ( bSuccess ) + { + m_pTreeView->getListBox().MakeVisible(pCommand); + m_pTreeView->getListBox().SetCursor(pCommand); + } + } + else if (!pCommandType) + { + if ( m_pCurrentlyDisplayed ) + { // tell the old entry (if any) it has been deselected + selectPath(m_pCurrentlyDisplayed, false); + m_pCurrentlyDisplayed = nullptr; + } + + // we have a command and need to display this in the rowset + return implLoadAnything(_rDataSourceName, _rCommand, _nCommandType, _bEscapeProcessing, _rxConnection); + } + } + return false; +} + +IMPL_LINK_NOARG(SbaTableQueryBrowser, OnSelectionChange, LinkParamNone*, void) +{ + implSelect( m_pTreeView->getListBox().FirstSelected() ); +} + +SvTreeListEntry* SbaTableQueryBrowser::implGetConnectionEntry(SvTreeListEntry* _pEntry) const +{ + SvTreeListEntry* pCurrentEntry = _pEntry; + DBTreeListUserData* pEntryData = static_cast< DBTreeListUserData* >( pCurrentEntry->GetUserData() ); + while(pEntryData->eType != etDatasource ) + { + pCurrentEntry = m_pTreeView->GetTreeModel()->GetParent(pCurrentEntry); + pEntryData = static_cast< DBTreeListUserData* >( pCurrentEntry->GetUserData() ); + } + return pCurrentEntry; +} + +bool SbaTableQueryBrowser::implSelect( SvTreeListEntry* _pEntry ) +{ + if ( !_pEntry ) + return false; + + DBTreeListUserData* pEntryData = static_cast< DBTreeListUserData* >( _pEntry->GetUserData() ); + switch (pEntryData->eType) + { + case etTableOrView: + case etQuery: + break; + default: + // nothing to do + return false; + } + + OSL_ENSURE(m_pTreeView->GetTreeModel()->HasParent(_pEntry), "SbaTableQueryBrowser::implSelect: invalid entry (1)!"); + OSL_ENSURE(m_pTreeView->GetTreeModel()->HasParent(m_pTreeView->GetTreeModel()->GetParent(_pEntry)), "SbaTableQueryBrowser::implSelect: invalid entry (2)!"); + + // get the entry for the tables or queries + SvTreeListEntry* pContainer = m_pTreeView->GetTreeModel()->GetParent(_pEntry); + DBTreeListUserData* pContainerData = static_cast<DBTreeListUserData*>(pContainer->GetUserData()); + + // get the entry for the datasource + SvTreeListEntry* pConnection = implGetConnectionEntry(pContainer); + DBTreeListUserData* pConData = static_cast<DBTreeListUserData*>(pConnection->GetUserData()); + + // reinitialize the rowset + // but first check if it is necessary + // get all old properties + Reference<XPropertySet> xRowSetProps(getRowSet(),UNO_QUERY); + OUString aOldName; + xRowSetProps->getPropertyValue(PROPERTY_COMMAND) >>= aOldName; + sal_Int32 nOldType = 0; + xRowSetProps->getPropertyValue(PROPERTY_COMMAND_TYPE) >>= nOldType; + Reference<XConnection> xOldConnection(xRowSetProps->getPropertyValue(PROPERTY_ACTIVE_CONNECTION),UNO_QUERY); + + // the name of the table or query + SvLBoxString* pString = static_cast<SvLBoxString*>(_pEntry->GetFirstItem(SvLBoxItemType::String)); + OSL_ENSURE(pString,"There must be a string item!"); + const OUString sSimpleName = pString->GetText(); + OUStringBuffer sNameBuffer(sSimpleName); + if ( etQueryContainer == pContainerData->eType ) + { + SvTreeListEntry* pTemp = pContainer; + while( m_pTreeView->GetTreeModel()->GetParent(pTemp) != pConnection ) + { + sNameBuffer.insert(0,'/'); + pString = static_cast<SvLBoxString*>(pTemp->GetFirstItem(SvLBoxItemType::String)); + OSL_ENSURE(pString,"There must be a string item!"); + sNameBuffer.insert(0,pString->GetText()); + pTemp = m_pTreeView->GetTreeModel()->GetParent(pTemp); + } + } + OUString aName = sNameBuffer.makeStringAndClear(); + + sal_Int32 nCommandType = ( etTableContainer == pContainerData->eType) + ? CommandType::TABLE + : CommandType::QUERY; + + // check if need to rebuild the rowset + bool bRebuild = ( xOldConnection != pConData->xConnection ) + || ( nOldType != nCommandType ) + || ( aName != aOldName ); + + Reference< css::form::XLoadable > xLoadable = getLoadable(); + bRebuild |= !xLoadable->isLoaded(); + bool bSuccess = true; + if ( bRebuild ) + { + try + { + weld::WaitObject aWaitCursor(getFrameWeld()); + + // tell the old entry it has been deselected + selectPath(m_pCurrentlyDisplayed, false); + m_pCurrentlyDisplayed = nullptr; + + // not really loaded + m_pCurrentlyDisplayed = _pEntry; + // tell the new entry it has been selected + selectPath(m_pCurrentlyDisplayed); + + // get the name of the data source currently selected + ensureConnection( m_pCurrentlyDisplayed, pConData->xConnection ); + + if ( !pConData->xConnection.is() ) + { + unloadAndCleanup( false ); + return false; + } + + Reference<XNameAccess> xNameAccess; + switch(nCommandType) + { + case CommandType::TABLE: + { + // only for tables + if ( !pContainerData->xContainer.is() ) + { + Reference<XTablesSupplier> xSup( pConData->xConnection, UNO_QUERY ); + if(xSup.is()) + xNameAccess = xSup->getTables(); + + pContainerData->xContainer = xNameAccess; + } + else + xNameAccess.set( pContainerData->xContainer, UNO_QUERY ); + } + break; + case CommandType::QUERY: + { + if ( pContainerData->xContainer.is() ) + xNameAccess.set( pContainerData->xContainer, UNO_QUERY ); + else + { + Reference<XQueriesSupplier> xSup( pConData->xConnection, UNO_QUERY ); + if(xSup.is()) + xNameAccess = xSup->getQueries(); + } + } + break; + } + OUString sStatus(DBA_RES(CommandType::TABLE == nCommandType ? STR_LOADING_TABLE : STR_LOADING_QUERY)); + sStatus = sStatus.replaceFirst("$name$", aName); + BrowserViewStatusDisplay aShowStatus(static_cast<UnoDataBrowserView*>(getView()), sStatus); + + bool bEscapeProcessing = true; + if(xNameAccess.is() && xNameAccess->hasByName(sSimpleName)) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(_pEntry->GetUserData()); + if ( !pData->xObjectProperties.is() ) + { + Reference<XInterface> xObject; + if(xNameAccess->getByName(sSimpleName) >>= xObject) // remember the table or query object + { + pData->xObjectProperties.set(xObject, css::uno::UNO_QUERY); + // if the query contains a parameterized statement and preview is enabled we won't get any data. + if ( nCommandType == CommandType::QUERY && xObject.is() ) + { + Reference<XPropertySet> xObjectProps(xObject,UNO_QUERY); + xObjectProps->getPropertyValue(PROPERTY_ESCAPE_PROCESSING) >>= bEscapeProcessing; + if ( m_bPreview ) + { + OUString sSql; + xObjectProps->getPropertyValue(PROPERTY_COMMAND) >>= sSql; + Reference< XMultiServiceFactory > xFactory( pConData->xConnection, UNO_QUERY ); + if (xFactory.is()) + { + try + { + Reference<XSingleSelectQueryAnalyzer> xAnalyzer(xFactory->createInstance(SERVICE_NAME_SINGLESELECTQUERYCOMPOSER),UNO_QUERY); + if ( xAnalyzer.is() ) + { + xAnalyzer->setQuery(sSql); + Reference<XParametersSupplier> xParSup(xAnalyzer,UNO_QUERY); + if ( xParSup->getParameters()->getCount() > 0 ) + { + OUString sFilter = " WHERE " + xAnalyzer->getFilter(); + OUString sReplace = sSql.replaceFirst(sFilter, ""); + xAnalyzer->setQuery(sReplace); + Reference<XSingleSelectQueryComposer> xComposer(xAnalyzer,UNO_QUERY); + xComposer->setFilter("0=1"); + aName = xAnalyzer->getQuery(); + nCommandType = CommandType::COMMAND; + } + } + } + catch (Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + } + } + } + } + + OUString sDataSourceName( getDataSourceAccessor( pConnection ) ); + bSuccess = implLoadAnything( sDataSourceName, aName, nCommandType, bEscapeProcessing, pConData->xConnection ); + if ( !bSuccess ) + { // clean up + criticalFail(); + } + } + catch(const SQLException& e) + { + showError(SQLExceptionInfo(e)); + // reset the values + xRowSetProps->setPropertyValue(PROPERTY_DATASOURCENAME,Any()); + xRowSetProps->setPropertyValue(PROPERTY_ACTIVE_CONNECTION,Any()); + bSuccess = false; + } + catch(WrappedTargetException& e) + { + SQLException aSql; + if(e.TargetException >>= aSql) + showError(SQLExceptionInfo(aSql)); + else + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::implSelect: something strange happened!"); + // reset the values + xRowSetProps->setPropertyValue(PROPERTY_DATASOURCENAME,Any()); + xRowSetProps->setPropertyValue(PROPERTY_ACTIVE_CONNECTION,Any()); + bSuccess = false; + } + catch(const Exception&) + { + // reset the values + xRowSetProps->setPropertyValue(PROPERTY_DATASOURCENAME,Any()); + xRowSetProps->setPropertyValue(PROPERTY_ACTIVE_CONNECTION,Any()); + bSuccess = false; + } + } + return bSuccess; +} + +SvTreeListEntry* SbaTableQueryBrowser::getEntryFromContainer(const Reference<XNameAccess>& _rxNameAccess) +{ + DBTreeListBox& rListBox = m_pTreeView->getListBox(); + SvTreeListEntry* pContainer = nullptr; + SvTreeListEntry* pDSLoop = rListBox.FirstChild(nullptr); + while (pDSLoop) + { + pContainer = rListBox.GetEntry(pDSLoop, CONTAINER_QUERIES); + DBTreeListUserData* pQueriesData = static_cast<DBTreeListUserData*>(pContainer->GetUserData()); + if ( pQueriesData && pQueriesData->xContainer == _rxNameAccess ) + break; + + pContainer = rListBox.GetEntry(pDSLoop, CONTAINER_TABLES); + DBTreeListUserData* pTablesData = static_cast<DBTreeListUserData*>(pContainer->GetUserData()); + if ( pTablesData && pTablesData->xContainer == _rxNameAccess ) + break; + + pDSLoop = pDSLoop->NextSibling(); + pContainer = nullptr; + } + return pContainer; +} + +void SAL_CALL SbaTableQueryBrowser::elementInserted( const ContainerEvent& _rEvent ) +{ + SolarMutexGuard aSolarGuard; + + Reference< XNameAccess > xNames(_rEvent.Source, UNO_QUERY); + // first search for a definition container where we can insert this element + + SvTreeListEntry* pEntry = getEntryFromContainer(xNames); + if(pEntry) // found one + { + // insert the new entry into the tree + DBTreeListUserData* pContainerData = static_cast<DBTreeListUserData*>(pEntry->GetUserData()); + OSL_ENSURE(pContainerData, "elementInserted: There must be user data for this type!"); + + DBTreeListUserData* pNewData = new DBTreeListUserData; + bool bIsTable = etTableContainer == pContainerData->eType; + if ( bIsTable ) + { + _rEvent.Element >>= pNewData->xObjectProperties;// remember the new element + pNewData->eType = etTableOrView; + } + else + { + if (static_cast<sal_Int32>(m_pTreeView->getListBox().GetChildCount(pEntry)) < ( xNames->getElementNames().getLength() - 1 ) ) + { + // the item inserts its children on demand, but it has not been expanded yet. So ensure here and + // now that it has all items + populateTree(xNames, pEntry, etQuery ); + } + pNewData->eType = etQuery; + } + implAppendEntry( pEntry, ::comphelper::getString( _rEvent.Accessor ), pNewData, pNewData->eType ); + } + else + SbaXDataBrowserController::elementInserted(_rEvent); +} + +bool SbaTableQueryBrowser::isCurrentlyDisplayedChanged(const OUString& _sName, SvTreeListEntry const * _pContainer) +{ + return m_pCurrentlyDisplayed + && getEntryType(m_pCurrentlyDisplayed) == getChildType(_pContainer) + && m_pTreeView->getListBox().GetParent(m_pCurrentlyDisplayed) == _pContainer + && m_pTreeView->getListBox().GetEntryText(m_pCurrentlyDisplayed) == _sName; +} + +void SAL_CALL SbaTableQueryBrowser::elementRemoved( const ContainerEvent& _rEvent ) +{ + SolarMutexGuard aSolarGuard; + + Reference< XNameAccess > xNames(_rEvent.Source, UNO_QUERY); + // get the top-level representing the removed data source + // and search for the queries and tables + SvTreeListEntry* pContainer = getEntryFromContainer(xNames); + if ( pContainer ) + { // a query or table has been removed + OUString aName = ::comphelper::getString(_rEvent.Accessor); + + if ( isCurrentlyDisplayedChanged( aName, pContainer) ) + { // the element displayed currently has been replaced + + // we need to remember the old value + SvTreeListEntry* pTemp = m_pCurrentlyDisplayed; + + // unload + unloadAndCleanup( false ); // don't dispose the connection + + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pTemp->GetUserData()); + pTemp->SetUserData(nullptr); + delete pData; + // the data could be null because we have a table which isn't correct + m_pTreeView->GetTreeModel()->Remove(pTemp); + } + else + { + // remove the entry from the model + SvTreeListEntry* pChild = m_pTreeView->GetTreeModel()->FirstChild(pContainer); + while(pChild) + { + if (m_pTreeView->getListBox().GetEntryText(pChild) == aName) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pChild->GetUserData()); + pChild->SetUserData(nullptr); + delete pData; + m_pTreeView->GetTreeModel()->Remove(pChild); + break; + } + pChild = pChild->NextSibling(); + } + } + + // maybe the object which is part of the document data source has been removed + checkDocumentDataSource(); + } + else + SbaXDataBrowserController::elementRemoved(_rEvent); +} + +void SAL_CALL SbaTableQueryBrowser::elementReplaced( const ContainerEvent& _rEvent ) +{ + SolarMutexGuard aSolarGuard; + + Reference< XNameAccess > xNames(_rEvent.Source, UNO_QUERY); + SvTreeListEntry* pContainer = getEntryFromContainer(xNames); + if ( pContainer ) + { // a table or query as been replaced + OUString aName = ::comphelper::getString(_rEvent.Accessor); + + if ( isCurrentlyDisplayedChanged( aName, pContainer) ) + { // the element displayed currently has been replaced + + // we need to remember the old value + SvTreeListEntry* pTemp = m_pCurrentlyDisplayed; + unloadAndCleanup( false ); // don't dispose the connection + + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pTemp->GetUserData()); + if (pData) + { + if ( etTableOrView == pData->eType ) + { // only insert userdata when we have a table because the query is only a commanddefinition object and not a query + _rEvent.Element >>= pData->xObjectProperties; // remember the new element + } + else + { + pTemp->SetUserData(nullptr); + delete pData; + } + } + } + else + { + // find the entry for this name + SvTreeListEntry* pChild = m_pTreeView->GetTreeModel()->FirstChild(pContainer); + while(pChild) + { + if (m_pTreeView->getListBox().GetEntryText(pChild) == aName) + { + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pChild->GetUserData()); + if (pData) + { + if ( etTableOrView == pData->eType ) + { // only insert userdata when we have a table because the query is only a commanddefinition object and not a query + _rEvent.Element >>= pData->xObjectProperties; // remember the new element + } + else + { + pChild->SetUserData(nullptr); + delete pData; + } + } + break; + } + pChild = pChild->NextSibling(); + } + } + + // maybe the object which is part of the document data source has been removed + checkDocumentDataSource(); + } + else if (xNames.get() == m_xDatabaseContext.get()) + { // a datasource has been replaced in the context + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::elementReplaced: no support for replaced data sources!"); + // very suspicious: the database context should not allow to replace data source, only to register + // and revoke them + } + else + SbaXDataBrowserController::elementReplaced(_rEvent); +} + +void SbaTableQueryBrowser::impl_releaseConnection( SharedConnection& _rxConnection ) +{ + // remove as event listener + Reference< XComponent > xComponent( _rxConnection, UNO_QUERY ); + if ( xComponent.is() ) + { + Reference< XEventListener > xListener( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY ); + xComponent->removeEventListener( xListener ); + } + + try + { + // temporary (hopefully!) hack for #i55274# + Reference< XFlushable > xFlush( _rxConnection, UNO_QUERY ); + if ( xFlush.is() ) + xFlush->flush(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // clear + _rxConnection.clear(); + // will implicitly dispose if we have the ownership, since xConnection is a SharedConnection +} + +void SbaTableQueryBrowser::disposeConnection( SvTreeListEntry* _pDSEntry ) +{ + OSL_ENSURE( _pDSEntry, "SbaTableQueryBrowser::disposeConnection: invalid entry (NULL)!" ); + OSL_ENSURE( impl_isDataSourceEntry( _pDSEntry ), "SbaTableQueryBrowser::disposeConnection: invalid entry (not top-level)!" ); + + if ( _pDSEntry ) + { + DBTreeListUserData* pTreeListData = static_cast< DBTreeListUserData* >( _pDSEntry->GetUserData() ); + if ( pTreeListData ) + impl_releaseConnection( pTreeListData->xConnection ); + } +} + +void SbaTableQueryBrowser::closeConnection(SvTreeListEntry* _pDSEntry, bool _bDisposeConnection) +{ + OSL_ENSURE(_pDSEntry, "SbaTableQueryBrowser::closeConnection: invalid entry (NULL)!"); + OSL_ENSURE( impl_isDataSourceEntry( _pDSEntry ), "SbaTableQueryBrowser::closeConnection: invalid entry (not top-level)!"); + + // if one of the entries of the given DS is displayed currently, unload the form + if (m_pCurrentlyDisplayed && (m_pTreeView->getListBox().GetRootLevelParent(m_pCurrentlyDisplayed) == _pDSEntry)) + unloadAndCleanup(_bDisposeConnection); + + // collapse the query/table container + for (SvTreeListEntry* pContainers = m_pTreeView->GetTreeModel()->FirstChild(_pDSEntry); pContainers; pContainers = pContainers->NextSibling()) + { + SvTreeListEntry* pElements = m_pTreeView->GetTreeModel()->FirstChild(pContainers); + if ( pElements ) + m_pTreeView->getListBox().Collapse(pContainers); + m_pTreeView->getListBox().EnableExpandHandler(pContainers); + // and delete their children (they are connection-relative) + for (; pElements; ) + { + SvTreeListEntry* pRemove = pElements; + pElements = pElements->NextSibling(); + DBTreeListUserData* pData = static_cast<DBTreeListUserData*>(pRemove->GetUserData()); + pRemove->SetUserData(nullptr); + delete pData; + m_pTreeView->GetTreeModel()->Remove(pRemove); + } + } + // collapse the entry itself + m_pTreeView->getListBox().Collapse(_pDSEntry); + + // dispose/reset the connection + if ( _bDisposeConnection ) + disposeConnection( _pDSEntry ); +} + +void SbaTableQueryBrowser::unloadAndCleanup( bool _bDisposeConnection ) +{ + if (!m_pCurrentlyDisplayed) + // nothing to do + return; + + SvTreeListEntry* pDSEntry = m_pTreeView->getListBox().GetRootLevelParent(m_pCurrentlyDisplayed); + + // de-select the path for the currently displayed table/query + if (m_pCurrentlyDisplayed) + { + selectPath(m_pCurrentlyDisplayed, false); + } + m_pCurrentlyDisplayed = nullptr; + + try + { + // get the active connection. We need to dispose it. + + // unload the form + Reference< XLoadable > xLoadable = getLoadable(); + if (xLoadable->isLoaded()) + xLoadable->unload(); + + // clear the grid control + Reference< XNameContainer > xConta(getControlModel(),UNO_QUERY); + clearGridColumns(xConta); + + // dispose the connection + if(_bDisposeConnection) + disposeConnection( pDSEntry ); + } + catch(SQLException& e) + { + showError(SQLExceptionInfo(e)); + } + catch(WrappedTargetException& e) + { + SQLException aSql; + if(e.TargetException >>= aSql) + showError(SQLExceptionInfo(aSql)); + else + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::unloadAndCleanup: something strange happened!"); + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::unloadAndCleanup: could not reset the form"); + } +} + +namespace +{ + Reference< XInterface > lcl_getDataSource( const Reference< XDatabaseContext >& _rxDatabaseContext, + const OUString& _rDataSourceName, const Reference< XConnection >& _rxConnection ) + { + Reference< XDataSource > xDataSource; + try + { + if ( !_rDataSourceName.isEmpty() && _rxDatabaseContext->hasByName( _rDataSourceName ) ) + xDataSource.set( _rxDatabaseContext->getByName( _rDataSourceName ), UNO_QUERY_THROW ); + + if ( !xDataSource.is() ) + { + Reference< XChild > xConnAsChild( _rxConnection, UNO_QUERY ); + if ( xConnAsChild.is() ) + xDataSource.set( xConnAsChild->getParent(), UNO_QUERY_THROW ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return xDataSource.get(); + } +} + +void SbaTableQueryBrowser::impl_initialize() +{ + SolarMutexGuard aGuard; + // doin' a lot of VCL stuff here -> lock the SolarMutex + + // first initialize the parent + SbaXDataBrowserController::impl_initialize(); + + Reference<XConnection> xForeignConnection; + Reference< XFrame > xFrame; + + OUString aTableName, aCatalogName, aSchemaName; + + bool bEscapeProcessing = true; + sal_Int32 nInitialDisplayCommandType = CommandType::COMMAND; + OUString sInitialDataSourceName; + OUString sInitialCommand; + + const NamedValueCollection& rArguments( getInitParams() ); + + rArguments.get_ensureType( PROPERTY_DATASOURCENAME, sInitialDataSourceName ); + rArguments.get_ensureType( PROPERTY_COMMAND_TYPE, nInitialDisplayCommandType ); + rArguments.get_ensureType( PROPERTY_COMMAND, sInitialCommand ); + rArguments.get_ensureType( PROPERTY_ACTIVE_CONNECTION, xForeignConnection ); + rArguments.get_ensureType( PROPERTY_UPDATE_CATALOGNAME, aCatalogName ); + rArguments.get_ensureType( PROPERTY_UPDATE_SCHEMANAME, aSchemaName ); + rArguments.get_ensureType( PROPERTY_UPDATE_TABLENAME, aTableName ); + rArguments.get_ensureType( PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing ); + rArguments.get_ensureType( "Frame", xFrame ); + rArguments.get_ensureType( PROPERTY_SHOWMENU, m_bShowMenu ); + + // disable the browser if either of ShowTreeViewButton (compatibility name) or EnableBrowser + // is present and set to FALSE + bool bDisableBrowser = !rArguments.getOrDefault( "ShowTreeViewButton", true ) // compatibility name + || !rArguments.getOrDefault( PROPERTY_ENABLE_BROWSER, true ); + OSL_ENSURE( !rArguments.has( "ShowTreeViewButton" ), + "SbaTableQueryBrowser::impl_initialize: ShowTreeViewButton is superseded by EnableBrowser!" ); + m_bEnableBrowser = !bDisableBrowser; + + // hide the tree view it is disabled in general, or if the settings tell to hide it initially + bool bHideTreeView = ( !m_bEnableBrowser ) + || !rArguments.getOrDefault( "ShowTreeView", true ) // compatibility name + || !rArguments.getOrDefault( PROPERTY_SHOW_BROWSER, true ); + OSL_ENSURE( !rArguments.has( "ShowTreeView" ), + "SbaTableQueryBrowser::impl_initialize: ShowTreeView is superseded by ShowBrowser!" ); + + if ( bHideTreeView ) + hideExplorer(); + else + showExplorer(); + + if ( m_bPreview ) + { + try + { + Sequence< OUString> aProperties(5); + Sequence< Any> aValues(5); + + OUString* pStringIter = aProperties.getArray(); + Any* pValueIter = aValues.getArray(); + *pStringIter++ = "AlwaysShowCursor"; + *pValueIter++ <<= false; + *pStringIter++ = PROPERTY_BORDER; + *pValueIter++ <<= sal_Int16(0); + + *pStringIter++ = "HasNavigationBar"; + *pValueIter++ <<= false; + *pStringIter++ = "HasRecordMarker"; + *pValueIter++ <<= false; + + *pStringIter++ = "Tabstop"; + *pValueIter++ <<= false; + + Reference< XMultiPropertySet > xFormMultiSet(getFormComponent(), UNO_QUERY); + if ( xFormMultiSet.is() ) + xFormMultiSet->setPropertyValues(aProperties, aValues); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + // are we loaded into a (sub)frame of an embedded document (i.e. a form belonging to a database + // document)? + bool bSubFrameOfEmbeddedDocument = false; + if ( xFrame.is() ) + { + Reference<XFramesSupplier> xSup = xFrame->getCreator(); + Reference<XController> xCont = xSup.is() ? xSup->getController() : Reference<XController>(); + + bSubFrameOfEmbeddedDocument = xCont.is() && ::dbtools::isEmbeddedInDatabase( xCont->getModel(), xForeignConnection ); + } + + // if we have a connection at this point, it was either passed from outside, our + // determined from an outer DB document. In both cases, do not dispose it later on. + SharedConnection xConnection( xForeignConnection, SharedConnection::NoTakeOwnership ); + + // should we display all registered databases in the left hand side tree? + // or only *one* special? + bool bLimitedTreeEntries = false; + // if we're part of a frame which is a secondary frame of a database document, then only + // display the database for this document, not all registered ones + bLimitedTreeEntries |= bSubFrameOfEmbeddedDocument; + // if the tree view is not to be displayed at all, then only display the data source + // which was given as initial selection + bLimitedTreeEntries |= !m_bEnableBrowser; + + if ( bLimitedTreeEntries ) + { + if ( xConnection.is() ) + { + startConnectionListening( xConnection ); + + // if no initial name was given, try to obtain one from the data source + if ( sInitialDataSourceName.isEmpty() ) + { + Reference< XChild > xChild( xConnection, UNO_QUERY ); + Reference< XPropertySet > xDataSourceProperties; + if ( xChild.is() ) + xDataSourceProperties.set(xChild->getParent(), css::uno::UNO_QUERY); + if ( xDataSourceProperties.is() ) + { + try + { + OSL_VERIFY( xDataSourceProperties->getPropertyValue( PROPERTY_NAME ) >>= sInitialDataSourceName ); + } + catch( const Exception& ) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::impl_initialize: a connection parent which does not have a 'Name'!??" ); + } + } + } + } + + implAddDatasource( sInitialDataSourceName, xConnection ); + m_pTreeView->getListBox().Expand( m_pTreeView->getListBox().First() ); + } + else + initializeTreeModel(); + + if ( m_bEnableBrowser ) + { + m_aDocScriptSupport = ::std::optional< bool >( false ); + } + else + { + // we are not used as "browser", but as mere view for a single table/query/command. In particular, + // there is a specific database document which we belong to. + Reference< XOfficeDatabaseDocument > xDocument( getDataSourceOrModel( + lcl_getDataSource( m_xDatabaseContext, sInitialDataSourceName, xConnection ) ), UNO_QUERY ); + m_aDocScriptSupport = ::std::optional< bool >( Reference< XEmbeddedScripts >( xDocument, UNO_QUERY ).is() ); + } + + if ( implSelect( sInitialDataSourceName, sInitialCommand, nInitialDisplayCommandType, bEscapeProcessing, xConnection, true ) ) + { + try + { + Reference< XPropertySet > xRowSetProps(getRowSet(), UNO_QUERY); + xRowSetProps->setPropertyValue(PROPERTY_UPDATE_CATALOGNAME,makeAny(aCatalogName)); + xRowSetProps->setPropertyValue(PROPERTY_UPDATE_SCHEMANAME,makeAny(aSchemaName)); + xRowSetProps->setPropertyValue(PROPERTY_UPDATE_TABLENAME,makeAny(aTableName)); + + } + catch(const Exception&) + { + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::impl_initialize: could not set the update related names!"); + } + } + + InvalidateAll(); +} + +bool SbaTableQueryBrowser::haveExplorer() const +{ + return m_pTreeView && m_pTreeView->IsVisible(); +} + +void SbaTableQueryBrowser::hideExplorer() +{ + if (!haveExplorer()) + return; + if (!getBrowserView()) + return; + + m_pTreeView->Hide(); + m_pSplitter->Hide(); + getBrowserView()->Resize(); + + InvalidateFeature(ID_BROWSER_EXPLORER); +} + +void SbaTableQueryBrowser::showExplorer() +{ + if (haveExplorer()) + return; + + if (!getBrowserView()) + return; + + m_pTreeView->Show(); + m_pSplitter->Show(); + getBrowserView()->Resize(); + + InvalidateFeature(ID_BROWSER_EXPLORER); +} + +bool SbaTableQueryBrowser::ensureConnection(SvTreeListEntry* _pAnyEntry, SharedConnection& _rConnection) +{ + SvTreeListEntry* pDSEntry = m_pTreeView->getListBox().GetRootLevelParent(_pAnyEntry); + DBTreeListUserData* pDSData = + pDSEntry + ? static_cast<DBTreeListUserData*>(pDSEntry->GetUserData()) + : nullptr; + + return ensureConnection( pDSEntry, pDSData, _rConnection ); +} + +std::unique_ptr< ImageProvider > SbaTableQueryBrowser::getImageProviderFor( SvTreeListEntry* _pAnyEntry ) +{ + std::unique_ptr< ImageProvider > pImageProvider( new ImageProvider ); + SharedConnection xConnection; + if ( getExistentConnectionFor( _pAnyEntry, xConnection ) ) + pImageProvider.reset( new ImageProvider( xConnection ) ); + return pImageProvider; +} + +bool SbaTableQueryBrowser::getExistentConnectionFor( SvTreeListEntry* _pAnyEntry, SharedConnection& _rConnection ) +{ + SvTreeListEntry* pDSEntry = m_pTreeView->getListBox().GetRootLevelParent( _pAnyEntry ); + DBTreeListUserData* pDSData = + pDSEntry + ? static_cast< DBTreeListUserData* >( pDSEntry->GetUserData() ) + : nullptr; + if ( pDSData ) + _rConnection = pDSData->xConnection; + return _rConnection.is(); +} + +bool SbaTableQueryBrowser::impl_isDataSourceEntry( SvTreeListEntry* _pEntry ) const +{ + return m_pTreeView->GetTreeModel()->GetRootLevelParent( _pEntry ) == _pEntry; +} + +bool SbaTableQueryBrowser::ensureConnection( SvTreeListEntry* _pDSEntry, void* pDSData, SharedConnection& _rConnection ) +{ + OSL_ENSURE( impl_isDataSourceEntry( _pDSEntry ), "SbaTableQueryBrowser::ensureConnection: this entry does not denote a data source!" ); + if(_pDSEntry) + { + DBTreeListUserData* pTreeListData = static_cast<DBTreeListUserData*>(pDSData); + OUString aDSName = GetEntryText(_pDSEntry); + + if ( pTreeListData ) + _rConnection = pTreeListData->xConnection; + + if ( !_rConnection.is() && pTreeListData ) + { + // show the "connecting to ..." status + OUString sConnecting(DBA_RES(STR_CONNECTING_DATASOURCE)); + sConnecting = sConnecting.replaceFirst("$name$", aDSName); + BrowserViewStatusDisplay aShowStatus(static_cast<UnoDataBrowserView*>(getView()), sConnecting); + + // build a string showing context information in case of error + OUString sConnectingContext(DBA_RES(STR_COULDNOTCONNECT_DATASOURCE)); + sConnectingContext = sConnectingContext.replaceFirst("$name$", aDSName); + + // connect + _rConnection.reset( + connect( getDataSourceAccessor( _pDSEntry ), sConnectingContext, nullptr ), + SharedConnection::TakeOwnership + ); + + // remember the connection + pTreeListData->xConnection = _rConnection; + } + } + + return _rConnection.is(); +} + +IMPL_LINK( SbaTableQueryBrowser, OnTreeEntryCompare, const SvSortData&, _rSortData, sal_Int32 ) +{ + const SvTreeListEntry* pLHS = _rSortData.pLeft; + const SvTreeListEntry* pRHS = _rSortData.pRight; + OSL_ENSURE(pLHS && pRHS, "SbaTableQueryBrowser::OnTreeEntryCompare: invalid tree entries!"); + // we want the table entry and the end so we have to do a check + + if (isContainer(pRHS)) + { + // don't use getEntryType (directly or indirecly) for the LHS: + // LHS is currently being inserted, so it is not "completely valid" at the moment + + const EntryType eRight = getEntryType(pRHS); + if (etTableContainer == eRight) + // every other container should be placed _before_ the bookmark container + return -1; + + const OUString sLeft = m_pTreeView->getListBox().GetEntryText(const_cast<SvTreeListEntry*>(pLHS)); + + EntryType eLeft = etTableContainer; + if (DBA_RES(RID_STR_TABLES_CONTAINER) == sLeft) + eLeft = etTableContainer; + else if (DBA_RES(RID_STR_QUERIES_CONTAINER) == sLeft) + eLeft = etQueryContainer; + + if ( eLeft == eRight ) + return 0; + + if ( ( eLeft == etTableContainer ) && ( eRight == etQueryContainer ) ) + return 1; + + if ( ( eLeft == etQueryContainer ) && ( eRight == etTableContainer ) ) + return -1; + + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::OnTreeEntryCompare: unexpected case!" ); + return 0; + } + + const SvLBoxString* pLeftTextItem = static_cast<const SvLBoxString*>(pLHS->GetFirstItem(SvLBoxItemType::String)); + const SvLBoxString* pRightTextItem = static_cast<const SvLBoxString*>(pRHS->GetFirstItem(SvLBoxItemType::String)); + OSL_ENSURE(pLeftTextItem && pRightTextItem, "SbaTableQueryBrowser::OnTreeEntryCompare: invalid text items!"); + + OUString sLeftText = pLeftTextItem->GetText(); + OUString sRightText = pRightTextItem->GetText(); + + sal_Int32 nCompareResult = 0; // equal by default + + if (m_xCollator.is()) + { + try + { + nCompareResult = m_xCollator->compareString(sLeftText, sRightText); + } + catch(const Exception&) + { + } + } + else + // default behaviour if we do not have a collator -> do the simple string compare + nCompareResult = sLeftText.compareTo(sRightText); + + return nCompareResult; +} + +void SbaTableQueryBrowser::implAdministrate( SvTreeListEntry* _pApplyTo ) +{ + OSL_PRECOND( _pApplyTo, "SbaTableQueryBrowser::implAdministrate: illegal entry!" ); + if ( !_pApplyTo ) + return; + + try + { + // get the desktop object + Reference< XDesktop2 > xFrameLoader = Desktop::create( getORB() ); + + // the initial selection + SvTreeListEntry* pTopLevelSelected = _pApplyTo; + while (pTopLevelSelected && m_pTreeView->getListBox().GetParent(pTopLevelSelected)) + pTopLevelSelected = m_pTreeView->getListBox().GetParent(pTopLevelSelected); + OUString sInitialSelection; + if (pTopLevelSelected) + sInitialSelection = getDataSourceAccessor( pTopLevelSelected ); + + Reference< XDataSource > xDataSource( getDataSourceByName( sInitialSelection, getFrameWeld(), getORB(), nullptr ) ); + Reference< XModel > xDocumentModel( getDataSourceOrModel( xDataSource ), UNO_QUERY ); + + if ( xDocumentModel.is() ) + { + Reference< XInteractionHandler2 > xInteractionHandler( + InteractionHandler::createWithParent(getORB(), nullptr) ); + + ::comphelper::NamedValueCollection aLoadArgs; + aLoadArgs.put( "Model", xDocumentModel ); + aLoadArgs.put( "InteractionHandler", xInteractionHandler ); + aLoadArgs.put( "MacroExecutionMode", MacroExecMode::USE_CONFIG ); + + Sequence< PropertyValue > aLoadArgPV; + aLoadArgs >>= aLoadArgPV; + + xFrameLoader->loadComponentFromURL( + xDocumentModel->getURL(), + "_default", + FrameSearchFlag::ALL | FrameSearchFlag::GLOBAL, + aLoadArgPV + ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +bool SbaTableQueryBrowser::requestQuickHelp( const SvTreeListEntry* _pEntry, OUString& _rText ) const +{ + const DBTreeListUserData* pData = static_cast< const DBTreeListUserData* >( _pEntry->GetUserData() ); + if ( ( pData->eType == etDatasource ) && !pData->sAccessor.isEmpty() ) + { + _rText = ::svt::OFileNotation( pData->sAccessor ).get( ::svt::OFileNotation::N_SYSTEM ); + return true; + } + return false; +} + +OUString SbaTableQueryBrowser::getContextMenuResourceName( Control& _rControl ) const +{ + OSL_PRECOND( &m_pTreeView->getListBox() == &_rControl, + "SbaTableQueryBrowser::getContextMenuResourceName: where does this come from?" ); + if ( &m_pTreeView->getListBox() != &_rControl ) + return OUString(); + + return "explorer"; +} + +IController& SbaTableQueryBrowser::getCommandController() +{ + return *this; +} + +::comphelper::OInterfaceContainerHelper2* SbaTableQueryBrowser::getContextMenuInterceptors() +{ + return &m_aContextMenuInterceptors; +} + +Any SbaTableQueryBrowser::getCurrentSelection( Control& _rControl ) const +{ + OSL_PRECOND( &m_pTreeView->getListBox() == &_rControl, + "SbaTableQueryBrowser::getCurrentSelection: where does this come from?" ); + + if ( &m_pTreeView->getListBox() != &_rControl ) + return Any(); + + SvTreeListEntry* pSelected = m_pTreeView->getListBox().FirstSelected(); + if ( !pSelected ) + return Any(); + + OSL_ENSURE( m_pTreeView->getListBox().NextSelected( pSelected ) == nullptr, + "SbaTableQueryBrowser::getCurrentSelection: single-selection is expected here!" ); + + NamedDatabaseObject aSelectedObject; + DBTreeListUserData* pData = static_cast< DBTreeListUserData* >( pSelected->GetUserData() ); + aSelectedObject.Type = static_cast< sal_Int32 >( pData->eType ); + + switch ( aSelectedObject.Type ) + { + case DatabaseObject::QUERY: + case DatabaseObject::TABLE: + aSelectedObject.Name = m_pTreeView->getListBox().GetEntryText( pSelected ); + break; + + case DatabaseObjectContainer::DATA_SOURCE: + case DatabaseObjectContainer::QUERIES: + case DatabaseObjectContainer::TABLES: + aSelectedObject.Name = getDataSourceAccessor( pSelected ); + break; + + default: + SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::getCurrentSelection: invalid (unexpected) object type!" ); + break; + } + + return makeAny( aSelectedObject ); +} + +bool SbaTableQueryBrowser::implGetQuerySignature( OUString& _rCommand, bool& _bEscapeProcessing ) +{ + _rCommand.clear(); + _bEscapeProcessing = false; + + try + { + // contain the dss (data source signature) of the form + OUString sDataSourceName; + OUString sCommand; + sal_Int32 nCommandType = CommandType::COMMAND; + Reference< XPropertySet > xRowsetProps( getRowSet(), UNO_QUERY ); + ODataAccessDescriptor aDesc( xRowsetProps ); + sDataSourceName = aDesc.getDataSource(); + aDesc[ DataAccessDescriptorProperty::Command ] >>= sCommand; + aDesc[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType; + + // do we need to do anything? + if ( CommandType::QUERY != nCommandType ) + return false; + + // get the query object + Reference< XQueryDefinitionsSupplier > xSuppQueries; + Reference< XNameAccess > xQueries; + Reference< XPropertySet > xQuery; + m_xDatabaseContext->getByName( sDataSourceName ) >>= xSuppQueries; + if ( xSuppQueries.is() ) + xQueries = xSuppQueries->getQueryDefinitions(); + if ( xQueries.is() ) + xQueries->getByName( sCommand ) >>= xQuery; + OSL_ENSURE( xQuery.is(), "SbaTableQueryBrowser::implGetQuerySignature: could not retrieve the query object!" ); + + // get the two properties we need + if ( xQuery.is() ) + { + xQuery->getPropertyValue( PROPERTY_COMMAND ) >>= _rCommand; + _bEscapeProcessing = ::cppu::any2bool( xQuery->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) ); + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return false; +} + +void SbaTableQueryBrowser::frameAction(const css::frame::FrameActionEvent& aEvent) +{ + if (aEvent.Frame == m_xCurrentFrameParent) + { + if(aEvent.Action == FrameAction_COMPONENT_DETACHING) + implRemoveStatusListeners(); + else if (aEvent.Action == FrameAction_COMPONENT_REATTACHED) + connectExternalDispatches(); + } + else + SbaXDataBrowserController::frameAction(aEvent); + +} + +void SbaTableQueryBrowser::clearGridColumns(const Reference< XNameContainer >& _xColContainer) +{ + // first we have to clear the grid + Reference< XInterface > xColumn; + const Sequence<OUString> aColNames = _xColContainer->getElementNames(); + for (const OUString& rName : aColNames) + { + _xColContainer->getByName(rName) >>= xColumn; + _xColContainer->removeByName(rName); + ::comphelper::disposeComponent(xColumn); + } +} + +void SbaTableQueryBrowser::loadMenu(const Reference< XFrame >& _xFrame) +{ + if ( m_bShowMenu ) + { + OGenericUnoController::loadMenu(_xFrame); + } + else if ( !m_bPreview ) + { + Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(_xFrame); + + if ( xLayoutManager.is() ) + { + xLayoutManager->lock(); + xLayoutManager->createElement( "private:resource/toolbar/toolbar" ); + xLayoutManager->unlock(); + xLayoutManager->doLayout(); + } + onLoadedMenu( xLayoutManager ); + } +} + +OUString SbaTableQueryBrowser::getPrivateTitle() const +{ + OUString sTitle; + if ( m_pCurrentlyDisplayed ) + { + SvTreeListEntry* pContainer = m_pTreeView->GetTreeModel()->GetParent(m_pCurrentlyDisplayed); + // get the entry for the datasource + SvTreeListEntry* pConnection = implGetConnectionEntry(pContainer); + OUString sName = m_pTreeView->getListBox().GetEntryText(m_pCurrentlyDisplayed); + sTitle = GetEntryText( pConnection ); + INetURLObject aURL(sTitle); + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + sTitle = aURL.getBase(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::WithCharset); + if ( !sName.isEmpty() ) + { + sName += " - " + sTitle; + sTitle = sName; + } + } + + return sTitle; +} + +bool SbaTableQueryBrowser::preReloadForm() +{ + bool bIni = false; + if ( !m_pCurrentlyDisplayed ) + { + // switch the grid to design mode while loading + getBrowserView()->getGridControl()->setDesignMode(true); + // we had an invalid statement so we need to connect the column models + Reference<XPropertySet> xRowSetProps(getRowSet(),UNO_QUERY); + svx::ODataAccessDescriptor aDesc(xRowSetProps); + // extract the props + OUString sDataSource; + OUString sCommand; + sal_Int32 nCommandType = CommandType::COMMAND; + bool bEscapeProcessing = true; + extractDescriptorProps(aDesc, sDataSource, sCommand, nCommandType, bEscapeProcessing); + if ( !sDataSource.isEmpty() && !sCommand.isEmpty() && (-1 != nCommandType) ) + { + SvTreeListEntry* pDataSource = nullptr; + SvTreeListEntry* pCommandType = nullptr; + m_pCurrentlyDisplayed = getObjectEntry( sDataSource, sCommand, nCommandType, &pDataSource, &pCommandType ); + bIni = true; + } + } + return bIni; +} + +void SbaTableQueryBrowser::postReloadForm() +{ + InitializeGridModel(getFormComponent()); + LoadFinished(true); +} + +Reference< XEmbeddedScripts > SAL_CALL SbaTableQueryBrowser::getScriptContainer() +{ + // update our database document + Reference< XModel > xDocument; + try + { + Reference< XPropertySet > xCursorProps( getRowSet(), UNO_QUERY_THROW ); + Reference< XConnection > xConnection( xCursorProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ), UNO_QUERY ); + if ( xConnection.is() ) + { + Reference< XChild > xChild( xConnection, UNO_QUERY_THROW ); + Reference< XDocumentDataSource > xDataSource( xChild->getParent(), UNO_QUERY_THROW ); + xDocument.set( xDataSource->getDatabaseDocument(), UNO_QUERY_THROW ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + Reference< XEmbeddedScripts > xScripts( xDocument, UNO_QUERY ); + OSL_ENSURE( xScripts.is() || !xDocument.is(), + "SbaTableQueryBrowser::getScriptContainer: invalid database document!" ); + return xScripts; +} + +void SAL_CALL SbaTableQueryBrowser::registerContextMenuInterceptor( const Reference< XContextMenuInterceptor >& Interceptor ) +{ + if ( Interceptor.is() ) + m_aContextMenuInterceptors.addInterface( Interceptor ); +} + +void SAL_CALL SbaTableQueryBrowser::releaseContextMenuInterceptor( const Reference< XContextMenuInterceptor >& Interceptor ) +{ + if ( Interceptor.is() ) + m_aContextMenuInterceptors.removeInterface( Interceptor ); +} + +void SAL_CALL SbaTableQueryBrowser::registeredDatabaseLocation( const DatabaseRegistrationEvent& Event ) +{ + SolarMutexGuard aGuard; + implAddDatasource( Event.Name, SharedConnection() ); +} + +void SbaTableQueryBrowser::impl_cleanupDataSourceEntry( const OUString& _rDataSourceName ) +{ + // get the top-level representing the removed data source + SvTreeListEntry* pDataSourceEntry = m_pTreeView->getListBox().FirstChild( nullptr ); + while ( pDataSourceEntry ) + { + if ( m_pTreeView->getListBox().GetEntryText( pDataSourceEntry ) == _rDataSourceName ) + break; + + pDataSourceEntry = pDataSourceEntry->NextSibling(); + } + + OSL_ENSURE( pDataSourceEntry, "SbaTableQueryBrowser::impl_cleanupDataSourceEntry: do not know this data source!" ); + if ( !pDataSourceEntry ) + return; + + if ( isSelected( pDataSourceEntry ) ) + { // a table or query belonging to the deleted data source is currently being displayed. + OSL_ENSURE( m_pTreeView->getListBox().GetRootLevelParent( m_pCurrentlyDisplayed ) == pDataSourceEntry, + "SbaTableQueryBrowser::impl_cleanupDataSourceEntry: inconsistence (1)!" ); + unloadAndCleanup(); + } + else + OSL_ENSURE( + ( nullptr == m_pCurrentlyDisplayed ) + || ( m_pTreeView->getListBox().GetRootLevelParent( m_pCurrentlyDisplayed ) != pDataSourceEntry ), + "SbaTableQueryBrowser::impl_cleanupDataSourceEntry: inconsistence (2)!"); + + // delete any user data of the child entries of the to-be-removed entry + std::pair<SvTreeListEntries::const_iterator, SvTreeListEntries::const_iterator> aIters = + m_pTreeView->GetTreeModel()->GetChildIterators(pDataSourceEntry); + + SvTreeListEntries::const_iterator it = aIters.first, itEnd = aIters.second; + + for (; it != itEnd; ++it) + { + SvTreeListEntry* pEntry = (*it).get(); + const DBTreeListUserData* pData = static_cast<const DBTreeListUserData*>(pEntry->GetUserData()); + pEntry->SetUserData(nullptr); + delete pData; + } + + // remove the entry + DBTreeListUserData* pData = static_cast< DBTreeListUserData* >( pDataSourceEntry->GetUserData() ); + pDataSourceEntry->SetUserData( nullptr ); + delete pData; + m_pTreeView->GetTreeModel()->Remove( pDataSourceEntry ); +} + +void SAL_CALL SbaTableQueryBrowser::revokedDatabaseLocation( const DatabaseRegistrationEvent& Event ) +{ + SolarMutexGuard aGuard; + + impl_cleanupDataSourceEntry( Event.Name ); + + // maybe the object which is part of the document data source has been removed + checkDocumentDataSource(); +} + +void SAL_CALL SbaTableQueryBrowser::changedDatabaseLocation( const DatabaseRegistrationEvent& Event ) +{ + SolarMutexGuard aGuard; + + // in case the data source was expanded, and connected, we need to clean it up + // for simplicity, just do as if the data source were completely removed and re-added + impl_cleanupDataSourceEntry( Event.Name ); + implAddDatasource( Event.Name, SharedConnection() ); +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |