bump product version to 4.1.6.2
[LibreOffice.git] / dbaccess / source / ui / querydesign / QueryTableView.cxx
blob1971bcce2845739e9ca524a1f8d8f6bc51bd6e91
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 .
21 #include "QueryTableView.hxx"
22 #include "TableFieldInfo.hxx"
23 #include "TableFieldDescription.hxx"
24 #include <tools/diagnose_ex.h>
25 #include <osl/diagnose.h>
26 #include "dbaccess_helpid.hrc"
27 #include "QTableWindow.hxx"
28 #include "QTableConnection.hxx"
29 #include "QTableConnectionData.hxx"
30 #include "QueryDesignView.hxx"
31 #include "querycontroller.hxx"
32 #include "QueryAddTabConnUndoAction.hxx"
33 #include "QueryTabWinShowUndoAct.hxx"
34 #include "browserids.hxx"
35 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
36 #include <com/sun/star/sdbc/XConnection.hpp>
37 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
38 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
39 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
40 #include "JAccess.hxx"
41 #include <com/sun/star/sdbcx/KeyType.hpp>
42 #include <com/sun/star/container/XIndexAccess.hpp>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include "dbustrings.hrc"
45 #include <connectivity/dbtools.hxx>
46 #include <comphelper/sequence.hxx>
47 #include "querydlg.hxx"
48 #include "JoinExchange.hxx"
49 #include <comphelper/extract.hxx>
50 #include "dbu_qry.hrc"
51 #include <vcl/msgbox.hxx>
52 #include "svtools/treelistentry.hxx"
54 using namespace dbaui;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::sdbc;
57 using namespace ::com::sun::star::sdbcx;
58 using namespace ::com::sun::star::beans;
59 using namespace ::com::sun::star::container;
60 using namespace ::com::sun::star::accessibility;
62 //------------------------------------------------------------------------------
63 namespace
65 // -----------------------------------------------------------------------------
66 sal_Bool isColumnInKeyType(const Reference<XIndexAccess>& _rxKeys,const OUString& _rColumnName,sal_Int32 _nKeyType)
68 sal_Bool bReturn = sal_False;
69 if(_rxKeys.is())
71 Reference<XColumnsSupplier> xColumnsSupplier;
72 // search the one and only primary key
73 const sal_Int32 nCount = _rxKeys->getCount();
74 for(sal_Int32 i=0;i< nCount;++i)
76 Reference<XPropertySet> xProp(_rxKeys->getByIndex(i),UNO_QUERY);
77 if(xProp.is())
79 sal_Int32 nKeyType = 0;
80 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
81 if(_nKeyType == nKeyType)
83 xColumnsSupplier.set(xProp,UNO_QUERY);
84 if(xColumnsSupplier.is())
86 Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
87 if(xColumns.is() && xColumns->hasByName(_rColumnName))
89 bReturn = sal_True;
90 break;
97 return bReturn;
99 // -----------------------------------------------------------------------------
100 /** appends a new TabAdd Undo action at controller
101 @param _pView the view which we use
102 @param _pUndoAction the undo action which should be added
103 @param _pConnection the connection for which the undo action should be appended
104 @param _bOwner is the undo action the owner
106 // -----------------------------------------------------------------------------
107 void addUndoAction( OQueryTableView* _pView,
108 OQueryTabConnUndoAction* _pUndoAction,
109 OQueryTableConnection* _pConnection,
110 sal_Bool _bOwner = sal_False)
112 _pUndoAction->SetOwnership(_bOwner);
113 _pUndoAction->SetConnection(_pConnection);
114 _pView->getDesignView()->getController().addUndoActionAndInvalidate(_pUndoAction);
116 // -----------------------------------------------------------------------------
117 /** openJoinDialog opens the join dialog with this connection data
118 @param _pView the view which we use
119 @param _pConnectionData the connection data
121 @return true when OK was pressed otherwise false
123 sal_Bool openJoinDialog(OQueryTableView* _pView,const TTableConnectionData::value_type& _pConnectionData,sal_Bool _bSelectableTables)
125 OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pConnectionData.get());
127 DlgQryJoin aDlg(_pView,_pConnectionData,_pView->GetTabWinMap(),_pView->getDesignView()->getController().getConnection(),_bSelectableTables);
128 sal_Bool bOk = aDlg.Execute() == RET_OK;
129 if( bOk )
131 pData->SetJoinType(aDlg.GetJoinType());
132 _pView->getDesignView()->getController().setModified(sal_True);
135 return bOk;
137 // -----------------------------------------------------------------------------
138 /** connectionModified adds an undo action for the modified connection and forces an redraw
139 @param _pView the view which we use
140 @param _pConnection the connection which was modified
141 @param _bAddUndo true when an undo action should be appended
143 void connectionModified(OQueryTableView* _pView,
144 OTableConnection* _pConnection,
145 sal_Bool _bAddUndo)
147 OSL_ENSURE(_pConnection,"Invalid connection!");
148 _pConnection->UpdateLineList();
150 // add an undo action
151 if ( _bAddUndo )
152 addUndoAction( _pView,
153 new OQueryAddTabConnUndoAction(_pView),
154 static_cast< OQueryTableConnection*>(_pConnection));
155 // redraw
156 _pConnection->RecalcLines();
157 // force an invalidation of the bounding rectangle
158 _pConnection->InvalidateConnection();
160 _pView->Invalidate(INVALIDATE_NOCHILDREN);
162 // -----------------------------------------------------------------------------
163 void addConnections(OQueryTableView* _pView,
164 const OQueryTableWindow& _rSource,
165 const OQueryTableWindow& _rDest,
166 const Reference<XNameAccess>& _rxSourceForeignKeyColumns)
168 if ( _rSource.GetData()->isQuery() || _rDest.GetData()->isQuery() )
169 // nothing to do if one of both denotes a query
170 return;
172 // we found a table in our view where we can insert some connections
173 // the key columns have a property called RelatedColumn
174 // OQueryTableConnectionData aufbauen
175 OQueryTableConnectionData* pNewConnData = new OQueryTableConnectionData( _rSource.GetData(), _rDest.GetData() );
176 TTableConnectionData::value_type aNewConnData(pNewConnData);
178 Reference<XIndexAccess> xReferencedKeys( _rDest.GetData()->getKeys());
179 OUString sRelatedColumn;
181 // iterate through all foreignkey columns to create the connections
182 Sequence< OUString> aElements(_rxSourceForeignKeyColumns->getElementNames());
183 const OUString* pIter = aElements.getConstArray();
184 const OUString* pEnd = pIter + aElements.getLength();
185 for(sal_Int32 i=0;pIter != pEnd;++pIter,++i)
187 Reference<XPropertySet> xColumn;
188 if ( !( _rxSourceForeignKeyColumns->getByName(*pIter) >>= xColumn ) )
190 OSL_FAIL( "addConnections: invalid foreign key column!" );
191 continue;
194 pNewConnData->SetFieldType(JTCS_FROM,TAB_NORMAL_FIELD);
196 xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn;
197 pNewConnData->SetFieldType(JTCS_TO,isColumnInKeyType(xReferencedKeys,sRelatedColumn,KeyType::PRIMARY) ? TAB_PRIMARY_FIELD : TAB_NORMAL_FIELD);
200 Sequence< sal_Int16> aFind(::comphelper::findValue(_rSource.GetOriginalColumns()->getElementNames(),*pIter,sal_True));
201 if(aFind.getLength())
202 pNewConnData->SetFieldIndex(JTCS_FROM,aFind[0]+1);
203 else
204 OSL_FAIL("Column not found!");
206 // get the position inside the tabe
207 Reference<XNameAccess> xRefColumns = _rDest.GetOriginalColumns();
208 if(xRefColumns.is())
210 Sequence< sal_Int16> aFind(::comphelper::findValue(xRefColumns->getElementNames(),sRelatedColumn,sal_True));
211 if(aFind.getLength())
212 pNewConnData->SetFieldIndex(JTCS_TO,aFind[0]+1);
213 else
214 OSL_FAIL("Column not found!");
216 pNewConnData->AppendConnLine(*pIter,sRelatedColumn);
218 // now add the Conn itself
219 OQueryTableConnection aNewConn(_pView, aNewConnData);
220 // refering to the local variable is not important, as NotifyQueryTabConn creates a new copy
221 // to add me (if not existent)
222 _pView->NotifyTabConnection(aNewConn, sal_False);
223 // don't create an Undo-Action for the new connection : the connection is
224 // covered by the Undo-Action for the tabwin, as the "Undo the insert" will
225 // automatically remove all connections adjacent to the win.
226 // (Because of this automatism we would have an ownerhsip ambiguity for
227 // the connection data if we would insert the conn-Undo-Action)
231 //==================================================================
232 // class OQueryTableView
233 //==================================================================
234 DBG_NAME(OQueryTableView)
235 //------------------------------------------------------------------------
236 OQueryTableView::OQueryTableView( Window* pParent,OQueryDesignView* pView)
237 : OJoinTableView( pParent,pView)
239 DBG_CTOR(OQueryTableView,NULL);
240 SetHelpId(HID_CTL_QRYDGNTAB);
243 //------------------------------------------------------------------------
244 OQueryTableView::~OQueryTableView()
246 DBG_DTOR(OQueryTableView,NULL);
249 //------------------------------------------------------------------------
250 sal_Int32 OQueryTableView::CountTableAlias(const String& rName, sal_Int32& rMax)
252 DBG_CHKTHIS(OQueryTableView,NULL);
253 sal_Int32 nRet = 0;
255 OTableWindowMapIterator aIter = GetTabWinMap()->find(rName);
256 while(aIter != GetTabWinMap()->end())
258 OUString aNewName = OUString(rName) + "_" + OUString::number(++nRet);
259 aIter = GetTabWinMap()->find(aNewName);
262 rMax = nRet;
264 return nRet;
266 //------------------------------------------------------------------------
267 void OQueryTableView::ReSync()
269 DBG_CHKTHIS(OQueryTableView,NULL);
270 TTableWindowData* pTabWinDataList = m_pView->getController().getTableWindowData();
271 OSL_ENSURE((getTableConnections()->size()==0) && (GetTabWinMap()->size()==0),
272 "before calling OQueryTableView::ReSync() please call ClearAll !");
275 // I need a collection of all window names that cannot be created so that I do not initialize connections for them.
276 ::std::vector<String> arrInvalidTables;
278 TTableWindowData::reverse_iterator aIter = pTabWinDataList->rbegin();
279 // Create the window and add it
281 for(;aIter != pTabWinDataList->rend();++aIter)
283 OQueryTableWindowData* pData = static_cast<OQueryTableWindowData*>(aIter->get());
284 OTableWindow* pTabWin = createWindow(*aIter);
286 // I dont't use ShowTabWin as this adds the window data to the list of documents.
287 // This would be bad as I am getting them from there.
288 // Instead, I do it step by step
289 if (!pTabWin->Init())
291 // The initialisation has gone wrong, this TabWin is not available, so
292 // I must clean up the data and the document
293 pTabWin->clearListBox();
294 delete pTabWin;
295 arrInvalidTables.push_back(pData->GetAliasName());
297 pTabWinDataList->erase( ::std::remove(pTabWinDataList->begin(),pTabWinDataList->end(),*aIter) ,pTabWinDataList->end());
298 continue;
301 (*GetTabWinMap())[pData->GetAliasName()] = pTabWin; // add at the beginning as I am going backwards through the DataList
302 // Use the default if there is no position or size
303 if (!pData->HasPosition() && !pData->HasSize())
304 SetDefaultTabWinPosSize(pTabWin);
306 pTabWin->Show();
309 // Add the connections
310 TTableConnectionData* pTabConnDataList = m_pView->getController().getTableConnectionData();
311 TTableConnectionData::reverse_iterator aConIter = pTabConnDataList->rbegin();
313 for(;aConIter != pTabConnDataList->rend();++aConIter)
315 OQueryTableConnectionData* pTabConnData = static_cast<OQueryTableConnectionData*>(aConIter->get());
317 // do both tables for the connection exist ?
318 String strTabExistenceTest = pTabConnData->getReferencingTable()->GetWinName();
319 sal_Bool bInvalid = ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
320 strTabExistenceTest = pTabConnData->getReferencedTable()->GetWinName();
321 bInvalid = bInvalid && ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
323 if (bInvalid)
325 // no -> bad luck, no connection
326 pTabConnDataList->erase( ::std::remove(pTabConnDataList->begin(),pTabConnDataList->end(),*aConIter) ,pTabConnDataList->end());
327 continue;
330 // adds a new connection to join view and notifies our accessible and invaldates the controller
331 addConnection(new OQueryTableConnection(this, *aConIter));
335 //------------------------------------------------------------------------
336 void OQueryTableView::ClearAll()
338 DBG_CHKTHIS(OQueryTableView,NULL);
339 OJoinTableView::ClearAll();
341 SetUpdateMode(sal_True);
342 m_pView->getController().setModified(sal_True);
345 // -----------------------------------------------------------------------------
346 OTableWindow* OQueryTableView::createWindow(const TTableWindowData::value_type& _pData)
348 return new OQueryTableWindow(this,_pData);
351 //------------------------------------------------------------------------------
352 void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, sal_Bool _bCreateUndoAction)
354 DBG_CHKTHIS(OQueryTableView,NULL);
355 // let's first check if I have the connection already
356 OQueryTableConnection* pTabConn = NULL;
357 const ::std::vector<OTableConnection*>* pConnections = getTableConnections();
358 ::std::vector<OTableConnection*>::const_iterator aEnd = pConnections->end();
359 ::std::vector<OTableConnection*>::const_iterator aIter = ::std::find( pConnections->begin(),
360 aEnd,
361 static_cast<const OTableConnection*>(&rNewConn)
363 if(aIter == aEnd )
365 aIter = pConnections->begin();
366 for(;aIter != aEnd;++aIter)
368 if(*static_cast<OQueryTableConnection*>(*aIter) == rNewConn)
370 pTabConn = static_cast<OQueryTableConnection*>(*aIter);
371 break;
375 else
376 pTabConn = static_cast<OQueryTableConnection*>(*aIter);
378 // no -> insert
379 if (pTabConn == NULL)
381 // the new data ...
382 OQueryTableConnectionData* pNewData = static_cast< OQueryTableConnectionData*>(rNewConn.GetData()->NewInstance());
383 pNewData->CopyFrom(*rNewConn.GetData());
384 TTableConnectionData::value_type aData(pNewData);
385 OQueryTableConnection* pNewConn = new OQueryTableConnection(this, aData);
386 GetConnection(pNewConn);
388 connectionModified(this,pNewConn,_bCreateUndoAction);
391 // -----------------------------------------------------------------------------
392 OTableWindowData* OQueryTableView::CreateImpl(const OUString& _rComposedName
393 ,const OUString& _sTableName
394 ,const OUString& _rWinName)
396 return new OQueryTableWindowData( _rComposedName, _sTableName,_rWinName );
398 //------------------------------------------------------------------------------
399 void OQueryTableView::AddTabWin(const OUString& _rTableName, const OUString& _rAliasName, sal_Bool bNewTable)
401 DBG_CHKTHIS(OQueryTableView,NULL);
402 // this method has been inherited from the base class, linking back to the parent and which constructs
403 // an Alias and which passes on to my other AddTabWin
405 // pity _rTableName is fully qualified, OQueryDesignView expects a string which only
406 // contains schema and tables but no catalog.
407 Reference< XConnection> xConnection = m_pView->getController().getConnection();
408 if(!xConnection.is())
409 return;
412 Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
413 OUString sCatalog, sSchema, sTable;
414 ::dbtools::qualifiedNameComponents(xMetaData,
415 _rTableName,
416 sCatalog,
417 sSchema,
418 sTable,
419 ::dbtools::eInDataManipulation);
420 OUString sRealName(sSchema);
421 if (!sRealName.isEmpty())
422 sRealName+= OUString('.');
423 sRealName += sTable;
425 AddTabWin(_rTableName, sRealName, _rAliasName, bNewTable);
427 catch(SQLException&)
429 OSL_FAIL("qualifiedNameComponents");
432 // -----------------------------------------------------------------------------
433 // find the table which has a foreign key with this referencedTable name
434 Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,const OUString& _rReferencedTable)
436 if(!_rxKeys.is())
437 return Reference<XPropertySet>();
439 if ( !_rxKeys.is() )
440 return Reference<XPropertySet>();
441 // search the one and only primary key
442 const sal_Int32 nCount = _rxKeys->getCount();
443 for(sal_Int32 i=0;i<nCount ;++i)
445 Reference<XPropertySet> xKey(_rxKeys->getByIndex(i),UNO_QUERY);
446 if(xKey.is())
448 sal_Int32 nKeyType = 0;
449 xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
450 if(KeyType::FOREIGN == nKeyType)
452 OUString sReferencedTable;
453 xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable;
454 // TODO check case
455 if(sReferencedTable == _rReferencedTable)
456 return xKey;
460 return Reference<XPropertySet>();
462 //------------------------------------------------------------------------------
463 void OQueryTableView::AddTabWin(const OUString& _rComposedName, const OUString& _rTableName, const OUString& strAlias, sal_Bool bNewTable)
465 DBG_CHKTHIS(OQueryTableView,NULL);
466 OSL_ENSURE(!_rTableName.isEmpty() || !strAlias.isEmpty(), "OQueryTableView::AddTabWin : no tables or aliases !");
467 // If the table is not set, then it is a dummy window, but at least the alias must be set
469 // build a new data structure
470 // first check if this already has its data
471 sal_Bool bAppend = bNewTable;
472 TTableWindowData::value_type pNewTabWinData;
473 TTableWindowData* pWindowData = getDesignView()->getController().getTableWindowData();
474 TTableWindowData::iterator aWinIter = pWindowData->begin();
475 TTableWindowData::iterator aWinEnd = pWindowData->end();
476 for(;aWinIter != aWinEnd;++aWinIter)
478 pNewTabWinData = *aWinIter;
479 if (pNewTabWinData && pNewTabWinData->GetWinName() == strAlias && pNewTabWinData->GetComposedName() == _rComposedName && pNewTabWinData->GetTableName() == _rTableName)
480 break;
482 if ( !bAppend )
483 bAppend = ( aWinIter == aWinEnd );
484 if ( bAppend )
485 pNewTabWinData = createTableWindowData(_rComposedName, _rTableName, strAlias);
486 // I do not need to add TabWinData to the DocShell list, ShowTabWin does that.
488 // Create a new window
489 OQueryTableWindow* pNewTabWin = static_cast<OQueryTableWindow*>(createWindow(pNewTabWinData));
490 // No need to initialize, as that happens in ShowTabWin
492 // New UndoAction
493 OQueryTabWinShowUndoAct* pUndoAction = new OQueryTabWinShowUndoAct(this);
494 pUndoAction->SetTabWin(pNewTabWin); // Window
495 sal_Bool bSuccess = ShowTabWin(pNewTabWin, pUndoAction,bAppend);
496 if(!bSuccess)
498 // reset table window
499 pUndoAction->SetTabWin(NULL);
500 pUndoAction->SetOwnership(sal_False);
502 delete pUndoAction;
503 return;
506 // Show the relations between the individual tables
507 OTableWindowMap* pTabWins = GetTabWinMap();
508 if(bNewTable && !pTabWins->empty() && !_rTableName.isEmpty())
510 modified();
511 if ( m_pAccessible )
512 m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
513 Any(),
514 makeAny(pNewTabWin->GetAccessible())
517 do {
519 if ( pNewTabWin->GetData()->isQuery() )
520 break;
524 //////////////////////////////////////////////////////////////////////
525 // find relations between the table an the tables already inserted
526 Reference< XIndexAccess> xKeyIndex = pNewTabWin->GetData()->getKeys();
527 if ( !xKeyIndex.is() )
528 break;
530 Reference<XNameAccess> xFKeyColumns;
531 OUString aReferencedTable;
532 Reference<XColumnsSupplier> xColumnsSupplier;
534 const sal_Int32 nKeyCount = xKeyIndex->getCount();
535 for ( sal_Int32 i=0; i<nKeyCount ; ++i )
537 Reference< XPropertySet > xProp( xKeyIndex->getByIndex(i), UNO_QUERY_THROW );
538 xColumnsSupplier.set( xProp, UNO_QUERY_THROW );
539 xFKeyColumns.set( xColumnsSupplier->getColumns(), UNO_QUERY_THROW );
541 sal_Int32 nKeyType = 0;
542 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
544 switch ( nKeyType )
546 case KeyType::FOREIGN:
547 { // our new table has a foreign key
548 // so look if the referenced table is already in our list
549 xProp->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= aReferencedTable;
550 OSL_ENSURE(!aReferencedTable.isEmpty(),"Foreign key without referencedTableName");
552 OTableWindowMap::const_iterator aIter = pTabWins->find(aReferencedTable);
553 OTableWindowMap::const_iterator aEnd = pTabWins->end();
554 if(aIter == aEnd)
556 for(aIter = pTabWins->begin();aIter != aEnd;++aIter)
558 OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second);
559 OSL_ENSURE( pTabWinTmp,"TableWindow is null!" );
560 if ( pTabWinTmp != pNewTabWin && pTabWinTmp->GetComposedName() == aReferencedTable )
561 break;
564 if ( aIter != aEnd && pNewTabWin != aIter->second )
565 addConnections( this, *pNewTabWin, *static_cast<OQueryTableWindow*>(aIter->second), xFKeyColumns );
567 break;
569 case KeyType::PRIMARY:
571 // we have a primary key so look in our list if there exsits a key which this is refered to
572 OTableWindowMap::const_iterator aIter = pTabWins->begin();
573 OTableWindowMap::const_iterator aEnd = pTabWins->end();
574 for(;aIter != aEnd;++aIter)
576 OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second);
577 if ( pTabWinTmp == pNewTabWin )
578 continue;
580 if ( pTabWinTmp->GetData()->isQuery() )
581 continue;
583 OSL_ENSURE(pTabWinTmp,"TableWindow is null!");
584 Reference< XPropertySet > xFKKey = getKeyReferencedTo( pTabWinTmp->GetData()->getKeys(), pNewTabWin->GetComposedName() );
585 if ( !xFKKey.is() )
586 continue;
588 Reference<XColumnsSupplier> xFKColumnsSupplier( xFKKey, UNO_QUERY_THROW );
589 Reference< XNameAccess > xTColumns( xFKColumnsSupplier->getColumns(), UNO_QUERY_THROW );
590 addConnections( this, *pTabWinTmp, *pNewTabWin, xTColumns );
593 break;
597 catch( const Exception& )
599 DBG_UNHANDLED_EXCEPTION();
602 } while ( false );
605 // My parent needs to be informed about the delete
606 m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
608 if (bSuccess && m_lnkTabWinsChangeHandler.IsSet())
610 TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_ADDED_WIN, pNewTabWin->GetAliasName());
611 m_lnkTabWinsChangeHandler.Call(&aHint);
614 // -----------------------------------------------------------------------------
615 // -----------------------------------------------------------------------------
616 void OQueryTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest)
618 DBG_CHKTHIS(OQueryTableView,NULL);
619 OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(jxdSource.pListBox->GetTabWin());
620 OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(jxdDest.pListBox->GetTabWin());
622 String aSourceFieldName, aDestFieldName;
623 aSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry);
624 aDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry);
626 OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
627 if ( !pConn )
629 // new data object
630 OQueryTableConnectionData* pNewConnectionData = new OQueryTableConnectionData(pSourceWin->GetData(), pDestWin->GetData());
631 TTableConnectionData::value_type aNewConnectionData(pNewConnectionData);
633 sal_uInt32 nSourceFieldIndex, nDestFieldIndex;
634 ETableFieldType eSourceFieldType, eDestFieldType;
636 // Get name/position/type of both affected fields ...
637 // Source
639 nSourceFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry);
640 eSourceFieldType = static_cast< OTableFieldInfo*>(jxdSource.pEntry->GetUserData())->GetKeyType();
642 // Dest
644 nDestFieldIndex = jxdDest.pListBox->GetModel()->GetAbsPos(jxdDest.pEntry);
645 eDestFieldType = static_cast< OTableFieldInfo*>(jxdDest.pEntry->GetUserData())->GetKeyType();
647 // ... and set them
648 pNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex);
649 pNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex);
651 pNewConnectionData->SetFieldType(JTCS_FROM, eSourceFieldType);
652 pNewConnectionData->SetFieldType(JTCS_TO, eDestFieldType);
654 pNewConnectionData->AppendConnLine( aSourceFieldName,aDestFieldName );
656 OQueryTableConnection aNewConnection(this, aNewConnectionData);
657 NotifyTabConnection(aNewConnection);
658 // As usual with NotifyTabConnection, using a local variable is fine because a copy is made
660 else
662 // the connection could point on the other side
663 if(pConn->GetSourceWin() == pDestWin)
665 String aTmp(aSourceFieldName);
666 aSourceFieldName = aDestFieldName;
667 aDestFieldName = aTmp;
670 pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName );
672 connectionModified(this,pConn,sal_False);
675 // -----------------------------------------------------------------------------
676 void OQueryTableView::ConnDoubleClicked(OTableConnection* pConnection)
678 DBG_CHKTHIS(OQueryTableView,NULL);
679 if( openJoinDialog(this,pConnection->GetData(),sal_False) )
681 connectionModified(this,pConnection,sal_False);
682 SelectConn( pConnection );
685 // -----------------------------------------------------------------------------
686 void OQueryTableView::createNewConnection()
688 TTableConnectionData::value_type pData(new OQueryTableConnectionData());
689 if( openJoinDialog(this,pData,sal_True) )
691 OTableWindowMap* pMap = GetTabWinMap();
692 OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>((*pMap)[pData->getReferencingTable()->GetWinName()]);
693 OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>((*pMap)[pData->getReferencedTable()->GetWinName()]);
694 // first we have to look if the this connection already exists
695 OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
696 sal_Bool bNew = sal_True;
697 if ( pConn )
699 pConn->GetData()->CopyFrom( *pData );
700 bNew = sal_False;
702 else
704 // create a new conenction and append it
705 OQueryTableConnection* pQConn = new OQueryTableConnection(this, pData);
706 GetConnection(pQConn);
707 pConn = pQConn;
709 connectionModified(this,pConn,bNew);
710 if ( !bNew && pConn == GetSelectedConn() ) // our connection was selected before so we have to reselect it
711 SelectConn( pConn );
714 //------------------------------------------------------------------------------
715 bool OQueryTableView::RemoveConnection( OTableConnection* _pConnection,sal_Bool /*_bDelete*/ )
717 DBG_CHKTHIS(OQueryTableView,NULL);
719 // we don't want that our connection will be deleted, we put it in the undo manager
720 bool bRet = OJoinTableView::RemoveConnection( _pConnection,sal_False);
722 // add undo action
723 addUndoAction( this,
724 new OQueryDelTabConnUndoAction(this),
725 static_cast< OQueryTableConnection*>(_pConnection),
726 sal_True);
727 return bRet;
730 //------------------------------------------------------------------------------
731 void OQueryTableView::KeyInput( const KeyEvent& rEvt )
733 DBG_CHKTHIS(OQueryTableView,NULL);
734 OJoinTableView::KeyInput( rEvt );
737 //------------------------------------------------------------------------------
738 OQueryTableWindow* OQueryTableView::FindTable(const String& rAliasName)
740 DBG_CHKTHIS(OQueryTableView,NULL);
741 OSL_ENSURE(rAliasName.Len(), "OQueryTableView::FindTable : the AliasName should not be empty !");
742 // (it is harmless but does not make sense and indicates that there is probably an error in the caller)
743 OTableWindowMap::const_iterator aIter = GetTabWinMap()->find(rAliasName);
744 if(aIter != GetTabWinMap()->end())
745 return static_cast<OQueryTableWindow*>(aIter->second);
746 return NULL;
749 //------------------------------------------------------------------------------
750 sal_Bool OQueryTableView::FindTableFromField(const String& rFieldName, OTableFieldDescRef& rInfo, sal_uInt16& rCnt)
752 DBG_CHKTHIS(OQueryTableView,NULL);
753 rCnt = 0;
754 OTableWindowMap::const_iterator aIter = GetTabWinMap()->begin();
755 OTableWindowMap::const_iterator aEnd = GetTabWinMap()->end();
756 for(;aIter != aEnd;++aIter)
758 if(static_cast<OQueryTableWindow*>(aIter->second)->ExistsField(rFieldName, rInfo))
759 ++rCnt;
762 return rCnt == 1;
765 //------------------------------------------------------------------------------
766 void OQueryTableView::RemoveTabWin(OTableWindow* pTabWin)
768 DBG_CHKTHIS(OQueryTableView,NULL);
769 OSL_ENSURE(pTabWin != NULL, "OQueryTableView::RemoveTabWin : Window should not be NULL !");
771 // I need my parent so it can be informed about the deletion
772 OQueryDesignView* pParent = static_cast<OQueryDesignView*>(getDesignView());
774 SfxUndoManager& rUndoMgr = m_pView->getController().GetUndoManager();
775 rUndoMgr.EnterListAction( String( ModuleRes(STR_QUERY_UNDO_TABWINDELETE) ), String() );
777 // add the Undo-Action
778 OQueryTabWinDelUndoAct* pUndoAction = new OQueryTabWinDelUndoAct(this);
779 pUndoAction->SetTabWin(static_cast< OQueryTableWindow*>(pTabWin));
781 // and hide the window
782 HideTabWin(static_cast< OQueryTableWindow*>(pTabWin), pUndoAction);
784 // Undo Actions and delete the fields in SelectionBrowseBox
785 pParent->TableDeleted( static_cast< OQueryTableWindowData*>(pTabWin->GetData().get())->GetAliasName() );
787 m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
788 rUndoMgr.LeaveListAction();
790 if (m_lnkTabWinsChangeHandler.IsSet())
792 TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_REMOVED_WIN, static_cast< OQueryTableWindow*>(pTabWin)->GetAliasName());
793 m_lnkTabWinsChangeHandler.Call(&aHint);
796 modified();
797 if ( m_pAccessible )
798 m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
799 makeAny(pTabWin->GetAccessible()),
800 Any()
804 //------------------------------------------------------------------------
805 void OQueryTableView::EnsureVisible(const OTableWindow* pWin)
807 DBG_CHKTHIS(OQueryTableView,NULL);
809 Invalidate(INVALIDATE_NOCHILDREN);
810 OJoinTableView::EnsureVisible(pWin);
813 //------------------------------------------------------------------------
814 void OQueryTableView::GetConnection(OQueryTableConnection* pConn)
816 DBG_CHKTHIS(OQueryTableView,NULL);
817 // add to me and the document
819 addConnection( pConn );
822 //------------------------------------------------------------------------
823 void OQueryTableView::DropConnection(OQueryTableConnection* pConn)
825 DBG_CHKTHIS(OQueryTableView,NULL);
826 // Pay attention to the selection
827 // remove from me and the document
828 RemoveConnection( pConn ,sal_False);
831 //------------------------------------------------------------------------
832 void OQueryTableView::HideTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction )
834 DBG_CHKTHIS(OQueryTableView,NULL);
835 OTableWindowMap* pTabWins = GetTabWinMap();
836 OSL_ENSURE(pTabWins != NULL, "OQueryTableView::HideTabWin : have no TabWins !");
838 if (pTabWin)
840 // Window
841 // save the position in its data
842 getDesignView()->SaveTabWinUIConfig(pTabWin);
843 // (I need to go via the parent, as only the parent knows the position of the scrollbars)
844 // and then out of the TabWins list and hide
845 OTableWindowMap::iterator aIter = pTabWins->begin();
846 OTableWindowMap::iterator aEnd = pTabWins->end();
847 for ( ;aIter != aEnd ; ++aIter )
848 if ( aIter->second == pTabWin )
850 pTabWins->erase( aIter );
851 break;
854 pTabWin->Hide(); // do not destroy it, as it is still in the undo list!!
856 // the TabWin data must also be passed out of my responsibility
857 TTableWindowData* pTabWinDataList = m_pView->getController().getTableWindowData();
858 pTabWinDataList->erase( ::std::remove(pTabWinDataList->begin(),pTabWinDataList->end(),pTabWin->GetData()),pTabWinDataList->end());
859 // The data should not be destroyed as TabWin itself - which is still alive - needs them
860 // Either it goes back into my responsibility, (via ShowTabWin), then I add the data back,
861 // or the Undo-Action, which currently has full responsibility for the window
862 // and its data, gets destroyed and destroys both the window and its data
864 if (m_pLastFocusTabWin == pTabWin)
865 m_pLastFocusTabWin = NULL;
867 // collect connections belonging to the window and pass to UndoAction
868 sal_Int16 nCnt = 0;
869 const ::std::vector<OTableConnection*>* pTabConList = getTableConnections();
870 ::std::vector<OTableConnection*>::const_iterator aIter2 = pTabConList->begin();
871 for(;aIter2 != pTabConList->end();)// the end may change
873 OQueryTableConnection* pTmpEntry = static_cast<OQueryTableConnection*>(*aIter2);
874 OSL_ENSURE(pTmpEntry,"OQueryTableConnection is null!");
875 if( pTmpEntry->GetAliasName(JTCS_FROM) == pTabWin->GetAliasName() ||
876 pTmpEntry->GetAliasName(JTCS_TO) == pTabWin->GetAliasName() )
878 // add to undo list
879 pUndoAction->InsertConnection(pTmpEntry);
881 // call base class because we append an undo action
882 // but this time we are in a undo action list
883 OJoinTableView::RemoveConnection(pTmpEntry,sal_False);
884 aIter2 = pTabConList->begin();
885 ++nCnt;
887 else
888 ++aIter2;
891 if (nCnt)
892 InvalidateConnections();
894 m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
896 // inform the UndoAction that the window and connections belong to it
897 pUndoAction->SetOwnership(sal_True);
899 // by doing so, we have modified the document
900 m_pView->getController().setModified( sal_True );
901 m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
905 //------------------------------------------------------------------------
906 sal_Bool OQueryTableView::ShowTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction,sal_Bool _bAppend )
908 DBG_CHKTHIS(OQueryTableView,NULL);
910 sal_Bool bSuccess = sal_False;
912 if (pTabWin)
914 if (pTabWin->Init())
916 TTableWindowData::value_type pData = pTabWin->GetData();
917 OSL_ENSURE(pData != NULL, "OQueryTableView::ShowTabWin : TabWin has no data !");
918 // If there is a position and size defined, we use them
919 if (pData->HasPosition() && pData->HasSize())
921 Size aSize(CalcZoom(pData->GetSize().Width()),CalcZoom(pData->GetSize().Height()));
922 pTabWin->SetPosSizePixel(pData->GetPosition(), aSize);
924 else
925 // else set a default position
926 SetDefaultTabWinPosSize(pTabWin);
928 // Show the window and add to the list
929 OUString sName = static_cast< OQueryTableWindowData*>(pData.get())->GetAliasName();
930 OSL_ENSURE(GetTabWinMap()->find(sName) == GetTabWinMap()->end(),"Alias name already in list!");
931 GetTabWinMap()->insert(OTableWindowMap::value_type(sName,pTabWin));
933 pTabWin->Show();
935 pTabWin->Update();
936 // We must call Update() in order to show the connections in the window correctly. This sounds strange,
937 // but the Listbox has an internal Member which is initialized when the Listbox is first shown (after the Listbox
938 // is filled in Init). This Member will eventually be needed for
939 // GetEntryPos, and then in turn by the Connection, when its starting point to the window must be determined.
941 // the Connections
942 ::std::vector<OTableConnection*>* pTableCon = pUndoAction->GetTabConnList();
943 ::std::vector<OTableConnection*>::iterator aIter = pTableCon->begin();
944 ::std::vector<OTableConnection*>::iterator aEnd = pTableCon->end();
946 for(;aIter != aEnd;++aIter)
947 addConnection(*aIter); // add all connections from the undo action
949 pTableCon->clear();
951 // and add the window's data to the list (of the document)
952 if(_bAppend)
953 m_pView->getController().getTableWindowData()->push_back(pTabWin->GetData());
955 m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
957 // and inform the UndoAction that the window belongs to me
958 pUndoAction->SetOwnership(sal_False);
960 bSuccess = sal_True;
962 else
964 // Initialisation failed
965 // (for example when the Connection to the database is not available at the moment)
966 pTabWin->clearListBox();
967 delete pTabWin;
971 // show that I have changed the document
972 if(!m_pView->getController().isReadOnly())
973 m_pView->getController().setModified( sal_True );
975 m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
977 return bSuccess;
979 //------------------------------------------------------------------------
980 void OQueryTableView::InsertField(const OTableFieldDescRef& rInfo)
982 DBG_CHKTHIS(OQueryTableView,NULL);
983 OSL_ENSURE(getDesignView() != NULL, "OQueryTableView::InsertField : has no Parent !");
984 static_cast<OQueryDesignView*>(getDesignView())->InsertField(rInfo);
986 //------------------------------------------------------------------------------
987 sal_Bool OQueryTableView::ExistsAVisitedConn(const OQueryTableWindow* pFrom) const
989 DBG_CHKTHIS(OQueryTableView,NULL);
990 const ::std::vector<OTableConnection*>* pList = getTableConnections();
991 if (pList)
993 ::std::vector<OTableConnection*>::const_iterator aIter = pList->begin();
994 ::std::vector<OTableConnection*>::const_iterator aEnd = pList->end();
995 for(;aIter != aEnd;++aIter)
997 OQueryTableConnection* pTemp = static_cast<OQueryTableConnection*>(*aIter);
998 if (pTemp->IsVisited() &&
999 (pFrom == static_cast< OQueryTableWindow*>(pTemp->GetSourceWin()) || pFrom == static_cast< OQueryTableWindow*>(pTemp->GetDestWin())))
1000 return pTemp != NULL;
1004 return sal_False;
1006 // -----------------------------------------------------------------------------
1007 void OQueryTableView::onNoColumns_throw()
1009 String sError( ModuleRes( STR_STATEMENT_WITHOUT_RESULT_SET ) );
1010 ::dbtools::throwSQLException( sError, ::dbtools::SQL_GENERAL_ERROR, NULL );
1012 //------------------------------------------------------------------------------
1013 bool OQueryTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& _pData) const
1015 OQueryTableConnectionData* pQueryData = static_cast<OQueryTableConnectionData*>(_pData.get());
1016 return pQueryData && (pQueryData->GetJoinType() == CROSS_JOIN);
1018 // -----------------------------------------------------------------------------
1020 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */