Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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"
52 :
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;
60 :
61 : namespace
62 : {
63 0 : sal_Bool isColumnInKeyType(const Reference<XIndexAccess>& _rxKeys,const OUString& _rColumnName,sal_Int32 _nKeyType)
64 : {
65 0 : sal_Bool bReturn = sal_False;
66 0 : if(_rxKeys.is())
67 : {
68 0 : Reference<XColumnsSupplier> xColumnsSupplier;
69 : // search the one and only primary key
70 0 : const sal_Int32 nCount = _rxKeys->getCount();
71 0 : for(sal_Int32 i=0;i< nCount;++i)
72 : {
73 0 : Reference<XPropertySet> xProp(_rxKeys->getByIndex(i),UNO_QUERY);
74 0 : if(xProp.is())
75 : {
76 0 : sal_Int32 nKeyType = 0;
77 0 : xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
78 0 : if(_nKeyType == nKeyType)
79 : {
80 0 : xColumnsSupplier.set(xProp,UNO_QUERY);
81 0 : if(xColumnsSupplier.is())
82 : {
83 0 : Reference<XNameAccess> xColumns = xColumnsSupplier->getColumns();
84 0 : if(xColumns.is() && xColumns->hasByName(_rColumnName))
85 : {
86 0 : bReturn = sal_True;
87 0 : break;
88 0 : }
89 : }
90 : }
91 : }
92 0 : }
93 : }
94 0 : return bReturn;
95 : }
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
101 : */
102 0 : void addUndoAction( OQueryTableView* _pView,
103 : OQueryTabConnUndoAction* _pUndoAction,
104 : OQueryTableConnection* _pConnection,
105 : sal_Bool _bOwner = sal_False)
106 : {
107 0 : _pUndoAction->SetOwnership(_bOwner);
108 0 : _pUndoAction->SetConnection(_pConnection);
109 0 : _pView->getDesignView()->getController().addUndoActionAndInvalidate(_pUndoAction);
110 0 : }
111 : /** openJoinDialog opens the join dialog with this connection data
112 : @param _pView the view which we use
113 : @param _pConnectionData the connection data
114 :
115 : @return true when OK was pressed otherwise false
116 : */
117 0 : sal_Bool openJoinDialog(OQueryTableView* _pView,const TTableConnectionData::value_type& _pConnectionData,sal_Bool _bSelectableTables)
118 : {
119 0 : OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pConnectionData.get());
120 :
121 0 : DlgQryJoin aDlg(_pView,_pConnectionData,_pView->GetTabWinMap(),_pView->getDesignView()->getController().getConnection(),_bSelectableTables);
122 0 : sal_Bool bOk = aDlg.Execute() == RET_OK;
123 0 : if( bOk )
124 : {
125 0 : pData->SetJoinType(aDlg.GetJoinType());
126 0 : _pView->getDesignView()->getController().setModified(sal_True);
127 : }
128 :
129 0 : return bOk;
130 : }
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
135 : */
136 0 : void connectionModified(OQueryTableView* _pView,
137 : OTableConnection* _pConnection,
138 : sal_Bool _bAddUndo)
139 : {
140 : OSL_ENSURE(_pConnection,"Invalid connection!");
141 0 : _pConnection->UpdateLineList();
142 :
143 : // add an undo action
144 0 : if ( _bAddUndo )
145 : addUndoAction( _pView,
146 0 : new OQueryAddTabConnUndoAction(_pView),
147 0 : static_cast< OQueryTableConnection*>(_pConnection));
148 : // redraw
149 0 : _pConnection->RecalcLines();
150 : // force an invalidation of the bounding rectangle
151 0 : _pConnection->InvalidateConnection();
152 :
153 0 : _pView->Invalidate(INVALIDATE_NOCHILDREN);
154 0 : }
155 0 : void addConnections(OQueryTableView* _pView,
156 : const OQueryTableWindow& _rSource,
157 : const OQueryTableWindow& _rDest,
158 : const Reference<XNameAccess>& _rxSourceForeignKeyColumns)
159 : {
160 0 : if ( _rSource.GetData()->isQuery() || _rDest.GetData()->isQuery() )
161 : // nothing to do if one of both denotes a query
162 0 : return;
163 :
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 0 : OQueryTableConnectionData* pNewConnData = new OQueryTableConnectionData( _rSource.GetData(), _rDest.GetData() );
168 0 : TTableConnectionData::value_type aNewConnData(pNewConnData);
169 :
170 0 : Reference<XIndexAccess> xReferencedKeys( _rDest.GetData()->getKeys());
171 0 : OUString sRelatedColumn;
172 :
173 : // iterate through all foreignkey columns to create the connections
174 0 : Sequence< OUString> aElements(_rxSourceForeignKeyColumns->getElementNames());
175 0 : const OUString* pIter = aElements.getConstArray();
176 0 : const OUString* pEnd = pIter + aElements.getLength();
177 0 : for(sal_Int32 i=0;pIter != pEnd;++pIter,++i)
178 : {
179 0 : Reference<XPropertySet> xColumn;
180 0 : if ( !( _rxSourceForeignKeyColumns->getByName(*pIter) >>= xColumn ) )
181 : {
182 : OSL_FAIL( "addConnections: invalid foreign key column!" );
183 0 : continue;
184 : }
185 :
186 0 : pNewConnData->SetFieldType(JTCS_FROM,TAB_NORMAL_FIELD);
187 :
188 0 : xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn;
189 0 : pNewConnData->SetFieldType(JTCS_TO,isColumnInKeyType(xReferencedKeys,sRelatedColumn,KeyType::PRIMARY) ? TAB_PRIMARY_FIELD : TAB_NORMAL_FIELD);
190 :
191 : {
192 0 : Sequence< sal_Int16> aFind(::comphelper::findValue(_rSource.GetOriginalColumns()->getElementNames(),*pIter,true));
193 0 : if(aFind.getLength())
194 0 : pNewConnData->SetFieldIndex(JTCS_FROM,aFind[0]+1);
195 : else
196 0 : OSL_FAIL("Column not found!");
197 : }
198 : // get the position inside the tabe
199 0 : Reference<XNameAccess> xRefColumns = _rDest.GetOriginalColumns();
200 0 : if(xRefColumns.is())
201 : {
202 0 : Sequence< sal_Int16> aFind(::comphelper::findValue(xRefColumns->getElementNames(),sRelatedColumn,true));
203 0 : if(aFind.getLength())
204 0 : pNewConnData->SetFieldIndex(JTCS_TO,aFind[0]+1);
205 : else
206 0 : OSL_FAIL("Column not found!");
207 : }
208 0 : pNewConnData->AppendConnLine(*pIter,sRelatedColumn);
209 :
210 : // now add the Conn itself
211 0 : OQueryTableConnection aNewConn(_pView, aNewConnData);
212 : // refering to the local variable is not important, as NotifyQueryTabConn creates a new copy
213 : // to add me (if not existent)
214 0 : _pView->NotifyTabConnection(aNewConn, sal_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)
220 0 : }
221 : }
222 : }
223 :
224 : // class OQueryTableView
225 0 : OQueryTableView::OQueryTableView( Window* pParent,OQueryDesignView* pView)
226 0 : : OJoinTableView( pParent,pView)
227 : {
228 0 : SetHelpId(HID_CTL_QRYDGNTAB);
229 0 : }
230 :
231 0 : OQueryTableView::~OQueryTableView()
232 : {
233 0 : }
234 :
235 0 : sal_Int32 OQueryTableView::CountTableAlias(const OUString& rName, sal_Int32& rMax)
236 : {
237 0 : sal_Int32 nRet = 0;
238 :
239 0 : OTableWindowMap::iterator aIter = GetTabWinMap()->find(rName);
240 0 : while(aIter != GetTabWinMap()->end())
241 : {
242 0 : OUString aNewName = OUString(rName) + "_" + OUString::number(++nRet);
243 0 : aIter = GetTabWinMap()->find(aNewName);
244 0 : }
245 :
246 0 : rMax = nRet;
247 :
248 0 : return nRet;
249 : }
250 :
251 0 : void OQueryTableView::ReSync()
252 : {
253 0 : TTableWindowData* pTabWinDataList = m_pView->getController().getTableWindowData();
254 : OSL_ENSURE((getTableConnections()->size()==0) && (GetTabWinMap()->size()==0),
255 : "before calling OQueryTableView::ReSync() please call ClearAll !");
256 :
257 : // I need a collection of all window names that cannot be created so that I do not initialize connections for them.
258 0 : ::std::vector<OUString> arrInvalidTables;
259 :
260 0 : TTableWindowData::reverse_iterator aIter = pTabWinDataList->rbegin();
261 : // Create the window and add it
262 :
263 0 : for(;aIter != pTabWinDataList->rend();++aIter)
264 : {
265 0 : OQueryTableWindowData* pData = static_cast<OQueryTableWindowData*>(aIter->get());
266 0 : OTableWindow* pTabWin = createWindow(*aIter);
267 :
268 : // I dont't use ShowTabWin as this adds the window data to the list of documents.
269 : // This would be bad as I am getting them from there.
270 : // Instead, I do it step by step
271 0 : if (!pTabWin->Init())
272 : {
273 : // The initialisation has gone wrong, this TabWin is not available, so
274 : // I must clean up the data and the document
275 0 : pTabWin->clearListBox();
276 0 : delete pTabWin;
277 0 : arrInvalidTables.push_back(pData->GetAliasName());
278 :
279 0 : pTabWinDataList->erase( ::std::remove(pTabWinDataList->begin(),pTabWinDataList->end(),*aIter) ,pTabWinDataList->end());
280 0 : continue;
281 : }
282 :
283 0 : (*GetTabWinMap())[pData->GetAliasName()] = pTabWin; // add at the beginning as I am going backwards through the DataList
284 : // Use the default if there is no position or size
285 0 : if (!pData->HasPosition() && !pData->HasSize())
286 0 : SetDefaultTabWinPosSize(pTabWin);
287 :
288 0 : pTabWin->Show();
289 : }
290 :
291 : // Add the connections
292 0 : TTableConnectionData* pTabConnDataList = m_pView->getController().getTableConnectionData();
293 0 : TTableConnectionData::reverse_iterator aConIter = pTabConnDataList->rbegin();
294 :
295 0 : for(;aConIter != pTabConnDataList->rend();++aConIter)
296 : {
297 0 : OQueryTableConnectionData* pTabConnData = static_cast<OQueryTableConnectionData*>(aConIter->get());
298 :
299 : // do both tables for the connection exist ?
300 0 : OUString strTabExistenceTest = pTabConnData->getReferencingTable()->GetWinName();
301 0 : sal_Bool bInvalid = ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
302 0 : strTabExistenceTest = pTabConnData->getReferencedTable()->GetWinName();
303 0 : bInvalid = bInvalid && ::std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end();
304 :
305 0 : if (bInvalid)
306 : {
307 : // no -> bad luck, no connection
308 0 : pTabConnDataList->erase( ::std::remove(pTabConnDataList->begin(),pTabConnDataList->end(),*aConIter) ,pTabConnDataList->end());
309 0 : continue;
310 : }
311 :
312 : // adds a new connection to join view and notifies our accessible and invaldates the controller
313 0 : addConnection(new OQueryTableConnection(this, *aConIter));
314 0 : }
315 0 : }
316 :
317 0 : void OQueryTableView::ClearAll()
318 : {
319 0 : OJoinTableView::ClearAll();
320 :
321 0 : SetUpdateMode(true);
322 0 : m_pView->getController().setModified(sal_True);
323 0 : }
324 :
325 0 : OTableWindow* OQueryTableView::createWindow(const TTableWindowData::value_type& _pData)
326 : {
327 0 : return new OQueryTableWindow(this,_pData);
328 : }
329 :
330 0 : void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, sal_Bool _bCreateUndoAction)
331 : {
332 : // let's first check if I have the connection already
333 0 : OQueryTableConnection* pTabConn = NULL;
334 0 : const ::std::vector<OTableConnection*>* pConnections = getTableConnections();
335 0 : ::std::vector<OTableConnection*>::const_iterator aEnd = pConnections->end();
336 : ::std::vector<OTableConnection*>::const_iterator aIter = ::std::find( pConnections->begin(),
337 : aEnd,
338 : static_cast<const OTableConnection*>(&rNewConn)
339 0 : );
340 0 : if(aIter == aEnd )
341 : {
342 0 : aIter = pConnections->begin();
343 0 : for(;aIter != aEnd;++aIter)
344 : {
345 0 : if(*static_cast<OQueryTableConnection*>(*aIter) == rNewConn)
346 : {
347 0 : pTabConn = static_cast<OQueryTableConnection*>(*aIter);
348 0 : break;
349 : }
350 : }
351 : }
352 : else
353 0 : pTabConn = static_cast<OQueryTableConnection*>(*aIter);
354 :
355 : // no -> insert
356 0 : if (pTabConn == NULL)
357 : {
358 : // the new data ...
359 0 : OQueryTableConnectionData* pNewData = static_cast< OQueryTableConnectionData*>(rNewConn.GetData()->NewInstance());
360 0 : pNewData->CopyFrom(*rNewConn.GetData());
361 0 : TTableConnectionData::value_type aData(pNewData);
362 0 : OQueryTableConnection* pNewConn = new OQueryTableConnection(this, aData);
363 0 : GetConnection(pNewConn);
364 :
365 0 : connectionModified(this,pNewConn,_bCreateUndoAction);
366 : }
367 0 : }
368 :
369 0 : OTableWindowData* OQueryTableView::CreateImpl(const OUString& _rComposedName
370 : ,const OUString& _sTableName
371 : ,const OUString& _rWinName)
372 : {
373 0 : return new OQueryTableWindowData( _rComposedName, _sTableName,_rWinName );
374 : }
375 :
376 0 : void OQueryTableView::AddTabWin(const OUString& _rTableName, const OUString& _rAliasName, sal_Bool bNewTable)
377 : {
378 : // this method has been inherited from the base class, linking back to the parent and which constructs
379 : // an Alias and which passes on to my other AddTabWin
380 :
381 : // pity _rTableName is fully qualified, OQueryDesignView expects a string which only
382 : // contains schema and tables but no catalog.
383 0 : Reference< XConnection> xConnection = m_pView->getController().getConnection();
384 0 : if(!xConnection.is())
385 0 : return;
386 : try
387 : {
388 0 : Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData();
389 0 : OUString sCatalog, sSchema, sTable;
390 : ::dbtools::qualifiedNameComponents(xMetaData,
391 : _rTableName,
392 : sCatalog,
393 : sSchema,
394 : sTable,
395 0 : ::dbtools::eInDataManipulation);
396 0 : OUString sRealName(sSchema);
397 0 : if (!sRealName.isEmpty())
398 0 : sRealName+= OUString('.');
399 0 : sRealName += sTable;
400 :
401 0 : AddTabWin(_rTableName, sRealName, _rAliasName, bNewTable);
402 : }
403 0 : catch(SQLException&)
404 : {
405 : OSL_FAIL("qualifiedNameComponents");
406 0 : }
407 : }
408 :
409 : // find the table which has a foreign key with this referencedTable name
410 0 : Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,const OUString& _rReferencedTable)
411 : {
412 0 : if(!_rxKeys.is())
413 0 : return Reference<XPropertySet>();
414 :
415 0 : if ( !_rxKeys.is() )
416 0 : return Reference<XPropertySet>();
417 : // search the one and only primary key
418 0 : const sal_Int32 nCount = _rxKeys->getCount();
419 0 : for(sal_Int32 i=0;i<nCount ;++i)
420 : {
421 0 : Reference<XPropertySet> xKey(_rxKeys->getByIndex(i),UNO_QUERY);
422 0 : if(xKey.is())
423 : {
424 0 : sal_Int32 nKeyType = 0;
425 0 : xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
426 0 : if(KeyType::FOREIGN == nKeyType)
427 : {
428 0 : OUString sReferencedTable;
429 0 : xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable;
430 : // TODO check case
431 0 : if(sReferencedTable == _rReferencedTable)
432 0 : return xKey;
433 : }
434 : }
435 0 : }
436 0 : return Reference<XPropertySet>();
437 : }
438 :
439 0 : void OQueryTableView::AddTabWin(const OUString& _rComposedName, const OUString& _rTableName, const OUString& strAlias, sal_Bool bNewTable)
440 : {
441 : OSL_ENSURE(!_rTableName.isEmpty() || !strAlias.isEmpty(), "OQueryTableView::AddTabWin : no tables or aliases !");
442 : // If the table is not set, then it is a dummy window, but at least the alias must be set
443 :
444 : // build a new data structure
445 : // first check if this already has its data
446 0 : sal_Bool bAppend = bNewTable;
447 0 : TTableWindowData::value_type pNewTabWinData;
448 0 : TTableWindowData* pWindowData = getDesignView()->getController().getTableWindowData();
449 0 : TTableWindowData::iterator aWinIter = pWindowData->begin();
450 0 : TTableWindowData::iterator aWinEnd = pWindowData->end();
451 0 : for(;aWinIter != aWinEnd;++aWinIter)
452 : {
453 0 : pNewTabWinData = *aWinIter;
454 0 : if (pNewTabWinData && pNewTabWinData->GetWinName() == strAlias && pNewTabWinData->GetComposedName() == _rComposedName && pNewTabWinData->GetTableName() == _rTableName)
455 0 : break;
456 : }
457 0 : if ( !bAppend )
458 0 : bAppend = ( aWinIter == aWinEnd );
459 0 : if ( bAppend )
460 0 : pNewTabWinData = createTableWindowData(_rComposedName, _rTableName, strAlias);
461 : // I do not need to add TabWinData to the DocShell list, ShowTabWin does that.
462 :
463 : // Create a new window
464 0 : OQueryTableWindow* pNewTabWin = static_cast<OQueryTableWindow*>(createWindow(pNewTabWinData));
465 : // No need to initialize, as that happens in ShowTabWin
466 :
467 : // New UndoAction
468 0 : OQueryTabWinShowUndoAct* pUndoAction = new OQueryTabWinShowUndoAct(this);
469 0 : pUndoAction->SetTabWin(pNewTabWin); // Window
470 0 : sal_Bool bSuccess = ShowTabWin(pNewTabWin, pUndoAction,bAppend);
471 0 : if(!bSuccess)
472 : {
473 : // reset table window
474 0 : pUndoAction->SetTabWin(NULL);
475 0 : pUndoAction->SetOwnership(sal_False);
476 :
477 0 : delete pUndoAction;
478 0 : return;
479 : }
480 :
481 : // Show the relations between the individual tables
482 0 : OTableWindowMap* pTabWins = GetTabWinMap();
483 0 : if(bNewTable && !pTabWins->empty() && !_rTableName.isEmpty())
484 : {
485 0 : modified();
486 0 : if ( m_pAccessible )
487 : m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
488 : Any(),
489 : makeAny(pNewTabWin->GetAccessible())
490 0 : );
491 :
492 : do {
493 :
494 0 : if ( pNewTabWin->GetData()->isQuery() )
495 0 : break;
496 :
497 : try
498 : {
499 : // find relations between the table an the tables already inserted
500 0 : Reference< XIndexAccess> xKeyIndex = pNewTabWin->GetData()->getKeys();
501 0 : if ( !xKeyIndex.is() )
502 0 : break;
503 :
504 0 : Reference<XNameAccess> xFKeyColumns;
505 0 : OUString aReferencedTable;
506 0 : Reference<XColumnsSupplier> xColumnsSupplier;
507 :
508 0 : const sal_Int32 nKeyCount = xKeyIndex->getCount();
509 0 : for ( sal_Int32 i=0; i<nKeyCount ; ++i )
510 : {
511 0 : Reference< XPropertySet > xProp( xKeyIndex->getByIndex(i), UNO_QUERY_THROW );
512 0 : xColumnsSupplier.set( xProp, UNO_QUERY_THROW );
513 0 : xFKeyColumns.set( xColumnsSupplier->getColumns(), UNO_QUERY_THROW );
514 :
515 0 : sal_Int32 nKeyType = 0;
516 0 : xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType;
517 :
518 0 : switch ( nKeyType )
519 : {
520 : case KeyType::FOREIGN:
521 : { // our new table has a foreign key
522 : // so look if the referenced table is already in our list
523 0 : xProp->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= aReferencedTable;
524 : OSL_ENSURE(!aReferencedTable.isEmpty(),"Foreign key without referencedTableName");
525 :
526 0 : OTableWindowMap::const_iterator aIter = pTabWins->find(aReferencedTable);
527 0 : OTableWindowMap::const_iterator aEnd = pTabWins->end();
528 0 : if(aIter == aEnd)
529 : {
530 0 : for(aIter = pTabWins->begin();aIter != aEnd;++aIter)
531 : {
532 0 : OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second);
533 : OSL_ENSURE( pTabWinTmp,"TableWindow is null!" );
534 0 : if ( pTabWinTmp != pNewTabWin && pTabWinTmp->GetComposedName() == aReferencedTable )
535 0 : break;
536 : }
537 : }
538 0 : if ( aIter != aEnd && pNewTabWin != aIter->second )
539 0 : addConnections( this, *pNewTabWin, *static_cast<OQueryTableWindow*>(aIter->second), xFKeyColumns );
540 : }
541 0 : break;
542 :
543 : case KeyType::PRIMARY:
544 : {
545 : // we have a primary key so look in our list if there exsits a key which this is refered to
546 0 : OTableWindowMap::const_iterator aIter = pTabWins->begin();
547 0 : OTableWindowMap::const_iterator aEnd = pTabWins->end();
548 0 : for(;aIter != aEnd;++aIter)
549 : {
550 0 : OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second);
551 0 : if ( pTabWinTmp == pNewTabWin )
552 0 : continue;
553 :
554 0 : if ( pTabWinTmp->GetData()->isQuery() )
555 0 : continue;
556 :
557 : OSL_ENSURE(pTabWinTmp,"TableWindow is null!");
558 0 : Reference< XPropertySet > xFKKey = getKeyReferencedTo( pTabWinTmp->GetData()->getKeys(), pNewTabWin->GetComposedName() );
559 0 : if ( !xFKKey.is() )
560 0 : continue;
561 :
562 0 : Reference<XColumnsSupplier> xFKColumnsSupplier( xFKKey, UNO_QUERY_THROW );
563 0 : Reference< XNameAccess > xTColumns( xFKColumnsSupplier->getColumns(), UNO_QUERY_THROW );
564 0 : addConnections( this, *pTabWinTmp, *pNewTabWin, xTColumns );
565 0 : }
566 : }
567 0 : break;
568 : }
569 0 : }
570 : }
571 0 : catch( const Exception& )
572 : {
573 : DBG_UNHANDLED_EXCEPTION();
574 : }
575 :
576 : } while ( false );
577 : }
578 :
579 : // My parent needs to be informed about the delete
580 0 : m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
581 :
582 0 : if (bSuccess && m_lnkTabWinsChangeHandler.IsSet())
583 : {
584 0 : TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_ADDED_WIN, pNewTabWin->GetAliasName());
585 0 : m_lnkTabWinsChangeHandler.Call(&aHint);
586 0 : }
587 : }
588 :
589 0 : void OQueryTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest)
590 : {
591 0 : OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(jxdSource.pListBox->GetTabWin());
592 0 : OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(jxdDest.pListBox->GetTabWin());
593 :
594 0 : OUString aSourceFieldName, aDestFieldName;
595 0 : aSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry);
596 0 : aDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry);
597 :
598 0 : OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
599 0 : if ( !pConn )
600 : {
601 : // new data object
602 0 : OQueryTableConnectionData* pNewConnectionData = new OQueryTableConnectionData(pSourceWin->GetData(), pDestWin->GetData());
603 0 : TTableConnectionData::value_type aNewConnectionData(pNewConnectionData);
604 :
605 : sal_uInt32 nSourceFieldIndex, nDestFieldIndex;
606 : ETableFieldType eSourceFieldType, eDestFieldType;
607 :
608 : // Get name/position/type of both affected fields ...
609 : // Source
610 :
611 0 : nSourceFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry);
612 0 : eSourceFieldType = static_cast< OTableFieldInfo*>(jxdSource.pEntry->GetUserData())->GetKeyType();
613 :
614 : // Dest
615 :
616 0 : nDestFieldIndex = jxdDest.pListBox->GetModel()->GetAbsPos(jxdDest.pEntry);
617 0 : eDestFieldType = static_cast< OTableFieldInfo*>(jxdDest.pEntry->GetUserData())->GetKeyType();
618 :
619 : // ... and set them
620 0 : pNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex);
621 0 : pNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex);
622 :
623 0 : pNewConnectionData->SetFieldType(JTCS_FROM, eSourceFieldType);
624 0 : pNewConnectionData->SetFieldType(JTCS_TO, eDestFieldType);
625 :
626 0 : pNewConnectionData->AppendConnLine( aSourceFieldName,aDestFieldName );
627 :
628 0 : OQueryTableConnection aNewConnection(this, aNewConnectionData);
629 0 : NotifyTabConnection(aNewConnection);
630 : // As usual with NotifyTabConnection, using a local variable is fine because a copy is made
631 : }
632 : else
633 : {
634 : // the connection could point on the other side
635 0 : if(pConn->GetSourceWin() == pDestWin)
636 : {
637 0 : OUString aTmp(aSourceFieldName);
638 0 : aSourceFieldName = aDestFieldName;
639 0 : aDestFieldName = aTmp;
640 : }
641 :
642 0 : pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName );
643 :
644 0 : connectionModified(this,pConn,sal_False);
645 0 : }
646 0 : }
647 :
648 0 : void OQueryTableView::ConnDoubleClicked(OTableConnection* pConnection)
649 : {
650 0 : if( openJoinDialog(this,pConnection->GetData(),sal_False) )
651 : {
652 0 : connectionModified(this,pConnection,sal_False);
653 0 : SelectConn( pConnection );
654 : }
655 0 : }
656 :
657 0 : void OQueryTableView::createNewConnection()
658 : {
659 0 : TTableConnectionData::value_type pData(new OQueryTableConnectionData());
660 0 : if( openJoinDialog(this,pData,sal_True) )
661 : {
662 0 : OTableWindowMap* pMap = GetTabWinMap();
663 0 : OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>((*pMap)[pData->getReferencingTable()->GetWinName()]);
664 0 : OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>((*pMap)[pData->getReferencedTable()->GetWinName()]);
665 : // first we have to look if the this connection already exists
666 0 : OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true);
667 0 : sal_Bool bNew = sal_True;
668 0 : if ( pConn )
669 : {
670 0 : pConn->GetData()->CopyFrom( *pData );
671 0 : bNew = sal_False;
672 : }
673 : else
674 : {
675 : // create a new conenction and append it
676 0 : OQueryTableConnection* pQConn = new OQueryTableConnection(this, pData);
677 0 : GetConnection(pQConn);
678 0 : pConn = pQConn;
679 : }
680 0 : connectionModified(this,pConn,bNew);
681 0 : if ( !bNew && pConn == GetSelectedConn() ) // our connection was selected before so we have to reselect it
682 0 : SelectConn( pConn );
683 0 : }
684 0 : }
685 :
686 0 : bool OQueryTableView::RemoveConnection( OTableConnection* _pConnection,sal_Bool /*_bDelete*/ )
687 : {
688 :
689 : // we don't want that our connection will be deleted, we put it in the undo manager
690 0 : bool bRet = OJoinTableView::RemoveConnection( _pConnection,sal_False);
691 :
692 : // add undo action
693 : addUndoAction( this,
694 0 : new OQueryDelTabConnUndoAction(this),
695 : static_cast< OQueryTableConnection*>(_pConnection),
696 0 : sal_True);
697 0 : return bRet;
698 : }
699 :
700 0 : void OQueryTableView::KeyInput( const KeyEvent& rEvt )
701 : {
702 0 : OJoinTableView::KeyInput( rEvt );
703 0 : }
704 :
705 0 : OQueryTableWindow* OQueryTableView::FindTable(const OUString& rAliasName)
706 : {
707 : OSL_ENSURE(!rAliasName.isEmpty(), "OQueryTableView::FindTable : the AliasName should not be empty !");
708 : // (it is harmless but does not make sense and indicates that there is probably an error in the caller)
709 0 : OTableWindowMap::const_iterator aIter = GetTabWinMap()->find(rAliasName);
710 0 : if(aIter != GetTabWinMap()->end())
711 0 : return static_cast<OQueryTableWindow*>(aIter->second);
712 0 : return NULL;
713 : }
714 :
715 0 : sal_Bool OQueryTableView::FindTableFromField(const OUString& rFieldName, OTableFieldDescRef& rInfo, sal_uInt16& rCnt)
716 : {
717 0 : rCnt = 0;
718 0 : OTableWindowMap::const_iterator aIter = GetTabWinMap()->begin();
719 0 : OTableWindowMap::const_iterator aEnd = GetTabWinMap()->end();
720 0 : for(;aIter != aEnd;++aIter)
721 : {
722 0 : if(static_cast<OQueryTableWindow*>(aIter->second)->ExistsField(rFieldName, rInfo))
723 0 : ++rCnt;
724 : }
725 :
726 0 : return rCnt == 1;
727 : }
728 :
729 0 : bool OQueryTableView::ContainsTabWin(const OTableWindow& rTabWin)
730 : {
731 0 : OTableWindowMap* pTabWins = GetTabWinMap();
732 : OSL_ENSURE(pTabWins != NULL, "OQueryTableView::RemoveTabWin : Window should not be NULL !");
733 :
734 0 : OTableWindowMap::iterator aIter = pTabWins->begin();
735 0 : OTableWindowMap::iterator aEnd = pTabWins->end();
736 :
737 0 : for ( ;aIter != aEnd ; ++aIter )
738 : {
739 0 : if ( aIter->second == &rTabWin )
740 : {
741 0 : return true;
742 : }
743 : }
744 :
745 0 : return false;
746 : }
747 :
748 0 : void OQueryTableView::RemoveTabWin(OTableWindow* pTabWin)
749 : {
750 : OSL_ENSURE(pTabWin != NULL, "OQueryTableView::RemoveTabWin : Window should not be NULL !");
751 :
752 0 : if(pTabWin && ContainsTabWin(*pTabWin)) // #i122589# check if registered before deleting
753 : {
754 : // I need my parent so it can be informed about the deletion
755 0 : OQueryDesignView* pParent = static_cast<OQueryDesignView*>(getDesignView());
756 :
757 0 : SfxUndoManager& rUndoMgr = m_pView->getController().GetUndoManager();
758 0 : rUndoMgr.EnterListAction( OUString( ModuleRes(STR_QUERY_UNDO_TABWINDELETE) ), OUString() );
759 :
760 : // add the Undo-Action
761 0 : OQueryTabWinDelUndoAct* pUndoAction = new OQueryTabWinDelUndoAct(this);
762 0 : pUndoAction->SetTabWin(static_cast< OQueryTableWindow*>(pTabWin));
763 :
764 : // and hide the window
765 0 : HideTabWin(static_cast< OQueryTableWindow*>(pTabWin), pUndoAction);
766 :
767 : // Undo Actions and delete the fields in SelectionBrowseBox
768 0 : pParent->TableDeleted( static_cast< OQueryTableWindowData*>(pTabWin->GetData().get())->GetAliasName() );
769 :
770 0 : m_pView->getController().addUndoActionAndInvalidate( pUndoAction );
771 0 : rUndoMgr.LeaveListAction();
772 :
773 0 : if (m_lnkTabWinsChangeHandler.IsSet())
774 : {
775 0 : TabWinsChangeNotification aHint(TabWinsChangeNotification::AT_REMOVED_WIN, static_cast< OQueryTableWindow*>(pTabWin)->GetAliasName());
776 0 : m_lnkTabWinsChangeHandler.Call(&aHint);
777 : }
778 :
779 0 : modified();
780 0 : if ( m_pAccessible )
781 : m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD,
782 : makeAny(pTabWin->GetAccessible()),
783 : Any()
784 0 : );
785 : }
786 0 : }
787 :
788 0 : void OQueryTableView::EnsureVisible(const OTableWindow* pWin)
789 : {
790 :
791 0 : Invalidate(INVALIDATE_NOCHILDREN);
792 0 : OJoinTableView::EnsureVisible(pWin);
793 0 : }
794 :
795 0 : void OQueryTableView::GetConnection(OQueryTableConnection* pConn)
796 : {
797 : // add to me and the document
798 :
799 0 : addConnection( pConn );
800 0 : }
801 :
802 0 : void OQueryTableView::DropConnection(OQueryTableConnection* pConn)
803 : {
804 : // Pay attention to the selection
805 : // remove from me and the document
806 0 : RemoveConnection( pConn ,sal_False);
807 0 : }
808 :
809 0 : void OQueryTableView::HideTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction )
810 : {
811 0 : OTableWindowMap* pTabWins = GetTabWinMap();
812 : OSL_ENSURE(pTabWins != NULL, "OQueryTableView::HideTabWin : have no TabWins !");
813 :
814 0 : if (pTabWin)
815 : {
816 : // Window
817 : // save the position in its data
818 0 : getDesignView()->SaveTabWinUIConfig(pTabWin);
819 : // (I need to go via the parent, as only the parent knows the position of the scrollbars)
820 : // and then out of the TabWins list and hide
821 0 : OTableWindowMap::iterator aIter = pTabWins->begin();
822 0 : OTableWindowMap::iterator aEnd = pTabWins->end();
823 0 : for ( ;aIter != aEnd ; ++aIter )
824 0 : if ( aIter->second == pTabWin )
825 : {
826 0 : pTabWins->erase( aIter );
827 0 : break;
828 : }
829 :
830 0 : pTabWin->Hide(); // do not destroy it, as it is still in the undo list!!
831 :
832 : // the TabWin data must also be passed out of my responsibility
833 0 : TTableWindowData* pTabWinDataList = m_pView->getController().getTableWindowData();
834 0 : pTabWinDataList->erase( ::std::remove(pTabWinDataList->begin(),pTabWinDataList->end(),pTabWin->GetData()),pTabWinDataList->end());
835 : // The data should not be destroyed as TabWin itself - which is still alive - needs them
836 : // Either it goes back into my responsibility, (via ShowTabWin), then I add the data back,
837 : // or the Undo-Action, which currently has full responsibility for the window
838 : // and its data, gets destroyed and destroys both the window and its data
839 :
840 0 : if (m_pLastFocusTabWin == pTabWin)
841 0 : m_pLastFocusTabWin = NULL;
842 :
843 : // collect connections belonging to the window and pass to UndoAction
844 0 : sal_Int16 nCnt = 0;
845 0 : const ::std::vector<OTableConnection*>* pTabConList = getTableConnections();
846 0 : ::std::vector<OTableConnection*>::const_iterator aIter2 = pTabConList->begin();
847 0 : for(;aIter2 != pTabConList->end();)// the end may change
848 : {
849 0 : OQueryTableConnection* pTmpEntry = static_cast<OQueryTableConnection*>(*aIter2);
850 : OSL_ENSURE(pTmpEntry,"OQueryTableConnection is null!");
851 0 : if( pTmpEntry->GetAliasName(JTCS_FROM) == pTabWin->GetAliasName() ||
852 0 : pTmpEntry->GetAliasName(JTCS_TO) == pTabWin->GetAliasName() )
853 : {
854 : // add to undo list
855 0 : pUndoAction->InsertConnection(pTmpEntry);
856 :
857 : // call base class because we append an undo action
858 : // but this time we are in a undo action list
859 0 : OJoinTableView::RemoveConnection(pTmpEntry,sal_False);
860 0 : aIter2 = pTabConList->begin();
861 0 : ++nCnt;
862 : }
863 : else
864 0 : ++aIter2;
865 : }
866 :
867 0 : if (nCnt)
868 0 : InvalidateConnections();
869 :
870 0 : m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
871 :
872 : // inform the UndoAction that the window and connections belong to it
873 0 : pUndoAction->SetOwnership(sal_True);
874 :
875 : // by doing so, we have modified the document
876 0 : m_pView->getController().setModified( sal_True );
877 0 : m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
878 : }
879 0 : }
880 :
881 0 : sal_Bool OQueryTableView::ShowTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction,sal_Bool _bAppend )
882 : {
883 :
884 0 : sal_Bool bSuccess = sal_False;
885 :
886 0 : if (pTabWin)
887 : {
888 0 : if (pTabWin->Init())
889 : {
890 0 : TTableWindowData::value_type pData = pTabWin->GetData();
891 : OSL_ENSURE(pData != 0, "OQueryTableView::ShowTabWin : TabWin has no data !");
892 : // If there is a position and size defined, we use them
893 0 : if (pData->HasPosition() && pData->HasSize())
894 : {
895 0 : Size aSize(CalcZoom(pData->GetSize().Width()),CalcZoom(pData->GetSize().Height()));
896 0 : pTabWin->SetPosSizePixel(pData->GetPosition(), aSize);
897 : }
898 : else
899 : // else set a default position
900 0 : SetDefaultTabWinPosSize(pTabWin);
901 :
902 : // Show the window and add to the list
903 0 : OUString sName = static_cast< OQueryTableWindowData*>(pData.get())->GetAliasName();
904 : OSL_ENSURE(GetTabWinMap()->find(sName) == GetTabWinMap()->end(),"Alias name already in list!");
905 0 : GetTabWinMap()->insert(OTableWindowMap::value_type(sName,pTabWin));
906 :
907 0 : pTabWin->Show();
908 :
909 0 : pTabWin->Update();
910 : // We must call Update() in order to show the connections in the window correctly. This sounds strange,
911 : // but the Listbox has an internal Member which is initialized when the Listbox is first shown (after the Listbox
912 : // is filled in Init). This Member will eventually be needed for
913 : // GetEntryPos, and then in turn by the Connection, when its starting point to the window must be determined.
914 :
915 : // the Connections
916 0 : ::std::vector<OTableConnection*>* pTableCon = pUndoAction->GetTabConnList();
917 0 : ::std::vector<OTableConnection*>::iterator aIter = pTableCon->begin();
918 0 : ::std::vector<OTableConnection*>::iterator aEnd = pTableCon->end();
919 :
920 0 : for(;aIter != aEnd;++aIter)
921 0 : addConnection(*aIter); // add all connections from the undo action
922 :
923 0 : pTableCon->clear();
924 :
925 : // and add the window's data to the list (of the document)
926 0 : if(_bAppend)
927 0 : m_pView->getController().getTableWindowData()->push_back(pTabWin->GetData());
928 :
929 0 : m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE);
930 :
931 : // and inform the UndoAction that the window belongs to me
932 0 : pUndoAction->SetOwnership(sal_False);
933 :
934 0 : bSuccess = sal_True;
935 : }
936 : else
937 : {
938 : // Initialisation failed
939 : // (for example when the Connection to the database is not available at the moment)
940 0 : pTabWin->clearListBox();
941 0 : delete pTabWin;
942 : }
943 : }
944 :
945 : // show that I have changed the document
946 0 : if(!m_pView->getController().isReadOnly())
947 0 : m_pView->getController().setModified( sal_True );
948 :
949 0 : m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY);
950 :
951 0 : return bSuccess;
952 : }
953 :
954 0 : void OQueryTableView::InsertField(const OTableFieldDescRef& rInfo)
955 : {
956 : OSL_ENSURE(getDesignView() != NULL, "OQueryTableView::InsertField : has no Parent !");
957 0 : static_cast<OQueryDesignView*>(getDesignView())->InsertField(rInfo);
958 0 : }
959 :
960 0 : sal_Bool OQueryTableView::ExistsAVisitedConn(const OQueryTableWindow* pFrom) const
961 : {
962 0 : const ::std::vector<OTableConnection*>* pList = getTableConnections();
963 0 : if (pList)
964 : {
965 0 : ::std::vector<OTableConnection*>::const_iterator aIter = pList->begin();
966 0 : ::std::vector<OTableConnection*>::const_iterator aEnd = pList->end();
967 0 : for(;aIter != aEnd;++aIter)
968 : {
969 0 : OQueryTableConnection* pTemp = static_cast<OQueryTableConnection*>(*aIter);
970 0 : if (pTemp->IsVisited() &&
971 0 : (pFrom == static_cast< OQueryTableWindow*>(pTemp->GetSourceWin()) || pFrom == static_cast< OQueryTableWindow*>(pTemp->GetDestWin())))
972 0 : return true;
973 : }
974 : }
975 :
976 0 : return false;
977 : }
978 :
979 0 : void OQueryTableView::onNoColumns_throw()
980 : {
981 0 : OUString sError( ModuleRes( STR_STATEMENT_WITHOUT_RESULT_SET ) );
982 0 : ::dbtools::throwSQLException( sError, ::dbtools::SQL_GENERAL_ERROR, NULL );
983 0 : }
984 :
985 0 : bool OQueryTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& _pData) const
986 : {
987 0 : OQueryTableConnectionData* pQueryData = static_cast<OQueryTableConnectionData*>(_pData.get());
988 0 : return pQueryData && (pQueryData->GetJoinType() == CROSS_JOIN);
989 : }
990 :
991 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|