1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
;
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
)
105 void OTableTreeListBox::notifyHiContrastChanged()
107 implSetDefaultImages();
109 SvTreeListEntry
* pEntryLoop
= First();
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
);
121 if ( isFolderEntry( pEntryLoop
) )
123 aImage
= ImageProvider::getFolderImage( DatabaseObject::TABLE
);
127 OUString
sCompleteName( getQualifiedTableName( pEntryLoop
) );
128 m_xImageProvider
->getImages( sCompleteName
, DatabaseObject::TABLE
, aImage
);
131 rContextBitmapItem
.SetBitmap1( aImage
);
132 rContextBitmapItem
.SetBitmap2( aImage
);
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();
163 sViews
= xViews
->getElementNames();
166 xTables
= xTableSupp
->getTables();
168 sTables
= xTables
->getElementNames();
170 catch(RuntimeException
&)
172 OSL_FAIL("OTableTreeListBox::UpdateTableList : caught an RuntimeException!");
174 catch ( const SQLException
& )
180 // a non-SQLException exception occurred ... simply throw an SQLException
182 aInfo
.Message
= sCurrentActionError
;
186 UpdateTableList( _rxConnection
, sTables
, sViews
);
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
;
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
));
211 void OTableTreeListBox::UpdateTableList(
212 const Reference
< XConnection
>& _rxConnection
,
213 const Sequence
< OUString
>& _rTables
,
214 const Sequence
< OUString
>& _rViews
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() ) );
229 DBG_UNHANDLED_EXCEPTION();
231 UpdateTableList( _rxConnection
, aTables
);
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
) );
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
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
);
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)
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
)
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
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();
316 SvTreeListEntry
* pFolder
= GetEntryPosByName( *folder
, pRootEntry
);
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
)
333 OBoldListboxString
* pTextItem
= static_cast<OBoldListboxString
*>(_pEntry
->GetFirstItem(SV_ITEM_ID_BOLDLBSTRING
));
335 return pTextItem
->isEmphasized();
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
));
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
);
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
,
421 OSL_PRECOND( _rxMeta
.is(), "OTableTreeListBox::implAddEntry: invalid meta data!" );
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
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
);
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
);
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
);
467 m_xImageProvider
->getImages( _rTableName
, DatabaseObject::TABLE
, aImage
);
469 SetExpandedEntryBmp( pRet
, aImage
);
470 SetCollapsedEntryBmp( pRet
, aImage
);
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
) );
514 aObject
.Type
= DatabaseObject::TABLE
;
515 aObject
.Name
= getQualifiedTableName( _pEntry
);
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();
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
) )
558 SvTreeListEntry
* pSchema
= GetParent( _pEntry
);
561 SvTreeListEntry
* pCatalog
= GetParent( pSchema
);
563 || ( xMeta
->supportsCatalogsInDataManipulation()
564 && !xMeta
->supportsSchemasInDataManipulation()
565 ) // here we support catalog but no schema
568 if ( pCatalog
== nullptr )
573 sCatalog
= GetEntryText( pCatalog
);
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();
589 SvTreeListEntry
* OTableTreeListBox::getEntryByQualifiedName( const OUString
& _rName
)
593 Reference
< XDatabaseMetaData
> xMeta
;
594 if ( !impl_getAndAssertMetaData( xMeta
) )
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
);
611 if ( !sSchema
.isEmpty() )
613 pSchema
= GetEntryPosByName(sSchema
, pParent
);
618 return GetEntryPosByName(sName
, pParent
);
620 catch( const Exception
& )
622 DBG_UNHANDLED_EXCEPTION();
627 void OTableTreeListBox::removedTable( const OUString
& _rName
)
631 SvTreeListEntry
* pEntry
= getEntryByQualifiedName( _rName
);
633 GetModel()->Remove( pEntry
);
635 catch( const Exception
& )
637 DBG_UNHANDLED_EXCEPTION();
643 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */