merge the formfield patch from ooo-build
[ooovba.git] / dbaccess / source / ui / querydesign / QueryTableView.cxx
blob26b49474395c10cce9426474ec914bf1f2ca159a
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: QueryTableView.cxx,v $
10 * $Revision: 1.45 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_dbaccess.hxx"
34 #ifndef DBAUI_QUERYTABLEVIEW_HXX
35 #include "QueryTableView.hxx"
36 #endif
37 #ifndef DBAUI_TABLEFIELDINFO_HXX
38 #include "TableFieldInfo.hxx"
39 #endif
40 #ifndef DBAUI_TABLEFIELDDESC_HXX
41 #include "TableFieldDescription.hxx"
42 #endif
43 #ifndef _TOOLS_DEBUG_HXX
44 #include <tools/debug.hxx>
45 #endif
46 #ifndef TOOLS_DIAGNOSE_EX_H
47 #include <tools/diagnose_ex.h>
48 #endif
49 #ifndef _DBA_DBACCESS_HELPID_HRC_
50 #include "dbaccess_helpid.hrc"
51 #endif
52 #ifndef DBAUI_QUERY_TABLEWINDOW_HXX
53 #include "QTableWindow.hxx"
54 #endif
55 #ifndef DBAUI_QUERYTABLECONNECTION_HXX
56 #include "QTableConnection.hxx"
57 #endif
58 #ifndef DBAUI_QTABLECONNECTIONDATA_HXX
59 #include "QTableConnectionData.hxx"
60 #endif
61 #ifndef DBAUI_QUERYDESIGNVIEW_HXX
62 #include "QueryDesignView.hxx"
63 #endif
64 #ifndef DBAUI_QUERYCONTROLLER_HXX
65 #include "querycontroller.hxx"
66 #endif
67 #ifndef DBAUI_QUERYADDTABCONNUNDOACTION_HXX
68 #include "QueryAddTabConnUndoAction.hxx"
69 #endif
70 #ifndef DBAUI_QUERYTABWINSHOWUNDOACT_HXX
71 #include "QueryTabWinShowUndoAct.hxx"
72 #endif
73 #ifndef DBACCESS_UI_BROWSER_ID_HXX
74 #include "browserids.hxx"
75 #endif
76 #ifndef _COM_SUN_STAR_SDBCX_XTABLESSUPPLIER_HPP_
77 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
78 #endif
79 #ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
80 #include <com/sun/star/sdbc/XConnection.hpp>
81 #endif
82 #ifndef _COM_SUN_STAR_SDBCX_XKEYSSUPPLIER_HPP_
83 #include <com/sun/star/sdbcx/XKeysSupplier.hpp>
84 #endif
85 #ifndef _COM_SUN_STAR_SDBCX_XCOLUMNSSUPPLIER_HPP_
86 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
87 #endif
88 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
89 #ifndef DBACCESS_JACCESS_HXX
90 #include "JAccess.hxx"
91 #endif
92 #ifndef _COM_SUN_STAR_SDBCX_KEYTYPE_HPP_
93 #include <com/sun/star/sdbcx/KeyType.hpp>
94 #endif
95 #ifndef _COM_SUN_STAR_CONTAINER_XINDEXACCESS_HPP_
96 #include <com/sun/star/container/XIndexAccess.hpp>
97 #endif
98 #ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
99 #include <com/sun/star/beans/XPropertySet.hpp>
100 #endif
101 #ifndef DBACCESS_SHARED_DBUSTRINGS_HRC
102 #include "dbustrings.hrc"
103 #endif
104 #ifndef _CONNECTIVITY_DBTOOLS_HXX_
105 #include <connectivity/dbtools.hxx>
106 #endif
107 #ifndef _COMPHELPER_SEQUENCE_HXX_
108 #include <comphelper/sequence.hxx>
109 #endif
110 #ifndef DBAUI_QUERYDLG_HXX
111 #include "querydlg.hxx"
112 #endif
113 #ifndef DBAUI_JOINEXCHANGE_HXX
114 #include "JoinExchange.hxx"
115 #endif
116 #ifndef _COMPHELPER_EXTRACT_HXX_
117 #include <comphelper/extract.hxx>
118 #endif
119 #ifndef DBAUI_QUERYDESIGNVIEW_HXX
120 #include "QueryDesignView.hxx"
121 #endif
122 #ifndef _DBU_QRY_HRC_
123 #include "dbu_qry.hrc"
124 #endif
125 #ifndef _SV_MSGBOX_HXX
126 #include <vcl/msgbox.hxx>
127 #endif
129 using namespace dbaui;
130 using namespace ::com::sun::star::uno;
131 using namespace ::com::sun::star::sdbc;
132 using namespace ::com::sun::star::sdbcx;
133 using namespace ::com::sun::star::beans;
134 using namespace ::com::sun::star::container;
135 using namespace ::com::sun::star::accessibility;
137 //------------------------------------------------------------------------------
138 namespace
140 // -----------------------------------------------------------------------------
141 sal_Bool isColumnInKeyType(const Reference<XIndexAccess>& _rxKeys,const ::rtl::OUString& _rColumnName,sal_Int32 _nKeyType)
143 sal_Bool bReturn = sal_False;
144 if(_rxKeys.is())
146 Reference<XColumnsSupplier> xColumnsSupplier;
147 // search the one and only primary key
148 const sal_Int32 nCount = _rxKeys->getCount();
149 for(sal_Int32 i=0;i< nCount;++i)
151 Reference<XPropertySet> xProp(_rxKeys->getByIndex(i),UNO_QUERY);
152 if(xProp.is())
154 sal_Int32 nKeyType = 0;
155 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
156 if(_nKeyType == nKeyType)
158 xColumnsSupplier.set(xProp,UNO_QUERY);
159 if(xColumnsSupplier.is())
161 Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
162 if(xColumns.is() && xColumns->hasByName(_rColumnName))
164 bReturn = sal_True;
165 break;
172 return bReturn;
174 // -----------------------------------------------------------------------------
175 /** appends a new TabAdd Undo action at controller
176 @param _pView the view which we use
177 @param _pUndoAction the undo action which should be added
178 @param _pConnection the connection for which the undo action should be appended
179 @param _bOwner is the undo action the owner
181 // -----------------------------------------------------------------------------
182 void addUndoAction( OQueryTableView* _pView,
183 OQueryTabConnUndoAction* _pUndoAction,
184 OQueryTableConnection* _pConnection,
185 sal_Bool _bOwner = sal_False)
187 _pUndoAction->SetOwnership(_bOwner);
188 _pUndoAction->SetConnection(_pConnection);
189 _pView->getDesignView()->getController().addUndoActionAndInvalidate(_pUndoAction);
191 // -----------------------------------------------------------------------------
192 /** openJoinDialog opens the join dialog with this connection data
193 @param _pView the view which we use
194 @param _pConnectionData the connection data
196 @return true when OK was pressed otherwise false
198 sal_Bool openJoinDialog(OQueryTableView* _pView,const TTableConnectionData::value_type& _pConnectionData,BOOL _bSelectableTables)
200 OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pConnectionData.get());
202 DlgQryJoin aDlg(_pView,_pConnectionData,_pView->GetTabWinMap(),_pView->getDesignView()->getController().getConnection(),_bSelectableTables);
203 sal_Bool bOk = aDlg.Execute() == RET_OK;
204 if( bOk )
206 pData->SetJoinType(aDlg.GetJoinType());
207 _pView->getDesignView()->getController().setModified(sal_True);
210 return bOk;
212 // -----------------------------------------------------------------------------
213 /** connectionModified adds an undo action for the modified connection and forces an redraw
214 @param _pView the view which we use
215 @param _pConnection the connection which was modified
216 @param _bAddUndo true when an undo action should be appended
218 void connectionModified(OQueryTableView* _pView,
219 OTableConnection* _pConnection,
220 sal_Bool _bAddUndo)
222 OSL_ENSURE(_pConnection,"Invalid connection!");
223 _pConnection->UpdateLineList();
225 // add an undo action
226 if ( _bAddUndo )
227 addUndoAction( _pView,
228 new OQueryAddTabConnUndoAction(_pView),
229 static_cast< OQueryTableConnection*>(_pConnection));
230 // redraw
231 _pConnection->RecalcLines();
232 // force an invalidation of the bounding rectangle
233 _pConnection->InvalidateConnection();
235 _pView->Invalidate(INVALIDATE_NOCHILDREN);
237 // -----------------------------------------------------------------------------
238 void addConnections(OQueryTableView* _pView,
239 const OQueryTableWindow& _rSource,
240 const OQueryTableWindow& _rDest,
241 const Reference<XNameAccess>& _rxSourceForeignKeyColumns)
243 if ( _rSource.GetData()->isQuery() || _rDest.GetData()->isQuery() )
244 // nothing to do if one of both denotes a query
245 return;
247 // we found a table in our view where we can insert some connections
248 // the key columns have a property called RelatedColumn
249 // OQueryTableConnectionData aufbauen
250 OQueryTableConnectionData* pNewConnData = new OQueryTableConnectionData( _rSource.GetData(), _rDest.GetData() );
251 TTableConnectionData::value_type aNewConnData(pNewConnData);
253 Reference<XIndexAccess> xReferencedKeys( _rDest.GetData()->getKeys());
254 ::rtl::OUString sRelatedColumn;
256 // iterate through all foreignkey columns to create the connections
257 Sequence< ::rtl::OUString> aElements(_rxSourceForeignKeyColumns->getElementNames());
258 const ::rtl::OUString* pIter = aElements.getConstArray();
259 const ::rtl::OUString* pEnd = pIter + aElements.getLength();
260 for(sal_Int32 i=0;pIter != pEnd;++pIter,++i)
262 Reference<XPropertySet> xColumn;
263 if ( !( _rxSourceForeignKeyColumns->getByName(*pIter) >>= xColumn ) )
265 OSL_ENSURE( false, "addConnections: invalid foreign key column!" );
266 continue;
269 pNewConnData->SetFieldType(JTCS_FROM,TAB_NORMAL_FIELD);
271 xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn;
272 pNewConnData->SetFieldType(JTCS_TO,isColumnInKeyType(xReferencedKeys,sRelatedColumn,KeyType::PRIMARY) ? TAB_PRIMARY_FIELD : TAB_NORMAL_FIELD);
275 Sequence< sal_Int16> aFind(::comphelper::findValue(_rSource.GetOriginalColumns()->getElementNames(),*pIter,sal_True));
276 if(aFind.getLength())
277 pNewConnData->SetFieldIndex(JTCS_FROM,aFind[0]+1);
278 else
279 OSL_ENSURE(0,"Column not found!");
281 // get the position inside the tabe
282 Reference<XNameAccess> xRefColumns = _rDest.GetOriginalColumns();
283 if(xRefColumns.is())
285 Sequence< sal_Int16> aFind(::comphelper::findValue(xRefColumns->getElementNames(),sRelatedColumn,sal_True));
286 if(aFind.getLength())
287 pNewConnData->SetFieldIndex(JTCS_TO,aFind[0]+1);
288 else
289 OSL_ENSURE(0,"Column not found!");
291 pNewConnData->AppendConnLine(*pIter,sRelatedColumn);
293 // dann die Conn selber dazu
294 OQueryTableConnection aNewConn(_pView, aNewConnData);
295 // der Verweis auf die lokale Variable ist unkritisch, da NotifyQueryTabConn eine neue Kopie anlegt
296 // und mir hinzufuegen (wenn nicht schon existent)
297 _pView->NotifyTabConnection(aNewConn, sal_False);
298 // don't create an Undo-Action for the new connection : the connection is
299 // covered by the Undo-Action for the tabwin, as the "Undo the insert" will
300 // automatically remove all connections adjacent to the win.
301 // (Because of this automatism we would have an ownerhsip ambiguity for
302 // the connection data if we would insert the conn-Undo-Action)
303 // FS - 21.10.99 - 69183
307 //==================================================================
308 // class OQueryTableView
309 //==================================================================
310 DBG_NAME(OQueryTableView)
311 //------------------------------------------------------------------------
312 OQueryTableView::OQueryTableView( Window* pParent,OQueryDesignView* pView)
313 : OJoinTableView( pParent,pView)
315 DBG_CTOR(OQueryTableView,NULL);
316 SetHelpId(HID_CTL_QRYDGNTAB);
319 //------------------------------------------------------------------------
320 OQueryTableView::~OQueryTableView()
322 DBG_DTOR(OQueryTableView,NULL);
325 //------------------------------------------------------------------------
326 sal_Int32 OQueryTableView::CountTableAlias(const String& rName, sal_Int32& rMax)
328 DBG_CHKTHIS(OQueryTableView,NULL);
329 sal_Int32 nRet = 0;
331 OTableWindowMapIterator aIter = GetTabWinMap()->find(rName);
332 while(aIter != GetTabWinMap()->end())
334 String aNewName;
335 aNewName = rName;
336 aNewName += '_';
337 aNewName += String::CreateFromInt32(++nRet);
339 aIter = GetTabWinMap()->find(aNewName);
342 rMax = nRet;
344 return nRet;
346 //------------------------------------------------------------------------
347 void OQueryTableView::ReSync()
349 DBG_CHKTHIS(OQueryTableView,NULL);
350 TTableWindowData* pTabWinDataList = m_pView->getController().getTableWindowData();
351 DBG_ASSERT((getTableConnections()->size()==0) && (GetTabWinMap()->size()==0),
352 "vor OQueryTableView::ReSync() bitte ClearAll aufrufen !");
354 // ich brauche eine Sammlung aller Fensternamen, deren Anlegen schief geht, damit ich die entsprechenden Connections
355 // gar nicht erst anlege
356 ::std::vector<String> arrInvalidTables;
358 TTableWindowData::reverse_iterator aIter = pTabWinDataList->rbegin();
359 // Fenster kreieren und einfuegen
361 for(;aIter != pTabWinDataList->rend();++aIter)
363 OQueryTableWindowData* pData = static_cast<OQueryTableWindowData*>(aIter->get());
364 OTableWindow* pTabWin = createWindow(*aIter);
366 // ich gehe jetzt NICHT ueber ShowTabWin, da dieses die Daten des Fensters in die Liste des Docs einfuegt, was
367 // schlecht waere, denn genau von dort hole ich sie ja gerade
368 // also Schritt fuer Schritt
369 if (!pTabWin->Init())
371 // das Initialisieren ging schief, dass heisst, dieses TabWin steht nicht zur Verfuegung, also muss ich es inklusive
372 // seiner Daten am Dokument aufraeumen
373 pTabWin->clearListBox();
374 delete pTabWin;
375 arrInvalidTables.push_back(pData->GetAliasName());
377 pTabWinDataList->erase( ::std::remove(pTabWinDataList->begin(),pTabWinDataList->end(),*aIter) ,pTabWinDataList->end());
378 continue;
381 (*GetTabWinMap())[pData->GetAliasName()] = pTabWin; // am Anfang einfuegen, da ich die DataList ja rueckwaerts durchlaufe
382 // wenn in den Daten keine Position oder Groesse steht -> Default
383 if (!pData->HasPosition() && !pData->HasSize())
384 SetDefaultTabWinPosSize(pTabWin);
386 pTabWin->Show();
389 // Verbindungen einfuegen
390 TTableConnectionData* pTabConnDataList = m_pView->getController().getTableConnectionData();
391 TTableConnectionData::reverse_iterator aConIter = pTabConnDataList->rbegin();
393 for(;aConIter != pTabConnDataList->rend();++aConIter)
395 OQueryTableConnectionData* pTabConnData = static_cast<OQueryTableConnectionData*>(aConIter->get());
397 // gibt es die beiden Tabellen zur Connection ?
398 String strTabExistenceTest = pTabConnData->getReferencingTable()->GetWinName();
399 sal_Bool bInvalid = ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
400 strTabExistenceTest = pTabConnData->getReferencedTable()->GetWinName();
401 bInvalid = bInvalid && ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
403 if (bInvalid)
404 { // nein -> Pech gehabt, die Connection faellt weg
405 pTabConnDataList->erase( ::std::remove(pTabConnDataList->begin(),pTabConnDataList->end(),*aConIter) ,pTabConnDataList->end());
406 continue;
409 // adds a new connection to join view and notifies our accessible and invaldates the controller
410 addConnection(new OQueryTableConnection(this, *aConIter));
414 //------------------------------------------------------------------------
415 void OQueryTableView::ClearAll()
417 DBG_CHKTHIS(OQueryTableView,NULL);
418 OJoinTableView::ClearAll();
420 SetUpdateMode(sal_True);
421 m_pView->getController().setModified(sal_True);
424 // -----------------------------------------------------------------------------
425 OTableWindow* OQueryTableView::createWindow(const TTableWindowData::value_type& _pData)
427 return new OQueryTableWindow(this,_pData);
430 //------------------------------------------------------------------------------
431 void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, sal_Bool _bCreateUndoAction)
433 DBG_CHKTHIS(OQueryTableView,NULL);
434 // erst mal schauen, ob ich diese Connection schon habe
435 OQueryTableConnection* pTabConn = NULL;
436 const ::std::vector<OTableConnection*>* pConnections = getTableConnections();
437 ::std::vector<OTableConnection*>::const_iterator aEnd = pConnections->end();
438 ::std::vector<OTableConnection*>::const_iterator aIter = ::std::find( pConnections->begin(),
439 aEnd,
440 static_cast<const OTableConnection*>(&rNewConn)
442 if(aIter == aEnd )
444 aIter = pConnections->begin();
445 for(;aIter != aEnd;++aIter)
447 if(*static_cast<OQueryTableConnection*>(*aIter) == rNewConn)
449 pTabConn = static_cast<OQueryTableConnection*>(*aIter);
450 break;
454 else
455 pTabConn = static_cast<OQueryTableConnection*>(*aIter);
456 // nein -> einfuegen
457 if (pTabConn == NULL)
459 // die neuen Daten ...
460 OQueryTableConnectionData* pNewData = static_cast< OQueryTableConnectionData*>(rNewConn.GetData()->NewInstance());
461 pNewData->CopyFrom(*rNewConn.GetData());
462 TTableConnectionData::value_type aData(pNewData);
463 OQueryTableConnection* pNewConn = new OQueryTableConnection(this, aData);
464 GetConnection(pNewConn);
466 connectionModified(this,pNewConn,_bCreateUndoAction);
469 // -----------------------------------------------------------------------------
470 OTableWindowData* OQueryTableView::CreateImpl(const ::rtl::OUString& _rComposedName
471 ,const ::rtl::OUString& _sTableName
472 ,const ::rtl::OUString& _rWinName)
474 return new OQueryTableWindowData( _rComposedName, _sTableName,_rWinName );
476 //------------------------------------------------------------------------------
477 void OQueryTableView::AddTabWin(const ::rtl::OUString& _rTableName, const ::rtl::OUString& _rAliasName, sal_Bool bNewTable)
479 DBG_CHKTHIS(OQueryTableView,NULL);
480 // das ist die aus der Basisklasse geerbte Methode, die fuehre ich auf die an meinem Parent zurueck, die mir eventuell einen
481 // Alias dazu bastelt und das an mein anderes AddTabWin weiterreicht
483 // leider ist _rTableName voll qualifiziert, das OQueryDesignView erwartet aber einen String, der
484 // nur aus Schema und Tabelle besteht und keinen Katalog enthaelt.
485 Reference< XConnection> xConnection = m_pView->getController().getConnection();
486 if(!xConnection.is())
487 return;
490 Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
491 ::rtl::OUString sCatalog, sSchema, sTable;
492 ::dbtools::qualifiedNameComponents(xMetaData,
493 _rTableName,
494 sCatalog,
495 sSchema,
496 sTable,
497 ::dbtools::eInDataManipulation);
498 ::rtl::OUString sRealName(sSchema);
499 if (sRealName.getLength())
500 sRealName+= ::rtl::OUString('.');
501 sRealName += sTable;
503 AddTabWin(_rTableName, sRealName, _rAliasName, bNewTable);
505 catch(SQLException&)
507 OSL_ASSERT(!"qualifiedNameComponents");
510 // -----------------------------------------------------------------------------
511 // find the table which has a foreign key with this referencedTable name
512 Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,const ::rtl::OUString& _rReferencedTable)
514 if(!_rxKeys.is())
515 return Reference<XPropertySet>();
517 if ( !_rxKeys.is() )
518 return Reference<XPropertySet>();
519 // search the one and only primary key
520 const sal_Int32 nCount = _rxKeys->getCount();
521 for(sal_Int32 i=0;i<nCount ;++i)
523 Reference<XPropertySet> xKey(_rxKeys->getByIndex(i),UNO_QUERY);
524 if(xKey.is())
526 sal_Int32 nKeyType = 0;
527 xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
528 if(KeyType::FOREIGN == nKeyType)
530 ::rtl::OUString sReferencedTable;
531 xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable;
532 // TODO check case
533 if(sReferencedTable == _rReferencedTable)
534 return xKey;
538 return Reference<XPropertySet>();
540 //------------------------------------------------------------------------------
541 void OQueryTableView::AddTabWin(const ::rtl::OUString& _rComposedName, const ::rtl::OUString& _rTableName, const ::rtl::OUString& strAlias, sal_Bool bNewTable)
543 DBG_CHKTHIS(OQueryTableView,NULL);
544 DBG_ASSERT(_rTableName.getLength() || strAlias.getLength(), "OQueryTableView::AddTabWin : kein Tabellen- und kein Aliasname !");
545 // wenn der Tabellenname nicht gesetzt ist, steht das fuer ein Dummy-Fenster, das braucht aber wenigstens einen Alias-Namen
547 // neue Datenstruktur erzeugen
548 // first check if this already hav it's data
549 sal_Bool bAppend = bNewTable;
550 TTableWindowData::value_type pNewTabWinData;
551 TTableWindowData* pWindowData = getDesignView()->getController().getTableWindowData();
552 TTableWindowData::iterator aWinIter = pWindowData->begin();
553 TTableWindowData::iterator aWinEnd = pWindowData->end();
554 for(;aWinIter != aWinEnd;++aWinIter)
556 pNewTabWinData = *aWinIter;
557 if (pNewTabWinData && pNewTabWinData->GetWinName() == strAlias && pNewTabWinData->GetComposedName() == _rComposedName && pNewTabWinData->GetTableName() == _rTableName)
558 break;
560 if ( !bAppend )
561 bAppend = ( aWinIter == aWinEnd );
562 if ( bAppend )
563 pNewTabWinData = createTableWindowData(_rComposedName, _rTableName, strAlias);
564 // die TabWinData brauche ich nicht in die entsprechende Liste der DocShell eintragen, das macht ShowTabWin
566 // neues Fenster erzeugen
567 OQueryTableWindow* pNewTabWin = static_cast<OQueryTableWindow*>(createWindow(pNewTabWinData));
568 // das Init kann ich hier weglassen, da das in ShowTabWin passiert
570 // Neue UndoAction
571 OQueryTabWinShowUndoAct* pUndoAction = new OQueryTabWinShowUndoAct(this);
572 pUndoAction->SetTabWin(pNewTabWin); // Fenster
573 sal_Bool bSuccess = ShowTabWin(pNewTabWin, pUndoAction,bAppend);
574 if(!bSuccess)
576 // reset table window
577 pUndoAction->SetTabWin(NULL);
578 pUndoAction->SetOwnership(sal_False);
580 delete pUndoAction;
581 return;
584 // Relationen zwischen den einzelnen Tabellen anzeigen
585 OTableWindowMap* pTabWins = GetTabWinMap();
586 if(bNewTable && !pTabWins->empty() && _rTableName.getLength())
588 modified();
589 if ( m_pAccessible )
590 m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
591 Any(),
592 makeAny(pNewTabWin->GetAccessible())
595 do {
597 if ( pNewTabWin->GetData()->isQuery() )
598 break;
602 //////////////////////////////////////////////////////////////////////
603 // find relations between the table an the tables already inserted
604 Reference< XIndexAccess> xKeyIndex = pNewTabWin->GetData()->getKeys();
605 if ( !xKeyIndex.is() )
606 break;
608 Reference<XNameAccess> xFKeyColumns;
609 ::rtl::OUString aReferencedTable;
610 Reference<XColumnsSupplier> xColumnsSupplier;
612 const sal_Int32 nKeyCount = xKeyIndex->getCount();
613 for ( sal_Int32 i=0; i<nKeyCount ; ++i )
615 Reference< XPropertySet > xProp( xKeyIndex->getByIndex(i), UNO_QUERY_THROW );
616 xColumnsSupplier.set( xProp, UNO_QUERY_THROW );
617 xFKeyColumns.set( xColumnsSupplier->getColumns(), UNO_QUERY_THROW );
619 sal_Int32 nKeyType = 0;
620 xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
622 switch ( nKeyType )
624 case KeyType::FOREIGN:
625 { // our new table has a foreign key
626 // so look if the referenced table is already in our list
627 xProp->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= aReferencedTable;
628 OSL_ENSURE(aReferencedTable.getLength(),"Foreign key without referencedTableName");
630 OTableWindowMap::const_iterator aIter = pTabWins->find(aReferencedTable);
631 OTableWindowMap::const_iterator aEnd = pTabWins->end();
632 if(aIter == aEnd)
634 for(aIter = pTabWins->begin();aIter != aEnd;++aIter)
636 OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second);
637 OSL_ENSURE( pTabWinTmp,"TableWindow is null!" );
638 if ( pTabWinTmp != pNewTabWin && pTabWinTmp->GetComposedName() == aReferencedTable )
639 break;
642 if ( aIter != aEnd && pNewTabWin != aIter->second )
643 addConnections( this, *pNewTabWin, *static_cast<OQueryTableWindow*>(aIter->second), xFKeyColumns );
645 break;
647 case KeyType::PRIMARY:
649 // we have a primary key so look in our list if there exsits a key which this is refered to
650 OTableWindowMap::const_iterator aIter = pTabWins->begin();
651 OTableWindowMap::const_iterator aEnd = pTabWins->end();
652 for(;aIter != aEnd;++aIter)
654 OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second);
655 if ( pTabWinTmp == pNewTabWin )
656 continue;
658 if ( pTabWinTmp->GetData()->isQuery() )
659 continue;
661 OSL_ENSURE(pTabWinTmp,"TableWindow is null!");
662 Reference< XPropertySet > xFKKey = getKeyReferencedTo( pTabWinTmp->GetData()->getKeys(), pNewTabWin->GetComposedName() );
663 if ( !xFKKey.is() )
664 continue;
666 Reference<XColumnsSupplier> xFKColumnsSupplier( xFKKey, UNO_QUERY_THROW );
667 Reference< XNameAccess > xTColumns( xFKColumnsSupplier->getColumns(), UNO_QUERY_THROW );
668 addConnections( this, *pTabWinTmp, *pNewTabWin, xTColumns );
671 break;
675 catch( const Exception& )
677 DBG_UNHANDLED_EXCEPTION();
680 } while ( false );
683 // mein Parent brauche ich, da es vom Loeschen erfahren soll
684 m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
686 if (bSuccess && m_lnkTabWinsChangeHandler.IsSet())
688 TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_ADDED_WIN, pNewTabWin->GetAliasName());
689 m_lnkTabWinsChangeHandler.Call(&aHint);
692 // -----------------------------------------------------------------------------
693 // -----------------------------------------------------------------------------
694 void OQueryTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest)
696 DBG_CHKTHIS(OQueryTableView,NULL);
697 OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(jxdSource.pListBox->GetTabWin());
698 OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(jxdDest.pListBox->GetTabWin());
700 String aSourceFieldName, aDestFieldName;
701 aSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry);
702 aDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry);
704 OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
705 if ( !pConn )
707 // neues Daten-Objekt
708 OQueryTableConnectionData* pNewConnectionData = new OQueryTableConnectionData(pSourceWin->GetData(), pDestWin->GetData());
709 TTableConnectionData::value_type aNewConnectionData(pNewConnectionData);
711 sal_uInt32 nSourceFieldIndex, nDestFieldIndex;
712 ETableFieldType eSourceFieldType, eDestFieldType;
714 // Namen/Position/Typ der beiden betroffenen Felder besorgen ...
715 // Source
717 nSourceFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry);
718 eSourceFieldType = static_cast< OTableFieldInfo*>(jxdSource.pEntry->GetUserData())->GetKeyType();
720 // Dest
722 nDestFieldIndex = jxdDest.pListBox->GetModel()->GetAbsPos(jxdDest.pEntry);
723 eDestFieldType = static_cast< OTableFieldInfo*>(jxdDest.pEntry->GetUserData())->GetKeyType();
725 // ... und setzen
727 pNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex);
728 pNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex);
730 pNewConnectionData->SetFieldType(JTCS_FROM, eSourceFieldType);
731 pNewConnectionData->SetFieldType(JTCS_TO, eDestFieldType);
733 pNewConnectionData->AppendConnLine( aSourceFieldName,aDestFieldName );
735 OQueryTableConnection aNewConnection(this, aNewConnectionData);
736 NotifyTabConnection(aNewConnection);
737 // wie immer bei NotifyTabConnection ist das Verwenden lokaler Variablen unkritisch, da sowieso eine Kopie erzeugt wird
739 else
741 // the connection could point on the other side
742 if(pConn->GetSourceWin() == pDestWin)
744 String aTmp(aSourceFieldName);
745 aSourceFieldName = aDestFieldName;
746 aDestFieldName = aTmp;
749 pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName );
751 connectionModified(this,pConn,sal_False);
754 // -----------------------------------------------------------------------------
755 void OQueryTableView::ConnDoubleClicked(OTableConnection* pConnection)
757 DBG_CHKTHIS(OQueryTableView,NULL);
758 if( openJoinDialog(this,pConnection->GetData(),FALSE) )
760 connectionModified(this,pConnection,sal_False);
761 SelectConn( pConnection );
764 // -----------------------------------------------------------------------------
765 void OQueryTableView::createNewConnection()
767 TTableConnectionData::value_type pData(new OQueryTableConnectionData());
768 if( openJoinDialog(this,pData,TRUE) )
770 OTableWindowMap* pMap = GetTabWinMap();
771 OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>((*pMap)[pData->getReferencingTable()->GetWinName()]);
772 OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>((*pMap)[pData->getReferencedTable()->GetWinName()]);
773 // first we have to look if the this connection already exists
774 OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
775 sal_Bool bNew = sal_True;
776 if ( pConn )
778 pConn->GetData()->CopyFrom( *pData );
779 bNew = sal_False;
781 else
783 // create a new conenction and append it
784 OQueryTableConnection* pQConn = new OQueryTableConnection(this, pData);
785 GetConnection(pQConn);
786 pConn = pQConn;
788 connectionModified(this,pConn,bNew);
789 if ( !bNew && pConn == GetSelectedConn() ) // our connection was selected before so we have to reselect it
790 SelectConn( pConn );
793 //------------------------------------------------------------------------------
794 bool OQueryTableView::RemoveConnection( OTableConnection* _pConnection,sal_Bool /*_bDelete*/ )
796 DBG_CHKTHIS(OQueryTableView,NULL);
798 // we don't want that our connection will be deleted, we put it in the undo manager
799 bool bRet = OJoinTableView::RemoveConnection( _pConnection,sal_False);
801 // add undo action
802 addUndoAction( this,
803 new OQueryDelTabConnUndoAction(this),
804 static_cast< OQueryTableConnection*>(_pConnection),
805 sal_True);
806 return bRet;
809 //------------------------------------------------------------------------------
810 void OQueryTableView::KeyInput( const KeyEvent& rEvt )
812 DBG_CHKTHIS(OQueryTableView,NULL);
813 OJoinTableView::KeyInput( rEvt );
816 //------------------------------------------------------------------------------
817 OQueryTableWindow* OQueryTableView::FindTable(const String& rAliasName)
819 DBG_CHKTHIS(OQueryTableView,NULL);
820 DBG_ASSERT(rAliasName.Len(), "OQueryTableView::FindTable : der AliasName sollte nicht leer sein !");
821 // (nicht dass es schadet, aber es ist sinnlos und weist vielleicht auf Fehler beim Aufrufer hin)
822 OTableWindowMap::const_iterator aIter = GetTabWinMap()->find(rAliasName);
823 if(aIter != GetTabWinMap()->end())
824 return static_cast<OQueryTableWindow*>(aIter->second);
825 return NULL;
828 //------------------------------------------------------------------------------
829 sal_Bool OQueryTableView::FindTableFromField(const String& rFieldName, OTableFieldDescRef& rInfo, sal_uInt16& rCnt)
831 DBG_CHKTHIS(OQueryTableView,NULL);
832 rCnt = 0;
833 OTableWindowMap::const_iterator aIter = GetTabWinMap()->begin();
834 OTableWindowMap::const_iterator aEnd = GetTabWinMap()->end();
835 for(;aIter != aEnd;++aIter)
837 if(static_cast<OQueryTableWindow*>(aIter->second)->ExistsField(rFieldName, rInfo))
838 ++rCnt;
841 return rCnt == 1;
844 //------------------------------------------------------------------------------
845 void OQueryTableView::RemoveTabWin(OTableWindow* pTabWin)
847 DBG_CHKTHIS(OQueryTableView,NULL);
848 DBG_ASSERT(pTabWin != NULL, "OQueryTableView::RemoveTabWin : Fenster sollte ungleich NULL sein !");
850 // mein Parent brauche ich, da es vom Loeschen erfahren soll
851 OQueryDesignView* pParent = static_cast<OQueryDesignView*>(getDesignView());
853 SfxUndoManager* pUndoMgr = m_pView->getController().getUndoMgr();
854 pUndoMgr->EnterListAction( String( ModuleRes(STR_QUERY_UNDO_TABWINDELETE) ), String() );
856 // Undo-Action anlegen
857 OQueryTabWinDelUndoAct* pUndoAction = new OQueryTabWinDelUndoAct(this);
858 pUndoAction->SetTabWin(static_cast< OQueryTableWindow*>(pTabWin));
860 // und Fenster verstecken
861 HideTabWin(static_cast< OQueryTableWindow*>(pTabWin), pUndoAction);
863 // Undo Actions und Loeschen der Felder in SelectionBrowseBox
864 pParent->TableDeleted( static_cast< OQueryTableWindowData*>(pTabWin->GetData().get())->GetAliasName() );
866 m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
867 pUndoMgr->LeaveListAction();
869 if (m_lnkTabWinsChangeHandler.IsSet())
871 TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_REMOVED_WIN, static_cast< OQueryTableWindow*>(pTabWin)->GetAliasName());
872 m_lnkTabWinsChangeHandler.Call(&aHint);
875 modified();
876 if ( m_pAccessible )
877 m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
878 makeAny(pTabWin->GetAccessible()),
879 Any()
883 //------------------------------------------------------------------------
884 void OQueryTableView::EnsureVisible(const OTableWindow* pWin)
886 DBG_CHKTHIS(OQueryTableView,NULL);
888 Invalidate(INVALIDATE_NOCHILDREN);
889 OJoinTableView::EnsureVisible(pWin);
892 //------------------------------------------------------------------------
893 void OQueryTableView::GetConnection(OQueryTableConnection* pConn)
895 DBG_CHKTHIS(OQueryTableView,NULL);
896 // bei mir und dem Dokument einfuegen
898 addConnection( pConn );
899 // invalidieren (damit es neu gezeichnet wird)
900 // pConn->Invalidate();
903 //------------------------------------------------------------------------
904 void OQueryTableView::DropConnection(OQueryTableConnection* pConn)
906 DBG_CHKTHIS(OQueryTableView,NULL);
907 // Selektion beachten
908 // bei mir und dem Dokument rausnehmen
909 RemoveConnection( pConn ,sal_False);
912 //------------------------------------------------------------------------
913 void OQueryTableView::HideTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction )
915 DBG_CHKTHIS(OQueryTableView,NULL);
916 OTableWindowMap* pTabWins = GetTabWinMap();
917 DBG_ASSERT(pTabWins != NULL, "OQueryTableView::HideTabWin : habe keine TabWins !");
919 if (pTabWin)
921 // Fenster
922 // die Position in seinen Daten speichern
923 getDesignView()->SaveTabWinUIConfig(pTabWin);
924 // (ich muss ueber das Parent gehen, da nur das die Position der Scrollbars kennt)
925 // dann aus der Liste der TabWins raus und verstecken
926 OTableWindowMap::iterator aIter = pTabWins->begin();
927 OTableWindowMap::iterator aEnd = pTabWins->end();
928 for ( ;aIter != aEnd ; ++aIter )
929 if ( aIter->second == pTabWin )
931 pTabWins->erase( aIter );
932 break;
935 pTabWin->Hide(); // nicht zerstoeren, steht im Undo!!
937 // die Daten zum TabWin muessen auch aus meiner Verantwortung entlassen werden
938 TTableWindowData* pTabWinDataList = m_pView->getController().getTableWindowData();
939 pTabWinDataList->erase( ::std::remove(pTabWinDataList->begin(),pTabWinDataList->end(),pTabWin->GetData()),pTabWinDataList->end());
940 // NICHT loeschen, da ja das TabWin selber - das noch lebt - sie auch noch braucht
941 // Entweder geht es irgendwann wieder in meine Verantwortung ueber, (ueber ShowTabWin), dann fuege ich
942 // auch die Daten wieder ein, oder die Undo-Action, die im Augenblick die alleinige Verantwortung fuer das Fenster
943 // und dessen Daten hat, wird zestoert, dann loescht es beides
945 if (m_pLastFocusTabWin == pTabWin)
946 m_pLastFocusTabWin = NULL;
948 // Verbindungen, die zum Fenster gehoeren, einsammeln und der UndoAction uebergeben
949 sal_Int16 nCnt = 0;
950 const ::std::vector<OTableConnection*>* pTabConList = getTableConnections();
951 ::std::vector<OTableConnection*>::const_iterator aIter2 = pTabConList->begin();
952 for(;aIter2 != pTabConList->end();)// the end may change
954 OQueryTableConnection* pTmpEntry = static_cast<OQueryTableConnection*>(*aIter2);
955 OSL_ENSURE(pTmpEntry,"OQueryTableConnection is null!");
956 if( pTmpEntry->GetAliasName(JTCS_FROM) == pTabWin->GetAliasName() ||
957 pTmpEntry->GetAliasName(JTCS_TO) == pTabWin->GetAliasName() )
959 // add to undo list
960 pUndoAction->InsertConnection(pTmpEntry);
962 // call base class because we append an undo action
963 // but this time we are in a undo action list
964 OJoinTableView::RemoveConnection(pTmpEntry,sal_False);
965 aIter2 = pTabConList->begin();
966 ++nCnt;
968 else
969 ++aIter2;
972 if (nCnt)
973 InvalidateConnections();
975 m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
977 // der UndoAction sagen, dass das Fenster (inklusive der Connections) jetzt in seinem Besitzt ist
978 pUndoAction->SetOwnership(sal_True);
980 // damit habe ich das Doc natuerlich modifiziert
981 m_pView->getController().setModified( sal_True );
982 m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
986 //------------------------------------------------------------------------
987 sal_Bool OQueryTableView::ShowTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction,sal_Bool _bAppend )
989 DBG_CHKTHIS(OQueryTableView,NULL);
991 sal_Bool bSuccess = sal_False;
993 if (pTabWin)
995 if (pTabWin->Init())
997 TTableWindowData::value_type pData = pTabWin->GetData();
998 DBG_ASSERT(pData != NULL, "OQueryTableView::ShowTabWin : TabWin hat keine Daten !");
999 // Wenn die Daten schon PosSize haben, diese benutzen
1000 if (pData->HasPosition() && pData->HasSize())
1002 Size aSize(CalcZoom(pData->GetSize().Width()),CalcZoom(pData->GetSize().Height()));
1003 pTabWin->SetPosSizePixel(pData->GetPosition(), aSize);
1005 else
1006 // ansonsten selber eine Default-Position ermitteln
1007 SetDefaultTabWinPosSize(pTabWin);
1009 // Fenster zeigen und in Liste eintragen
1010 ::rtl::OUString sName = static_cast< OQueryTableWindowData*>(pData.get())->GetAliasName();
1011 OSL_ENSURE(GetTabWinMap()->find(sName) == GetTabWinMap()->end(),"Alias name already in list!");
1012 GetTabWinMap()->insert(OTableWindowMap::value_type(sName,pTabWin));
1014 pTabWin->Show();
1016 pTabWin->Update();
1017 // Das Update ist notwendig, damit die Connections an dem Fenster richtig gezeichnet werden. Klingt absurd,
1018 // ich weiss. Aber die Listbox haelt sich intern ein Member, was bei ersten Zeichnen (nachdem die Listbox im Init
1019 // gerade neu gefuellt wurde) initialisiert wird, und genau dieses Member wird irgendwann benoetigt fuer
1020 // GetEntryPos, und dieses wiederum von der Connection, wenn sie ihren Ansatzpunkt am Fenster feststellen will.
1022 // die Connections
1023 ::std::vector<OTableConnection*>* pTableCon = pUndoAction->GetTabConnList();
1024 ::std::vector<OTableConnection*>::iterator aIter = pTableCon->begin();
1025 ::std::vector<OTableConnection*>::iterator aEnd = pTableCon->end();
1027 for(;aIter != aEnd;++aIter)
1028 addConnection(*aIter); // add all connections from the undo action
1030 // each connection should invalidated inside addConnection so we don't need this here any longer
1031 // if ( !pOwnList->empty() )
1032 // InvalidateConnections();
1033 pTableCon->clear();
1035 // und die Daten des Fensters ebenfalls in Liste (des Docs)
1036 if(_bAppend)
1037 m_pView->getController().getTableWindowData()->push_back(pTabWin->GetData());
1039 m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
1041 // und der UndoAction sagen, dass das Fenster jetzt meine ist ...
1042 pUndoAction->SetOwnership(sal_False);
1044 bSuccess = sal_True;
1046 else
1048 //////////////////////////////////////////////////////////////////
1049 // Initialisierung fehlgeschlagen
1050 // (z.B. wenn Verbindung zur Datenbank in diesem Augenblick unterbrochen worden ist)
1051 pTabWin->clearListBox();
1052 delete pTabWin;
1056 // damit habe ich das Doc natuerlich modifiziert
1057 if(!m_pView->getController().isReadOnly())
1058 m_pView->getController().setModified( sal_True );
1060 m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
1062 return bSuccess;
1064 //------------------------------------------------------------------------
1065 void OQueryTableView::InsertField(const OTableFieldDescRef& rInfo)
1067 DBG_CHKTHIS(OQueryTableView,NULL);
1068 DBG_ASSERT(getDesignView() != NULL, "OQueryTableView::InsertField : habe kein Parent !");
1069 static_cast<OQueryDesignView*>(getDesignView())->InsertField(rInfo);
1071 //------------------------------------------------------------------------------
1072 sal_Bool OQueryTableView::ExistsAVisitedConn(const OQueryTableWindow* pFrom) const
1074 DBG_CHKTHIS(OQueryTableView,NULL);
1075 const ::std::vector<OTableConnection*>* pList = getTableConnections();
1076 if (pList)
1078 ::std::vector<OTableConnection*>::const_iterator aIter = pList->begin();
1079 ::std::vector<OTableConnection*>::const_iterator aEnd = pList->end();
1080 for(;aIter != aEnd;++aIter)
1082 OQueryTableConnection* pTemp = static_cast<OQueryTableConnection*>(*aIter);
1083 if (pTemp->IsVisited() &&
1084 (pFrom == static_cast< OQueryTableWindow*>(pTemp->GetSourceWin()) || pFrom == static_cast< OQueryTableWindow*>(pTemp->GetDestWin())))
1085 return pTemp != NULL;
1089 return sal_False;
1091 // -----------------------------------------------------------------------------
1092 void OQueryTableView::onNoColumns_throw()
1094 String sError( ModuleRes( STR_STATEMENT_WITHOUT_RESULT_SET ) );
1095 ::dbtools::throwSQLException( sError, ::dbtools::SQL_GENERAL_ERROR, NULL );
1097 //------------------------------------------------------------------------------
1098 bool OQueryTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& _pData) const
1100 OQueryTableConnectionData* pQueryData = static_cast<OQueryTableConnectionData*>(_pData.get());
1101 return pQueryData && (pQueryData->GetJoinType() == CROSS_JOIN);
1103 // -----------------------------------------------------------------------------