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 <com/sun/star/container/XNameAccess.hpp>
21 #include <com/sun/star/sdbc/XConnection.hpp>
22 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
23 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
24 #include <com/sun/star/sdb/DatabaseContext.hpp>
25 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
26 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/string.hxx>
29 #include <com/sun/star/container/XContainerListener.hpp>
30 #include <cppuhelper/implbase.hxx>
31 #include <i18nlangtag/languagetag.hxx>
32 #include <osl/diagnose.h>
37 #include <vcl/settings.hxx>
38 #include <vcl/svapp.hxx>
40 #include <bitmaps.hlst>
43 using namespace ::com::sun::star
;
44 using namespace ::com::sun::star::uno
;
45 using namespace ::com::sun::star::container
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star::sdb
;
48 using namespace ::com::sun::star::sdbc
;
49 using namespace ::com::sun::star::sdbcx
;
50 using namespace ::com::sun::star::beans
;
52 class SwDBTreeList_Impl
: public cppu::WeakImplHelper
< XContainerListener
>
54 Reference
< XDatabaseContext
> m_xDatabaseContext
;
55 SwWrtShell
* m_pWrtShell
;
58 explicit SwDBTreeList_Impl()
59 : m_pWrtShell(nullptr)
62 virtual ~SwDBTreeList_Impl() override
;
64 virtual void SAL_CALL
elementInserted( const ContainerEvent
& Event
) override
;
65 virtual void SAL_CALL
elementRemoved( const ContainerEvent
& Event
) override
;
66 virtual void SAL_CALL
elementReplaced( const ContainerEvent
& Event
) override
;
67 virtual void SAL_CALL
disposing( const EventObject
& Source
) override
;
70 SwWrtShell
* GetWrtShell() { return m_pWrtShell
;}
71 void SetWrtShell(SwWrtShell
& rSh
) { m_pWrtShell
= &rSh
;}
72 const Reference
<XDatabaseContext
>& GetContext() const {return m_xDatabaseContext
;}
73 Reference
<XConnection
> GetConnection(const OUString
& rSourceName
);
76 SwDBTreeList_Impl::~SwDBTreeList_Impl()
78 if(m_xDatabaseContext
.is())
80 osl_atomic_increment(&m_refCount
);
81 //block necessary due to solaris' compiler behaviour to
82 //remove temporaries at the block's end
84 m_xDatabaseContext
->removeContainerListener( this );
86 osl_atomic_decrement(&m_refCount
);
90 void SwDBTreeList_Impl::elementInserted( const ContainerEvent
& )
92 // information not needed
95 void SwDBTreeList_Impl::elementRemoved( const ContainerEvent
& )
99 void SwDBTreeList_Impl::disposing( const EventObject
& )
101 m_xDatabaseContext
= nullptr;
104 void SwDBTreeList_Impl::elementReplaced( const ContainerEvent
& rEvent
)
106 elementRemoved(rEvent
);
109 bool SwDBTreeList_Impl::HasContext()
111 if(!m_xDatabaseContext
.is())
113 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
114 m_xDatabaseContext
= DatabaseContext::create(xContext
);
115 m_xDatabaseContext
->addContainerListener( this );
117 return m_xDatabaseContext
.is();
120 Reference
<XConnection
> SwDBTreeList_Impl::GetConnection(const OUString
& rSourceName
)
122 Reference
<XConnection
> xRet
;
123 if (m_xDatabaseContext
.is() && m_pWrtShell
)
125 xRet
= m_pWrtShell
->GetDBManager()->RegisterConnection(rSourceName
);
130 SwDBTreeList::SwDBTreeList(std::unique_ptr
<weld::TreeView
> xTreeView
)
131 : m_bInitialized(false)
132 , m_bShowColumns(false)
133 , m_pImpl(new SwDBTreeList_Impl
)
134 , m_xTreeView(std::move(xTreeView
))
135 , m_xScratchIter(m_xTreeView
->make_iterator())
137 m_xTreeView
->connect_expanding(LINK(this, SwDBTreeList
, RequestingChildrenHdl
));
140 SwDBTreeList::~SwDBTreeList()
144 void SwDBTreeList::InitTreeList()
146 if (!m_pImpl
->HasContext() && m_pImpl
->GetWrtShell())
149 Sequence
< OUString
> aDBNames
= m_pImpl
->GetContext()->getElementNames();
150 auto const sort
= comphelper::string::NaturalStringSorter(
151 comphelper::getProcessComponentContext(),
152 Application::GetSettings().GetUILanguageTag().getLocale());
153 auto [begin
, end
] = asNonConstRange(aDBNames
);
156 [&sort
](OUString
const & x
, OUString
const & y
)
157 { return sort
.compare(x
, y
) < 0; });
159 OUString
aImg(RID_BMP_DB
);
160 for (const OUString
& rDBName
: std::as_const(aDBNames
))
162 // If this database has a password or a (missing) remote connection,
163 // then it might take a long time or spam for unnecessary credentials.
164 // Just check that it basically exists to weed out any broken/obsolete registrations.
165 if (SwDBManager::getDataSourceAsParent(Reference
<sdbc::XConnection
>(), rDBName
).is())
167 m_xTreeView
->insert(nullptr, -1, &rDBName
, nullptr, nullptr, nullptr, true, m_xScratchIter
.get());
168 m_xTreeView
->set_image(*m_xScratchIter
, aImg
);
171 Select(u
"", u
"", u
"");
173 m_bInitialized
= true;
176 void SwDBTreeList::AddDataSource(const OUString
& rSource
)
178 m_xTreeView
->insert(nullptr, -1, &rSource
, nullptr, nullptr, nullptr, true, m_xScratchIter
.get());
179 m_xTreeView
->set_image(*m_xScratchIter
, RID_BMP_DB
);
180 m_xTreeView
->select(*m_xScratchIter
);
183 IMPL_LINK(SwDBTreeList
, RequestingChildrenHdl
, const weld::TreeIter
&, rParent
, bool)
185 if (!m_xTreeView
->iter_has_child(rParent
))
187 if (m_xTreeView
->get_iter_depth(rParent
)) // column names
191 std::unique_ptr
<weld::TreeIter
> xGrandParent(m_xTreeView
->make_iterator(&rParent
));
192 m_xTreeView
->iter_parent(*xGrandParent
);
193 OUString sSourceName
= m_xTreeView
->get_text(*xGrandParent
);
194 OUString sTableName
= m_xTreeView
->get_text(rParent
);
196 if(!m_pImpl
->GetContext()->hasByName(sSourceName
))
198 Reference
<XConnection
> xConnection
= m_pImpl
->GetConnection(sSourceName
);
199 bool bTable
= m_xTreeView
->get_id(rParent
).isEmpty();
200 Reference
<XColumnsSupplier
> xColsSupplier
;
203 Reference
<XTablesSupplier
> xTSupplier(xConnection
, UNO_QUERY
);
206 Reference
<XNameAccess
> xTables
= xTSupplier
->getTables();
207 OSL_ENSURE(xTables
->hasByName(sTableName
), "table not available anymore?");
210 Any aTable
= xTables
->getByName(sTableName
);
211 Reference
<XPropertySet
> xPropSet
;
213 xColsSupplier
.set(xPropSet
, UNO_QUERY
);
215 catch (const Exception
&)
222 Reference
<XQueriesSupplier
> xQSupplier(xConnection
, UNO_QUERY
);
225 Reference
<XNameAccess
> xQueries
= xQSupplier
->getQueries();
226 OSL_ENSURE(xQueries
->hasByName(sTableName
), "table not available anymore?");
229 Any aQuery
= xQueries
->getByName(sTableName
);
230 Reference
<XPropertySet
> xPropSet
;
232 xColsSupplier
.set(xPropSet
, UNO_QUERY
);
234 catch (const Exception
&)
240 if(xColsSupplier
.is())
242 Reference
<XNameAccess
> xCols
= xColsSupplier
->getColumns();
243 const Sequence
< OUString
> aColNames
= xCols
->getElementNames();
244 for (const OUString
& rColName
: aColNames
)
246 m_xTreeView
->append(&rParent
, rColName
);
250 catch (const Exception
&)
258 OUString sSourceName
= m_xTreeView
->get_text(rParent
);
259 if (!m_pImpl
->GetContext()->hasByName(sSourceName
))
261 Reference
<XConnection
> xConnection
= m_pImpl
->GetConnection(sSourceName
);
262 if (xConnection
.is())
264 Reference
<XTablesSupplier
> xTSupplier(xConnection
, UNO_QUERY
);
267 Reference
<XNameAccess
> xTables
= xTSupplier
->getTables();
268 const Sequence
< OUString
> aTableNames
= xTables
->getElementNames();
269 OUString
aImg(RID_BMP_DBTABLE
);
270 for (const OUString
& rTableName
: aTableNames
)
272 m_xTreeView
->insert(&rParent
, -1, &rTableName
, nullptr,
273 nullptr, nullptr, m_bShowColumns
, m_xScratchIter
.get());
274 m_xTreeView
->set_image(*m_xScratchIter
, aImg
);
278 Reference
<XQueriesSupplier
> xQSupplier(xConnection
, UNO_QUERY
);
281 Reference
<XNameAccess
> xQueries
= xQSupplier
->getQueries();
282 const Sequence
< OUString
> aQueryNames
= xQueries
->getElementNames();
283 OUString
aImg(RID_BMP_DBQUERY
);
284 for (const OUString
& rQueryName
: aQueryNames
)
286 //to discriminate between queries and tables the user data of query entries is set
287 OUString
sId(OUString::number(1));
288 m_xTreeView
->insert(&rParent
, -1, &rQueryName
, &sId
,
289 nullptr, nullptr, m_bShowColumns
, m_xScratchIter
.get());
290 m_xTreeView
->set_image(*m_xScratchIter
, aImg
);
295 catch (const Exception
&)
303 OUString
SwDBTreeList::GetDBName(OUString
& rTableName
, OUString
& rColumnName
, sal_Bool
* pbIsTable
)
306 std::unique_ptr
<weld::TreeIter
> xIter(m_xTreeView
->make_iterator());
307 if (m_xTreeView
->get_selected(xIter
.get()))
309 if (m_xTreeView
->get_iter_depth(*xIter
) == 2)
311 rColumnName
= m_xTreeView
->get_text(*xIter
);
312 m_xTreeView
->iter_parent(*xIter
); // column name was selected
314 if (m_xTreeView
->get_iter_depth(*xIter
) == 1)
317 *pbIsTable
= m_xTreeView
->get_id(*xIter
).isEmpty();
318 rTableName
= m_xTreeView
->get_text(*xIter
);
319 m_xTreeView
->iter_parent(*xIter
);
321 sDBName
= m_xTreeView
->get_text(*xIter
);
326 // Format: database.table
327 void SwDBTreeList::Select(std::u16string_view rDBName
, std::u16string_view rTableName
, std::u16string_view rColumnName
)
329 std::unique_ptr
<weld::TreeIter
> xParent(m_xTreeView
->make_iterator());
330 if (!m_xTreeView
->get_iter_first(*xParent
))
335 if (rDBName
== m_xTreeView
->get_text(*xParent
))
337 if (rTableName
.empty() && rColumnName
.empty())
339 // Just select the database node, do not expand
340 m_xTreeView
->scroll_to_row(*xParent
);
341 m_xTreeView
->select(*xParent
);
344 if (!m_xTreeView
->iter_has_child(*xParent
))
346 m_xTreeView
->set_children_on_demand(*xParent
, false); // tdf#142294 drop placeholder on-demand node
347 RequestingChildrenHdl(*xParent
);
348 // If successful, it will be expanded in a call to scroll_to_row for its children
350 std::unique_ptr
<weld::TreeIter
> xChild(m_xTreeView
->make_iterator(xParent
.get()));
351 if (!m_xTreeView
->iter_children(*xChild
))
353 m_xTreeView
->scroll_to_row(*xParent
);
354 m_xTreeView
->select(*xParent
);
359 if (rTableName
== m_xTreeView
->get_text(*xChild
))
361 m_xTreeView
->copy_iterator(*xChild
, *xParent
);
363 bool bNoChild
= false;
364 if (m_bShowColumns
&& !rColumnName
.empty())
366 if (!m_xTreeView
->iter_has_child(*xParent
))
368 m_xTreeView
->set_children_on_demand(*xParent
, false); // tdf#142294 drop placeholder on-demand node
369 RequestingChildrenHdl(*xParent
);
370 m_xTreeView
->expand_row(*xParent
);
374 if (m_xTreeView
->iter_children(*xChild
))
378 if (rColumnName
== m_xTreeView
->get_text(*xChild
))
384 while (m_xTreeView
->iter_next_sibling(*xChild
));
389 m_xTreeView
->copy_iterator(*xParent
, *xChild
);
391 m_xTreeView
->scroll_to_row(*xChild
);
392 m_xTreeView
->select(*xChild
);
396 while (m_xTreeView
->iter_next_sibling(*xChild
));
398 } while (m_xTreeView
->iter_next_sibling(*xParent
));
401 void SwDBTreeList::SetWrtShell(SwWrtShell
& rSh
)
403 m_pImpl
->SetWrtShell(rSh
);
404 if (m_xTreeView
->get_visible() && !m_bInitialized
)
410 void GotoRootLevelParent(const weld::TreeView
& rTreeView
, weld::TreeIter
& rEntry
)
412 while (rTreeView
.get_iter_depth(rEntry
))
413 rTreeView
.iter_parent(rEntry
);
417 void SwDBTreeList::ShowColumns(bool bShowCol
)
419 if (bShowCol
== m_bShowColumns
)
422 m_bShowColumns
= bShowCol
;
424 OUString sColumnName
;
425 const OUString
sDBName(GetDBName(sTableName
, sColumnName
));
427 m_xTreeView
->freeze();
429 std::unique_ptr
<weld::TreeIter
> xIter(m_xTreeView
->make_iterator());
430 std::unique_ptr
<weld::TreeIter
> xChild(m_xTreeView
->make_iterator());
431 if (m_xTreeView
->get_iter_first(*xIter
))
435 GotoRootLevelParent(*m_xTreeView
, *xIter
);
436 m_xTreeView
->collapse_row(*xIter
);
437 while (m_xTreeView
->iter_has_child(*xIter
))
439 m_xTreeView
->copy_iterator(*xIter
, *xChild
);
440 (void)m_xTreeView
->iter_children(*xChild
);
441 m_xTreeView
->remove(*xChild
);
443 } while (m_xTreeView
->iter_next(*xIter
));
448 if (!sDBName
.isEmpty())
450 Select(sDBName
, sTableName
, sColumnName
); // force RequestingChildren
454 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */