/* -*- 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 "tablespage.hxx"
#include <dsitems.hxx>
#include <datasourceconnector.hxx>
#include <comphelper/types.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/dbexception.hxx>
#include <stringlistitem.hxx>
#include <svl/stritem.hxx>
#include <strings.hxx>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <sqlmessage.hxx>
#include <UITools.hxx>
#include <osl/diagnose.h>
#include <TablesSingleDlg.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <cppuhelper/exc_hlp.hxx>
namespace dbaui
{
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::util;
using namespace ::dbtools;
using namespace ::comphelper;
// OTableSubscriptionPage
OTableSubscriptionPage::OTableSubscriptionPage(weld::Container* pPage, OTableSubscriptionDialog* pTablesDlg, const SfxItemSet& _rCoreAttrs)
: OGenericAdministrationPage(pPage, pTablesDlg, u"dbaccess/ui/tablesfilterpage.ui"_ustr, u"TablesFilterPage"_ustr, _rCoreAttrs)
, m_bCatalogAtStart(true)
, m_pTablesDlg(pTablesDlg)
, m_xTables(m_xBuilder->weld_widget(u"TablesFilterPage"_ustr))
, m_xTablesList(new OTableTreeListBox(m_xBuilder->weld_tree_view(u"treeview"_ustr), true))
{
m_xTablesList->init();
weld::TreeView& rWidget = m_xTablesList->GetWidget();
rWidget.set_size_request(rWidget.get_approximate_digit_width() * 48,
rWidget.get_height_rows(12));
// initialize the TabListBox
rWidget.set_selection_mode(SelectionMode::Multiple);
rWidget.connect_toggled(LINK(this, OTableSubscriptionPage, OnTreeEntryChecked));
}
OTableSubscriptionPage::~OTableSubscriptionPage()
{
// just to make sure that our connection will be removed
try
{
::comphelper::disposeComponent(m_xCurrentConnection);
}
catch (RuntimeException&) { }
}
void OTableSubscriptionPage::implCheckTables(const Sequence< OUString >& _rTables)
{
// the meta data for the current connection, used for splitting up table names
Reference< XDatabaseMetaData > xMeta;
try
{
if (m_xCurrentConnection.is())
xMeta = m_xCurrentConnection->getMetaData();
}
catch(SQLException&)
{
OSL_FAIL("OTableSubscriptionPage::implCheckTables : could not retrieve the current connection's meta data!");
}
// uncheck all
CheckAll(false);
// check the ones which are in the list
OUString sCatalog, sSchema, sName;
std::unique_ptr<weld::TreeIter> xRootEntry(m_xTablesList->getAllObjectsEntry());
for (const OUString& rIncludeTable : _rTables)
{
if (xMeta.is())
qualifiedNameComponents(xMeta, rIncludeTable, sCatalog, sSchema, sName,::dbtools::EComposeRule::InDataManipulation);
else
sName = rIncludeTable;
bool bAllTables = (1 == sName.getLength()) && ('%' == sName[0]);
bool bAllSchemas = (1 == sSchema.getLength()) && ('%' == sSchema[0]);
// the catalog entry
std::unique_ptr<weld::TreeIter> xCatalog(m_xTablesList->GetEntryPosByName(sCatalog, xRootEntry.get()));
if (!(xCatalog || sCatalog.isEmpty()))
// the table (resp. its catalog) referred in this filter entry does not exist anymore
continue;
if (bAllSchemas && xCatalog)
{
m_xTablesList->checkWildcard(*xCatalog);
continue;
}
// the schema entry
std::unique_ptr<weld::TreeIter> xSchema = m_xTablesList->GetEntryPosByName(sSchema, (xCatalog ? xCatalog.get() : xRootEntry.get()));
if (!(xSchema || sSchema.isEmpty()))
// the table (resp. its schema) referred in this filter entry does not exist anymore
continue;
if (bAllTables && xSchema)
{
m_xTablesList->checkWildcard(*xSchema);
continue;
}
std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetEntryPosByName(sName, xSchema ? xSchema.get() : (xCatalog ? xCatalog.get() : xRootEntry.get())));
if (xEntry)
m_xTablesList->GetWidget().set_toggle(*xEntry, TRISTATE_TRUE);
}
m_xTablesList->CheckButtons();
}
void OTableSubscriptionPage::implCompleteTablesCheck( const css::uno::Sequence< OUString >& _rTableFilter )
{
if (!_rTableFilter.hasElements())
{ // no tables visible
CheckAll(false);
}
else
{
if ((1 == _rTableFilter.getLength()) && _rTableFilter[0] == "%")
{ // all tables visible
CheckAll();
}
else
implCheckTables( _rTableFilter );
}
}
void OTableSubscriptionPage::implInitControls(const SfxItemSet& _rSet, bool _bSaveValue)
{
// check whether or not the selection is invalid or readonly (invalid implies readonly, but not vice versa)
bool bValid, bReadonly;
getFlags(_rSet, bValid, bReadonly);
// get the name of the data source we're working for
const SfxStringItem* pNameItem = _rSet.GetItem<SfxStringItem>(DSID_NAME);
OSL_ENSURE(pNameItem, "OTableSubscriptionPage::implInitControls: missing the name attribute!");
OUString sDSName = pNameItem->GetValue();
if (bValid && !sDSName.isEmpty() && !m_xCurrentConnection.is() )
{ // get the current table list from the connection for the current settings
// the PropertyValues for the current dialog settings
Sequence< PropertyValue > aConnectionParams;
OSL_ENSURE(m_pTablesDlg, "OTableSubscriptionPage::implInitControls: need a parent dialog doing the translation!");
if ( m_pTablesDlg )
{
if (!m_pTablesDlg->getCurrentSettings(aConnectionParams))
{
m_xTablesList->GetWidget().clear();
m_pTablesDlg->endExecution();
return;
}
}
// fill the table list with this connection information
SQLExceptionInfo aErrorInfo;
try
{
weld::WaitObject aWaitCursor(GetFrameWeld());
Reference<XPropertySet> xProp = m_pTablesDlg->getCurrentDataSource();
OSL_ENSURE(xProp.is(),"No data source set!");
if ( xProp.is() )
{
Any aTableFilter = xProp->getPropertyValue(PROPERTY_TABLEFILTER);
Any aTableTypeFilter = xProp->getPropertyValue(PROPERTY_TABLETYPEFILTER);
Reference<XModifiable> xModi(getDataSourceOrModel(xProp),UNO_QUERY);
bool bModified = ( xModi.is() && xModi->isModified() );
Sequence< OUString > aNewTableFilter { u"%"_ustr };
xProp->setPropertyValue(PROPERTY_TABLEFILTER,Any(aNewTableFilter));
xProp->setPropertyValue( PROPERTY_TABLETYPEFILTER, Any( Sequence< OUString >() ) );
Reference< css::lang::XEventListener> xEvt;
aErrorInfo = ::dbaui::createConnection(xProp, m_xORB, xEvt, m_xCurrentConnection);
xProp->setPropertyValue(PROPERTY_TABLEFILTER,aTableFilter);
xProp->setPropertyValue(PROPERTY_TABLETYPEFILTER,aTableTypeFilter);
if ( xModi.is() && !bModified )
xModi->setModified(false);
}
if ( m_xCurrentConnection.is() )
{
m_xTablesList->UpdateTableList( m_xCurrentConnection );
if (m_pTablesDlg)
m_pTablesDlg->successfullyConnected();
}
}
catch (const SQLException&)
{
aErrorInfo = ::cppu::getCaughtException();
}
if (aErrorInfo.isValid())
{
// establishing the connection failed. Show an error window and exit.
OSQLMessageBox aMessageBox(GetFrameWeld(), aErrorInfo);
aMessageBox.run();
m_xTables->set_sensitive(false);
m_xTablesList->GetWidget().clear();
if ( m_pTablesDlg )
{
m_pTablesDlg->clearPassword();
m_pTablesDlg->endExecution();
}
}
else
{
// in addition, we need some infos about the connection used
m_sCatalogSeparator = "."; // (default)
m_bCatalogAtStart = true; // (default)
try
{
Reference< XDatabaseMetaData > xMeta;
if (m_xCurrentConnection.is())
xMeta = m_xCurrentConnection->getMetaData();
if (xMeta.is() && xMeta->supportsCatalogsInDataManipulation())
{
m_sCatalogSeparator = xMeta->getCatalogSeparator();
m_bCatalogAtStart = xMeta->isCatalogAtStart();
}
}
catch(Exception&)
{
DBG_UNHANDLED_EXCEPTION("dbaccess");
}
}
}
// get the current table filter
const OStringListItem* pTableFilter = _rSet.GetItem<OStringListItem>(DSID_TABLEFILTER);
Sequence< OUString > aTableFilter;
if (pTableFilter)
aTableFilter = pTableFilter->getList();
implCompleteTablesCheck( aTableFilter );
// expand the first entry by default
std::unique_ptr<weld::TreeIter> xExpand = m_xTablesList->getAllObjectsEntry();
while (xExpand)
{
m_xTablesList->GetWidget().expand_row(*xExpand);
if (!m_xTablesList->GetWidget().iter_children(*xExpand))
break;
std::unique_ptr<weld::TreeIter> xSibling(m_xTablesList->GetWidget().make_iterator(xExpand.get()));
if (m_xTablesList->GetWidget().iter_next_sibling(*xSibling))
xExpand.reset();
}
// update the toolbox according the current selection and check state
OGenericAdministrationPage::implInitControls(_rSet, _bSaveValue);
}
void OTableSubscriptionPage::CheckAll( bool _bCheck )
{
std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetWidget().make_iterator());
if (m_xTablesList->GetWidget().get_iter_first(*xEntry))
{
do
{
m_xTablesList->GetWidget().set_toggle(*xEntry, _bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
}
while (m_xTablesList->GetWidget().iter_next(*xEntry));
}
if (_bCheck)
{
auto xRoot = m_xTablesList->getAllObjectsEntry();
if (xRoot)
m_xTablesList->checkWildcard(*xRoot);
}
}
DeactivateRC OTableSubscriptionPage::DeactivatePage(SfxItemSet* _pSet)
{
DeactivateRC nResult = OGenericAdministrationPage::DeactivatePage(_pSet);
// dispose the connection, we don't need it anymore, so we're not wasting resources
try
{
::comphelper::disposeComponent(m_xCurrentConnection);
}
catch (RuntimeException&) { }
return nResult;
}
IMPL_LINK(OTableSubscriptionPage, OnTreeEntryChecked, const weld::TreeView::iter_col&, rRowCol, void)
{
m_xTablesList->checkedButton_noBroadcast(rRowCol.first);
callModifiedHdl();
}
Sequence< OUString > OTableSubscriptionPage::collectDetailedSelection() const
{
Sequence< OUString > aTableFilter;
static constexpr OUString sWildcard = u"%"_ustr;
std::unique_ptr<weld::TreeIter> xAllObjectsEntry(m_xTablesList->getAllObjectsEntry());
if (!xAllObjectsEntry)
return aTableFilter;
std::unique_ptr<weld::TreeIter> xEntry(m_xTablesList->GetWidget().make_iterator(xAllObjectsEntry.get()));
if (!m_xTablesList->GetWidget().iter_next(*xEntry))
xEntry.reset();
while (xEntry)
{
bool bCatalogWildcard = false;
bool bSchemaWildcard = false;
std::unique_ptr<weld::TreeIter> xSchema;
std::unique_ptr<weld::TreeIter> xCatalog;
if (m_xTablesList->GetWidget().get_toggle(*xEntry) == TRISTATE_TRUE && !m_xTablesList->GetWidget().iter_has_child(*xEntry))
{ // checked and a leaf, which means it's no catalog, no schema, but a real table
OUStringBuffer sComposedName;
OUString sCatalog;
if (m_xTablesList->GetWidget().get_iter_depth(*xEntry))
{
xSchema = m_xTablesList->GetWidget().make_iterator(xEntry.get());
m_xTablesList->GetWidget().iter_parent(*xSchema);
if (xAllObjectsEntry->equal(*xSchema))
{
// do not want to have the root entry
xSchema.reset();
}
if (xSchema)
{ // it's a real schema entry, not the "all objects" root
if (m_xTablesList->GetWidget().get_iter_depth(*xSchema))
{
xCatalog = m_xTablesList->GetWidget().make_iterator(xSchema.get());
m_xTablesList->GetWidget().iter_parent(*xCatalog);
if (xAllObjectsEntry->equal(*xCatalog))
{
// do not want to have the root entry
xCatalog.reset();
}
if (xCatalog)
{ // it's a real catalog entry, not the "all objects" root
bCatalogWildcard = m_xTablesList->isWildcardChecked(*xCatalog);
if (m_bCatalogAtStart)
{
sComposedName.append(m_xTablesList->GetWidget().get_text(*xCatalog) + m_sCatalogSeparator);
if (bCatalogWildcard)
sComposedName.append(sWildcard);
}
else
{
if (bCatalogWildcard)
sCatalog = sWildcard;
else
sCatalog.clear();
sCatalog += m_sCatalogSeparator + m_xTablesList->GetWidget().get_text(*xCatalog) ;
}
}
}
bSchemaWildcard = m_xTablesList->isWildcardChecked(*xSchema);
sComposedName.append(m_xTablesList->GetWidget().get_text(*xSchema) + ".");
}
if (bSchemaWildcard)
sComposedName.append(sWildcard);
}
if (!bSchemaWildcard && !bCatalogWildcard)
sComposedName.append(m_xTablesList->GetWidget().get_text(*xEntry));
if (!m_bCatalogAtStart && !bCatalogWildcard)
sComposedName.append(sCatalog);
// need some space
sal_Int32 nOldLen = aTableFilter.getLength();
aTableFilter.realloc(nOldLen + 1);
// add the new name
aTableFilter.getArray()[nOldLen] = sComposedName.makeStringAndClear();
}
if (bCatalogWildcard)
xEntry = implNextSibling(xCatalog.get());
else if (bSchemaWildcard)
xEntry = implNextSibling(xSchema.get());
else
{
if (!m_xTablesList->GetWidget().iter_next(*xEntry))
xEntry.reset();
}
}
return aTableFilter;
}
std::unique_ptr<weld::TreeIter> OTableSubscriptionPage::implNextSibling(const weld::TreeIter* pEntry) const
{
std::unique_ptr<weld::TreeIter> xReturn;
if (pEntry)
{
xReturn = m_xTablesList->GetWidget().make_iterator(pEntry);
if (!m_xTablesList->GetWidget().iter_next_sibling(*xReturn))
{
std::unique_ptr<weld::TreeIter> xParent = m_xTablesList->GetWidget().make_iterator(pEntry);
if (m_xTablesList->GetWidget().iter_parent(*xParent))
xReturn = implNextSibling(xParent.get());
else
xReturn.reset();
}
}
return xReturn;
}
bool OTableSubscriptionPage::FillItemSet( SfxItemSet* _rCoreAttrs )
{
bool bValid, bReadonly;
getFlags(*_rCoreAttrs, bValid, bReadonly);
if (!bValid || bReadonly)
// don't store anything if the data we're working with is invalid or readonly
return true;
// create the output string which contains all the table names
if ( m_xCurrentConnection.is() )
{ // collect the table filter data only if we have a connection - else no tables are displayed at all
Sequence< OUString > aTableFilter;
auto xRoot = m_xTablesList->getAllObjectsEntry();
if (xRoot && m_xTablesList->isWildcardChecked(*xRoot))
{
aTableFilter = { u"%"_ustr };
}
else
{
aTableFilter = collectDetailedSelection();
}
_rCoreAttrs->Put( OStringListItem(DSID_TABLEFILTER, aTableFilter) );
}
return true;
}
void OTableSubscriptionPage::fillControls(std::vector< std::unique_ptr<ISaveValueWrapper> >& /*_rControlList*/)
{
}
void OTableSubscriptionPage::fillWindows(std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList)
{
_rControlList.emplace_back(new ODisableWidgetWrapper<weld::Widget>(m_xTables.get()));
}
} // namespace dbaui
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V595 The 'm_pTablesDlg' pointer was utilized before it was verified against nullptr. Check lines: 194, 222.
↑ V1023 A pointer without owner is added to the '_rControlList' container by the 'emplace_back' method. A memory leak will occur in case of an exception.