Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / dbaccess / source / ui / control / tabletree.cxx
blobac69822cac493d50968cbd6369d08ad962a58692
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "tabletree.hxx"
21 #include "imageprovider.hxx"
22 #include "moduledbu.hxx"
23 #include "dbu_control.hrc"
24 #include <vcl/layout.hxx>
25 #include <vcl/menu.hxx>
26 #include <vcl/builderfactory.hxx>
27 #include <connectivity/dbtools.hxx>
28 #include <comphelper/types.hxx>
29 #include "dbustrings.hrc"
30 #include <com/sun/star/sdb/application/DatabaseObject.hpp>
31 #include <com/sun/star/sdb/application/DatabaseObjectContainer.hpp>
32 #include <com/sun/star/sdbc/XDriverAccess.hpp>
33 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
34 #include <com/sun/star/sdbcx/XViewsSupplier.hpp>
35 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
36 #include <com/sun/star/sdb/SQLContext.hpp>
37 #include <com/sun/star/sdbc/XRow.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include "commontypes.hxx"
40 #include "listviewitems.hxx"
41 #include <tools/diagnose_ex.h>
42 #include <osl/diagnose.h>
43 #include <rtl/ustrbuf.hxx>
44 #include <connectivity/dbmetadata.hxx>
45 #include "svtools/treelistentry.hxx"
46 #include <o3tl/make_unique.hxx>
48 #include <algorithm>
50 namespace dbaui
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::sdb;
55 using namespace ::com::sun::star::lang;
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::sdb::application;
62 using namespace ::dbtools;
63 using namespace ::comphelper;
65 namespace DatabaseObject = ::com::sun::star::sdb::application::DatabaseObject;
66 namespace DatabaseObjectContainer = ::com::sun::star::sdb::application::DatabaseObjectContainer;
68 // OTableTreeListBox
69 OTableTreeListBox::OTableTreeListBox(vcl::Window* pParent, WinBits nWinStyle)
70 :OMarkableTreeListBox(pParent, nWinStyle)
71 ,m_xImageProvider( new ImageProvider )
72 ,m_bVirtualRoot(false)
73 ,m_bNoEmptyFolders( false )
75 implSetDefaultImages();
78 VCL_BUILDER_DECL_FACTORY(OTableTreeListBox)
80 WinBits nWinStyle = 0;
81 OString sBorder = VclBuilder::extractCustomProperty(rMap);
82 if (!sBorder.isEmpty())
83 nWinStyle |= WB_BORDER;
84 rRet = VclPtr<OTableTreeListBox>::Create(pParent, nWinStyle);
87 void OTableTreeListBox::implSetDefaultImages()
89 ImageProvider aImageProvider;
90 SetDefaultExpandedEntryBmp( ImageProvider::getFolderImage( DatabaseObject::TABLE ) );
91 SetDefaultCollapsedEntryBmp( ImageProvider::getFolderImage( DatabaseObject::TABLE ) );
94 bool OTableTreeListBox::isFolderEntry( const SvTreeListEntry* _pEntry )
96 sal_Int32 nEntryType = reinterpret_cast< sal_IntPtr >( _pEntry->GetUserData() );
97 if ( ( nEntryType == DatabaseObjectContainer::TABLES )
98 || ( nEntryType == DatabaseObjectContainer::CATALOG )
99 || ( nEntryType == DatabaseObjectContainer::SCHEMA )
101 return true;
102 return false;
105 void OTableTreeListBox::notifyHiContrastChanged()
107 implSetDefaultImages();
109 SvTreeListEntry* pEntryLoop = First();
110 while (pEntryLoop)
112 size_t nCount = pEntryLoop->ItemCount();
113 for (size_t i=0;i<nCount;++i)
115 SvLBoxItem& rItem = pEntryLoop->GetItem(i);
116 if (rItem.GetType() == SV_ITEM_ID_LBOXCONTEXTBMP)
118 SvLBoxContextBmp& rContextBitmapItem = static_cast< SvLBoxContextBmp& >( rItem );
120 Image aImage;
121 if ( isFolderEntry( pEntryLoop ) )
123 aImage = ImageProvider::getFolderImage( DatabaseObject::TABLE );
125 else
127 OUString sCompleteName( getQualifiedTableName( pEntryLoop ) );
128 m_xImageProvider->getImages( sCompleteName, DatabaseObject::TABLE, aImage );
131 rContextBitmapItem.SetBitmap1( aImage );
132 rContextBitmapItem.SetBitmap2( aImage );
133 break;
136 pEntryLoop = Next(pEntryLoop);
140 void OTableTreeListBox::implOnNewConnection( const Reference< XConnection >& _rxConnection )
142 m_xConnection = _rxConnection;
143 m_xImageProvider.reset( new ImageProvider( m_xConnection ) );
146 void OTableTreeListBox::UpdateTableList( const Reference< XConnection >& _rxConnection ) throw(SQLException, std::exception)
148 Sequence< OUString > sTables, sViews;
150 OUString sCurrentActionError;
153 Reference< XTablesSupplier > xTableSupp( _rxConnection, UNO_QUERY_THROW );
154 sCurrentActionError = ModuleRes(STR_NOTABLEINFO);
156 Reference< XNameAccess > xTables,xViews;
158 Reference< XViewsSupplier > xViewSupp( _rxConnection, UNO_QUERY );
159 if ( xViewSupp.is() )
161 xViews = xViewSupp->getViews();
162 if (xViews.is())
163 sViews = xViews->getElementNames();
166 xTables = xTableSupp->getTables();
167 if (xTables.is())
168 sTables = xTables->getElementNames();
170 catch(RuntimeException&)
172 OSL_FAIL("OTableTreeListBox::UpdateTableList : caught an RuntimeException!");
174 catch ( const SQLException& )
176 throw;
178 catch(Exception&)
180 // a non-SQLException exception occurred ... simply throw an SQLException
181 SQLException aInfo;
182 aInfo.Message = sCurrentActionError;
183 throw aInfo;
186 UpdateTableList( _rxConnection, sTables, sViews );
189 namespace
191 struct OViewSetter : public ::std::unary_function< OTableTreeListBox::TNames::value_type, bool>
193 const Sequence< OUString> m_aViews;
194 ::comphelper::UStringMixEqual m_aEqualFunctor;
196 OViewSetter(const Sequence< OUString>& _rViews,bool _bCase) : m_aViews(_rViews),m_aEqualFunctor(_bCase){}
197 OTableTreeListBox::TNames::value_type operator() (const OUString& lhs)
199 OTableTreeListBox::TNames::value_type aRet;
200 aRet.first = lhs;
201 const OUString* pIter = m_aViews.getConstArray();
202 const OUString* pEnd = m_aViews.getConstArray() + m_aViews.getLength();
203 aRet.second = ::std::any_of(pIter,pEnd,::std::bind2nd(m_aEqualFunctor,lhs));
205 return aRet;
211 void OTableTreeListBox::UpdateTableList(
212 const Reference< XConnection >& _rxConnection,
213 const Sequence< OUString>& _rTables,
214 const Sequence< OUString>& _rViews
217 TNames aTables;
218 aTables.resize(_rTables.getLength());
219 const OUString* pIter = _rTables.getConstArray();
220 const OUString* pEnd = _rTables.getConstArray() + _rTables.getLength();
223 Reference< XDatabaseMetaData > xMeta( _rxConnection->getMetaData(), UNO_QUERY_THROW );
224 ::std::transform( pIter, pEnd,
225 aTables.begin(), OViewSetter( _rViews, xMeta->supportsMixedCaseQuotedIdentifiers() ) );
227 catch(Exception&)
229 DBG_UNHANDLED_EXCEPTION();
231 UpdateTableList( _rxConnection, aTables );
234 namespace
236 ::std::vector< OUString > lcl_getMetaDataStrings_throw( const Reference< XResultSet >& _rxMetaDataResult, sal_Int32 _nColumnIndex )
238 ::std::vector< OUString > aStrings;
239 Reference< XRow > xRow( _rxMetaDataResult, UNO_QUERY_THROW );
240 while ( _rxMetaDataResult->next() )
241 aStrings.push_back( xRow->getString( _nColumnIndex ) );
242 return aStrings;
245 bool lcl_shouldDisplayEmptySchemasAndCatalogs( const Reference< XConnection >& _rxConnection )
247 ::dbtools::DatabaseMetaData aMetaData( _rxConnection );
248 return aMetaData.displayEmptyTableFolders();
252 void OTableTreeListBox::UpdateTableList( const Reference< XConnection >& _rxConnection, const TNames& _rTables )
254 implOnNewConnection( _rxConnection );
256 // throw away all the old stuff
257 Clear();
261 if (haveVirtualRoot())
263 OUString sRootEntryText;
264 if ( ::std::none_of(_rTables.begin(),_rTables.end(),
265 [] (const TNames::value_type& name) { return !name.second; }) )
266 sRootEntryText = ModuleRes(STR_ALL_TABLES);
267 else if ( ::std::none_of(_rTables.begin(),_rTables.end(),
268 [] (const TNames::value_type& name) { return name.second; }) )
269 sRootEntryText = ModuleRes(STR_ALL_VIEWS);
270 else
271 sRootEntryText = ModuleRes(STR_ALL_TABLES_AND_VIEWS);
272 InsertEntry( sRootEntryText, nullptr, false, TREELIST_APPEND, reinterpret_cast< void* >( DatabaseObjectContainer::TABLES ) );
275 if ( _rTables.empty() )
276 // nothing to do (besides inserting the root entry)
277 return;
279 // get the table/view names
280 TNames::const_iterator aIter = _rTables.begin();
281 TNames::const_iterator aEnd = _rTables.end();
283 Reference< XDatabaseMetaData > xMeta( _rxConnection->getMetaData(), UNO_QUERY_THROW );
284 for ( ; aIter != aEnd; ++aIter )
286 // add the entry
287 implAddEntry(
288 xMeta,
289 aIter->first,
290 false
294 if ( !m_bNoEmptyFolders && lcl_shouldDisplayEmptySchemasAndCatalogs( _rxConnection ) )
296 bool bSupportsCatalogs = xMeta->supportsCatalogsInDataManipulation();
297 bool bSupportsSchemas = xMeta->supportsSchemasInDataManipulation();
299 if ( bSupportsCatalogs || bSupportsSchemas )
301 // we display empty catalogs if the DB supports catalogs, and they're noted at the beginning of a
302 // composed name. Otherwise, we display empty schematas. (also see the tree structure explained in
303 // implAddEntry)
304 bool bCatalogs = bSupportsCatalogs && xMeta->isCatalogAtStart();
306 ::std::vector< OUString > aFolderNames( lcl_getMetaDataStrings_throw(
307 bCatalogs ? xMeta->getCatalogs() : xMeta->getSchemas(), 1 ) );
308 sal_Int32 nFolderType = bCatalogs ? DatabaseObjectContainer::CATALOG : DatabaseObjectContainer::SCHEMA;
310 SvTreeListEntry* pRootEntry = getAllObjectsEntry();
311 for ( ::std::vector< OUString >::const_iterator folder = aFolderNames.begin();
312 folder != aFolderNames.end();
313 ++folder
316 SvTreeListEntry* pFolder = GetEntryPosByName( *folder, pRootEntry );
317 if ( !pFolder )
318 InsertEntry( *folder, pRootEntry, false, TREELIST_APPEND, reinterpret_cast< void* >( nFolderType ) );
323 catch ( const Exception& )
325 DBG_UNHANDLED_EXCEPTION();
329 bool OTableTreeListBox::isWildcardChecked(SvTreeListEntry* _pEntry)
331 if (_pEntry)
333 OBoldListboxString* pTextItem = static_cast<OBoldListboxString*>(_pEntry->GetFirstItem(SV_ITEM_ID_BOLDLBSTRING));
334 if (pTextItem)
335 return pTextItem->isEmphasized();
337 return false;
340 void OTableTreeListBox::checkWildcard(SvTreeListEntry* _pEntry)
342 SetCheckButtonState(_pEntry, SvButtonState::Checked);
343 checkedButton_noBroadcast(_pEntry);
346 SvTreeListEntry* OTableTreeListBox::getAllObjectsEntry() const
348 return haveVirtualRoot() ? First() : nullptr;
351 void OTableTreeListBox::checkedButton_noBroadcast(SvTreeListEntry* _pEntry)
353 OMarkableTreeListBox::checkedButton_noBroadcast(_pEntry);
355 // if an entry has children, it makes a difference if the entry is checked
356 // because all children are checked or if the user checked it explicitly.
357 // So we track explicit (un)checking
359 SvButtonState eState = GetCheckButtonState(_pEntry);
360 OSL_ENSURE(SvButtonState::Tristate != eState, "OTableTreeListBox::CheckButtonHdl: user action which lead to TRISTATE?");
361 implEmphasize(_pEntry, SvButtonState::Checked == eState);
364 void OTableTreeListBox::implEmphasize(SvTreeListEntry* _pEntry, bool _bChecked, bool _bUpdateDescendants, bool _bUpdateAncestors)
366 OSL_ENSURE(_pEntry, "OTableTreeListBox::implEmphasize: invalid entry (NULL)!");
368 // special emphasizing handling for the "all objects" entry
369 bool bAllObjectsEntryAffected = haveVirtualRoot() && (getAllObjectsEntry() == _pEntry);
370 if ( GetModel()->HasChildren(_pEntry) // the entry has children
371 || bAllObjectsEntryAffected // or it is the "all objects" entry
374 OBoldListboxString* pTextItem = static_cast<OBoldListboxString*>(_pEntry->GetFirstItem(SV_ITEM_ID_BOLDLBSTRING));
375 if (pTextItem)
376 pTextItem->emphasize(_bChecked);
378 if (bAllObjectsEntryAffected)
379 InvalidateEntry(_pEntry);
382 if (_bUpdateDescendants)
384 // remove the mark for all children of the checked entry
385 SvTreeListEntry* pChildLoop = FirstChild(_pEntry);
386 while (pChildLoop)
388 if (GetModel()->HasChildren(pChildLoop))
389 implEmphasize(pChildLoop, false, true, false);
390 pChildLoop = NextSibling(pChildLoop);
394 if (_bUpdateAncestors)
396 // remove the mark for all ancestors of the entry
397 if (GetModel()->HasParent(_pEntry))
398 implEmphasize(GetParent(_pEntry), false, false);
402 void OTableTreeListBox::InitEntry(SvTreeListEntry* _pEntry, const OUString& _rString, const Image& _rCollapsedBitmap, const Image& _rExpandedBitmap, SvLBoxButtonKind _eButtonKind)
404 OMarkableTreeListBox::InitEntry(_pEntry, _rString, _rCollapsedBitmap, _rExpandedBitmap, _eButtonKind);
406 // replace the text item with our own one
407 SvLBoxItem* pTextItem = _pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING);
408 OSL_ENSURE(pTextItem, "OTableTreeListBox::InitEntry: no text item!?");
409 size_t nTextPos = _pEntry->GetPos(pTextItem);
410 OSL_ENSURE(SvTreeListEntry::ITEM_NOT_FOUND != nTextPos, "OTableTreeListBox::InitEntry: no text item pos!");
412 _pEntry->ReplaceItem(o3tl::make_unique<OBoldListboxString>(_rString), nTextPos);
415 SvTreeListEntry* OTableTreeListBox::implAddEntry(
416 const Reference< XDatabaseMetaData >& _rxMeta,
417 const OUString& _rTableName,
418 bool _bCheckName
421 OSL_PRECOND( _rxMeta.is(), "OTableTreeListBox::implAddEntry: invalid meta data!" );
422 if ( !_rxMeta.is() )
423 return nullptr;
425 // split the complete name into its components
426 OUString sCatalog, sSchema, sName;
427 qualifiedNameComponents( _rxMeta, _rTableName, sCatalog, sSchema, sName, ::dbtools::EComposeRule::InDataManipulation );
429 SvTreeListEntry* pParentEntry = getAllObjectsEntry();
431 // if the DB uses catalog at the start of identifiers, then our hierarchy is
432 // catalog
433 // +- schema
434 // +- table
435 // else it is
436 // schema
437 // +- catalog
438 // +- table
439 bool bCatalogAtStart = _rxMeta->isCatalogAtStart();
440 const OUString& rFirstName = bCatalogAtStart ? sCatalog : sSchema;
441 const sal_Int32 nFirstFolderType = bCatalogAtStart ? DatabaseObjectContainer::CATALOG : DatabaseObjectContainer::SCHEMA;
442 const OUString& rSecondName = bCatalogAtStart ? sSchema : sCatalog;
443 const sal_Int32 nSecondFolderType = bCatalogAtStart ? DatabaseObjectContainer::SCHEMA : DatabaseObjectContainer::CATALOG;
445 if ( !rFirstName.isEmpty() )
447 SvTreeListEntry* pFolder = GetEntryPosByName( rFirstName, pParentEntry );
448 if ( !pFolder )
449 pFolder = InsertEntry( rFirstName, pParentEntry, false, TREELIST_APPEND, reinterpret_cast< void* >( nFirstFolderType ) );
450 pParentEntry = pFolder;
453 if ( !rSecondName.isEmpty() )
455 SvTreeListEntry* pFolder = GetEntryPosByName( rSecondName, pParentEntry );
456 if ( !pFolder )
457 pFolder = InsertEntry( rSecondName, pParentEntry, false, TREELIST_APPEND, reinterpret_cast< void* >( nSecondFolderType ) );
458 pParentEntry = pFolder;
461 SvTreeListEntry* pRet = nullptr;
462 if ( !_bCheckName || !GetEntryPosByName( sName, pParentEntry ) )
464 pRet = InsertEntry( sName, pParentEntry );
466 Image aImage;
467 m_xImageProvider->getImages( _rTableName, DatabaseObject::TABLE, aImage );
469 SetExpandedEntryBmp( pRet, aImage );
470 SetCollapsedEntryBmp( pRet, aImage );
472 return pRet;
475 NamedDatabaseObject OTableTreeListBox::describeObject( SvTreeListEntry* _pEntry )
477 NamedDatabaseObject aObject;
479 sal_Int32 nEntryType = reinterpret_cast< sal_IntPtr >( _pEntry->GetUserData() );
481 if ( nEntryType == DatabaseObjectContainer::TABLES )
483 aObject.Type = DatabaseObjectContainer::TABLES;
485 else if ( ( nEntryType == DatabaseObjectContainer::CATALOG )
486 || ( nEntryType == DatabaseObjectContainer::SCHEMA )
489 SvTreeListEntry* pParent = GetParent( _pEntry );
490 sal_Int32 nParentEntryType = pParent ? reinterpret_cast< sal_IntPtr >( pParent->GetUserData() ) : -1;
492 OUStringBuffer buffer;
493 if ( nEntryType == DatabaseObjectContainer::CATALOG )
495 if ( nParentEntryType == DatabaseObjectContainer::SCHEMA )
497 buffer.append( GetEntryText( pParent ) );
498 buffer.append( '.' );
500 buffer.append( GetEntryText( _pEntry ) );
502 else if ( nEntryType == DatabaseObjectContainer::SCHEMA )
504 if ( nParentEntryType == DatabaseObjectContainer::CATALOG )
506 buffer.append( GetEntryText( pParent ) );
507 buffer.append( '.' );
509 buffer.append( GetEntryText( _pEntry ) );
512 else
514 aObject.Type = DatabaseObject::TABLE;
515 aObject.Name = getQualifiedTableName( _pEntry );
518 return aObject;
521 SvTreeListEntry* OTableTreeListBox::addedTable( const OUString& _rName )
525 Reference< XDatabaseMetaData > xMeta;
526 if ( impl_getAndAssertMetaData( xMeta ) )
527 return implAddEntry( xMeta, _rName );
529 catch( const Exception& )
531 DBG_UNHANDLED_EXCEPTION();
533 return nullptr;
536 bool OTableTreeListBox::impl_getAndAssertMetaData( Reference< XDatabaseMetaData >& _out_rMetaData ) const
538 if ( m_xConnection.is() )
539 _out_rMetaData = m_xConnection->getMetaData();
540 OSL_PRECOND( _out_rMetaData.is(), "OTableTreeListBox::impl_getAndAssertMetaData: invalid current connection!" );
541 return _out_rMetaData.is();
544 OUString OTableTreeListBox::getQualifiedTableName( SvTreeListEntry* _pEntry ) const
546 OSL_PRECOND( !isFolderEntry( _pEntry ), "OTableTreeListBox::getQualifiedTableName: folder entries not allowed here!" );
550 Reference< XDatabaseMetaData > xMeta;
551 if ( !impl_getAndAssertMetaData( xMeta ) )
552 return OUString();
554 OUString sCatalog;
555 OUString sSchema;
556 OUString sTable;
558 SvTreeListEntry* pSchema = GetParent( _pEntry );
559 if ( pSchema )
561 SvTreeListEntry* pCatalog = GetParent( pSchema );
562 if ( pCatalog
563 || ( xMeta->supportsCatalogsInDataManipulation()
564 && !xMeta->supportsSchemasInDataManipulation()
565 ) // here we support catalog but no schema
568 if ( pCatalog == nullptr )
570 pCatalog = pSchema;
571 pSchema = nullptr;
573 sCatalog = GetEntryText( pCatalog );
575 if ( pSchema )
576 sSchema = GetEntryText(pSchema);
578 sTable = GetEntryText( _pEntry );
580 return ::dbtools::composeTableName( xMeta, sCatalog, sSchema, sTable, false, ::dbtools::EComposeRule::InDataManipulation );
582 catch( const Exception& )
584 DBG_UNHANDLED_EXCEPTION();
586 return OUString();
589 SvTreeListEntry* OTableTreeListBox::getEntryByQualifiedName( const OUString& _rName )
593 Reference< XDatabaseMetaData > xMeta;
594 if ( !impl_getAndAssertMetaData( xMeta ) )
595 return nullptr;
597 // split the complete name into its components
598 OUString sCatalog, sSchema, sName;
599 qualifiedNameComponents(xMeta, _rName, sCatalog, sSchema, sName,::dbtools::EComposeRule::InDataManipulation);
601 SvTreeListEntry* pParent = getAllObjectsEntry();
602 SvTreeListEntry* pCat = nullptr;
603 SvTreeListEntry* pSchema = nullptr;
604 if ( !sCatalog.isEmpty() )
606 pCat = GetEntryPosByName(sCatalog, pParent);
607 if ( pCat )
608 pParent = pCat;
611 if ( !sSchema.isEmpty() )
613 pSchema = GetEntryPosByName(sSchema, pParent);
614 if ( pSchema )
615 pParent = pSchema;
618 return GetEntryPosByName(sName, pParent);
620 catch( const Exception& )
622 DBG_UNHANDLED_EXCEPTION();
624 return nullptr;
627 void OTableTreeListBox::removedTable( const OUString& _rName )
631 SvTreeListEntry* pEntry = getEntryByQualifiedName( _rName );
632 if ( pEntry )
633 GetModel()->Remove( pEntry );
635 catch( const Exception& )
637 DBG_UNHANDLED_EXCEPTION();
641 } // namespace dbaui
643 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */