/* -*- 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 <browserids.hxx>
#include <core_resource.hxx>
#include <helpids.h>
#include <dbtreelistbox.hxx>
#include "dbtreemodel.hxx"
#include <strings.hrc>
#include <imageprovider.hxx>
#include <sbagrid.hxx>
#include <strings.hxx>
#include <UITools.hxx>
#include <unodatbr.hxx>
 
#include <com/sun/star/awt/MouseWheelBehavior.hpp>
#include <com/sun/star/awt/TextAlign.hpp>
#include <com/sun/star/awt/VisualEffect.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/form/XGridColumnFactory.hpp>
#include <com/sun/star/form/XLoadable.hpp>
#include <com/sun/star/form/XReset.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/frame/XLayoutManager.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/i18n/Collator.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>
#include <com/sun/star/sdb/XDatabaseContext.hpp>
#include <com/sun/star/sdb/XDatabaseRegistrations.hpp>
#include <com/sun/star/sdb/XDocumentDataSource.hpp>
#include <com/sun/star/sdb/XParametersSupplier.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp>
#include <com/sun/star/sdb/XResultSetAccess.hpp>
#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
#include <com/sun/star/sdb/application/NamedDatabaseObject.hpp>
#include <com/sun/star/sdbc/ColumnValue.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/FetchDirection.hpp>
#include <com/sun/star/sdbc/SQLWarning.hpp>
#include <com/sun/star/sdbc/XDataSource.hpp>
#include <com/sun/star/sdbc/XWarningsSupplier.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdbcx/XViewsSupplier.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/util/XFlushable.hpp>
#include <com/sun/star/util/XNumberFormatter.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/document/MacroExecMode.hpp>
#include <com/sun/star/ui/XContextMenuInterceptor.hpp>
 
#include <comphelper/extract.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/types.hxx>
#include <connectivity/dbexception.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <svl/filenotation.hxx>
#include <svx/dataaccessdescriptor.hxx>
#include <svx/databaseregistrationui.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <tools/multisel.hxx>
#include <tools/urlobj.hxx>
#include <unotools/confignode.hxx>
#include <utility>
#include <vcl/split.hxx>
#include <vcl/svapp.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/settings.hxx>
#include <vcl/weld.hxx>
 
#include <memory>
 
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdb::application;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::ui::dialogs;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::form;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::view;
using namespace ::com::sun::star::datatransfer;
using namespace ::com::sun::star::document;
using namespace ::com::sun::star::ui;
using namespace ::dbtools;
using namespace ::comphelper;
using namespace ::svx;
 
// SbaTableQueryBrowser
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbu_ODatasourceBrowser_get_implementation(
    css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
{
    SolarMutexGuard aGuard;
    return cppu::acquire(new ::dbaui::SbaTableQueryBrowser(context));
}
 
namespace dbaui
{
 
namespace DatabaseObject = css::sdb::application::DatabaseObject;
namespace DatabaseObjectContainer = css::sdb::application::DatabaseObjectContainer;
 
static void SafeAddPropertyListener(const Reference< XPropertySet > & xSet, const OUString& rPropName, XPropertyChangeListener* pListener)
{
    Reference< XPropertySetInfo >  xInfo = xSet->getPropertySetInfo();
    if (xInfo->hasPropertyByName(rPropName))
        xSet->addPropertyChangeListener(rPropName, pListener);
}
 
static void SafeRemovePropertyListener(const Reference< XPropertySet > & xSet, const OUString& rPropName, XPropertyChangeListener* pListener)
{
    Reference< XPropertySetInfo >  xInfo = xSet->getPropertySetInfo();
    if (xInfo->hasPropertyByName(rPropName))
        xSet->removePropertyChangeListener(rPropName, pListener);
}
 
OUString SAL_CALL SbaTableQueryBrowser::getImplementationName()
{
    return u"org.openoffice.comp.dbu.ODatasourceBrowser"_ustr;
}
 
css::uno::Sequence<OUString> SAL_CALL SbaTableQueryBrowser::getSupportedServiceNames()
{
    return { u"com.sun.star.sdb.DataSourceBrowser"_ustr };
}
 
SbaTableQueryBrowser::SbaTableQueryBrowser(const Reference< XComponentContext >& _rM)
    :SbaXDataBrowserController(_rM)
    ,m_aSelectionListeners( getMutex() )
    ,m_aContextMenuInterceptors( getMutex() )
    ,m_aTableCopyHelper(this)
    ,m_pTreeView(nullptr)
    ,m_pSplitter(nullptr)
    ,m_nAsyncDrop(nullptr)
    ,m_bQueryEscapeProcessing( false )
    ,m_bShowMenu(false)
    ,m_bInSuspend(false)
    ,m_bEnableBrowser(true)
{
}
 
SbaTableQueryBrowser::~SbaTableQueryBrowser()
{
    if ( !rBHelper.bDisposed && !rBHelper.bInDispose )
    {
        SAL_WARN("dbaccess.ui", "Please check who doesn't dispose this component!");
        // increment ref count to prevent double call of Dtor
        osl_atomic_increment( &m_refCount );
        dispose();
    }
    SolarMutexGuard g;
    m_pTreeView.reset();
    m_pSplitter.reset();
}
 
Any SAL_CALL SbaTableQueryBrowser::queryInterface(const Type& _rType)
{
    if ( _rType.equals( cppu::UnoType<XScriptInvocationContext>::get() ) )
    {
        OSL_PRECOND( m_aDocScriptSupport.has_value(), "SbaTableQueryBrowser::queryInterface: did not initialize this, yet!" );
        if ( m_aDocScriptSupport.has_value() && *m_aDocScriptSupport )
            return Any( Reference< XScriptInvocationContext >( this ) );
        return Any();
    }
 
    Any aReturn = SbaXDataBrowserController::queryInterface(_rType);
    if (!aReturn.hasValue())
        aReturn = SbaTableQueryBrowser_Base::queryInterface(_rType);
    return aReturn;
}
 
Sequence< Type > SAL_CALL SbaTableQueryBrowser::getTypes(  )
{
    Sequence< Type > aTypes( ::comphelper::concatSequences(
        SbaXDataBrowserController::getTypes(),
        SbaTableQueryBrowser_Base::getTypes()
    ) );
 
    OSL_PRECOND( m_aDocScriptSupport.has_value(), "SbaTableQueryBrowser::getTypes: did not initialize this, yet!" );
    if ( !m_aDocScriptSupport.has_value() || !*m_aDocScriptSupport )
    {
        auto [begin, end] = asNonConstRange(aTypes);
        auto newEnd = std::remove_if( begin, end,
                                      [](const Type& type)
                                      { return type == cppu::UnoType<XScriptInvocationContext>::get(); } );
        aTypes.realloc( std::distance(begin, newEnd) );
    }
    return aTypes;
}
 
Sequence< sal_Int8 > SAL_CALL SbaTableQueryBrowser::getImplementationId(  )
{
    return css::uno::Sequence<sal_Int8>();
}
 
void SAL_CALL SbaTableQueryBrowser::disposing()
{
    SolarMutexGuard aGuard;
        // doin' a lot of VCL stuff here -> lock the SolarMutex
 
    // kiss our listeners goodbye
    css::lang::EventObject aEvt(*this);
    m_aSelectionListeners.disposeAndClear(aEvt);
    m_aContextMenuInterceptors.disposeAndClear(aEvt);
 
    if (getBrowserView())
    {
        // Need to do some cleanup of the data pointed to the tree view entries before we remove the treeview
        clearTreeModel();
        m_pTreeView = nullptr;
        getBrowserView()->setTreeView(nullptr);
    }
 
    // remove ourself as status listener
    implRemoveStatusListeners();
 
    // check out from all the objects we are listening
    // the frame
    if (m_xCurrentFrameParent.is())
        m_xCurrentFrameParent->removeFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this));
 
    // remove the container listener from the database context
    try
    {
        Reference< XDatabaseRegistrations > xDatabaseRegistrations( m_xDatabaseContext, UNO_QUERY_THROW );
        xDatabaseRegistrations->removeDatabaseRegistrationsListener( this );
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    SbaXDataBrowserController::disposing();
}
 
bool SbaTableQueryBrowser::Construct(vcl::Window* pParent)
{
    if ( !SbaXDataBrowserController::Construct( pParent ) )
        return false;
 
    try
    {
        Reference< XDatabaseRegistrations > xDatabaseRegistrations( m_xDatabaseContext, UNO_QUERY_THROW );
        xDatabaseRegistrations->addDatabaseRegistrationsListener( this );
 
        // the collator for the string compares
        m_xCollator = Collator::create( getORB() );
        m_xCollator->loadDefaultCollator( Application::GetSettings().GetLanguageTag().getLocale(), 0 );
    }
    catch(const Exception&)
    {
        SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::Construct: could not create (or start listening at) the database context!");
    }
 
    // some help ids
    if (!getBrowserView() || !getBrowserView()->getVclControl())
        return true;
 
    // create controls and set sizes
    const tools::Long  nFrameWidth = getBrowserView()->LogicToPixel(::Size(3, 0), MapMode(MapUnit::MapAppFont)).Width();
 
    m_pSplitter = VclPtr<Splitter>::Create(getBrowserView(),WB_HSCROLL);
    m_pSplitter->SetPosSizePixel( ::Point(0,0), ::Size(nFrameWidth,0) );
    m_pSplitter->SetBackground( Wallpaper( Application::GetSettings().GetStyleSettings().GetDialogColor() ) );
 
    m_pTreeView = VclPtr<InterimDBTreeListBox>::Create(getBrowserView());
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    rTreeView.connect_expanding(LINK(this, SbaTableQueryBrowser, OnExpandEntry));
 
    m_pTreeView->setCopyHandler(LINK(this, SbaTableQueryBrowser, OnCopyEntry));
 
    m_pTreeView->setContextMenuProvider( this );
    m_pTreeView->setControlActionListener( this );
    m_pTreeView->SetHelpId(HID_CTL_TREEVIEW);
 
    // a default pos for the splitter, so that the listbox is about 80 (logical) pixels wide
    m_pSplitter->SetSplitPosPixel(getBrowserView()->LogicToPixel(::Size(80, 0), MapMode(MapUnit::MapAppFont)).Width());
 
    getBrowserView()->setSplitter(m_pSplitter);
    getBrowserView()->setTreeView(m_pTreeView);
 
    // fill view with data
    rTreeView.set_sort_order(true);
    rTreeView.set_sort_func([this](const weld::TreeIter& rLeft, const weld::TreeIter& rRight){
        return OnTreeEntryCompare(rLeft, rRight);
    });
    rTreeView.make_sorted();
    m_pTreeView->SetSelChangeHdl(LINK(this, SbaTableQueryBrowser, OnSelectionChange));
    m_pTreeView->show_container();
 
    // TODO
    getBrowserView()->getVclControl()->SetHelpId(HID_CTL_TABBROWSER);
    if (getBrowserView()->getVclControl()->GetHeaderBar())
        getBrowserView()->getVclControl()->GetHeaderBar()->SetHelpId(HID_DATABROWSE_HEADER);
    InvalidateFeature(ID_BROWSER_EXPLORER);
 
    return true;
}
 
namespace
{
    struct SelectValueByName
    {
        const Any& operator()( OUString const& i_name ) const
        {
            return m_rCollection.get( i_name );
        }
 
        explicit SelectValueByName( ::comphelper::NamedValueCollection const& i_collection )
            :m_rCollection( i_collection )
        {
        }
 
        ::comphelper::NamedValueCollection const&   m_rCollection;
    };
}
 
void SbaTableQueryBrowser::impl_sanitizeRowSetClauses_nothrow()
{
    try
    {
        Reference< XPropertySet > xRowSetProps( getRowSet(), UNO_QUERY_THROW );
        bool bEscapeProcessing = false;
        OSL_VERIFY( xRowSetProps->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bEscapeProcessing );
        if ( !bEscapeProcessing )
            // don't touch or interpret anything if escape processing is disabled
            return;
 
        Reference< XSingleSelectQueryComposer > xComposer( createParser_nothrow() );
        if ( !xComposer.is() )
            // can't do anything. Already reported via assertion in createParser_nothrow.
            return;
 
        // the tables participating in the statement
        const Reference< XTablesSupplier > xSuppTables( xComposer, UNO_QUERY_THROW );
        const Reference< XNameAccess > xTableNames( xSuppTables->getTables(), UNO_SET_THROW );
 
        // the columns participating in the statement
        const Reference< XColumnsSupplier > xSuppColumns( xComposer, UNO_QUERY_THROW );
        const Reference< XNameAccess > xColumnNames( xSuppColumns->getColumns(), UNO_SET_THROW );
 
        // check if the order columns apply to tables which really exist in the statement
        const Reference< XIndexAccess > xOrderColumns( xComposer->getOrderColumns(), UNO_SET_THROW );
        const sal_Int32 nOrderColumns( xOrderColumns->getCount() );
        bool invalidColumn = nOrderColumns == 0;
        for ( sal_Int32 c=0; ( c < nOrderColumns ) && !invalidColumn; ++c )
        {
            const Reference< XPropertySet > xOrderColumn( xOrderColumns->getByIndex(c), UNO_QUERY_THROW );
            OUString sTableName;
            OSL_VERIFY( xOrderColumn->getPropertyValue( PROPERTY_TABLENAME ) >>= sTableName );
            OUString sColumnName;
            OSL_VERIFY( xOrderColumn->getPropertyValue( PROPERTY_NAME ) >>= sColumnName );
 
            if ( sTableName.isEmpty() )
            {
                if ( !xColumnNames->hasByName( sColumnName ) )
                {
                    invalidColumn = true;
                    break;
                }
            }
            else
            {
                if ( !xTableNames->hasByName( sTableName ) )
                {
                    invalidColumn = true;
                    break;
                }
 
                const Reference< XColumnsSupplier > xSuppTableColumns( xTableNames->getByName( sTableName ), UNO_QUERY_THROW );
                const Reference< XNameAccess > xTableColumnNames( xSuppTableColumns->getColumns(), UNO_SET_THROW );
                if ( !xTableColumnNames->hasByName( sColumnName ) )
                {
                    invalidColumn = true;
                    break;
                }
            }
        }
 
        if ( invalidColumn )
        {
            // reset the complete order statement at both the row set and the parser
            xRowSetProps->setPropertyValue( PROPERTY_ORDER, Any( OUString() ) );
            xComposer->setOrder( u""_ustr );
        }
 
        // check if the columns participating in the filter refer to existing tables
        // TODO: there's no API at all for this. The method which comes nearest to what we need is
        // "getStructuredFilter", but it returns pure column names only. That is, for a statement like
        // "SELECT * FROM <table> WHERE <other_table>.<column> = <value>", it will return "<column>". But
        // there's no API at all to retrieve the information about  "<other_table>" - which is what would
        // be needed here.
        // That'd be a chance to replace getStructuredFilter with something more reasonable.
        // So, what really would be handy, is some
        //   XNormalizedFilter getNormalizedFilter();
        // with
        //   interface XDisjunctiveFilterExpression
        //   {
        //     XConjunctiveFilterTerm getTerm( int index );
        //   }
        //   interface XConjunctiveFilterTerm
        //   {
        //     ComparisonPredicate getPredicate( int index );
        //   }
        //   struct ComparisonPredicate
        //   {
        //     XComparisonOperand   Lhs;
        //     SQLFilterOperator    Operator;
        //     XComparisonOperand   Rhs;
        //   }
        //   interface XComparisonOperand
        //   {
        //     SQLFilterOperand Type;
        //     XPropertySet     getColumn();
        //     string           getLiteral();
        //     ...
        //   }
        //   enum SQLFilterOperand { Column, Literal, ... }
        // ... or something like this...
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}
 
bool SbaTableQueryBrowser::InitializeForm( const Reference< XPropertySet > & i_formProperties )
{
    if (!m_xCurrentlyDisplayed)
        return true;
 
    // this method set all format settings from the original table or query
    try
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*m_xCurrentlyDisplayed));
        ENSURE_OR_RETURN_FALSE( pData, "SbaTableQueryBrowser::InitializeForm: No user data set at the currently displayed entry!" );
        ENSURE_OR_RETURN_FALSE( pData->xObjectProperties.is(), "SbaTableQueryBrowser::InitializeForm: No table available!" );
 
        Reference< XPropertySetInfo > xPSI( pData->xObjectProperties->getPropertySetInfo(), UNO_SET_THROW );
 
        ::comphelper::NamedValueCollection aPropertyValues;
 
        const OUString aTransferProperties[] =
        {
            PROPERTY_APPLYFILTER,
            PROPERTY_FILTER,
            PROPERTY_HAVING_CLAUSE,
            PROPERTY_ORDER
        };
        for (const auto & aTransferProperty : aTransferProperties)
        {
            if ( !xPSI->hasPropertyByName( aTransferProperty ) )
                continue;
            aPropertyValues.put( aTransferProperty, pData->xObjectProperties->getPropertyValue( aTransferProperty ) );
        }
 
        std::vector< OUString > aNames( aPropertyValues.getNames() );
        std::sort(aNames.begin(), aNames.end());
        Sequence< OUString > aPropNames( comphelper::containerToSequence(aNames) );
 
        Sequence< Any > aPropValues( aNames.size() );
        std::transform( aNames.begin(), aNames.end(), aPropValues.getArray(), SelectValueByName( aPropertyValues ) );
 
        Reference< XMultiPropertySet > xFormMultiSet( i_formProperties, UNO_QUERY_THROW );
        xFormMultiSet->setPropertyValues( aPropNames, aPropValues );
 
        impl_sanitizeRowSetClauses_nothrow();
    }
    catch ( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
        return false;
    }
 
    return true;
}
 
void SbaTableQueryBrowser::initializePreviewMode()
{
    if ( getBrowserView() && getBrowserView()->getVclControl() )
    {
        getBrowserView()->getVclControl()->AlwaysEnableInput( false );
        getBrowserView()->getVclControl()->EnableInput( false );
        getBrowserView()->getVclControl()->ForceHideScrollbars();
    }
    Reference< XPropertySet >  xDataSourceSet(getRowSet(), UNO_QUERY);
    if ( xDataSourceSet.is() )
    {
        xDataSourceSet->setPropertyValue(u"AllowInserts"_ustr,Any(false));
        xDataSourceSet->setPropertyValue(u"AllowUpdates"_ustr,Any(false));
        xDataSourceSet->setPropertyValue(u"AllowDeletes"_ustr,Any(false));
    }
}
 
void SbaTableQueryBrowser::InitializeGridModel(const Reference< css::form::XFormComponent > & xGrid)
{
    try
    {
        Reference< css::form::XGridColumnFactory >  xColFactory(xGrid, UNO_QUERY);
        Reference< XNameContainer >  xColContainer(xGrid, UNO_QUERY);
        clearGridColumns( xColContainer );
 
        Reference< XLoadable > xFormAsLoadable;
        if (xGrid.is())
            xFormAsLoadable.set(xGrid->getParent(), css::uno::UNO_QUERY);
        if (xFormAsLoadable.is() && xFormAsLoadable->isLoaded())
        {
            // set the formats from the table
            if (m_xCurrentlyDisplayed)
            {
                Sequence< OUString> aProperties(6 + ( m_bPreview ? 5 : 0 ));
                Sequence< Any> aValues(6 + ( m_bPreview ? 5 : 0 ));
 
                weld::TreeView& rTreeView = m_pTreeView->GetWidget();
                DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*m_xCurrentlyDisplayed));
                OSL_ENSURE( pData->xObjectProperties.is(), "SbaTableQueryBrowser::InitializeGridModel: No table available!" );
                if ( !pData->xObjectProperties.is() )
                    return;
 
                OUString* pStringIter = aProperties.getArray();
                Any* pValueIter = aValues.getArray();
                if ( m_bPreview )
                {
                    *pStringIter++  = "AlwaysShowCursor";
                    *pValueIter++   <<= false;
                    *pStringIter++  = PROPERTY_BORDER;
                    *pValueIter++   <<= sal_Int16(0);
                }
 
                *pStringIter++  = PROPERTY_FONT;
                *pValueIter++   = pData->xObjectProperties->getPropertyValue(PROPERTY_FONT);
                *pStringIter++  = PROPERTY_TEXTEMPHASIS;
                *pValueIter++   = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTEMPHASIS);
                *pStringIter++  = PROPERTY_TEXTRELIEF;
                *pValueIter++   = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTRELIEF);
                if ( m_bPreview )
                {
                    *pStringIter++  = "HasNavigationBar";
                    *pValueIter++       <<= false;
                    *pStringIter++  = "HasRecordMarker";
                    *pValueIter++       <<= false;
                }
                *pStringIter++  = PROPERTY_ROW_HEIGHT;
                *pValueIter++   = pData->xObjectProperties->getPropertyValue(PROPERTY_ROW_HEIGHT);
                if ( m_bPreview )
                {
                    *pStringIter++  = "Tabstop";
                    *pValueIter++       <<= false;
                }
                *pStringIter++  = PROPERTY_TEXTCOLOR;
                *pValueIter++   = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTCOLOR);
                *pStringIter++  = PROPERTY_TEXTLINECOLOR;
                *pValueIter++   = pData->xObjectProperties->getPropertyValue(PROPERTY_TEXTLINECOLOR);
 
                Reference< XMultiPropertySet >  xFormMultiSet(xGrid, UNO_QUERY);
                xFormMultiSet->setPropertyValues(aProperties, aValues);
            }
 
            // get the formats supplier of the database we're working with
            Reference< css::util::XNumberFormatsSupplier >  xSupplier = getNumberFormatter()->getNumberFormatsSupplier();
 
            Reference<XConnection> xConnection;
            Reference<XPropertySet> xRowSetProps(getRowSet(),UNO_QUERY);
            xRowSetProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ) >>= xConnection;
            OSL_ENSURE(xConnection.is(),"A ActiveConnection should normally exists!");
 
            Reference<XChild> xChild(xConnection,UNO_QUERY);
            Reference<XPropertySet> xDataSourceProp(xChild->getParent(),UNO_QUERY);
            bool bSuppressVersionCol = false;
            OSL_VERIFY( xDataSourceProp->getPropertyValue( PROPERTY_SUPPRESSVERSIONCL ) >>= bSuppressVersionCol );
 
            // insert the column into the gridcontrol so that we see something :-)
            OUString aCurrentModelType;
            Reference<XColumnsSupplier> xSupCols(getRowSet(),UNO_QUERY);
            Reference<XNameAccess> xColumns     = xSupCols->getColumns();
 
            OUString sDefaultProperty;
            Reference< XPropertySet > xColumn;
            Reference< XPropertySetInfo > xColPSI;
            const Sequence<OUString> aColNames = xColumns->getElementNames();
            for (const OUString& rName : aColNames)
            {
                xColumn.set( xColumns->getByName( rName ), UNO_QUERY_THROW );
                xColPSI.set( xColumn->getPropertySetInfo(), UNO_SET_THROW );
 
                // ignore the column when it is a rowversion one
                if  (   bSuppressVersionCol
                    &&  xColPSI->hasPropertyByName( PROPERTY_ISROWVERSION )
                    &&  ::cppu::any2bool( xColumn->getPropertyValue( PROPERTY_ISROWVERSION ) )
                    )
                    continue;
 
                // use the result set column's type to determine the type of grid column to create
                bool bFormattedIsNumeric    = true;
                sal_Int32 nType = ::comphelper::getINT32( xColumn->getPropertyValue( PROPERTY_TYPE ) );
 
                std::vector< NamedValue > aInitialValues;
                std::vector< OUString > aCopyProperties;
                Any aDefault;
 
                switch(nType)
                {
                    case DataType::BIT:
                    case DataType::BOOLEAN:
                    {
                        aCurrentModelType = "CheckBox";
                        aInitialValues.emplace_back( "VisualEffect", Any( VisualEffect::FLAT ) );
                        sDefaultProperty = PROPERTY_DEFAULTSTATE;
 
                        sal_Int32 nNullable = ColumnValue::NULLABLE_UNKNOWN;
                        OSL_VERIFY( xColumn->getPropertyValue( PROPERTY_ISNULLABLE ) >>= nNullable );
                        aInitialValues.emplace_back(
                            "TriState",
                            Any( ColumnValue::NO_NULLS != nNullable )
                        );
                        if ( ColumnValue::NO_NULLS == nNullable )
                            aDefault <<= sal_Int16(TRISTATE_FALSE);
                    }
                    break;
 
                    case DataType::LONGVARCHAR:
                    case DataType::CLOB:
                        aInitialValues.emplace_back( "MultiLine", Any( true ) );
                        [[fallthrough]];
                    case DataType::BINARY:
                    case DataType::VARBINARY:
                    case DataType::LONGVARBINARY:
                        aCurrentModelType = "TextField";
                        sDefaultProperty = PROPERTY_DEFAULTTEXT;
                        break;
 
                    case DataType::VARCHAR:
                    case DataType::CHAR:
                        bFormattedIsNumeric = false;
                        [[fallthrough]];
                    default:
                        aCurrentModelType = "FormattedField";
                        sDefaultProperty = PROPERTY_EFFECTIVEDEFAULT;
 
                        if ( xSupplier.is() )
                            aInitialValues.emplace_back( "FormatsSupplier", Any( xSupplier ) );
                        aInitialValues.emplace_back( "TreatAsNumber", Any( bFormattedIsNumeric ) );
                        aCopyProperties.emplace_back(PROPERTY_FORMATKEY );
                        break;
                }
 
                aInitialValues.emplace_back( PROPERTY_CONTROLSOURCE, Any( rName ) );
                OUString sLabel;
                xColumn->getPropertyValue(PROPERTY_LABEL) >>= sLabel;
                if ( !sLabel.isEmpty() )
                    aInitialValues.emplace_back( PROPERTY_LABEL, Any( sLabel ) );
                else
                    aInitialValues.emplace_back( PROPERTY_LABEL, Any( rName ) );
 
                Reference< XPropertySet > xGridCol( xColFactory->createColumn( aCurrentModelType ), UNO_SET_THROW );
                Reference< XPropertySetInfo > xGridColPSI( xGridCol->getPropertySetInfo(), UNO_SET_THROW );
 
                // calculate the default
                if ( xGridColPSI->hasPropertyByName( PROPERTY_CONTROLDEFAULT ) )
                {
                    aDefault = xColumn->getPropertyValue( PROPERTY_CONTROLDEFAULT );
                    // default value
                    if ( nType == DataType::BIT || nType == DataType::BOOLEAN )
                    {
                        if ( aDefault.hasValue() )
                            aDefault <<= (comphelper::getString(aDefault).toInt32() == 0) ? sal_Int16(TRISTATE_FALSE) : sal_Int16(TRISTATE_TRUE);
                        else
                            aDefault <<= sal_Int16(TRISTATE_INDET);
                    }
                }
 
                if ( aDefault.hasValue() )
                    aInitialValues.emplace_back( sDefaultProperty, aDefault );
 
                // transfer properties from the definition to the UNO-model :
                aCopyProperties.emplace_back(PROPERTY_HIDDEN );
                aCopyProperties.emplace_back(PROPERTY_WIDTH );
 
                // help text to display for the column
                Any aDescription;
                if ( xColPSI->hasPropertyByName( PROPERTY_HELPTEXT ) )
                    aDescription = xColumn->getPropertyValue( PROPERTY_HELPTEXT );
                OUString sTemp;
                aDescription >>= sTemp;
                if ( sTemp.isEmpty() )
                    xColumn->getPropertyValue( PROPERTY_DESCRIPTION ) >>= sTemp;
 
                aDescription <<= sTemp;
                aInitialValues.emplace_back( PROPERTY_HELPTEXT, aDescription );
 
                // ... horizontal justify
                Any aAlign; aAlign <<= sal_Int16( 0 );
                Any aColAlign( xColumn->getPropertyValue( PROPERTY_ALIGN ) );
                if ( aColAlign.hasValue() )
                    aAlign <<= sal_Int16( ::comphelper::getINT32( aColAlign ) );
                aInitialValues.emplace_back( PROPERTY_ALIGN, aAlign );
 
                // don't allow the mouse to scroll in the cells
                if ( xGridColPSI->hasPropertyByName( PROPERTY_MOUSE_WHEEL_BEHAVIOR ) )
                    aInitialValues.emplace_back( PROPERTY_MOUSE_WHEEL_BEHAVIOR, Any( MouseWheelBehavior::SCROLL_DISABLED ) );
 
                // now set all those values
                for (auto const& property : aInitialValues)
                {
                    xGridCol->setPropertyValue( property.Name, property.Value );
                }
                for (auto const& copyPropertyName : aCopyProperties)
                    xGridCol->setPropertyValue( copyPropertyName, xColumn->getPropertyValue(copyPropertyName) );
 
                xColContainer->insertByName(rName, Any(xGridCol));
            }
        }
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}
 
static Reference<XPropertySet> getColumnHelper(const weld::TreeView& rTreeView,
                                               const weld::TreeIter* pCurrentlyDisplayed,
                                               const Reference<XPropertySet>& rxSource)
{
    Reference<XPropertySet> xRet;
    if (pCurrentlyDisplayed)
    {
        DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*pCurrentlyDisplayed));
        Reference<XColumnsSupplier> xColumnsSup(pData->xObjectProperties,UNO_QUERY);
        Reference<XNameAccess> xNames = xColumnsSup->getColumns();
        OUString aName;
        rxSource->getPropertyValue(PROPERTY_NAME) >>= aName;
        if(xNames.is() && xNames->hasByName(aName))
            xRet.set(xNames->getByName(aName),UNO_QUERY);
    }
    return xRet;
}
 
void SbaTableQueryBrowser::transferChangedControlProperty(const OUString& _rProperty, const Any& _rNewValue)
{
    if (m_xCurrentlyDisplayed)
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*m_xCurrentlyDisplayed));
        Reference< XPropertySet > xObjectProps = pData->xObjectProperties;
        OSL_ENSURE(xObjectProps.is(),"SbaTableQueryBrowser::transferChangedControlProperty: no table/query object!");
        if (xObjectProps.is())
            xObjectProps->setPropertyValue(_rProperty, _rNewValue);
    }
}
 
void SbaTableQueryBrowser::propertyChange(const PropertyChangeEvent& evt)
{
    SbaXDataBrowserController::propertyChange(evt);
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
 
    try
    {
        Reference< XPropertySet >  xSource(evt.Source, UNO_QUERY);
        if (!xSource.is())
            return;
        // one of the many properties which require us to update the definition ?
        // a column's width ?
        else if (evt.PropertyName == PROPERTY_WIDTH)
        {   // a column width has changed -> update the model
            // (the update of the view is done elsewhere)
            Reference<XPropertySet> xProp = getColumnHelper(rTreeView, m_xCurrentlyDisplayed.get(), xSource);
            if(xProp.is())
            {
                if(!evt.NewValue.hasValue())
                    xProp->setPropertyValue(PROPERTY_WIDTH,Any(sal_Int32(227)));
                else
                    xProp->setPropertyValue(PROPERTY_WIDTH,evt.NewValue);
            }
        }
 
        // a column's 'visible' state ?
        else if (evt.PropertyName == PROPERTY_HIDDEN)
        {
            Reference<XPropertySet> xProp = getColumnHelper(rTreeView, m_xCurrentlyDisplayed.get(), xSource);
            if(xProp.is())
                xProp->setPropertyValue(PROPERTY_HIDDEN,evt.NewValue);
        }
 
        // a columns alignment ?
        else if (evt.PropertyName == PROPERTY_ALIGN)
        {
            Reference<XPropertySet> xProp = getColumnHelper(rTreeView, m_xCurrentlyDisplayed.get(), xSource);
            try
            {
                if(xProp.is())
                {
                    if(evt.NewValue.hasValue())
                    {
                        sal_Int16 nAlign = 0;
                        if(evt.NewValue >>= nAlign)
                            xProp->setPropertyValue(PROPERTY_ALIGN,Any(sal_Int32(nAlign)));
                        else
                            xProp->setPropertyValue(PROPERTY_ALIGN,evt.NewValue);
                    }
                    else
                        xProp->setPropertyValue(PROPERTY_ALIGN,Any(css::awt::TextAlign::LEFT));
                }
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }
        }
 
        // a column's format ?
        else if (   evt.PropertyName == PROPERTY_FORMATKEY
            &&  (TypeClass_LONG == evt.NewValue.getValueTypeClass())
            )
        {
            // update the model (means the definition object)
            Reference<XPropertySet> xProp = getColumnHelper(rTreeView, m_xCurrentlyDisplayed.get(), xSource);
            if(xProp.is())
                xProp->setPropertyValue(PROPERTY_FORMATKEY,evt.NewValue);
        }
 
        // some table definition properties ?
        // the height of the rows in the grid ?
        else if (evt.PropertyName == PROPERTY_ROW_HEIGHT)
        {
            if (m_xCurrentlyDisplayed)
            {
                DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*m_xCurrentlyDisplayed));
                OSL_ENSURE( pData->xObjectProperties.is(), "No table available!" );
 
                bool bDefault = !evt.NewValue.hasValue();
                if (bDefault)
                    pData->xObjectProperties->setPropertyValue(PROPERTY_ROW_HEIGHT,Any(sal_Int32(45)));
                else
                    pData->xObjectProperties->setPropertyValue(PROPERTY_ROW_HEIGHT,evt.NewValue);
            }
        }
 
        else if (   evt.PropertyName == PROPERTY_FONT          // the font ?
                ||  evt.PropertyName == PROPERTY_TEXTCOLOR     // the text color ?
                ||  evt.PropertyName == PROPERTY_FILTER        // the filter ?
                ||  evt.PropertyName == PROPERTY_HAVING_CLAUSE // the having clause ?
                ||  evt.PropertyName == PROPERTY_ORDER         // the sort ?
                ||  evt.PropertyName == PROPERTY_APPLYFILTER   // the appliance of the filter ?
                ||  evt.PropertyName == PROPERTY_TEXTLINECOLOR // the text line color ?
                ||  evt.PropertyName == PROPERTY_TEXTEMPHASIS  // the text emphasis ?
                ||  evt.PropertyName == PROPERTY_TEXTRELIEF    // the text relief ?
                )
        {
            transferChangedControlProperty(evt.PropertyName, evt.NewValue);
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}
 
sal_Bool SbaTableQueryBrowser::suspend(sal_Bool bSuspend)
{
    SolarMutexGuard aSolarGuard;
    ::osl::MutexGuard aGuard( getMutex() );
    if ( getView() && getView()->IsInModalMode() )
        return false;
    bool bRet = false;
    if ( !m_bInSuspend )
    {
        m_bInSuspend = true;
        if ( rBHelper.bDisposed )
            throw DisposedException( OUString(), *this );
 
        bRet = SbaXDataBrowserController::suspend(bSuspend);
        if ( bRet && getView() )
            getView()->Hide();
 
        m_bInSuspend = false;
    }
 
    return bRet;
}
 
void SAL_CALL SbaTableQueryBrowser::statusChanged( const FeatureStateEvent& _rEvent )
{
    // search the external dispatcher causing this call
    Reference< XDispatch > xSource(_rEvent.Source, UNO_QUERY);
    bool bFound = false;
    for (auto & externalFeature : m_aExternalFeatures)
    {
        if ( _rEvent.FeatureURL.Complete == externalFeature.second.aURL.Complete)
        {
            bFound = true;
            OSL_ENSURE( xSource.get() == externalFeature.second.xDispatcher.get(), "SbaTableQueryBrowser::statusChanged: inconsistent!" );
            // update the enabled state
            externalFeature.second.bEnabled = _rEvent.IsEnabled;
 
            switch ( externalFeature.first )
            {
                case ID_BROWSER_DOCUMENT_DATASOURCE:
                {
                    // if it's the slot for the document data source, remember the state
                    Sequence< PropertyValue > aDescriptor;
                    bool bProperFormat = _rEvent.State >>= aDescriptor;
                    OSL_ENSURE(bProperFormat, "SbaTableQueryBrowser::statusChanged: need a data access descriptor here!");
                    m_aDocumentDataSource.initializeFrom(aDescriptor);
 
                    OSL_ENSURE( (   m_aDocumentDataSource.has(DataAccessDescriptorProperty::DataSource)
                                ||  m_aDocumentDataSource.has(DataAccessDescriptorProperty::DatabaseLocation)
                                )
                                &&  m_aDocumentDataSource.has(DataAccessDescriptorProperty::Command)
                                &&  m_aDocumentDataSource.has(DataAccessDescriptorProperty::CommandType),
                        "SbaTableQueryBrowser::statusChanged: incomplete descriptor!");
 
                    // check if we know the object which is set as document data source
                    checkDocumentDataSource();
                }
                break;
 
                default:
                    // update the toolbox
                    implCheckExternalSlot( externalFeature.first );
                    break;
            }
            break;
        }
    }
 
    OSL_ENSURE(bFound, "SbaTableQueryBrowser::statusChanged: don't know who sent this!");
}
 
void SbaTableQueryBrowser::checkDocumentDataSource()
{
    std::unique_ptr<weld::TreeIter> xDataSourceEntry;
    std::unique_ptr<weld::TreeIter> xContainerEntry;
    std::unique_ptr<weld::TreeIter> xObjectEntry = getObjectEntry(m_aDocumentDataSource, &xDataSourceEntry, &xContainerEntry);
    bool bKnownDocDataSource = static_cast<bool>(xObjectEntry);
    if (!bKnownDocDataSource)
    {
        if (xDataSourceEntry)
        {
            // at least the data source is known
            if (xContainerEntry)
            {
                bKnownDocDataSource = true; // assume we know it.
                // TODO: should we expand the object container? This may be too expensive just for checking...
            }
            else
            {
                if (m_aDocumentDataSource.has(DataAccessDescriptorProperty::CommandType)
                    && m_aDocumentDataSource.has(DataAccessDescriptorProperty::Command))
                {   // maybe we have a command to be displayed ?
                    sal_Int32 nCommandType = CommandType::TABLE;
                    m_aDocumentDataSource[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
 
                    OUString sCommand;
                    m_aDocumentDataSource[DataAccessDescriptorProperty::Command] >>= sCommand;
 
                    bKnownDocDataSource = (CommandType::COMMAND == nCommandType) && (!sCommand.isEmpty());
                }
            }
        }
    }
 
    if ( !bKnownDocDataSource )
        m_aExternalFeatures[ ID_BROWSER_DOCUMENT_DATASOURCE ].bEnabled = false;
 
    // update the toolbox
    implCheckExternalSlot(ID_BROWSER_DOCUMENT_DATASOURCE);
}
 
void SbaTableQueryBrowser::extractDescriptorProps(const svx::ODataAccessDescriptor& _rDescriptor, OUString& _rDataSource, OUString& _rCommand, sal_Int32& _rCommandType, bool& _rEscapeProcessing)
{
    _rDataSource = _rDescriptor.getDataSource();
    if ( _rDescriptor.has(DataAccessDescriptorProperty::Command) )
        _rDescriptor[DataAccessDescriptorProperty::Command] >>= _rCommand;
    if ( _rDescriptor.has(DataAccessDescriptorProperty::CommandType) )
        _rDescriptor[DataAccessDescriptorProperty::CommandType] >>= _rCommandType;
 
    // escape processing is the only one allowed not to be present
    _rEscapeProcessing = true;
    if (_rDescriptor.has(DataAccessDescriptorProperty::EscapeProcessing))
        _rEscapeProcessing = ::cppu::any2bool(_rDescriptor[DataAccessDescriptorProperty::EscapeProcessing]);
}
 
namespace
{
    bool getDataSourceDisplayName_isURL( const OUString& _rDS, OUString& _rDisplayName, OUString& _rUniqueId )
    {
        INetURLObject aURL( _rDS );
        if ( aURL.GetProtocol() != INetProtocol::NotValid )
        {
            _rDisplayName = aURL.getBase(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::WithCharset);
            _rUniqueId = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
            return true;
        }
        _rDisplayName = _rDS;
        _rUniqueId.clear();
        return false;
    }
 
    struct FilterByEntryDataId : public IEntryFilter
    {
        OUString sId;
        explicit FilterByEntryDataId( OUString _aId ) : sId(std::move( _aId )) { }
 
        virtual ~FilterByEntryDataId() {}
 
        virtual bool    includeEntry(const void* pEntry) const override;
    };
 
    bool FilterByEntryDataId::includeEntry(const void* pUserData) const
    {
        const DBTreeListUserData* pData = static_cast<const DBTreeListUserData*>(pUserData);
        return ( !pData || ( pData->sAccessor == sId ) );
    }
}
 
OUString SbaTableQueryBrowser::getDataSourceAccessor(const weld::TreeIter& rDataSourceEntry) const
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(rDataSourceEntry));
    assert(pData && "SbaTableQueryBrowser::getDataSourceAccessor: invalid entry data!");
    OSL_ENSURE( pData->eType == etDatasource, "SbaTableQueryBrowser::getDataSourceAccessor: entry does not denote a data source!" );
    return !pData->sAccessor.isEmpty() ? pData->sAccessor : GetEntryText(rDataSourceEntry);
}
 
std::unique_ptr<weld::TreeIter> SbaTableQueryBrowser::getObjectEntry(const OUString& _rDataSource, const OUString& _rCommand, sal_Int32 nCommandType,
        std::unique_ptr<weld::TreeIter>* ppDataSourceEntry, std::unique_ptr<weld::TreeIter>* ppContainerEntry, bool bExpandAncestors,
        const SharedConnection& _rxConnection )
{
    if (ppDataSourceEntry)
        ppDataSourceEntry->reset();
    if (ppContainerEntry)
        ppContainerEntry->reset();
 
    std::unique_ptr<weld::TreeIter> xObject;
    if ( m_pTreeView )
    {
        // look for the data source entry
        OUString sDisplayName, sDataSourceId;
        bool bIsDataSourceURL = getDataSourceDisplayName_isURL( _rDataSource, sDisplayName, sDataSourceId );
            // the display name may differ from the URL for readability reasons
            // #i33699#
 
        FilterByEntryDataId aFilter( sDataSourceId );
        std::unique_ptr<weld::TreeIter> xDataSource = m_pTreeView->GetEntryPosByName( sDisplayName, nullptr, &aFilter );
        if (!xDataSource) // check if the data source name is a file location
        {
            if ( bIsDataSourceURL )
            {
                // special case, the data source is a URL
                // add new entries to the list box model
                implAddDatasource( _rDataSource, _rxConnection );
                xDataSource = m_pTreeView->GetEntryPosByName( sDisplayName, nullptr, &aFilter );
                OSL_ENSURE( xDataSource, "SbaTableQueryBrowser::getObjectEntry: hmm - did not find it again!" );
            }
        }
 
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
 
        if (xDataSource)
        {
            if (ppDataSourceEntry)
            {
                // (caller wants to have it...)
                *ppDataSourceEntry = rTreeView.make_iterator(xDataSource.get());
            }
 
            // expand if required so
            if (bExpandAncestors)
                rTreeView.expand_row(*xDataSource);
 
            // look for the object container
            std::unique_ptr<weld::TreeIter> xCommandType;
            if (nCommandType == CommandType::QUERY || nCommandType == CommandType::TABLE)
            {
                xCommandType = rTreeView.make_iterator(xDataSource.get());
                if (!rTreeView.iter_children(*xCommandType))
                    xCommandType.reset();
                else
                {
                    // 1st child is queries, so we're already done if looking for CommandType::QUERY
 
                    // 2nd child is tables
                    if (nCommandType == CommandType::TABLE && !rTreeView.iter_next_sibling(*xCommandType))
                        xCommandType.reset();
                }
            }
 
            if (xCommandType)
            {
                if (ppContainerEntry)
                {
                    // (caller wants to have it...)
                    *ppContainerEntry = rTreeView.make_iterator(xCommandType.get());
                }
 
                rTreeView.make_unsorted();
 
                // expand if required so
                if (bExpandAncestors)
                {
                    rTreeView.expand_row(*xCommandType);
                }
 
                // look for the object
                sal_Int32 nIndex = 0;
                do
                {
                    OUString sPath;
                    switch (nCommandType)
                    {
                    case CommandType::TABLE:
                        sPath = _rCommand;
                        nIndex = -1;
                        break;
 
                    case CommandType::QUERY:
                        sPath = _rCommand.getToken( 0, '/', nIndex );
                        break;
 
                    default:
                        assert(false);
                    }
                    xObject = m_pTreeView->GetEntryPosByName(sPath, xCommandType.get());
                    if (xObject)
                        rTreeView.copy_iterator(*xObject, *xCommandType);
                    else
                        xCommandType.reset();
                    if ( nIndex >= 0 )
                    {
                        if (ensureEntryObject(*xObject))
                        {
                            DBTreeListUserData* pParentData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xObject));
                            Reference< XNameAccess > xCollection( pParentData->xContainer, UNO_QUERY );
                            sal_Int32 nIndex2 = nIndex;
                            sPath = _rCommand.getToken( 0, '/', nIndex2 );
                            try
                            {
                                if ( xCollection->hasByName(sPath) )
                                {
                                    if(!m_pTreeView->GetEntryPosByName(sPath, xObject.get()))
                                    {
                                        Reference<XNameAccess> xChild(xCollection->getByName(sPath),UNO_QUERY);
                                        DBTreeListUserData* pEntryData = new DBTreeListUserData;
                                        pEntryData->eType = etQuery;
                                        if ( xChild.is() )
                                        {
                                            pEntryData->eType = etQueryContainer;
                                        }
                                        implAppendEntry(xObject.get(), sPath, pEntryData);
                                    }
                                }
                            }
                            catch(const Exception&)
                            {
                                SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::populateTree: could not fill the tree");
                            }
                        }
                    }
                }
                while ( nIndex >= 0 );
 
                rTreeView.make_sorted();
            }
        }
    }
    return xObject;
}
 
std::unique_ptr<weld::TreeIter> SbaTableQueryBrowser::getObjectEntry(const svx::ODataAccessDescriptor& rDescriptor,
        std::unique_ptr<weld::TreeIter>* ppDataSourceEntry, std::unique_ptr<weld::TreeIter>* ppContainerEntry)
{
    // extract the props from the descriptor
    OUString sDataSource;
    OUString sCommand;
    sal_Int32 nCommandType = CommandType::COMMAND;
    bool bEscapeProcessing = true;
    extractDescriptorProps(rDescriptor, sDataSource, sCommand, nCommandType, bEscapeProcessing);
 
    return getObjectEntry(sDataSource, sCommand, nCommandType, ppDataSourceEntry, ppContainerEntry, false /*bExpandAncestors*/);
}
 
void SbaTableQueryBrowser::connectExternalDispatches()
{
    Reference< XDispatchProvider >  xProvider( getFrame(), UNO_QUERY );
    OSL_ENSURE(xProvider.is(), "SbaTableQueryBrowser::connectExternalDispatches: no DispatchProvider !");
    if (!xProvider.is())
        return;
 
    if ( m_aExternalFeatures.empty() )
    {
        static constexpr OUString aURLs[] {
            u".uno:DataSourceBrowser/DocumentDataSource"_ustr,
            u".uno:DataSourceBrowser/FormLetter"_ustr,
            u".uno:DataSourceBrowser/InsertColumns"_ustr,
            u".uno:DataSourceBrowser/InsertContent"_ustr,
        };
        static constexpr sal_uInt16 nIds[] = {
            ID_BROWSER_DOCUMENT_DATASOURCE,
            ID_BROWSER_FORMLETTER,
            ID_BROWSER_INSERTCOLUMNS,
            ID_BROWSER_INSERTCONTENT
        };
 
        for ( size_t i=0; i < std::size( aURLs ); ++i )
        {
            URL aURL;
            aURL.Complete = aURLs[i];
            if ( m_xUrlTransformer.is() )
                m_xUrlTransformer->parseStrict( aURL );
            m_aExternalFeatures[ nIds[ i ] ] = ExternalFeature( std::move(aURL) );
        }
    }
 
    for (auto & externalFeature : m_aExternalFeatures)
    {
        externalFeature.second.xDispatcher = xProvider->queryDispatch(
            externalFeature.second.aURL, u"_parent"_ustr, FrameSearchFlag::PARENT
        );
 
        if ( externalFeature.second.xDispatcher.get() == static_cast< XDispatch* >( this ) )
        {
            SAL_WARN("dbaccess.ui",  "SbaTableQueryBrowser::connectExternalDispatches: this should not happen anymore!" );
                // (nowadays, the URLs aren't in our SupportedFeatures list anymore, so we should
                // not supply a dispatcher for this)
            externalFeature.second.xDispatcher.clear();
        }
 
        if ( externalFeature.second.xDispatcher.is() )
        {
            try
            {
                externalFeature.second.xDispatcher->addStatusListener( this, externalFeature.second.aURL );
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }
        }
 
        implCheckExternalSlot( externalFeature.first );
    }
}
 
void SbaTableQueryBrowser::implCheckExternalSlot( sal_uInt16 _nId )
{
    if ( !m_xMainToolbar.is() )
        return;
 
    VclPtr<vcl::Window> pToolboxWindow = VCLUnoHelper::GetWindow( m_xMainToolbar );
    ToolBox* pToolbox = dynamic_cast< ToolBox* >( pToolboxWindow.get() );
    OSL_ENSURE( pToolbox, "SbaTableQueryBrowser::implCheckExternalSlot: cannot obtain the toolbox window!" );
 
    // check if we have to hide this item from the toolbox
    if ( pToolbox )
    {
        bool bHaveDispatcher = m_aExternalFeatures[ _nId ].xDispatcher.is();
        if ( bHaveDispatcher != pToolbox->IsItemVisible( ToolBoxItemId(_nId) ) )
            bHaveDispatcher ? pToolbox->ShowItem( ToolBoxItemId(_nId) ) : pToolbox->HideItem( ToolBoxItemId(_nId) );
    }
 
    // and invalidate this feature in general
    InvalidateFeature( _nId );
}
 
void SAL_CALL SbaTableQueryBrowser::disposing( const css::lang::EventObject& _rSource )
{
    // our frame ?
    Reference< css::frame::XFrame >  xSourceFrame(_rSource.Source, UNO_QUERY);
    if (m_xCurrentFrameParent.is() && (xSourceFrame == m_xCurrentFrameParent))
        m_xCurrentFrameParent->removeFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this));
    else
    {
        // search the external dispatcher causing this call in our map
        Reference< XDispatch > xSource(_rSource.Source, UNO_QUERY);
        if(xSource.is())
        {
            ExternalFeaturesMap::const_iterator aLoop = m_aExternalFeatures.begin();
            ExternalFeaturesMap::const_iterator aEnd = m_aExternalFeatures.end();
            while (aLoop != aEnd)
            {
                if ( aLoop->second.xDispatcher.get() == xSource.get() )
                {
                    sal_uInt16 nSlot = aLoop->first;
 
                    // remove it
                    aLoop = m_aExternalFeatures.erase(aLoop);
 
                    // maybe update the UI
                    implCheckExternalSlot(nSlot);
 
                    // continue, the same XDispatch may be responsible for more than one URL
                }
                ++aLoop;
            }
        }
        else
        {
            Reference<XConnection> xCon(_rSource.Source, UNO_QUERY);
            if ( xCon.is() && m_pTreeView )
            {
                // our connection is in dispose so we have to find the entry equal with this connection
                // and close it what means to collapse the entry
                // get the top-level representing the removed data source
                weld::TreeView& rTreeView = m_pTreeView->GetWidget();
                std::unique_ptr<weld::TreeIter> xDSLoop(rTreeView.make_iterator());
                if (rTreeView.get_iter_first(*xDSLoop))
                {
                    do
                    {
                        DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xDSLoop));
                        if ( pData && pData->xConnection == xCon )
                        {
                            // we set the connection to null to avoid a second disposing of the connection
                            pData->xConnection.clear();
                            closeConnection(*xDSLoop, false);
                            break;
                        }
                    }
                    while (rTreeView.iter_next_sibling(*xDSLoop));
                }
            }
            else
                SbaXDataBrowserController::disposing(_rSource);
        }
    }
}
 
void SbaTableQueryBrowser::implRemoveStatusListeners()
{
    // clear all old dispatches
    for (auto const& externalFeature : m_aExternalFeatures)
    {
        if ( externalFeature.second.xDispatcher.is() )
        {
            try
            {
                externalFeature.second.xDispatcher->removeStatusListener( this, externalFeature.second.aURL );
            }
            catch (Exception&)
            {
                SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::implRemoveStatusListeners: could not remove a status listener!");
            }
        }
    }
    m_aExternalFeatures.clear();
}
 
sal_Bool SAL_CALL SbaTableQueryBrowser::select( const Any& _rSelection )
{
    SolarMutexGuard aGuard;
        // doin' a lot of VCL stuff here -> lock the SolarMutex
 
    Sequence< PropertyValue > aDescriptorSequence;
    if (!(_rSelection >>= aDescriptorSequence))
        throw IllegalArgumentException(OUString(), *this, 1);
        // TODO: error message
 
    ODataAccessDescriptor aDescriptor;
    try
    {
        aDescriptor = ODataAccessDescriptor(aDescriptorSequence);
    }
    catch(const Exception&)
    {
        SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::select: could not extract the descriptor!");
    }
 
    // check the presence of the props we need
    if ( !(aDescriptor.has(DataAccessDescriptorProperty::DataSource) || aDescriptor.has(DataAccessDescriptorProperty::DatabaseLocation)) || !aDescriptor.has(DataAccessDescriptorProperty::Command) || !aDescriptor.has(DataAccessDescriptorProperty::CommandType))
        throw IllegalArgumentException(OUString(), *this, 1);
        // TODO: error message
 
    return implSelect(aDescriptor,true);
}
 
Any SAL_CALL SbaTableQueryBrowser::getSelection(  )
{
    Any aReturn;
 
    try
    {
        Reference< XLoadable > xLoadable(getRowSet(), UNO_QUERY);
        if (xLoadable.is() && xLoadable->isLoaded())
        {
            Reference< XPropertySet > aFormProps(getRowSet(), UNO_QUERY);
            ODataAccessDescriptor aDescriptor(aFormProps);
            // remove properties which are not part of our "selection"
            aDescriptor.erase(DataAccessDescriptorProperty::Connection);
            aDescriptor.erase(DataAccessDescriptorProperty::Cursor);
 
            aReturn <<= aDescriptor.createPropertyValueSequence();
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    return aReturn;
}
 
void SAL_CALL SbaTableQueryBrowser::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
{
    m_aSelectionListeners.addInterface(_rxListener);
}
 
void SAL_CALL SbaTableQueryBrowser::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
{
    m_aSelectionListeners.removeInterface(_rxListener);
}
 
void SbaTableQueryBrowser::attachFrame(const Reference< css::frame::XFrame > & _xFrame)
{
    implRemoveStatusListeners();
 
    if (m_xCurrentFrameParent.is())
        m_xCurrentFrameParent->removeFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this));
 
    SbaXDataBrowserController::attachFrame(_xFrame);
 
    Reference< XFrame > xCurrentFrame( getFrame() );
    if ( xCurrentFrame.is() )
    {
        m_xCurrentFrameParent = xCurrentFrame->findFrame(u"_parent"_ustr,FrameSearchFlag::PARENT);
        if ( m_xCurrentFrameParent.is() )
            m_xCurrentFrameParent->addFrameActionListener(static_cast<css::frame::XFrameActionListener*>(this));
 
        // obtain our toolbox
        try
        {
            Reference< XPropertySet > xFrameProps( m_aCurrentFrame.getFrame(), UNO_QUERY_THROW );
            Reference< XLayoutManager > xLayouter(
                xFrameProps->getPropertyValue(u"LayoutManager"_ustr),
                UNO_QUERY );
 
            if ( xLayouter.is() )
            {
                Reference< XUIElement > xUI(
                    xLayouter->getElement( u"private:resource/toolbar/toolbar"_ustr ),
                    UNO_SET_THROW );
                m_xMainToolbar.set(xUI->getRealInterface(), css::uno::UNO_QUERY);
                OSL_ENSURE( m_xMainToolbar.is(), "SbaTableQueryBrowser::attachFrame: where's my toolbox?" );
            }
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
    }
 
    // get the dispatchers for the external slots
    connectExternalDispatches();
}
 
void SbaTableQueryBrowser::addModelListeners(const Reference< css::awt::XControlModel > & _xGridControlModel)
{
    SbaXDataBrowserController::addModelListeners(_xGridControlModel);
    Reference< XPropertySet >  xSourceSet(_xGridControlModel, UNO_QUERY);
    if (xSourceSet.is())
    {
        xSourceSet->addPropertyChangeListener(PROPERTY_ROW_HEIGHT, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->addPropertyChangeListener(PROPERTY_FONT, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->addPropertyChangeListener(PROPERTY_TEXTCOLOR, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->addPropertyChangeListener(PROPERTY_TEXTLINECOLOR, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->addPropertyChangeListener(PROPERTY_TEXTEMPHASIS, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->addPropertyChangeListener(PROPERTY_TEXTRELIEF, static_cast<XPropertyChangeListener*>(this));
    }
 
}
 
void SbaTableQueryBrowser::removeModelListeners(const Reference< css::awt::XControlModel > & _xGridControlModel)
{
    SbaXDataBrowserController::removeModelListeners(_xGridControlModel);
    Reference< XPropertySet >  xSourceSet(_xGridControlModel, UNO_QUERY);
    if (xSourceSet.is())
    {
        xSourceSet->removePropertyChangeListener(PROPERTY_ROW_HEIGHT, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->removePropertyChangeListener(PROPERTY_FONT, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->removePropertyChangeListener(PROPERTY_TEXTCOLOR, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->removePropertyChangeListener(PROPERTY_TEXTLINECOLOR, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->removePropertyChangeListener(PROPERTY_TEXTEMPHASIS, static_cast<XPropertyChangeListener*>(this));
        xSourceSet->removePropertyChangeListener(PROPERTY_TEXTRELIEF, static_cast<XPropertyChangeListener*>(this));
    }
}
 
void SbaTableQueryBrowser::RowChanged()
{
    if(getBrowserView())
    {
        SbaGridControl* pControl = getBrowserView()->getVclControl();
        if (!pControl->IsEditing())
            InvalidateFeature(ID_BROWSER_COPY);
    }
    SbaXDataBrowserController::RowChanged();
}
 
void SbaTableQueryBrowser::ColumnChanged()
{
    if(getBrowserView())
    {
        SbaGridControl* pControl = getBrowserView()->getVclControl();
        if (!pControl->IsEditing())
            InvalidateFeature(ID_BROWSER_COPY);
    }
    SbaXDataBrowserController::ColumnChanged();
}
 
void SbaTableQueryBrowser::AddColumnListener(const Reference< XPropertySet > & xCol)
{
    SbaXDataBrowserController::AddColumnListener(xCol);
    SafeAddPropertyListener(xCol, PROPERTY_WIDTH, static_cast<XPropertyChangeListener*>(this));
    SafeAddPropertyListener(xCol, PROPERTY_HIDDEN, static_cast<XPropertyChangeListener*>(this));
    SafeAddPropertyListener(xCol, PROPERTY_ALIGN, static_cast<XPropertyChangeListener*>(this));
    SafeAddPropertyListener(xCol, PROPERTY_FORMATKEY, static_cast<XPropertyChangeListener*>(this));
}
 
void SbaTableQueryBrowser::RemoveColumnListener(const Reference< XPropertySet > & xCol)
{
    SbaXDataBrowserController::RemoveColumnListener(xCol);
    SafeRemovePropertyListener(xCol, PROPERTY_WIDTH, static_cast<XPropertyChangeListener*>(this));
    SafeRemovePropertyListener(xCol, PROPERTY_HIDDEN, static_cast<XPropertyChangeListener*>(this));
    SafeRemovePropertyListener(xCol, PROPERTY_ALIGN, static_cast<XPropertyChangeListener*>(this));
    SafeRemovePropertyListener(xCol, PROPERTY_FORMATKEY, static_cast<XPropertyChangeListener*>(this));
}
 
void SbaTableQueryBrowser::criticalFail()
{
    SbaXDataBrowserController::criticalFail();
    unloadAndCleanup( false );
}
 
void SbaTableQueryBrowser::LoadFinished(bool _bWasSynch)
{
    SbaXDataBrowserController::LoadFinished(_bWasSynch);
 
    m_sQueryCommand.clear();
    m_bQueryEscapeProcessing = false;
 
    if (isValid() && !loadingCancelled())
    {
        // did we load a query?
        bool bTemporary;    // needed because we m_bQueryEscapeProcessing is only one bit wide (and we want to pass it by reference)
        if ( implGetQuerySignature( m_sQueryCommand, bTemporary ) )
            m_bQueryEscapeProcessing = bTemporary;
    }
 
    // if the form has been loaded, this means that our "selection" has changed
    css::lang::EventObject aEvent( *this );
    m_aSelectionListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aEvent );
}
 
bool SbaTableQueryBrowser::getExternalSlotState( sal_uInt16 _nId ) const
{
    bool bEnabled = false;
    ExternalFeaturesMap::const_iterator aPos = m_aExternalFeatures.find( _nId );
    if ( ( m_aExternalFeatures.end() != aPos ) && aPos->second.xDispatcher.is() )
        bEnabled = aPos->second.bEnabled;
    return bEnabled;
}
 
FeatureState SbaTableQueryBrowser::GetState(sal_uInt16 nId) const
{
    FeatureState aReturn;
        // (disabled automatically)
 
    // no chance without a view
    if (!getBrowserView() || !getBrowserView()->getVclControl())
        return aReturn;
 
    switch ( nId )
    {
        case ID_TREE_ADMINISTRATE:
            aReturn.bEnabled = true;
            return aReturn;
 
        case ID_BROWSER_CLOSE:
            // the close button should always be enabled
            aReturn.bEnabled = !m_bEnableBrowser;
            return aReturn;
 
            // "toggle explorer" is always enabled (if we have an explorer)
        case ID_BROWSER_EXPLORER:
            aReturn.bEnabled = m_bEnableBrowser;
            aReturn.bChecked = haveExplorer();
            return aReturn;
 
        case ID_BROWSER_REMOVEFILTER:
            return SbaXDataBrowserController::GetState( nId );
 
        case ID_BROWSER_COPY:
            if ( !m_pTreeView->HasChildPathFocus() )
                // handled below
                break;
            [[fallthrough]];
        case ID_TREE_CLOSE_CONN:
        case ID_TREE_EDIT_DATABASE:
        {
            weld::TreeView& rTreeView = m_pTreeView->GetWidget();
            std::unique_ptr<weld::TreeIter> xCurrentEntry(rTreeView.make_iterator());
            if (!rTreeView.get_cursor(xCurrentEntry.get()))
                return aReturn;
 
            EntryType eType = getEntryType(*xCurrentEntry);
            if ( eType == etUnknown )
                return aReturn;
 
            std::unique_ptr<weld::TreeIter> xDataSourceEntry = m_pTreeView->GetRootLevelParent(xCurrentEntry.get());
            DBTreeListUserData* pDSData
                =   xDataSourceEntry
                ?   weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xDataSourceEntry))
                :   nullptr;
 
            if ( nId == ID_TREE_CLOSE_CONN )
            {
                aReturn.bEnabled = ( pDSData != nullptr ) && pDSData->xConnection.is();
            }
            else if ( nId == ID_TREE_EDIT_DATABASE )
            {
                ::utl::OConfigurationTreeRoot aConfig( ::utl::OConfigurationTreeRoot::createWithComponentContext( getORB(),
                    u"/org.openoffice.Office.DataAccess/Policies/Features/Common"_ustr ) );
                bool bHaveEditDatabase( true );
                OSL_VERIFY( aConfig.getNodeValue( u"EditDatabaseFromDataSourceView"_ustr ) >>= bHaveEditDatabase );
                aReturn.bEnabled = getORB().is() && xDataSourceEntry && bHaveEditDatabase;
            }
            else if ( nId == ID_BROWSER_COPY )
            {
                aReturn.bEnabled = isEntryCopyAllowed(*xCurrentEntry);
            }
 
            return aReturn;
        }
    }
 
    // all slots not handled above are not available if no form is loaded
    if (!isLoaded())
        return aReturn;
 
    try
    {
        bool bHandled = false;
        switch (nId)
        {
            case ID_BROWSER_DOCUMENT_DATASOURCE:
                // the slot is enabled if we have an external dispatcher able to handle it,
                // and the dispatcher must have enabled the slot in general
                aReturn.bEnabled = getExternalSlotState( ID_BROWSER_DOCUMENT_DATASOURCE );
                bHandled = true;
                break;
            case ID_BROWSER_REFRESH:
                aReturn.bEnabled = true;
                bHandled = true;
                break;
        }
 
        if (bHandled)
            return aReturn;
 
        // no chance without valid models
        if (isValid() && !isValidCursor() && nId != ID_BROWSER_CLOSE)
            return aReturn;
 
        switch (nId)
        {
            case ID_BROWSER_INSERTCOLUMNS:
            case ID_BROWSER_INSERTCONTENT:
            case ID_BROWSER_FORMLETTER:
            {
                // the slot is enabled if we have an external dispatcher able to handle it,
                // and the dispatcher must have enabled the slot in general
                aReturn.bEnabled = getExternalSlotState( nId );
 
                // for the Insert* slots, we need at least one selected row
                if (ID_BROWSER_FORMLETTER != nId)
                    aReturn.bEnabled = aReturn.bEnabled && getBrowserView()->getVclControl()->GetSelectRowCount();
 
                // disabled for native queries which are not saved within the database
                Reference< XPropertySet >  xDataSource(getRowSet(), UNO_QUERY);
                try
                {
                    aReturn.bEnabled = aReturn.bEnabled && xDataSource.is();
 
                    if (xDataSource.is())
                    {
                        sal_Int32 nType = ::comphelper::getINT32(xDataSource->getPropertyValue(PROPERTY_COMMAND_TYPE));
                        aReturn.bEnabled = aReturn.bEnabled &&
                                           ( ::comphelper::getBOOL(xDataSource->getPropertyValue(PROPERTY_ESCAPE_PROCESSING)) ||
                                             (nType == css::sdb::CommandType::QUERY) );
                    }
                }
                catch(DisposedException&)
                {
                    SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::GetState: object already disposed!");
                }
                catch( const Exception& )
                {
                    DBG_UNHANDLED_EXCEPTION("dbaccess");
                }
            }
            break;
 
            case ID_BROWSER_TITLE:
                {
                    Reference<XPropertySet> xProp(getRowSet(),UNO_QUERY);
                    sal_Int32 nCommandType = CommandType::TABLE;
                    xProp->getPropertyValue(PROPERTY_COMMAND_TYPE) >>= nCommandType;
                    OUString sTitle;
                    switch (nCommandType)
                    {
                        case CommandType::TABLE:
                            sTitle = DBA_RES(STR_TBL_TITLE); break;
                        case CommandType::QUERY:
                        case CommandType::COMMAND:
                            sTitle = DBA_RES(STR_QRY_TITLE); break;
                        default:
                            SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::GetState: unknown command type!");
                    }
                    OUString aName;
                    xProp->getPropertyValue(PROPERTY_COMMAND) >>= aName;
                    OUString sObject(aName);
 
                    aReturn.sTitle = sTitle.replaceFirst("#", sObject);
                    aReturn.bEnabled = true;
                }
                break;
            case ID_BROWSER_TABLEATTR:
            case ID_BROWSER_ROWHEIGHT:
            case ID_BROWSER_COLATTRSET:
            case ID_BROWSER_COLWIDTH:
                aReturn.bEnabled = getBrowserView()->getVclControl() && isValid() && isValidCursor();
                //  aReturn.bEnabled &= getDefinition() && !getDefinition()->GetDatabase()->IsReadOnly();
                break;
 
            case ID_BROWSER_COPY:
                OSL_ENSURE( !m_pTreeView->HasChildPathFocus(), "SbaTableQueryBrowser::GetState( ID_BROWSER_COPY ): this should have been handled above!" );
                if (getBrowserView()->getVclControl() && !getBrowserView()->getVclControl()->IsEditing())
                {
                    SbaGridControl* pControl = getBrowserView()->getVclControl();
                    if ( pControl->GetSelectRowCount() )
                    {
                        aReturn.bEnabled = m_aCurrentFrame.isActive();
                        break;
                    }
                    else
                        aReturn.bEnabled = pControl->canCopyCellText(pControl->GetCurRow(), pControl->GetCurColumnId());
                    break;
                }
                [[fallthrough]];
            default:
                return SbaXDataBrowserController::GetState(nId);
        }
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    return aReturn;
 
}
 
void SbaTableQueryBrowser::Execute(sal_uInt16 nId, const Sequence< PropertyValue >& aArgs)
{
    switch (nId)
    {
        default:
            SbaXDataBrowserController::Execute(nId,aArgs);
            break;
 
        case ID_TREE_EDIT_DATABASE:
        {
            weld::TreeView& rTreeView = m_pTreeView->GetWidget();
            std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
            if (rTreeView.get_cursor(xIter.get()))
                implAdministrate(*xIter);
            break;
        }
        case ID_TREE_CLOSE_CONN:
        {
            weld::TreeView& rTreeView = m_pTreeView->GetWidget();
            std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
            if (rTreeView.get_cursor(xIter.get()))
            {
                xIter = m_pTreeView->GetRootLevelParent(xIter.get());
                closeConnection(*xIter);
            }
            break;
        }
        case ID_TREE_ADMINISTRATE:
            svx::administrateDatabaseRegistration( getFrameWeld() );
            break;
 
        case ID_BROWSER_REFRESH:
        {
            if ( !SaveModified( ) )
                // nothing to do
                break;
 
            bool bFullReinit = false;
            // check if the query signature (if the form is based on a query) has changed
            if ( !m_sQueryCommand.isEmpty() )
            {
                OUString sNewQueryCommand;
                bool bNewQueryEP;
 
                bool bIsQuery =
                    implGetQuerySignature( sNewQueryCommand, bNewQueryEP );
                OSL_ENSURE( bIsQuery, "SbaTableQueryBrowser::Execute: was a query before, but is not anymore?" );
 
                bFullReinit = ( sNewQueryCommand != m_sQueryCommand ) || ( m_bQueryEscapeProcessing != bNewQueryEP );
            }
            if ( !bFullReinit )
            {
                // let the base class do a simple reload
                SbaXDataBrowserController::Execute(nId,aArgs);
                break;
            }
            [[fallthrough]];
        }
 
        case ID_BROWSER_REFRESH_REBUILD:
        {
            if ( !SaveModified() )
                // nothing to do
                break;
 
            weld::TreeView& rTreeView = m_pTreeView->GetWidget();
            std::unique_ptr<weld::TreeIter> xSelected = m_xCurrentlyDisplayed ?
                rTreeView.make_iterator(m_xCurrentlyDisplayed.get()) : nullptr;
 
            // unload
            unloadAndCleanup( false );
 
            // reselect the entry
            if ( xSelected )
            {
                implSelect(xSelected.get());
            }
            else
            {
                Reference<XPropertySet> xProp(getRowSet(),UNO_QUERY);
                implSelect(svx::ODataAccessDescriptor(xProp));
            }
        }
        break;
 
        case ID_BROWSER_EXPLORER:
            toggleExplorer();
            break;
 
        case ID_BROWSER_DOCUMENT_DATASOURCE:
            implSelect(m_aDocumentDataSource);
            break;
 
        case ID_BROWSER_INSERTCOLUMNS:
        case ID_BROWSER_INSERTCONTENT:
        case ID_BROWSER_FORMLETTER:
            if (getBrowserView() && isValidCursor())
            {
                // the URL the slot id is assigned to
                OSL_ENSURE( m_aExternalFeatures.find( nId ) != m_aExternalFeatures.end(),
                    "SbaTableQueryBrowser::Execute( ID_BROWSER_?): how could this ever be enabled?" );
                URL aParentUrl = m_aExternalFeatures[ nId ].aURL;
 
                // let the dispatcher execute the slot
                Reference< XDispatch > xDispatch( m_aExternalFeatures[ nId ].xDispatcher );
                if (xDispatch.is())
                {
                    // set the properties for the dispatch
 
                    // first fill the selection
                    SbaGridControl* pGrid = getBrowserView()->getVclControl();
                    MultiSelection* pSelection = const_cast<MultiSelection*>(pGrid->GetSelection());
                    Sequence< Any > aSelection;
                    if ( !pGrid->IsAllSelected() )
                    {   // transfer the selected rows only if not all rows are selected
                        // (all rows means the whole table)
                        // #i3832#
                        if (pSelection != nullptr)
                        {
                            aSelection.realloc(pSelection->GetSelectCount());
                            tools::Long nIdx = pSelection->FirstSelected();
                            Any* pSelectionNos = aSelection.getArray();
                            while (nIdx != SFX_ENDOFSELECTION)
                            {
                                *pSelectionNos++ <<= static_cast<sal_Int32>(nIdx + 1);
                                nIdx = pSelection->NextSelected();
                            }
                        }
                    }
 
                    Reference< XResultSet > xCursorClone;
                    try
                    {
                        Reference< XResultSetAccess > xResultSetAccess(getRowSet(),UNO_QUERY);
                        if (xResultSetAccess.is())
                            xCursorClone = xResultSetAccess->createResultSet();
                    }
                    catch(DisposedException&)
                    {
                        SAL_WARN("dbaccess.ui", "Object already disposed!");
                    }
                    catch(const Exception&)
                    {
                        SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::Execute(ID_BROWSER_?): could not clone the cursor!");
                    }
 
                    Reference<XPropertySet> xProp(getRowSet(),UNO_QUERY);
 
                    try
                    {
                        ODataAccessDescriptor aDescriptor;
                        OUString sDataSourceName;
                        xProp->getPropertyValue(PROPERTY_DATASOURCENAME) >>= sDataSourceName;
 
                        aDescriptor.setDataSource(sDataSourceName);
                        aDescriptor[DataAccessDescriptorProperty::Command]      =   xProp->getPropertyValue(PROPERTY_COMMAND);
                        aDescriptor[DataAccessDescriptorProperty::CommandType]  =   xProp->getPropertyValue(PROPERTY_COMMAND_TYPE);
                        aDescriptor[DataAccessDescriptorProperty::Connection]   =   xProp->getPropertyValue(PROPERTY_ACTIVE_CONNECTION);
                        aDescriptor[DataAccessDescriptorProperty::Cursor]       <<= xCursorClone;
                        if ( aSelection.hasElements() )
                        {
                            aDescriptor[DataAccessDescriptorProperty::Selection]            <<= aSelection;
                            aDescriptor[DataAccessDescriptorProperty::BookmarkSelection]    <<= false;
                                // these are selection indices
                                // before we change this, all clients have to be adjusted
                                // so that they recognize the new BookmarkSelection property!
                        }
 
                        xDispatch->dispatch(aParentUrl, aDescriptor.createPropertyValueSequence());
                    }
                    catch( const Exception& )
                    {
                        DBG_UNHANDLED_EXCEPTION("dbaccess");
                    }
                }
            }
            break;
 
        case ID_BROWSER_CLOSE:
            closeTask();
            // if it's not 0, such an async close is already pending
            break;
 
        case ID_BROWSER_COPY:
            if(m_pTreeView->HasChildPathFocus())
            {
                weld::TreeView& rTreeView = m_pTreeView->GetWidget();
                std::unique_ptr<weld::TreeIter> xCursor(rTreeView.make_iterator());
                if (rTreeView.get_cursor(xCursor.get()))
                    copyEntry(*xCursor);
            }
            else if (getBrowserView() && getBrowserView()->getVclControl() && !getBrowserView()->getVclControl()->IsEditing() && getBrowserView()->getVclControl()->GetSelectRowCount() < 1)
            {
                SbaGridControl* pControl = getBrowserView()->getVclControl();
                pControl->copyCellText(pControl->GetCurRow(), pControl->GetCurColumnId());
            }
            else
                SbaXDataBrowserController::Execute(nId,aArgs);
            break;
    }
}
 
void SbaTableQueryBrowser::implAddDatasource( const OUString& _rDataSourceName, const SharedConnection& _rxConnection )
{
    OUString a, b, c, d, e;
    implAddDatasource( _rDataSourceName, a, d, b, e, c, _rxConnection );
}
 
void SbaTableQueryBrowser::implAddDatasource(const OUString& _rDbName, OUString& _rDbImage,
        OUString& _rQueryName, OUString& _rQueryImage, OUString& _rTableName, OUString& _rTableImage,
        const SharedConnection& _rxConnection)
{
    SolarMutexGuard aGuard;
    // initialize the names/images if necessary
    if (_rQueryName.isEmpty())
        _rQueryName = DBA_RES(RID_STR_QUERIES_CONTAINER);
    if (_rTableName.isEmpty())
        _rTableName = DBA_RES(RID_STR_TABLES_CONTAINER);
 
    if (_rQueryImage.isEmpty())
        _rQueryImage = ImageProvider::getFolderImageId(DatabaseObject::QUERY);
    if (_rTableImage.isEmpty())
        _rTableImage = ImageProvider::getFolderImageId(DatabaseObject::TABLE);
 
    if (_rDbImage.isEmpty())
        _rDbImage = ImageProvider::getDatabaseImage();
 
    // add the entry for the data source
    // special handling for data sources denoted by URLs - we do not want to display this ugly URL, do we?
    // #i33699#
    OUString sDSDisplayName, sDataSourceId;
    getDataSourceDisplayName_isURL( _rDbName, sDSDisplayName, sDataSourceId );
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    DBTreeListUserData* pDSData = new DBTreeListUserData;
    pDSData->eType = etDatasource;
    pDSData->sAccessor = sDataSourceId;
    pDSData->xConnection = _rxConnection;
    OUString sId(weld::toId(pDSData));
 
    std::unique_ptr<weld::TreeIter> xDatasourceEntry(rTreeView.make_iterator());
    rTreeView.insert(nullptr, -1, &sDSDisplayName, &sId, nullptr, nullptr, false, xDatasourceEntry.get());
    rTreeView.set_image(*xDatasourceEntry, _rDbImage);
    rTreeView.set_text_emphasis(*xDatasourceEntry, false, 0);
 
    // the child for the queries container
    {
        DBTreeListUserData* pQueriesData = new DBTreeListUserData;
        pQueriesData->eType = etQueryContainer;
        sId = weld::toId(pQueriesData);
 
        std::unique_ptr<weld::TreeIter> xRet(rTreeView.make_iterator());
        rTreeView.insert(xDatasourceEntry.get(), -1, &_rQueryName, &sId,
                         nullptr, nullptr, true /*ChildrenOnDemand*/, xRet.get());
        rTreeView.set_image(*xRet, _rQueryImage);
        rTreeView.set_text_emphasis(*xRet, false, 0);
    }
 
    // the child for the tables container
    {
        DBTreeListUserData* pTablesData = new DBTreeListUserData;
        pTablesData->eType = etTableContainer;
        sId = weld::toId(pTablesData);
 
        std::unique_ptr<weld::TreeIter> xRet(rTreeView.make_iterator());
        rTreeView.insert(xDatasourceEntry.get(), -1, &_rTableName, &sId,
                         nullptr, nullptr, true /*ChildrenOnDemand*/, xRet.get());
        rTreeView.set_image(*xRet, _rTableImage);
        rTreeView.set_text_emphasis(*xRet, false, 0);
    }
}
 
void SbaTableQueryBrowser::initializeTreeModel()
{
    if (m_xDatabaseContext.is())
    {
        OUString aDBImage, aQueriesImage, aTablesImage;
        OUString sQueriesName, sTablesName;
 
        // fill the model with the names of the registered datasources
        const Sequence<OUString> aDatasourceNames = m_xDatabaseContext->getElementNames();
        for (const OUString& rDatasource : aDatasourceNames)
            implAddDatasource( rDatasource, aDBImage, sQueriesName, aQueriesImage, sTablesName, aTablesImage, SharedConnection() );
    }
}
 
void SbaTableQueryBrowser::populateTree(const Reference<XNameAccess>& _xNameAccess,
                                        const weld::TreeIter& rParent,
                                        EntryType eEntryType)
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    rTreeView.make_unsorted();
 
    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(rParent));
    if (pData) // don't ask if the nameaccess is already set see OnExpandEntry views and tables
        pData->xContainer = _xNameAccess;
 
    try
    {
        const Sequence<OUString> aNames = _xNameAccess->getElementNames();
        for (const OUString& rName : aNames)
        {
            if( !m_pTreeView->GetEntryPosByName(rName, &rParent))
            {
                DBTreeListUserData* pEntryData = new DBTreeListUserData;
                pEntryData->eType = eEntryType;
                if ( eEntryType == etQuery )
                {
                    Reference<XNameAccess> xChild(_xNameAccess->getByName(rName),UNO_QUERY);
                    if ( xChild.is() )
                        pEntryData->eType = etQueryContainer;
                }
                implAppendEntry(&rParent, rName, pEntryData);
            }
        }
    }
    catch(const Exception&)
    {
        SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::populateTree: could not fill the tree");
    }
 
    rTreeView.make_sorted();
}
 
std::unique_ptr<weld::TreeIter> SbaTableQueryBrowser::implAppendEntry(const weld::TreeIter* pParent, const OUString& rName, const DBTreeListUserData* pUserData)
{
    EntryType eEntryType = pUserData->eType;
 
    std::unique_ptr<ImageProvider> xImageProvider(getImageProviderFor(pParent));
 
    OUString aImage = xImageProvider->getImageId(rName, getDatabaseObjectType(eEntryType));
 
    OUString sId(weld::toId(pUserData));
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    std::unique_ptr<weld::TreeIter> xNewEntry(rTreeView.make_iterator());
    rTreeView.insert(pParent, -1, &rName, &sId, nullptr, nullptr, eEntryType == etQueryContainer, xNewEntry.get());
    rTreeView.set_image(*xNewEntry, aImage);
    rTreeView.set_text_emphasis(*xNewEntry, false, 0);
 
    return xNewEntry;
}
 
IMPL_LINK(SbaTableQueryBrowser, OnExpandEntry, const weld::TreeIter&, rParent, bool)
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    if (rTreeView.iter_has_child(rParent))
    {
        // nothing to do...
        return true;
    }
 
    std::unique_ptr<weld::TreeIter> xFirstParent = m_pTreeView->GetRootLevelParent(&rParent);
    OSL_ENSURE(xFirstParent,"SbaTableQueryBrowser::OnExpandEntry: No rootlevelparent!");
 
    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(rParent));
    assert(pData && "SbaTableQueryBrowser::OnExpandEntry: No user data!");
 
    if (etTableContainer == pData->eType)
    {
        weld::WaitObject aWaitCursor(getFrameWeld());
 
        // it could be that we already have a connection
        SharedConnection xConnection;
        if (ensureConnection(xFirstParent.get(), xConnection) && xConnection.is())
        {
            SQLExceptionInfo aInfo;
            try
            {
                Reference< XWarningsSupplier > xWarnings(xConnection, UNO_QUERY);
                if (xWarnings.is())
                    xWarnings->clearWarnings();
 
                // first insert the views because the tables can also include
                // views but that time the bitmap is the wrong one
                // the nameaccess will be overwritten in populateTree
                Reference<XViewsSupplier> xViewSup(xConnection,UNO_QUERY);
                if(xViewSup.is())
                    populateTree( xViewSup->getViews(), rParent, etTableOrView );
 
                Reference<XTablesSupplier> xTabSup(xConnection,UNO_QUERY);
                if(xTabSup.is())
                {
                    populateTree( xTabSup->getTables(), rParent, etTableOrView );
                    Reference<XContainer> xCont(xTabSup->getTables(),UNO_QUERY);
                    if(xCont.is())
                        // add as listener to know when elements are inserted or removed
                        xCont->addContainerListener(this);
                }
 
                if (xWarnings.is())
                {
#if 0
                    SQLExceptionInfo aWarnings(xWarnings->getWarnings());
// Obviously this if test is always false. So to avoid a Clang warning
// "use of logical '&&' with constant operand" I put this in #if
// 0. Yeah, I know it is fairly likely nobody will ever read this
// comment and make a decision what to do here, so I could as well
// have just binned this...
                    if (aWarnings.isValid() && sal_False)
                    {
                        SQLContext aContext;
                        aContext.Message = DBA_RES(STR_OPENTABLES_WARNINGS);
                        aContext.Details = DBA_RES(STR_OPENTABLES_WARNINGS_DETAILS);
                        aContext.NextException = aWarnings.get();
                        aWarnings = aContext;
                        showError(aWarnings);
                    }
#endif
                    // TODO: we need a better concept for these warnings:
                    // something like "don't show any warnings for this datasource, again" would be nice
                    // But this requires an extension of the InteractionHandler and an additional property on the data source
                }
            }
            catch(const SQLContext& e) { aInfo = e; }
            catch(const SQLWarning& e) { aInfo = e; }
            catch(const SQLException& e) { aInfo = e; }
            catch(const WrappedTargetException& e)
            {
                SQLException aSql;
                if(e.TargetException >>= aSql)
                    aInfo = aSql;
                else
                    SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::OnExpandEntry: something strange happened!");
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }
            if (aInfo.isValid())
                showError(aInfo);
        }
        else
            return false;
                // 0 indicates that an error occurred
    }
    else
    {
        // we have to expand the queries or bookmarks
        if (ensureEntryObject(rParent))
        {
            DBTreeListUserData* pParentData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(rParent));
            Reference< XNameAccess > xCollection( pParentData->xContainer, UNO_QUERY );
            populateTree(xCollection, rParent, etQuery);
        }
    }
    return true;
}
 
bool SbaTableQueryBrowser::ensureEntryObject(const weld::TreeIter& rEntry)
{
    EntryType eType = getEntryType(rEntry);
 
    // the user data of the entry
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    DBTreeListUserData* pEntryData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(rEntry));
    assert(pEntryData && "ensureEntryObject: user data should already be set!");
 
    std::unique_ptr<weld::TreeIter> xDataSourceEntry = m_pTreeView->GetRootLevelParent(&rEntry);
 
    bool bSuccess = false;
    switch (eType)
    {
        case etQueryContainer:
        {
            if ( pEntryData->xContainer.is() )
            {
                // nothing to do
                bSuccess = true;
                break;
            }
 
            std::unique_ptr<weld::TreeIter> xParent(rTreeView.make_iterator(&rEntry));
            if (rTreeView.iter_parent(*xParent))
            {
                if (rTreeView.iter_compare(*xParent, *xDataSourceEntry) != 0)
                {
                    OUString aName(rTreeView.get_text(rEntry));
                    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xParent));
                    try
                    {
                        Reference< XNameAccess > xNameAccess(pData->xContainer,UNO_QUERY);
                        if ( xNameAccess.is() )
                            pEntryData->xContainer.set(xNameAccess->getByName(aName),UNO_QUERY);
                    }
                    catch(const Exception& )
                    {
                        DBG_UNHANDLED_EXCEPTION("dbaccess");
                    }
 
                    bSuccess = pEntryData->xContainer.is();
                }
                else
                {
                    try
                    {
                        Reference< XQueryDefinitionsSupplier > xQuerySup;
                        m_xDatabaseContext->getByName(getDataSourceAccessor(*xDataSourceEntry)) >>= xQuerySup;
                        if (xQuerySup.is())
                        {
                            Reference< XNameAccess > xQueryDefs = xQuerySup->getQueryDefinitions();
                            Reference< XContainer > xCont(xQueryDefs, UNO_QUERY);
                            if (xCont.is())
                                // add as listener to get notified if elements are inserted or removed
                                xCont->addContainerListener(this);
 
                            pEntryData->xContainer = xQueryDefs;
                            bSuccess = pEntryData->xContainer.is();
                        }
                        else {
                            SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::ensureEntryObject: no XQueryDefinitionsSupplier interface!");
                        }
                    }
                    catch( const Exception& )
                    {
                        DBG_UNHANDLED_EXCEPTION("dbaccess");
                    }
                }
            }
            break;
        }
        default:
            SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::ensureEntryObject: ooops ... missing some implementation here!");
            // TODO ...
            break;
    }
    return bSuccess;
}
 
bool SbaTableQueryBrowser::implSelect(const svx::ODataAccessDescriptor& _rDescriptor, bool _bSelectDirect)
{
    // extract the props
    OUString sDataSource;
    OUString sCommand;
    sal_Int32 nCommandType = CommandType::COMMAND;
    bool bEscapeProcessing = true;
    extractDescriptorProps(_rDescriptor, sDataSource, sCommand, nCommandType, bEscapeProcessing);
 
    // select it
    return implSelect( sDataSource, sCommand, nCommandType, bEscapeProcessing, SharedConnection(), _bSelectDirect );
}
 
bool SbaTableQueryBrowser::implLoadAnything(const OUString& _rDataSourceName, const OUString& _rCommand,
    const sal_Int32 nCommandType, const bool _bEscapeProcessing, const SharedConnection& _rxConnection)
{
    try
    {
        Reference<XPropertySet> xProp( getRowSet(), UNO_QUERY_THROW );
        Reference< XLoadable >  xLoadable( xProp, UNO_QUERY_THROW );
        // the values allowing the RowSet to re-execute
        xProp->setPropertyValue(PROPERTY_DATASOURCENAME, Any(_rDataSourceName));
        if(_rxConnection.is())
            xProp->setPropertyValue( PROPERTY_ACTIVE_CONNECTION, Any( _rxConnection.getTyped() ) );
 
            // set this _before_ setting the connection, else the rowset would rebuild it ...
        xProp->setPropertyValue(PROPERTY_COMMAND_TYPE, Any(nCommandType));
        xProp->setPropertyValue(PROPERTY_COMMAND, Any(_rCommand));
        xProp->setPropertyValue(PROPERTY_ESCAPE_PROCESSING, css::uno::Any(_bEscapeProcessing));
        if ( m_bPreview )
        {
            xProp->setPropertyValue(PROPERTY_FETCHDIRECTION, Any(FetchDirection::FORWARD));
        }
 
        // the formatter depends on the data source we're working on, so rebuild it here ...
        initFormatter();
 
        // switch the grid to design mode while loading
        getBrowserView()->getGridControl()->setDesignMode(true);
        InitializeForm( xProp );
 
        bool bSuccess = true;
 
        {
            {
                Reference< XNameContainer >  xColContainer(getFormComponent(), UNO_QUERY);
                // first we have to clear the grid
                clearGridColumns(xColContainer);
            }
            FormErrorHelper aHelper(this);
            // load the form
            bSuccess = reloadForm(xLoadable);
 
            // initialize the model
            InitializeGridModel(getFormComponent());
 
            Any aVal = xProp->getPropertyValue(PROPERTY_ISNEW);
            if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
            {
                // then set the default values and the parameters given from the parent
                Reference< XReset> xReset(xProp, UNO_QUERY);
                xReset->reset();
            }
 
            if ( m_bPreview )
                initializePreviewMode();
 
            LoadFinished(true);
        }
 
        InvalidateAll();
        return bSuccess;
    }
    catch( const SQLException& )
    {
        Any aException( ::cppu::getCaughtException() );
        showError( SQLExceptionInfo( aException ) );
    }
    catch( const WrappedTargetException& e )
    {
        if  ( e.TargetException.isExtractableTo( ::cppu::UnoType< SQLException >::get() ) )
            showError( SQLExceptionInfo( e.TargetException ) );
        else
        {
            TOOLS_WARN_EXCEPTION("dbaccess", "");
        }
    }
    catch(const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    InvalidateAll();
    return false;
}
 
bool SbaTableQueryBrowser::implSelect(const OUString& _rDataSourceName, const OUString& _rCommand,
                                      const sal_Int32 nCommandType, const bool _bEscapeProcessing,
                                      const SharedConnection& _rxConnection,
                                      bool _bSelectDirect)
{
    if (!_rDataSourceName.getLength() || !_rCommand.getLength() || (-1 == nCommandType))
        return false;
 
    std::unique_ptr<weld::TreeIter> xDataSource;
    std::unique_ptr<weld::TreeIter> xCommandType;
    std::unique_ptr<weld::TreeIter> xCommand = getObjectEntry( _rDataSourceName, _rCommand, nCommandType, &xDataSource, &xCommandType, true, _rxConnection );
 
    if (xCommand)
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
 
        bool bSuccess = true;
        if ( _bSelectDirect )
        {
            bSuccess = implSelect(xCommand.get());
        }
        else
        {
            rTreeView.select(*xCommand);
        }
 
        if ( bSuccess )
        {
            rTreeView.scroll_to_row(*xCommand);
            rTreeView.set_cursor(*xCommand);
        }
    }
    else if (!xCommandType)
    {
        if (m_xCurrentlyDisplayed)
        {
            // tell the old entry (if any) it has been deselected
            selectPath(m_xCurrentlyDisplayed.get(), false);
            m_xCurrentlyDisplayed.reset();
        }
 
        // we have a command and need to display this in the rowset
        return implLoadAnything(_rDataSourceName, _rCommand, nCommandType, _bEscapeProcessing, _rxConnection);
    }
    return false;
}
 
IMPL_LINK_NOARG(SbaTableQueryBrowser, OnSelectionChange, LinkParamNone*, void)
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    std::unique_ptr<weld::TreeIter> xSelection(rTreeView.make_iterator());
    if (!rTreeView.get_selected(xSelection.get()))
        xSelection.reset();
    implSelect(xSelection.get());
}
 
std::unique_ptr<weld::TreeIter> SbaTableQueryBrowser::implGetConnectionEntry(const weld::TreeIter& rEntry) const
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    std::unique_ptr<weld::TreeIter> xCurrentEntry(rTreeView.make_iterator(&rEntry));
    DBTreeListUserData* pEntryData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xCurrentEntry));
    while (pEntryData->eType != etDatasource)
    {
        rTreeView.iter_parent(*xCurrentEntry);
        pEntryData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xCurrentEntry));
    }
    return xCurrentEntry;
}
 
bool SbaTableQueryBrowser::implSelect(const weld::TreeIter* pEntry)
{
    if ( !pEntry )
        return false;
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    DBTreeListUserData* pEntryData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*pEntry));
    switch (pEntryData->eType)
    {
        case etTableOrView:
        case etQuery:
            break;
        default:
            // nothing to do
            return false;
    }
 
    OSL_ENSURE(rTreeView.get_iter_depth(*pEntry) >= 2, "SbaTableQueryBrowser::implSelect: invalid entry!");
 
    // get the entry for the tables or queries
    std::unique_ptr<weld::TreeIter> xContainer = rTreeView.make_iterator(pEntry);
    rTreeView.iter_parent(*xContainer);
    DBTreeListUserData* pContainerData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xContainer));
 
    // get the entry for the datasource
    std::unique_ptr<weld::TreeIter> xConnection = implGetConnectionEntry(*xContainer);
    DBTreeListUserData* pConData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xConnection));
 
    // reinitialize the rowset
    // but first check if it is necessary
    // get all old properties
    Reference<XPropertySet> xRowSetProps(getRowSet(),UNO_QUERY);
    OUString aOldName;
    xRowSetProps->getPropertyValue(PROPERTY_COMMAND) >>= aOldName;
    sal_Int32 nOldType = 0;
    xRowSetProps->getPropertyValue(PROPERTY_COMMAND_TYPE) >>= nOldType;
    Reference<XConnection> xOldConnection(xRowSetProps->getPropertyValue(PROPERTY_ACTIVE_CONNECTION),UNO_QUERY);
 
    // the name of the table or query
    const OUString sSimpleName = rTreeView.get_text(*pEntry);
    OUStringBuffer sNameBuffer(sSimpleName);
    if ( etQueryContainer == pContainerData->eType )
    {
        std::unique_ptr<weld::TreeIter> xTemp = rTreeView.make_iterator(xContainer.get());
        std::unique_ptr<weld::TreeIter> xNextTemp = rTreeView.make_iterator(xTemp.get());
        if (rTreeView.iter_parent(*xNextTemp))
        {
            while (rTreeView.iter_compare(*xNextTemp, *xConnection) != 0)
            {
                sNameBuffer.insert(0, rTreeView.get_text(*xTemp) + "/");
                rTreeView.copy_iterator(*xNextTemp, *xTemp);
                if (!rTreeView.iter_parent(*xNextTemp))
                    break;
            }
        }
    }
    OUString aName = sNameBuffer.makeStringAndClear();
 
    sal_Int32 nCommandType =    ( etTableContainer == pContainerData->eType)
                            ?   CommandType::TABLE
                            :   CommandType::QUERY;
 
    // check if need to rebuild the rowset
    bool bRebuild = ( xOldConnection != pConData->xConnection )
                     || ( nOldType != nCommandType )
                     || ( aName != aOldName );
 
    Reference< css::form::XLoadable >  xLoadable = getLoadable();
    bRebuild |= !xLoadable->isLoaded();
    bool bSuccess = true;
    if ( bRebuild )
    {
        try
        {
            weld::WaitObject aWaitCursor(getFrameWeld());
 
            // tell the old entry it has been deselected
            selectPath(m_xCurrentlyDisplayed.get(), false);
            m_xCurrentlyDisplayed.reset();
 
            // not really loaded
            m_xCurrentlyDisplayed = rTreeView.make_iterator(pEntry);
            // tell the new entry it has been selected
            selectPath(m_xCurrentlyDisplayed.get());
 
            // get the name of the data source currently selected
            (void)ensureConnection(m_xCurrentlyDisplayed.get(), pConData->xConnection);
 
            if ( !pConData->xConnection.is() )
            {
                unloadAndCleanup( false );
                return false;
            }
 
            Reference<XNameAccess> xNameAccess;
            switch(nCommandType)
            {
                case CommandType::TABLE:
                    {
                        // only for tables
                        if ( !pContainerData->xContainer.is() )
                        {
                            Reference<XTablesSupplier> xSup( pConData->xConnection, UNO_QUERY );
                            if(xSup.is())
                                xNameAccess = xSup->getTables();
 
                            pContainerData->xContainer = xNameAccess;
                        }
                        else
                            xNameAccess.set( pContainerData->xContainer, UNO_QUERY );
                    }
                    break;
                case CommandType::QUERY:
                    {
                        if ( pContainerData->xContainer.is() )
                            xNameAccess.set( pContainerData->xContainer, UNO_QUERY );
                        else
                        {
                            Reference<XQueriesSupplier> xSup( pConData->xConnection, UNO_QUERY );
                            if(xSup.is())
                                xNameAccess = xSup->getQueries();
                        }
                    }
                    break;
            }
            OUString sStatus(DBA_RES(CommandType::TABLE == nCommandType ? STR_LOADING_TABLE : STR_LOADING_QUERY));
            sStatus = sStatus.replaceFirst("$name$", aName);
            BrowserViewStatusDisplay aShowStatus(static_cast<UnoDataBrowserView*>(getView()), sStatus);
 
            bool bEscapeProcessing = true;
            if(xNameAccess.is() && xNameAccess->hasByName(sSimpleName))
            {
                DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*pEntry));
                if ( !pData->xObjectProperties.is() )
                {
                    Reference<XInterface> xObject;
                    if(xNameAccess->getByName(sSimpleName) >>= xObject) // remember the table or query object
                    {
                        pData->xObjectProperties.set(xObject, css::uno::UNO_QUERY);
                        // if the query contains a parameterized statement and preview is enabled we won't get any data.
                        if ( nCommandType == CommandType::QUERY && xObject.is() )
                        {
                            Reference<XPropertySet> xObjectProps(xObject,UNO_QUERY);
                            xObjectProps->getPropertyValue(PROPERTY_ESCAPE_PROCESSING) >>= bEscapeProcessing;
                            if ( m_bPreview )
                            {
                                OUString sSql;
                                xObjectProps->getPropertyValue(PROPERTY_COMMAND) >>= sSql;
                                Reference< XMultiServiceFactory >  xFactory( pConData->xConnection, UNO_QUERY );
                                if (xFactory.is())
                                {
                                    try
                                    {
                                        Reference<XSingleSelectQueryAnalyzer> xAnalyzer(xFactory->createInstance(SERVICE_NAME_SINGLESELECTQUERYCOMPOSER),UNO_QUERY);
                                        if ( xAnalyzer.is() )
                                        {
                                            xAnalyzer->setQuery(sSql);
                                            Reference<XParametersSupplier> xParSup(xAnalyzer,UNO_QUERY);
                                            if ( xParSup->getParameters()->getCount() > 0 )
                                            {
                                                OUString sFilter = " WHERE " + xAnalyzer->getFilter();
                                                OUString sReplace = sSql.replaceFirst(sFilter, "");
                                                xAnalyzer->setQuery(sReplace);
                                                Reference<XSingleSelectQueryComposer> xComposer(xAnalyzer,UNO_QUERY);
                                                xComposer->setFilter(u"0=1"_ustr);
                                                aName = xAnalyzer->getQuery();
                                                nCommandType = CommandType::COMMAND;
                                            }
                                        }
                                    }
                                    catch (Exception&)
                                    {
                                        DBG_UNHANDLED_EXCEPTION("dbaccess");
                                    }
                                }
                            }
                        }
                    }
                }
            }
 
            OUString sDataSourceName(getDataSourceAccessor(*xConnection));
            bSuccess = implLoadAnything( sDataSourceName, aName, nCommandType, bEscapeProcessing, pConData->xConnection );
            if ( !bSuccess )
            {   // clean up
                criticalFail();
            }
        }
        catch(const SQLException& e)
        {
            showError(SQLExceptionInfo(e));
            // reset the values
            xRowSetProps->setPropertyValue(PROPERTY_DATASOURCENAME,Any());
            xRowSetProps->setPropertyValue(PROPERTY_ACTIVE_CONNECTION,Any());
            bSuccess = false;
        }
        catch(WrappedTargetException& e)
        {
            SQLException aSql;
            if(e.TargetException >>= aSql)
                showError(SQLExceptionInfo(aSql));
            else
                SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::implSelect: something strange happened!");
            // reset the values
            xRowSetProps->setPropertyValue(PROPERTY_DATASOURCENAME,Any());
            xRowSetProps->setPropertyValue(PROPERTY_ACTIVE_CONNECTION,Any());
            bSuccess = false;
        }
        catch(const Exception&)
        {
            // reset the values
            xRowSetProps->setPropertyValue(PROPERTY_DATASOURCENAME,Any());
            xRowSetProps->setPropertyValue(PROPERTY_ACTIVE_CONNECTION,Any());
            bSuccess = false;
        }
    }
    return bSuccess;
}
 
std::unique_ptr<weld::TreeIter> SbaTableQueryBrowser::getEntryFromContainer(const Reference<XNameAccess>& rxNameAccess)
{
    std::unique_ptr<weld::TreeIter> xContainer;
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    std::unique_ptr<weld::TreeIter> xDSLoop(rTreeView.make_iterator(xContainer.get()));
    if (rTreeView.get_iter_first(*xDSLoop))
    {
        do
        {
            xContainer = rTreeView.make_iterator(xDSLoop.get());
            if (rTreeView.iter_children(*xContainer))
            {
                // 1st child is queries
                DBTreeListUserData* pQueriesData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xContainer));
                if (pQueriesData && pQueriesData->xContainer == rxNameAccess)
                    break;
 
                if (rTreeView.iter_next_sibling(*xContainer))
                {
                    // 2nd child is tables
                    DBTreeListUserData* pTablesData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xContainer));
                    if (pTablesData && pTablesData->xContainer == rxNameAccess)
                        break;
                }
            }
            xContainer.reset();
        }
        while (rTreeView.iter_next_sibling(*xDSLoop));
    }
 
    return xContainer;
}
 
void SAL_CALL SbaTableQueryBrowser::elementInserted(const ContainerEvent& rEvent)
{
    SolarMutexGuard aSolarGuard;
 
    Reference< XNameAccess > xNames(rEvent.Source, UNO_QUERY);
    // first search for a definition container where we can insert this element
 
    std::unique_ptr<weld::TreeIter> xEntry = getEntryFromContainer(xNames);
    if (xEntry)  // found one
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        rTreeView.make_unsorted();
 
        // insert the new entry into the tree
        DBTreeListUserData* pContainerData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xEntry));
        assert(pContainerData && "elementInserted: There must be user data for this type!");
 
        DBTreeListUserData* pNewData = new DBTreeListUserData;
        bool bIsTable = etTableContainer == pContainerData->eType;
        if ( bIsTable )
        {
            rEvent.Element >>= pNewData->xObjectProperties;// remember the new element
            pNewData->eType = etTableOrView;
        }
        else
        {
            if (rTreeView.iter_n_children(*xEntry) < xNames->getElementNames().getLength() - 1)
            {
                // the item inserts its children on demand, but it has not been expanded yet. So ensure here and
                // now that it has all items
                populateTree(xNames, *xEntry, etQuery);
            }
            pNewData->eType = etQuery;
        }
        implAppendEntry(xEntry.get(), ::comphelper::getString(rEvent.Accessor), pNewData);
 
        rTreeView.make_sorted();
    }
    else
        SbaXDataBrowserController::elementInserted(rEvent);
}
 
bool SbaTableQueryBrowser::isCurrentlyDisplayedChanged(std::u16string_view rName, const weld::TreeIter& rContainer)
{
    if (!m_xCurrentlyDisplayed)
        return false;
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    if (getEntryType(*m_xCurrentlyDisplayed) != getChildType(rContainer))
        return false;
    if (rTreeView.get_text(*m_xCurrentlyDisplayed) != rName)
        return false;
    std::unique_ptr<weld::TreeIter> xParent = rTreeView.make_iterator(m_xCurrentlyDisplayed.get());
    return rTreeView.iter_parent(*xParent) && rTreeView.iter_compare(*xParent, rContainer) == 0;
}
 
void SAL_CALL SbaTableQueryBrowser::elementRemoved( const ContainerEvent& _rEvent )
{
    SolarMutexGuard aSolarGuard;
 
    Reference< XNameAccess > xNames(_rEvent.Source, UNO_QUERY);
    // get the top-level representing the removed data source
    // and search for the queries and tables
    std::unique_ptr<weld::TreeIter> xContainer = getEntryFromContainer(xNames);
    if (xContainer)
    {
        // a query or table has been removed
        OUString aName = ::comphelper::getString(_rEvent.Accessor);
 
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        if (isCurrentlyDisplayedChanged(aName, *xContainer))
        {
            // the element displayed currently has been replaced
 
            // we need to remember the old value
            std::unique_ptr<weld::TreeIter> xTemp = rTreeView.make_iterator(m_xCurrentlyDisplayed.get());
 
            // unload
            unloadAndCleanup( false ); // don't dispose the connection
 
            DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xTemp));
            rTreeView.set_id(*xTemp, OUString());
            delete pData; // the data could be null because we have a table which isn't correct
            rTreeView.remove(*xTemp);
        }
        else
        {
            // remove the entry from the model
            std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator(xContainer.get()));
            if (rTreeView.get_iter_first(*xChild))
            {
                do
                {
                    if (rTreeView.get_text(*xChild) == aName)
                    {
                        DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xChild));
                        rTreeView.set_id(*xChild, OUString());
                        delete pData;
                        rTreeView.remove(*xChild);
                        break;
                    }
                } while (rTreeView.iter_next_sibling(*xChild));
            }
        }
 
        // maybe the object which is part of the document data source has been removed
        checkDocumentDataSource();
    }
    else
        SbaXDataBrowserController::elementRemoved(_rEvent);
}
 
void SAL_CALL SbaTableQueryBrowser::elementReplaced( const ContainerEvent& _rEvent )
{
    SolarMutexGuard aSolarGuard;
 
    Reference< XNameAccess > xNames(_rEvent.Source, UNO_QUERY);
    std::unique_ptr<weld::TreeIter> xContainer = getEntryFromContainer(xNames);
    if (xContainer)
    {
        // a table or query has been replaced
        OUString aName = ::comphelper::getString(_rEvent.Accessor);
 
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        if (isCurrentlyDisplayedChanged(aName, *xContainer))
        {   // the element displayed currently has been replaced
 
            // we need to remember the old value
            std::unique_ptr<weld::TreeIter> xTemp = rTreeView.make_iterator(m_xCurrentlyDisplayed.get());
            unloadAndCleanup( false ); // don't dispose the connection
 
            DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xTemp));
            if (pData)
            {
                if ( etTableOrView == pData->eType )
                {
                    // only insert userdata when we have a table because the query is only a commanddefinition object and not a query
                    _rEvent.Element >>= pData->xObjectProperties;  // remember the new element
                }
                else
                {
                    rTreeView.set_id(*xTemp, OUString());
                    delete pData;
                }
            }
        }
        else
        {
            // find the entry for this name
            std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator(xContainer.get()));
            if (rTreeView.get_iter_first(*xChild))
            {
                do
                {
                    if (rTreeView.get_text(*xChild) == aName)
                    {
                        DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xChild));
                        if (pData)
                        {
                            if ( etTableOrView == pData->eType )
                            {
                                // only insert userdata when we have a table because the query is only a commanddefinition object and not a query
                                _rEvent.Element >>= pData->xObjectProperties;   // remember the new element
                            }
                            else
                            {
                                rTreeView.set_id(*xChild, OUString());
                                delete pData;
                            }
                        }
                        break;
                    }
                } while (rTreeView.iter_next_sibling(*xChild));
            }
        }
 
        // maybe the object which is part of the document data source has been removed
        checkDocumentDataSource();
    }
    else if (xNames.get() == m_xDatabaseContext.get())
    {   // a datasource has been replaced in the context
        SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::elementReplaced: no support for replaced data sources!");
            // very suspicious: the database context should not allow to replace data source, only to register
            // and revoke them
    }
    else
        SbaXDataBrowserController::elementReplaced(_rEvent);
}
 
void SbaTableQueryBrowser::impl_releaseConnection( SharedConnection& _rxConnection )
{
    // remove as event listener
    Reference< XComponent > xComponent( _rxConnection, UNO_QUERY );
    if ( xComponent.is() )
    {
        Reference< XEventListener > xListener( static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY );
        xComponent->removeEventListener( xListener );
    }
 
    try
    {
        // temporary (hopefully!) hack for #i55274#
        Reference< XFlushable > xFlush( _rxConnection, UNO_QUERY );
        if ( xFlush.is() )
            xFlush->flush();
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    // clear
    _rxConnection.clear();
        // will implicitly dispose if we have the ownership, since xConnection is a SharedConnection
}
 
void SbaTableQueryBrowser::disposeConnection(const weld::TreeIter* pDSEntry)
{
    OSL_ENSURE( pDSEntry, "SbaTableQueryBrowser::disposeConnection: invalid entry (NULL)!" );
    OSL_ENSURE( impl_isDataSourceEntry( pDSEntry ), "SbaTableQueryBrowser::disposeConnection: invalid entry (not top-level)!" );
 
    if (pDSEntry)
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        DBTreeListUserData* pTreeListData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*pDSEntry));
        if (pTreeListData)
            impl_releaseConnection(pTreeListData->xConnection);
    }
}
 
void SbaTableQueryBrowser::closeConnection(const weld::TreeIter& rDSEntry, bool _bDisposeConnection)
{
    OSL_ENSURE(impl_isDataSourceEntry(&rDSEntry), "SbaTableQueryBrowser::closeConnection: invalid entry (not top-level)!");
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
 
    // if one of the entries of the given DS is displayed currently, unload the form
    if (m_xCurrentlyDisplayed)
    {
        std::unique_ptr<weld::TreeIter> xRoot = m_pTreeView->GetRootLevelParent(m_xCurrentlyDisplayed.get());
        if (rTreeView.iter_compare(*xRoot, rDSEntry) == 0)
            unloadAndCleanup(_bDisposeConnection);
    }
 
    // collapse the query/table container
    std::unique_ptr<weld::TreeIter> xContainers(rTreeView.make_iterator(&rDSEntry));
    if (rTreeView.iter_children(*xContainers))
    {
        do
        {
            std::unique_ptr<weld::TreeIter> xElements(rTreeView.make_iterator(xContainers.get()));
            if (rTreeView.iter_children(*xElements))
            {
                rTreeView.collapse_row(*xContainers);
                // and delete their children (they are connection-relative)
                bool bElements = true;
                while (bElements)
                {
                    std::unique_ptr<weld::TreeIter> xRemove(rTreeView.make_iterator(xElements.get()));
                    bElements = rTreeView.iter_next_sibling(*xElements);
                    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xRemove));
                    rTreeView.set_id(*xRemove, OUString());
                    delete pData;
                    rTreeView.remove(*xRemove);
                }
            }
        }
        while (rTreeView.iter_next_sibling(*xContainers));
    }
 
    // collapse the entry itself
    rTreeView.collapse_row(rDSEntry);
 
    // dispose/reset the connection
    if ( _bDisposeConnection )
        disposeConnection(&rDSEntry);
}
 
void SbaTableQueryBrowser::unloadAndCleanup( bool _bDisposeConnection )
{
    if (!m_xCurrentlyDisplayed)
        // nothing to do
        return;
 
    std::unique_ptr<weld::TreeIter> xDSEntry = m_pTreeView->GetRootLevelParent(m_xCurrentlyDisplayed.get());
 
    // de-select the path for the currently displayed table/query
    selectPath(m_xCurrentlyDisplayed.get(), false);
    m_xCurrentlyDisplayed.reset();
 
    try
    {
        // get the active connection. We need to dispose it.
 
        // unload the form
        Reference< XLoadable > xLoadable = getLoadable();
        if (xLoadable->isLoaded())
            xLoadable->unload();
 
        // clear the grid control
        Reference< XNameContainer > xConta(getControlModel(),UNO_QUERY);
        clearGridColumns(xConta);
 
        // dispose the connection
        if(_bDisposeConnection)
            disposeConnection(xDSEntry.get());
    }
    catch(SQLException& e)
    {
        showError(SQLExceptionInfo(e));
    }
    catch(WrappedTargetException& e)
    {
        SQLException aSql;
        if(e.TargetException >>= aSql)
            showError(SQLExceptionInfo(aSql));
        else
            SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::unloadAndCleanup: something strange happened!");
    }
    catch(const Exception&)
    {
        SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::unloadAndCleanup: could not reset the form");
    }
}
 
namespace
{
    Reference< XInterface > lcl_getDataSource( const Reference< XDatabaseContext >& _rxDatabaseContext,
        const OUString& _rDataSourceName, const Reference< XConnection >& _rxConnection )
    {
        Reference< XDataSource > xDataSource;
        try
        {
            if ( !_rDataSourceName.isEmpty() && _rxDatabaseContext->hasByName( _rDataSourceName ) )
                xDataSource.set( _rxDatabaseContext->getByName( _rDataSourceName ), UNO_QUERY_THROW );
 
            if ( !xDataSource.is() )
            {
                Reference< XChild > xConnAsChild( _rxConnection, UNO_QUERY );
                if ( xConnAsChild.is() )
                    xDataSource.set( xConnAsChild->getParent(), UNO_QUERY_THROW );
            }
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
        return xDataSource;
    }
}
 
void SbaTableQueryBrowser::impl_initialize(const ::comphelper::NamedValueCollection& rArguments)
{
    SolarMutexGuard aGuard;
        // doin' a lot of VCL stuff here -> lock the SolarMutex
 
    // first initialize the parent
    SbaXDataBrowserController::impl_initialize(rArguments);
 
    Reference<XConnection> xForeignConnection;
    Reference< XFrame > xFrame;
 
    OUString aTableName, aCatalogName, aSchemaName;
 
    bool bEscapeProcessing = true;
    sal_Int32 nInitialDisplayCommandType = CommandType::COMMAND;
    OUString sInitialDataSourceName;
    OUString sInitialCommand;
 
    rArguments.get_ensureType( PROPERTY_DATASOURCENAME, sInitialDataSourceName );
    rArguments.get_ensureType( PROPERTY_COMMAND_TYPE, nInitialDisplayCommandType );
    rArguments.get_ensureType( PROPERTY_COMMAND, sInitialCommand );
    rArguments.get_ensureType( PROPERTY_ACTIVE_CONNECTION, xForeignConnection );
    rArguments.get_ensureType( PROPERTY_UPDATE_CATALOGNAME, aCatalogName );
    rArguments.get_ensureType( PROPERTY_UPDATE_SCHEMANAME, aSchemaName );
    rArguments.get_ensureType( PROPERTY_UPDATE_TABLENAME, aTableName );
    rArguments.get_ensureType( PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing );
    rArguments.get_ensureType( u"Frame"_ustr, xFrame );
    rArguments.get_ensureType( PROPERTY_SHOWMENU, m_bShowMenu );
 
    // disable the browser if either of ShowTreeViewButton (compatibility name) or EnableBrowser
    // is present and set to FALSE
    bool bDisableBrowser =  !rArguments.getOrDefault( u"ShowTreeViewButton"_ustr, true )   // compatibility name
                            ||  !rArguments.getOrDefault( PROPERTY_ENABLE_BROWSER, true );
    OSL_ENSURE( !rArguments.has( u"ShowTreeViewButton"_ustr ),
        "SbaTableQueryBrowser::impl_initialize: ShowTreeViewButton is superseded by EnableBrowser!" );
    m_bEnableBrowser = !bDisableBrowser;
 
    // hide the tree view it is disabled in general, or if the settings tell to hide it initially
    bool bHideTreeView =    ( !m_bEnableBrowser )
                            ||  !rArguments.getOrDefault( u"ShowTreeView"_ustr, true )  // compatibility name
                            ||  !rArguments.getOrDefault( PROPERTY_SHOW_BROWSER, true );
    OSL_ENSURE( !rArguments.has( u"ShowTreeView"_ustr ),
        "SbaTableQueryBrowser::impl_initialize: ShowTreeView is superseded by ShowBrowser!" );
 
    if ( bHideTreeView )
        hideExplorer();
    else
        showExplorer();
 
    if ( m_bPreview )
    {
        try
        {
            Sequence< OUString> aProperties
            {
                u"AlwaysShowCursor"_ustr, PROPERTY_BORDER, u"HasNavigationBar"_ustr, u"HasRecordMarker"_ustr, u"Tabstop"_ustr
            };
            Sequence< Any> aValues
            {
                Any(false), Any(sal_Int16(0)), Any(false), Any(false), Any(false)
            };
            Reference< XMultiPropertySet >  xFormMultiSet(getFormComponent(), UNO_QUERY);
            if ( xFormMultiSet.is() )
                xFormMultiSet->setPropertyValues(aProperties, aValues);
        }
        catch(const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
    }
 
    // are we loaded into a (sub)frame of an embedded document (i.e. a form belonging to a database
    // document)?
    bool bSubFrameOfEmbeddedDocument = false;
    if ( xFrame.is() )
    {
        Reference<XFramesSupplier> xSup = xFrame->getCreator();
        Reference<XController> xCont = xSup.is() ? xSup->getController() : Reference<XController>();
 
        bSubFrameOfEmbeddedDocument = xCont.is() && ::dbtools::isEmbeddedInDatabase( xCont->getModel(), xForeignConnection );
    }
 
    // if we have a connection at this point, it was either passed from outside, our
    // determined from an outer DB document. In both cases, do not dispose it later on.
    SharedConnection xConnection( xForeignConnection, SharedConnection::NoTakeOwnership );
 
    // should we display all registered databases in the left hand side tree?
    // or only *one* special?
    bool bLimitedTreeEntries = false;
    // if we're part of a frame which is a secondary frame of a database document, then only
    // display the database for this document, not all registered ones
    bLimitedTreeEntries |= bSubFrameOfEmbeddedDocument;
    // if the tree view is not to be displayed at all, then only display the data source
    // which was given as initial selection
    bLimitedTreeEntries |= !m_bEnableBrowser;
 
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    rTreeView.make_unsorted();
 
    if ( bLimitedTreeEntries )
    {
        if ( xConnection.is() )
        {
            startConnectionListening( xConnection );
 
            // if no initial name was given, try to obtain one from the data source
            if ( sInitialDataSourceName.isEmpty() )
            {
                Reference< XChild > xChild( xConnection, UNO_QUERY );
                Reference< XPropertySet > xDataSourceProperties;
                if ( xChild.is() )
                    xDataSourceProperties.set(xChild->getParent(), css::uno::UNO_QUERY);
                if ( xDataSourceProperties.is() )
                {
                    try
                    {
                        OSL_VERIFY( xDataSourceProperties->getPropertyValue( PROPERTY_NAME ) >>= sInitialDataSourceName );
                    }
                    catch( const Exception& )
                    {
                        SAL_WARN("dbaccess.ui",  "SbaTableQueryBrowser::impl_initialize: a connection parent which does not have a 'Name'!??" );
                    }
                }
            }
        }
 
        implAddDatasource( sInitialDataSourceName, xConnection );
 
        std::unique_ptr<weld::TreeIter> xFirst(rTreeView.make_iterator());
        if (rTreeView.get_iter_first(*xFirst))
            rTreeView.expand_row(*xFirst);
    }
    else
        initializeTreeModel();
 
    rTreeView.make_sorted();
 
    if ( m_bEnableBrowser )
    {
        m_aDocScriptSupport = ::std::optional< bool >( false );
    }
    else
    {
        // we are not used as "browser", but as mere view for a single table/query/command. In particular,
        // there is a specific database document which we belong to.
        Reference< XOfficeDatabaseDocument > xDocument( getDataSourceOrModel(
            lcl_getDataSource( m_xDatabaseContext, sInitialDataSourceName, xConnection ) ), UNO_QUERY );
        m_aDocScriptSupport = ::std::optional< bool >( Reference< XEmbeddedScripts >( xDocument, UNO_QUERY ).is() );
    }
 
    if ( implSelect( sInitialDataSourceName, sInitialCommand, nInitialDisplayCommandType, bEscapeProcessing, xConnection, true ) )
    {
        try
        {
            Reference< XPropertySet > xRowSetProps(getRowSet(), UNO_QUERY);
            xRowSetProps->setPropertyValue(PROPERTY_UPDATE_CATALOGNAME,Any(aCatalogName));
            xRowSetProps->setPropertyValue(PROPERTY_UPDATE_SCHEMANAME,Any(aSchemaName));
            xRowSetProps->setPropertyValue(PROPERTY_UPDATE_TABLENAME,Any(aTableName));
 
        }
        catch(const Exception&)
        {
            SAL_WARN("dbaccess.ui", "SbaTableQueryBrowser::impl_initialize: could not set the update related names!");
        }
    }
 
    InvalidateAll();
}
 
bool SbaTableQueryBrowser::haveExplorer() const
{
    return m_pTreeView && m_pTreeView->IsVisible();
}
 
void SbaTableQueryBrowser::hideExplorer()
{
    if (!haveExplorer())
        return;
    if (!getBrowserView())
        return;
 
    m_pTreeView->Hide();
    m_pSplitter->Hide();
    getBrowserView()->Resize();
 
    InvalidateFeature(ID_BROWSER_EXPLORER);
}
 
void SbaTableQueryBrowser::showExplorer()
{
    if (haveExplorer())
        return;
 
    if (!getBrowserView())
        return;
 
    m_pTreeView->Show();
    m_pSplitter->Show();
    getBrowserView()->Resize();
 
    InvalidateFeature(ID_BROWSER_EXPLORER);
}
 
bool SbaTableQueryBrowser::ensureConnection(const weld::TreeIter* pAnyEntry, SharedConnection& rConnection)
{
    std::unique_ptr<weld::TreeIter> xDSEntry = m_pTreeView->GetRootLevelParent(pAnyEntry);
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    DBTreeListUserData* pDSData =
                xDSEntry
            ?   weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xDSEntry))
            :   nullptr;
 
    return ensureConnection(xDSEntry.get(), pDSData, rConnection);
}
 
std::unique_ptr< ImageProvider > SbaTableQueryBrowser::getImageProviderFor(const weld::TreeIter* pAnyEntry)
{
    std::unique_ptr<ImageProvider> xImageProvider(new ImageProvider);
    SharedConnection xConnection;
    if (getExistentConnectionFor(pAnyEntry, xConnection))
        xImageProvider.reset(new ImageProvider(xConnection));
    return xImageProvider;
}
 
bool SbaTableQueryBrowser::getExistentConnectionFor(const weld::TreeIter* pAnyEntry, SharedConnection& rConnection)
{
    std::unique_ptr<weld::TreeIter> xDSEntry = m_pTreeView->GetRootLevelParent(pAnyEntry);
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    DBTreeListUserData* pDSData =
                xDSEntry
            ?   weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xDSEntry))
            :   nullptr;
    if (pDSData)
        rConnection = pDSData->xConnection;
    return rConnection.is();
}
 
bool SbaTableQueryBrowser::impl_isDataSourceEntry(const weld::TreeIter* pEntry) const
{
    if (!pEntry)
        return false;
    std::unique_ptr<weld::TreeIter> xRoot(m_pTreeView->GetRootLevelParent(pEntry));
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    return rTreeView.iter_compare(*xRoot, *pEntry) == 0;
}
 
bool SbaTableQueryBrowser::ensureConnection(const weld::TreeIter* pDSEntry, void* pDSData, SharedConnection& rConnection)
{
    OSL_ENSURE( impl_isDataSourceEntry( pDSEntry ), "SbaTableQueryBrowser::ensureConnection: this entry does not denote a data source!" );
    if (pDSEntry)
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        OUString aDSName = rTreeView.get_text(*pDSEntry);
 
        DBTreeListUserData* pTreeListData = static_cast<DBTreeListUserData*>(pDSData);
        if ( pTreeListData )
            rConnection = pTreeListData->xConnection;
 
        if ( !rConnection.is() && pTreeListData )
        {
            // show the "connecting to ..." status
            OUString sConnecting(DBA_RES(STR_CONNECTING_DATASOURCE));
            sConnecting = sConnecting.replaceFirst("$name$", aDSName);
            BrowserViewStatusDisplay aShowStatus(static_cast<UnoDataBrowserView*>(getView()), sConnecting);
 
            // build a string showing context information in case of error
            OUString sConnectingContext(DBA_RES(STR_COULDNOTCONNECT_DATASOURCE));
            sConnectingContext = sConnectingContext.replaceFirst("$name$", aDSName);
 
            // connect
            rConnection.reset(
                connect(getDataSourceAccessor(*pDSEntry), sConnectingContext, nullptr),
                SharedConnection::TakeOwnership);
 
            // remember the connection
            pTreeListData->xConnection = rConnection;
        }
    }
    return rConnection.is();
}
 
int SbaTableQueryBrowser::OnTreeEntryCompare(const weld::TreeIter& rLHS, const weld::TreeIter& rRHS)
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
 
    // we want the table entry and the end so we have to do a check
    if (isContainer(rRHS))
    {
        // don't use getEntryType (directly or indirectly) for the LHS:
        // LHS is currently being inserted, so it is not "completely valid" at the moment
 
        const EntryType eRight = getEntryType(rRHS);
        if (etTableContainer == eRight)
            // every other container should be placed _before_ the bookmark container
            return -1;
 
        const OUString sLeft = rTreeView.get_text(rLHS);
 
        EntryType eLeft = etTableContainer;
        if (DBA_RES(RID_STR_TABLES_CONTAINER) == sLeft)
            eLeft = etTableContainer;
        else if (DBA_RES(RID_STR_QUERIES_CONTAINER) == sLeft)
            eLeft = etQueryContainer;
 
        if ( eLeft == eRight )
            return 0;
 
        if ( ( eLeft == etTableContainer ) && ( eRight == etQueryContainer ) )
            return 1;
 
        if ( ( eLeft == etQueryContainer ) && ( eRight == etTableContainer ) )
            return -1;
 
        SAL_WARN("dbaccess.ui",  "SbaTableQueryBrowser::OnTreeEntryCompare: unexpected case!" );
        return 0;
    }
 
    OUString sLeftText = rTreeView.get_text(rLHS);
    OUString sRightText = rTreeView.get_text(rRHS);
 
    sal_Int32 nCompareResult = 0;   // equal by default
 
    if (m_xCollator.is())
    {
        try
        {
            nCompareResult = m_xCollator->compareString(sLeftText, sRightText);
        }
        catch(const Exception&)
        {
        }
    }
    else
        // default behaviour if we do not have a collator -> do the simple string compare
        nCompareResult = sLeftText.compareTo(sRightText);
 
    return nCompareResult;
}
 
void SbaTableQueryBrowser::implAdministrate(const weld::TreeIter& rApplyTo)
{
    try
    {
        // get the desktop object
        Reference< XDesktop2 > xFrameLoader = Desktop::create( getORB() );
 
        // the initial selection
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        std::unique_ptr<weld::TreeIter> xTopLevelSelected(rTreeView.make_iterator(&rApplyTo));
 
        while (rTreeView.get_iter_depth(*xTopLevelSelected))
            rTreeView.iter_parent(*xTopLevelSelected);
 
        OUString sInitialSelection = getDataSourceAccessor(*xTopLevelSelected);
 
        Reference< XDataSource > xDataSource( getDataSourceByName( sInitialSelection, getFrameWeld(), getORB(), nullptr ) );
        Reference< XModel > xDocumentModel( getDataSourceOrModel( xDataSource ), UNO_QUERY );
 
        if ( xDocumentModel.is() )
        {
            Reference< XInteractionHandler2 > xInteractionHandler(
                InteractionHandler::createWithParent(getORB(), nullptr) );
 
            ::comphelper::NamedValueCollection aLoadArgs;
            aLoadArgs.put( u"Model"_ustr, xDocumentModel );
            aLoadArgs.put( u"InteractionHandler"_ustr, xInteractionHandler );
            aLoadArgs.put( u"MacroExecutionMode"_ustr, MacroExecMode::USE_CONFIG );
 
            Sequence< PropertyValue > aLoadArgPV;
            aLoadArgs >>= aLoadArgPV;
 
            xFrameLoader->loadComponentFromURL(
                xDocumentModel->getURL(),
                u"_default"_ustr,
                FrameSearchFlag::ALL | FrameSearchFlag::GLOBAL,
                aLoadArgPV
            );
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}
 
bool SbaTableQueryBrowser::requestQuickHelp(const void* pUserData, OUString& rText) const
{
    const DBTreeListUserData* pData = static_cast<const DBTreeListUserData*>(pUserData);
    if (pData->eType == etDatasource && !pData->sAccessor.isEmpty())
    {
        rText = ::svt::OFileNotation(pData->sAccessor).get( ::svt::OFileNotation::N_SYSTEM);
        return true;
    }
    return false;
}
 
OUString SbaTableQueryBrowser::getContextMenuResourceName() const
{
    return u"explorer"_ustr;
}
 
IController& SbaTableQueryBrowser::getCommandController()
{
    return *this;
}
 
::comphelper::OInterfaceContainerHelper2* SbaTableQueryBrowser::getContextMenuInterceptors()
{
    return &m_aContextMenuInterceptors;
}
 
Any SbaTableQueryBrowser::getCurrentSelection(weld::TreeView& rControl) const
{
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
 
    OSL_PRECOND( &rTreeView == &rControl,
        "SbaTableQueryBrowser::getCurrentSelection: where does this come from?" );
 
    if (&rTreeView != &rControl)
        return Any();
 
    std::unique_ptr<weld::TreeIter> xSelected(rTreeView.make_iterator());
    if (!rTreeView.get_selected(xSelected.get()))
        return Any();
 
    NamedDatabaseObject aSelectedObject;
    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xSelected));
    aSelectedObject.Type = static_cast< sal_Int32 >( pData->eType );
 
    switch ( aSelectedObject.Type )
    {
    case DatabaseObject::QUERY:
    case DatabaseObject::TABLE:
        aSelectedObject.Name = rTreeView.get_text(*xSelected);
        break;
 
    case DatabaseObjectContainer::DATA_SOURCE:
    case DatabaseObjectContainer::QUERIES:
    case DatabaseObjectContainer::TABLES:
        aSelectedObject.Name = getDataSourceAccessor(*xSelected);
        break;
 
    default:
        SAL_WARN("dbaccess.ui",  "SbaTableQueryBrowser::getCurrentSelection: invalid (unexpected) object type!" );
        break;
    }
 
    return Any( aSelectedObject );
}
 
vcl::Window* SbaTableQueryBrowser::getMenuParent() const
{
    return m_pTreeView;
}
 
void SbaTableQueryBrowser::adjustMenuPosition(const weld::TreeView&, ::Point&) const
{
}
 
bool SbaTableQueryBrowser::implGetQuerySignature( OUString& _rCommand, bool& _bEscapeProcessing )
{
    _rCommand.clear();
    _bEscapeProcessing = false;
 
    try
    {
        // contain the dss (data source signature) of the form
        OUString sDataSourceName;
        OUString sCommand;
        sal_Int32       nCommandType = CommandType::COMMAND;
        Reference< XPropertySet > xRowsetProps( getRowSet(), UNO_QUERY );
        ODataAccessDescriptor aDesc( xRowsetProps );
        sDataSourceName = aDesc.getDataSource();
        aDesc[ DataAccessDescriptorProperty::Command ]      >>= sCommand;
        aDesc[ DataAccessDescriptorProperty::CommandType ]  >>= nCommandType;
 
        // do we need to do anything?
        if ( CommandType::QUERY != nCommandType )
            return false;
 
        // get the query object
        Reference< XQueryDefinitionsSupplier > xSuppQueries;
        Reference< XNameAccess > xQueries;
        Reference< XPropertySet > xQuery;
        m_xDatabaseContext->getByName( sDataSourceName ) >>= xSuppQueries;
        if ( xSuppQueries.is() )
            xQueries = xSuppQueries->getQueryDefinitions();
        if ( xQueries.is() )
            xQueries->getByName( sCommand ) >>= xQuery;
        OSL_ENSURE( xQuery.is(), "SbaTableQueryBrowser::implGetQuerySignature: could not retrieve the query object!" );
 
        // get the two properties we need
        if ( xQuery.is() )
        {
            xQuery->getPropertyValue( PROPERTY_COMMAND ) >>= _rCommand;
            _bEscapeProcessing = ::cppu::any2bool( xQuery->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) );
            return true;
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
 
    return false;
}
 
void SbaTableQueryBrowser::frameAction(const css::frame::FrameActionEvent& aEvent)
{
    if (aEvent.Frame == m_xCurrentFrameParent)
    {
        if(aEvent.Action == FrameAction_COMPONENT_DETACHING)
            implRemoveStatusListeners();
        else if (aEvent.Action == FrameAction_COMPONENT_REATTACHED)
            connectExternalDispatches();
    }
    else
        SbaXDataBrowserController::frameAction(aEvent);
 
}
 
void SbaTableQueryBrowser::clearGridColumns(const Reference< XNameContainer >& _xColContainer)
{
    // first we have to clear the grid
    Reference< XInterface > xColumn;
    const Sequence<OUString> aColNames = _xColContainer->getElementNames();
    for (const OUString& rName : aColNames)
    {
        _xColContainer->getByName(rName) >>= xColumn;
        _xColContainer->removeByName(rName);
        ::comphelper::disposeComponent(xColumn);
    }
}
 
void SbaTableQueryBrowser::loadMenu(const Reference< XFrame >& _xFrame)
{
    if ( m_bShowMenu )
    {
        OGenericUnoController::loadMenu(_xFrame);
    }
    else if ( !m_bPreview )
    {
        Reference< css::frame::XLayoutManager > xLayoutManager = getLayoutManager(_xFrame);
 
        if ( xLayoutManager.is() )
        {
            xLayoutManager->lock();
            xLayoutManager->createElement( u"private:resource/toolbar/toolbar"_ustr );
            xLayoutManager->unlock();
            xLayoutManager->doLayout();
        }
        onLoadedMenu( xLayoutManager );
    }
}
 
OUString SbaTableQueryBrowser::getPrivateTitle() const
{
    OUString sTitle;
    if (m_xCurrentlyDisplayed)
    {
        weld::TreeView& rTreeView = m_pTreeView->GetWidget();
        std::unique_ptr<weld::TreeIter> xContainer = rTreeView.make_iterator(m_xCurrentlyDisplayed.get());
        if (!rTreeView.iter_parent(*xContainer))
            return OUString();
        // get the entry for the datasource
        std::unique_ptr<weld::TreeIter> xConnection = implGetConnectionEntry(*xContainer);
        OUString sName = rTreeView.get_text(*m_xCurrentlyDisplayed);
        sTitle = GetEntryText(*xConnection);
        INetURLObject aURL(sTitle);
        if ( aURL.GetProtocol() != INetProtocol::NotValid )
            sTitle = aURL.getBase(INetURLObject::LAST_SEGMENT,true,INetURLObject::DecodeMechanism::WithCharset);
        if ( !sName.isEmpty() )
        {
            sName += " - " + sTitle;
            sTitle = sName;
        }
    }
 
    return sTitle;
}
 
bool SbaTableQueryBrowser::preReloadForm()
{
    bool bIni = false;
    if (!m_xCurrentlyDisplayed)
    {
        // switch the grid to design mode while loading
        getBrowserView()->getGridControl()->setDesignMode(true);
        // we had an invalid statement so we need to connect the column models
        Reference<XPropertySet> xRowSetProps(getRowSet(),UNO_QUERY);
        svx::ODataAccessDescriptor aDesc(xRowSetProps);
        // extract the props
        OUString sDataSource;
        OUString sCommand;
        sal_Int32 nCommandType = CommandType::COMMAND;
        bool bEscapeProcessing = true;
        extractDescriptorProps(aDesc, sDataSource, sCommand, nCommandType, bEscapeProcessing);
        if ( !sDataSource.isEmpty() && !sCommand.isEmpty() && (-1 != nCommandType) )
        {
            m_xCurrentlyDisplayed = getObjectEntry(sDataSource, sCommand, nCommandType, nullptr, nullptr);
            bIni = true;
        }
    }
    return bIni;
}
 
void SbaTableQueryBrowser::postReloadForm()
{
    InitializeGridModel(getFormComponent());
    LoadFinished(true);
}
 
Reference< XEmbeddedScripts > SAL_CALL SbaTableQueryBrowser::getScriptContainer()
{
    // update our database document
    Reference< XModel > xDocument;
    try
    {
        Reference< XPropertySet > xCursorProps( getRowSet(), UNO_QUERY_THROW );
        Reference< XConnection > xConnection( xCursorProps->getPropertyValue( PROPERTY_ACTIVE_CONNECTION ), UNO_QUERY );
        if ( xConnection.is() )
        {
            Reference< XChild > xChild( xConnection, UNO_QUERY_THROW );
            Reference< XDocumentDataSource > xDataSource( xChild->getParent(), UNO_QUERY_THROW );
            xDocument.set( xDataSource->getDatabaseDocument(), UNO_QUERY_THROW );
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
    Reference< XEmbeddedScripts > xScripts( xDocument, UNO_QUERY );
    OSL_ENSURE( xScripts.is() || !xDocument.is(),
        "SbaTableQueryBrowser::getScriptContainer: invalid database document!" );
    return xScripts;
}
 
void SAL_CALL SbaTableQueryBrowser::registerContextMenuInterceptor( const Reference< XContextMenuInterceptor >& Interceptor )
{
    if ( Interceptor.is() )
        m_aContextMenuInterceptors.addInterface( Interceptor );
}
 
void SAL_CALL SbaTableQueryBrowser::releaseContextMenuInterceptor( const Reference< XContextMenuInterceptor >& Interceptor )
{
    if ( Interceptor.is() )
        m_aContextMenuInterceptors.removeInterface( Interceptor );
}
 
void SAL_CALL SbaTableQueryBrowser::registeredDatabaseLocation( const DatabaseRegistrationEvent& Event )
{
    SolarMutexGuard aGuard;
    implAddDatasource( Event.Name, SharedConnection() );
}
 
void SbaTableQueryBrowser::impl_cleanupDataSourceEntry(std::u16string_view rDataSourceName)
{
    // get the top-level representing the removed data source
    weld::TreeView& rTreeView = m_pTreeView->GetWidget();
    std::unique_ptr<weld::TreeIter> xDataSourceEntry(rTreeView.make_iterator());
    bool bDataSourceEntry = rTreeView.get_iter_first(*xDataSourceEntry);
    while (bDataSourceEntry)
    {
        if (rTreeView.get_text(*xDataSourceEntry) == rDataSourceName)
            break;
        bDataSourceEntry = rTreeView.iter_next_sibling(*xDataSourceEntry);
    }
 
    OSL_ENSURE( bDataSourceEntry, "SbaTableQueryBrowser::impl_cleanupDataSourceEntry: do not know this data source!" );
    if (!bDataSourceEntry)
        return;
 
    if (isSelected(*xDataSourceEntry))
    {
        // a table or query belonging to the deleted data source is currently being displayed.
        unloadAndCleanup();
    }
 
    std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator(xDataSourceEntry.get()));
    if (rTreeView.iter_children(*xChild))
    {
        do
        {
            // delete any user data of the child entries of the to-be-removed entry
            const DBTreeListUserData* pData = weld::fromId<const DBTreeListUserData*>(rTreeView.get_id(*xChild));
            rTreeView.set_id(*xChild, OUString());
            delete pData;
        } while (rTreeView.iter_next_sibling(*xChild));
    }
 
    // remove the entry
    DBTreeListUserData* pData = weld::fromId<DBTreeListUserData*>(rTreeView.get_id(*xDataSourceEntry));
    rTreeView.set_id(*xDataSourceEntry, OUString());
    delete pData;
    rTreeView.remove(*xDataSourceEntry);
}
 
void SAL_CALL SbaTableQueryBrowser::revokedDatabaseLocation( const DatabaseRegistrationEvent& Event )
{
    SolarMutexGuard aGuard;
 
    impl_cleanupDataSourceEntry( Event.Name );
 
    // maybe the object which is part of the document data source has been removed
    checkDocumentDataSource();
}
 
void SAL_CALL SbaTableQueryBrowser::changedDatabaseLocation( const DatabaseRegistrationEvent& Event )
{
    SolarMutexGuard aGuard;
 
    // in case the data source was expanded, and connected, we need to clean it up
    // for simplicity, just do as if the data source were completely removed and re-added
    impl_cleanupDataSourceEntry( Event.Name );
    implAddDatasource( Event.Name, SharedConnection() );
}
 
}   // namespace dbaui
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression 'ColumnValue::NO_NULLS != nNullable' is always true.

V547 Expression 'ColumnValue::NO_NULLS == nNullable' is always false.

V547 Expression 'CommandType::QUERY != nCommandType' is always true.

V547 Expression '!bEscapeProcessing' is always true.

V560 A part of conditional expression is always false: (CommandType::COMMAND == nCommandType).

V560 A part of conditional expression is always true: nId != (5000 + 621).

V785 Constant expression in switch statement.