1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <filtnav.hxx>
25 #include <svx/strings.hrc>
27 #include <com/sun/star/awt/XControlModel.hpp>
28 #include <com/sun/star/awt/XControl.hpp>
29 #include <com/sun/star/form/runtime/XFormController.hpp>
30 #include <com/sun/star/lang/XUnoTunnel.hpp>
31 #include <com/sun/star/util/NumberFormatter.hpp>
32 #include <com/sun/star/beans/XFastPropertySet.hpp>
33 #include <com/sun/star/sdb/SQLContext.hpp>
35 #include <comphelper/processfactory.hxx>
36 #include <comphelper/string.hxx>
37 #include <connectivity/dbtools.hxx>
38 #include <connectivity/sqlnode.hxx>
39 #include <cppuhelper/implbase.hxx>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <fmservs.hxx>
42 #include <fmshimp.hxx>
43 #include <sfx2/dispatch.hxx>
44 #include <sfx2/objitem.hxx>
45 #include <sfx2/objsh.hxx>
46 #include <sfx2/request.hxx>
47 #include <svx/dialmgr.hxx>
48 #include <svx/fmshell.hxx>
49 #include <svx/fmtools.hxx>
50 #include <svx/svxids.hrc>
51 #include <vcl/settings.hxx>
52 #include <tools/diagnose_ex.h>
53 #include <vcl/commandevent.hxx>
54 #include <vcl/event.hxx>
55 #include <vcl/svlbitm.hxx>
56 #include <vcl/treelistentry.hxx>
57 #include <vcl/viewdataentry.hxx>
58 #include <vcl/svapp.hxx>
60 #include <bitmaps.hlst>
64 #define DROP_ACTION_TIMER_INITIAL_TICKS 10
65 // it takes this long for the scrolling to begin
66 #define DROP_ACTION_TIMER_SCROLL_TICKS 3
67 // a line is scrolled in these intervals
68 #define DROP_ACTION_TIMER_TICK_BASE 10
69 // this is the basis for multiplying both figures (in ms)
71 using namespace ::svxform
;
72 using namespace ::connectivity
;
73 using namespace ::dbtools
;
80 using ::com::sun::star::uno::Reference
;
81 using ::com::sun::star::container::XIndexAccess
;
82 using ::com::sun::star::uno::UNO_QUERY
;
83 using ::com::sun::star::beans::XPropertySet
;
84 using ::com::sun::star::form::runtime::XFormController
;
85 using ::com::sun::star::form::runtime::XFilterController
;
86 using ::com::sun::star::form::runtime::XFilterControllerListener
;
87 using ::com::sun::star::form::runtime::FilterEvent
;
88 using ::com::sun::star::lang::EventObject
;
89 using ::com::sun::star::uno::RuntimeException
;
90 using ::com::sun::star::form::XForm
;
91 using ::com::sun::star::container::XChild
;
92 using ::com::sun::star::awt::XControl
;
93 using ::com::sun::star::sdbc::XConnection
;
94 using ::com::sun::star::util::XNumberFormatsSupplier
;
95 using ::com::sun::star::util::XNumberFormatter
;
96 using ::com::sun::star::util::NumberFormatter
;
97 using ::com::sun::star::sdbc::XRowSet
;
98 using ::com::sun::star::lang::Locale
;
99 using ::com::sun::star::sdb::SQLContext
;
100 using ::com::sun::star::uno::XInterface
;
101 using ::com::sun::star::uno::UNO_QUERY_THROW
;
102 using ::com::sun::star::uno::UNO_SET_THROW
;
103 using ::com::sun::star::uno::Exception
;
104 using ::com::sun::star::uno::Sequence
;
107 OFilterItemExchange::OFilterItemExchange()
108 : m_pFormItem(nullptr)
112 void OFilterItemExchange::AddSupportedFormats()
114 AddFormat(getFormatId());
118 SotClipboardFormatId
OFilterItemExchange::getFormatId()
120 static SotClipboardFormatId s_nFormat
=
121 SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"form.FilterControlExchange\"");
122 DBG_ASSERT(static_cast<SotClipboardFormatId
>(-1) != s_nFormat
, "OFilterExchangeHelper::getFormatId: bad exchange id!");
127 OLocalExchange
* OFilterExchangeHelper::createExchange() const
129 return new OFilterItemExchange
;
133 Image
FmFilterData::GetImage() const
138 FmParentData::~FmParentData()
142 Image
FmFormItem::GetImage() const
144 return Image(StockImage::Yes
, RID_SVXBMP_FORM
);
147 FmFilterItem
* FmFilterItems::Find( const ::sal_Int32 _nFilterComponentIndex
) const
149 for ( auto & pData
: m_aChildren
)
151 FmFilterItem
& rCondition
= dynamic_cast<FmFilterItem
&>(*pData
);
152 if ( _nFilterComponentIndex
== rCondition
.GetComponentIndex() )
158 Image
FmFilterItems::GetImage() const
160 return Image(StockImage::Yes
, RID_SVXBMP_FILTER
);
163 FmFilterItem::FmFilterItem( FmFilterItems
* pParent
,
164 const OUString
& aFieldName
,
165 const OUString
& aText
,
166 const sal_Int32 _nComponentIndex
)
167 :FmFilterData(pParent
, aText
)
168 ,m_aFieldName(aFieldName
)
169 ,m_nComponentIndex( _nComponentIndex
)
173 Image
FmFilterItem::GetImage() const
175 return Image(StockImage::Yes
, RID_SVXBMP_FIELD
);
178 // Hints for communication between model and view
180 class FmFilterHint
: public SfxHint
182 FmFilterData
* const m_pData
;
185 explicit FmFilterHint(FmFilterData
* pData
):m_pData(pData
){}
186 FmFilterData
* GetData() const { return m_pData
; }
189 class FmFilterInsertedHint
: public FmFilterHint
191 size_t const m_nPos
; // Position relative to the parent of the data
194 FmFilterInsertedHint(FmFilterData
* pData
, size_t nRelPos
)
198 size_t GetPos() const { return m_nPos
; }
201 class FmFilterRemovedHint
: public FmFilterHint
204 explicit FmFilterRemovedHint(FmFilterData
* pData
)
205 :FmFilterHint(pData
){}
209 class FmFilterTextChangedHint
: public FmFilterHint
212 explicit FmFilterTextChangedHint(FmFilterData
* pData
)
213 :FmFilterHint(pData
){}
216 class FilterClearingHint
: public SfxHint
219 FilterClearingHint(){}
222 class FmFilterCurrentChangedHint
: public SfxHint
225 FmFilterCurrentChangedHint(){}
228 // class FmFilterAdapter, listener at the FilterControls
229 class FmFilterAdapter
: public ::cppu::WeakImplHelper
< XFilterControllerListener
>
231 FmFilterModel
* m_pModel
;
232 Reference
< XIndexAccess
> m_xControllers
;
235 FmFilterAdapter(FmFilterModel
* pModel
, const Reference
< XIndexAccess
>& xControllers
);
238 virtual void SAL_CALL
disposing(const EventObject
& Source
) override
;
240 // XFilterControllerListener
241 virtual void SAL_CALL
predicateExpressionChanged( const FilterEvent
& Event
) override
;
242 virtual void SAL_CALL
disjunctiveTermRemoved( const FilterEvent
& Event
) override
;
243 virtual void SAL_CALL
disjunctiveTermAdded( const FilterEvent
& Event
) override
;
246 /// @throws RuntimeException
249 void AddOrRemoveListener( const Reference
< XIndexAccess
>& _rxControllers
, const bool _bAdd
);
251 static void setText(sal_Int32 nPos
,
252 const FmFilterItem
* pFilterItem
,
253 const OUString
& rText
);
257 FmFilterAdapter::FmFilterAdapter(FmFilterModel
* pModel
, const Reference
< XIndexAccess
>& xControllers
)
259 ,m_xControllers( xControllers
)
261 AddOrRemoveListener( m_xControllers
, true );
265 void FmFilterAdapter::dispose()
267 AddOrRemoveListener( m_xControllers
, false );
271 void FmFilterAdapter::AddOrRemoveListener( const Reference
< XIndexAccess
>& _rxControllers
, const bool _bAdd
)
273 for (sal_Int32 i
= 0, nLen
= _rxControllers
->getCount(); i
< nLen
; ++i
)
275 Reference
< XIndexAccess
> xElement( _rxControllers
->getByIndex(i
), UNO_QUERY
);
278 AddOrRemoveListener( xElement
, _bAdd
);
280 // handle this particular controller
281 Reference
< XFilterController
> xController( xElement
, UNO_QUERY
);
282 OSL_ENSURE( xController
.is(), "FmFilterAdapter::InsertElements: no XFilterController, cannot sync data!" );
283 if ( xController
.is() )
286 xController
->addFilterControllerListener( this );
288 xController
->removeFilterControllerListener( this );
294 void FmFilterAdapter::setText(sal_Int32 nRowPos
,
295 const FmFilterItem
* pFilterItem
,
296 const OUString
& rText
)
298 FmFormItem
* pFormItem
= dynamic_cast<FmFormItem
*>( pFilterItem
->GetParent()->GetParent() );
302 Reference
< XFilterController
> xController( pFormItem
->GetController(), UNO_QUERY_THROW
);
303 xController
->setPredicateExpression( pFilterItem
->GetComponentIndex(), nRowPos
, rText
);
305 catch( const Exception
& )
307 DBG_UNHANDLED_EXCEPTION("svx");
314 void SAL_CALL
FmFilterAdapter::disposing(const EventObject
& /*e*/)
321 OUString
lcl_getLabelName_nothrow( const Reference
< XControl
>& _rxControl
)
326 Reference
< XPropertySet
> xModel( _rxControl
->getModel(), UNO_QUERY_THROW
);
327 sLabelName
= getLabelName( xModel
);
329 catch( const Exception
& )
331 DBG_UNHANDLED_EXCEPTION("svx");
336 Reference
< XPropertySet
> lcl_getBoundField_nothrow( const Reference
< XControl
>& _rxControl
)
338 Reference
< XPropertySet
> xField
;
341 Reference
< XPropertySet
> xModelProps( _rxControl
->getModel(), UNO_QUERY_THROW
);
342 xField
.set( xModelProps
->getPropertyValue( FM_PROP_BOUNDFIELD
), UNO_QUERY_THROW
);
344 catch( const Exception
& )
346 DBG_UNHANDLED_EXCEPTION("svx");
352 // XFilterControllerListener
354 void FmFilterAdapter::predicateExpressionChanged( const FilterEvent
& Event
)
356 SolarMutexGuard aGuard
;
361 // the controller which sent the event
362 Reference
< XFormController
> xController( Event
.Source
, UNO_QUERY_THROW
);
363 Reference
< XFilterController
> xFilterController( Event
.Source
, UNO_QUERY_THROW
);
364 Reference
< XForm
> xForm( xController
->getModel(), UNO_QUERY_THROW
);
366 FmFormItem
* pFormItem
= m_pModel
->Find( m_pModel
->m_aChildren
, xForm
);
367 OSL_ENSURE( pFormItem
, "FmFilterAdapter::predicateExpressionChanged: don't know this form!" );
371 const sal_Int32
nActiveTerm( xFilterController
->getActiveTerm() );
373 FmFilterData
* pData
= pFormItem
->GetChildren()[nActiveTerm
].get();
374 FmFilterItems
& rFilter
= dynamic_cast<FmFilterItems
&>(*pData
);
375 FmFilterItem
* pFilterItem
= rFilter
.Find( Event
.FilterComponent
);
378 if ( !Event
.PredicateExpression
.isEmpty())
380 pFilterItem
->SetText( Event
.PredicateExpression
);
382 FmFilterTextChangedHint
aChangeHint(pFilterItem
);
383 m_pModel
->Broadcast( aChangeHint
);
387 // no text anymore so remove the condition
388 m_pModel
->Remove(pFilterItem
);
393 // searching the component by field name
394 OUString
aFieldName( lcl_getLabelName_nothrow( xFilterController
->getFilterComponent( Event
.FilterComponent
) ) );
396 std::unique_ptr
<FmFilterItem
> pNewFilterItem(new FmFilterItem(&rFilter
, aFieldName
, Event
.PredicateExpression
, Event
.FilterComponent
));
397 m_pModel
->Insert(rFilter
.GetChildren().end(), std::move(pNewFilterItem
));
400 // ensure there's one empty term in the filter, just in case the active term was previously empty
401 m_pModel
->EnsureEmptyFilterRows( *pFormItem
);
405 void SAL_CALL
FmFilterAdapter::disjunctiveTermRemoved( const FilterEvent
& Event
)
407 SolarMutexGuard aGuard
;
409 Reference
< XFormController
> xController( Event
.Source
, UNO_QUERY_THROW
);
410 Reference
< XFilterController
> xFilterController( Event
.Source
, UNO_QUERY_THROW
);
411 Reference
< XForm
> xForm( xController
->getModel(), UNO_QUERY_THROW
);
413 FmFormItem
* pFormItem
= m_pModel
->Find( m_pModel
->m_aChildren
, xForm
);
414 OSL_ENSURE( pFormItem
, "FmFilterAdapter::disjunctiveTermRemoved: don't know this form!" );
418 auto& rTermItems
= pFormItem
->GetChildren();
419 const bool bValidIndex
= ( Event
.DisjunctiveTerm
>= 0 ) && ( static_cast<size_t>(Event
.DisjunctiveTerm
) < rTermItems
.size() );
420 OSL_ENSURE( bValidIndex
, "FmFilterAdapter::disjunctiveTermRemoved: invalid term index!" );
424 // if the first term was removed, then the to-be first term needs its text updated
425 if ( Event
.DisjunctiveTerm
== 0 )
427 rTermItems
[1]->SetText( SvxResId(RID_STR_FILTER_FILTER_FOR
));
428 FmFilterTextChangedHint
aChangeHint( rTermItems
[1].get() );
429 m_pModel
->Broadcast( aChangeHint
);
432 // finally remove the entry from the model
433 m_pModel
->Remove( rTermItems
.begin() + Event
.DisjunctiveTerm
);
435 // ensure there's one empty term in the filter, just in case the currently removed one was the last empty one
436 m_pModel
->EnsureEmptyFilterRows( *pFormItem
);
440 void SAL_CALL
FmFilterAdapter::disjunctiveTermAdded( const FilterEvent
& Event
)
442 SolarMutexGuard aGuard
;
444 Reference
< XFormController
> xController( Event
.Source
, UNO_QUERY_THROW
);
445 Reference
< XFilterController
> xFilterController( Event
.Source
, UNO_QUERY_THROW
);
446 Reference
< XForm
> xForm( xController
->getModel(), UNO_QUERY_THROW
);
448 FmFormItem
* pFormItem
= m_pModel
->Find( m_pModel
->m_aChildren
, xForm
);
449 OSL_ENSURE( pFormItem
, "FmFilterAdapter::disjunctiveTermAdded: don't know this form!" );
453 const sal_Int32 nInsertPos
= Event
.DisjunctiveTerm
;
454 bool bValidIndex
= ( nInsertPos
>= 0 ) && ( static_cast<size_t>(nInsertPos
) <= pFormItem
->GetChildren().size() );
457 OSL_FAIL( "FmFilterAdapter::disjunctiveTermAdded: invalid index!" );
461 auto insertPos
= pFormItem
->GetChildren().begin() + nInsertPos
;
463 // "Filter for" for first position, "Or" for the other positions
464 std::unique_ptr
<FmFilterItems
> pFilterItems(new FmFilterItems(pFormItem
, (nInsertPos
?SvxResId(RID_STR_FILTER_FILTER_OR
):SvxResId(RID_STR_FILTER_FILTER_FOR
))));
465 m_pModel
->Insert( insertPos
, std::move(pFilterItems
) );
469 FmFilterModel::FmFilterModel()
470 :FmParentData(nullptr, OUString())
471 ,OSQLParserClient(comphelper::getProcessComponentContext())
472 ,m_pCurrentItems(nullptr)
477 FmFilterModel::~FmFilterModel()
483 void FmFilterModel::Clear()
486 FilterClearingHint aClearedHint
;
487 Broadcast( aClearedHint
);
492 m_pAdapter
->dispose();
496 m_pCurrentItems
= nullptr;
497 m_xController
= nullptr;
498 m_xControllers
= nullptr;
504 void FmFilterModel::Update(const Reference
< XIndexAccess
> & xControllers
, const Reference
< XFormController
> & xCurrent
)
506 if ( xCurrent
== m_xController
)
509 if (!xControllers
.is())
515 // there is only a new current controller
516 if ( m_xControllers
!= xControllers
)
520 m_xControllers
= xControllers
;
521 Update(m_xControllers
, this);
523 DBG_ASSERT(xCurrent
.is(), "FmFilterModel::Update(...) no current controller");
525 // Listening for TextChanges
526 m_pAdapter
= new FmFilterAdapter(this, xControllers
);
528 SetCurrentController(xCurrent
);
529 EnsureEmptyFilterRows( *this );
532 SetCurrentController(xCurrent
);
536 void FmFilterModel::Update(const Reference
< XIndexAccess
> & xControllers
, FmParentData
* pParent
)
540 sal_Int32 nCount
= xControllers
->getCount();
541 for ( sal_Int32 i
= 0; i
< nCount
; ++i
)
543 Reference
< XFormController
> xController( xControllers
->getByIndex(i
), UNO_QUERY_THROW
);
545 Reference
< XPropertySet
> xFormProperties( xController
->getModel(), UNO_QUERY_THROW
);
547 OSL_VERIFY( xFormProperties
->getPropertyValue( FM_PROP_NAME
) >>= aName
);
549 // Insert a new item for the form
550 FmFormItem
* pFormItem
= new FmFormItem( pParent
, xController
, aName
);
551 Insert( pParent
->GetChildren().end(), std::unique_ptr
<FmFilterData
>(pFormItem
) );
553 Reference
< XFilterController
> xFilterController( pFormItem
->GetFilterController(), UNO_SET_THROW
);
555 // insert the existing filters for the form
556 OUString
aTitle(SvxResId(RID_STR_FILTER_FILTER_FOR
));
558 const Sequence
< Sequence
< OUString
> > aExpressions
= xFilterController
->getPredicateExpressions();
559 for ( auto const & conjunctionTerm
: aExpressions
)
561 // we always display one row, even if there's no term to be displayed
562 FmFilterItems
* pFilterItems
= new FmFilterItems( pFormItem
, aTitle
);
563 Insert( pFormItem
->GetChildren().end(), std::unique_ptr
<FmFilterData
>(pFilterItems
) );
565 const Sequence
< OUString
>& rDisjunction( conjunctionTerm
);
566 sal_Int32 nComponentIndex
= -1;
567 for ( const OUString
& rDisjunctiveTerm
: rDisjunction
)
571 if ( rDisjunctiveTerm
.isEmpty() )
572 // no condition for this particular component in this particular conjunction term
575 // determine the display name of the control
576 const Reference
< XControl
> xFilterControl( xFilterController
->getFilterComponent( nComponentIndex
) );
577 const OUString
sDisplayName( lcl_getLabelName_nothrow( xFilterControl
) );
579 // insert a new entry
580 std::unique_ptr
<FmFilterItem
> pANDCondition(new FmFilterItem( pFilterItems
, sDisplayName
, rDisjunctiveTerm
, nComponentIndex
));
581 Insert( pFilterItems
->GetChildren().end(), std::move(pANDCondition
) );
584 // title for the next conditions
585 aTitle
= SvxResId( RID_STR_FILTER_FILTER_OR
);
588 // now add dependent controllers
589 Update( xController
, pFormItem
);
592 catch( const Exception
& )
594 DBG_UNHANDLED_EXCEPTION("svx");
599 FmFormItem
* FmFilterModel::Find(const ::std::vector
<std::unique_ptr
<FmFilterData
>>& rItems
, const Reference
< XFormController
> & xController
) const
601 for (const auto& rItem
: rItems
)
603 FmFormItem
* pForm
= dynamic_cast<FmFormItem
*>( rItem
.get() );
606 if ( xController
== pForm
->GetController() )
610 pForm
= Find(pForm
->GetChildren(), xController
);
620 FmFormItem
* FmFilterModel::Find(const ::std::vector
<std::unique_ptr
<FmFilterData
>>& rItems
, const Reference
< XForm
>& xForm
) const
622 for (const auto& rItem
: rItems
)
624 FmFormItem
* pForm
= dynamic_cast<FmFormItem
*>( rItem
.get() );
627 if (xForm
== pForm
->GetController()->getModel())
631 pForm
= Find(pForm
->GetChildren(), xForm
);
641 void FmFilterModel::SetCurrentController(const Reference
< XFormController
> & xCurrent
)
643 if ( xCurrent
== m_xController
)
646 m_xController
= xCurrent
;
648 FmFormItem
* pItem
= Find( m_aChildren
, xCurrent
);
654 Reference
< XFilterController
> xFilterController( m_xController
, UNO_QUERY_THROW
);
655 const sal_Int32
nActiveTerm( xFilterController
->getActiveTerm() );
656 if ( pItem
->GetChildren().size() > static_cast<size_t>(nActiveTerm
) )
658 SetCurrentItems( static_cast< FmFilterItems
* >( pItem
->GetChildren()[ nActiveTerm
].get() ) );
661 catch( const Exception
& )
663 DBG_UNHANDLED_EXCEPTION("svx");
668 void FmFilterModel::AppendFilterItems( FmFormItem
& _rFormItem
)
670 // insert the condition behind the last filter items
671 auto iter
= std::find_if(_rFormItem
.GetChildren().rbegin(), _rFormItem
.GetChildren().rend(),
672 [](const std::unique_ptr
<FmFilterData
>& rChild
) { return dynamic_cast<const FmFilterItems
*>(rChild
.get()) != nullptr; });
674 sal_Int32 nInsertPos
= iter
.base() - _rFormItem
.GetChildren().begin();
675 // delegate this to the FilterController, it will notify us, which will let us update our model
678 Reference
< XFilterController
> xFilterController( _rFormItem
.GetFilterController(), UNO_SET_THROW
);
679 if ( nInsertPos
>= xFilterController
->getDisjunctiveTerms() )
680 xFilterController
->appendEmptyDisjunctiveTerm();
682 catch( const Exception
& )
684 DBG_UNHANDLED_EXCEPTION("svx");
688 void FmFilterModel::Insert(const ::std::vector
<std::unique_ptr
<FmFilterData
>>::iterator
& rPos
, std::unique_ptr
<FmFilterData
> pData
)
690 auto pTemp
= pData
.get();
692 ::std::vector
<std::unique_ptr
<FmFilterData
>>& rItems
= pData
->GetParent()->GetChildren();
693 if (rPos
== rItems
.end())
695 nPos
= rItems
.size();
696 rItems
.push_back(std::move(pData
));
700 nPos
= rPos
- rItems
.begin();
701 rItems
.insert(rPos
, std::move(pData
));
705 FmFilterInsertedHint
aInsertedHint(pTemp
, nPos
);
706 Broadcast( aInsertedHint
);
709 void FmFilterModel::Remove(FmFilterData
* pData
)
711 FmParentData
* pParent
= pData
->GetParent();
712 ::std::vector
<std::unique_ptr
<FmFilterData
>>& rItems
= pParent
->GetChildren();
714 // erase the item from the model
715 auto i
= ::std::find_if(rItems
.begin(), rItems
.end(),
716 [&](const std::unique_ptr
<FmFilterData
>& p
) { return p
.get() == pData
; } );
717 DBG_ASSERT(i
!= rItems
.end(), "FmFilterModel::Remove(): unknown Item");
718 // position within the parent
719 sal_Int32 nPos
= i
- rItems
.begin();
720 if (dynamic_cast<const FmFilterItems
*>( pData
) != nullptr)
722 FmFormItem
* pFormItem
= static_cast<FmFormItem
*>(pParent
);
726 Reference
< XFilterController
> xFilterController( pFormItem
->GetFilterController(), UNO_SET_THROW
);
728 bool bEmptyLastTerm
= ( ( nPos
== 0 ) && xFilterController
->getDisjunctiveTerms() == 1 );
729 if ( bEmptyLastTerm
)
731 // remove all children (by setting an empty predicate expression)
732 ::std::vector
< std::unique_ptr
<FmFilterData
> >& rChildren
= static_cast<FmFilterItems
*>(pData
)->GetChildren();
733 while ( !rChildren
.empty() )
735 auto removePos
= rChildren
.end() - 1;
736 if (FmFilterItem
* pFilterItem
= dynamic_cast<FmFilterItem
*>( removePos
->get() ))
738 FmFilterAdapter::setText( nPos
, pFilterItem
, OUString() );
745 xFilterController
->removeDisjunctiveTerm( nPos
);
748 catch( const Exception
& )
750 DBG_UNHANDLED_EXCEPTION("svx");
753 else // FormItems can not be deleted
755 FmFilterItem
& rFilterItem
= dynamic_cast<FmFilterItem
&>(*pData
);
757 // if it's the last condition remove the parent
758 if (rItems
.size() == 1)
759 Remove(rFilterItem
.GetParent());
762 // find the position of the father within his father
763 ::std::vector
<std::unique_ptr
<FmFilterData
>>& rParentParentItems
= pData
->GetParent()->GetParent()->GetChildren();
764 auto j
= ::std::find_if(rParentParentItems
.begin(), rParentParentItems
.end(),
765 [&](const std::unique_ptr
<FmFilterData
>& p
) { return p
.get() == rFilterItem
.GetParent(); });
766 DBG_ASSERT(j
!= rParentParentItems
.end(), "FmFilterModel::Remove(): unknown Item");
767 sal_Int32 nParentPos
= j
- rParentParentItems
.begin();
769 // EmptyText removes the filter
770 FmFilterAdapter::setText(nParentPos
, &rFilterItem
, OUString());
776 void FmFilterModel::Remove( const ::std::vector
<std::unique_ptr
<FmFilterData
>>::iterator
& rPos
)
778 // remove from parent's child list
779 std::unique_ptr
<FmFilterData
> pData
= std::move(*rPos
);
780 pData
->GetParent()->GetChildren().erase( rPos
);
782 // notify the view, this will remove the actual SvTreeListEntry
783 FmFilterRemovedHint
aRemoveHint( pData
.get() );
784 Broadcast( aRemoveHint
);
788 bool FmFilterModel::ValidateText(FmFilterItem
const * pItem
, OUString
& rText
, OUString
& rErrorMsg
) const
790 FmFormItem
* pFormItem
= dynamic_cast<FmFormItem
*>( pItem
->GetParent()->GetParent() );
794 Reference
< XFormController
> xFormController( pFormItem
->GetController() );
795 // obtain the connection of the form belonging to the controller
796 Reference
< XRowSet
> xRowSet( xFormController
->getModel(), UNO_QUERY_THROW
);
797 Reference
< XConnection
> xConnection( getConnection( xRowSet
) );
799 // obtain a number formatter for this connection
800 // TODO: shouldn't this be cached?
801 Reference
< XNumberFormatsSupplier
> xFormatSupplier
= getNumberFormats( xConnection
, true );
802 Reference
< XNumberFormatter
> xFormatter( NumberFormatter::create( comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
803 xFormatter
->attachNumberFormatsSupplier( xFormatSupplier
);
805 // get the field (database column) which the item is responsible for
806 Reference
< XFilterController
> xFilterController( xFormController
, UNO_QUERY_THROW
);
807 Reference
< XPropertySet
> xField( lcl_getBoundField_nothrow( xFilterController
->getFilterComponent( pItem
->GetComponentIndex() ) ), UNO_SET_THROW
);
809 // parse the given text as filter predicate
810 OUString aErr
, aTxt( rText
);
811 std::unique_ptr
< OSQLParseNode
> pParseNode
= predicateTree( aErr
, aTxt
, xFormatter
, xField
);
814 if ( pParseNode
!= nullptr )
816 OUString aPreparedText
;
817 Locale aAppLocale
= Application::GetSettings().GetUILanguageTag().getLocale();
818 pParseNode
->parseNodeToPredicateStr(
819 aPreparedText
, xConnection
, xFormatter
, xField
, OUString(), aAppLocale
, OUString("."), getParseContext() );
820 rText
= aPreparedText
;
824 catch( const Exception
& )
826 DBG_UNHANDLED_EXCEPTION("svx");
833 void FmFilterModel::Append(FmFilterItems
* pItems
, std::unique_ptr
<FmFilterItem
> pFilterItem
)
835 Insert(pItems
->GetChildren().end(), std::move(pFilterItem
));
839 void FmFilterModel::SetTextForItem(FmFilterItem
* pItem
, const OUString
& rText
)
841 ::std::vector
<std::unique_ptr
<FmFilterData
>>& rItems
= pItem
->GetParent()->GetParent()->GetChildren();
842 auto i
= ::std::find_if(rItems
.begin(), rItems
.end(),
843 [&](const std::unique_ptr
<FmFilterData
>& p
) { return p
.get() == pItem
->GetParent(); });
844 sal_Int32 nParentPos
= i
- rItems
.begin();
846 FmFilterAdapter::setText(nParentPos
, pItem
, rText
);
853 pItem
->SetText(rText
);
854 FmFilterTextChangedHint
aChangeHint(pItem
);
855 Broadcast( aChangeHint
);
860 void FmFilterModel::SetCurrentItems(FmFilterItems
* pCurrent
)
862 if (m_pCurrentItems
== pCurrent
)
865 // search for the condition
868 FmFormItem
* pFormItem
= static_cast<FmFormItem
*>(pCurrent
->GetParent());
869 ::std::vector
<std::unique_ptr
<FmFilterData
>>& rItems
= pFormItem
->GetChildren();
870 auto i
= ::std::find_if(rItems
.begin(), rItems
.end(),
871 [&](const std::unique_ptr
<FmFilterData
>& p
) { return p
.get() == pCurrent
; });
873 if (i
!= rItems
.end())
875 // determine the filter position
876 sal_Int32 nPos
= i
- rItems
.begin();
879 Reference
< XFilterController
> xFilterController( pFormItem
->GetFilterController(), UNO_SET_THROW
);
880 xFilterController
->setActiveTerm( nPos
);
882 catch( const Exception
& )
884 DBG_UNHANDLED_EXCEPTION("svx");
887 if ( m_xController
!= pFormItem
->GetController() )
888 // calls SetCurrentItems again
889 SetCurrentController( pFormItem
->GetController() );
891 m_pCurrentItems
= pCurrent
;
894 m_pCurrentItems
= nullptr;
897 m_pCurrentItems
= nullptr;
901 FmFilterCurrentChangedHint aHint
;
906 void FmFilterModel::EnsureEmptyFilterRows( FmParentData
& _rItem
)
908 // checks whether for each form there's one free level for input
909 ::std::vector
< std::unique_ptr
<FmFilterData
> >& rChildren
= _rItem
.GetChildren();
910 bool bAppendLevel
= dynamic_cast<const FmFormItem
*>(&_rItem
) != nullptr;
912 for ( const auto& rpChild
: rChildren
)
914 FmFilterItems
* pItems
= dynamic_cast<FmFilterItems
*>( rpChild
.get() );
915 if ( pItems
&& pItems
->GetChildren().empty() )
917 bAppendLevel
= false;
921 FmFormItem
* pFormItem
= dynamic_cast<FmFormItem
*>( rpChild
.get() );
924 EnsureEmptyFilterRows( *pFormItem
);
931 FmFormItem
* pFormItem
= dynamic_cast<FmFormItem
*>( &_rItem
);
932 OSL_ENSURE( pFormItem
, "FmFilterModel::EnsureEmptyFilterRows: no FmFormItem, but a FmFilterItems child?" );
934 AppendFilterItems( *pFormItem
);
938 class FmFilterItemsString
: public SvLBoxString
941 explicit FmFilterItemsString(const OUString
& rStr
)
946 virtual void Paint(const Point
& rPos
, SvTreeListBox
& rDev
, vcl::RenderContext
& rRenderContext
,
947 const SvViewDataEntry
* pView
, const SvTreeListEntry
& rEntry
) override
;
948 virtual void InitViewData( SvTreeListBox
* pView
,SvTreeListEntry
* pEntry
, SvViewDataItem
* pViewData
= nullptr) override
;
951 const int nxDBmp
= 12;
953 void FmFilterItemsString::Paint(const Point
& rPos
, SvTreeListBox
& rDev
, vcl::RenderContext
& rRenderContext
,
954 const SvViewDataEntry
* /*pView*/, const SvTreeListEntry
& rEntry
)
956 FmFilterItems
* pRow
= static_cast<FmFilterItems
*>(rEntry
.GetUserData());
957 FmFormItem
* pForm
= static_cast<FmFormItem
*>(pRow
->GetParent());
959 // current filter is significant painted
960 const bool bIsCurrentFilter
= pForm
->GetChildren()[ pForm
->GetFilterController()->getActiveTerm() ].get() == pRow
;
961 if (bIsCurrentFilter
)
963 rRenderContext
.Push(PushFlags::LINECOLOR
);
964 rRenderContext
.SetLineColor(rRenderContext
.GetTextColor());
966 Size
aSize(GetWidth(&rDev
, &rEntry
), GetHeight(&rDev
, &rEntry
));
967 tools::Rectangle
aRect(rPos
, aSize
);
968 Point
aFirst(rPos
.X(), aRect
.Bottom() - 6);
969 Point
aSecond(aFirst
.X() + 2, aFirst
.Y() + 3);
971 rRenderContext
.DrawLine(aFirst
, aSecond
);
976 aSecond
.AdjustY( -5 );
978 rRenderContext
.DrawLine(aFirst
, aSecond
);
979 rRenderContext
.Pop();
982 rRenderContext
.DrawText(Point(rPos
.X() + nxDBmp
, rPos
.Y()), GetText());
986 void FmFilterItemsString::InitViewData( SvTreeListBox
* pView
,SvTreeListEntry
* pEntry
, SvViewDataItem
* pViewData
)
989 pViewData
= pView
->GetViewDataItem( pEntry
, this );
991 Size
aSize(pView
->GetTextWidth(GetText()), pView
->GetTextHeight());
992 aSize
.AdjustWidth(nxDBmp
);
993 pViewData
->mnWidth
= aSize
.Width();
994 pViewData
->mnHeight
= aSize
.Height();
997 class FmFilterString
: public SvLBoxString
1002 FmFilterString( const OUString
& rStr
, const OUString
& aName
)
1003 : SvLBoxString(rStr
)
1009 virtual void Paint(const Point
& rPos
, SvTreeListBox
& rDev
, vcl::RenderContext
& rRenderContext
,
1010 const SvViewDataEntry
* pView
, const SvTreeListEntry
& rEntry
) override
;
1011 virtual void InitViewData( SvTreeListBox
* pView
,SvTreeListEntry
* pEntry
, SvViewDataItem
* pViewData
= nullptr) override
;
1016 void FmFilterString::InitViewData( SvTreeListBox
* pView
,SvTreeListEntry
* pEntry
, SvViewDataItem
* pViewData
)
1019 pViewData
= pView
->GetViewDataItem( pEntry
, this );
1021 vcl::Font
aOldFont( pView
->GetFont());
1022 vcl::Font
aFont( aOldFont
);
1023 aFont
.SetWeight(WEIGHT_BOLD
);
1024 pView
->Control::SetFont( aFont
);
1026 Size
aSize(pView
->GetTextWidth(m_aName
), pView
->GetTextHeight());
1027 pView
->Control::SetFont( aOldFont
);
1028 aSize
.AdjustWidth(pView
->GetTextWidth(GetText()) + nxD
);
1029 pViewData
->mnWidth
= aSize
.Width();
1030 pViewData
->mnHeight
= aSize
.Height();
1033 void FmFilterString::Paint(const Point
& rPos
, SvTreeListBox
& rDev
, vcl::RenderContext
& rRenderContext
,
1034 const SvViewDataEntry
* /*pView*/, const SvTreeListEntry
& /*rEntry*/)
1036 rRenderContext
.Push(PushFlags::FONT
);
1037 vcl::Font
aFont(rRenderContext
.GetFont());
1038 aFont
.SetWeight(WEIGHT_BOLD
);
1039 rRenderContext
.SetFont(aFont
);
1042 rRenderContext
.DrawText(aPos
, m_aName
);
1044 // position for the second text
1045 aPos
.AdjustX(rDev
.GetTextWidth(m_aName
) + nxD
);
1046 rRenderContext
.Pop();
1047 rDev
.DrawText(aPos
, GetText());
1050 FmFilterNavigator::FmFilterNavigator( vcl::Window
* pParent
)
1051 :SvTreeListBox( pParent
, WB_HASBUTTONS
|WB_HASLINES
|WB_BORDER
|WB_HASBUTTONSATROOT
)
1052 ,m_pEditingCurrently( nullptr )
1053 ,m_aControlExchange( this )
1054 ,m_aTimerCounter( 0 )
1055 ,m_aDropActionType( DA_SCROLLUP
)
1057 SetHelpId( HID_FILTER_NAVIGATOR
);
1060 Image(StockImage::Yes
, RID_SVXBMP_COLLAPSEDNODE
),
1061 Image(StockImage::Yes
, RID_SVXBMP_EXPANDEDNODE
)
1064 m_pModel
.reset( new FmFilterModel() );
1065 StartListening( *m_pModel
);
1067 EnableInplaceEditing( true );
1068 SetSelectionMode(SelectionMode::Multiple
);
1070 SetDragDropMode(DragDropMode::ALL
);
1072 m_aDropActionTimer
.SetInvokeHandler(LINK(this, FmFilterNavigator
, OnDropActionTimer
));
1076 FmFilterNavigator::~FmFilterNavigator()
1081 void FmFilterNavigator::dispose()
1083 EndListening( *m_pModel
);
1085 SvTreeListBox::dispose();
1089 void FmFilterNavigator::UpdateContent(const Reference
< XIndexAccess
> & xControllers
, const Reference
< XFormController
> & xCurrent
)
1091 if (xCurrent
== m_pModel
->GetCurrentController())
1094 m_pModel
->Update(xControllers
, xCurrent
);
1096 // expand the filters for the current controller
1097 SvTreeListEntry
* pEntry
= FindEntry(m_pModel
->GetCurrentForm());
1098 if (pEntry
&& !IsExpanded(pEntry
))
1102 if (!IsExpanded(pEntry
))
1105 pEntry
= FindEntry(m_pModel
->GetCurrentItems());
1108 if (!IsExpanded(pEntry
))
1116 bool FmFilterNavigator::EditingEntry( SvTreeListEntry
* pEntry
, Selection
& rSelection
)
1118 m_pEditingCurrently
= pEntry
;
1119 if (!SvTreeListBox::EditingEntry( pEntry
, rSelection
))
1122 return pEntry
&& dynamic_cast<const FmFilterItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr;
1126 bool FmFilterNavigator::EditedEntry( SvTreeListEntry
* pEntry
, const OUString
& rNewText
)
1128 DBG_ASSERT(pEntry
== m_pEditingCurrently
, "FmFilterNavigator::EditedEntry: suspicious entry!");
1129 m_pEditingCurrently
= nullptr;
1131 if (EditingCanceled())
1134 DBG_ASSERT(dynamic_cast<const FmFilterItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr,
1135 "FmFilterNavigator::EditedEntry() wrong entry");
1137 OUString
aText(comphelper::string::strip(rNewText
, ' '));
1138 if (aText
.isEmpty())
1140 // deleting the entry asynchron
1141 PostUserEvent(LINK(this, FmFilterNavigator
, OnRemove
), pEntry
, true);
1147 if (m_pModel
->ValidateText(static_cast<FmFilterItem
*>(pEntry
->GetUserData()), aText
, aErrorMsg
))
1150 // this will set the text at the FmFilterItem, as well as update any filter controls
1151 // which are connected to this particular entry
1152 m_pModel
->SetTextForItem( static_cast< FmFilterItem
* >( pEntry
->GetUserData() ), aText
);
1154 SetCursor( pEntry
, true );
1155 SetEntryText( pEntry
, aText
);
1159 // display the error and return sal_False
1161 aError
.Message
= SvxResId(RID_STR_SYNTAXERROR
);
1162 aError
.Details
= aErrorMsg
;
1163 displayException(aError
, this);
1172 IMPL_LINK( FmFilterNavigator
, OnRemove
, void*, p
, void )
1174 SvTreeListEntry
* pEntry
= static_cast<SvTreeListEntry
*>(p
);
1175 // now remove the entry
1176 m_pModel
->Remove(static_cast<FmFilterData
*>(pEntry
->GetUserData()));
1180 IMPL_LINK_NOARG(FmFilterNavigator
, OnDropActionTimer
, Timer
*, void)
1182 if (--m_aTimerCounter
> 0)
1185 switch (m_aDropActionType
)
1188 ScrollOutputArea(1);
1189 m_aTimerCounter
= DROP_ACTION_TIMER_SCROLL_TICKS
;
1191 case DA_SCROLLDOWN
:
1192 ScrollOutputArea(-1);
1193 m_aTimerCounter
= DROP_ACTION_TIMER_SCROLL_TICKS
;
1197 SvTreeListEntry
* pToExpand
= GetEntry(m_aTimerTriggered
);
1198 if (pToExpand
&& (GetChildCount(pToExpand
) > 0) && !IsExpanded(pToExpand
))
1200 m_aDropActionTimer
.Stop();
1207 sal_Int8
FmFilterNavigator::AcceptDrop( const AcceptDropEvent
& rEvt
)
1209 Point aDropPos
= rEvt
.maPosPixel
;
1211 // possible DropActions scroll and expand
1214 if (m_aDropActionTimer
.IsActive())
1215 m_aDropActionTimer
.Stop();
1219 bool bNeedTrigger
= false;
1221 if ((aDropPos
.Y() >= 0) && (aDropPos
.Y() < GetEntryHeight()))
1223 m_aDropActionType
= DA_SCROLLUP
;
1224 bNeedTrigger
= true;
1228 if ((aDropPos
.Y() < GetSizePixel().Height()) && (aDropPos
.Y() >= GetSizePixel().Height() - GetEntryHeight()))
1230 m_aDropActionType
= DA_SCROLLDOWN
;
1231 bNeedTrigger
= true;
1234 { // is it an entry with children, and not yet expanded?
1235 SvTreeListEntry
* pDroppedOn
= GetEntry(aDropPos
);
1236 if (pDroppedOn
&& (GetChildCount(pDroppedOn
) > 0) && !IsExpanded(pDroppedOn
))
1239 m_aDropActionType
= DA_EXPANDNODE
;
1240 bNeedTrigger
= true;
1244 if (bNeedTrigger
&& (m_aTimerTriggered
!= aDropPos
))
1246 m_aTimerCounter
= DROP_ACTION_TIMER_INITIAL_TICKS
;
1247 // remember DropPos because there are QueryDrops even though the mouse was not moved
1248 m_aTimerTriggered
= aDropPos
;
1249 if (!m_aDropActionTimer
.IsActive())
1251 m_aDropActionTimer
.SetTimeout(DROP_ACTION_TIMER_TICK_BASE
);
1252 m_aDropActionTimer
.Start();
1255 else if (!bNeedTrigger
)
1256 m_aDropActionTimer
.Stop();
1259 if (!m_aControlExchange
.isDragSource())
1260 return DND_ACTION_NONE
;
1262 if (!OFilterItemExchange::hasFormat(GetDataFlavorExVector()))
1263 return DND_ACTION_NONE
;
1265 // do we contain the formitem?
1266 if (!FindEntry(m_aControlExchange
->getFormItem()))
1267 return DND_ACTION_NONE
;
1269 SvTreeListEntry
* pDropTarget
= GetEntry(aDropPos
);
1271 return DND_ACTION_NONE
;
1273 FmFilterData
* pData
= static_cast<FmFilterData
*>(pDropTarget
->GetUserData());
1274 FmFormItem
* pForm
= nullptr;
1275 if (dynamic_cast<const FmFilterItem
*>(pData
) != nullptr)
1277 pForm
= dynamic_cast<FmFormItem
*>( pData
->GetParent()->GetParent() );
1278 if (pForm
!= m_aControlExchange
->getFormItem())
1279 return DND_ACTION_NONE
;
1281 else if (dynamic_cast<const FmFilterItems
*>( pData
) != nullptr)
1283 pForm
= dynamic_cast<FmFormItem
*>( pData
->GetParent() );
1284 if (pForm
!= m_aControlExchange
->getFormItem())
1285 return DND_ACTION_NONE
;
1288 return DND_ACTION_NONE
;
1290 return rEvt
.mnAction
;
1295 FmFilterItems
* getTargetItems(SvTreeListEntry
const * _pTarget
)
1297 FmFilterData
* pData
= static_cast<FmFilterData
*>(_pTarget
->GetUserData());
1298 FmFilterItems
* pTargetItems
= dynamic_cast<FmFilterItems
*>(pData
);
1300 pTargetItems
= dynamic_cast<FmFilterItems
*>(pData
->GetParent());
1301 return pTargetItems
;
1305 sal_Int8
FmFilterNavigator::ExecuteDrop( const ExecuteDropEvent
& rEvt
)
1307 // you can't scroll after dropping...
1308 if (m_aDropActionTimer
.IsActive())
1309 m_aDropActionTimer
.Stop();
1311 if (!m_aControlExchange
.isDragSource())
1312 return DND_ACTION_NONE
;
1314 Point aDropPos
= rEvt
.maPosPixel
;
1315 SvTreeListEntry
* pDropTarget
= GetEntry( aDropPos
);
1317 return DND_ACTION_NONE
;
1319 // search the container where to add the items
1320 FmFilterItems
* pTargetItems
= getTargetItems(pDropTarget
);
1322 SvTreeListEntry
* pEntry
= FindEntry(pTargetItems
);
1324 SetCurEntry(pEntry
);
1326 insertFilterItem(m_aControlExchange
->getDraggedEntries(),pTargetItems
,DND_ACTION_COPY
== rEvt
.mnAction
);
1328 return DND_ACTION_COPY
;
1332 void FmFilterNavigator::InitEntry(SvTreeListEntry
* pEntry
,
1333 const OUString
& rStr
,
1337 SvTreeListBox::InitEntry( pEntry
, rStr
, rImg1
, rImg2
);
1338 std::unique_ptr
<SvLBoxString
> pString
;
1340 if (dynamic_cast<const FmFilterItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1341 pString
.reset(new FmFilterString(rStr
,
1342 static_cast<FmFilterItem
*>(pEntry
->GetUserData())->GetFieldName()));
1343 else if (dynamic_cast<const FmFilterItems
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1344 pString
.reset(new FmFilterItemsString(rStr
));
1347 pEntry
->ReplaceItem(std::move(pString
), 1 );
1351 bool FmFilterNavigator::Select( SvTreeListEntry
* pEntry
, bool bSelect
)
1353 if (bSelect
== IsSelected(pEntry
)) // This happens sometimes. I think the basic class errs too much on the side of caution. ;)
1356 if (SvTreeListBox::Select(pEntry
, bSelect
))
1360 FmFormItem
* pFormItem
= nullptr;
1361 if ( dynamic_cast<const FmFilterItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1362 pFormItem
= static_cast<FmFormItem
*>(static_cast<FmFilterItem
*>(pEntry
->GetUserData())->GetParent()->GetParent());
1363 else if (dynamic_cast<const FmFilterItems
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1364 pFormItem
= static_cast<FmFormItem
*>(static_cast<FmFilterItem
*>(pEntry
->GetUserData())->GetParent()->GetParent());
1365 else if (dynamic_cast<const FmFormItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1366 pFormItem
= static_cast<FmFormItem
*>(pEntry
->GetUserData());
1370 // will the controller be exchanged?
1371 if (dynamic_cast<const FmFilterItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1372 m_pModel
->SetCurrentItems(static_cast<FmFilterItems
*>(static_cast<FmFilterItem
*>(pEntry
->GetUserData())->GetParent()));
1373 else if (dynamic_cast<const FmFilterItems
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1374 m_pModel
->SetCurrentItems(static_cast<FmFilterItems
*>(pEntry
->GetUserData()));
1375 else if (dynamic_cast<const FmFormItem
*>(static_cast<FmFilterData
*>(pEntry
->GetUserData())) != nullptr)
1376 m_pModel
->SetCurrentController(static_cast<FmFormItem
*>(pEntry
->GetUserData())->GetController());
1385 void FmFilterNavigator::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
1387 if (const FmFilterInsertedHint
* pInsertHint
= dynamic_cast<const FmFilterInsertedHint
*>(&rHint
))
1389 Insert(pInsertHint
->GetData(), pInsertHint
->GetPos());
1391 else if( dynamic_cast<const FilterClearingHint
*>(&rHint
) )
1393 SvTreeListBox::Clear();
1395 else if (const FmFilterRemovedHint
* pRemoveHint
= dynamic_cast<const FmFilterRemovedHint
*>(&rHint
))
1397 Remove(pRemoveHint
->GetData());
1399 else if (const FmFilterTextChangedHint
*pChangeHint
= dynamic_cast<const FmFilterTextChangedHint
*>(&rHint
))
1401 SvTreeListEntry
* pEntry
= FindEntry(pChangeHint
->GetData());
1403 SetEntryText( pEntry
, pChangeHint
->GetData()->GetText());
1405 else if( dynamic_cast<const FmFilterCurrentChangedHint
*>(&rHint
) )
1407 // invalidate the entries
1408 for (SvTreeListEntry
* pEntry
= First(); pEntry
!= nullptr;
1409 pEntry
= Next(pEntry
))
1410 GetModel()->InvalidateEntry( pEntry
);
1414 SvTreeListEntry
* FmFilterNavigator::FindEntry(const FmFilterData
* pItem
) const
1416 SvTreeListEntry
* pEntry
= nullptr;
1419 for (pEntry
= First(); pEntry
!= nullptr; pEntry
= Next( pEntry
))
1421 FmFilterData
* pEntryItem
= static_cast<FmFilterData
*>(pEntry
->GetUserData());
1422 if (pEntryItem
== pItem
)
1430 void FmFilterNavigator::Insert(FmFilterData
* pItem
, sal_uLong nPos
)
1432 const FmParentData
* pParent
= pItem
->GetParent() ? pItem
->GetParent() : GetFilterModel();
1435 SvTreeListEntry
* pParentEntry
= FindEntry( pParent
);
1436 InsertEntry( pItem
->GetText(), pItem
->GetImage(), pItem
->GetImage(), pParentEntry
, false, nPos
, pItem
);
1438 Expand( pParentEntry
);
1442 void FmFilterNavigator::Remove(FmFilterData
const * pItem
)
1444 // the entry for the data
1445 SvTreeListEntry
* pEntry
= FindEntry(pItem
);
1447 if (pEntry
== m_pEditingCurrently
)
1452 GetModel()->Remove( pEntry
);
1455 FmFormItem
* FmFilterNavigator::getSelectedFilterItems(::std::vector
<FmFilterItem
*>& _rItemList
)
1457 // be sure that the data is only used within only one form!
1458 FmFormItem
* pFirstItem
= nullptr;
1460 bool bHandled
= true;
1461 bool bFoundSomething
= false;
1462 for (SvTreeListEntry
* pEntry
= FirstSelected();
1463 bHandled
&& pEntry
!= nullptr;
1464 pEntry
= NextSelected(pEntry
))
1466 FmFilterItem
* pFilter
= dynamic_cast<FmFilterItem
*>( static_cast<FmFilterData
*>(pEntry
->GetUserData()) );
1469 FmFormItem
* pForm
= dynamic_cast<FmFormItem
*>( pFilter
->GetParent()->GetParent() );
1472 else if (!pFirstItem
)
1474 else if (pFirstItem
!= pForm
)
1479 _rItemList
.push_back(pFilter
);
1480 bFoundSomething
= true;
1484 if ( !bHandled
|| !bFoundSomething
)
1485 pFirstItem
= nullptr;
1489 void FmFilterNavigator::insertFilterItem(const ::std::vector
<FmFilterItem
*>& _rFilterList
,FmFilterItems
* _pTargetItems
,bool _bCopy
)
1491 for (FmFilterItem
* pLookupItem
: _rFilterList
)
1493 if ( pLookupItem
->GetParent() == _pTargetItems
)
1496 FmFilterItem
* pFilterItem
= _pTargetItems
->Find( pLookupItem
->GetComponentIndex() );
1497 OUString aText
= pLookupItem
->GetText();
1500 pFilterItem
= new FmFilterItem( _pTargetItems
, pLookupItem
->GetFieldName(), aText
, pLookupItem
->GetComponentIndex() );
1501 m_pModel
->Append( _pTargetItems
, std::unique_ptr
<FmFilterItem
>(pFilterItem
) );
1505 m_pModel
->Remove( pLookupItem
);
1507 // now set the text for the new dragged item
1508 m_pModel
->SetTextForItem( pFilterItem
, aText
);
1511 m_pModel
->EnsureEmptyFilterRows( *_pTargetItems
->GetParent() );
1515 void FmFilterNavigator::StartDrag( sal_Int8
/*_nAction*/, const Point
& /*_rPosPixel*/ )
1519 // be sure that the data is only used within an only one form!
1520 m_aControlExchange
.prepareDrag();
1522 ::std::vector
<FmFilterItem
*> aItemList
;
1523 if ( FmFormItem
* pFirstItem
= getSelectedFilterItems(aItemList
) )
1525 m_aControlExchange
->setDraggedEntries(aItemList
);
1526 m_aControlExchange
->setFormItem(pFirstItem
);
1527 m_aControlExchange
.startDrag( DND_ACTION_COPYMOVE
);
1532 void FmFilterNavigator::Command( const CommandEvent
& rEvt
)
1534 bool bHandled
= false;
1535 switch (rEvt
.GetCommand())
1537 case CommandEventId::ContextMenu
:
1539 // the place where it was clicked
1541 SvTreeListEntry
* pClicked
= nullptr;
1542 if (rEvt
.IsMouseEvent())
1544 aWhere
= rEvt
.GetMousePosPixel();
1545 pClicked
= GetEntry(aWhere
);
1546 if (pClicked
== nullptr)
1549 if (!IsSelected(pClicked
))
1553 SetCurEntry(pClicked
);
1558 pClicked
= GetCurEntry();
1561 aWhere
= GetEntryPosition( pClicked
);
1564 ::std::vector
<FmFilterData
*> aSelectList
;
1565 for (SvTreeListEntry
* pEntry
= FirstSelected();
1567 pEntry
= NextSelected(pEntry
))
1569 // don't delete forms
1570 FmFormItem
* pForm
= dynamic_cast<FmFormItem
*>( static_cast<FmFilterData
*>(pEntry
->GetUserData()) );
1572 aSelectList
.push_back(static_cast<FmFilterData
*>(pEntry
->GetUserData()));
1574 if (aSelectList
.size() == 1)
1576 // don't delete the only empty row of a form
1577 FmFilterItems
* pFilterItems
= dynamic_cast<FmFilterItems
*>( aSelectList
[0] );
1578 if (pFilterItems
&& pFilterItems
->GetChildren().empty()
1579 && pFilterItems
->GetParent()->GetChildren().size() == 1)
1580 aSelectList
.clear();
1583 VclBuilder
aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/filtermenu.ui", "");
1584 VclPtr
<PopupMenu
> aContextMenu(aBuilder
.get_menu("menu"));
1586 // every condition could be deleted except the first one if it's the only one
1587 aContextMenu
->EnableItem(aContextMenu
->GetItemId("delete"), !aSelectList
.empty());
1590 bool bEdit
= dynamic_cast<FmFilterItem
*>( static_cast<FmFilterData
*>(pClicked
->GetUserData()) ) != nullptr &&
1591 IsSelected(pClicked
) && GetSelectionCount() == 1;
1593 aContextMenu
->EnableItem(aContextMenu
->GetItemId("edit"), bEdit
);
1594 aContextMenu
->EnableItem(aContextMenu
->GetItemId("isnull"), bEdit
);
1595 aContextMenu
->EnableItem(aContextMenu
->GetItemId("isnotnull"), bEdit
);
1597 aContextMenu
->RemoveDisabledEntries(true, true);
1598 aContextMenu
->Execute(this, aWhere
);
1599 OString sIdent
= aContextMenu
->GetCurItemIdent();
1600 if (sIdent
== "edit")
1601 EditEntry( pClicked
);
1602 else if (sIdent
== "isnull")
1605 OUString aText
= "IS NULL";
1606 m_pModel
->ValidateText(static_cast<FmFilterItem
*>(pClicked
->GetUserData()),
1608 m_pModel
->SetTextForItem(static_cast<FmFilterItem
*>(pClicked
->GetUserData()), aText
);
1610 else if (sIdent
== "isnotnull")
1613 OUString aText
= "IS NOT NULL";
1615 m_pModel
->ValidateText(static_cast<FmFilterItem
*>(pClicked
->GetUserData()),
1617 m_pModel
->SetTextForItem(static_cast<FmFilterItem
*>(pClicked
->GetUserData()), aText
);
1619 else if (sIdent
== "delete")
1630 SvTreeListBox::Command( rEvt
);
1633 SvTreeListEntry
* FmFilterNavigator::getNextEntry(SvTreeListEntry
* _pStartWith
)
1635 SvTreeListEntry
* pEntry
= _pStartWith
? _pStartWith
: LastSelected();
1636 pEntry
= Next(pEntry
);
1637 // we need the next filter entry
1638 while( pEntry
&& GetChildCount( pEntry
) == 0 && pEntry
!= Last() )
1639 pEntry
= Next(pEntry
);
1643 SvTreeListEntry
* FmFilterNavigator::getPrevEntry(SvTreeListEntry
* _pStartWith
)
1645 SvTreeListEntry
* pEntry
= _pStartWith
? _pStartWith
: FirstSelected();
1646 pEntry
= Prev(pEntry
);
1647 // check if the previous entry is a filter, if so get the next prev
1648 if ( pEntry
&& GetChildCount( pEntry
) != 0 )
1650 pEntry
= Prev(pEntry
);
1651 // if the entry is still no leaf return
1652 if ( pEntry
&& GetChildCount( pEntry
) != 0 )
1658 void FmFilterNavigator::KeyInput(const KeyEvent
& rKEvt
)
1660 const vcl::KeyCode
& rKeyCode
= rKEvt
.GetKeyCode();
1662 switch ( rKeyCode
.GetCode() )
1667 if ( !rKeyCode
.IsMod1() || !rKeyCode
.IsMod2() || rKeyCode
.IsShift() )
1670 ::std::vector
<FmFilterItem
*> aItemList
;
1671 if ( !getSelectedFilterItems( aItemList
) )
1674 ::std::function
<SvTreeListEntry
*(FmFilterNavigator
*, SvTreeListEntry
*)> getter
= ::std::mem_fn(&FmFilterNavigator::getNextEntry
);
1675 if ( rKeyCode
.GetCode() == KEY_UP
)
1676 getter
= ::std::mem_fn(&FmFilterNavigator::getPrevEntry
);
1678 SvTreeListEntry
* pTarget
= getter( this, nullptr );
1682 FmFilterItems
* pTargetItems
= getTargetItems( pTarget
);
1683 if ( !pTargetItems
)
1686 ::std::vector
<FmFilterItem
*>::const_iterator aEnd
= aItemList
.end();
1687 bool bNextTargetItem
= true;
1688 while ( bNextTargetItem
)
1690 ::std::vector
<FmFilterItem
*>::const_iterator i
= aItemList
.begin();
1691 for (; i
!= aEnd
; ++i
)
1693 if ( (*i
)->GetParent() == pTargetItems
)
1695 pTarget
= getter(this,pTarget
);
1698 pTargetItems
= getTargetItems( pTarget
);
1703 FmFilterItem
* pFilterItem
= pTargetItems
->Find( (*i
)->GetComponentIndex() );
1704 // we found the text component so jump above
1707 pTarget
= getter( this, pTarget
);
1711 pTargetItems
= getTargetItems( pTarget
);
1716 bNextTargetItem
= i
!= aEnd
&& pTargetItems
;
1721 insertFilterItem( aItemList
, pTargetItems
, false );
1729 if ( rKeyCode
.GetModifier() )
1732 if ( !IsSelected( First() ) || GetEntryCount() > 1 )
1738 SvTreeListBox::KeyInput(rKEvt
);
1742 void FmFilterNavigator::DeleteSelection()
1744 // to avoid the deletion of an entry twice (e.g. deletion of a parent and afterward
1745 // the deletion of its child, I have to shrink the selection list
1746 ::std::vector
<SvTreeListEntry
*> aEntryList
;
1747 for (SvTreeListEntry
* pEntry
= FirstSelected();
1749 pEntry
= NextSelected(pEntry
))
1751 FmFilterItem
* pFilterItem
= dynamic_cast<FmFilterItem
*>( static_cast<FmFilterData
*>(pEntry
->GetUserData()) );
1752 if (pFilterItem
&& IsSelected(GetParent(pEntry
)))
1755 FmFormItem
* pForm
= dynamic_cast<FmFormItem
*>( static_cast<FmFilterData
*>(pEntry
->GetUserData()) );
1757 aEntryList
.push_back(pEntry
);
1760 // Remove the selection
1763 for (::std::vector
<SvTreeListEntry
*>::reverse_iterator i
= aEntryList
.rbegin();
1764 // link problems with operator ==
1765 i
.base() != aEntryList
.rend().base(); ++i
)
1767 m_pModel
->Remove(static_cast<FmFilterData
*>((*i
)->GetUserData()));
1771 FmFilterNavigatorWin::FmFilterNavigatorWin( SfxBindings
* _pBindings
, SfxChildWindow
* _pMgr
,
1772 vcl::Window
* _pParent
)
1773 :SfxDockingWindow( _pBindings
, _pMgr
, _pParent
, WinBits(WB_STDMODELESS
|WB_SIZEABLE
|WB_ROLLABLE
|WB_3DLOOK
|WB_DOCKABLE
) )
1774 ,SfxControllerItem( SID_FM_FILTER_NAVIGATOR_CONTROL
, *_pBindings
)
1776 SetHelpId( HID_FILTER_NAVIGATOR_WIN
);
1778 m_pNavigator
= VclPtr
<FmFilterNavigator
>::Create( this );
1779 m_pNavigator
->Show();
1780 SetText( SvxResId(RID_STR_FILTER_NAVIGATOR
) );
1781 SfxDockingWindow::SetFloatingSize( Size(200,200) );
1785 FmFilterNavigatorWin::~FmFilterNavigatorWin()
1790 void FmFilterNavigatorWin::dispose()
1792 m_pNavigator
.disposeAndClear();
1793 ::SfxControllerItem::dispose();
1794 SfxDockingWindow::dispose();
1798 void FmFilterNavigatorWin::UpdateContent(FmFormShell
const * pFormShell
)
1804 m_pNavigator
->UpdateContent( nullptr, nullptr );
1807 Reference
<XFormController
> const xController(pFormShell
->GetImpl()->getActiveInternalController_Lock());
1808 Reference
< XIndexAccess
> xContainer
;
1809 if (xController
.is())
1811 Reference
< XChild
> xChild
= xController
;
1812 for (Reference
< XInterface
> xParent(xChild
->getParent());
1814 xParent
= xChild
.is() ? xChild
->getParent() : Reference
< XInterface
> ())
1816 xContainer
.set(xParent
, UNO_QUERY
);
1817 xChild
.set(xParent
, UNO_QUERY
);
1820 m_pNavigator
->UpdateContent(xContainer
, xController
);
1825 void FmFilterNavigatorWin::StateChanged( sal_uInt16 nSID
, SfxItemState eState
, const SfxPoolItem
* pState
)
1827 if( !pState
|| SID_FM_FILTER_NAVIGATOR_CONTROL
!= nSID
)
1830 if( eState
>= SfxItemState::DEFAULT
)
1832 FmFormShell
* pShell
= dynamic_cast<FmFormShell
*>( static_cast<const SfxObjectItem
*>(pState
)->GetShell() );
1833 UpdateContent( pShell
);
1836 UpdateContent( nullptr );
1840 bool FmFilterNavigatorWin::Close()
1842 if ( m_pNavigator
&& m_pNavigator
->IsEditingActive() )
1843 m_pNavigator
->EndEditing();
1845 if ( m_pNavigator
&& m_pNavigator
->IsEditingActive() )
1846 // the EndEditing was vetoed (perhaps of an syntax error or such)
1849 UpdateContent( nullptr );
1850 return SfxDockingWindow::Close();
1854 void FmFilterNavigatorWin::FillInfo( SfxChildWinInfo
& rInfo
) const
1856 SfxDockingWindow::FillInfo( rInfo
);
1857 rInfo
.bVisible
= false;
1861 Size
FmFilterNavigatorWin::CalcDockingSize( SfxChildAlignment eAlign
)
1863 if ( ( eAlign
== SfxChildAlignment::TOP
) || ( eAlign
== SfxChildAlignment::BOTTOM
) )
1866 return SfxDockingWindow::CalcDockingSize( eAlign
);
1870 SfxChildAlignment
FmFilterNavigatorWin::CheckAlignment( SfxChildAlignment eActAlign
, SfxChildAlignment eAlign
)
1874 case SfxChildAlignment::LEFT
:
1875 case SfxChildAlignment::RIGHT
:
1876 case SfxChildAlignment::NOALIGNMENT
:
1886 void FmFilterNavigatorWin::Resize()
1888 SfxDockingWindow::Resize();
1890 Size aLogOutputSize
= PixelToLogic(GetOutputSizePixel(), MapMode(MapUnit::MapAppFont
));
1891 Size aLogExplSize
= aLogOutputSize
;
1892 aLogExplSize
.AdjustWidth( -6 );
1893 aLogExplSize
.AdjustHeight( -6 );
1895 Point aExplPos
= LogicToPixel(Point(3,3), MapMode(MapUnit::MapAppFont
));
1896 Size aExplSize
= LogicToPixel(aLogExplSize
, MapMode(MapUnit::MapAppFont
));
1898 m_pNavigator
->SetPosSizePixel( aExplPos
, aExplSize
);
1901 void FmFilterNavigatorWin::GetFocus()
1905 m_pNavigator
->GrabFocus();
1908 SFX_IMPL_DOCKINGWINDOW( FmFilterNavigatorWinMgr
, SID_FM_FILTER_NAVIGATOR
)
1911 FmFilterNavigatorWinMgr::FmFilterNavigatorWinMgr( vcl::Window
*_pParent
, sal_uInt16 _nId
,
1912 SfxBindings
*_pBindings
, SfxChildWinInfo
* _pInfo
)
1913 :SfxChildWindow( _pParent
, _nId
)
1915 SetWindow( VclPtr
<FmFilterNavigatorWin
>::Create( _pBindings
, this, _pParent
) );
1916 static_cast<SfxDockingWindow
*>(GetWindow())->Initialize( _pInfo
);
1922 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */