Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svx/source/form/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 61 kB image not shown  

Quelle  filtnav.cxx   Sprache: C

 
/* -*- 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 <memory>
#include <filtnav.hxx>
#include <fmexch.hxx>
#include <helpids.h>
#include <fmprop.hxx>
#include <svx/strings.hrc>

#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/form/runtime/XFormController.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/sdb/SQLContext.hpp>

#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/sqlnode.hxx>
#include <cppuhelper/implbase.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <fmshimp.hxx>
#include <o3tl/safeint.hxx>
#include <sfx2/objitem.hxx>
#include <svx/dialmgr.hxx>
#include <svx/fmshell.hxx>
#include <svx/fmtools.hxx>
#include <svx/svxids.hrc>
#include <toolkit/helper/vclunohelper.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <utility>
#include <vcl/commandevent.hxx>
#include <vcl/event.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>

#include <bitmaps.hlst>

#include <functional>

using namespace ::svxform;
using namespace ::connectivity;
using namespace ::dbtools;

namespace svxform
{
    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::container::XIndexAccess;
    using ::com::sun::star::uno::UNO_QUERY;
    using ::com::sun::star::beans::XPropertySet;
    using ::com::sun::star::form::runtime::XFormController;
    using ::com::sun::star::form::runtime::XFilterController;
    using ::com::sun::star::form::runtime::XFilterControllerListener;
    using ::com::sun::star::form::runtime::FilterEvent;
    using ::com::sun::star::lang::EventObject;
    using ::com::sun::star::form::XForm;
    using ::com::sun::star::container::XChild;
    using ::com::sun::star::awt::XControl;
    using ::com::sun::star::sdbc::XConnection;
    using ::com::sun::star::util::XNumberFormatsSupplier;
    using ::com::sun::star::util::XNumberFormatter;
    using ::com::sun::star::util::NumberFormatter;
    using ::com::sun::star::sdbc::XRowSet;
    using ::com::sun::star::lang::Locale;
    using ::com::sun::star::sdb::SQLContext;
    using ::com::sun::star::uno::XInterface;
    using ::com::sun::star::uno::UNO_QUERY_THROW;
    using ::com::sun::star::uno::UNO_SET_THROW;
    using ::com::sun::star::uno::Exception;
    using ::com::sun::star::uno::Sequence;


OFilterItemExchange::OFilterItemExchange()
    : m_pFormItem(nullptr)
{
}

void OFilterItemExchange::AddSupportedFormats()
{
    AddFormat(getFormatId());
}

SotClipboardFormatId OFilterItemExchange::getFormatId()
{
    static SotClipboardFormatId s_nFormat =
         SotExchange::RegisterFormatName(u"application/x-openoffice;windows_formatname=\"form.FilterControlExchange\""_ustr);
    DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OFilterExchangeHelper::getFormatId: bad exchange id!");
    return s_nFormat;
}

rtl::Reference<OLocalExchange> OFilterExchangeHelper::createExchange() const
{
    return new OFilterItemExchange;
}

OUString FmFilterData::GetImage() const
{
    return OUString();
}

FmParentData::~FmParentData()
{
}

OUString FmFormItem::GetImage() const
{
    return RID_SVXBMP_FORM;
}

FmFilterItem* FmFilterItems::Find( const ::sal_Int32 _nFilterComponentIndex ) const
{
    for ( auto & pData : m_aChildren )
    {
        FmFilterItem& rCondition = dynamic_cast<FmFilterItem&>(*pData);
        if ( _nFilterComponentIndex == rCondition.GetComponentIndex() )
            return &rCondition;
    }
    return nullptr;
}

OUString FmFilterItems::GetImage() const
{
    return RID_SVXBMP_FILTER;
}

FmFilterItem::FmFilterItem( FmFilterItems* pParent,
                            OUString aFieldName,
                            const OUString& aText,
                            const sal_Int32 _nComponentIndex )
          :FmFilterData(pParent, aText)
          ,m_aFieldName(std::move(aFieldName))
          ,m_nComponentIndex( _nComponentIndex )
{
}

OUString FmFilterItem::GetImage() const
{
    return RID_SVXBMP_FIELD;
}

// Hints for communication between model and view

namespace {

class FmFilterHint : public SfxHint
{
    FmFilterData*   m_pData;

public:
    explicit FmFilterHint(SfxHintId nHintId, FmFilterData* pData) : SfxHint(nHintId), m_pData(pData) {}
    FmFilterData* GetData() const { return m_pData; }
};

class FmFilterInsertedHint : public FmFilterHint
{
    size_t m_nPos;   // Position relative to the parent of the data

public:
    FmFilterInsertedHint(FmFilterData* pData, size_t nRelPos)
        :FmFilterHint(SfxHintId::FmFilterInserted, pData)
        ,m_nPos(nRelPos){}

    size_t GetPos() const { return m_nPos; }
};

class FmFilterRemovedHint : public FmFilterHint
{
public:
    explicit FmFilterRemovedHint(FmFilterData* pData)
        :FmFilterHint(SfxHintId::FmFilterRemoved, pData){}
};


class FmFilterTextChangedHint : public FmFilterHint
{
public:
    explicit FmFilterTextChangedHint(FmFilterData* pData)
        :FmFilterHint(SfxHintId::FmFilterTextChanged, pData){}
};

class FilterClearingHint : public SfxHint
{
public:
    FilterClearingHint() : SfxHint(SfxHintId::FilterClearing) {}
};

class FmFilterCurrentChangedHint : public SfxHint
{
public:
    FmFilterCurrentChangedHint() : SfxHint(SfxHintId::FmFilterCurrentChanged) {}
};

}

// class FmFilterAdapter, listener at the FilterControls
class FmFilterAdapter : public ::cppu::WeakImplHelper< XFilterControllerListener >
{
    FmFilterModel*              m_pModel;
    Reference< XIndexAccess >   m_xControllers;

public:
    FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers);

// XEventListener
    virtual void SAL_CALL disposing(const EventObject& Source) override;

// XFilterControllerListener
    virtual void SAL_CALL predicateExpressionChanged( const FilterEvent& Event ) override;
    virtual void SAL_CALL disjunctiveTermRemoved( const FilterEvent& Event ) override;
    virtual void SAL_CALL disjunctiveTermAdded( const FilterEvent& Event ) override;

// helpers
    /// @throws RuntimeException
    void dispose();

    void AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd );

    static void setText(sal_Int32 nPos,
        const FmFilterItem* pFilterItem,
        const OUString& rText);
};


FmFilterAdapter::FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >&&nbsp;xControllers)
    :m_pModel( pModel )
    ,m_xControllers( xControllers )
{
    AddOrRemoveListener( m_xControllers, true );
}


void FmFilterAdapter::dispose()
{
    AddOrRemoveListener( m_xControllers, false );
}


void FmFilterAdapter::AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd )
{
    for (sal_Int32 i = 0, nLen = _rxControllers->getCount(); i < nLen; ++i)
    {
        Reference< XIndexAccess > xElement( _rxControllers->getByIndex(i), UNO_QUERY );

        // step down
        AddOrRemoveListener( xElement, _bAdd );

        // handle this particular controller
        Reference< XFilterController > xController( xElement, UNO_QUERY );
        OSL_ENSURE( xController.is(), "FmFilterAdapter::InsertElements: no XFilterController, cannot sync data!" );
        if ( xController.is() )
        {
            if ( _bAdd )
                xController->addFilterControllerListener( this );
            else
                xController->removeFilterControllerListener( this );
        }
    }
}


void FmFilterAdapter::setText(sal_Int32 nRowPos,
                              const FmFilterItem* pFilterItem,
                              const OUString& rText)
{
    FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pFilterItem->GetParent()->GetParent()  );
    assert(pFormItem);
    try
    {
        Reference< XFilterController > xController( pFormItem->GetController(), UNO_QUERY_THROW );
        xController->setPredicateExpression( pFilterItem->GetComponentIndex(), nRowPos, rText );
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


// XEventListener

void SAL_CALL FmFilterAdapter::disposing(const EventObject& /*e*/)
{
}


namespace
{
    OUString lcl_getLabelName_nothrow( const Reference< XControl >& _rxControl )
    {
        OUString sLabelName;
        try
        {
            Reference< XPropertySet > xModel( _rxControl->getModel(), UNO_QUERY_THROW );
            sLabelName = getLabelName( xModel );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
        return sLabelName;
    }

    Reference< XPropertySet > lcl_getBoundField_nothrow( const Reference< XControl >& _rxControl )
    {
        Reference< XPropertySet > xField;
        try
        {
            Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY_THROW );
            xField.set( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
        return xField;
    }
}

// XFilterControllerListener
void FmFilterAdapter::predicateExpressionChanged( const FilterEvent& Event )
{
    SolarMutexGuard aGuard;

    if ( !m_pModel )
        return;

    // the controller which sent the event
    Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
    Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
    Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );

    FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
    OSL_ENSURE( pFormItem, "FmFilterAdapter::predicateExpressionChanged: don't know this form!" );
    if ( !pFormItem )
        return;

    const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );

    FmFilterData* pData = pFormItem->GetChildren()[nActiveTerm].get();
    FmFilterItems& rFilter = dynamic_cast<FmFilterItems&>(*pData);
    FmFilterItem* pFilterItem = rFilter.Find( Event.FilterComponent );
    if ( pFilterItem )
    {
        if ( !Event.PredicateExpression.isEmpty())
        {
            pFilterItem->SetText( Event.PredicateExpression );
            // notify the UI
            FmFilterTextChangedHint aChangeHint(pFilterItem);
            m_pModel->Broadcast( aChangeHint );
        }
        else
        {
            // no text anymore so remove the condition
            m_pModel->Remove(pFilterItem);
        }
    }
    else
    {
        // searching the component by field name
        OUString aFieldName( lcl_getLabelName_nothrow( xFilterController->getFilterComponent( Event.FilterComponent ) ) );

        std::unique_ptr<FmFilterItem> pNewFilterItem(new FmFilterItem(&rFilter, aFieldName, Event.PredicateExpression, Event.FilterComponent));
        m_pModel->Insert(rFilter.GetChildren().end(), std::move(pNewFilterItem));
    }

    // ensure there's one empty term in the filter, just in case the active term was previously empty
    m_pModel->EnsureEmptyFilterRows( *pFormItem );
}


void SAL_CALL FmFilterAdapter::disjunctiveTermRemoved( const FilterEvent& Event )
{
    SolarMutexGuard aGuard;

    Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
    Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
    Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );

    FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
    OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermRemoved: don't know this form!" );
    if ( !pFormItem )
        return;

    auto& rTermItems = pFormItem->GetChildren();
    const bool bValidIndex = ( Event.DisjunctiveTerm >= 0 ) && ( o3tl::make_unsigned(Event.DisjunctiveTerm) < rTermItems.size() );
    OSL_ENSURE( bValidIndex, "FmFilterAdapter::disjunctiveTermRemoved: invalid term index!" );
    if ( !bValidIndex )
        return;

    // if the first term was removed, then the to-be first term needs its text updated
    if ( Event.DisjunctiveTerm == 0 )
    {
        rTermItems[1]->SetText( SvxResId(RID_STR_FILTER_FILTER_FOR));
        FmFilterTextChangedHint aChangeHint( rTermItems[1].get() );
        m_pModel->Broadcast( aChangeHint );
    }

    // finally remove the entry from the model
    m_pModel->Remove( rTermItems.begin() + Event.DisjunctiveTerm );

    // ensure there's one empty term in the filter, just in case the currently removed one was the last empty one
    m_pModel->EnsureEmptyFilterRows( *pFormItem );
}


void SAL_CALL FmFilterAdapter::disjunctiveTermAdded( const FilterEvent& Event )
{
    SolarMutexGuard aGuard;

    Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
    Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
    Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );

    FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
    OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermAdded: don't know this form!" );
    if ( !pFormItem )
        return;

    const sal_Int32 nInsertPos = Event.DisjunctiveTerm;
    bool bValidIndex = ( nInsertPos >= 0 ) && ( o3tl::make_unsigned(nInsertPos) <= pFormItem->GetChildren().size() );
    if ( !bValidIndex )
    {
        OSL_FAIL( "FmFilterAdapter::disjunctiveTermAdded: invalid index!" );
        return;
    }

    auto insertPos = pFormItem->GetChildren().begin() + nInsertPos;

    // "Filter for" for first position, "Or" for the other positions
    std::unique_ptr<FmFilterItems> pFilterItems(new FmFilterItems(pFormItem, (nInsertPos?SvxResId(RID_STR_FILTER_FILTER_OR):SvxResId(RID_STR_FILTER_FILTER_FOR))));
    m_pModel->Insert( insertPos, std::move(pFilterItems) );
}


FmFilterModel::FmFilterModel()
              :FmParentData(nullptr, OUString())
              ,OSQLParserClient(comphelper::getProcessComponentContext())
              ,m_pCurrentItems(nullptr)
{
}


FmFilterModel::~FmFilterModel()
{
    Clear();
}


void FmFilterModel::Clear()
{
    // notify
    FilterClearingHint aClearedHint;
    Broadcast( aClearedHint );

    // lose endings
    if (m_pAdapter.is())
    {
        m_pAdapter->dispose();
        m_pAdapter.clear();
    }

    m_pCurrentItems  = nullptr;
    m_xController    = nullptr;
    m_xControllers   = nullptr;

    m_aChildren.clear();
}


void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
{
    if ( xCurrent == m_xController )
        return;

    if (!xControllers.is())
    {
        Clear();
        return;
    }

    // there is only a new current controller
    if ( m_xControllers != xControllers )
    {
        Clear();

        m_xControllers = xControllers;
        Update(m_xControllers, this);

        DBG_ASSERT(xCurrent.is(), "FmFilterModel::Update(...) no current controller");

        // Listening for TextChanges
        m_pAdapter = new FmFilterAdapter(this, xControllers);

        SetCurrentController(xCurrent);
        EnsureEmptyFilterRows( *this );
    }
    else
        SetCurrentController(xCurrent);
}


void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, FmParentData* pParent)
{
    try
    {
        sal_Int32 nCount = xControllers->getCount();
        for ( sal_Int32 i = 0; i < nCount; ++i )
        {
            Reference< XFormController > xController( xControllers->getByIndex(i), UNO_QUERY_THROW );

            Reference< XPropertySet > xFormProperties( xController->getModel(), UNO_QUERY_THROW );
            OUString aName;
            OSL_VERIFY( xFormProperties->getPropertyValue( FM_PROP_NAME ) >>= aName );

            // Insert a new item for the form
            FmFormItem* pFormItem = new FmFormItem( pParent, xController, aName );
            Insert( pParent->GetChildren().end(), std::unique_ptr<FmFilterData>(pFormItem) );

            Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );

            // insert the existing filters for the form
            OUString aTitle(SvxResId(RID_STR_FILTER_FILTER_FOR));

            const Sequence< Sequence< OUString > > aExpressions = xFilterController->getPredicateExpressions();
            for ( auto const & conjunctionTerm : aExpressions )
            {
                // we always display one row, even if there's no term to be displayed
                FmFilterItems* pFilterItems = new FmFilterItems( pFormItem, aTitle );
                Insert( pFormItem->GetChildren().end(), std::unique_ptr<FmFilterData>(pFilterItems) );

                const Sequence< OUString >& rDisjunction( conjunctionTerm );
                sal_Int32 nComponentIndex = -1;
                for ( const OUString& rDisjunctiveTerm : rDisjunction )
                {
                    ++nComponentIndex;

                    if ( rDisjunctiveTerm.isEmpty() )
                        // no condition for this particular component in this particular conjunction term
                        continue;

                    // determine the display name of the control
                    const Reference< XControl > xFilterControl( xFilterController->getFilterComponent( nComponentIndex ) );
                    const OUString sDisplayName( lcl_getLabelName_nothrow( xFilterControl ) );

                    // insert a new entry
                    std::unique_ptr<FmFilterItem> pANDCondition(new FmFilterItem( pFilterItems, sDisplayName, rDisjunctiveTerm, nComponentIndex ));
                    Insert( pFilterItems->GetChildren().end(), std::move(pANDCondition) );
                }

                // title for the next conditions
                aTitle = SvxResId( RID_STR_FILTER_FILTER_OR );
            }

            // now add dependent controllers
            Update( xController, pFormItem );
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& ;rItems, const Reference< XFormController > & xController) const
{
    for (const auto& rItem : rItems)
    {
        FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
        if (pForm)
        {
            if ( xController == pForm->GetController() )
                return pForm;
            else
            {
                pForm = Find(pForm->GetChildren(), xController);
                if (pForm)
                    return pForm;
            }
        }
    }
    return nullptr;
}


FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& ;rItems, const Reference< XForm >& xForm) const
{
    for (const auto& rItem : rItems)
    {
        FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
        if (pForm)
        {
            if (xForm == pForm->GetController()->getModel())
                return pForm;
            else
            {
                pForm = Find(pForm->GetChildren(), xForm);
                if (pForm)
                    return pForm;
            }
        }
    }
    return nullptr;
}

void FmFilterModel::SetCurrentController(const Reference< XFormController > & xCurrent)
{
    if ( xCurrent == m_xController )
        return;

    m_xController = xCurrent;

    FmFormItem* pItem = Find( m_aChildren, xCurrent );
    if ( !pItem )
        return;

    try
    {
        Reference< XFilterController > xFilterController( m_xController, UNO_QUERY_THROW );
        const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
        if (nActiveTerm != -1 && pItem->GetChildren().size() > o3tl::make_unsigned(nActiveTerm))
        {
            SetCurrentItems( static_cast< FmFilterItems* >( pItem->GetChildren()[ nActiveTerm ].get() ) );
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}

void FmFilterModel::AppendFilterItems( FmFormItem& _rFormItem )
{
    // insert the condition behind the last filter items
    auto iter = std::find_if(_rFormItem.GetChildren().rbegin(), _rFormItem.GetChildren().rend(),
        [](const std::unique_ptr<FmFilterData>& rChild) { return dynamic_cast<const FmFilterItems*>(rChild.get()) != nullptr; });

    sal_Int32 nInsertPos = iter.base() - _rFormItem.GetChildren().begin();
    // delegate this to the FilterController, it will notify us, which will let us update our model
    try
    {
        Reference< XFilterController > xFilterController( _rFormItem.GetFilterController(), UNO_SET_THROW );
        if ( nInsertPos >= xFilterController->getDisjunctiveTerms() )
            xFilterController->appendEmptyDisjunctiveTerm();
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}

void FmFilterModel::Insert(const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos, std::unique_ptr<FmFilterData> pData)
{
    auto pTemp = pData.get();
    size_t nPos;
    ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pData->GetParent()->GetChildren();
    if (rPos == rItems.end())
    {
        nPos = rItems.size();
        rItems.push_back(std::move(pData));
    }
    else
    {
        nPos = rPos - rItems.begin();
        rItems.insert(rPos, std::move(pData));
    }

    // notify the UI
    FmFilterInsertedHint aInsertedHint(pTemp, nPos);
    Broadcast( aInsertedHint );
}

void FmFilterModel::Remove(FmFilterData* pData)
{
    FmParentData* pParent = pData->GetParent();
    ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pParent->GetChildren();

    // erase the item from the model
    auto i = ::std::find_if(rItems.begin(), rItems.end(),
            [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pData; } );
    DBG_ASSERT(i != rItems.end(), "FmFilterModel::Remove(): unknown Item");
    // position within the parent
    sal_Int32 nPos = i - rItems.begin();
    if (auto pFilterItems = dynamic_cast<FmFilterItems*>( pData))
    {
        FmFormItem* pFormItem = static_cast<FmFormItem*>(pParent);

        try
        {
            Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );

            bool bEmptyLastTerm = ( ( nPos == 0 ) && xFilterController->getDisjunctiveTerms() == 1 );
            if ( bEmptyLastTerm )
            {
                // remove all children (by setting an empty predicate expression)
                ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = pFilterItems->GetChildren();
                while ( !rChildren.empty() )
                {
                    auto removePos = rChildren.end() - 1;
                    if (FmFilterItem* pFilterItem = dynamic_cast<FmFilterItem*>( removePos->get() ))
                    {
                        FmFilterAdapter::setText( nPos, pFilterItem, OUString() );
                    }
                    Remove( removePos );
                }
            }
            else
            {
                xFilterController->removeDisjunctiveTerm( nPos );
            }
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
    }
    else // FormItems can not be deleted
    {
        FmFilterItem& rFilterItem = dynamic_cast<FmFilterItem&>(*pData);

        // if it's the last condition remove the parent
        if (rItems.size() == 1)
            Remove(rFilterItem.GetParent());
        else
        {
            // find the position of the father within his father
            ::std::vector<std::unique_ptr<FmFilterData>>& rParentParentItems = pData->GetParent()->GetParent()->GetChildren();
            auto j = ::std::find_if(rParentParentItems.begin(), rParentParentItems.end(),
                [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == rFilterItem.GetParent(); });
            DBG_ASSERT(j != rParentParentItems.end(), "FmFilterModel::Remove(): unknown Item");
            sal_Int32 nParentPos = j - rParentParentItems.begin();

            // EmptyText removes the filter
            FmFilterAdapter::setText(nParentPos, &rFilterItem, OUString());
            Remove( i );
        }
    }
}

void FmFilterModel::Remove( const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos )
{
    // remove from parent's child list
    std::unique_ptr<FmFilterData> pData = std::move(*rPos);
    pData->GetParent()->GetChildren().erase( rPos );

    // notify the view, this will remove the actual SvTreeListEntry
    FmFilterRemovedHint aRemoveHint( pData.get() );
    Broadcast( aRemoveHint );
}


bool FmFilterModel::ValidateText(FmFilterItem const * pItem, OUString& rText, OUString& rErrorMsg) const
{
    FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pItem->GetParent()->GetParent()  );
    assert(pFormItem);
    try
    {
        Reference< XFormController > xFormController( pFormItem->GetController() );
        // obtain the connection of the form belonging to the controller
        Reference< XRowSet > xRowSet( xFormController->getModel(), UNO_QUERY_THROW );
        Reference< XConnection > xConnection( getConnection( xRowSet ) );

        // obtain a number formatter for this connection
        // TODO: shouldn't this be cached?
        Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats( xConnection, true );
        Reference< XNumberFormatter > xFormatter( NumberFormatter::create( comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
        xFormatter->attachNumberFormatsSupplier( xFormatSupplier );

        // get the field (database column) which the item is responsible for
        Reference< XFilterController > xFilterController( xFormController, UNO_QUERY_THROW );
        Reference< XPropertySet > xField( lcl_getBoundField_nothrow( xFilterController->getFilterComponent( pItem->GetComponentIndex() ) ), UNO_SET_THROW );

        // parse the given text as filter predicate
        OUString aErr, aTxt( rText );
        std::unique_ptr< OSQLParseNode > pParseNode = predicateTree( aErr, aTxt, xFormatter, xField );
        rErrorMsg = aErr;
        rText = aTxt;
        if ( pParseNode != nullptr )
        {
            OUString aPreparedText;
            Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
            pParseNode->parseNodeToPredicateStr(
                aPreparedText, xConnection, xFormatter, xField, OUString(), aAppLocale, u"."_ustr, getParseContext() );
            rText = aPreparedText;
            return true;
        }
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    return false;
}


void FmFilterModel::Append(FmFilterItems* pItems, std::unique_ptr<FmFilterItem> pFilterItem)
{
    Insert(pItems->GetChildren().end(), std::move(pFilterItem));
}


void FmFilterModel::SetTextForItem(FmFilterItem* pItem, const OUString& rText)
{
    ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pItem->GetParent()->GetParent()->GetChildren();
    auto i = ::std::find_if(rItems.begin(), rItems.end(),
                [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pItem->GetParent(); });
    sal_Int32 nParentPos = i - rItems.begin();

    FmFilterAdapter::setText(nParentPos, pItem, rText);

    if (rText.isEmpty())
        Remove(pItem);
    else
    {
        // Change the text
        pItem->SetText(rText);
        FmFilterTextChangedHint aChangeHint(pItem);
        Broadcast( aChangeHint );
    }
}


void FmFilterModel::SetCurrentItems(FmFilterItems* pCurrent)
{
    if (m_pCurrentItems == pCurrent)
        return;

    // search for the condition
    if (pCurrent)
    {
        FmFormItem* pFormItem = static_cast<FmFormItem*>(pCurrent->GetParent());
        ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pFormItem->GetChildren();
        auto i = ::std::find_if(rItems.begin(), rItems.end(),
                    [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pCurrent; });

        if (i != rItems.end())
        {
            // determine the filter position
            sal_Int32 nPos = i - rItems.begin();
            try
            {
                Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
                xFilterController->setActiveTerm( nPos );
            }
            catchconst Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("svx");
            }

            if ( m_xController != pFormItem->GetController() )
                // calls SetCurrentItems again
                SetCurrentController( pFormItem->GetController() );
            else
                m_pCurrentItems = pCurrent;
        }
        else
            m_pCurrentItems = nullptr;
    }
    else
        m_pCurrentItems = nullptr;


    // notify the UI
    FmFilterCurrentChangedHint aHint;
    Broadcast( aHint );
}


void FmFilterModel::EnsureEmptyFilterRows( FmParentData& _rItem )
{
    // checks whether for each form there's one free level for input
    ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = _rItem.GetChildren();
    bool bAppendLevel = dynamic_cast<const FmFormItem*>(&_rItem) !=  nullptr;

    for ( const auto& rpChild : rChildren )
    {
        FmFilterItems* pItems = dynamic_cast<FmFilterItems*>( rpChild.get() );
        if ( pItems && pItems->GetChildren().empty() )
        {
            bAppendLevel = false;
            break;
        }

        FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( rpChild.get() );
        if (pFormItem)
        {
            EnsureEmptyFilterRows( *pFormItem );
            continue;
        }
    }

    if ( bAppendLevel )
    {
        FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( &_rItem  );
        OSL_ENSURE( pFormItem, "FmFilterModel::EnsureEmptyFilterRows: no FmFormItem, but a FmFilterItems child?" );
        if ( pFormItem )
            AppendFilterItems( *pFormItem );
    }
}

const int nxD = 4;
const int nxDBmp = 12;

IMPL_STATIC_LINK(FmFilterNavigator, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size)
{
    vcl::RenderContext& rRenderContext = aPayload.first;
    const OUString& rId = aPayload.second;

    Size aSize;

    FmFilterData* pData = weld::fromId<FmFilterData*>(rId);
    OUString sText = pData->GetText();

    if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
    {
        rRenderContext.Push(vcl::PushFlags::FONT);
        vcl::Font aFont(rRenderContext.GetFont());
        aFont.SetWeight(WEIGHT_BOLD);
        rRenderContext.SetFont(aFont);

        OUString sName = pItem->GetFieldName() + ": ";
        aSize = Size(rRenderContext.GetTextWidth(sName), rRenderContext.GetTextHeight());

        rRenderContext.Pop();

        aSize.AdjustWidth(rRenderContext.GetTextWidth(sText) + nxD);
    }
    else
    {
        aSize = Size(rRenderContext.GetTextWidth(sText), rRenderContext.GetTextHeight());
        if (dynamic_cast<FmFilterItems*>(pData))
            aSize.AdjustWidth(nxDBmp);
    }

    return aSize;
}

IMPL_STATIC_LINK(FmFilterNavigator, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
{
    vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
    const ::tools::Rectangle& rRect = std::get<1>(aPayload);
    ::tools::Rectangle aRect(rRect.TopLeft(), Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight()));
    bool bSelected = std::get<2>(aPayload);
    const OUString& rId = std::get<3>(aPayload);

    rRenderContext.Push(vcl::PushFlags::TEXTCOLOR);
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    if (bSelected)
        rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
    else
        rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());

    FmFilterData* pData = weld::fromId<FmFilterData*>(rId);
    OUString sText = pData->GetText();
    Point aPos(aRect.TopLeft());

    if (FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pData))
    {
        vcl::Font aFont(rRenderContext.GetFont());
        aFont.SetWeight(WEIGHT_BOLD);

        rRenderContext.Push(vcl::PushFlags::FONT);
        rRenderContext.SetFont(aFont);

        OUString sName = pFilter->GetFieldName() + ": ";
        rRenderContext.DrawText(aPos, sName);

        // position for the second text
        aPos.AdjustX(rRenderContext.GetTextWidth(sName) + nxD);
        rRenderContext.Pop();

        rRenderContext.DrawText(aPos, sText);
    }
    else if (FmFilterItems* pRow = dynamic_cast<FmFilterItems*>(pData))
    {
        FmFormItem* pForm = static_cast<FmFormItem*>(pRow->GetParent());

        // current filter is significant painted
        const bool bIsCurrentFilter = pForm->GetChildren()[ pForm->GetFilterController()->getActiveTerm() ].get() == pRow;
        if (bIsCurrentFilter)
        {
            rRenderContext.Push(vcl::PushFlags::LINECOLOR);
            rRenderContext.SetLineColor(rRenderContext.GetTextColor());

            Point aFirst(aPos.X(), aRect.Bottom() - 6);
            Point aSecond(aFirst .X() + 2, aFirst.Y() + 3);

            rRenderContext.DrawLine(aFirst, aSecond);

            aFirst = aSecond;
            aFirst.AdjustX(1);
            aSecond.AdjustX(6);
            aSecond.AdjustY(-5);

            rRenderContext.DrawLine(aFirst, aSecond);
            rRenderContext.Pop();
        }

        rRenderContext.DrawText(Point(aPos.X() + nxDBmp, aPos.Y()), sText);
    }
    else
        rRenderContext.DrawText(aPos, sText);

    rRenderContext.Pop();
}

FmFilterNavigatorDropTarget::FmFilterNavigatorDropTarget(FmFilterNavigator& rTreeView)
    : DropTargetHelper(rTreeView.get_widget().get_drop_target())
    , m_rTreeView(rTreeView)
{
}

sal_Int8 FmFilterNavigatorDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
{
    sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);

    if (nAccept != DND_ACTION_NONE)
    {
        // to enable the autoscroll when we're close to the edges
        weld::TreeView& rWidget = m_rTreeView.get_widget();
        rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
    }

    return nAccept;
}

sal_Int8 FmFilterNavigatorDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
{
    return m_rTreeView.ExecuteDrop(rEvt);
}

FmFilterNavigator::FmFilterNavigator(vcl::Window* pTopLevel, std::unique_ptr<weld::TreeView> xTreeView)
    : m_xTopLevel(pTopLevel)
    , m_xTreeView(std::move(xTreeView))
    , m_aDropTargetHelper(*this)
    , m_nAsyncRemoveEvent(nullptr)
{
    m_xTreeView->set_help_id(HID_FILTER_NAVIGATOR);

    m_xTreeView->set_selection_mode(SelectionMode::Multiple);

    m_pModel.reset( new FmFilterModel() );
    StartListening( *m_pModel );

    m_xTreeView->connect_custom_get_size(LINK(this, FmFilterNavigator, CustomGetSizeHdl));
    m_xTreeView->connect_custom_render(LINK(this, FmFilterNavigator, CustomRenderHdl));
    m_xTreeView->set_column_custom_renderer(1, true);

    m_xTreeView->connect_selection_changed(LINK(this, FmFilterNavigator, SelectHdl));
    m_xTreeView->connect_key_press(LINK(this, FmFilterNavigator, KeyInputHdl));
    m_xTreeView->connect_popup_menu(LINK(this, FmFilterNavigator, PopupMenuHdl));
    m_xTreeView->connect_editing(LINK(this, FmFilterNavigator, EditingEntryHdl),
                                 LINK(this, FmFilterNavigator, EditedEntryHdl));
    m_xTreeView->connect_drag_begin(LINK(this, FmFilterNavigator, DragBeginHdl));
}

FmFilterNavigator::~FmFilterNavigator()
{
    if (m_nAsyncRemoveEvent)
        Application::RemoveUserEvent(m_nAsyncRemoveEvent);
    EndListening(*m_pModel);
    m_pModel.reset();
}

void FmFilterNavigator::UpdateContent(const Reference< XIndexAccess > & xControllersconst Reference< XFormController > & xCurrent)
{
    if (xCurrent == m_pModel->GetCurrentController())
        return;

    m_pModel->Update(xControllers, xCurrent);

    // expand the filters for the current controller
    std::unique_ptr<weld::TreeIter> xEntry = FindEntry(m_pModel->GetCurrentForm());
    if (!xEntry || m_xTreeView->get_row_expanded(*xEntry))
        return;

    m_xTreeView->unselect_all();

    m_xTreeView->expand_row(*xEntry);

    xEntry = FindEntry(m_pModel->GetCurrentItems());
    if (xEntry)
    {
        if (!m_xTreeView->get_row_expanded(*xEntry))
            m_xTreeView->expand_row(*xEntry);
        m_xTreeView->select(*xEntry);
        SelectHdl(*m_xTreeView);
    }
}

IMPL_LINK(FmFilterNavigator, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
{
    // returns true to allow editing
    if (dynamic_cast<const FmFilterItem*>(weld::fromId<FmFilterData*>(m_xTreeView->get_id(rIter))))
    {
        m_xEditingCurrently = m_xTreeView->make_iterator(&rIter);
        return true;
    }
    m_xEditingCurrently.reset();
    return false;
}

IMPL_LINK(FmFilterNavigator, EditedEntryHdl, const IterString&, rIterString, bool)
{
    const weld::TreeIter& rIter = rIterString.first;
    const OUString& rNewText = rIterString.second;

    assert(m_xEditingCurrently && m_xTreeView->iter_compare(rIter, *m_xEditingCurrently) == 0 &&
               "FmFilterNavigator::EditedEntry: suspicious entry!");
    m_xEditingCurrently.reset();

    FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rIter));

    DBG_ASSERT(dynamic_cast<const FmFilterItem*>(pData) != nullptr,
                    "FmFilterNavigator::EditedEntry() wrong entry");

    OUString aText(comphelper::string::strip(rNewText, ' '));
    if (aText.isEmpty())
    {
        // deleting the entry asynchron
        m_nAsyncRemoveEvent = Application::PostUserEvent(LINK(this, FmFilterNavigator, OnRemove), pData);
    }
    else
    {
        OUString aErrorMsg;

        if (m_pModel->ValidateText(static_cast<FmFilterItem*>(pData), aText, aErrorMsg))
        {
            // this will set the text at the FmFilterItem, as well as update any filter controls
            // which are connected to this particular entry
            m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pData), aText);
            m_xTreeView->set_text(rIter, aText);
        }
        else
        {
            // display the error and return sal_False
            SQLContext aError(SvxResId(RID_STR_SYNTAXERROR), {}, {}, 0, {}, aErrorMsg);
            displayException(aError, VCLUnoHelper::GetInterface(m_xTopLevel));

            return false;
        }
    }
    return true;
}

IMPL_LINK( FmFilterNavigator, OnRemove, void*, p, void )
{
    m_nAsyncRemoveEvent = nullptr;
    // now remove the entry
    m_pModel->Remove(static_cast<FmFilterData*>(p));
}

sal_Int8 FmFilterNavigator::AcceptDrop( const AcceptDropEvent& rEvt )
{
    if (!m_aControlExchange.isDragSource())
        return DND_ACTION_NONE;

    if (!OFilterItemExchange::hasFormat(m_aDropTargetHelper.GetDataFlavorExVector()))
        return DND_ACTION_NONE;

    // do we contain the formitem?
    if (!FindEntry(m_aControlExchange->getFormItem()))
        return DND_ACTION_NONE;

    Point aDropPos = rEvt.maPosPixel;
    std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
    // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
    if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
        xDropTarget.reset();

    if (!xDropTarget)
        return DND_ACTION_NONE;

    FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xDropTarget));
    FmFormItem* pForm = nullptr;
    if (dynamic_cast<const FmFilterItem*>(pData) !=  nullptr)
    {
        pForm = dynamic_cast<FmFormItem*>( pData->GetParent()->GetParent() );
        if (pForm != m_aControlExchange->getFormItem())
            return DND_ACTION_NONE;
    }
    else if (dynamic_cast<const FmFilterItems*>( pData) !=  nullptr)
    {
        pForm = dynamic_cast<FmFormItem*>( pData->GetParent() );
        if (pForm != m_aControlExchange->getFormItem())
            return DND_ACTION_NONE;
    }
    else
        return DND_ACTION_NONE;

    return rEvt.mnAction;
}

namespace
{
    FmFilterItems* getTargetItems(const weld::TreeView& rTreeView, const weld::TreeIter& rTarget)
    {
        FmFilterData* pData = weld::fromId<FmFilterData*>(rTreeView.get_id(rTarget));
        FmFilterItems*  pTargetItems = dynamic_cast<FmFilterItems*>(pData);
        if (!pTargetItems)
            pTargetItems = dynamic_cast<FmFilterItems*>(pData->GetParent());
        return pTargetItems;
    }
}

sal_Int8 FmFilterNavigator::ExecuteDrop( const ExecuteDropEvent& rEvt )
{
    if (!m_aControlExchange.isDragSource())
        return DND_ACTION_NONE;

    Point aDropPos = rEvt.maPosPixel;
    std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
    // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
    if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
        xDropTarget.reset();
    if (!xDropTarget)
        return DND_ACTION_NONE;

    // search the container where to add the items
    FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xDropTarget);
    m_xTreeView->unselect_all();
    std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pTargetItems);
    if (xEntry)
    {
        m_xTreeView->select(*xEntry);
        m_xTreeView->set_cursor(*xEntry);
    }

    insertFilterItem(m_aControlExchange->getDraggedEntries(),pTargetItems,DND_ACTION_COPY == rEvt.mnAction);

    return DND_ACTION_COPY;
}

IMPL_LINK_NOARG(FmFilterNavigator, SelectHdl, weld::TreeView&, void)
{
    std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
    if (!m_xTreeView->get_selected(xIter.get()))
        return;

    FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xIter));

    FmFormItem* pFormItem = nullptr;
    if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
        pFormItem = static_cast<FmFormItem*>(pItem->GetParent()->GetParent());
    else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
        pFormItem = static_cast<FmFormItem*>(pItems->GetParent()->GetParent());
    else
        pFormItem = dynamic_cast<FmFormItem*>(pData);

    if (pFormItem)
    {
        // will the controller be exchanged?
        if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
            m_pModel->SetCurrentItems(static_cast<FmFilterItems*>(pItem->GetParent()));
        else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
            m_pModel->SetCurrentItems(pItems);
        else
            m_pModel->SetCurrentController(pFormItem->GetController());
    }
}

void FmFilterNavigator::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
    if (rHint.GetId() == SfxHintId::FmFilterInserted)
    {
        const FmFilterInsertedHint* pInsertHint = static_cast<const FmFilterInsertedHint*>(&rHint);
        Insert(pInsertHint->GetData(), pInsertHint->GetPos());
    }
    else if( rHint.GetId() == SfxHintId::FilterClearing )
    {
        m_xTreeView->clear();
    }
    else if ( rHint.GetId() == SfxHintId::FmFilterRemoved )
    {
        const FmFilterRemovedHint* pRemoveHint = static_cast<const FmFilterRemovedHint*>(&rHint);
        Remove(pRemoveHint->GetData());
    }
    else if ( rHint.GetId() == SfxHintId::FmFilterTextChanged )
    {
        const FmFilterTextChangedHint *pChangeHint = static_cast<const FmFilterTextChangedHint*>(&rHint);
        std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pChangeHint->GetData());
        if (xEntry)
            m_xTreeView->set_text(*xEntry, pChangeHint->GetData()->GetText());
    }
    else if( rHint.GetId() == SfxHintId::FmFilterCurrentChanged )
    {
        m_xTreeView->queue_draw();
    }
}

std::unique_ptr<weld::TreeIter> FmFilterNavigator::FindEntry(const FmFilterData* pItem) const
{
    if (!pItem)
        return nullptr;
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
    if (!m_xTreeView->get_iter_first(*xEntry))
        return nullptr;
    do
    {
        FmFilterData* pEntryItem = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xEntry));
        if (pEntryItem == pItem)
            return xEntry;
    }
    while (m_xTreeView->iter_next(*xEntry));

    return nullptr;
}

void FmFilterNavigator::Insert(const FmFilterData* pItem, int nPos)
{
    const FmParentData* pParent = pItem->GetParent() ? pItem->GetParent() : m_pModel.get();

    // insert the item
    std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry(pParent);

    OUString sId(weld::toId(pItem));
    std::unique_ptr<weld::TreeIter> xRet(m_xTreeView->make_iterator());
    m_xTreeView->insert(xParentEntry.get(), nPos, &pItem->GetText(), &sId,
                        nullptr, nullptr, false, xRet.get());
    m_xTreeView->set_image(*xRet, pItem->GetImage());

    if (!xParentEntry)
        return;
    m_xTreeView->expand_row(*xParentEntry);
}

void FmFilterNavigator::EndEditing()
{
    if (m_xEditingCurrently)
    {
        // end editing
        m_xTreeView->end_editing();
        m_xEditingCurrently.reset();
    }
}

void FmFilterNavigator::Remove(FmFilterData const * pItem)
{
    // the entry for the data
    std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pItem);
    if (!xEntry)
        return;

    if (m_xEditingCurrently && m_xTreeView->iter_compare(*xEntry, *m_xEditingCurrently) == 0)
        EndEditing();

    m_xTreeView->remove(*xEntry);
}

FmFormItem* FmFilterNavigator::getSelectedFilterItems(::std::vector<FmFilterItem*>&&nbsp;_rItemList)
{
    // be sure that the data is only used within only one form!
    FmFormItem* pFirstItem = nullptr;

    bool bHandled = true;
    bool bFoundSomething = false;

    m_xTreeView->selected_foreach([this, &bHandled, &bFoundSomething, &pFirstItem, &_rItemList](weld::TreeIter& rEntry) {
        FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
        FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pFilterEntry);
        if (pFilter)
        {
            FmFormItem* pForm = dynamic_cast<FmFormItem*>( pFilter->GetParent()->GetParent() );
            if (!pForm)
                bHandled = false;
            else if (!pFirstItem)
                pFirstItem = pForm;
            else if (pFirstItem != pForm)
                bHandled = false;

            if (bHandled)
            {
                _rItemList.push_back(pFilter);
                bFoundSomething = true;
            }
        }
        return !bHandled;
    });

    if ( !bHandled || !bFoundSomething )
        pFirstItem = nullptr;
    return pFirstItem;
}

void FmFilterNavigator::insertFilterItem(const ::std::vector<FmFilterItem*>& _rFilterList,FmFilterItems* _pTargetItems,bool _bCopy)
{
    for (FmFilterItem* pLookupItem : _rFilterList)
    {
        if ( pLookupItem->GetParent() == _pTargetItems )
            continue;

        FmFilterItem* pFilterItem = _pTargetItems->Find( pLookupItem->GetComponentIndex() );
        OUString aText = pLookupItem->GetText();
        if ( !pFilterItem )
        {
            pFilterItem = new FmFilterItem( _pTargetItems, pLookupItem->GetFieldName(), aText, pLookupItem->GetComponentIndex() );
            m_pModel->Append( _pTargetItems, std::unique_ptr<FmFilterItem>(pFilterItem) );
        }

        if ( !_bCopy )
            m_pModel->Remove( pLookupItem );

        // now set the text for the new dragged item
        m_pModel->SetTextForItem( pFilterItem, aText );
    }

    m_pModel->EnsureEmptyFilterRows( *_pTargetItems->GetParent() );
}

IMPL_LINK(FmFilterNavigator, DragBeginHdl, bool&, rUnsetDragIcon, bool)
{
    rUnsetDragIcon = false;

    // be sure that the data is only used within an only one form!
    m_aControlExchange.prepareDrag();

    ::std::vector<FmFilterItem*> aItemList;
    if (FmFormItem* pFirstItem = getSelectedFilterItems(aItemList))
    {
        m_aControlExchange->setDraggedEntries(std::move(aItemList));
        m_aControlExchange->setFormItem(pFirstItem);

        OFilterItemExchange& rExchange = *m_aControlExchange;
        rtl::Reference<TransferDataContainer> xHelper(&rExchange);
        m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVE);
        rExchange.setDragging(true);

        return false;
    }
    return true;
}

IMPL_LINK(FmFilterNavigator, PopupMenuHdl, const CommandEvent&, rEvt, bool)
{
    bool bHandled = false;
    switch (rEvt.GetCommand())
    {
        case CommandEventId::ContextMenu:
        {
            // the place where it was clicked
            Point aWhere;
            std::unique_ptr<weld::TreeIter> xClicked(m_xTreeView->make_iterator());
            if (rEvt.IsMouseEvent())
            {
                aWhere = rEvt.GetMousePosPixel();
                if (!m_xTreeView->get_dest_row_at_pos(aWhere, xClicked.get(), false))
                    break;

                if (!m_xTreeView->is_selected(*xClicked))
                {
                    m_xTreeView->unselect_all();
                    m_xTreeView->select(*xClicked);
                    m_xTreeView->set_cursor(*xClicked);
                }
            }
            else
            {
                if (!m_xTreeView->get_cursor(xClicked.get()))
                    break;
                aWhere = m_xTreeView->get_row_area(*xClicked).Center();
            }

            ::std::vector<FmFilterData*> aSelectList;
            m_xTreeView->selected_foreach([this, &aSelectList](weld::TreeIter& rEntry) {
                FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));

                // don't delete forms
                FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
                if (!pForm)
                    aSelectList.push_back(pFilterEntry);

                return false;
            });

            if (aSelectList.size() == 1)
            {
                // don't delete the only empty row of a form
                FmFilterItems* pFilterItems = dynamic_cast<FmFilterItems*>( aSelectList[0] );
                if (pFilterItems && pFilterItems->GetChildren().empty()
                    && pFilterItems->GetParent()->GetChildren().size() == 1)
                    aSelectList.clear();
            }

            std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), u"svx/ui/filtermenu.ui"_ustr));
            std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));

            // every condition could be deleted except the first one if it's the only one
            bool bNoDelete = false;
            if (aSelectList.empty())
            {
                bNoDelete = true;
                xContextMenu->remove(u"delete"_ustr);
            }

            FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xClicked));
            auto pFilterItem = dynamic_cast<FmFilterItem*>(pFilterEntry);
            bool bEdit = pFilterItem &&
                m_xTreeView->is_selected(*xClicked) && m_xTreeView->count_selected_rows() == 1;

            if (bNoDelete && !bEdit)
            {
                // nothing is in the menu, don't bother
                return true;
            }

            if (!bEdit)
            {
                xContextMenu->remove(u"edit"_ustr);
                xContextMenu->remove(u"isnull"_ustr);
                xContextMenu->remove(u"isnotnull"_ustr);
            }

            OUString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(aWhere, ::Size(1, 1)));
            if (sIdent == "edit")
            {
                m_xTreeView->start_editing(*xClicked);
            }
            else if (sIdent == "isnull")
            {
                OUString aErrorMsg;
                OUString aText = u"IS NULL"_ustr;
                assert(pFilterItem && "if item is null this menu entry was removed and unavailable");
                m_pModel->ValidateText(pFilterItem, aText, aErrorMsg);
                m_pModel->SetTextForItem(pFilterItem, aText);
            }
            else if (sIdent == "isnotnull")
            {
                OUString aErrorMsg;
                OUString aText = u"IS NOT NULL"_ustr;

                assert(pFilterItem && "if item is null this menu entry was removed and unavailable");
                m_pModel->ValidateText(pFilterItem, aText, aErrorMsg);
                m_pModel->SetTextForItem(pFilterItem, aText);
            }
            else if (sIdent == "delete")
            {
                DeleteSelection();
            }
            bHandled = true;
        }
        break;
        defaultbreak;
    }

    return bHandled;
}

bool FmFilterNavigator::getNextEntry(weld::TreeIter& rEntry)
{
    bool bEntry = m_xTreeView->iter_next(rEntry);
    // we need the next filter entry
    if (bEntry)
    {
        while (!m_xTreeView->iter_has_child(rEntry))
        {
            std::unique_ptr<weld::TreeIter> xNext = m_xTreeView->make_iterator(&rEntry);
            if (!m_xTreeView->iter_next(*xNext))
                break;
            m_xTreeView->copy_iterator(*xNext, rEntry);
        }
    }
    return bEntry;
}

bool FmFilterNavigator::getPrevEntry(weld::TreeIter& rEntry)
{
    bool bEntry = m_xTreeView->iter_previous(rEntry);
    // check if the previous entry is a filter, if so get the next prev
    if (bEntry && m_xTreeView->iter_has_child(rEntry))
    {
        bEntry = m_xTreeView->iter_previous(rEntry);
        // if the entry is still no leaf return
        if (bEntry && m_xTreeView->iter_has_child(rEntry))
            bEntry = false;
    }
    return bEntry;
}
IMPL_LINK(FmFilterNavigator, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
{
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();

    switch ( rKeyCode.GetCode() )
    {
        case KEY_UP:
        case KEY_DOWN:
        {
            if ( !rKeyCode.IsMod1() || !rKeyCode.IsMod2() || rKeyCode.IsShift() )
                break;

            ::std::vector<FmFilterItem*> aItemList;
            if ( !getSelectedFilterItems( aItemList ) )
                break;


            std::vector<std::unique_ptr<weld::TreeIter>> aSelected;
            m_xTreeView->selected_foreach([this, &aSelected](weld::TreeIter& rEntry){
                aSelected.emplace_back(m_xTreeView->make_iterator(&rEntry));
                return false;
            });

            std::unique_ptr<weld::TreeIter> xTarget;
            ::std::function<bool(FmFilterNavigator*, weld::TreeIter&)> getter;

            if (rKeyCode.GetCode() == KEY_UP)
            {
                xTarget = m_xTreeView->make_iterator(aSelected.front().get());
                getter = ::std::mem_fn(&FmFilterNavigator::getPrevEntry);
            }
            else
            {
                xTarget = m_xTreeView->make_iterator(aSelected.back().get());
                getter = ::std::mem_fn(&FmFilterNavigator::getNextEntry);
            }

            bool bTarget = getter(this, *xTarget);
            if (!bTarget)
                break;

            FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
            if (!pTargetItems)
                break;

            ::std::vector<FmFilterItem*>::const_iterator aEnd = aItemList.end();
            bool bNextTargetItem = true;
            while ( bNextTargetItem )
            {
                ::std::vector<FmFilterItem*>::const_iterator i = aItemList.begin();
                for (; i != aEnd; ++i)
                {
                    if ( (*i)->GetParent() == pTargetItems )
                    {
                        bTarget = getter(this, *xTarget);
                        if (!bTarget)
                            return true;
                        pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
                        break;
                    }
                    else
                    {
                        FmFilterItem* pFilterItem = pTargetItems->Find( (*i)->GetComponentIndex() );
                        // we found the text component so jump above
                        if ( pFilterItem )
                        {
                            bTarget = getter(this, *xTarget);
                            if (!bTarget)
                                return true;

                            pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
                            break;
                        }
                    }
                }
                bNextTargetItem = i != aEnd && pTargetItems;
            }

            if ( pTargetItems )
            {
                insertFilterItem( aItemList, pTargetItems, false );
                return true;
            }
        }
        break;

        case KEY_DELETE:
        {
            if ( rKeyCode.GetModifier() )
                break;

            std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
            if (m_xTreeView->get_iter_first(*xEntry) && !m_xTreeView->is_selected(*xEntry))
                DeleteSelection();

            return true;
        }
    }

    return false;
}

void FmFilterNavigator::DeleteSelection()
{
    // to avoid the deletion of an entry twice (e.g. deletion of a parent and afterward
    // the deletion of its child, I have to shrink the selection list
    std::vector<FmFilterData*> aEntryList;

    m_xTreeView->selected_foreach([this, &aEntryList](weld::TreeIter& rEntry) {
        FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));

        if (dynamic_cast<FmFilterItem*>(pFilterEntry))
        {
            std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
            if (m_xTreeView->iter_parent(*xParent) && m_xTreeView->is_selected(*xParent))
                return false;
        }

        FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
        if (!pForm)
            aEntryList.emplace_back(pFilterEntry);

        return false;
    });

    // Remove the selection
    m_xTreeView->unselect_all();

    for (auto i = aEntryList.rbegin(); i != aEntryList.rend(); ++i)
        m_pModel->Remove(*i);
}

FmFilterNavigatorWin::FmFilterNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr,
                                           vcl::Window* _pParent)
    : SfxDockingWindow(_pBindings, _pMgr, _pParent, u"FilterNavigator"_ustr, u"svx/ui/filternavigator.ui"_ustr)
    , SfxControllerItem( SID_FM_FILTER_NAVIGATOR_CONTROL, *_pBindings )
    , m_xNavigatorTree(new FmFilterNavigator(this, m_xBuilder->weld_tree_view(u"treeview"_ustr)))
{
    SetHelpId( HID_FILTER_NAVIGATOR_WIN );

    SetText( SvxResId(RID_STR_FILTER_NAVIGATOR) );
    SfxDockingWindow::SetFloatingSize( Size(200,200) );
}

FmFilterNavigatorWin::~FmFilterNavigatorWin()
{
    disposeOnce();
}

void FmFilterNavigatorWin::dispose()
{
    m_xNavigatorTree.reset();
    ::SfxControllerItem::dispose();
    SfxDockingWindow::dispose();
}

void FmFilterNavigatorWin::UpdateContent(FmFormShell const * pFormShell)
{
    if (!m_xNavigatorTree)
        return;

    if (!pFormShell)
        m_xNavigatorTree->UpdateContent( nullptr, nullptr );
    else
    {
        Reference<XFormController> const xController(pFormShell->GetImpl()->getActiveInternalController_Lock());
        Reference< XIndexAccess >   xContainer;
        if (xController.is())
        {
            Reference< XChild >  xChild = xController;
            for (Reference< XInterface >  xParent(xChild->getParent());
                 xParent.is();
                 xParent = xChild.is() ? xChild->getParent() : Reference< XInterface > ())
            {
                xContainer.set(xParent, UNO_QUERY);
                xChild.set(xParent, UNO_QUERY);
            }
        }
        m_xNavigatorTree->UpdateContent(xContainer, xController);
    }
}

void FmFilterNavigatorWin::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
{
    if( !pState  || SID_FM_FILTER_NAVIGATOR_CONTROL != nSID )
        return;

    if( eState >= SfxItemState::DEFAULT )
    {
        FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell()  );
        UpdateContent( pShell );
    }
    else
        UpdateContent( nullptr );
}

bool FmFilterNavigatorWin::Close()
{
    if (m_xNavigatorTree)
        m_xNavigatorTree->EndEditing();

    UpdateContent( nullptr );
    return SfxDockingWindow::Close();
}

void FmFilterNavigatorWin::FillInfo( SfxChildWinInfo& rInfo ) const
{
    SfxDockingWindow::FillInfo( rInfo );
    rInfo.bVisible = false;
}

Size FmFilterNavigatorWin::CalcDockingSize( SfxChildAlignment eAlign )
{
    if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
        return Size();

    return SfxDockingWindow::CalcDockingSize( eAlign );
}

SfxChildAlignment FmFilterNavigatorWin::CheckAlignment( SfxChildAlignment eActAlign, SfxChildAlignment eAlign )
{
    switch (eAlign)
    {
        case SfxChildAlignment::LEFT:
        case SfxChildAlignment::RIGHT:
        case SfxChildAlignment::NOALIGNMENT:
            return eAlign;
        default:
            break;
    }

    return eActAlign;
}

void FmFilterNavigatorWin::GetFocus()
{
    // oj #97405#
    if (m_xNavigatorTree)
        m_xNavigatorTree->GrabFocus();
    else
        SfxDockingWindow::GetFocus();
}

SFX_IMPL_DOCKINGWINDOW( FmFilterNavigatorWinMgr, SID_FM_FILTER_NAVIGATOR )


FmFilterNavigatorWinMgr::FmFilterNavigatorWinMgr( vcl::Window *_pParent, sal_uInt16 _nId,
                                    SfxBindings *_pBindings, SfxChildWinInfo* _pInfo )
                 :SfxChildWindow( _pParent, _nId )
{
    SetWindow( VclPtr<FmFilterNavigatorWin>::Create( _pBindings, this, _pParent ) );
    static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
}


}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.17 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.