summaryrefslogtreecommitdiffstats
path: root/dbaccess/source/ui/querydesign/QueryTableView.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /dbaccess/source/ui/querydesign/QueryTableView.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.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/querydesign/QueryTableView.cxx')
-rw-r--r--dbaccess/source/ui/querydesign/QueryTableView.cxx887
1 files changed, 887 insertions, 0 deletions
diff --git a/dbaccess/source/ui/querydesign/QueryTableView.cxx b/dbaccess/source/ui/querydesign/QueryTableView.cxx
new file mode 100644
index 000000000..1847cfe7b
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTableView.cxx
@@ -0,0 +1,887 @@
+/* -*- 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 <QueryTableView.hxx>
+#include <TableFieldDescription.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+#include <helpids.h>
+#include "QTableWindow.hxx"
+#include "QTableConnection.hxx"
+#include "QTableConnectionData.hxx"
+#include <QueryDesignView.hxx>
+#include "QueryAddTabConnUndoAction.hxx"
+#include "QueryTabWinShowUndoAct.hxx"
+#include <browserids.hxx>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <JAccess.hxx>
+#include <com/sun/star/sdbcx/KeyType.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <connectivity/dbtools.hxx>
+#include <comphelper/sequence.hxx>
+#include "querydlg.hxx"
+#include <core_resource.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+
+using namespace dbaui;
+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;
+
+namespace
+{
+ /** appends a new TabAdd Undo action at controller
+ @param _pView the view which we use
+ @param _pUndoAction the undo action which should be added
+ @param _pConnection the connection for which the undo action should be appended
+ @param _bOwner is the undo action the owner
+ */
+ void addUndoAction( OQueryTableView const * _pView,
+ std::unique_ptr<OQueryTabConnUndoAction> _pUndoAction,
+ OQueryTableConnection* _pConnection,
+ bool _bOwner = false)
+ {
+ _pUndoAction->SetOwnership(_bOwner);
+ _pUndoAction->SetConnection(_pConnection);
+ _pView->getDesignView()->getController().addUndoActionAndInvalidate(std::move(_pUndoAction));
+ }
+ /** openJoinDialog opens the join dialog with this connection data
+ @param _pView the view which we use
+ @param _pConnectionData the connection data
+
+ @return true when OK was pressed otherwise false
+ */
+ bool openJoinDialog(OQueryTableView* _pView,const TTableConnectionData::value_type& _pConnectionData,bool _bSelectableTables)
+ {
+ OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pConnectionData.get());
+
+ DlgQryJoin aDlg(_pView,_pConnectionData,&_pView->GetTabWinMap(),_pView->getDesignView()->getController().getConnection(),_bSelectableTables);
+ bool bOk = aDlg.run() == RET_OK;
+ if( bOk )
+ {
+ pData->SetJoinType(aDlg.GetJoinType());
+ _pView->getDesignView()->getController().setModified(true);
+ }
+
+ return bOk;
+ }
+ /** connectionModified adds an undo action for the modified connection and forces a redraw
+ @param _pView the view which we use
+ @param _pConnection the connection which was modified
+ @param _bAddUndo true when an undo action should be appended
+ */
+ void connectionModified(OQueryTableView* _pView,
+ OTableConnection* _pConnection,
+ bool _bAddUndo)
+ {
+ OSL_ENSURE(_pConnection,"Invalid connection!");
+ _pConnection->UpdateLineList();
+
+ // add an undo action
+ if ( _bAddUndo )
+ addUndoAction( _pView,
+ std::make_unique<OQueryAddTabConnUndoAction>(_pView),
+ static_cast< OQueryTableConnection*>(_pConnection));
+ // redraw
+ _pConnection->RecalcLines();
+ // force an invalidation of the bounding rectangle
+ _pConnection->InvalidateConnection();
+
+ _pView->Invalidate(InvalidateFlags::NoChildren);
+ }
+ void addConnections(OQueryTableView* _pView,
+ const OQueryTableWindow& _rSource,
+ const OQueryTableWindow& _rDest,
+ const Reference<XNameAccess>& _rxSourceForeignKeyColumns)
+ {
+ if ( _rSource.GetData()->isQuery() || _rDest.GetData()->isQuery() )
+ // nothing to do if one of both denotes a query
+ return;
+
+ // we found a table in our view where we can insert some connections
+ // the key columns have a property called RelatedColumn
+ // build OQueryTableConnectionData
+ auto xNewConnData = std::make_shared<OQueryTableConnectionData>( _rSource.GetData(), _rDest.GetData() );
+
+ OUString sRelatedColumn;
+
+ // iterate through all foreignkey columns to create the connections
+ const Sequence<OUString> aKeyCols = _rxSourceForeignKeyColumns->getElementNames();
+ for(const OUString& rElement : aKeyCols)
+ {
+ Reference<XPropertySet> xColumn;
+ if ( !( _rxSourceForeignKeyColumns->getByName(rElement) >>= xColumn ) )
+ {
+ OSL_FAIL( "addConnections: invalid foreign key column!" );
+ continue;
+ }
+
+ xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn;
+
+ {
+ sal_Int32 nFindIndex = ::comphelper::findValue(_rSource.GetOriginalColumns()->getElementNames(),rElement);
+ if(nFindIndex != -1)
+ xNewConnData->SetFieldIndex(JTCS_FROM,nFindIndex+1);
+ else
+ OSL_FAIL("Column not found!");
+ }
+ // get the position inside the table
+ Reference<XNameAccess> xRefColumns = _rDest.GetOriginalColumns();
+ if(xRefColumns.is())
+ {
+ sal_Int32 nFindIndex = ::comphelper::findValue(xRefColumns->getElementNames(),sRelatedColumn);
+ if(nFindIndex != -1)
+ xNewConnData->SetFieldIndex(JTCS_TO,nFindIndex+1);
+ else
+ OSL_FAIL("Column not found!");
+ }
+ xNewConnData->AppendConnLine(rElement,sRelatedColumn);
+
+ // now add the Conn itself
+ ScopedVclPtrInstance< OQueryTableConnection > aNewConn(_pView, xNewConnData);
+ // referring to the local variable is not important, as NotifyQueryTabConn creates a new copy
+ // to add me (if not existent)
+ _pView->NotifyTabConnection(*aNewConn, false);
+ // don't create an Undo-Action for the new connection : the connection is
+ // covered by the Undo-Action for the tabwin, as the "Undo the insert" will
+ // automatically remove all connections adjacent to the win.
+ // (Because of this automatism we would have an ownership ambiguity for
+ // the connection data if we would insert the conn-Undo-Action)
+ }
+ }
+}
+
+OQueryTableView::OQueryTableView( vcl::Window* pParent,OQueryDesignView* pView)
+ : OJoinTableView( pParent,pView)
+{
+ SetHelpId(HID_CTL_QRYDGNTAB);
+}
+
+sal_Int32 OQueryTableView::CountTableAlias(const OUString& rName, sal_Int32& rMax)
+{
+ sal_Int32 nRet = 0;
+
+ OTableWindowMap::const_iterator aIter = GetTabWinMap().find(rName);
+ while(aIter != GetTabWinMap().end())
+ {
+ OUString aNewName = rName + "_" + OUString::number(++nRet);
+ aIter = GetTabWinMap().find(aNewName);
+ }
+
+ rMax = nRet;
+
+ return nRet;
+}
+
+void OQueryTableView::ReSync()
+{
+ TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData();
+ OSL_ENSURE((getTableConnections().empty()) && (GetTabWinMap().empty()),
+ "before calling OQueryTableView::ReSync() please call ClearAll !");
+
+ // I need a collection of all window names that cannot be created so that I do not initialize connections for them.
+ std::vector<OUString> arrInvalidTables;
+
+ TTableWindowData::const_reverse_iterator aIter = rTabWinDataList.rbegin();
+ // Create the window and add it
+
+ for(;aIter != rTabWinDataList.rend();++aIter)
+ {
+ OQueryTableWindowData* pData = static_cast<OQueryTableWindowData*>(aIter->get());
+ VclPtr<OTableWindow> pTabWin = createWindow(*aIter);
+
+ // I don't use ShowTabWin as this adds the window data to the list of documents.
+ // This would be bad as I am getting them from there.
+ // Instead, I do it step by step
+ if (!pTabWin->Init())
+ {
+ // The initialisation has gone wrong, this TabWin is not available, so
+ // I must clean up the data and the document
+ pTabWin->clearListBox();
+ pTabWin.disposeAndClear();
+ arrInvalidTables.push_back(pData->GetAliasName());
+
+ rTabWinDataList.erase( std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), *aIter), rTabWinDataList.end());
+ continue;
+ }
+
+ GetTabWinMap()[pData->GetAliasName()] = pTabWin; // add at the beginning as I am going backwards through the DataList
+ // Use the default if there is no position or size
+ if (!pData->HasPosition() && !pData->HasSize())
+ SetDefaultTabWinPosSize(pTabWin);
+
+ pTabWin->Show();
+ }
+
+ // Add the connections
+ TTableConnectionData& rTabConnDataList = m_pView->getController().getTableConnectionData();
+ TTableConnectionData::const_reverse_iterator aConIter = rTabConnDataList.rbegin();
+
+ for(;aConIter != rTabConnDataList.rend();++aConIter)
+ {
+ OQueryTableConnectionData* pTabConnData = static_cast<OQueryTableConnectionData*>(aConIter->get());
+
+ // do both tables for the connection exist ?
+ OUString strTabExistenceTest = pTabConnData->getReferencingTable()->GetWinName();
+ bool bInvalid = std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
+ strTabExistenceTest = pTabConnData->getReferencedTable()->GetWinName();
+ bInvalid = bInvalid && std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
+
+ if (bInvalid)
+ {
+ // no -> bad luck, no connection
+ rTabConnDataList.erase( std::remove(rTabConnDataList.begin(), rTabConnDataList.end(), *aConIter), rTabConnDataList.end());
+ continue;
+ }
+
+ // adds a new connection to join view and notifies our accessible and invalidates the controller
+ addConnection(VclPtr<OQueryTableConnection>::Create(this, *aConIter));
+ }
+}
+
+void OQueryTableView::ClearAll()
+{
+ OJoinTableView::ClearAll();
+
+ SetUpdateMode(true);
+ m_pView->getController().setModified(true);
+}
+
+VclPtr<OTableWindow> OQueryTableView::createWindow(const TTableWindowData::value_type& _pData)
+{
+ return VclPtr<OQueryTableWindow>::Create(this,_pData);
+}
+
+void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, bool _bCreateUndoAction)
+{
+ // let's first check if I have the connection already
+ OQueryTableConnection* pTabConn = nullptr;
+ const auto& rConnections = getTableConnections();
+ auto aEnd = rConnections.end();
+ auto aIter = std::find( rConnections.begin(),
+ aEnd,
+ VclPtr<OTableConnection>(const_cast<OTableConnection*>(static_cast<const OTableConnection*>(&rNewConn)))
+ );
+ if(aIter == aEnd)
+ {
+ for (auto const& connection : rConnections)
+ {
+ if(*static_cast<OQueryTableConnection*>(connection.get()) == rNewConn)
+ {
+ pTabConn = static_cast<OQueryTableConnection*>(connection.get());
+ break;
+ }
+ }
+ }
+ else
+ pTabConn = static_cast<OQueryTableConnection*>((*aIter).get());
+
+ // no -> insert
+ if (pTabConn == nullptr)
+ {
+ // the new data ...
+ auto pNewData = std::static_pointer_cast<OQueryTableConnectionData>(rNewConn.GetData()->NewInstance());
+ pNewData->CopyFrom(*rNewConn.GetData());
+ VclPtrInstance<OQueryTableConnection> pNewConn(this, pNewData);
+ GetConnection(pNewConn);
+
+ connectionModified(this,pNewConn,_bCreateUndoAction);
+ }
+}
+
+std::shared_ptr<OTableWindowData> OQueryTableView::CreateImpl(const OUString& _rComposedName
+ ,const OUString& _sTableName
+ ,const OUString& _rWinName)
+{
+ return std::make_shared<OQueryTableWindowData>( _rComposedName, _sTableName,_rWinName );
+}
+
+void OQueryTableView::AddTabWin(const OUString& _rTableName, const OUString& _rAliasName, bool bNewTable)
+{
+ // this method has been inherited from the base class, linking back to the parent and which constructs
+ // an Alias and which passes on to my other AddTabWin
+
+ // pity _rTableName is fully qualified, OQueryDesignView expects a string which only
+ // contains schema and tables but no catalog.
+ Reference< XConnection> xConnection = m_pView->getController().getConnection();
+ if(!xConnection.is())
+ return;
+ try
+ {
+ Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
+ OUString sCatalog, sSchema, sTable;
+ ::dbtools::qualifiedNameComponents(xMetaData,
+ _rTableName,
+ sCatalog,
+ sSchema,
+ sTable,
+ ::dbtools::EComposeRule::InDataManipulation);
+ OUString sRealName(sSchema);
+ if (!sRealName.isEmpty())
+ sRealName += ".";
+ sRealName += sTable;
+
+ AddTabWin(_rTableName, sRealName, _rAliasName, bNewTable);
+ }
+ catch(SQLException&)
+ {
+ OSL_FAIL("qualifiedNameComponents");
+ }
+}
+
+// find the table which has a foreign key with this referencedTable name
+static Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,const OUString& _rReferencedTable)
+{
+ if(!_rxKeys.is())
+ return Reference<XPropertySet>();
+
+ // search the one and only primary key
+ const sal_Int32 nCount = _rxKeys->getCount();
+ for(sal_Int32 i=0;i<nCount ;++i)
+ {
+ Reference<XPropertySet> xKey(_rxKeys->getByIndex(i),UNO_QUERY);
+ if(xKey.is())
+ {
+ sal_Int32 nKeyType = 0;
+ xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
+ if(KeyType::FOREIGN == nKeyType)
+ {
+ OUString sReferencedTable;
+ xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable;
+ // TODO check case
+ if(sReferencedTable == _rReferencedTable)
+ return xKey;
+ }
+ }
+ }
+ return Reference<XPropertySet>();
+}
+
+void OQueryTableView::AddTabWin(const OUString& _rComposedName, const OUString& _rTableName, const OUString& strAlias, bool bNewTable)
+{
+ OSL_ENSURE(!_rTableName.isEmpty() || !strAlias.isEmpty(), "OQueryTableView::AddTabWin : no tables or aliases !");
+ // If the table is not set, then it is a dummy window, but at least the alias must be set
+
+ // build a new data structure
+ // first check if this already has its data
+ bool bAppend = bNewTable;
+ TTableWindowData::value_type pNewTabWinData;
+ TTableWindowData& rWindowData = getDesignView()->getController().getTableWindowData();
+ bool bFoundElem = false;
+ for (auto const& elem : rWindowData)
+ {
+ pNewTabWinData = elem;
+ if (pNewTabWinData && pNewTabWinData->GetWinName() == strAlias && pNewTabWinData->GetComposedName() == _rComposedName && pNewTabWinData->GetTableName() == _rTableName)
+ {
+ bFoundElem = true;
+ break;
+ }
+ }
+ if ( !bAppend )
+ bAppend = !bFoundElem;
+ if ( bAppend )
+ pNewTabWinData = createTableWindowData(_rComposedName, _rTableName, strAlias);
+ // I do not need to add TabWinData to the DocShell list, ShowTabWin does that.
+
+ // Create a new window
+ VclPtr<OQueryTableWindow> pNewTabWin = static_cast<OQueryTableWindow*>(createWindow(pNewTabWinData).get());
+ // No need to initialize, as that happens in ShowTabWin
+
+ // New UndoAction
+ std::unique_ptr<OQueryTabWinShowUndoAct> pUndoAction(new OQueryTabWinShowUndoAct(this));
+ pUndoAction->SetTabWin(pNewTabWin); // Window
+ bool bSuccess = ShowTabWin(pNewTabWin, pUndoAction.get(), bAppend);
+ if(!bSuccess)
+ {
+ // reset table window
+ pUndoAction->SetTabWin(nullptr);
+ pUndoAction->SetOwnership(false);
+ return;
+ }
+
+ // Show the relations between the individual tables
+ OTableWindowMap& rTabWins = GetTabWinMap();
+ if(bNewTable && !rTabWins.empty() && !_rTableName.isEmpty())
+ {
+ modified();
+ if ( m_pAccessible )
+ m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
+ Any(),
+ makeAny(pNewTabWin->GetAccessible())
+ );
+
+ do {
+
+ if ( pNewTabWin->GetData()->isQuery() )
+ break;
+
+ try
+ {
+ // find relations between the table and the tables already inserted
+ Reference< XIndexAccess> xKeyIndex = pNewTabWin->GetData()->getKeys();
+ if ( !xKeyIndex.is() )
+ break;
+
+ Reference<XNameAccess> xFKeyColumns;
+ OUString aReferencedTable;
+ Reference<XColumnsSupplier> xColumnsSupplier;
+
+ const sal_Int32 nKeyCount = xKeyIndex->getCount();
+ for ( sal_Int32 i=0; i<nKeyCount ; ++i )
+ {
+ Reference< XPropertySet > xProp( xKeyIndex->getByIndex(i), UNO_QUERY_THROW );
+ xColumnsSupplier.set( xProp, UNO_QUERY_THROW );
+ xFKeyColumns.set( xColumnsSupplier->getColumns(), UNO_SET_THROW );
+
+ sal_Int32 nKeyType = 0;
+ xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
+
+ switch ( nKeyType )
+ {
+ case KeyType::FOREIGN:
+ { // our new table has a foreign key
+ // so look if the referenced table is already in our list
+ xProp->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= aReferencedTable;
+ OSL_ENSURE(!aReferencedTable.isEmpty(),"Foreign key without referencedTableName");
+
+ OTableWindowMap::const_iterator aIter = rTabWins.find(aReferencedTable);
+ OTableWindowMap::const_iterator aEnd = rTabWins.end();
+ if(aIter == aEnd)
+ {
+ for(aIter = rTabWins.begin();aIter != aEnd;++aIter)
+ {
+ OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second.get());
+ OSL_ENSURE( pTabWinTmp,"TableWindow is null!" );
+ if ( pTabWinTmp != pNewTabWin && pTabWinTmp->GetComposedName() == aReferencedTable )
+ break;
+ }
+ }
+ if ( aIter != aEnd && pNewTabWin.get() != aIter->second.get() )
+ addConnections( this, *pNewTabWin, *static_cast<OQueryTableWindow*>(aIter->second.get()), xFKeyColumns );
+ }
+ break;
+
+ case KeyType::PRIMARY:
+ {
+ // we have a primary key so look in our list if there exists a key which this is referred to
+ for (auto const& tabWin : rTabWins)
+ {
+ OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(tabWin.second.get());
+ if ( pTabWinTmp == pNewTabWin )
+ continue;
+
+ if ( pTabWinTmp->GetData()->isQuery() )
+ continue;
+
+ OSL_ENSURE(pTabWinTmp,"TableWindow is null!");
+ Reference< XPropertySet > xFKKey = getKeyReferencedTo( pTabWinTmp->GetData()->getKeys(), pNewTabWin->GetComposedName() );
+ if ( !xFKKey.is() )
+ continue;
+
+ Reference<XColumnsSupplier> xFKColumnsSupplier( xFKKey, UNO_QUERY_THROW );
+ Reference< XNameAccess > xTColumns( xFKColumnsSupplier->getColumns(), UNO_SET_THROW );
+ addConnections( this, *pTabWinTmp, *pNewTabWin, xTColumns );
+ }
+ }
+ break;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("dbaccess");
+ }
+
+ } while ( false );
+ }
+
+ // My parent needs to be informed about the delete
+ m_pView->getController().addUndoActionAndInvalidate( std::move(pUndoAction) );
+}
+
+void OQueryTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest)
+{
+ OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(jxdSource.pListBox->GetTabWin());
+ OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(jxdDest.pListBox->GetTabWin());
+
+ OUString aSourceFieldName, aDestFieldName;
+ aSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry);
+ aDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry);
+
+ OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
+ if ( !pConn )
+ {
+ // new data object
+ auto xNewConnectionData = std::make_shared<OQueryTableConnectionData>(pSourceWin->GetData(), pDestWin->GetData());
+
+ sal_uInt32 nSourceFieldIndex, nDestFieldIndex;
+
+ // Get name/position of both affected fields ...
+ // Source
+ nSourceFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry);
+ // Dest
+ nDestFieldIndex = jxdDest.pListBox->GetModel()->GetAbsPos(jxdDest.pEntry);
+
+ // ... and set them
+ xNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex);
+ xNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex);
+
+ xNewConnectionData->AppendConnLine( aSourceFieldName,aDestFieldName );
+
+ ScopedVclPtrInstance< OQueryTableConnection > aNewConnection(this, xNewConnectionData);
+ NotifyTabConnection(*aNewConnection);
+ // As usual with NotifyTabConnection, using a local variable is fine because a copy is made
+ }
+ else
+ {
+ // the connection could point on the other side
+ if(pConn->GetSourceWin() == pDestWin)
+ {
+ OUString aTmp(aSourceFieldName);
+ aSourceFieldName = aDestFieldName;
+ aDestFieldName = aTmp;
+ }
+
+ pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName );
+
+ connectionModified(this,pConn,false);
+ }
+}
+
+void OQueryTableView::ConnDoubleClicked(VclPtr<OTableConnection>& rConnection)
+{
+ if (openJoinDialog(this, rConnection->GetData(), false))
+ {
+ connectionModified(this, rConnection, false);
+ SelectConn(rConnection);
+ }
+}
+
+void OQueryTableView::createNewConnection()
+{
+ TTableConnectionData::value_type pData = std::make_shared<OQueryTableConnectionData>();
+ if( !openJoinDialog(this,pData,true) )
+ return;
+
+ OTableWindowMap& rMap = GetTabWinMap();
+ OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(rMap[pData->getReferencingTable()->GetWinName()].get());
+ OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(rMap[pData->getReferencedTable()->GetWinName()].get());
+ // first we have to look if the this connection already exists
+ OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
+ bool bNew = true;
+ if ( pConn )
+ {
+ pConn->GetData()->CopyFrom( *pData );
+ bNew = false;
+ }
+ else
+ {
+ // create a new connection and append it
+ VclPtrInstance<OQueryTableConnection> pQConn(this, pData);
+ GetConnection(pQConn);
+ pConn = pQConn;
+ }
+ connectionModified(this,pConn,bNew);
+ if ( !bNew && pConn == GetSelectedConn() ) // our connection was selected before so we have to reselect it
+ SelectConn( pConn );
+}
+
+bool OQueryTableView::RemoveConnection(VclPtr<OTableConnection>& rConnection, bool /*_bDelete*/)
+{
+ VclPtr<OQueryTableConnection> xConnection(static_cast<OQueryTableConnection*>(rConnection.get()));
+
+ // we don't want that our connection will be deleted, we put it in the undo manager
+ bool bRet = OJoinTableView::RemoveConnection(rConnection, false);
+
+ // add undo action
+ addUndoAction(this,
+ std::make_unique<OQueryDelTabConnUndoAction>(this),
+ xConnection.get(),
+ true);
+
+ return bRet;
+}
+
+OQueryTableWindow* OQueryTableView::FindTable(const OUString& rAliasName)
+{
+ OSL_ENSURE(!rAliasName.isEmpty(), "OQueryTableView::FindTable : the AliasName should not be empty !");
+ // (it is harmless but does not make sense and indicates that there is probably an error in the caller)
+ OTableWindowMap::const_iterator aIter = GetTabWinMap().find(rAliasName);
+ if(aIter != GetTabWinMap().end())
+ return static_cast<OQueryTableWindow*>(aIter->second.get());
+ return nullptr;
+}
+
+bool OQueryTableView::FindTableFromField(const OUString& rFieldName, OTableFieldDescRef const & rInfo, sal_uInt16& rCnt)
+{
+ rCnt = 0;
+ for (auto const& tabWin : GetTabWinMap())
+ {
+ if(static_cast<OQueryTableWindow*>(tabWin.second.get())->ExistsField(rFieldName, rInfo))
+ ++rCnt;
+ }
+ // TODO JNA : what should we rCnt > 1?
+
+ return rCnt == 1;
+}
+
+bool OQueryTableView::ContainsTabWin(const OTableWindow& rTabWin)
+{
+
+ for (auto const& tabWin : GetTabWinMap())
+ {
+ if ( tabWin.second == &rTabWin )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void OQueryTableView::RemoveTabWin(OTableWindow* pTabWin)
+{
+ OSL_ENSURE(pTabWin != nullptr, "OQueryTableView::RemoveTabWin : Window should not be NULL !");
+
+ if(!(pTabWin && ContainsTabWin(*pTabWin))) // #i122589# check if registered before deleting
+ return;
+
+ // I need my parent so it can be informed about the deletion
+ OQueryDesignView* pParent = static_cast<OQueryDesignView*>(getDesignView());
+
+ SfxUndoManager& rUndoMgr = m_pView->getController().GetUndoManager();
+ rUndoMgr.EnterListAction(DBA_RES(STR_QUERY_UNDO_TABWINDELETE) , OUString(), 0, ViewShellId(-1));
+
+ // add the Undo-Action
+ std::unique_ptr<OQueryTabWinDelUndoAct> pUndoAction(new OQueryTabWinDelUndoAct(this));
+ pUndoAction->SetTabWin(static_cast< OQueryTableWindow*>(pTabWin));
+
+ // and hide the window
+ HideTabWin(static_cast< OQueryTableWindow*>(pTabWin), pUndoAction.get());
+
+ // Undo Actions and delete the fields in SelectionBrowseBox
+ pParent->TableDeleted( static_cast< OQueryTableWindowData*>(pTabWin->GetData().get())->GetAliasName() );
+
+ m_pView->getController().addUndoActionAndInvalidate( std::move(pUndoAction) );
+ rUndoMgr.LeaveListAction();
+
+ modified();
+ if ( m_pAccessible )
+ m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
+ makeAny(pTabWin->GetAccessible()),
+ Any()
+ );
+}
+
+void OQueryTableView::EnsureVisible(const OTableWindow* pWin)
+{
+
+ Invalidate(InvalidateFlags::NoChildren);
+ OJoinTableView::EnsureVisible(pWin);
+}
+
+void OQueryTableView::GetConnection(OQueryTableConnection* pConn)
+{
+ // add to me and the document
+
+ addConnection( pConn );
+}
+
+void OQueryTableView::DropConnection(VclPtr<OQueryTableConnection> const & rConn)
+{
+ // Pay attention to the selection
+ // remove from me and the document
+ VclPtr<OTableConnection> xConn(rConn.get());
+ RemoveConnection(xConn, false);
+}
+
+void OQueryTableView::HideTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction )
+{
+ OTableWindowMap& rTabWins = GetTabWinMap();
+
+ // Window
+ // save the position in its data
+ getDesignView()->SaveTabWinUIConfig(pTabWin);
+ // (I need to go via the parent, as only the parent knows the position of the scrollbars)
+ // and then out of the TabWins list and hide
+ OTableWindowMap::const_iterator aIter = std::find_if(rTabWins.begin(), rTabWins.end(),
+ [&pTabWin](const OTableWindowMap::value_type& rEntry) { return rEntry.second == pTabWin; });
+ if (aIter != rTabWins.end())
+ rTabWins.erase( aIter );
+
+ pTabWin->Hide(); // do not destroy it, as it is still in the undo list!!
+
+ // the TabWin data must also be passed out of my responsibility
+ TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData();
+ rTabWinDataList.erase( std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), pTabWin->GetData()), rTabWinDataList.end());
+ // The data should not be destroyed as TabWin itself - which is still alive - needs them
+ // Either it goes back into my responsibility, (via ShowTabWin), then I add the data back,
+ // or the Undo-Action, which currently has full responsibility for the window
+ // and its data, gets destroyed and destroys both the window and its data
+
+ if (m_pLastFocusTabWin == pTabWin)
+ m_pLastFocusTabWin = nullptr;
+
+ // collect connections belonging to the window and pass to UndoAction
+ sal_Int16 nCnt = 0;
+ const auto& rTabConList = getTableConnections();
+ auto aIter2 = rTabConList.begin();
+ for(;aIter2 != rTabConList.end();)// the end may change
+ {
+ VclPtr<OTableConnection> xTmpEntry = *aIter2;
+ OQueryTableConnection* pTmpEntry = static_cast<OQueryTableConnection*>(xTmpEntry.get());
+ OSL_ENSURE(pTmpEntry,"OQueryTableConnection is null!");
+ if( pTmpEntry->GetAliasName(JTCS_FROM) == pTabWin->GetAliasName() ||
+ pTmpEntry->GetAliasName(JTCS_TO) == pTabWin->GetAliasName() )
+ {
+ // add to undo list
+ pUndoAction->InsertConnection(xTmpEntry);
+
+ // call base class because we append an undo action
+ // but this time we are in an undo action list
+ OJoinTableView::RemoveConnection(xTmpEntry, false);
+ aIter2 = rTabConList.begin();
+ ++nCnt;
+ }
+ else
+ ++aIter2;
+ }
+
+ if (nCnt)
+ InvalidateConnections();
+
+ m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
+
+ // inform the UndoAction that the window and connections belong to it
+ pUndoAction->SetOwnership(true);
+
+ // by doing so, we have modified the document
+ m_pView->getController().setModified( true );
+ m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
+}
+
+bool OQueryTableView::ShowTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction, bool _bAppend )
+{
+
+ bool bSuccess = false;
+
+ if (pTabWin)
+ {
+ if (pTabWin->Init())
+ {
+ TTableWindowData::value_type pData = pTabWin->GetData();
+ OSL_ENSURE(pData != nullptr, "OQueryTableView::ShowTabWin : TabWin has no data !");
+ // If there is a position and size defined, we use them
+ if (pData->HasPosition() && pData->HasSize())
+ {
+ Size aSize(CalcZoom(pData->GetSize().Width()),CalcZoom(pData->GetSize().Height()));
+ pTabWin->SetPosSizePixel(pData->GetPosition(), aSize);
+ }
+ else
+ // else set a default position
+ SetDefaultTabWinPosSize(pTabWin);
+
+ // Show the window and add to the list
+ OUString sName = static_cast< OQueryTableWindowData*>(pData.get())->GetAliasName();
+ OSL_ENSURE(GetTabWinMap().find(sName) == GetTabWinMap().end(),"Alias name already in list!");
+ GetTabWinMap().emplace(sName,pTabWin);
+
+ pTabWin->Show();
+
+ pTabWin->PaintImmediately();
+ // We must call Update() in order to show the connections in the window correctly. This sounds strange,
+ // but the Listbox has an internal Member which is initialized when the Listbox is first shown (after the Listbox
+ // is filled in Init). This Member will eventually be needed for
+ // GetEntryPos, and then in turn by the Connection, when its starting point to the window must be determined.
+
+ // the Connections
+ auto rTableCon = pUndoAction->GetTabConnList();
+ for(const auto& conn : rTableCon)
+ addConnection(conn); // add all connections from the undo action
+
+ rTableCon.clear();
+
+ // and add the window's data to the list (of the document)
+ if(_bAppend)
+ m_pView->getController().getTableWindowData().push_back(pTabWin->GetData());
+
+ m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
+
+ // and inform the UndoAction that the window belongs to me
+ pUndoAction->SetOwnership(false);
+
+ bSuccess = true;
+ }
+ else
+ {
+ // Initialisation failed
+ // (for example when the Connection to the database is not available at the moment)
+ pTabWin->clearListBox();
+ pTabWin->disposeOnce();
+ }
+ }
+
+ // show that I have changed the document
+ if(!m_pView->getController().isReadOnly())
+ m_pView->getController().setModified( true );
+
+ m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
+
+ return bSuccess;
+}
+
+void OQueryTableView::InsertField(const OTableFieldDescRef& rInfo)
+{
+ OSL_ENSURE(getDesignView() != nullptr, "OQueryTableView::InsertField : has no Parent !");
+ static_cast<OQueryDesignView*>(getDesignView())->InsertField(rInfo);
+}
+
+bool OQueryTableView::ExistsAVisitedConn(const OQueryTableWindow* pFrom) const
+{
+ for(const auto& conn : getTableConnections())
+ {
+ OQueryTableConnection* pTemp = static_cast<OQueryTableConnection*>(conn.get());
+ if (pTemp->IsVisited() &&
+ (pFrom == static_cast< OQueryTableWindow*>(pTemp->GetSourceWin()) || pFrom == static_cast< OQueryTableWindow*>(pTemp->GetDestWin())))
+ return true;
+ }
+
+ return false;
+}
+
+void OQueryTableView::onNoColumns_throw()
+{
+ OUString sError(DBA_RES(STR_STATEMENT_WITHOUT_RESULT_SET));
+ ::dbtools::throwSQLException( sError, ::dbtools::StandardSQLState::GENERAL_ERROR, nullptr );
+}
+
+bool OQueryTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& _pData) const
+{
+ OQueryTableConnectionData* pQueryData = static_cast<OQueryTableConnectionData*>(_pData.get());
+ return pQueryData && (pQueryData->GetJoinType() == CROSS_JOIN);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */