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 .
20 #include "dbtreelistbox.hxx"
21 #include "dbu_resource.hrc"
22 #include "browserids.hxx"
23 #include "listviewitems.hxx"
24 #include "callbacks.hxx"
26 #include <com/sun/star/datatransfer/dnd/XDragGestureListener.hpp>
27 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
28 #include <com/sun/star/ui/XContextMenuInterceptor.hpp>
29 #include <com/sun/star/frame/XFrame.hpp>
30 #include <com/sun/star/util/URL.hpp>
31 #include <cppuhelper/implbase.hxx>
32 #include <comphelper/interfacecontainer2.hxx>
33 #include <vcl/help.hxx>
34 #include <vcl/commandinfoprovider.hxx>
35 #include <dbaccess/IController.hxx>
36 #include <framework/actiontriggerhelper.hxx>
37 #include <toolkit/helper/vclunohelper.hxx>
38 #include <vcl/svapp.hxx>
39 #include "svtools/treelistentry.hxx"
42 #include <o3tl/make_unique.hxx>
46 using namespace ::com::sun::star
;
47 using namespace ::com::sun::star::uno
;
48 using namespace ::com::sun::star::beans
;
49 using namespace ::com::sun::star::lang
;
50 using namespace ::com::sun::star::datatransfer
;
51 using namespace ::com::sun::star::frame
;
52 using namespace ::com::sun::star::ui
;
53 using namespace ::com::sun::star::view
;
55 #define SPACEBETWEENENTRIES 4
56 // class DBTreeListBox
57 DBTreeListBox::DBTreeListBox( vcl::Window
* pParent
, WinBits nWinStyle
)
58 :SvTreeListBox(pParent
,nWinStyle
)
59 ,m_pDragedEntry(nullptr)
60 ,m_pActionListener(nullptr)
61 ,m_pContextMenuProvider( nullptr )
62 ,m_bHandleEnterKey(false)
67 void DBTreeListBox::init()
69 sal_uInt16 nSize
= SPACEBETWEENENTRIES
;
70 SetSpaceBetweenEntries(nSize
);
72 m_aTimer
.SetTimeout(900);
73 m_aTimer
.SetTimeoutHdl(LINK(this, DBTreeListBox
, OnTimeOut
));
75 m_aScrollHelper
.setUpScrollMethod( LINK(this, DBTreeListBox
, ScrollUpHdl
) );
76 m_aScrollHelper
.setDownScrollMethod( LINK(this, DBTreeListBox
, ScrollDownHdl
) );
78 SetNodeDefaultImages( );
80 EnableContextMenuHandling();
82 SetStyle( GetStyle() | WB_QUICK_SEARCH
);
85 DBTreeListBox::~DBTreeListBox()
90 void DBTreeListBox::dispose()
92 implStopSelectionTimer();
93 SvTreeListBox::dispose();
96 SvTreeListEntry
* DBTreeListBox::GetEntryPosByName( const OUString
& aName
, SvTreeListEntry
* pStart
, const IEntryFilter
* _pFilter
) const
98 SvTreeList
* myModel
= GetModel();
99 std::pair
<SvTreeListEntries::const_iterator
,SvTreeListEntries::const_iterator
> aIters
=
100 myModel
->GetChildIterators(pStart
);
102 SvTreeListEntry
* pEntry
= nullptr;
103 SvTreeListEntries::const_iterator it
= aIters
.first
, itEnd
= aIters
.second
;
104 for (; it
!= itEnd
; ++it
)
106 pEntry
= (*it
).get();
107 const SvLBoxString
* pItem
= static_cast<const SvLBoxString
*>(
108 pEntry
->GetFirstItem(SV_ITEM_ID_LBOXSTRING
));
110 if (pItem
&& pItem
->GetText().equals(aName
))
112 if (!_pFilter
|| _pFilter
->includeEntry(pEntry
))
122 void DBTreeListBox::EnableExpandHandler(SvTreeListEntry
* _pEntry
)
124 LINK(this, DBTreeListBox
, OnResetEntry
).Call(_pEntry
);
127 void DBTreeListBox::RequestingChildren( SvTreeListEntry
* pParent
)
129 if (m_aPreExpandHandler
.IsSet() && !m_aPreExpandHandler
.Call(pParent
))
131 // an error occurred. The method calling us will reset the entry flags, so it can't be expanded again.
132 // But we want that the user may do a second try (i.e. because he misstypes a password in this try), so
133 // we have to reset these flags controlling the expand ability
134 PostUserEvent(LINK(this, DBTreeListBox
, OnResetEntry
), pParent
, true);
138 void DBTreeListBox::InitEntry(SvTreeListEntry
* _pEntry
, const OUString
& aStr
, const Image
& _rCollEntryBmp
, const Image
& _rExpEntryBmp
, SvLBoxButtonKind eButtonKind
)
140 SvTreeListBox::InitEntry( _pEntry
, aStr
, _rCollEntryBmp
,_rExpEntryBmp
, eButtonKind
);
141 SvLBoxItem
* pTextItem(_pEntry
->GetFirstItem(SV_ITEM_ID_LBOXSTRING
));
142 _pEntry
->ReplaceItem(o3tl::make_unique
<OBoldListboxString
>(aStr
), _pEntry
->GetPos(pTextItem
));
145 void DBTreeListBox::implStopSelectionTimer()
147 if ( m_aTimer
.IsActive() )
151 void DBTreeListBox::implStartSelectionTimer()
153 implStopSelectionTimer();
157 void DBTreeListBox::DeselectHdl()
159 m_aSelectedEntries
.erase( GetHdlEntry() );
160 SvTreeListBox::DeselectHdl();
161 implStartSelectionTimer();
164 void DBTreeListBox::SelectHdl()
166 m_aSelectedEntries
.insert( GetHdlEntry() );
167 SvTreeListBox::SelectHdl();
168 implStartSelectionTimer();
171 void DBTreeListBox::MouseButtonDown( const MouseEvent
& rMEvt
)
173 bool bHitEmptySpace
= (nullptr == GetEntry(rMEvt
.GetPosPixel(), true));
174 if (bHitEmptySpace
&& (rMEvt
.GetClicks() == 2) && rMEvt
.IsMod1())
175 Control::MouseButtonDown(rMEvt
);
177 SvTreeListBox::MouseButtonDown(rMEvt
);
180 IMPL_LINK_TYPED(DBTreeListBox
, OnResetEntry
, void*, p
, void)
182 SvTreeListEntry
* pEntry
= static_cast<SvTreeListEntry
*>(p
);
183 // set the flag which allows if the entry can be expanded
184 pEntry
->SetFlags( (pEntry
->GetFlags() & ~SvTLEntryFlags(SvTLEntryFlags::NO_NODEBMP
| SvTLEntryFlags::HAD_CHILDREN
)) | SvTLEntryFlags::CHILDREN_ON_DEMAND
);
186 GetModel()->InvalidateEntry( pEntry
);
189 void DBTreeListBox::ModelHasEntryInvalidated( SvTreeListEntry
* _pEntry
)
191 SvTreeListBox::ModelHasEntryInvalidated( _pEntry
);
193 SvTreeListEntry
* pLBEntry
= static_cast<SvTreeListEntry
*>(_pEntry
);
194 if (m_aSelectedEntries
.find(pLBEntry
) != m_aSelectedEntries
.end())
196 SvLBoxItem
* pTextItem
= pLBEntry
->GetFirstItem(SV_ITEM_ID_BOLDLBSTRING
);
197 if ( pTextItem
&& !static_cast< OBoldListboxString
* >( pTextItem
)->isEmphasized() )
199 implStopSelectionTimer();
200 m_aSelectedEntries
.erase(pLBEntry
);
206 void DBTreeListBox::ModelHasRemoved( SvTreeListEntry
* _pEntry
)
208 SvTreeListBox::ModelHasRemoved(_pEntry
);
209 SvTreeListEntry
* pLBEntry
= static_cast<SvTreeListEntry
*>(_pEntry
);
210 if (m_aSelectedEntries
.find(pLBEntry
) != m_aSelectedEntries
.end())
212 implStopSelectionTimer();
213 m_aSelectedEntries
.erase(pLBEntry
);
217 sal_Int8
DBTreeListBox::AcceptDrop( const AcceptDropEvent
& _rEvt
)
219 sal_Int8 nDropOption
= DND_ACTION_NONE
;
220 if ( m_pActionListener
)
222 SvTreeListEntry
* pDroppedEntry
= GetEntry(_rEvt
.maPosPixel
);
223 // check if drag is on child entry, which is not allowed
224 SvTreeListEntry
* pParent
= nullptr;
225 if ( _rEvt
.mnAction
& DND_ACTION_MOVE
)
227 if ( !m_pDragedEntry
) // no entry to move
229 nDropOption
= m_pActionListener
->queryDrop( _rEvt
, GetDataFlavorExVector() );
230 m_aMousePos
= _rEvt
.maPosPixel
;
231 m_aScrollHelper
.scroll(m_aMousePos
,GetOutputSizePixel());
235 pParent
= pDroppedEntry
? GetParent(pDroppedEntry
) : nullptr;
236 while ( pParent
&& pParent
!= m_pDragedEntry
)
237 pParent
= GetParent(pParent
);
242 nDropOption
= m_pActionListener
->queryDrop( _rEvt
, GetDataFlavorExVector() );
243 // check if move is allowed
244 if ( nDropOption
& DND_ACTION_MOVE
)
246 if ( m_pDragedEntry
== pDroppedEntry
|| GetEntryPosByName(GetEntryText(m_pDragedEntry
),pDroppedEntry
) )
247 nDropOption
= nDropOption
& ~DND_ACTION_MOVE
;//DND_ACTION_NONE;
249 m_aMousePos
= _rEvt
.maPosPixel
;
250 m_aScrollHelper
.scroll(m_aMousePos
,GetOutputSizePixel());
257 sal_Int8
DBTreeListBox::ExecuteDrop( const ExecuteDropEvent
& _rEvt
)
259 if ( m_pActionListener
)
260 return m_pActionListener
->executeDrop( _rEvt
);
262 return DND_ACTION_NONE
;
265 void DBTreeListBox::StartDrag( sal_Int8 _nAction
, const Point
& _rPosPixel
)
267 if ( m_pActionListener
)
269 m_pDragedEntry
= GetEntry(_rPosPixel
);
270 if ( m_pDragedEntry
&& m_pActionListener
->requestDrag( _nAction
, _rPosPixel
) )
272 // if the (asynchronous) drag started, stop the selection timer
273 implStopSelectionTimer();
274 // and stop selecting entries by simply moving the mouse
280 void DBTreeListBox::RequestHelp( const HelpEvent
& rHEvt
)
282 if ( !m_pActionListener
)
284 SvTreeListBox::RequestHelp( rHEvt
);
288 if( rHEvt
.GetMode() & HelpEventMode::QUICK
)
290 Point
aPos( ScreenToOutputPixel( rHEvt
.GetMousePosPixel() ));
291 SvTreeListEntry
* pEntry
= GetEntry( aPos
);
294 OUString sQuickHelpText
;
295 if ( m_pActionListener
->requestQuickHelp( pEntry
, sQuickHelpText
) )
297 Size
aSize( GetOutputSizePixel().Width(), GetEntryHeight() );
298 Rectangle
aScreenRect( OutputToScreenPixel( GetEntryPosition( pEntry
) ), aSize
);
300 Help::ShowQuickHelp( this, aScreenRect
,
301 sQuickHelpText
, QuickHelpFlags::Left
| QuickHelpFlags::VCenter
);
307 SvTreeListBox::RequestHelp( rHEvt
);
310 void DBTreeListBox::KeyInput( const KeyEvent
& rKEvt
)
312 KeyFuncType eFunc
= rKEvt
.GetKeyCode().GetFunction();
313 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
314 bool bHandled
= false;
316 if(eFunc
!= KeyFuncType::DONTKNOW
)
320 case KeyFuncType::COPY
:
321 bHandled
= ( m_aCopyHandler
.IsSet() && !m_aSelectedEntries
.empty() );
323 m_aCopyHandler
.Call( nullptr );
325 case KeyFuncType::PASTE
:
326 bHandled
= ( m_aPasteHandler
.IsSet() && !m_aSelectedEntries
.empty() );
328 m_aPasteHandler
.Call( nullptr );
330 case KeyFuncType::DELETE
:
331 bHandled
= ( m_aDeleteHandler
.IsSet() && !m_aSelectedEntries
.empty() );
333 m_aDeleteHandler
.Call( nullptr );
340 if ( KEY_RETURN
== nCode
)
342 bHandled
= m_bHandleEnterKey
;
343 m_aEnterKeyHdl
.Call(this);
344 // this is a HACK. If the data source browser is opened in the "beamer", while the main frame
346 // contains a writer document, then pressing enter in the DSB would be rerouted to the writer
348 // document if we would not do this hack here.
349 // The problem is that the Writer uses RETURN as _accelerator_ (which is quite weird itself),
351 // so the SFX framework is _obligated_ to pass it to the Writer if nobody else handled it. There
353 // is no chance to distinguish between
354 // "accelerators which are to be executed if the main document has the focus"
356 // "accelerators which are always to be executed"
358 // Thus we cannot prevent the handling of this key in the writer without declaring the key event
359 // as "handled" herein.
361 // The bad thing about this approach is that it does not scale. Every other accelerator which
362 // is used by the document will raise a similar bug once somebody discovers it.
363 // If this is the case, we should discuss a real solution with the framework (SFX) and the
368 SvTreeListBox::KeyInput(rKEvt
);
371 bool DBTreeListBox::EditingEntry( SvTreeListEntry
* /*pEntry*/, Selection
& /*_aSelection*/)
376 bool DBTreeListBox::EditedEntry( SvTreeListEntry
* pEntry
, const OUString
& rNewText
)
378 DBTreeEditedEntry aEntry
;
379 aEntry
.pEntry
= pEntry
;
380 aEntry
.aNewText
= rNewText
;
381 SetEntryText(pEntry
,aEntry
.aNewText
);
383 return false; // we never want that the base change our text
386 bool DBTreeListBox::DoubleClickHdl()
388 // continue default processing if the DoubleClickHandler didn't handle it
389 return !aDoubleClickHdl
.Call( this );
392 void scrollWindow(DBTreeListBox
* _pListBox
, const Point
& _rPos
,bool _bUp
)
394 SvTreeListEntry
* pEntry
= _pListBox
->GetEntry( _rPos
);
395 if( pEntry
&& pEntry
!= _pListBox
->Last() )
397 _pListBox
->ScrollOutputArea( _bUp
? -1 : 1 );
401 IMPL_LINK_NOARG_TYPED( DBTreeListBox
, ScrollUpHdl
, LinkParamNone
*, void )
403 scrollWindow(this,m_aMousePos
,true);
406 IMPL_LINK_NOARG_TYPED( DBTreeListBox
, ScrollDownHdl
, LinkParamNone
*, void )
408 scrollWindow(this,m_aMousePos
,false);
413 void lcl_enableEntries( PopupMenu
* _pPopup
, IController
& _rController
)
418 sal_uInt16 nCount
= _pPopup
->GetItemCount();
419 for (sal_uInt16 i
=0; i
< nCount
; ++i
)
421 if ( _pPopup
->GetItemType(i
) != MenuItemType::SEPARATOR
)
423 sal_uInt16 nId
= _pPopup
->GetItemId(i
);
424 PopupMenu
* pSubPopUp
= _pPopup
->GetPopupMenu(nId
);
427 lcl_enableEntries( pSubPopUp
, _rController
);
428 _pPopup
->EnableItem(nId
,pSubPopUp
->HasValidEntries());
432 OUString
sCommandURL( _pPopup
->GetItemCommand( nId
) );
433 bool bEnabled
= sCommandURL
.isEmpty()
434 ? _rController
.isCommandEnabled( nId
)
435 : _rController
.isCommandEnabled( sCommandURL
);
436 _pPopup
->EnableItem( nId
, bEnabled
);
441 _pPopup
->RemoveDisabledEntries();
447 void lcl_adjustMenuItemIDs( Menu
& _rMenu
, IController
& _rCommandController
)
449 sal_uInt16 nCount
= _rMenu
.GetItemCount();
450 for ( sal_uInt16 pos
= 0; pos
< nCount
; ++pos
)
452 // do not adjust separators
453 if ( _rMenu
.GetItemType( pos
) == MenuItemType::SEPARATOR
)
456 sal_uInt16 nId
= _rMenu
.GetItemId(pos
);
457 OUString aCommand
= _rMenu
.GetItemCommand( nId
);
458 PopupMenu
* pPopup
= _rMenu
.GetPopupMenu( nId
);
461 lcl_adjustMenuItemIDs( *pPopup
, _rCommandController
);
465 const sal_uInt16 nCommandId
= _rCommandController
.registerCommandURL( aCommand
);
466 _rMenu
.InsertItem( nCommandId
, _rMenu
.GetItemText( nId
), _rMenu
.GetItemImage( nId
),
467 _rMenu
.GetItemBits( nId
), OString(), pos
);
469 // more things to preserve:
470 // - the help command
471 OUString sHelpURL
= _rMenu
.GetHelpCommand( nId
);
472 if ( !sHelpURL
.isEmpty() )
473 _rMenu
.SetHelpCommand( nCommandId
, sHelpURL
);
475 // remove the "old" item
476 _rMenu
.RemoveItem( pos
+1 );
479 void lcl_insertMenuItemImages( Menu
& _rMenu
, IController
& _rCommandController
)
481 uno::Reference
< frame::XController
> xController
= _rCommandController
.getXController();
482 uno::Reference
< frame::XFrame
> xFrame
;
483 if ( xController
.is() )
484 xFrame
= xController
->getFrame();
485 sal_uInt16 nCount
= _rMenu
.GetItemCount();
486 for ( sal_uInt16 pos
= 0; pos
< nCount
; ++pos
)
488 // do not adjust separators
489 if ( _rMenu
.GetItemType( pos
) == MenuItemType::SEPARATOR
)
492 sal_uInt16 nId
= _rMenu
.GetItemId(pos
);
493 OUString aCommand
= _rMenu
.GetItemCommand( nId
);
494 PopupMenu
* pPopup
= _rMenu
.GetPopupMenu( nId
);
497 lcl_insertMenuItemImages( *pPopup
, _rCommandController
);
502 _rMenu
.SetItemImage(nId
, vcl::CommandInfoProvider::Instance().GetImageForCommand(aCommand
, false, xFrame
));
506 typedef ::cppu::WeakImplHelper
< XSelectionSupplier
507 > SelectionSupplier_Base
;
508 class SelectionSupplier
: public SelectionSupplier_Base
511 explicit SelectionSupplier( const Any
& _rSelection
)
512 :m_aSelection( _rSelection
)
516 virtual sal_Bool SAL_CALL
select( const Any
& xSelection
) throw (IllegalArgumentException
, RuntimeException
, std::exception
) override
;
517 virtual Any SAL_CALL
getSelection( ) throw (RuntimeException
, std::exception
) override
;
518 virtual void SAL_CALL
addSelectionChangeListener( const Reference
< XSelectionChangeListener
>& xListener
) throw (RuntimeException
, std::exception
) override
;
519 virtual void SAL_CALL
removeSelectionChangeListener( const Reference
< XSelectionChangeListener
>& xListener
) throw (RuntimeException
, std::exception
) override
;
522 virtual ~SelectionSupplier()
530 sal_Bool SAL_CALL
SelectionSupplier::select( const Any
& /*_Selection*/ ) throw (IllegalArgumentException
, RuntimeException
, std::exception
)
532 throw IllegalArgumentException();
533 // API bug: this should be a NoSupportException
536 Any SAL_CALL
SelectionSupplier::getSelection( ) throw (RuntimeException
, std::exception
)
541 void SAL_CALL
SelectionSupplier::addSelectionChangeListener( const Reference
< XSelectionChangeListener
>& /*_Listener*/ ) throw (RuntimeException
, std::exception
)
543 OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
544 // API bug: this should be a NoSupportException
547 void SAL_CALL
SelectionSupplier::removeSelectionChangeListener( const Reference
< XSelectionChangeListener
>& /*_Listener*/ ) throw (RuntimeException
, std::exception
)
549 OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
550 // API bug: this should be a NoSupportException
554 std::unique_ptr
<PopupMenu
> DBTreeListBox::CreateContextMenu()
556 ::std::unique_ptr
< PopupMenu
> pContextMenu
;
558 if ( !m_pContextMenuProvider
)
561 // the basic context menu
562 pContextMenu
.reset( m_pContextMenuProvider
->getContextMenu( *this ) );
563 // disable what is not available currently
564 lcl_enableEntries( pContextMenu
.get(), m_pContextMenuProvider
->getCommandController() );
566 lcl_insertMenuItemImages( *pContextMenu
, m_pContextMenuProvider
->getCommandController() );
567 // allow context menu interception
568 ::comphelper::OInterfaceContainerHelper2
* pInterceptors
= m_pContextMenuProvider
->getContextMenuInterceptors();
569 if ( !pInterceptors
|| !pInterceptors
->getLength() )
572 ContextMenuExecuteEvent aEvent
;
573 aEvent
.SourceWindow
= VCLUnoHelper::GetInterface( this );
574 aEvent
.ExecutePosition
.X
= -1;
575 aEvent
.ExecutePosition
.Y
= -1;
576 aEvent
.ActionTriggerContainer
= ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
577 pContextMenu
.get(), nullptr );
578 aEvent
.Selection
= new SelectionSupplier( m_pContextMenuProvider
->getCurrentSelection( *this ) );
580 ::comphelper::OInterfaceIteratorHelper2
aIter( *pInterceptors
);
581 bool bModifiedMenu
= false;
582 bool bAskInterceptors
= true;
583 while ( aIter
.hasMoreElements() && bAskInterceptors
)
585 Reference
< XContextMenuInterceptor
> xInterceptor( aIter
.next(), UNO_QUERY
);
586 if ( !xInterceptor
.is() )
591 ContextMenuInterceptorAction eAction
= xInterceptor
->notifyContextMenuExecute( aEvent
);
594 case ContextMenuInterceptorAction_CANCELLED
:
597 case ContextMenuInterceptorAction_EXECUTE_MODIFIED
:
598 bModifiedMenu
= true;
599 bAskInterceptors
= false;
602 case ContextMenuInterceptorAction_CONTINUE_MODIFIED
:
603 bModifiedMenu
= true;
604 bAskInterceptors
= true;
608 OSL_FAIL( "DBTreeListBox::CreateContextMenu: unexpected return value of the interceptor call!" );
610 case ContextMenuInterceptorAction_IGNORED
:
614 catch( const DisposedException
& e
)
616 if ( e
.Context
== xInterceptor
)
623 // the interceptor(s) modified the menu description => create a new PopupMenu
624 PopupMenu
* pModifiedMenu
= new PopupMenu
;
625 ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
626 pModifiedMenu
, aEvent
.ActionTriggerContainer
);
627 aEvent
.ActionTriggerContainer
.clear();
628 pContextMenu
.reset( pModifiedMenu
);
630 // the interceptors only know command URLs, but our menus primarily work
631 // with IDs -> we need to translate the commands to IDs
632 lcl_adjustMenuItemIDs( *pModifiedMenu
, m_pContextMenuProvider
->getCommandController() );
638 void DBTreeListBox::ExecuteContextMenuAction( sal_uInt16 _nSelectedPopupEntry
)
640 if ( m_pContextMenuProvider
&& _nSelectedPopupEntry
)
641 m_pContextMenuProvider
->getCommandController().executeChecked( _nSelectedPopupEntry
, Sequence
< PropertyValue
>() );
644 IMPL_LINK_NOARG_TYPED(DBTreeListBox
, OnTimeOut
, Timer
*, void)
646 implStopSelectionTimer();
648 m_aSelChangeHdl
.Call( nullptr );
651 void DBTreeListBox::StateChanged( StateChangedType nStateChange
)
653 if ( nStateChange
== StateChangedType::Visible
)
654 implStopSelectionTimer();
659 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */