diff options
Diffstat (limited to 'sw/source/uibase/dbui/dbtree.cxx')
-rw-r--r-- | sw/source/uibase/dbui/dbtree.cxx | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/sw/source/uibase/dbui/dbtree.cxx b/sw/source/uibase/dbui/dbtree.cxx new file mode 100644 index 000000000..432a0a28e --- /dev/null +++ b/sw/source/uibase/dbui/dbtree.cxx @@ -0,0 +1,454 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <com/sun/star/container/XContainerListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <osl/diagnose.h> + +#include <dbmgr.hxx> +#include <wrtsh.hxx> +#include <dbtree.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <bitmaps.hlst> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; + +class SwDBTreeList_Impl : public cppu::WeakImplHelper < XContainerListener > +{ + Reference< XDatabaseContext > m_xDatabaseContext; + SwWrtShell* m_pWrtShell; + + public: + explicit SwDBTreeList_Impl() + : m_pWrtShell(nullptr) + { + } + virtual ~SwDBTreeList_Impl() override; + + virtual void SAL_CALL elementInserted( const ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& Event ) override; + virtual void SAL_CALL disposing( const EventObject& Source ) override; + + bool HasContext(); + SwWrtShell* GetWrtShell() { return m_pWrtShell;} + void SetWrtShell(SwWrtShell& rSh) { m_pWrtShell = &rSh;} + const Reference<XDatabaseContext>& GetContext() const {return m_xDatabaseContext;} + Reference<XConnection> GetConnection(const OUString& rSourceName); +}; + +SwDBTreeList_Impl::~SwDBTreeList_Impl() +{ + if(m_xDatabaseContext.is()) + { + osl_atomic_increment(&m_refCount); + //block necessary due to solaris' compiler behaviour to + //remove temporaries at the block's end + { + m_xDatabaseContext->removeContainerListener( this ); + } + osl_atomic_decrement(&m_refCount); + } +} + +void SwDBTreeList_Impl::elementInserted( const ContainerEvent& ) +{ + // information not needed +} + +void SwDBTreeList_Impl::elementRemoved( const ContainerEvent& ) +{ +} + +void SwDBTreeList_Impl::disposing( const EventObject& ) +{ + m_xDatabaseContext = nullptr; +} + +void SwDBTreeList_Impl::elementReplaced( const ContainerEvent& rEvent ) +{ + elementRemoved(rEvent); +} + +bool SwDBTreeList_Impl::HasContext() +{ + if(!m_xDatabaseContext.is()) + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + m_xDatabaseContext = DatabaseContext::create(xContext); + m_xDatabaseContext->addContainerListener( this ); + } + return m_xDatabaseContext.is(); +} + +Reference<XConnection> SwDBTreeList_Impl::GetConnection(const OUString& rSourceName) +{ + Reference<XConnection> xRet; + if (m_xDatabaseContext.is() && m_pWrtShell) + { + xRet = m_pWrtShell->GetDBManager()->RegisterConnection(rSourceName); + } + return xRet; +} + +SwDBTreeList::SwDBTreeList(std::unique_ptr<weld::TreeView> xTreeView) + : m_bInitialized(false) + , m_bShowColumns(false) + , m_pImpl(new SwDBTreeList_Impl) + , m_xTreeView(std::move(xTreeView)) + , m_xScratchIter(m_xTreeView->make_iterator()) +{ + m_xTreeView->connect_expanding(LINK(this, SwDBTreeList, RequestingChildrenHdl)); +} + +SwDBTreeList::~SwDBTreeList() +{ +} + +void SwDBTreeList::InitTreeList() +{ + if (!m_pImpl->HasContext() && m_pImpl->GetWrtShell()) + return; + + Sequence< OUString > aDBNames = m_pImpl->GetContext()->getElementNames(); + auto const sort = comphelper::string::NaturalStringSorter( + comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale()); + auto [begin, end] = asNonConstRange(aDBNames); + std::sort( + begin, end, + [&sort](OUString const & x, OUString const & y) + { return sort.compare(x, y) < 0; }); + + OUString aImg(RID_BMP_DB); + for (const OUString& rDBName : std::as_const(aDBNames)) + { + // If this database has a password or a (missing) remote connection, + // then it might take a long time or spam for unnecessary credentials. + // Just check that it basically exists to weed out any broken/obsolete registrations. + if (SwDBManager::getDataSourceAsParent(Reference<sdbc::XConnection>(), rDBName).is()) + { + m_xTreeView->insert(nullptr, -1, &rDBName, nullptr, nullptr, nullptr, true, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, aImg); + } + } + Select(u"", u"", u""); + + m_bInitialized = true; +} + +void SwDBTreeList::AddDataSource(const OUString& rSource) +{ + m_xTreeView->insert(nullptr, -1, &rSource, nullptr, nullptr, nullptr, true, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, RID_BMP_DB); + m_xTreeView->select(*m_xScratchIter); +} + +IMPL_LINK(SwDBTreeList, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool) +{ + if (!m_xTreeView->iter_has_child(rParent)) + { + if (m_xTreeView->get_iter_depth(rParent)) // column names + { + try + { + std::unique_ptr<weld::TreeIter> xGrandParent(m_xTreeView->make_iterator(&rParent)); + m_xTreeView->iter_parent(*xGrandParent); + OUString sSourceName = m_xTreeView->get_text(*xGrandParent); + OUString sTableName = m_xTreeView->get_text(rParent); + + if(!m_pImpl->GetContext()->hasByName(sSourceName)) + return true; + Reference<XConnection> xConnection = m_pImpl->GetConnection(sSourceName); + bool bTable = m_xTreeView->get_id(rParent).isEmpty(); + Reference<XColumnsSupplier> xColsSupplier; + if(bTable) + { + Reference<XTablesSupplier> xTSupplier(xConnection, UNO_QUERY); + if(xTSupplier.is()) + { + Reference<XNameAccess> xTables = xTSupplier->getTables(); + OSL_ENSURE(xTables->hasByName(sTableName), "table not available anymore?"); + try + { + Any aTable = xTables->getByName(sTableName); + Reference<XPropertySet> xPropSet; + aTable >>= xPropSet; + xColsSupplier.set(xPropSet, UNO_QUERY); + } + catch (const Exception&) + { + } + } + } + else + { + Reference<XQueriesSupplier> xQSupplier(xConnection, UNO_QUERY); + if(xQSupplier.is()) + { + Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + OSL_ENSURE(xQueries->hasByName(sTableName), "table not available anymore?"); + try + { + Any aQuery = xQueries->getByName(sTableName); + Reference<XPropertySet> xPropSet; + aQuery >>= xPropSet; + xColsSupplier.set(xPropSet, UNO_QUERY); + } + catch (const Exception&) + { + } + } + } + + if(xColsSupplier.is()) + { + Reference <XNameAccess> xCols = xColsSupplier->getColumns(); + const Sequence< OUString> aColNames = xCols->getElementNames(); + for (const OUString& rColName : aColNames) + { + m_xTreeView->append(&rParent, rColName); + } + } + } + catch (const Exception&) + { + } + } + else // table names + { + try + { + OUString sSourceName = m_xTreeView->get_text(rParent); + if (!m_pImpl->GetContext()->hasByName(sSourceName)) + return true; + Reference<XConnection> xConnection = m_pImpl->GetConnection(sSourceName); + if (xConnection.is()) + { + Reference<XTablesSupplier> xTSupplier(xConnection, UNO_QUERY); + if(xTSupplier.is()) + { + Reference<XNameAccess> xTables = xTSupplier->getTables(); + const Sequence< OUString> aTableNames = xTables->getElementNames(); + OUString aImg(RID_BMP_DBTABLE); + for (const OUString& rTableName : aTableNames) + { + m_xTreeView->insert(&rParent, -1, &rTableName, nullptr, + nullptr, nullptr, m_bShowColumns, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, aImg); + } + } + + Reference<XQueriesSupplier> xQSupplier(xConnection, UNO_QUERY); + if(xQSupplier.is()) + { + Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + const Sequence< OUString> aQueryNames = xQueries->getElementNames(); + OUString aImg(RID_BMP_DBQUERY); + for (const OUString& rQueryName : aQueryNames) + { + //to discriminate between queries and tables the user data of query entries is set + OUString sId(OUString::number(1)); + m_xTreeView->insert(&rParent, -1, &rQueryName, &sId, + nullptr, nullptr, m_bShowColumns, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, aImg); + } + } + } + } + catch (const Exception&) + { + } + } + } + return true; +} + +OUString SwDBTreeList::GetDBName(OUString& rTableName, OUString& rColumnName, sal_Bool* pbIsTable) +{ + OUString sDBName; + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xIter.get())) + { + if (m_xTreeView->get_iter_depth(*xIter) == 2) + { + rColumnName = m_xTreeView->get_text(*xIter); + m_xTreeView->iter_parent(*xIter); // column name was selected + } + if (m_xTreeView->get_iter_depth(*xIter) == 1) + { + if (pbIsTable) + *pbIsTable = m_xTreeView->get_id(*xIter).isEmpty(); + rTableName = m_xTreeView->get_text(*xIter); + m_xTreeView->iter_parent(*xIter); + } + sDBName = m_xTreeView->get_text(*xIter); + } + return sDBName; +} + +// Format: database.table +void SwDBTreeList::Select(std::u16string_view rDBName, std::u16string_view rTableName, std::u16string_view rColumnName) +{ + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xParent)) + return; + + do + { + if (rDBName == m_xTreeView->get_text(*xParent)) + { + if (rTableName.empty() && rColumnName.empty()) + { + // Just select the database node, do not expand + m_xTreeView->scroll_to_row(*xParent); + m_xTreeView->select(*xParent); + return; + } + if (!m_xTreeView->iter_has_child(*xParent)) + { + m_xTreeView->set_children_on_demand(*xParent, false); // tdf#142294 drop placeholder on-demand node + RequestingChildrenHdl(*xParent); + // If successful, it will be expanded in a call to scroll_to_row for its children + } + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xParent.get())); + if (!m_xTreeView->iter_children(*xChild)) + { + m_xTreeView->scroll_to_row(*xParent); + m_xTreeView->select(*xParent); + continue; + } + do + { + if (rTableName == m_xTreeView->get_text(*xChild)) + { + m_xTreeView->copy_iterator(*xChild, *xParent); + + bool bNoChild = false; + if (m_bShowColumns && !rColumnName.empty()) + { + if (!m_xTreeView->iter_has_child(*xParent)) + { + m_xTreeView->set_children_on_demand(*xParent, false); // tdf#142294 drop placeholder on-demand node + RequestingChildrenHdl(*xParent); + m_xTreeView->expand_row(*xParent); + } + + bNoChild = true; + if (m_xTreeView->iter_children(*xChild)) + { + do + { + if (rColumnName == m_xTreeView->get_text(*xChild)) + { + bNoChild = false; + break; + } + } + while (m_xTreeView->iter_next_sibling(*xChild)); + } + } + + if (bNoChild) + m_xTreeView->copy_iterator(*xParent, *xChild); + + m_xTreeView->scroll_to_row(*xChild); + m_xTreeView->select(*xChild); + return; + } + } + while (m_xTreeView->iter_next_sibling(*xChild)); + } + } while (m_xTreeView->iter_next_sibling(*xParent)); +} + +void SwDBTreeList::SetWrtShell(SwWrtShell& rSh) +{ + m_pImpl->SetWrtShell(rSh); + if (m_xTreeView->get_visible() && !m_bInitialized) + InitTreeList(); +} + +namespace +{ + void GotoRootLevelParent(const weld::TreeView& rTreeView, weld::TreeIter& rEntry) + { + while (rTreeView.get_iter_depth(rEntry)) + rTreeView.iter_parent(rEntry); + } +} + +void SwDBTreeList::ShowColumns(bool bShowCol) +{ + if (bShowCol == m_bShowColumns) + return; + + m_bShowColumns = bShowCol; + OUString sTableName; + OUString sColumnName; + const OUString sDBName(GetDBName(sTableName, sColumnName)); + + m_xTreeView->freeze(); + + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator()); + if (m_xTreeView->get_iter_first(*xIter)) + { + do + { + GotoRootLevelParent(*m_xTreeView, *xIter); + m_xTreeView->collapse_row(*xIter); + while (m_xTreeView->iter_has_child(*xIter)) + { + m_xTreeView->copy_iterator(*xIter, *xChild); + (void)m_xTreeView->iter_children(*xChild); + m_xTreeView->remove(*xChild); + } + } while (m_xTreeView->iter_next(*xIter)); + } + + m_xTreeView->thaw(); + + if (!sDBName.isEmpty()) + { + Select(sDBName, sTableName, sColumnName); // force RequestingChildren + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |