/* -*- 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 <fmitems.hxx>
#include <fmprop.hxx>
#include <svx/strings.hrc>
#include <com/sun/star/awt/XControlModel.hpp>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/form/runtime/XFormController.hpp>
#include <com/sun/star/lang/XUnoTunnel.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/beans/XFastPropertySet.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/sqlnode.hxx>
#include <cppuhelper/implbase.hxx>
#include <fmservs.hxx>
#include <fmshimp.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/objitem.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/request.hxx>
#include <svx/dialmgr.hxx>
#include <svx/fmshell.hxx>
#include <svx/fmtools.hxx>
#include <svx/svxids.hrc>
#include <vcl/wrkwin.hxx>
#include <vcl/settings.hxx>
#include <tools/diagnose_ex.h>
#include <svtools/svlbitm.hxx>
#include <svtools/treelistentry.hxx>
#include <svtools/viewdataentry.hxx>
#include <bitmaps.hlst>
#include <functional>
#define DROP_ACTION_TIMER_INITIAL_TICKS 10
// it takes this long for the scrolling to begin
#define DROP_ACTION_TIMER_SCROLL_TICKS 3
// a line is scrolled in these intervals
#define DROP_ACTION_TIMER_TICK_BASE 10
// this is the basis for multiplying both figures (in ms)
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::uno::RuntimeException;
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 = static_cast<SotClipboardFormatId>(-1);
if (static_cast<SotClipboardFormatId>(-1) == s_nFormat)
{
s_nFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"form.FilterControlExchange\"");
DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OFilterExchangeHelper::getFormatId: bad exchange id!");
}
return s_nFormat;
}
OLocalExchange* OFilterExchangeHelper::createExchange() const
{
return new OFilterItemExchange;
}
Image FmFilterData::GetImage() const
{
return Image();
}
FmParentData::~FmParentData()
{
for (::std::vector<FmFilterData*>::const_iterator i = m_aChildren.begin();
i != m_aChildren.end(); ++i)
delete *i;
}
Image FmFormItem::GetImage() const
{
return Image(BitmapEx(RID_SVXBMP_FORM));
}
FmFilterItem* FmFilterItems::Find( const ::sal_Int32 _nFilterComponentIndex ) const
{
for ( ::std::vector< FmFilterData* >::const_iterator i = m_aChildren.begin();
i != m_aChildren.end();
++i
)
{
FmFilterData* pData = *i;
FmFilterItem& rCondition = dynamic_cast<FmFilterItem&>(*pData);
if ( _nFilterComponentIndex == rCondition.GetComponentIndex() )
return &rCondition;
}
return nullptr;
}
Image FmFilterItems::GetImage() const
{
return Image(BitmapEx(RID_SVXBMP_FILTER));
}
FmFilterItem::FmFilterItem( FmFilterItems* pParent,
const OUString& aFieldName,
const OUString& aText,
const sal_Int32 _nComponentIndex )
:FmFilterData(pParent, aText)
,m_aFieldName(aFieldName)
,m_nComponentIndex( _nComponentIndex )
{
}
Image FmFilterItem::GetImage() const
{
return Image(BitmapEx(RID_SVXBMP_FIELD));
}
// Hints for communication between model and view
class FmFilterHint : public SfxHint
{
FmFilterData* m_pData;
public:
explicit FmFilterHint(FmFilterData* pData):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(pData)
,m_nPos(nRelPos){}
size_t GetPos() const { return m_nPos; }
};
class FmFilterRemovedHint : public FmFilterHint
{
public:
explicit FmFilterRemovedHint(FmFilterData* pData)
:FmFilterHint(pData){}
};
class FmFilterTextChangedHint : public FmFilterHint
{
public:
explicit FmFilterTextChangedHint(FmFilterData* pData)
:FmFilterHint(pData){}
};
class FilterClearingHint : public SfxHint
{
public:
FilterClearingHint(){}
};
class FmFilterCurrentChangedHint : public SfxHint
{
public:
FmFilterCurrentChangedHint(){}
};
// 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 >& 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() );
try
{
Reference< XFilterController > xController( pFormItem->GetController(), UNO_QUERY_THROW );
xController->setPredicateExpression( pFilterItem->GetComponentIndex(), nRowPos, rText );
}
catch( const 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 );
}
catch( const 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 );
}
catch( const 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];
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 ) ) );
pFilterItem = new FmFilterItem(&rFilter, aFieldName, Event.PredicateExpression, Event.FilterComponent);
m_pModel->Insert(rFilter.GetChildren().end(), pFilterItem);
}
// 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;
::std::vector< FmFilterData* >& rTermItems = pFormItem->GetChildren();
const bool bValidIndex = ( Event.DisjunctiveTerm >= 0 ) && ( static_cast<size_t>(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] );
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 ) && ( static_cast<size_t>(nInsertPos) <= pFormItem->GetChildren().size() );
if ( !bValidIndex )
{
OSL_FAIL( "FmFilterAdapter::disjunctiveTermAdded: invalid index!" );
return;
}
const ::std::vector< FmFilterData* >::iterator insertPos = pFormItem->GetChildren().begin() + nInsertPos;
// "Filter for" for first position, "Or" for the other positions
FmFilterItems* pFilterItems = new FmFilterItems(pFormItem, (nInsertPos?SvxResId(RID_STR_FILTER_FILTER_OR):SvxResId(RID_STR_FILTER_FILTER_FOR)));
m_pModel->Insert( insertPos, 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;
for (::std::vector<FmFilterData*>::const_iterator i = m_aChildren.begin();
i != m_aChildren.end(); ++i)
delete *i;
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(), pFormItem );
Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
// insert the existing filters for the form
OUString aTitle(SvxResId(RID_STR_FILTER_FILTER_FOR));
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(), pFilterItems );
const Sequence< OUString >& rDisjunction( conjunctionTerm );
for ( const OUString* pDisjunctiveTerm = rDisjunction.getConstArray();
pDisjunctiveTerm != rDisjunction.getConstArray() + rDisjunction.getLength();
++pDisjunctiveTerm
)
{
if ( pDisjunctiveTerm->isEmpty() )
// no condition for this particular component in this particular conjunction term
continue;
const sal_Int32 nComponentIndex = pDisjunctiveTerm - rDisjunction.getConstArray();
// 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
FmFilterItem* pANDCondition = new FmFilterItem( pFilterItems, sDisplayName, *pDisjunctiveTerm, nComponentIndex );
Insert( pFilterItems->GetChildren().end(), pANDCondition );
}
// title for the next conditions
aTitle = SvxResId( RID_STR_FILTER_FILTER_OR );
}
// now add dependent controllers
Update( xController, pFormItem );
}
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
FmFormItem* FmFilterModel::Find(const ::std::vector<FmFilterData*>& rItems, const Reference< XFormController > & xController) const
{
for (::std::vector<FmFilterData*>::const_iterator i = rItems.begin();
i != rItems.end(); ++i)
{
FmFormItem* pForm = dynamic_cast<FmFormItem*>( *i );
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<FmFilterData*>& rItems, const Reference< XForm >& xForm) const
{
for (::std::vector<FmFilterData*>::const_iterator i = rItems.begin();
i != rItems.end(); ++i)
{
FmFormItem* pForm = dynamic_cast<FmFormItem*>( *i );
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 ( pItem->GetChildren().size() > static_cast<size_t>(nActiveTerm) )
{
SetCurrentItems( static_cast< FmFilterItems* >( pItem->GetChildren()[ nActiveTerm ] ) );
}
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
void FmFilterModel::AppendFilterItems( FmFormItem& _rFormItem )
{
// insert the condition behind the last filter items
::std::vector<FmFilterData*>::const_reverse_iterator aEnd = _rFormItem.GetChildren().rend();
::std::vector<FmFilterData*>::reverse_iterator iter;
for ( iter = _rFormItem.GetChildren().rbegin();
iter != aEnd;
++iter
)
{
if (dynamic_cast<const FmFilterItems*>(*iter) != nullptr)
break;
}
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();
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
void FmFilterModel::Insert(const ::std::vector<FmFilterData*>::iterator& rPos, FmFilterData* pData)
{
size_t nPos;
::std::vector<FmFilterData*>& rItems = pData->GetParent()->GetChildren();
if (rPos == rItems.end())
{
nPos = rItems.size();
rItems.push_back(pData);
}
else
{
nPos = rPos - rItems.begin();
rItems.insert(rPos, pData);
}
// notify the UI
FmFilterInsertedHint aInsertedHint(pData, nPos);
Broadcast( aInsertedHint );
}
void FmFilterModel::Remove(FmFilterData* pData)
{
FmParentData* pParent = pData->GetParent();
::std::vector<FmFilterData*>& rItems = pParent->GetChildren();
// erase the item from the model
::std::vector<FmFilterData*>::iterator i = ::std::find(rItems.begin(), rItems.end(), pData);
DBG_ASSERT(i != rItems.end(), "FmFilterModel::Remove(): unknown Item");
// position within the parent
sal_Int32 nPos = i - rItems.begin();
if (dynamic_cast<const FmFilterItems*>( pData) != nullptr)
{
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< FmFilterData* >& rChildren = static_cast<FmFilterItems*>(pData)->GetChildren();
while ( !rChildren.empty() )
{
::std::vector< FmFilterData* >::iterator removePos = rChildren.end() - 1;
if (FmFilterItem* pFilterItem = dynamic_cast<FmFilterItem*>( *removePos))
{
FmFilterAdapter::setText( nPos, pFilterItem, OUString() );
}
Remove( removePos );
}
}
else
{
xFilterController->removeDisjunctiveTerm( nPos );
}
}
catch( const 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<FmFilterData*>& rParentParentItems = pData->GetParent()->GetParent()->GetChildren();
::std::vector<FmFilterData*>::iterator j = ::std::find(rParentParentItems.begin(), rParentParentItems.end(), 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<FmFilterData*>::iterator& rPos )
{
// remove from parent's child list
FmFilterData* pData = *rPos;
pData->GetParent()->GetChildren().erase( rPos );
// notify the view, this will remove the actual SvTreeListEntry
FmFilterRemovedHint aRemoveHint( pData );
Broadcast( aRemoveHint );
delete pData;
}
bool FmFilterModel::ValidateText(FmFilterItem const * pItem, OUString& rText, OUString& rErrorMsg) const
{
FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pItem->GetParent()->GetParent() );
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::shared_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, '.', getParseContext() );
rText = aPreparedText;
return true;
}
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
return false;
}
void FmFilterModel::Append(FmFilterItems* pItems, FmFilterItem* pFilterItem)
{
Insert(pItems->GetChildren().end(), pFilterItem);
}
void FmFilterModel::SetTextForItem(FmFilterItem* pItem, const OUString& rText)
{
::std::vector<FmFilterData*>& rItems = pItem->GetParent()->GetParent()->GetChildren();
::std::vector<FmFilterData*>::const_iterator i = ::std::find(rItems.begin(), rItems.end(), 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<FmFilterData*>& rItems = pFormItem->GetChildren();
::std::vector<FmFilterData*>::const_iterator i = ::std::find(rItems.begin(), rItems.end(), 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 );
}
catch( const 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< FmFilterData* >& rChildren = _rItem.GetChildren();
bool bAppendLevel = dynamic_cast<const FmFormItem*>(&_rItem) != nullptr;
::std::vector<FmFilterData*>::const_iterator aEnd = rChildren.end();
for ( ::std::vector<FmFilterData*>::iterator i = rChildren.begin();
i != aEnd;
++i
)
{
FmFilterItems* pItems = dynamic_cast<FmFilterItems*>( *i );
if ( pItems && pItems->GetChildren().empty() )
{
bAppendLevel = false;
break;
}
FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( *i );
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 );
}
}
class FmFilterItemsString : public SvLBoxString
{
public:
explicit FmFilterItemsString(const OUString& rStr)
: SvLBoxString(rStr)
{
}
virtual void Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
const SvViewDataEntry* pView, const SvTreeListEntry& rEntry) override;
virtual void InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData = nullptr) override;
};
const int nxDBmp = 12;
void FmFilterItemsString::Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry)
{
FmFilterItems* pRow = static_cast<FmFilterItems*>(rEntry.GetUserData());
FmFormItem* pForm = static_cast<FmFormItem*>(pRow->GetParent());
// current filter is significant painted
const bool bIsCurrentFilter = pForm->GetChildren()[ pForm->GetFilterController()->getActiveTerm() ] == pRow;
if (bIsCurrentFilter)
{
rRenderContext.Push(PushFlags::LINECOLOR);
rRenderContext.SetLineColor(rRenderContext.GetTextColor());
tools::Rectangle aRect(rPos, GetSize(&rDev, &rEntry));
Point aFirst(rPos.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(rPos.X() + nxDBmp, rPos.Y()), GetText());
}
void FmFilterItemsString::InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData)
{
if( !pViewData )
pViewData = pView->GetViewDataItem( pEntry, this );
Size aSize(pView->GetTextWidth(GetText()), pView->GetTextHeight());
aSize.AdjustWidth(nxDBmp );
pViewData->maSize = aSize;
}
class FmFilterString : public SvLBoxString
{
OUString m_aName;
public:
FmFilterString( const OUString& rStr, const OUString& aName)
: SvLBoxString(rStr)
, m_aName(aName)
{
m_aName += ": ";
}
virtual void Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
const SvViewDataEntry* pView, const SvTreeListEntry& rEntry) override;
virtual void InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData = nullptr) override;
};
const int nxD = 4;
void FmFilterString::InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData)
{
if( !pViewData )
pViewData = pView->GetViewDataItem( pEntry, this );
vcl::Font aOldFont( pView->GetFont());
vcl::Font aFont( aOldFont );
aFont.SetWeight(WEIGHT_BOLD);
pView->Control::SetFont( aFont );
Size aSize(pView->GetTextWidth(m_aName), pView->GetTextHeight());
pView->Control::SetFont( aOldFont );
aSize.AdjustWidth(pView->GetTextWidth(GetText()) + nxD );
pViewData->maSize = aSize;
}
void FmFilterString::Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
const SvViewDataEntry* /*pView*/, const SvTreeListEntry& /*rEntry*/)
{
rRenderContext.Push(PushFlags::FONT);
vcl::Font aFont(rRenderContext.GetFont());
aFont.SetWeight(WEIGHT_BOLD);
rRenderContext.SetFont(aFont);
Point aPos(rPos);
rRenderContext.DrawText(aPos, m_aName);
// position for the second text
aPos.AdjustX(rDev.GetTextWidth(m_aName) + nxD );
rRenderContext.Pop();
rDev.DrawText(aPos, GetText());
}
FmFilterNavigator::FmFilterNavigator( vcl::Window* pParent )
:SvTreeListBox( pParent, WB_HASBUTTONS|WB_HASLINES|WB_BORDER|WB_HASBUTTONSATROOT )
,m_pEditingCurrently( nullptr )
,m_aControlExchange( this )
,m_aTimerCounter( 0 )
,m_aDropActionType( DA_SCROLLUP )
{
SetHelpId( HID_FILTER_NAVIGATOR );
SetNodeBitmaps(
Image(BitmapEx(RID_SVXBMP_COLLAPSEDNODE)),
Image(BitmapEx(RID_SVXBMP_EXPANDEDNODE))
);
m_pModel.reset( new FmFilterModel() );
StartListening( *m_pModel );
EnableInplaceEditing( true );
SetSelectionMode(SelectionMode::Multiple);
SetDragDropMode(DragDropMode::ALL);
m_aDropActionTimer.SetInvokeHandler(LINK(this, FmFilterNavigator, OnDropActionTimer));
}
FmFilterNavigator::~FmFilterNavigator()
{
disposeOnce();
}
void FmFilterNavigator::dispose()
{
EndListening( *m_pModel );
m_pModel.reset();
SvTreeListBox::dispose();
}
void FmFilterNavigator::UpdateContent(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
{
if (xCurrent == m_pModel->GetCurrentController())
return;
m_pModel->Update(xControllers, xCurrent);
// expand the filters for the current controller
SvTreeListEntry* pEntry = FindEntry(m_pModel->GetCurrentForm());
if (pEntry && !IsExpanded(pEntry))
{
SelectAll(false);
if (!IsExpanded(pEntry))
Expand(pEntry);
pEntry = FindEntry(m_pModel->GetCurrentItems());
if (pEntry)
{
if (!IsExpanded(pEntry))
Expand(pEntry);
Select(pEntry);
}
}
}
bool FmFilterNavigator::EditingEntry( SvTreeListEntry* pEntry, Selection& rSelection )
{
m_pEditingCurrently = pEntry;
if (!SvTreeListBox::EditingEntry( pEntry, rSelection ))
return false;
return pEntry && dynamic_cast<const FmFilterItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr;
}
bool FmFilterNavigator::EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText )
{
DBG_ASSERT(pEntry == m_pEditingCurrently, "FmFilterNavigator::EditedEntry: suspicious entry!");
m_pEditingCurrently = nullptr;
if (EditingCanceled())
return true;
DBG_ASSERT(dynamic_cast<const FmFilterItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr,
"FmFilterNavigator::EditedEntry() wrong entry");
OUString aText(comphelper::string::strip(rNewText, ' '));
if (aText.isEmpty())
{
// deleting the entry asynchron
PostUserEvent(LINK(this, FmFilterNavigator, OnRemove), pEntry, true);
}
else
{
OUString aErrorMsg;
if (m_pModel->ValidateText(static_cast<FmFilterItem*>(pEntry->GetUserData()), aText, aErrorMsg))
{
GrabFocus();
// 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* >( pEntry->GetUserData() ), aText );
SetCursor( pEntry, true );
SetEntryText( pEntry, aText );
}
else
{
// display the error and return sal_False
SQLContext aError;
aError.Message = SvxResId(RID_STR_SYNTAXERROR);
aError.Details = aErrorMsg;
displayException(aError, this);
return false;
}
}
return true;
}
IMPL_LINK( FmFilterNavigator, OnRemove, void*, p, void )
{
SvTreeListEntry* pEntry = static_cast<SvTreeListEntry*>(p);
// now remove the entry
m_pModel->Remove(static_cast<FmFilterData*>(pEntry->GetUserData()));
}
IMPL_LINK_NOARG(FmFilterNavigator, OnDropActionTimer, Timer *, void)
{
if (--m_aTimerCounter > 0)
return;
switch (m_aDropActionType)
{
case DA_SCROLLUP :
ScrollOutputArea(1);
m_aTimerCounter = DROP_ACTION_TIMER_SCROLL_TICKS;
break;
case DA_SCROLLDOWN :
ScrollOutputArea(-1);
m_aTimerCounter = DROP_ACTION_TIMER_SCROLL_TICKS;
break;
case DA_EXPANDNODE:
{
SvTreeListEntry* pToExpand = GetEntry(m_aTimerTriggered);
if (pToExpand && (GetChildCount(pToExpand) > 0) && !IsExpanded(pToExpand))
Expand(pToExpand);
m_aDropActionTimer.Stop();
}
break;
}
}
sal_Int8 FmFilterNavigator::AcceptDrop( const AcceptDropEvent& rEvt )
{
Point aDropPos = rEvt.maPosPixel;
// possible DropActions scroll and expand
if (rEvt.mbLeaving)
{
if (m_aDropActionTimer.IsActive())
m_aDropActionTimer.Stop();
}
else
{
bool bNeedTrigger = false;
// first entry ?
if ((aDropPos.Y() >= 0) && (aDropPos.Y() < GetEntryHeight()))
{
m_aDropActionType = DA_SCROLLUP;
bNeedTrigger = true;
}
else
{
if ((aDropPos.Y() < GetSizePixel().Height()) && (aDropPos.Y() >= GetSizePixel().Height() - GetEntryHeight()))
{
m_aDropActionType = DA_SCROLLDOWN;
bNeedTrigger = true;
}
else
{ // is it an entry with children, and not yet expanded?
SvTreeListEntry* pDropppedOn = GetEntry(aDropPos);
if (pDropppedOn && (GetChildCount(pDropppedOn) > 0) && !IsExpanded(pDropppedOn))
{
// -> expand
m_aDropActionType = DA_EXPANDNODE;
bNeedTrigger = true;
}
}
}
if (bNeedTrigger && (m_aTimerTriggered != aDropPos))
{
m_aTimerCounter = DROP_ACTION_TIMER_INITIAL_TICKS;
// remember DropPos because there are QueryDrops even though the mouse was not moved
m_aTimerTriggered = aDropPos;
if (!m_aDropActionTimer.IsActive())
{
m_aDropActionTimer.SetTimeout(DROP_ACTION_TIMER_TICK_BASE);
m_aDropActionTimer.Start();
}
}
else if (!bNeedTrigger)
m_aDropActionTimer.Stop();
}
if (!m_aControlExchange.isDragSource())
return DND_ACTION_NONE;
if (!OFilterItemExchange::hasFormat(GetDataFlavorExVector()))
return DND_ACTION_NONE;
// do we contain the formitem?
if (!FindEntry(m_aControlExchange->getFormItem()))
return DND_ACTION_NONE;
SvTreeListEntry* pDropTarget = GetEntry(aDropPos);
if (!pDropTarget)
return DND_ACTION_NONE;
FmFilterData* pData = static_cast<FmFilterData*>(pDropTarget->GetUserData());
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(SvTreeListEntry const * _pTarget)
{
FmFilterData* pData = static_cast<FmFilterData*>(_pTarget->GetUserData());
FmFilterItems* pTargetItems = dynamic_cast<FmFilterItems*>(pData);
if (!pTargetItems)
pTargetItems = dynamic_cast<FmFilterItems*>(pData->GetParent());
return pTargetItems;
}
}
sal_Int8 FmFilterNavigator::ExecuteDrop( const ExecuteDropEvent& rEvt )
{
// you can't scroll after dropping...
if (m_aDropActionTimer.IsActive())
m_aDropActionTimer.Stop();
if (!m_aControlExchange.isDragSource())
return DND_ACTION_NONE;
Point aDropPos = rEvt.maPosPixel;
SvTreeListEntry* pDropTarget = GetEntry( aDropPos );
if (!pDropTarget)
return DND_ACTION_NONE;
// search the container where to add the items
FmFilterItems* pTargetItems = getTargetItems(pDropTarget);
SelectAll(false);
SvTreeListEntry* pEntry = FindEntry(pTargetItems);
Select(pEntry);
SetCurEntry(pEntry);
insertFilterItem(m_aControlExchange->getDraggedEntries(),pTargetItems,DND_ACTION_COPY == rEvt.mnAction);
return DND_ACTION_COPY;
}
void FmFilterNavigator::InitEntry(SvTreeListEntry* pEntry,
const OUString& rStr,
const Image& rImg1,
const Image& rImg2,
SvLBoxButtonKind eButtonKind)
{
SvTreeListBox::InitEntry( pEntry, rStr, rImg1, rImg2, eButtonKind );
std::unique_ptr<SvLBoxString> pString;
if (dynamic_cast<const FmFilterItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
pString.reset(new FmFilterString(rStr,
static_cast<FmFilterItem*>(pEntry->GetUserData())->GetFieldName()));
else if (dynamic_cast<const FmFilterItems*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
pString.reset(new FmFilterItemsString(rStr));
if (pString)
pEntry->ReplaceItem(std::move(pString), 1 );
}
bool FmFilterNavigator::Select( SvTreeListEntry* pEntry, bool bSelect )
{
if (bSelect == IsSelected(pEntry)) // This happens sometimes. I think the basic class errs too much on the side of caution. ;)
return true;
if (SvTreeListBox::Select(pEntry, bSelect))
{
if (bSelect)
{
FmFormItem* pFormItem = nullptr;
if ( dynamic_cast<const FmFilterItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
pFormItem = static_cast<FmFormItem*>(static_cast<FmFilterItem*>(pEntry->GetUserData())->GetParent()->GetParent());
else if (dynamic_cast<const FmFilterItems*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
pFormItem = static_cast<FmFormItem*>(static_cast<FmFilterItem*>(pEntry->GetUserData())->GetParent()->GetParent());
else if (dynamic_cast<const FmFormItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
pFormItem = static_cast<FmFormItem*>(pEntry->GetUserData());
if (pFormItem)
{
// will the controller be exchanged?
if (dynamic_cast<const FmFilterItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
m_pModel->SetCurrentItems(static_cast<FmFilterItems*>(static_cast<FmFilterItem*>(pEntry->GetUserData())->GetParent()));
else if (dynamic_cast<const FmFilterItems*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
m_pModel->SetCurrentItems(static_cast<FmFilterItems*>(pEntry->GetUserData()));
else if (dynamic_cast<const FmFormItem*>(static_cast<FmFilterData*>(pEntry->GetUserData())) != nullptr)
m_pModel->SetCurrentController(static_cast<FmFormItem*>(pEntry->GetUserData())->GetController());
}
}
return true;
}
else
return false;
}
void FmFilterNavigator::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
if (const FmFilterInsertedHint* pInsertHint = dynamic_cast<const FmFilterInsertedHint*>(&rHint))
{
Insert(pInsertHint->GetData(), pInsertHint->GetPos());
}
else if( dynamic_cast<const FilterClearingHint*>(&rHint) )
{
SvTreeListBox::Clear();
}
else if (const FmFilterRemovedHint* pRemoveHint = dynamic_cast<const FmFilterRemovedHint*>(&rHint))
{
Remove(pRemoveHint->GetData());
}
else if (const FmFilterTextChangedHint *pChangeHint = dynamic_cast<const FmFilterTextChangedHint*>(&rHint))
{
SvTreeListEntry* pEntry = FindEntry(pChangeHint->GetData());
if (pEntry)
SetEntryText( pEntry, pChangeHint->GetData()->GetText());
}
else if( dynamic_cast<const FmFilterCurrentChangedHint*>(&rHint) )
{
// invalidate the entries
for (SvTreeListEntry* pEntry = First(); pEntry != nullptr;
pEntry = Next(pEntry))
GetModel()->InvalidateEntry( pEntry );
}
}
SvTreeListEntry* FmFilterNavigator::FindEntry(const FmFilterData* pItem) const
{
SvTreeListEntry* pEntry = nullptr;
if (pItem)
{
for (pEntry = First(); pEntry != nullptr; pEntry = Next( pEntry ))
{
FmFilterData* pEntryItem = static_cast<FmFilterData*>(pEntry->GetUserData());
if (pEntryItem == pItem)
break;
}
}
return pEntry;
}
void FmFilterNavigator::Insert(FmFilterData* pItem, sal_uLong nPos)
{
const FmParentData* pParent = pItem->GetParent() ? pItem->GetParent() : GetFilterModel();
// insert the item
SvTreeListEntry* pParentEntry = FindEntry( pParent );
InsertEntry( pItem->GetText(), pItem->GetImage(), pItem->GetImage(), pParentEntry, false, nPos, pItem );
if ( pParentEntry )
Expand( pParentEntry );
}
void FmFilterNavigator::Remove(FmFilterData const * pItem)
{
// the entry for the data
SvTreeListEntry* pEntry = FindEntry(pItem);
if (pEntry == m_pEditingCurrently)
// cancel editing
EndEditing(true);
if (pEntry)
GetModel()->Remove( pEntry );
}
FmFormItem* FmFilterNavigator::getSelectedFilterItems(::std::vector<FmFilterItem*>& _rItemList)
{
// be sure that the data is only used within only one form!
FmFormItem* pFirstItem = nullptr;
bool bHandled = true;
bool bFoundSomething = false;
for (SvTreeListEntry* pEntry = FirstSelected();
bHandled && pEntry != nullptr;
pEntry = NextSelected(pEntry))
{
FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>( static_cast<FmFilterData*>(pEntry->GetUserData()) );
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;
}
}
}
if ( !bHandled || !bFoundSomething )
pFirstItem = nullptr;
return pFirstItem;
}
void FmFilterNavigator::insertFilterItem(const ::std::vector<FmFilterItem*>& _rFilterList,FmFilterItems* _pTargetItems,bool _bCopy)
{
::std::vector<FmFilterItem*>::const_iterator aEnd = _rFilterList.end();
for ( ::std::vector< FmFilterItem* >::const_iterator i = _rFilterList.begin();
i != aEnd;
++i
)
{
FmFilterItem* pLookupItem( *i );
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, 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() );
}
void FmFilterNavigator::StartDrag( sal_Int8 /*_nAction*/, const Point& /*_rPosPixel*/ )
{
EndSelection();
// be sure that the data is only used within a only one form!
m_aControlExchange.prepareDrag();
::std::vector<FmFilterItem*> aItemList;
if ( FmFormItem* pFirstItem = getSelectedFilterItems(aItemList) )
{
m_aControlExchange->setDraggedEntries(aItemList);
m_aControlExchange->setFormItem(pFirstItem);
m_aControlExchange.startDrag( DND_ACTION_COPYMOVE );
}
}
void FmFilterNavigator::Command( const CommandEvent& rEvt )
{
bool bHandled = false;
switch (rEvt.GetCommand())
{
case CommandEventId::ContextMenu:
{
// the place where it was clicked
Point aWhere;
SvTreeListEntry* pClicked = nullptr;
if (rEvt.IsMouseEvent())
{
aWhere = rEvt.GetMousePosPixel();
pClicked = GetEntry(aWhere);
if (pClicked == nullptr)
break;
if (!IsSelected(pClicked))
{
SelectAll(false);
Select(pClicked);
SetCurEntry(pClicked);
}
}
else
{
pClicked = GetCurEntry();
if (!pClicked)
break;
aWhere = GetEntryPosition( pClicked );
}
::std::vector<FmFilterData*> aSelectList;
for (SvTreeListEntry* pEntry = FirstSelected();
pEntry != nullptr;
pEntry = NextSelected(pEntry))
{
// don't delete forms
FmFormItem* pForm = dynamic_cast<FmFormItem*>( static_cast<FmFilterData*>(pEntry->GetUserData()) );
if (!pForm)
aSelectList.push_back(static_cast<FmFilterData*>(pEntry->GetUserData()));
}
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();
}
VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/filtermenu.ui", "");
VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
// every condition could be deleted except the first one if it's the only one
aContextMenu->EnableItem(aContextMenu->GetItemId("delete"), !aSelectList.empty());
bool bEdit = dynamic_cast<FmFilterItem*>( static_cast<FmFilterData*>(pClicked->GetUserData()) ) != nullptr &&
IsSelected(pClicked) && GetSelectionCount() == 1;
aContextMenu->EnableItem(aContextMenu->GetItemId("edit"), bEdit);
aContextMenu->EnableItem(aContextMenu->GetItemId("isnull"), bEdit);
aContextMenu->EnableItem(aContextMenu->GetItemId("isnotnull"), bEdit);
aContextMenu->RemoveDisabledEntries(true, true);
aContextMenu->Execute(this, aWhere);
OString sIdent = aContextMenu->GetCurItemIdent();
if (sIdent == "edit")
EditEntry( pClicked );
else if (sIdent == "isnull")
{
OUString aErrorMsg;
OUString aText = "IS NULL";
m_pModel->ValidateText(static_cast<FmFilterItem*>(pClicked->GetUserData()),
aText, aErrorMsg);
m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pClicked->GetUserData()), aText);
}
else if (sIdent == "isnotnull")
{
OUString aErrorMsg;
OUString aText = "IS NOT NULL";
m_pModel->ValidateText(static_cast<FmFilterItem*>(pClicked->GetUserData()),
aText, aErrorMsg);
m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pClicked->GetUserData()), aText);
}
else if (sIdent == "delete")
{
DeleteSelection();
}
bHandled = true;
}
break;
default: break;
}
if (!bHandled)
SvTreeListBox::Command( rEvt );
}
SvTreeListEntry* FmFilterNavigator::getNextEntry(SvTreeListEntry* _pStartWith)
{
SvTreeListEntry* pEntry = _pStartWith ? _pStartWith : LastSelected();
pEntry = Next(pEntry);
// we need the next filter entry
while( pEntry && GetChildCount( pEntry ) == 0 && pEntry != Last() )
pEntry = Next(pEntry);
return pEntry;
}
SvTreeListEntry* FmFilterNavigator::getPrevEntry(SvTreeListEntry* _pStartWith)
{
SvTreeListEntry* pEntry = _pStartWith ? _pStartWith : FirstSelected();
pEntry = Prev(pEntry);
// check if the previous entry is a filter, if so get the next prev
if ( pEntry && GetChildCount( pEntry ) != 0 )
{
pEntry = Prev(pEntry);
// if the entry is still no leaf return
if ( pEntry && GetChildCount( pEntry ) != 0 )
pEntry = nullptr;
}
return pEntry;
}
void FmFilterNavigator::KeyInput(const KeyEvent& rKEvt)
{
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::function<SvTreeListEntry*(FmFilterNavigator *, SvTreeListEntry*)> getter = ::std::mem_fn(&FmFilterNavigator::getNextEntry);
if ( rKeyCode.GetCode() == KEY_UP )
getter = ::std::mem_fn(&FmFilterNavigator::getPrevEntry);
SvTreeListEntry* pTarget = getter( this, nullptr );
if ( !pTarget )
break;
FmFilterItems* pTargetItems = getTargetItems( pTarget );
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 )
{
pTarget = getter(this,pTarget);
if ( !pTarget )
return;
pTargetItems = getTargetItems( pTarget );
break;
}
else
{
FmFilterItem* pFilterItem = pTargetItems->Find( (*i)->GetComponentIndex() );
// we found the text component so jump above
if ( pFilterItem )
{
pTarget = getter( this, pTarget );
if ( !pTarget )
return;
pTargetItems = getTargetItems( pTarget );
break;
}
}
}
bNextTargetItem = i != aEnd && pTargetItems;
}
if ( pTargetItems )
{
insertFilterItem( aItemList, pTargetItems, false );
return;
}
}
break;
case KEY_DELETE:
{
if ( rKeyCode.GetModifier() )
break;
if ( !IsSelected( First() ) || GetEntryCount() > 1 )
DeleteSelection();
return;
}
}
SvTreeListBox::KeyInput(rKEvt);
}
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<SvTreeListEntry*> aEntryList;
for (SvTreeListEntry* pEntry = FirstSelected();
pEntry != nullptr;
pEntry = NextSelected(pEntry))
{
FmFilterItem* pFilterItem = dynamic_cast<FmFilterItem*>( static_cast<FmFilterData*>(pEntry->GetUserData()) );
if (pFilterItem && IsSelected(GetParent(pEntry)))
continue;
FmFormItem* pForm = dynamic_cast<FmFormItem*>( static_cast<FmFilterData*>(pEntry->GetUserData()) );
if (!pForm)
aEntryList.push_back(pEntry);
}
// Remove the selection
SelectAll(false);
for (::std::vector<SvTreeListEntry*>::reverse_iterator i = aEntryList.rbegin();
// link problems with operator ==
i.base() != aEntryList.rend().base(); ++i)
{
m_pModel->Remove(static_cast<FmFilterData*>((*i)->GetUserData()));
}
}
FmFilterNavigatorWin::FmFilterNavigatorWin( SfxBindings* _pBindings, SfxChildWindow* _pMgr,
vcl::Window* _pParent )
:SfxDockingWindow( _pBindings, _pMgr, _pParent, WinBits(WB_STDMODELESS|WB_SIZEABLE|WB_ROLLABLE|WB_3DLOOK|WB_DOCKABLE) )
,SfxControllerItem( SID_FM_FILTER_NAVIGATOR_CONTROL, *_pBindings )
{
SetHelpId( HID_FILTER_NAVIGATOR_WIN );
m_pNavigator = VclPtr<FmFilterNavigator>::Create( this );
m_pNavigator->Show();
SetText( SvxResId(RID_STR_FILTER_NAVIGATOR) );
SfxDockingWindow::SetFloatingSize( Size(200,200) );
}
FmFilterNavigatorWin::~FmFilterNavigatorWin()
{
disposeOnce();
}
void FmFilterNavigatorWin::dispose()
{
m_pNavigator.disposeAndClear();
::SfxControllerItem::dispose();
SfxDockingWindow::dispose();
}
void FmFilterNavigatorWin::UpdateContent(FmFormShell const * pFormShell)
{
if (!m_pNavigator)
return;
if (!pFormShell)
m_pNavigator->UpdateContent( nullptr, nullptr );
else
{
Reference<XFormController> const xController(pFormShell->GetImpl()->getActiveInternalController_Lock());
Reference< XIndexAccess > xContainer;
if (xController.is())
{
Reference< XChild > xChild(xController, UNO_QUERY);
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_pNavigator->UpdateContent(xContainer, xController);
}
}
void FmFilterNavigatorWin::StateChanged( 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_pNavigator && m_pNavigator->IsEditingActive() )
m_pNavigator->EndEditing();
if ( m_pNavigator && m_pNavigator->IsEditingActive() )
// the EndEditing was vetoed (perhaps of an syntax error or such)
return false;
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::Resize()
{
SfxDockingWindow::Resize();
Size aLogOutputSize = PixelToLogic(GetOutputSizePixel(), MapMode(MapUnit::MapAppFont));
Size aLogExplSize = aLogOutputSize;
aLogExplSize.AdjustWidth( -6 );
aLogExplSize.AdjustHeight( -6 );
Point aExplPos = LogicToPixel(Point(3,3), MapMode(MapUnit::MapAppFont));
Size aExplSize = LogicToPixel(aLogExplSize, MapMode(MapUnit::MapAppFont));
m_pNavigator->SetPosSizePixel( aExplPos, aExplSize );
}
void FmFilterNavigatorWin::GetFocus()
{
// oj #97405#
if ( m_pNavigator )
m_pNavigator->GrabFocus();
}
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: */
↑ V522 There might be dereferencing of a potential null pointer 'pFormItem'.
↑ V522 There might be dereferencing of a potential null pointer 'pFormItem'.