/* -*- 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 .
*/
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;
}
{
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)
}
}
}
// 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
// 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());
std::erase(rTabWinDataList, *aIter); 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);
// 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
std::erase(rTabConnDataList, *aConIter); continue;
}
// adds a new connection to join view and notifies our accessible and invalidates the controller
addConnection(VclPtr<OQueryTableConnection>::Create(this, *aConIter));
}
}
void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, bool _bCreateUndoAction)
{ // let's first check if I have the connection already
OQueryTableConnection* pTabConn = nullptr; constauto& 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 (autoconst& 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);
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;
// find the table which has a foreign key with this referencedTable name static Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,std::u16string_view _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 (autoconst& 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
// 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(),
Any(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;
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");
case KeyType::PRIMARY:
{ // we have a primary key so look in our list if there exists a key which this is referred to for (autoconst& tabWin : rTabWins)
{
OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(tabWin.second.get()); if ( pTabWinTmp == pNewTabWin ) continue;
assert(pTabWinTmp && "TableWindow is null!"); if ( pTabWinTmp->GetData()->isQuery() ) continue;
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.nEntry; // Dest
nDestFieldIndex = jxdDest.nEntry;
// ... and set them
xNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex);
xNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex);
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)
std::swap(aSourceFieldName, aDestFieldName);
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 );
}
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()) returnstatic_cast<OQueryTableWindow*>(aIter->second.get()); return nullptr;
}
bool OQueryTableView::FindTableFromField(const OUString& rFieldName, OTableFieldDescRef const & rInfo, sal_uInt16& rCnt)
{
rCnt = 0; for (autoconst& tabWin : GetTabWinMap())
{ if(static_cast<OQueryTableWindow*>(tabWin.second.get())->ExistsField(rFieldName, rInfo))
++rCnt;
} // TODO JNA : what should we rCnt > 1?
for (autoconst& tabWin : GetTabWinMap())
{ if ( tabWin.second == &rTabWin )
{ returntrue;
}
}
returnfalse;
}
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());
// 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() );
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);
}
// 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();
std::erase(rTabWinDataList, pTabWin->GetData()); // 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; constauto& 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());
assert(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;
}
// 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);
}
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(constauto& 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());
// 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 );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.