Bump version to 5.0-14
[LibreOffice.git] / dbaccess / source / ui / querydesign / QueryTableView.cxx
blobb39bf31e0cf0e1389eacc3bf5c93e0daa88c3099
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "QueryTableView.hxx"
21 #include "TableFieldInfo.hxx"
22 #include "TableFieldDescription.hxx"
23 #include <tools/diagnose_ex.h>
24 #include <osl/diagnose.h>
25 #include "dbaccess_helpid.hrc"
26 #include "QTableWindow.hxx"
27 #include "QTableConnection.hxx"
28 #include "QTableConnectionData.hxx"
29 #include "QueryDesignView.hxx"
30 #include "querycontroller.hxx"
31 #include "QueryAddTabConnUndoAction.hxx"
32 #include "QueryTabWinShowUndoAct.hxx"
33 #include "browserids.hxx"
34 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
35 #include <com/sun/star/sdbc/XConnection.hpp>
36 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
37 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
38 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
39 #include "JAccess.hxx"
40 #include <com/sun/star/sdbcx/KeyType.hpp>
41 #include <com/sun/star/container/XIndexAccess.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include "dbustrings.hrc"
44 #include <connectivity/dbtools.hxx>
45 #include <comphelper/sequence.hxx>
46 #include "querydlg.hxx"
47 #include "JoinExchange.hxx"
48 #include <comphelper/extract.hxx>
49 #include "dbu_qry.hrc"
50 #include <vcl/msgbox.hxx>
51 #include "svtools/treelistentry.hxx"
53 using namespace dbaui;
54 using namespace ::com::sun::star::uno;
55 using namespace ::com::sun::star::sdbc;
56 using namespace ::com::sun::star::sdbcx;
57 using namespace ::com::sun::star::beans;
58 using namespace ::com::sun::star::container;
59 using namespace ::com::sun::star::accessibility;
61 namespace
63 bool isColumnInKeyType(const Reference<XIndexAccess>& _rxKeys,const OUString& _rColumnName,sal_Int32 _nKeyType)
65 bool bReturn = false;
66 if(_rxKeys.is())
68 Reference<XColumnsSupplier> xColumnsSupplier;
69 // search the one and only primary key
70 const sal_Int32 nCount = _rxKeys->getCount();
71 for(sal_Int32 i=0;i< nCount;++i)
73 Reference<XPropertySet> xProp(_rxKeys->getByIndex(i),UNO_QUERY);
74 if(xProp.is())
76 sal_Int32 nKeyType = 0;
77 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
78 if(_nKeyType == nKeyType)
80 xColumnsSupplier.set(xProp,UNO_QUERY);
81 if(xColumnsSupplier.is())
83 Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
84 if(xColumns.is() && xColumns->hasByName(_rColumnName))
86 bReturn = true;
87 break;
94 return bReturn;
96 /** appends a new TabAdd Undo action at controller
97 @param _pView the view which we use
98 @param _pUndoAction the undo action which should be added
99 @param _pConnection the connection for which the undo action should be appended
100 @param _bOwner is the undo action the owner
102 void addUndoAction( OQueryTableView* _pView,
103 OQueryTabConnUndoAction* _pUndoAction,
104 OQueryTableConnection* _pConnection,
105 bool _bOwner = false)
107 _pUndoAction->SetOwnership(_bOwner);
108 _pUndoAction->SetConnection(_pConnection);
109 _pView->getDesignView()->getController().addUndoActionAndInvalidate(_pUndoAction);
111 /** openJoinDialog opens the join dialog with this connection data
112 @param _pView the view which we use
113 @param _pConnectionData the connection data
115 @return true when OK was pressed otherwise false
117 bool openJoinDialog(OQueryTableView* _pView,const TTableConnectionData::value_type& _pConnectionData,bool _bSelectableTables)
119 OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pConnectionData.get());
121 ScopedVclPtrInstance< DlgQryJoin > aDlg(_pView,_pConnectionData,&_pView->GetTabWinMap(),_pView->getDesignView()->getController().getConnection(),_bSelectableTables);
122 bool bOk = aDlg->Execute() == RET_OK;
123 if( bOk )
125 pData->SetJoinType(aDlg->GetJoinType());
126 _pView->getDesignView()->getController().setModified(sal_True);
129 return bOk;
131 /** connectionModified adds an undo action for the modified connection and forces an redraw
132 @param _pView the view which we use
133 @param _pConnection the connection which was modified
134 @param _bAddUndo true when an undo action should be appended
136 void connectionModified(OQueryTableView* _pView,
137 OTableConnection* _pConnection,
138 bool _bAddUndo)
140 OSL_ENSURE(_pConnection,"Invalid connection!");
141 _pConnection->UpdateLineList();
143 // add an undo action
144 if ( _bAddUndo )
145 addUndoAction( _pView,
146 new OQueryAddTabConnUndoAction(_pView),
147 static_cast< OQueryTableConnection*>(_pConnection));
148 // redraw
149 _pConnection->RecalcLines();
150 // force an invalidation of the bounding rectangle
151 _pConnection->InvalidateConnection();
153 _pView->Invalidate(INVALIDATE_NOCHILDREN);
155 void addConnections(OQueryTableView* _pView,
156 const OQueryTableWindow& _rSource,
157 const OQueryTableWindow& _rDest,
158 const Reference<XNameAccess>& _rxSourceForeignKeyColumns)
160 if ( _rSource.GetData()->isQuery() || _rDest.GetData()->isQuery() )
161 // nothing to do if one of both denotes a query
162 return;
164 // we found a table in our view where we can insert some connections
165 // the key columns have a property called RelatedColumn
166 // OQueryTableConnectionData aufbauen
167 OQueryTableConnectionData* pNewConnData = new OQueryTableConnectionData( _rSource.GetData(), _rDest.GetData() );
168 TTableConnectionData::value_type aNewConnData(pNewConnData);
170 Reference<XIndexAccess> xReferencedKeys( _rDest.GetData()->getKeys());
171 OUString sRelatedColumn;
173 // iterate through all foreignkey columns to create the connections
174 Sequence< OUString> aElements(_rxSourceForeignKeyColumns->getElementNames());
175 const OUString* pIter = aElements.getConstArray();
176 const OUString* pEnd = pIter + aElements.getLength();
177 for(sal_Int32 i=0;pIter != pEnd;++pIter,++i)
179 Reference<XPropertySet> xColumn;
180 if ( !( _rxSourceForeignKeyColumns->getByName(*pIter) >>= xColumn ) )
182 OSL_FAIL( "addConnections: invalid foreign key column!" );
183 continue;
186 pNewConnData->SetFieldType(JTCS_FROM,TAB_NORMAL_FIELD);
188 xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn;
189 pNewConnData->SetFieldType(JTCS_TO,isColumnInKeyType(xReferencedKeys,sRelatedColumn,KeyType::PRIMARY) ? TAB_PRIMARY_FIELD : TAB_NORMAL_FIELD);
192 Sequence< sal_Int16> aFind(::comphelper::findValue(_rSource.GetOriginalColumns()->getElementNames(),*pIter,true));
193 if(aFind.getLength())
194 pNewConnData->SetFieldIndex(JTCS_FROM,aFind[0]+1);
195 else
196 OSL_FAIL("Column not found!");
198 // get the position inside the tabe
199 Reference<XNameAccess> xRefColumns = _rDest.GetOriginalColumns();
200 if(xRefColumns.is())
202 Sequence< sal_Int16> aFind(::comphelper::findValue(xRefColumns->getElementNames(),sRelatedColumn,true));
203 if(aFind.getLength())
204 pNewConnData->SetFieldIndex(JTCS_TO,aFind[0]+1);
205 else
206 OSL_FAIL("Column not found!");
208 pNewConnData->AppendConnLine(*pIter,sRelatedColumn);
210 // now add the Conn itself
211 ScopedVclPtrInstance< OQueryTableConnection > aNewConn(_pView, aNewConnData);
212 // referring to the local variable is not important, as NotifyQueryTabConn creates a new copy
213 // to add me (if not existent)
214 _pView->NotifyTabConnection(*aNewConn.get(), false);
215 // don't create an Undo-Action for the new connection : the connection is
216 // covered by the Undo-Action for the tabwin, as the "Undo the insert" will
217 // automatically remove all connections adjacent to the win.
218 // (Because of this automatism we would have an ownerhsip ambiguity for
219 // the connection data if we would insert the conn-Undo-Action)
224 // class OQueryTableView
225 OQueryTableView::OQueryTableView( vcl::Window* pParent,OQueryDesignView* pView)
226 : OJoinTableView( pParent,pView)
228 SetHelpId(HID_CTL_QRYDGNTAB);
231 sal_Int32 OQueryTableView::CountTableAlias(const OUString& rName, sal_Int32& rMax)
233 sal_Int32 nRet = 0;
235 OTableWindowMap::iterator aIter = GetTabWinMap().find(rName);
236 while(aIter != GetTabWinMap().end())
238 OUString aNewName = rName + "_" + OUString::number(++nRet);
239 aIter = GetTabWinMap().find(aNewName);
242 rMax = nRet;
244 return nRet;
247 void OQueryTableView::ReSync()
249 TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData();
250 OSL_ENSURE((getTableConnections().size()==0) && (GetTabWinMap().size()==0),
251 "before calling OQueryTableView::ReSync() please call ClearAll !");
253 // I need a collection of all window names that cannot be created so that I do not initialize connections for them.
254 ::std::vector<OUString> arrInvalidTables;
256 TTableWindowData::reverse_iterator aIter = rTabWinDataList.rbegin();
257 // Create the window and add it
259 for(;aIter != rTabWinDataList.rend();++aIter)
261 OQueryTableWindowData* pData = static_cast<OQueryTableWindowData*>(aIter->get());
262 VclPtr<OTableWindow> pTabWin = createWindow(*aIter);
264 // I dont't use ShowTabWin as this adds the window data to the list of documents.
265 // This would be bad as I am getting them from there.
266 // Instead, I do it step by step
267 if (!pTabWin->Init())
269 // The initialisation has gone wrong, this TabWin is not available, so
270 // I must clean up the data and the document
271 pTabWin->clearListBox();
272 pTabWin.disposeAndClear();
273 arrInvalidTables.push_back(pData->GetAliasName());
275 rTabWinDataList.erase( ::std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), *aIter), rTabWinDataList.end());
276 continue;
279 GetTabWinMap()[pData->GetAliasName()] = pTabWin; // add at the beginning as I am going backwards through the DataList
280 // Use the default if there is no position or size
281 if (!pData->HasPosition() && !pData->HasSize())
282 SetDefaultTabWinPosSize(pTabWin);
284 pTabWin->Show();
287 // Add the connections
288 TTableConnectionData& rTabConnDataList = m_pView->getController().getTableConnectionData();
289 TTableConnectionData::reverse_iterator aConIter = rTabConnDataList.rbegin();
291 for(;aConIter != rTabConnDataList.rend();++aConIter)
293 OQueryTableConnectionData* pTabConnData = static_cast<OQueryTableConnectionData*>(aConIter->get());
295 // do both tables for the connection exist ?
296 OUString strTabExistenceTest = pTabConnData->getReferencingTable()->GetWinName();
297 bool bInvalid = ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
298 strTabExistenceTest = pTabConnData->getReferencedTable()->GetWinName();
299 bInvalid = bInvalid && ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
301 if (bInvalid)
303 // no -> bad luck, no connection
304 rTabConnDataList.erase( ::std::remove(rTabConnDataList.begin(), rTabConnDataList.end(), *aConIter), rTabConnDataList.end());
305 continue;
308 // adds a new connection to join view and notifies our accessible and invaldates the controller
309 addConnection(VclPtr<OQueryTableConnection>::Create(this, *aConIter));
313 void OQueryTableView::ClearAll()
315 OJoinTableView::ClearAll();
317 SetUpdateMode(true);
318 m_pView->getController().setModified(sal_True);
321 VclPtr<OTableWindow> OQueryTableView::createWindow(const TTableWindowData::value_type& _pData)
323 return VclPtr<OQueryTableWindow>::Create(this,_pData);
326 void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, bool _bCreateUndoAction)
328 // let's first check if I have the connection already
329 OQueryTableConnection* pTabConn = NULL;
330 const auto& rConnections = getTableConnections();
331 auto aEnd = rConnections.end();
332 auto aIter = ::std::find( rConnections.begin(),
333 aEnd,
334 VclPtr<OTableConnection>(const_cast<OTableConnection*>(static_cast<const OTableConnection*>(&rNewConn)))
336 if(aIter == aEnd )
338 aIter = rConnections.begin();
339 for(;aIter != aEnd;++aIter)
341 if(*static_cast<OQueryTableConnection*>((*aIter).get()) == rNewConn)
343 pTabConn = static_cast<OQueryTableConnection*>((*aIter).get());
344 break;
348 else
349 pTabConn = static_cast<OQueryTableConnection*>((*aIter).get());
351 // no -> insert
352 if (pTabConn == NULL)
354 // the new data ...
355 OQueryTableConnectionData* pNewData = static_cast< OQueryTableConnectionData*>(rNewConn.GetData()->NewInstance());
356 pNewData->CopyFrom(*rNewConn.GetData());
357 TTableConnectionData::value_type aData(pNewData);
358 VclPtrInstance<OQueryTableConnection> pNewConn(this, aData);
359 GetConnection(pNewConn);
361 connectionModified(this,pNewConn,_bCreateUndoAction);
365 OTableWindowData* OQueryTableView::CreateImpl(const OUString& _rComposedName
366 ,const OUString& _sTableName
367 ,const OUString& _rWinName)
369 return new OQueryTableWindowData( _rComposedName, _sTableName,_rWinName );
372 void OQueryTableView::AddTabWin(const OUString& _rTableName, const OUString& _rAliasName, bool bNewTable)
374 // this method has been inherited from the base class, linking back to the parent and which constructs
375 // an Alias and which passes on to my other AddTabWin
377 // pity _rTableName is fully qualified, OQueryDesignView expects a string which only
378 // contains schema and tables but no catalog.
379 Reference< XConnection> xConnection = m_pView->getController().getConnection();
380 if(!xConnection.is())
381 return;
384 Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
385 OUString sCatalog, sSchema, sTable;
386 ::dbtools::qualifiedNameComponents(xMetaData,
387 _rTableName,
388 sCatalog,
389 sSchema,
390 sTable,
391 ::dbtools::eInDataManipulation);
392 OUString sRealName(sSchema);
393 if (!sRealName.isEmpty())
394 sRealName+= OUString('.');
395 sRealName += sTable;
397 AddTabWin(_rTableName, sRealName, _rAliasName, bNewTable);
399 catch(SQLException&)
401 OSL_FAIL("qualifiedNameComponents");
405 // find the table which has a foreign key with this referencedTable name
406 Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,const OUString& _rReferencedTable)
408 if(!_rxKeys.is())
409 return Reference<XPropertySet>();
411 if ( !_rxKeys.is() )
412 return Reference<XPropertySet>();
413 // search the one and only primary key
414 const sal_Int32 nCount = _rxKeys->getCount();
415 for(sal_Int32 i=0;i<nCount ;++i)
417 Reference<XPropertySet> xKey(_rxKeys->getByIndex(i),UNO_QUERY);
418 if(xKey.is())
420 sal_Int32 nKeyType = 0;
421 xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
422 if(KeyType::FOREIGN == nKeyType)
424 OUString sReferencedTable;
425 xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable;
426 // TODO check case
427 if(sReferencedTable == _rReferencedTable)
428 return xKey;
432 return Reference<XPropertySet>();
435 void OQueryTableView::AddTabWin(const OUString& _rComposedName, const OUString& _rTableName, const OUString& strAlias, bool bNewTable)
437 OSL_ENSURE(!_rTableName.isEmpty() || !strAlias.isEmpty(), "OQueryTableView::AddTabWin : no tables or aliases !");
438 // If the table is not set, then it is a dummy window, but at least the alias must be set
440 // build a new data structure
441 // first check if this already has its data
442 bool bAppend = bNewTable;
443 TTableWindowData::value_type pNewTabWinData;
444 TTableWindowData& rWindowData = getDesignView()->getController().getTableWindowData();
445 TTableWindowData::iterator aWinIter = rWindowData.begin();
446 TTableWindowData::iterator aWinEnd = rWindowData.end();
447 for(;aWinIter != aWinEnd;++aWinIter)
449 pNewTabWinData = *aWinIter;
450 if (pNewTabWinData && pNewTabWinData->GetWinName() == strAlias && pNewTabWinData->GetComposedName() == _rComposedName && pNewTabWinData->GetTableName() == _rTableName)
451 break;
453 if ( !bAppend )
454 bAppend = ( aWinIter == aWinEnd );
455 if ( bAppend )
456 pNewTabWinData = createTableWindowData(_rComposedName, _rTableName, strAlias);
457 // I do not need to add TabWinData to the DocShell list, ShowTabWin does that.
459 // Create a new window
460 VclPtr<OQueryTableWindow> pNewTabWin = static_cast<OQueryTableWindow*>(createWindow(pNewTabWinData).get());
461 // No need to initialize, as that happens in ShowTabWin
463 // New UndoAction
464 OQueryTabWinShowUndoAct* pUndoAction = new OQueryTabWinShowUndoAct(this);
465 pUndoAction->SetTabWin(pNewTabWin); // Window
466 bool bSuccess = ShowTabWin(pNewTabWin, pUndoAction,bAppend);
467 if(!bSuccess)
469 // reset table window
470 pUndoAction->SetTabWin(NULL);
471 pUndoAction->SetOwnership(false);
473 delete pUndoAction;
474 return;
477 // Show the relations between the individual tables
478 OTableWindowMap& rTabWins = GetTabWinMap();
479 if(bNewTable && !rTabWins.empty() && !_rTableName.isEmpty())
481 modified();
482 if ( m_pAccessible )
483 m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
484 Any(),
485 makeAny(pNewTabWin->GetAccessible())
488 do {
490 if ( pNewTabWin->GetData()->isQuery() )
491 break;
495 // find relations between the table an the tables already inserted
496 Reference< XIndexAccess> xKeyIndex = pNewTabWin->GetData()->getKeys();
497 if ( !xKeyIndex.is() )
498 break;
500 Reference<XNameAccess> xFKeyColumns;
501 OUString aReferencedTable;
502 Reference<XColumnsSupplier> xColumnsSupplier;
504 const sal_Int32 nKeyCount = xKeyIndex->getCount();
505 for ( sal_Int32 i=0; i<nKeyCount ; ++i )
507 Reference< XPropertySet > xProp( xKeyIndex->getByIndex(i), UNO_QUERY_THROW );
508 xColumnsSupplier.set( xProp, UNO_QUERY_THROW );
509 xFKeyColumns.set( xColumnsSupplier->getColumns(), UNO_QUERY_THROW );
511 sal_Int32 nKeyType = 0;
512 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
514 switch ( nKeyType )
516 case KeyType::FOREIGN:
517 { // our new table has a foreign key
518 // so look if the referenced table is already in our list
519 xProp->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= aReferencedTable;
520 OSL_ENSURE(!aReferencedTable.isEmpty(),"Foreign key without referencedTableName");
522 OTableWindowMap::const_iterator aIter = rTabWins.find(aReferencedTable);
523 OTableWindowMap::const_iterator aEnd = rTabWins.end();
524 if(aIter == aEnd)
526 for(aIter = rTabWins.begin();aIter != aEnd;++aIter)
528 OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second.get());
529 OSL_ENSURE( pTabWinTmp,"TableWindow is null!" );
530 if ( pTabWinTmp != pNewTabWin && pTabWinTmp->GetComposedName() == aReferencedTable )
531 break;
534 if ( aIter != aEnd && pNewTabWin.get() != aIter->second.get() )
535 addConnections( this, *pNewTabWin, *static_cast<OQueryTableWindow*>(aIter->second.get()), xFKeyColumns );
537 break;
539 case KeyType::PRIMARY:
541 // we have a primary key so look in our list if there exists a key which this is referred to
542 OTableWindowMap::const_iterator aIter = rTabWins.begin();
543 OTableWindowMap::const_iterator aEnd = rTabWins.end();
544 for(;aIter != aEnd;++aIter)
546 OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second.get());
547 if ( pTabWinTmp == pNewTabWin )
548 continue;
550 if ( pTabWinTmp->GetData()->isQuery() )
551 continue;
553 OSL_ENSURE(pTabWinTmp,"TableWindow is null!");
554 Reference< XPropertySet > xFKKey = getKeyReferencedTo( pTabWinTmp->GetData()->getKeys(), pNewTabWin->GetComposedName() );
555 if ( !xFKKey.is() )
556 continue;
558 Reference<XColumnsSupplier> xFKColumnsSupplier( xFKKey, UNO_QUERY_THROW );
559 Reference< XNameAccess > xTColumns( xFKColumnsSupplier->getColumns(), UNO_QUERY_THROW );
560 addConnections( this, *pTabWinTmp, *pNewTabWin, xTColumns );
563 break;
567 catch( const Exception& )
569 DBG_UNHANDLED_EXCEPTION();
572 } while ( false );
575 // My parent needs to be informed about the delete
576 m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
578 if (bSuccess && m_lnkTabWinsChangeHandler.IsSet())
580 TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_ADDED_WIN, pNewTabWin->GetAliasName());
581 m_lnkTabWinsChangeHandler.Call(&aHint);
585 void OQueryTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest)
587 OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(jxdSource.pListBox->GetTabWin());
588 OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(jxdDest.pListBox->GetTabWin());
590 OUString aSourceFieldName, aDestFieldName;
591 aSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry);
592 aDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry);
594 OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
595 if ( !pConn )
597 // new data object
598 OQueryTableConnectionData* pNewConnectionData = new OQueryTableConnectionData(pSourceWin->GetData(), pDestWin->GetData());
599 TTableConnectionData::value_type aNewConnectionData(pNewConnectionData);
601 sal_uInt32 nSourceFieldIndex, nDestFieldIndex;
602 ETableFieldType eSourceFieldType, eDestFieldType;
604 // Get name/position/type of both affected fields ...
605 // Source
607 nSourceFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry);
608 eSourceFieldType = static_cast< OTableFieldInfo*>(jxdSource.pEntry->GetUserData())->GetKeyType();
610 // Dest
612 nDestFieldIndex = jxdDest.pListBox->GetModel()->GetAbsPos(jxdDest.pEntry);
613 eDestFieldType = static_cast< OTableFieldInfo*>(jxdDest.pEntry->GetUserData())->GetKeyType();
615 // ... and set them
616 pNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex);
617 pNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex);
619 pNewConnectionData->SetFieldType(JTCS_FROM, eSourceFieldType);
620 pNewConnectionData->SetFieldType(JTCS_TO, eDestFieldType);
622 pNewConnectionData->AppendConnLine( aSourceFieldName,aDestFieldName );
624 ScopedVclPtrInstance< OQueryTableConnection > aNewConnection(this, aNewConnectionData);
625 NotifyTabConnection(*aNewConnection.get());
626 // As usual with NotifyTabConnection, using a local variable is fine because a copy is made
628 else
630 // the connection could point on the other side
631 if(pConn->GetSourceWin() == pDestWin)
633 OUString aTmp(aSourceFieldName);
634 aSourceFieldName = aDestFieldName;
635 aDestFieldName = aTmp;
638 pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName );
640 connectionModified(this,pConn,false);
644 void OQueryTableView::ConnDoubleClicked(OTableConnection* pConnection)
646 if( openJoinDialog(this,pConnection->GetData(),false) )
648 connectionModified(this,pConnection,false);
649 SelectConn( pConnection );
653 void OQueryTableView::createNewConnection()
655 TTableConnectionData::value_type pData(new OQueryTableConnectionData());
656 if( openJoinDialog(this,pData,true) )
658 OTableWindowMap& rMap = GetTabWinMap();
659 OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(rMap[pData->getReferencingTable()->GetWinName()].get());
660 OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(rMap[pData->getReferencedTable()->GetWinName()].get());
661 // first we have to look if the this connection already exists
662 OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
663 bool bNew = true;
664 if ( pConn )
666 pConn->GetData()->CopyFrom( *pData );
667 bNew = false;
669 else
671 // create a new conenction and append it
672 VclPtrInstance<OQueryTableConnection> pQConn(this, pData);
673 GetConnection(pQConn);
674 pConn = pQConn;
676 connectionModified(this,pConn,bNew);
677 if ( !bNew && pConn == GetSelectedConn() ) // our connection was selected before so we have to reselect it
678 SelectConn( pConn );
682 bool OQueryTableView::RemoveConnection( OTableConnection* _pConnection,bool /*_bDelete*/ )
685 // we don't want that our connection will be deleted, we put it in the undo manager
686 bool bRet = OJoinTableView::RemoveConnection( _pConnection,false);
688 // add undo action
689 addUndoAction( this,
690 new OQueryDelTabConnUndoAction(this),
691 static_cast< OQueryTableConnection*>(_pConnection),
692 true);
693 return bRet;
696 void OQueryTableView::KeyInput( const KeyEvent& rEvt )
698 OJoinTableView::KeyInput( rEvt );
701 OQueryTableWindow* OQueryTableView::FindTable(const OUString& rAliasName)
703 OSL_ENSURE(!rAliasName.isEmpty(), "OQueryTableView::FindTable : the AliasName should not be empty !");
704 // (it is harmless but does not make sense and indicates that there is probably an error in the caller)
705 OTableWindowMap::const_iterator aIter = GetTabWinMap().find(rAliasName);
706 if(aIter != GetTabWinMap().end())
707 return static_cast<OQueryTableWindow*>(aIter->second.get());
708 return NULL;
711 bool OQueryTableView::FindTableFromField(const OUString& rFieldName, OTableFieldDescRef& rInfo, sal_uInt16& rCnt)
713 rCnt = 0;
714 OTableWindowMap::const_iterator aIter = GetTabWinMap().begin();
715 OTableWindowMap::const_iterator aEnd = GetTabWinMap().end();
716 for(;aIter != aEnd;++aIter)
718 if(static_cast<OQueryTableWindow*>(aIter->second.get())->ExistsField(rFieldName, rInfo))
719 ++rCnt;
722 return rCnt == 1;
725 bool OQueryTableView::ContainsTabWin(const OTableWindow& rTabWin)
727 OTableWindowMap& rTabWins = GetTabWinMap();
729 OTableWindowMap::iterator aIter = rTabWins.begin();
730 OTableWindowMap::iterator aEnd = rTabWins.end();
732 for ( ;aIter != aEnd ; ++aIter )
734 if ( aIter->second == &rTabWin )
736 return true;
740 return false;
743 void OQueryTableView::RemoveTabWin(OTableWindow* pTabWin)
745 OSL_ENSURE(pTabWin != NULL, "OQueryTableView::RemoveTabWin : Window should not be NULL !");
747 if(pTabWin && ContainsTabWin(*pTabWin)) // #i122589# check if registered before deleting
749 // I need my parent so it can be informed about the deletion
750 OQueryDesignView* pParent = static_cast<OQueryDesignView*>(getDesignView());
752 SfxUndoManager& rUndoMgr = m_pView->getController().GetUndoManager();
753 rUndoMgr.EnterListAction( OUString( ModuleRes(STR_QUERY_UNDO_TABWINDELETE) ), OUString() );
755 // add the Undo-Action
756 OQueryTabWinDelUndoAct* pUndoAction = new OQueryTabWinDelUndoAct(this);
757 pUndoAction->SetTabWin(static_cast< OQueryTableWindow*>(pTabWin));
759 // and hide the window
760 HideTabWin(static_cast< OQueryTableWindow*>(pTabWin), pUndoAction);
762 // Undo Actions and delete the fields in SelectionBrowseBox
763 pParent->TableDeleted( static_cast< OQueryTableWindowData*>(pTabWin->GetData().get())->GetAliasName() );
765 m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
766 rUndoMgr.LeaveListAction();
768 if (m_lnkTabWinsChangeHandler.IsSet())
770 TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_REMOVED_WIN, static_cast< OQueryTableWindow*>(pTabWin)->GetAliasName());
771 m_lnkTabWinsChangeHandler.Call(&aHint);
774 modified();
775 if ( m_pAccessible )
776 m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
777 makeAny(pTabWin->GetAccessible()),
778 Any()
783 void OQueryTableView::EnsureVisible(const OTableWindow* pWin)
786 Invalidate(INVALIDATE_NOCHILDREN);
787 OJoinTableView::EnsureVisible(pWin);
790 void OQueryTableView::GetConnection(OQueryTableConnection* pConn)
792 // add to me and the document
794 addConnection( pConn );
797 void OQueryTableView::DropConnection(OQueryTableConnection* pConn)
799 // Pay attention to the selection
800 // remove from me and the document
801 RemoveConnection( pConn ,false);
804 void OQueryTableView::HideTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction )
806 OTableWindowMap& rTabWins = GetTabWinMap();
808 // Window
809 // save the position in its data
810 getDesignView()->SaveTabWinUIConfig(pTabWin);
811 // (I need to go via the parent, as only the parent knows the position of the scrollbars)
812 // and then out of the TabWins list and hide
813 OTableWindowMap::iterator aIter = rTabWins.begin();
814 OTableWindowMap::iterator aEnd = rTabWins.end();
815 for ( ;aIter != aEnd ; ++aIter )
816 if ( aIter->second == pTabWin )
818 rTabWins.erase( aIter );
819 break;
822 pTabWin->Hide(); // do not destroy it, as it is still in the undo list!!
824 // the TabWin data must also be passed out of my responsibility
825 TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData();
826 rTabWinDataList.erase( ::std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), pTabWin->GetData()), rTabWinDataList.end());
827 // The data should not be destroyed as TabWin itself - which is still alive - needs them
828 // Either it goes back into my responsibility, (via ShowTabWin), then I add the data back,
829 // or the Undo-Action, which currently has full responsibility for the window
830 // and its data, gets destroyed and destroys both the window and its data
832 if (m_pLastFocusTabWin == pTabWin)
833 m_pLastFocusTabWin = NULL;
835 // collect connections belonging to the window and pass to UndoAction
836 sal_Int16 nCnt = 0;
837 const auto& rTabConList = getTableConnections();
838 auto aIter2 = rTabConList.begin();
839 for(;aIter2 != rTabConList.end();)// the end may change
841 OQueryTableConnection* pTmpEntry = static_cast<OQueryTableConnection*>((*aIter2).get());
842 OSL_ENSURE(pTmpEntry,"OQueryTableConnection is null!");
843 if( pTmpEntry->GetAliasName(JTCS_FROM) == pTabWin->GetAliasName() ||
844 pTmpEntry->GetAliasName(JTCS_TO) == pTabWin->GetAliasName() )
846 // add to undo list
847 pUndoAction->InsertConnection(pTmpEntry);
849 // call base class because we append an undo action
850 // but this time we are in a undo action list
851 OJoinTableView::RemoveConnection(pTmpEntry,false);
852 aIter2 = rTabConList.begin();
853 ++nCnt;
855 else
856 ++aIter2;
859 if (nCnt)
860 InvalidateConnections();
862 m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
864 // inform the UndoAction that the window and connections belong to it
865 pUndoAction->SetOwnership(true);
867 // by doing so, we have modified the document
868 m_pView->getController().setModified( sal_True );
869 m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
872 bool OQueryTableView::ShowTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction, bool _bAppend )
875 bool bSuccess = false;
877 if (pTabWin)
879 if (pTabWin->Init())
881 TTableWindowData::value_type pData = pTabWin->GetData();
882 OSL_ENSURE(pData != 0, "OQueryTableView::ShowTabWin : TabWin has no data !");
883 // If there is a position and size defined, we use them
884 if (pData->HasPosition() && pData->HasSize())
886 Size aSize(CalcZoom(pData->GetSize().Width()),CalcZoom(pData->GetSize().Height()));
887 pTabWin->SetPosSizePixel(pData->GetPosition(), aSize);
889 else
890 // else set a default position
891 SetDefaultTabWinPosSize(pTabWin);
893 // Show the window and add to the list
894 OUString sName = static_cast< OQueryTableWindowData*>(pData.get())->GetAliasName();
895 OSL_ENSURE(GetTabWinMap().find(sName) == GetTabWinMap().end(),"Alias name already in list!");
896 GetTabWinMap().insert(OTableWindowMap::value_type(sName,pTabWin));
898 pTabWin->Show();
900 pTabWin->Update();
901 // We must call Update() in order to show the connections in the window correctly. This sounds strange,
902 // but the Listbox has an internal Member which is initialized when the Listbox is first shown (after the Listbox
903 // is filled in Init). This Member will eventually be needed for
904 // GetEntryPos, and then in turn by the Connection, when its starting point to the window must be determined.
906 // the Connections
907 auto rTableCon = pUndoAction->GetTabConnList();
908 for(auto conn : rTableCon)
909 addConnection(conn); // add all connections from the undo action
911 rTableCon.clear();
913 // and add the window's data to the list (of the document)
914 if(_bAppend)
915 m_pView->getController().getTableWindowData().push_back(pTabWin->GetData());
917 m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
919 // and inform the UndoAction that the window belongs to me
920 pUndoAction->SetOwnership(false);
922 bSuccess = true;
924 else
926 // Initialisation failed
927 // (for example when the Connection to the database is not available at the moment)
928 pTabWin->clearListBox();
929 pTabWin->disposeOnce();
933 // show that I have changed the document
934 if(!m_pView->getController().isReadOnly())
935 m_pView->getController().setModified( sal_True );
937 m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
939 return bSuccess;
942 void OQueryTableView::InsertField(const OTableFieldDescRef& rInfo)
944 OSL_ENSURE(getDesignView() != NULL, "OQueryTableView::InsertField : has no Parent !");
945 static_cast<OQueryDesignView*>(getDesignView())->InsertField(rInfo);
948 bool OQueryTableView::ExistsAVisitedConn(const OQueryTableWindow* pFrom) const
950 for(auto conn : getTableConnections())
952 OQueryTableConnection* pTemp = static_cast<OQueryTableConnection*>(conn.get());
953 if (pTemp->IsVisited() &&
954 (pFrom == static_cast< OQueryTableWindow*>(pTemp->GetSourceWin()) || pFrom == static_cast< OQueryTableWindow*>(pTemp->GetDestWin())))
955 return true;
958 return false;
961 void OQueryTableView::onNoColumns_throw()
963 OUString sError( ModuleRes( STR_STATEMENT_WITHOUT_RESULT_SET ) );
964 ::dbtools::throwSQLException( sError, ::dbtools::SQL_GENERAL_ERROR, NULL );
967 bool OQueryTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& _pData) const
969 OQueryTableConnectionData* pQueryData = static_cast<OQueryTableConnectionData*>(_pData.get());
970 return pQueryData && (pQueryData->GetJoinType() == CROSS_JOIN);
973 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */