diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /dbaccess/source/ui/relationdesign | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dbaccess/source/ui/relationdesign')
7 files changed, 1647 insertions, 0 deletions
diff --git a/dbaccess/source/ui/relationdesign/RTableConnection.cxx b/dbaccess/source/ui/relationdesign/RTableConnection.cxx new file mode 100644 index 000000000..b4bc2cfe1 --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RTableConnection.cxx @@ -0,0 +1,117 @@ +/* -*- 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 "RTableConnection.hxx" +#include <RTableConnectionData.hxx> +#include <RelationTableView.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <ConnectionLine.hxx> + +using namespace dbaui; +ORelationTableConnection::ORelationTableConnection( ORelationTableView* pContainer, + const TTableConnectionData::value_type& pTabConnData ) + :OTableConnection( pContainer, pTabConnData ) +{ +} + +ORelationTableConnection::ORelationTableConnection( const ORelationTableConnection& rConn ) + : VclReferenceBase(), OTableConnection( rConn ) +{ + // no own members, thus the base class functionality is enough +} + +ORelationTableConnection& ORelationTableConnection::operator=( const ORelationTableConnection& rConn ) +{ + // this doesn't change anything, since the base class tests this, too and I don't have my own members to copy + if (&rConn == this) + return *this; + + OTableConnection::operator=( rConn ); + return *this; +} + +void ORelationTableConnection::Draw(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) +{ + OTableConnection::Draw(rRenderContext, rRect); + ORelationTableConnectionData* pData = static_cast< ORelationTableConnectionData* >(GetData().get()); + if (pData && (pData->GetCardinality() == Cardinality::Undefined)) + return; + + // search lines for top line + tools::Rectangle aBoundingRect; + long nTop = GetBoundingRect().Bottom(); + long nTemp; + + const OConnectionLine* pTopLine = nullptr; + const std::vector<std::unique_ptr<OConnectionLine>>& rConnLineList = GetConnLineList(); + + for (auto const& elem : rConnLineList) + { + if( elem->IsValid() ) + { + aBoundingRect = elem->GetBoundingRect(); + nTemp = aBoundingRect.Top(); + if(nTemp < nTop) + { + nTop = nTemp; + pTopLine = elem.get(); + } + } + } + + // cardinality + if (!pTopLine) + return; + + tools::Rectangle aSourcePos = pTopLine->GetSourceTextPos(); + tools::Rectangle aDestPos = pTopLine->GetDestTextPos(); + + OUString aSourceText; + OUString aDestText; + + switch (pData->GetCardinality()) + { + case Cardinality::OneMany: + aSourceText = "1"; + aDestText = "n"; + break; + + case Cardinality::ManyOne: + aSourceText = "n"; + aDestText = "1"; + break; + + case Cardinality::OneOne: + aSourceText = "1"; + aDestText = "1"; + break; + default: break; + } + + if (IsSelected()) + rRenderContext.SetTextColor(Application::GetSettings().GetStyleSettings().GetHighlightColor()); + else + rRenderContext.SetTextColor(Application::GetSettings().GetStyleSettings().GetWindowTextColor()); + + rRenderContext.DrawText(aSourcePos, aSourceText, DrawTextFlags::Clip | DrawTextFlags::Center | DrawTextFlags::Bottom); + rRenderContext.DrawText(aDestPos, aDestText, DrawTextFlags::Clip | DrawTextFlags::Center | DrawTextFlags::Bottom); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/relationdesign/RTableConnection.hxx b/dbaccess/source/ui/relationdesign/RTableConnection.hxx new file mode 100644 index 000000000..dabdfe47d --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RTableConnection.hxx @@ -0,0 +1,42 @@ +/* -*- 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_RELATIONDESIGN_RTABLECONNECTION_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_RELATIONDESIGN_RTABLECONNECTION_HXX + +#include <TableConnection.hxx> + +namespace dbaui +{ + class ORelationTableView; + class ORelationTableConnection : public OTableConnection + { + public: + ORelationTableConnection( ORelationTableView* pContainer, const TTableConnectionData::value_type& pTabConnData ); + ORelationTableConnection( const ORelationTableConnection& rConn ); + // important comment to the CopyConstructor see OTableConnection(const OTableConnection&) + + ORelationTableConnection& operator=( const ORelationTableConnection& rConn ); + + virtual void Draw(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + using OTableConnection::Draw; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_RELATIONDESIGN_RTABLECONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/relationdesign/RTableConnectionData.cxx b/dbaccess/source/ui/relationdesign/RTableConnectionData.cxx new file mode 100644 index 000000000..e9b87f28e --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RTableConnectionData.cxx @@ -0,0 +1,399 @@ +/* -*- 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 <RTableConnectionData.hxx> +#include <com/sun/star/sdbc/KeyRule.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XDrop.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <strings.hrc> +#include <strings.hxx> +#include <core_resource.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <osl/diagnose.h> + +using namespace dbaui; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +ORelationTableConnectionData::ORelationTableConnectionData() + :OTableConnectionData() + ,m_nUpdateRules(KeyRule::NO_ACTION) + ,m_nDeleteRules(KeyRule::NO_ACTION) + ,m_nCardinality(Cardinality::Undefined) +{ +} + +ORelationTableConnectionData::ORelationTableConnectionData( const TTableWindowData::value_type& _pReferencingTable, + const TTableWindowData::value_type& _pReferencedTable, + const OUString& rConnName ) + :OTableConnectionData( _pReferencingTable, _pReferencedTable ) + ,m_nUpdateRules(KeyRule::NO_ACTION) + ,m_nDeleteRules(KeyRule::NO_ACTION) + ,m_nCardinality(Cardinality::Undefined) +{ + m_aConnName = rConnName; + + if ( !m_aConnName.isEmpty() ) + SetCardinality(); +} + +ORelationTableConnectionData::ORelationTableConnectionData( const ORelationTableConnectionData& rConnData ) + :OTableConnectionData( rConnData ) +{ + *this = rConnData; +} + +ORelationTableConnectionData::~ORelationTableConnectionData() +{ +} + +void ORelationTableConnectionData::DropRelation() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + // delete relation + Reference< XIndexAccess> xKeys = getReferencingTable()->getKeys(); + if( !(!m_aConnName.isEmpty() && xKeys.is()) ) + return; + + const sal_Int32 nCount = xKeys->getCount(); + for(sal_Int32 i = 0;i < nCount;++i) + { + Reference< XPropertySet> xKey(xKeys->getByIndex(i),UNO_QUERY); + OSL_ENSURE(xKey.is(),"Key is not valid!"); + if(xKey.is()) + { + OUString sName; + xKey->getPropertyValue(PROPERTY_NAME) >>= sName; + if(sName == m_aConnName) + { + Reference< XDrop> xDrop(xKeys,UNO_QUERY); + OSL_ENSURE(xDrop.is(),"can't drop key because we haven't a drop interface!"); + if(xDrop.is()) + xDrop->dropByIndex(i); + break; + } + } + } +} + +void ORelationTableConnectionData::ChangeOrientation() +{ + // exchange Source- and DestFieldName of the lines + OUString sTempString; + for (auto const& elem : m_vConnLineData) + { + sTempString = elem->GetSourceFieldName(); + elem->SetSourceFieldName( elem->GetDestFieldName() ); + elem->SetDestFieldName( sTempString ); + } + + // adapt member + TTableWindowData::value_type pTemp = m_pReferencingTable; + m_pReferencingTable = m_pReferencedTable; + m_pReferencedTable = pTemp; +} + +void ORelationTableConnectionData::SetCardinality() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + m_nCardinality = Cardinality::Undefined; + + if( IsSourcePrimKey() ) + { + if( IsDestPrimKey() ) + m_nCardinality = Cardinality::OneOne; + else + m_nCardinality = Cardinality::OneMany; + } + + if( IsDestPrimKey() ) + { + if( !IsSourcePrimKey() ) + m_nCardinality = Cardinality::ManyOne; + } + +} + +bool ORelationTableConnectionData::checkPrimaryKey(const Reference< XPropertySet>& i_xTable,EConnectionSide _eEConnectionSide) const +{ + // check if Table has the primary key column depending on _eEConnectionSide + sal_uInt16 nPrimKeysCount = 0, + nValidLinesCount = 0; + const Reference< XNameAccess> xKeyColumns = dbtools::getPrimaryKeyColumns_throw(i_xTable); + if ( xKeyColumns.is() ) + { + Sequence< OUString> aKeyColumns = xKeyColumns->getElementNames(); + const OUString* pKeyIter = aKeyColumns.getConstArray(); + const OUString* pKeyEnd = pKeyIter + aKeyColumns.getLength(); + + for(;pKeyIter != pKeyEnd;++pKeyIter) + { + for (auto const& elem : m_vConnLineData) + { + ++nValidLinesCount; + if ( elem->GetFieldName(_eEConnectionSide) == *pKeyIter ) + { + ++nPrimKeysCount; + break; + } + } + } + if ( nPrimKeysCount != aKeyColumns.getLength() ) + return false; + } + return nPrimKeysCount && nPrimKeysCount == nValidLinesCount; +} + +void ORelationTableConnectionData::IsConnectionPossible() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + // if the SourceFields are a PrimKey, it's only the orientation which is wrong + if ( IsSourcePrimKey() && !IsDestPrimKey() ) + ChangeOrientation(); +} + +void ORelationTableConnectionData::CopyFrom(const OTableConnectionData& rSource) +{ + // retract to the (non-virtual) operator= like in the base class + *this = *static_cast<const ORelationTableConnectionData*>(&rSource); +} + +ORelationTableConnectionData& ORelationTableConnectionData::operator=( const ORelationTableConnectionData& rConnData ) +{ + if (&rConnData == this) + return *this; + + OTableConnectionData::operator=( rConnData ); + m_nUpdateRules = rConnData.GetUpdateRules(); + m_nDeleteRules = rConnData.GetDeleteRules(); + m_nCardinality = rConnData.GetCardinality(); + + return *this; +} + +namespace dbaui +{ +bool operator==(const ORelationTableConnectionData& lhs, const ORelationTableConnectionData& rhs) +{ + bool bEqual = (lhs.m_nUpdateRules == rhs.m_nUpdateRules) + && (lhs.m_nDeleteRules == rhs.m_nDeleteRules) + && (lhs.m_nCardinality == rhs.m_nCardinality) + && (lhs.getReferencingTable() == rhs.getReferencingTable()) + && (lhs.getReferencedTable() == rhs.getReferencedTable()) + && (lhs.m_aConnName == rhs.m_aConnName) + && (lhs.m_vConnLineData.size() == rhs.m_vConnLineData.size()); + + if ( bEqual ) + { + sal_Int32 i = 0; + for (auto const& elem : lhs.m_vConnLineData) + { + if ( *(rhs.m_vConnLineData[i]) != *elem ) + { + bEqual = false; + break; + } + ++i; + } + } + return bEqual; +} + +} + +bool ORelationTableConnectionData::Update() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + // delete old relation + { + DropRelation(); + IsConnectionPossible(); + } + + // reassign the keys because the orientation could be changed + Reference<XPropertySet> xTableProp(getReferencingTable()->getTable()); + Reference< XIndexAccess> xKeys ( getReferencingTable()->getKeys()); + + if ( !xKeys.is() ) + return false; + // create new relation + Reference<XDataDescriptorFactory> xKeyFactory(xKeys,UNO_QUERY); + OSL_ENSURE(xKeyFactory.is(),"No XDataDescriptorFactory Interface!"); + Reference<XAppend> xAppend(xKeyFactory,UNO_QUERY); + OSL_ENSURE(xAppend.is(),"No XAppend Interface!"); + + Reference<XPropertySet> xKey = xKeyFactory->createDataDescriptor(); + OSL_ENSURE(xKey.is(),"Key is null!"); + if ( xKey.is() && xTableProp.is() ) + { + // build a foreign key name + OUString sSourceName; + xTableProp->getPropertyValue(PROPERTY_NAME) >>= sSourceName; + OUString sKeyName = sSourceName + getReferencedTable()->GetTableName(); + + xKey->setPropertyValue(PROPERTY_NAME,makeAny(sKeyName)); + xKey->setPropertyValue(PROPERTY_TYPE,makeAny(KeyType::FOREIGN)); + xKey->setPropertyValue(PROPERTY_REFERENCEDTABLE,makeAny(getReferencedTable()->GetTableName())); + xKey->setPropertyValue(PROPERTY_UPDATERULE, makeAny(GetUpdateRules())); + xKey->setPropertyValue(PROPERTY_DELETERULE, makeAny(GetDeleteRules())); + } + + Reference<XColumnsSupplier> xColSup(xKey,UNO_QUERY); + if ( xColSup.is() ) + { + Reference<XNameAccess> xColumns = xColSup->getColumns(); + Reference<XDataDescriptorFactory> xColumnFactory(xColumns,UNO_QUERY); + Reference<XAppend> xColumnAppend(xColumns,UNO_QUERY); + if ( xColumnFactory.is() ) + { + for (auto const& elem : m_vConnLineData) + { + if(!(elem->GetSourceFieldName().isEmpty() || elem->GetDestFieldName().isEmpty())) + { + Reference<XPropertySet> xColumn = xColumnFactory->createDataDescriptor(); + if ( xColumn.is() ) + { + xColumn->setPropertyValue(PROPERTY_NAME,makeAny(elem->GetSourceFieldName())); + xColumn->setPropertyValue(PROPERTY_RELATEDCOLUMN,makeAny(elem->GetDestFieldName())); + xColumnAppend->appendByDescriptor(xColumn); + } + } + } + + if ( xColumns->hasElements() ) + xAppend->appendByDescriptor(xKey); + } + // to get the key we have to reget it because after append it is no longer valid + } + + // get the name of foreign key; search for columns + m_aConnName.clear(); + xKey.clear(); + bool bDropRelation = false; + for(sal_Int32 i=0;i<xKeys->getCount();++i) + { + xKeys->getByIndex(i) >>= xKey; + OSL_ENSURE(xKey.is(),"Key is not valid!"); + if ( xKey.is() ) + { + OUString sReferencedTable; + xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable; + if ( sReferencedTable == getReferencedTable()->GetTableName() ) + { + xColSup.set(xKey,UNO_QUERY_THROW); + try + { + Reference<XNameAccess> xColumns = xColSup->getColumns(); + Sequence< OUString> aNames = xColumns->getElementNames(); + const OUString* pIter = aNames.getConstArray(); + const OUString* pEnd = pIter + aNames.getLength(); + + Reference<XPropertySet> xColumn; + OUString sName,sRelatedColumn; + for ( ; pIter != pEnd ; ++pIter ) + { + xColumn.set(xColumns->getByName(*pIter),UNO_QUERY_THROW); + xColumn->getPropertyValue(PROPERTY_NAME) >>= sName; + xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn; + + bool bFoundElem = false; + for (auto const& elem : m_vConnLineData) + { + if( elem->GetSourceFieldName() == sName + && elem->GetDestFieldName() == sRelatedColumn ) + { + bFoundElem = true; + break; + } + } + if (!bFoundElem) + break; + } + if ( pIter == pEnd ) + { + xKey->getPropertyValue(PROPERTY_NAME) >>= sName; + m_aConnName = sName; + bDropRelation = !aNames.hasElements(); // the key contains no column, so it isn't valid and we have to drop it + //here we already know our column structure so we don't have to recreate the table connection data + xColSup.clear(); + break; + } + } + catch(Exception&) + { + } + } + } + xKey.clear(); + } + if ( bDropRelation ) + { + DropRelation(); + OUString sError(DBA_RES(STR_QUERY_REL_COULD_NOT_CREATE)); + ::dbtools::throwGenericSQLException(sError,nullptr); + } + + // The fields the relation marks may not be the same as our LineDatas mark after the relation has been updated + if ( xColSup.is() ) + { + OConnectionLineDataVec().swap(m_vConnLineData); + Reference<XNameAccess> xColumns = xColSup->getColumns(); + Sequence< OUString> aNames = xColumns->getElementNames(); + const OUString* pIter = aNames.getConstArray(); + const OUString* pEnd = pIter + aNames.getLength(); + + m_vConnLineData.reserve( aNames.getLength() ); + Reference<XPropertySet> xColumn; + OUString sName,sRelatedColumn; + + for(;pIter != pEnd;++pIter) + { + xColumns->getByName(*pIter) >>= xColumn; + if ( xColumn.is() ) + { + OConnectionLineDataRef pNewData = new OConnectionLineData(); + + xColumn->getPropertyValue(PROPERTY_NAME) >>= sName; + xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn; + + pNewData->SetSourceFieldName(sName); + pNewData->SetDestFieldName(sRelatedColumn); + m_vConnLineData.push_back(pNewData); + } + } + } + // NOTE : the caller is responsible for updating any other objects referencing the old LineDatas (for instance a ConnLine) + + // determine cardinality + SetCardinality(); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/relationdesign/RTableWindow.hxx b/dbaccess/source/ui/relationdesign/RTableWindow.hxx new file mode 100644 index 000000000..3b59a9d9d --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RTableWindow.hxx @@ -0,0 +1,42 @@ +/* -*- 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_RELATIONDESIGN_RTABLEWINDOW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_RELATIONDESIGN_RTABLEWINDOW_HXX + +#include <TableWindow.hxx> + +namespace dbaui +{ + class ORelationTableWindow : public OTableWindow + { + public: + ORelationTableWindow( vcl::Window* pParent,const TTableWindowData::value_type& pTabWinData) + : OTableWindow(pParent, pTabWinData) {} + + /** returns the name which should be used when displaying join or relations + @return + The composed name or the window name. + */ + virtual OUString GetName() const override { return GetComposedName(); } + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_RELATIONDESIGN_RTABLEWINDOW_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/relationdesign/RelationController.cxx b/dbaccess/source/ui/relationdesign/RelationController.cxx new file mode 100644 index 000000000..21a7c9e8b --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RelationController.cxx @@ -0,0 +1,566 @@ +/* -*- 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 <iterator> +#include <map> + +#include <dbu_reghelper.hxx> +#include <uiservices.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <browserids.hxx> +#include <comphelper/types.hxx> +#include <core_resource.hxx> +#include <connectivity/dbtools.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbmetadata.hxx> +#include <sqlmessage.hxx> +#include <RelationController.hxx> +#include <TableWindowData.hxx> +#include <UITools.hxx> +#include <RTableConnectionData.hxx> +#include <RelationTableView.hxx> +#include <RelationDesignView.hxx> +#include <tools/diagnose_ex.h> +#include <osl/thread.hxx> +#include <osl/mutex.hxx> + +#define MAX_THREADS 10 + +extern "C" void createRegistryInfo_ORelationControl() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::ORelationController > aAutoRegistration; +} + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::util; +using namespace ::dbtools; +using namespace ::dbaui; +using namespace ::comphelper; +using namespace ::osl; + +OUString SAL_CALL ORelationController::getImplementationName() +{ + return getImplementationName_Static(); +} + +OUString ORelationController::getImplementationName_Static() +{ + return "org.openoffice.comp.dbu.ORelationDesign"; +} + +Sequence< OUString> ORelationController::getSupportedServiceNames_Static() +{ + Sequence<OUString> aSupported { "com.sun.star.sdb.RelationDesign" }; + return aSupported; +} + +Sequence< OUString> SAL_CALL ORelationController::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +Reference< XInterface > ORelationController::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + return *(new ORelationController(comphelper::getComponentContext(_rxFactory))); +} + +ORelationController::ORelationController(const Reference< XComponentContext >& _rM) + : OJoinController(_rM) + ,m_nThreadEvent(0) + ,m_bRelationsPossible(true) +{ + InvalidateAll(); +} + +ORelationController::~ORelationController() +{ +} + +FeatureState ORelationController::GetState(sal_uInt16 _nId) const +{ + FeatureState aReturn; + aReturn.bEnabled = m_bRelationsPossible; + switch (_nId) + { + case SID_RELATION_ADD_RELATION: + aReturn.bEnabled = !m_vTableData.empty() && isConnected() && isEditable(); + aReturn.bChecked = false; + break; + case ID_BROWSER_SAVEDOC: + aReturn.bEnabled = haveDataSource() && impl_isModified(); + break; + default: + aReturn = OJoinController::GetState(_nId); + break; + } + return aReturn; +} + +void ORelationController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs) +{ + switch(_nId) + { + case ID_BROWSER_SAVEDOC: + { + OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!"); + if(!::dbaui::checkDataSourceAvailable(::comphelper::getString(getDataSource()->getPropertyValue(PROPERTY_NAME)), getORB())) + { + OUString aMessage(DBA_RES(STR_DATASOURCE_DELETED)); + OSQLWarningBox aWarning(getFrameWeld(), aMessage); + aWarning.run(); + } + else + { + // now we save the layout information + // create the output stream + try + { + if ( haveDataSource() && getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION) ) + { + ::comphelper::NamedValueCollection aWindowsData; + saveTableWindows( aWindowsData ); + getDataSource()->setPropertyValue( PROPERTY_LAYOUTINFORMATION, makeAny( aWindowsData.getPropertyValues() ) ); + setModified(false); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + break; + case SID_RELATION_ADD_RELATION: + static_cast<ORelationTableView*>(static_cast<ORelationDesignView*>( getView() )->getTableView())->AddNewRelation(); + break; + default: + OJoinController::Execute(_nId,aArgs); + return; + } + InvalidateFeature(_nId); +} + +void ORelationController::impl_initialize() +{ + OJoinController::impl_initialize(); + + if( !getSdbMetaData().supportsRelations() ) + {// check if this database supports relations + + setEditable(false); + m_bRelationsPossible = false; + { + OUString sTitle(DBA_RES(STR_RELATIONDESIGN)); + sTitle = sTitle.copy(3); + OSQLMessageBox aDlg(getFrameWeld(), sTitle, DBA_RES(STR_RELATIONDESIGN_NOT_AVAILABLE)); + aDlg.run(); + } + disconnect(); + throw SQLException(); + } + + if(!m_bRelationsPossible) + InvalidateAll(); + + // we need a datasource + OSL_ENSURE(haveDataSource(),"ORelationController::initialize: need a datasource!"); + + Reference<XTablesSupplier> xSup(getConnection(),UNO_QUERY); + OSL_ENSURE(xSup.is(),"Connection isn't a XTablesSupplier!"); + if(xSup.is()) + m_xTables = xSup->getTables(); + // load the layoutInformation + loadLayoutInformation(); + try + { + loadData(); + if ( !m_nThreadEvent ) + Application::PostUserEvent(LINK(this, ORelationController, OnThreadFinished)); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + +} + +OUString ORelationController::getPrivateTitle( ) const +{ + OUString sName = getDataSourceName(); + return ::dbaui::getStrippedDatabaseName(getDataSource(),sName); +} + +bool ORelationController::Construct(vcl::Window* pParent) +{ + setView( VclPtr<ORelationDesignView>::Create( pParent, *this, getORB() ) ); + OJoinController::Construct(pParent); + return true; +} + +short ORelationController::saveModified() +{ + short nSaved = RET_YES; + if(haveDataSource() && isModified()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "dbaccess/ui/designsavemodifieddialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("DesignSaveModifiedDialog")); + nSaved = xQuery->run(); + if(nSaved == RET_YES) + Execute(ID_BROWSER_SAVEDOC,Sequence<PropertyValue>()); + } + return nSaved; +} + +void ORelationController::describeSupportedFeatures() +{ + OJoinController::describeSupportedFeatures(); + implDescribeSupportedFeature( ".uno:DBAddRelation", SID_RELATION_ADD_RELATION, CommandGroup::EDIT ); +} + +namespace +{ + class RelationLoader : public ::osl::Thread + { + typedef std::map<OUString, std::shared_ptr<OTableWindowData>, ::comphelper::UStringMixLess> TTableDataHelper; + TTableDataHelper m_aTableData; + TTableConnectionData m_vTableConnectionData; + const Sequence< OUString> m_aTableList; + ORelationController* m_pParent; + const Reference< XDatabaseMetaData> m_xMetaData; + const Reference< XNameAccess > m_xTables; + const sal_Int32 m_nStartIndex; + const sal_Int32 m_nEndIndex; + + public: + RelationLoader(ORelationController* _pParent + ,const Reference< XDatabaseMetaData>& _xMetaData + ,const Reference< XNameAccess >& _xTables + ,const Sequence< OUString>& _aTableList + ,const sal_Int32 _nStartIndex + ,const sal_Int32 _nEndIndex) + :m_aTableData(_xMetaData.is() && _xMetaData->supportsMixedCaseQuotedIdentifiers()) + ,m_aTableList(_aTableList) + ,m_pParent(_pParent) + ,m_xMetaData(_xMetaData) + ,m_xTables(_xTables) + ,m_nStartIndex(_nStartIndex) + ,m_nEndIndex(_nEndIndex) + { + } + + /// Working method which should be overridden. + virtual void SAL_CALL run() override; + virtual void SAL_CALL onTerminated() override; + protected: + virtual ~RelationLoader() override {} + + void loadTableData(const Any& _aTable); + }; + + void SAL_CALL RelationLoader::run() + { + osl_setThreadName("RelationLoader"); + + for(sal_Int32 i = m_nStartIndex; i < m_nEndIndex; ++i) + { + OUString sCatalog,sSchema,sTable; + ::dbtools::qualifiedNameComponents(m_xMetaData, + m_aTableList[i], + sCatalog, + sSchema, + sTable, + ::dbtools::EComposeRule::InDataManipulation); + Any aCatalog; + if ( !sCatalog.isEmpty() ) + aCatalog <<= sCatalog; + + try + { + Reference< XResultSet > xResult = m_xMetaData->getImportedKeys(aCatalog, sSchema,sTable); + if ( xResult.is() && xResult->next() ) + { + ::comphelper::disposeComponent(xResult); + loadTableData(m_xTables->getByName(m_aTableList[i])); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + void SAL_CALL RelationLoader::onTerminated() + { + m_pParent->mergeData(m_vTableConnectionData); + delete this; + } + + void RelationLoader::loadTableData(const Any& _aTable) + { + Reference<XPropertySet> xTableProp(_aTable,UNO_QUERY); + const OUString sSourceName = ::dbtools::composeTableName( m_xMetaData, xTableProp, ::dbtools::EComposeRule::InTableDefinitions, false ); + TTableDataHelper::const_iterator aFind = m_aTableData.find(sSourceName); + if ( aFind == m_aTableData.end() ) + { + aFind = m_aTableData.emplace(sSourceName,std::make_shared<OTableWindowData>(xTableProp,sSourceName, sSourceName, OUString())).first; + aFind->second->ShowAll(false); + } + TTableWindowData::value_type pReferencingTable = aFind->second; + Reference<XIndexAccess> xKeys = pReferencingTable->getKeys(); + const Reference<XKeysSupplier> xKeySup(xTableProp,UNO_QUERY); + + if ( !xKeys.is() && xKeySup.is() ) + { + xKeys = xKeySup->getKeys(); + } + + if ( !xKeys.is() ) + return; + + Reference<XPropertySet> xKey; + const sal_Int32 nCount = xKeys->getCount(); + for(sal_Int32 i = 0 ; i < nCount ; ++i) + { + xKeys->getByIndex(i) >>= xKey; + sal_Int32 nKeyType = 0; + xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType; + if ( KeyType::FOREIGN == nKeyType ) + { + OUString sReferencedTable; + xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable; + + // insert windows + TTableDataHelper::const_iterator aRefFind = m_aTableData.find(sReferencedTable); + if ( aRefFind == m_aTableData.end() ) + { + if ( m_xTables->hasByName(sReferencedTable) ) + { + Reference<XPropertySet> xReferencedTable(m_xTables->getByName(sReferencedTable),UNO_QUERY); + aRefFind = m_aTableData.emplace(sReferencedTable,std::make_shared<OTableWindowData>(xReferencedTable,sReferencedTable, sReferencedTable, OUString())).first; + aRefFind->second->ShowAll(false); + } + else + continue; // table name could not be found so we do not show this table relation + } + TTableWindowData::value_type pReferencedTable = aRefFind->second; + + OUString sKeyName; + xKey->getPropertyValue(PROPERTY_NAME) >>= sKeyName; + // insert connection + auto xTabConnData = std::make_shared<ORelationTableConnectionData>( pReferencingTable, pReferencedTable, sKeyName ); + m_vTableConnectionData.push_back(xTabConnData); + // insert columns + const Reference<XColumnsSupplier> xColsSup(xKey,UNO_QUERY); + OSL_ENSURE(xColsSup.is(),"Key is no XColumnsSupplier!"); + const Reference<XNameAccess> xColumns = xColsSup->getColumns(); + const Sequence< OUString> aNames = xColumns->getElementNames(); + OUString sColumnName,sRelatedName; + for(sal_Int32 j=0;j<aNames.getLength();++j) + { + const Reference<XPropertySet> xPropSet(xColumns->getByName(aNames[j]),UNO_QUERY); + OSL_ENSURE(xPropSet.is(),"Invalid column found in KeyColumns!"); + if ( xPropSet.is() ) + { + xPropSet->getPropertyValue(PROPERTY_NAME) >>= sColumnName; + xPropSet->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedName; + } + xTabConnData->SetConnLine( j, sColumnName, sRelatedName ); + } + // set update/del flags + sal_Int32 nUpdateRule = 0; + sal_Int32 nDeleteRule = 0; + xKey->getPropertyValue(PROPERTY_UPDATERULE) >>= nUpdateRule; + xKey->getPropertyValue(PROPERTY_DELETERULE) >>= nDeleteRule; + + xTabConnData->SetUpdateRules( nUpdateRule ); + xTabConnData->SetDeleteRules( nDeleteRule ); + + // set cardinality + xTabConnData->SetCardinality(); + } + } + } +} + +void ORelationController::mergeData(const TTableConnectionData& _aConnectionData) +{ + ::osl::MutexGuard aGuard( getMutex() ); + + std::copy( _aConnectionData.begin(), _aConnectionData.end(), std::back_inserter( m_vTableConnectionData )); + // here we are finished, so we can collect the table from connection data + for (auto const& elem : m_vTableConnectionData) + { + if ( !existsTable(elem->getReferencingTable()->GetComposedName()) ) + { + m_vTableData.push_back(elem->getReferencingTable()); + } + if ( !existsTable(elem->getReferencedTable()->GetComposedName()) ) + { + m_vTableData.push_back(elem->getReferencedTable()); + } + } + if ( m_nThreadEvent ) + { + --m_nThreadEvent; + if ( !m_nThreadEvent ) + Application::PostUserEvent(LINK(this, ORelationController, OnThreadFinished)); + } +} + +IMPL_LINK_NOARG( ORelationController, OnThreadFinished, void*, void ) +{ + ::SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + try + { + getView()->initialize(); // show the windows and fill with our information + getView()->Invalidate(InvalidateFlags::NoErase); + ClearUndoManager(); + setModified(false); // and we are not modified yet + + if(m_vTableData.empty()) + Execute(ID_BROWSER_ADDTABLE,Sequence<PropertyValue>()); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_xWaitObject.reset(); +} + +void ORelationController::loadData() +{ + m_xWaitObject.reset(new weld::WaitObject(getFrameWeld())); + try + { + if ( !m_xTables.is() ) + return; + DatabaseMetaData aMeta(getConnection()); + // this may take some time + const Reference< XDatabaseMetaData> xMetaData = getConnection()->getMetaData(); + const Sequence< OUString> aNames = m_xTables->getElementNames(); + const sal_Int32 nCount = aNames.getLength(); + if ( aMeta.supportsThreads() ) + { + const sal_Int32 nMaxElements = (nCount / MAX_THREADS) +1; + sal_Int32 nStart = 0,nEnd = std::min(nMaxElements,nCount); + while(nStart != nEnd) + { + ++m_nThreadEvent; + RelationLoader* pThread = new RelationLoader(this,xMetaData,m_xTables,aNames,nStart,nEnd); + pThread->createSuspended(); + pThread->setPriority(osl_Thread_PriorityBelowNormal); + pThread->resume(); + nStart = nEnd; + nEnd += nMaxElements; + nEnd = std::min(nEnd,nCount); + } + } + else + { + RelationLoader* pThread = new RelationLoader(this,xMetaData,m_xTables,aNames,0,nCount); + pThread->run(); + pThread->onTerminated(); + } + } + catch(SQLException& e) + { + showError(SQLExceptionInfo(e)); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +TTableWindowData::value_type ORelationController::existsTable(const OUString& _rComposedTableName) const +{ + ::comphelper::UStringMixEqual bCase(true); + for (auto const& elem : m_vTableData) + { + if(bCase(elem->GetComposedName(),_rComposedTableName)) + return elem; + } + return TTableWindowData::value_type(); +} + +void ORelationController::loadLayoutInformation() +{ + try + { + OSL_ENSURE(haveDataSource(),"We need a datasource from our connection!"); + if ( haveDataSource() ) + { + if ( getDataSource()->getPropertySetInfo()->hasPropertyByName(PROPERTY_LAYOUTINFORMATION) ) + { + Sequence<PropertyValue> aWindows; + getDataSource()->getPropertyValue(PROPERTY_LAYOUTINFORMATION) >>= aWindows; + loadTableWindows(aWindows); + } + } + } + catch(Exception&) + { + } +} + +void ORelationController::reset() +{ + loadLayoutInformation(); + ODataView* pView = getView(); + OSL_ENSURE(pView,"No current view!"); + if(pView) + { + pView->initialize(); + pView->Invalidate(InvalidateFlags::NoErase); + } +} + +bool ORelationController::allowViews() const +{ + return false; +} + +bool ORelationController::allowQueries() const +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/relationdesign/RelationDesignView.cxx b/dbaccess/source/ui/relationdesign/RelationDesignView.cxx new file mode 100644 index 000000000..014579cf7 --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RelationDesignView.cxx @@ -0,0 +1,74 @@ +/* -*- 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 <RelationDesignView.hxx> +#include <RelationTableView.hxx> +#include <RelationController.hxx> +#include <vcl/event.hxx> + +using namespace ::dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +ORelationDesignView::ORelationDesignView(vcl::Window* _pParent, ORelationController& _rController,const Reference< XComponentContext >& _rxContext) + :OJoinDesignView( _pParent, _rController, _rxContext ) +{ +} + +void ORelationDesignView::Construct() +{ + m_pTableView = VclPtr<ORelationTableView>::Create(m_pScrollWindow,this); + OJoinDesignView::Construct(); +} + +void ORelationDesignView::initialize() +{ + m_pTableView->clearLayoutInformation(); + m_pTableView->ReSync(); + + OJoinDesignView::initialize(); +} + +bool ORelationDesignView::PreNotify( NotifyEvent& rNEvt ) +{ + bool bDone = false; + if(rNEvt.GetType() == MouseNotifyEvent::GETFOCUS) + { + if(m_pTableView && !m_pTableView->HasChildPathFocus()) + { + m_pTableView->GrabTabWinFocus(); + bDone = true; + } + } + if(!bDone) + bDone = OJoinDesignView::PreNotify(rNEvt); + return bDone; +} + +void ORelationDesignView::GetFocus() +{ + OJoinDesignView::GetFocus(); + if ( m_pTableView && m_pTableView->IsVisible() && !m_pTableView->GetTabWinMap().empty() ) + m_pTableView->GrabTabWinFocus(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/relationdesign/RelationTableView.cxx b/dbaccess/source/ui/relationdesign/RelationTableView.cxx new file mode 100644 index 000000000..a55222096 --- /dev/null +++ b/dbaccess/source/ui/relationdesign/RelationTableView.cxx @@ -0,0 +1,407 @@ +/* -*- 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 <RelationTableView.hxx> +#include <core_resource.hxx> +#include <browserids.hxx> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <connectivity/dbtools.hxx> +#include <helpids.h> +#include <RelationDesignView.hxx> +#include <JoinController.hxx> +#include <TableWindow.hxx> +#include <TableWindowData.hxx> +#include "RTableConnection.hxx" +#include <RTableConnectionData.hxx> +#include <RelationDlg.hxx> +#include <sqlmessage.hxx> +#include <strings.hrc> +#include <connectivity/dbexception.hxx> +#include "RTableWindow.hxx" +#include <JAccess.hxx> +#include <vcl/stdtext.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + +using namespace dbaui; +using namespace ::dbtools; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +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::accessibility; + +ORelationTableView::ORelationTableView( vcl::Window* pParent, ORelationDesignView* pView ) + :OJoinTableView( pParent, pView ) + , ::comphelper::OContainerListener(m_aMutex) + ,m_pExistingConnection(nullptr) + ,m_bInRemove(false) + +{ + SetHelpId(HID_CTL_RELATIONTAB); +} + +ORelationTableView::~ORelationTableView() +{ + disposeOnce(); +} + +void ORelationTableView::dispose() +{ + if ( m_pContainerListener.is() ) + m_pContainerListener->dispose(); + m_pExistingConnection.clear(); + OJoinTableView::dispose(); +} + +void ORelationTableView::ReSync() +{ + if ( !m_pContainerListener.is() ) + { + Reference< XConnection> xConnection = m_pView->getController().getConnection(); + Reference< XTablesSupplier > xTableSupp( xConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xTables = xTableSupp->getTables(); + Reference< XContainer> xContainer(xTables,uno::UNO_QUERY); + if ( xContainer.is() ) + m_pContainerListener = new ::comphelper::OContainerListenerAdapter(this,xContainer); + } + // Tables could have been hidden in the database, which are part of a relation. Or a table was in layout + // (quite often without a relation) and does not exist anymore. In both cases creation of TabWins will fail + // and all TabWinDatas and related ConnDates should be deleted. + std::vector< OUString> arrInvalidTables; + + // create and insert windows + TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData(); + TTableWindowData::const_reverse_iterator aIter = rTabWinDataList.rbegin(); + for(;aIter != rTabWinDataList.rend();++aIter) + { + TTableWindowData::value_type pData = *aIter; + VclPtr<OTableWindow> pTabWin = createWindow(pData); + + if (!pTabWin->Init()) + { + // initialisation failed, which means this TabWin is not available, therefore, + // it should be cleaned up, including its data in the document + pTabWin->clearListBox(); + pTabWin.disposeAndClear(); + arrInvalidTables.push_back(pData->GetTableName()); + + rTabWinDataList.erase( std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), *aIter), rTabWinDataList.end()); + continue; + } + + GetTabWinMap()[pData->GetComposedName()] = pTabWin; // insert at the beginning, as the Datalist is walked through backward + // if there's no position or size contained in the data -> Default + if (!pData->HasPosition() && !pData->HasSize()) + SetDefaultTabWinPosSize(pTabWin); + + pTabWin->Show(); + } + + // insert connection + TTableConnectionData& rTabConnDataList = m_pView->getController().getTableConnectionData(); + TTableConnectionData::const_reverse_iterator aConIter = rTabConnDataList.rbegin(); + + for(;aConIter != rTabConnDataList.rend();++aConIter) + { + ORelationTableConnectionData* pTabConnData = static_cast<ORelationTableConnectionData*>(aConIter->get()); + if ( !arrInvalidTables.empty() ) + { + // do the tables to the connection exist? + OUString strTabExistenceTest = pTabConnData->getReferencingTable()->GetTableName(); + bool bInvalid = std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end(); + strTabExistenceTest = pTabConnData->getReferencedTable()->GetTableName(); + bInvalid = bInvalid || std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end(); + + if (bInvalid) + { + // no -> bad luck, the connection is gone + rTabConnDataList.erase( std::remove(rTabConnDataList.begin(), rTabConnDataList.end(), *aConIter), rTabConnDataList.end() ); + continue; + } + } + + addConnection( VclPtr<ORelationTableConnection>::Create(this, *aConIter), false ); + } + + if ( !GetTabWinMap().empty() ) + GetTabWinMap().begin()->second->GrabFocus(); +} + +bool ORelationTableView::IsAddAllowed() +{ + + return !m_pView->getController().isReadOnly(); +} + +void ORelationTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest) +{ + // Set LineDataObject based on selected fieldname + // check if relation already exists + OTableWindow* pSourceWin = jxdSource.pListBox->GetTabWin(); + OTableWindow* pDestWin = jxdDest.pListBox->GetTabWin(); + + for(VclPtr<OTableConnection> const & pFirst : getTableConnections()) + { + if((pFirst->GetSourceWin() == pSourceWin && pFirst->GetDestWin() == pDestWin) || + (pFirst->GetSourceWin() == pDestWin && pFirst->GetDestWin() == pSourceWin)) + { + m_pExistingConnection = pFirst; + break; + } + } + // insert table connection into view + TTableConnectionData::value_type pTabConnData = std::make_shared<ORelationTableConnectionData>(pSourceWin->GetData(), + pDestWin->GetData()); + + // the names of the affected fields + OUString sSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry); + OUString sDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry); + + // the number of PKey-Fields in the source + const Reference< XNameAccess> xPrimaryKeyColumns = getPrimaryKeyColumns_throw(pSourceWin->GetData()->getTable()); + bool bAskUser = xPrimaryKeyColumns.is() && Reference< XIndexAccess>(xPrimaryKeyColumns,UNO_QUERY_THROW)->getCount() > 1; + + pTabConnData->SetConnLine( 0, sSourceFieldName, sDestFieldName ); + + if ( bAskUser || m_pExistingConnection ) + m_pCurrentlyTabConnData = pTabConnData; // this implies that we ask the user what to do + else + { + try + { + // hand over data to the database + if( pTabConnData->Update() ) + { + // enter UI-object into ConnList + addConnection( VclPtr<ORelationTableConnection>::Create( this, pTabConnData ) ); + } + } + catch(const SQLException&) + { + throw; + } + catch(const Exception&) + { + OSL_FAIL("ORelationTableView::AddConnection: Exception occurred!"); + } + } +} + +void ORelationTableView::ConnDoubleClicked(VclPtr<OTableConnection>& rConnection) +{ + ORelationDialog aRelDlg(this, rConnection->GetData()); + switch (aRelDlg.run()) + { + case RET_OK: + // successfully updated + rConnection->UpdateLineList(); + // The connection references 1 ConnData and n ConnLines, each ConnData references n LineDatas, each Line exactly 1 LineData + // As the Dialog and the ConnData->Update may have changed the LineDatas we have to restore the consistent state + break; + + case RET_NO: + // tried at least one update, but did not succeed -> the original connection is lost + RemoveConnection(rConnection ,true); + break; + + case RET_CANCEL: + // no break, as nothing happened and we don't need the code below + return; + + } + + Invalidate(InvalidateFlags::NoChildren); +} + +void ORelationTableView::AddNewRelation() +{ + + TTableConnectionData::value_type pNewConnData = std::make_shared<ORelationTableConnectionData>(); + ORelationDialog aRelDlg(this, pNewConnData, true); + + bool bSuccess = (aRelDlg.run() == RET_OK); + if (bSuccess) + { + // already updated by the dialog + // announce it to the document + addConnection( VclPtr<ORelationTableConnection>::Create(this, pNewConnData) ); + } +} + +bool ORelationTableView::RemoveConnection(VclPtr<OTableConnection>& rConn, bool /*_bDelete*/) +{ + ORelationTableConnectionData* pTabConnData = static_cast<ORelationTableConnectionData*>(rConn->GetData().get()); + try + { + if (!m_bInRemove) + pTabConnData->DropRelation(); + return OJoinTableView::RemoveConnection(rConn, true); + } + catch(SQLException& e) + { + getDesignView()->getController().showError(SQLExceptionInfo(e)); + } + catch(Exception&) + { + OSL_FAIL("ORelationTableView::RemoveConnection: Something other than SQLException occurred!"); + } + return false; +} + +void ORelationTableView::AddTabWin(const OUString& _rComposedName, const OUString& rWinName, bool /*bNewTable*/) +{ + OSL_ENSURE(!_rComposedName.isEmpty(),"There must be a table name supplied!"); + OJoinTableView::OTableWindowMap::const_iterator aIter = GetTabWinMap().find(_rComposedName); + + if(aIter != GetTabWinMap().end()) + { + aIter->second->SetZOrder(nullptr, ZOrderFlags::First); + aIter->second->GrabFocus(); + EnsureVisible(aIter->second); + // no new one + return; + } + + // enter the new data structure into DocShell + TTableWindowData::value_type pNewTabWinData(createTableWindowData( _rComposedName, rWinName,rWinName )); + pNewTabWinData->ShowAll(false); + + // link new window into the window list + VclPtr<OTableWindow> pNewTabWin = createWindow( pNewTabWinData ); + if(pNewTabWin->Init()) + { + m_pView->getController().getTableWindowData().push_back( pNewTabWinData); + // when we already have a table with this name insert the full qualified one instead + GetTabWinMap()[_rComposedName] = pNewTabWin; + + SetDefaultTabWinPosSize( pNewTabWin ); + pNewTabWin->Show(); + + modified(); + + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + Any(), + makeAny(pNewTabWin->GetAccessible())); + } + else + { + pNewTabWin->clearListBox(); + pNewTabWin.disposeAndClear(); + } +} + +void ORelationTableView::RemoveTabWin( OTableWindow* pTabWin ) +{ + OSQLWarningBox aDlg(GetFrameWeld(), DBA_RES(STR_QUERY_REL_DELETE_WINDOW), MessBoxStyle::YesNo | MessBoxStyle::DefaultYes); + if (m_bInRemove || aDlg.run() == RET_YES) + { + m_pView->getController().ClearUndoManager(); + OJoinTableView::RemoveTabWin( pTabWin ); + + m_pView->getController().InvalidateFeature(SID_RELATION_ADD_RELATION); + m_pView->getController().InvalidateFeature(ID_BROWSER_UNDO); + m_pView->getController().InvalidateFeature(ID_BROWSER_REDO); + } +} + +void ORelationTableView::lookForUiActivities() +{ + if(m_pExistingConnection) + { + OUString sTitle(DBA_RES(STR_RELATIONDESIGN)); + sTitle = sTitle.copy(3); + OSQLMessageBox aDlg(GetFrameWeld(), DBA_RES(STR_QUERY_REL_EDIT_RELATION), OUString(), MessBoxStyle::NONE); + aDlg.set_title(sTitle); + aDlg.add_button(DBA_RES(STR_QUERY_REL_EDIT), RET_OK); + aDlg.set_default_response(RET_OK); + aDlg.add_button(DBA_RES(STR_QUERY_REL_CREATE), RET_YES); + aDlg.add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + sal_uInt16 nRet = aDlg.run(); + if (nRet == RET_CANCEL) + { + m_pCurrentlyTabConnData.reset(); + } + else if ( nRet == RET_OK ) // EDIT + { + ConnDoubleClicked(m_pExistingConnection); + m_pCurrentlyTabConnData.reset(); + } + m_pExistingConnection = nullptr; + } + if(m_pCurrentlyTabConnData) + { + ORelationDialog aRelDlg(this, m_pCurrentlyTabConnData); + if (aRelDlg.run() == RET_OK) + { + // already updated by the dialog + addConnection( VclPtr<ORelationTableConnection>::Create( this, m_pCurrentlyTabConnData ) ); + } + m_pCurrentlyTabConnData.reset(); + } +} + +VclPtr<OTableWindow> ORelationTableView::createWindow(const TTableWindowData::value_type& _pData) +{ + return VclPtr<ORelationTableWindow>::Create(this,_pData); +} + +bool ORelationTableView::allowQueries() const +{ + return false; +} + +void ORelationTableView::_elementInserted( const container::ContainerEvent& /*_rEvent*/ ) +{ + +} + +void ORelationTableView::_elementRemoved( const container::ContainerEvent& _rEvent ) +{ + m_bInRemove = true; + OUString sName; + if ( _rEvent.Accessor >>= sName ) + { + OTableWindow* pTableWindow = GetTabWindow(sName); + if ( pTableWindow ) + { + m_pView->getController().ClearUndoManager(); + OJoinTableView::RemoveTabWin( pTableWindow ); + + m_pView->getController().InvalidateFeature(SID_RELATION_ADD_RELATION); + m_pView->getController().InvalidateFeature(ID_BROWSER_UNDO); + m_pView->getController().InvalidateFeature(ID_BROWSER_REDO); + } + } + m_bInRemove = false; +} + +void ORelationTableView::_elementReplaced( const container::ContainerEvent& /*_rEvent*/ ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |