nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / treelist / svimpbox.cxx
blobbab21eb7c1c730103e24fdc0fc3db2c81f721e97
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <sal/config.h>
22 #include <o3tl/safeint.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/salnativewidgets.hxx>
25 #include <vcl/help.hxx>
26 #include <vcl/settings.hxx>
27 #include <vcl/commandevent.hxx>
29 #include <cstdlib>
30 #include <memory>
31 #include <stack>
33 #include <vcl/toolkit/treelistbox.hxx>
34 #include <vcl/toolkit/svlbitm.hxx>
35 #include <tools/wintypes.hxx>
36 #include <bitmaps.hlst>
37 #include <svimpbox.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/string.hxx>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <tools/debug.hxx>
43 #include <vcl/toolkit/treelistentry.hxx>
44 #include <vcl/toolkit/viewdataentry.hxx>
46 // #i27063# (pl), #i32300# (pb) never access VCL after DeInitVCL - also no destructors
47 Image* SvImpLBox::s_pDefCollapsed = nullptr;
48 Image* SvImpLBox::s_pDefExpanded = nullptr;
49 oslInterlockedCount SvImpLBox::s_nImageRefCount = 0;
51 SvImpLBox::SvImpLBox( SvTreeListBox* pLBView, SvTreeList* pLBTree, WinBits nWinStyle)
52 : m_aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
53 , m_aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
54 , m_aFctSet(this, pLBView)
55 , mbForceMakeVisible (false)
56 , m_aVerSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_VSCROLL))
57 , m_aOutputSize(0, 0)
58 , mbNoAutoCurEntry(false)
59 , m_aSelEng(pLBView, nullptr)
60 , m_nNextVerVisSize(0)
62 osl_atomic_increment(&s_nImageRefCount);
63 m_pView = pLBView;
64 m_pTree = pLBTree;
65 m_aSelEng.SetFunctionSet( static_cast<FunctionSet*>(&m_aFctSet) );
66 m_aSelEng.ExpandSelectionOnMouseMove( false );
67 SetStyle( nWinStyle );
68 SetSelectionMode( SelectionMode::Single );
69 SetDragDropMode( DragDropMode::NONE );
71 m_aVerSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollUpDownHdl ) );
72 m_aHorSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollLeftRightHdl ) );
73 m_aHorSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
74 m_aVerSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
75 m_aVerSBar->SetRange( Range(0,0) );
76 m_aVerSBar->Hide();
77 m_aHorSBar->SetRange( Range(0,0) );
78 m_aHorSBar->SetPageSize( 24 ); // pixels
79 m_aHorSBar->SetLineSize( 8 ); // pixels
81 m_nHorSBarHeight = static_cast<short>(m_aHorSBar->GetSizePixel().Height());
82 m_nVerSBarWidth = static_cast<short>(m_aVerSBar->GetSizePixel().Width());
84 m_pStartEntry = nullptr;
85 m_pCursor = nullptr;
86 m_pAnchor = nullptr;
87 m_nVisibleCount = 0; // number of rows of data in control
88 m_nNodeBmpTabDistance = NODE_BMP_TABDIST_NOTVALID;
89 m_nNodeBmpWidth = 0;
91 // button animation in listbox
92 m_pActiveButton = nullptr;
93 m_pActiveEntry = nullptr;
94 m_pActiveTab = nullptr;
96 m_nFlags = LBoxFlags::NONE;
98 m_aEditIdle.SetPriority( TaskPriority::LOWEST );
99 m_aEditIdle.SetInvokeHandler( LINK(this,SvImpLBox,EditTimerCall) );
101 m_nMostRight = -1;
102 m_pMostRightEntry = nullptr;
103 m_nCurUserEvent = nullptr;
105 m_bUpdateMode = true;
106 m_bInVScrollHdl = false;
107 m_nFlags |= LBoxFlags::Filling;
109 m_bSubLstOpLR = false;
112 SvImpLBox::~SvImpLBox()
114 m_aEditIdle.Stop();
115 StopUserEvent();
117 if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
119 delete s_pDefCollapsed;
120 s_pDefCollapsed = nullptr;
121 delete s_pDefExpanded;
122 s_pDefExpanded = nullptr;
124 m_aVerSBar.disposeAndClear();
125 m_aHorSBar.disposeAndClear();
126 m_aScrBarBox.disposeAndClear();
129 void SvImpLBox::UpdateStringSorter()
131 const css::lang::Locale& rNewLocale = Application::GetSettings().GetLanguageTag().getLocale();
133 if( m_pStringSorter )
135 // different Locale from the older one, drop it and force recreate
136 const css::lang::Locale &aLocale = m_pStringSorter->getLocale();
137 if( aLocale.Language != rNewLocale.Language ||
138 aLocale.Country != rNewLocale.Country ||
139 aLocale.Variant != rNewLocale.Variant )
140 m_pStringSorter.reset();
143 if( !m_pStringSorter )
145 m_pStringSorter.reset(new comphelper::string::NaturalStringSorter(
146 ::comphelper::getProcessComponentContext(),
147 rNewLocale));
151 short SvImpLBox::UpdateContextBmpWidthVector( SvTreeListEntry const * pEntry, short nWidth )
153 DBG_ASSERT( m_pView->pModel, "View and Model aren't valid!" );
155 sal_uInt16 nDepth = m_pView->pModel->GetDepth( pEntry );
156 // initialize vector if necessary
157 std::vector< short >::size_type nSize = m_aContextBmpWidthVector.size();
158 while ( nDepth > nSize )
160 m_aContextBmpWidthVector.resize( nSize + 1 );
161 m_aContextBmpWidthVector.at( nSize ) = nWidth;
162 ++nSize;
164 if( m_aContextBmpWidthVector.size() == nDepth )
166 m_aContextBmpWidthVector.resize( nDepth + 1 );
167 m_aContextBmpWidthVector.at( nDepth ) = 0;
169 short nContextBmpWidth = m_aContextBmpWidthVector[ nDepth ];
170 if( nContextBmpWidth < nWidth )
172 m_aContextBmpWidthVector.at( nDepth ) = nWidth;
173 return nWidth;
175 else
176 return nContextBmpWidth;
179 void SvImpLBox::UpdateContextBmpWidthVectorFromMovedEntry( SvTreeListEntry* pEntry )
181 DBG_ASSERT( pEntry, "Moved Entry is invalid!" );
183 SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry->GetFirstItem(SvLBoxItemType::ContextBmp) );
184 short nExpWidth = static_cast<short>(pBmpItem->GetBitmap1().GetSizePixel().Width());
185 short nColWidth = static_cast<short>(pBmpItem->GetBitmap2().GetSizePixel().Width());
186 short nMax = std::max(nExpWidth, nColWidth);
187 UpdateContextBmpWidthVector( pEntry, nMax );
189 if( pEntry->HasChildren() ) // recursive call, whether expanded or not
191 SvTreeListEntry* pChild = m_pView->FirstChild( pEntry );
192 DBG_ASSERT( pChild, "The first child is invalid!" );
195 UpdateContextBmpWidthVectorFromMovedEntry( pChild );
196 pChild = m_pView->Next( pChild );
197 } while ( pChild );
201 void SvImpLBox::UpdateContextBmpWidthMax( SvTreeListEntry const * pEntry )
203 sal_uInt16 nDepth = m_pView->pModel->GetDepth( pEntry );
204 if( m_aContextBmpWidthVector.empty() )
205 return;
206 short nWidth = m_aContextBmpWidthVector[ nDepth ];
207 if( nWidth != m_pView->nContextBmpWidthMax ) {
208 m_pView->nContextBmpWidthMax = nWidth;
209 m_nFlags |= LBoxFlags::IgnoreChangedTabs;
210 m_pView->SetTabs();
211 m_nFlags &= ~LBoxFlags::IgnoreChangedTabs;
215 void SvImpLBox::SetStyle( WinBits i_nWinStyle )
217 m_nStyle = i_nWinStyle;
218 if ( ( m_nStyle & WB_SIMPLEMODE) && ( m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) )
219 m_aSelEng.AddAlways( true );
222 void SvImpLBox::SetNoAutoCurEntry( bool b )
224 mbNoAutoCurEntry = b;
227 // don't touch the model any more
228 void SvImpLBox::Clear()
230 StopUserEvent();
231 m_pStartEntry = nullptr;
232 m_pAnchor = nullptr;
234 m_pActiveButton = nullptr;
235 m_pActiveEntry = nullptr;
236 m_pActiveTab = nullptr;
238 m_nMostRight = -1;
239 m_pMostRightEntry = nullptr;
241 // don't touch the cursor any more
242 if( m_pCursor )
244 if( m_pView->HasFocus() )
245 m_pView->HideFocus();
246 m_pCursor = nullptr;
248 m_aVerSBar->Hide();
249 m_aVerSBar->SetThumbPos( 0 );
250 Range aRange( 0, 0 );
251 m_aVerSBar->SetRange( aRange );
252 m_aOutputSize = m_pView->Control::GetOutputSizePixel();
253 m_aHorSBar->Hide();
254 m_aHorSBar->SetThumbPos( 0 );
255 MapMode aMapMode( m_pView->GetMapMode());
256 aMapMode.SetOrigin( Point(0,0) );
257 m_pView->Control::SetMapMode( aMapMode );
258 m_aHorSBar->SetRange( aRange );
259 m_aHorSBar->SetSizePixel(Size(m_aOutputSize.Width(),m_nHorSBarHeight));
260 m_pView->SetClipRegion();
261 if( GetUpdateMode() )
262 m_pView->Invalidate( GetVisibleArea() );
263 m_nFlags |= LBoxFlags::Filling;
264 if( !m_aHorSBar->IsVisible() && !m_aVerSBar->IsVisible() )
265 m_aScrBarBox->Hide();
267 m_aContextBmpWidthVector.clear();
269 CallEventListeners( VclEventId::ListboxItemRemoved );
272 // *********************************************************************
273 // Paint, navigate, scroll
274 // *********************************************************************
276 IMPL_LINK_NOARG(SvImpLBox, EndScrollHdl, ScrollBar*, void)
278 if( m_nFlags & LBoxFlags::EndScrollSetVisSize )
280 m_aVerSBar->SetVisibleSize( m_nNextVerVisSize );
281 m_nFlags &= ~LBoxFlags::EndScrollSetVisSize;
285 // handler for vertical scrollbar
287 IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar, void )
289 DBG_ASSERT(!m_bInVScrollHdl,"Scroll handler out-paces itself!");
290 tools::Long nDelta = pScrollBar->GetDelta();
291 if( !nDelta )
292 return;
294 m_nFlags &= ~LBoxFlags::Filling;
296 m_bInVScrollHdl = true;
298 if( m_pView->IsEditingActive() )
300 m_pView->EndEditing( true ); // Cancel
301 m_pView->PaintImmediately();
304 if( nDelta > 0 )
306 if( nDelta == 1 )
307 CursorDown();
308 else
309 PageDown( static_cast<sal_uInt16>(nDelta) );
311 else
313 nDelta *= -1;
314 if( nDelta == 1 )
315 CursorUp();
316 else
317 PageUp( static_cast<sal_uInt16>(nDelta) );
319 m_bInVScrollHdl = false;
323 void SvImpLBox::CursorDown()
325 if (!m_pStartEntry)
326 return;
328 SvTreeListEntry* pNextFirstToDraw = m_pView->NextVisible(m_pStartEntry);
329 if( pNextFirstToDraw )
331 m_nFlags &= ~LBoxFlags::Filling;
332 ShowCursor( false );
333 m_pView->PaintImmediately();
334 m_pStartEntry = pNextFirstToDraw;
335 tools::Rectangle aArea( GetVisibleArea() );
336 m_pView->Scroll( 0, -(m_pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren );
337 m_pView->PaintImmediately();
338 ShowCursor( true );
339 m_pView->NotifyScrolled();
343 void SvImpLBox::CursorUp()
345 if (!m_pStartEntry)
346 return;
348 SvTreeListEntry* pPrevFirstToDraw = m_pView->PrevVisible(m_pStartEntry);
349 if( !pPrevFirstToDraw )
350 return;
352 m_nFlags &= ~LBoxFlags::Filling;
353 tools::Long nEntryHeight = m_pView->GetEntryHeight();
354 ShowCursor( false );
355 m_pView->PaintImmediately();
356 m_pStartEntry = pPrevFirstToDraw;
357 tools::Rectangle aArea( GetVisibleArea() );
358 aArea.AdjustBottom( -nEntryHeight );
359 m_pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren );
360 m_pView->PaintImmediately();
361 ShowCursor( true );
362 m_pView->NotifyScrolled();
365 void SvImpLBox::PageDown( sal_uInt16 nDelta )
367 sal_uInt16 nRealDelta = nDelta;
369 if( !nDelta )
370 return;
372 if (!m_pStartEntry)
373 return;
375 SvTreeListEntry* pNext = m_pView->NextVisible(m_pStartEntry, nRealDelta);
376 if( pNext == m_pStartEntry )
377 return;
379 ShowCursor( false );
381 m_nFlags &= ~LBoxFlags::Filling;
382 m_pStartEntry = pNext;
384 if( nRealDelta >= m_nVisibleCount )
386 m_pView->Invalidate( GetVisibleArea() );
387 m_pView->PaintImmediately();
389 else
391 tools::Rectangle aArea( GetVisibleArea() );
392 tools::Long nScroll = m_pView->GetEntryHeight() * static_cast<tools::Long>(nRealDelta);
393 nScroll = -nScroll;
394 m_pView->PaintImmediately();
395 m_pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren );
396 m_pView->PaintImmediately();
397 m_pView->NotifyScrolled();
400 ShowCursor( true );
403 void SvImpLBox::PageUp( sal_uInt16 nDelta )
405 sal_uInt16 nRealDelta = nDelta;
406 if( !nDelta )
407 return;
409 if (!m_pStartEntry)
410 return;
412 SvTreeListEntry* pPrev = m_pView->PrevVisible(m_pStartEntry, nRealDelta);
413 if( pPrev == m_pStartEntry )
414 return;
416 m_nFlags &= ~LBoxFlags::Filling;
417 ShowCursor( false );
419 m_pStartEntry = pPrev;
420 if( nRealDelta >= m_nVisibleCount )
422 m_pView->Invalidate( GetVisibleArea() );
423 m_pView->PaintImmediately();
425 else
427 tools::Long nEntryHeight = m_pView->GetEntryHeight();
428 tools::Rectangle aArea( GetVisibleArea() );
429 m_pView->PaintImmediately();
430 m_pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren );
431 m_pView->PaintImmediately();
432 m_pView->NotifyScrolled();
435 ShowCursor( true );
438 void SvImpLBox::KeyUp( bool bPageUp )
440 if( !m_aVerSBar->IsVisible() )
441 return;
443 tools::Long nDelta;
444 if( bPageUp )
445 nDelta = m_aVerSBar->GetPageSize();
446 else
447 nDelta = 1;
449 tools::Long nThumbPos = m_aVerSBar->GetThumbPos();
451 if( nThumbPos < nDelta )
452 nDelta = nThumbPos;
454 if( nDelta <= 0 )
455 return;
457 m_nFlags &= ~LBoxFlags::Filling;
459 m_aVerSBar->SetThumbPos( nThumbPos - nDelta );
460 if( bPageUp )
461 PageUp( static_cast<short>(nDelta) );
462 else
463 CursorUp();
467 void SvImpLBox::KeyDown( bool bPageDown )
469 if( !m_aVerSBar->IsVisible() )
470 return;
472 tools::Long nDelta;
473 if( bPageDown )
474 nDelta = m_aVerSBar->GetPageSize();
475 else
476 nDelta = 1;
478 tools::Long nThumbPos = m_aVerSBar->GetThumbPos();
479 tools::Long nVisibleSize = m_aVerSBar->GetVisibleSize();
480 tools::Long nRange = m_aVerSBar->GetRange().Len();
482 tools::Long nTmp = nThumbPos+nVisibleSize;
483 while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
484 nDelta--;
486 if( nDelta <= 0 )
487 return;
489 m_nFlags &= ~LBoxFlags::Filling;
491 m_aVerSBar->SetThumbPos( nThumbPos+nDelta );
492 if( bPageDown )
493 PageDown( static_cast<short>(nDelta) );
494 else
495 CursorDown();
499 void SvImpLBox::InvalidateEntriesFrom( tools::Long nY ) const
501 if( !(m_nFlags & LBoxFlags::InPaint ))
503 tools::Rectangle aRect( GetVisibleArea() );
504 aRect.SetTop( nY );
505 m_pView->Invalidate( aRect );
509 void SvImpLBox::InvalidateEntry( tools::Long nY ) const
511 if( m_nFlags & LBoxFlags::InPaint )
512 return;
514 tools::Rectangle aRect( GetVisibleArea() );
515 tools::Long nMaxBottom = aRect.Bottom();
516 aRect.SetTop( nY );
517 aRect.SetBottom( nY ); aRect.AdjustBottom(m_pView->GetEntryHeight() );
518 if( aRect.Top() > nMaxBottom )
519 return;
520 if( aRect.Bottom() > nMaxBottom )
521 aRect.SetBottom( nMaxBottom );
522 if (m_pView->SupportsDoubleBuffering())
523 // Perform full paint when flicker is to be avoided explicitly.
524 m_pView->Invalidate();
525 else
526 m_pView->Invalidate(aRect);
529 void SvImpLBox::InvalidateEntry( SvTreeListEntry* pEntry )
531 if( GetUpdateMode() )
533 tools::Long nPrev = m_nMostRight;
534 SetMostRight( pEntry );
535 if( nPrev < m_nMostRight )
536 ShowVerSBar();
538 if( !(m_nFlags & LBoxFlags::InPaint ))
540 bool bHasFocusRect = false;
541 if( pEntry==m_pCursor && m_pView->HasFocus() )
543 bHasFocusRect = true;
544 ShowCursor( false );
546 InvalidateEntry( GetEntryLine( pEntry ) );
547 if( bHasFocusRect )
548 ShowCursor( true );
553 void SvImpLBox::RecalcFocusRect()
555 if( m_pView->HasFocus() && m_pCursor )
557 m_pView->HideFocus();
558 tools::Long nY = GetEntryLine( m_pCursor );
559 tools::Rectangle aRect = m_pView->GetFocusRect( m_pCursor, nY );
560 vcl::Region aOldClip( m_pView->GetClipRegion());
561 vcl::Region aClipRegion( GetClipRegionRect() );
562 m_pView->SetClipRegion( aClipRegion );
563 m_pView->ShowFocus( aRect );
564 m_pView->SetClipRegion( aOldClip );
569 // Sets cursor. When using SingleSelection, the selection is adjusted.
570 void SvImpLBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
572 SvViewDataEntry* pViewDataNewCur = nullptr;
573 if( pEntry )
574 pViewDataNewCur= m_pView->GetViewDataEntry(pEntry);
575 if( pEntry &&
576 pEntry == m_pCursor &&
577 pViewDataNewCur &&
578 pViewDataNewCur->HasFocus() &&
579 pViewDataNewCur->IsSelected())
581 return;
584 // if this cursor is not selectable, find first visible that is and use it
585 while( pEntry && pViewDataNewCur && !pViewDataNewCur->IsSelectable() )
587 pEntry = m_pView->NextVisible(pEntry);
588 pViewDataNewCur = pEntry ? m_pView->GetViewDataEntry(pEntry) : nullptr;
591 SvTreeListEntry* pOldCursor = m_pCursor;
592 if( m_pCursor && pEntry != m_pCursor )
594 m_pView->SetEntryFocus( m_pCursor, false );
595 if( m_bSimpleTravel )
596 m_pView->Select( m_pCursor, false );
597 m_pView->HideFocus();
599 m_pCursor = pEntry;
600 if( m_pCursor )
602 if (pViewDataNewCur)
603 pViewDataNewCur->SetFocus( true );
604 if(!bForceNoSelect && m_bSimpleTravel && !(m_nFlags & LBoxFlags::DeselectAll) && GetUpdateMode())
606 m_pView->Select( m_pCursor );
607 CallEventListeners( VclEventId::ListboxTreeFocus, m_pCursor );
609 // multiple selection: select in cursor move if we're not in
610 // Add mode (Ctrl-F8)
611 else if( GetUpdateMode() &&
612 m_pView->GetSelectionMode() == SelectionMode::Multiple &&
613 !(m_nFlags & LBoxFlags::DeselectAll) && !m_aSelEng.IsAddMode() &&
614 !bForceNoSelect )
616 m_pView->Select( m_pCursor );
617 CallEventListeners( VclEventId::ListboxTreeFocus, m_pCursor );
619 else
621 ShowCursor( true );
622 if (bForceNoSelect && GetUpdateMode())
624 CallEventListeners( VclEventId::ListboxTreeFocus, m_pCursor);
628 if( m_pAnchor )
630 DBG_ASSERT(m_aSelEng.GetSelectionMode() != SelectionMode::Single,"Mode?");
631 SetAnchorSelection( pOldCursor, m_pCursor );
634 m_nFlags &= ~LBoxFlags::DeselectAll;
636 m_pView->OnCurrentEntryChanged();
639 void SvImpLBox::ShowCursor( bool bShow )
641 if( !bShow || !m_pCursor || !m_pView->HasFocus() )
643 vcl::Region aOldClip( m_pView->GetClipRegion());
644 vcl::Region aClipRegion( GetClipRegionRect() );
645 m_pView->SetClipRegion( aClipRegion );
646 m_pView->HideFocus();
647 m_pView->SetClipRegion( aOldClip );
649 else
651 tools::Long nY = GetEntryLine( m_pCursor );
652 tools::Rectangle aRect = m_pView->GetFocusRect( m_pCursor, nY );
653 vcl::Region aOldClip( m_pView->GetClipRegion());
654 vcl::Region aClipRegion( GetClipRegionRect() );
655 m_pView->SetClipRegion( aClipRegion );
656 m_pView->ShowFocus( aRect );
657 m_pView->SetClipRegion( aOldClip );
662 void SvImpLBox::UpdateAll( bool bInvalidateCompleteView )
664 FindMostRight();
665 m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1 ) );
666 SyncVerThumb();
667 FillView();
668 ShowVerSBar();
669 if( m_bSimpleTravel && m_pCursor && m_pView->HasFocus() )
670 m_pView->Select( m_pCursor );
671 ShowCursor( true );
672 if( bInvalidateCompleteView )
673 m_pView->Invalidate();
674 else
675 m_pView->Invalidate( GetVisibleArea() );
678 IMPL_LINK( SvImpLBox, ScrollLeftRightHdl, ScrollBar *, pScrollBar, void )
680 tools::Long nDelta = pScrollBar->GetDelta();
681 if( nDelta )
683 if( m_pView->IsEditingActive() )
685 m_pView->EndEditing( true ); // Cancel
686 m_pView->PaintImmediately();
688 m_pView->nFocusWidth = -1;
689 KeyLeftRight( nDelta );
693 void SvImpLBox::KeyLeftRight( tools::Long nDelta )
695 if( !(m_nFlags & LBoxFlags::InResize) )
696 m_pView->PaintImmediately();
697 m_nFlags &= ~LBoxFlags::Filling;
698 ShowCursor( false );
700 // calculate new origin
701 tools::Long nPos = m_aHorSBar->GetThumbPos();
702 Point aOrigin( -nPos, 0 );
704 MapMode aMapMode( m_pView->GetMapMode() );
705 aMapMode.SetOrigin( aOrigin );
706 m_pView->SetMapMode( aMapMode );
708 if( !(m_nFlags & LBoxFlags::InResize) )
710 tools::Rectangle aRect( GetVisibleArea() );
711 m_pView->Scroll( -nDelta, 0, aRect, ScrollFlags::NoChildren );
713 else
714 m_pView->Invalidate();
715 RecalcFocusRect();
716 ShowCursor( true );
717 m_pView->NotifyScrolled();
721 // returns the last entry if position is just past the last entry
722 SvTreeListEntry* SvImpLBox::GetClickedEntry( const Point& rPoint ) const
724 DBG_ASSERT( m_pView->GetModel(), "SvImpLBox::GetClickedEntry: how can this ever happen? Please tell me (frank.schoenheit@sun.com) how to reproduce!" );
725 if ( !m_pView->GetModel() )
726 // this is quite impossible. Nevertheless, stack traces from the crash reporter
727 // suggest it isn't. Okay, make it safe, and wait for somebody to reproduce it
728 // reliably :-\ ...
729 // #122359# / 2005-05-23 / frank.schoenheit@sun.com
730 return nullptr;
731 if( m_pView->GetEntryCount() == 0 || !m_pStartEntry || !m_pView->GetEntryHeight())
732 return nullptr;
734 sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / m_pView->GetEntryHeight() );
735 sal_uInt16 nTemp = nClickedEntry;
736 SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp);
737 return pEntry;
741 // checks if the entry was hit "the right way"
742 // (Focusrect+ ContextBitmap at TreeListBox)
744 bool SvImpLBox::EntryReallyHit(SvTreeListEntry* pEntry, const Point& rPosPixel, tools::Long nLine)
746 bool bRet;
747 // we are not too exact when it comes to "special" entries
748 // (with CheckButtons etc.)
749 if( pEntry->ItemCount() >= 3 )
750 return true;
752 tools::Rectangle aRect( m_pView->GetFocusRect( pEntry, nLine ));
753 aRect.SetRight( GetOutputSize().Width() - m_pView->GetMapMode().GetOrigin().X() );
755 SvLBoxContextBmp* pBmp = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
756 aRect.AdjustLeft( -pBmp->GetWidth(m_pView,pEntry) );
757 aRect.AdjustLeft( -4 ); // a little tolerance
759 Point aPos( rPosPixel );
760 aPos -= m_pView->GetMapMode().GetOrigin();
761 bRet = aRect.IsInside( aPos );
762 return bRet;
766 // returns 0 if position is just past the last entry
767 SvTreeListEntry* SvImpLBox::GetEntry( const Point& rPoint ) const
769 if( (m_pView->GetEntryCount() == 0) || !m_pStartEntry ||
770 (rPoint.Y() > m_aOutputSize.Height())
771 || !m_pView->GetEntryHeight())
772 return nullptr;
774 sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / m_pView->GetEntryHeight() );
775 sal_uInt16 nTemp = nClickedEntry;
776 SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp);
777 if( nTemp != nClickedEntry )
778 pEntry = nullptr;
779 return pEntry;
783 SvTreeListEntry* SvImpLBox::MakePointVisible(const Point& rPoint)
785 if( !m_pCursor )
786 return nullptr;
787 tools::Long nY = rPoint.Y();
788 SvTreeListEntry* pEntry = nullptr;
789 tools::Long nMax = m_aOutputSize.Height();
790 if( nY < 0 || nY >= nMax ) // aOutputSize.Height() )
792 if( nY < 0 )
793 pEntry = m_pView->PrevVisible(m_pCursor);
794 else
795 pEntry = m_pView->NextVisible(m_pCursor);
797 if( pEntry && pEntry != m_pCursor )
798 m_pView->SetEntryFocus( m_pCursor, false );
800 if( nY < 0 )
801 KeyUp( false );
802 else
803 KeyDown( false );
805 else
807 pEntry = GetClickedEntry( rPoint );
808 if( !pEntry )
810 sal_uInt16 nSteps = 0xFFFF;
811 // TODO: LastVisible is not yet implemented!
812 pEntry = m_pView->NextVisible(m_pStartEntry, nSteps);
814 if( pEntry )
816 if( pEntry != m_pCursor &&
817 m_aSelEng.GetSelectionMode() == SelectionMode::Single
819 m_pView->Select( m_pCursor, false );
822 return pEntry;
825 tools::Rectangle SvImpLBox::GetClipRegionRect() const
827 Point aOrigin( m_pView->GetMapMode().GetOrigin() );
828 aOrigin.setX( aOrigin.X() * -1 ); // conversion document coordinates
829 tools::Rectangle aClipRect( aOrigin, m_aOutputSize );
830 aClipRect.AdjustBottom( 1 );
831 return aClipRect;
835 void SvImpLBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
837 if (!m_pView->GetVisibleCount())
838 return;
840 m_nFlags |= LBoxFlags::InPaint;
842 if (m_nFlags & LBoxFlags::Filling)
844 SvTreeListEntry* pFirst = m_pView->First();
845 if (pFirst != m_pStartEntry)
847 ShowCursor(false);
848 m_pStartEntry = m_pView->First();
849 m_aVerSBar->SetThumbPos( 0 );
850 StopUserEvent();
851 ShowCursor(true);
852 m_nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
853 reinterpret_cast<void*>(1));
854 return;
858 if (!m_pStartEntry)
860 m_pStartEntry = m_pView->First();
863 if (m_nNodeBmpTabDistance == NODE_BMP_TABDIST_NOTVALID)
864 SetNodeBmpTabDistance();
866 tools::Long nRectHeight = rRect.GetHeight();
867 tools::Long nEntryHeight = m_pView->GetEntryHeight();
869 // calculate area for the entries we want to draw
870 sal_uInt16 nStartLine = static_cast<sal_uInt16>(rRect.Top() / nEntryHeight);
871 sal_uInt16 nCount = static_cast<sal_uInt16>(nRectHeight / nEntryHeight);
872 nCount += 2; // don't miss a row
874 tools::Long nY = nStartLine * nEntryHeight;
875 SvTreeListEntry* pEntry = m_pStartEntry;
876 while (nStartLine && pEntry)
878 pEntry = m_pView->NextVisible(pEntry);
879 nStartLine--;
882 if (!m_pCursor && !mbNoAutoCurEntry)
884 // do not select if multiselection or explicit set
885 bool bNotSelect = (m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
886 SetCursor(m_pStartEntry, bNotSelect);
889 for(sal_uInt16 n=0; n< nCount && pEntry; n++)
891 /*long nMaxRight=*/
892 m_pView->PaintEntry1(*pEntry, nY, rRenderContext );
893 nY += nEntryHeight;
894 pEntry = m_pView->NextVisible(pEntry);
897 if (m_nStyle & (WB_HASLINES | WB_HASLINESATROOT))
898 DrawNet(rRenderContext);
900 m_nFlags &= ~LBoxFlags::DeselectAll;
901 m_nFlags &= ~LBoxFlags::InPaint;
904 void SvImpLBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
906 if( !pEntry )
907 return;
909 bool bInView = IsEntryInView( pEntry );
911 if( bInView && (!bMoveToTop || m_pStartEntry == pEntry) )
912 return; // is already visible
914 if( m_pStartEntry || mbForceMakeVisible )
915 m_nFlags &= ~LBoxFlags::Filling;
916 if( !bInView )
918 if( !m_pView->IsEntryVisible(pEntry) ) // Parent(s) collapsed?
920 SvTreeListEntry* pParent = m_pView->GetParent( pEntry );
921 while( pParent )
923 if( !m_pView->IsExpanded( pParent ) )
925 bool bRet = m_pView->Expand( pParent );
926 DBG_ASSERT(bRet,"Not expanded!");
928 pParent = m_pView->GetParent( pParent );
930 // do the parent's children fit into the view or do we have to scroll?
931 if( IsEntryInView( pEntry ) && !bMoveToTop )
932 return; // no need to scroll
936 m_pStartEntry = pEntry;
937 ShowCursor( false );
938 FillView();
939 m_aVerSBar->SetThumbPos( static_cast<tools::Long>(m_pView->GetVisiblePos( m_pStartEntry )) );
940 ShowCursor( true );
941 m_pView->Invalidate();
944 void SvImpLBox::ScrollToAbsPos( tools::Long nPos )
946 if( m_pView->GetVisibleCount() == 0 )
947 return;
948 tools::Long nLastEntryPos = m_pView->GetAbsPos( m_pView->Last() );
950 if( nPos < 0 )
951 nPos = 0;
952 else if( nPos > nLastEntryPos )
953 nPos = nLastEntryPos;
955 SvTreeListEntry* pEntry = m_pView->GetEntryAtAbsPos( nPos );
956 if( !pEntry || pEntry == m_pStartEntry )
957 return;
959 if( m_pStartEntry || mbForceMakeVisible )
960 m_nFlags &= ~LBoxFlags::Filling;
962 if( m_pView->IsEntryVisible(pEntry) )
964 m_pStartEntry = pEntry;
965 ShowCursor( false );
966 m_aVerSBar->SetThumbPos( nPos );
967 ShowCursor( true );
968 if (GetUpdateMode())
969 m_pView->Invalidate();
973 void SvImpLBox::DrawNet(vcl::RenderContext& rRenderContext)
975 if (m_pView->GetVisibleCount() < 2 && !m_pStartEntry->HasChildrenOnDemand() &&
976 !m_pStartEntry->HasChildren())
978 return;
981 // for platforms that don't have nets, DrawNativeControl does nothing and returns true
982 // so that SvImpLBox::DrawNet() doesn't draw anything either
983 if (rRenderContext.IsNativeControlSupported(ControlType::ListNet, ControlPart::Entire))
985 ImplControlValue aControlValue;
986 if (rRenderContext.DrawNativeControl(ControlType::ListNet, ControlPart::Entire,
987 tools::Rectangle(), ControlState::ENABLED, aControlValue, OUString()))
989 return;
993 tools::Long nEntryHeight = m_pView->GetEntryHeight();
994 tools::Long nEntryHeightDIV2 = nEntryHeight / 2;
995 if( nEntryHeightDIV2 && !(nEntryHeight & 0x0001))
996 nEntryHeightDIV2--;
998 SvTreeListEntry* pChild;
999 SvTreeListEntry* pEntry = m_pStartEntry;
1001 SvLBoxTab* pFirstDynamicTab = m_pView->GetFirstDynamicTab();
1002 while (m_pTree->GetDepth( pEntry ) > 0)
1004 pEntry = m_pView->GetParent(pEntry);
1006 sal_uInt16 nOffs = static_cast<sal_uInt16>(m_pView->GetVisiblePos(m_pStartEntry) - m_pView->GetVisiblePos(pEntry));
1007 tools::Long nY = 0;
1008 nY -= (nOffs * nEntryHeight);
1010 DBG_ASSERT(pFirstDynamicTab,"No Tree!");
1012 rRenderContext.Push(PushFlags::LINECOLOR);
1014 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1015 Color aCol = rStyleSettings.GetFaceColor();
1017 if (aCol.IsRGBEqual(rRenderContext.GetBackground().GetColor()))
1018 aCol = rStyleSettings.GetShadowColor();
1019 rRenderContext.SetLineColor(aCol);
1020 Point aPos1, aPos2;
1021 sal_uInt16 nDistance;
1022 sal_uLong nMax = m_nVisibleCount + nOffs + 1;
1024 const Image& rExpandedNodeBitmap = GetExpandedNodeBmp();
1026 for (sal_uLong n=0; n< nMax && pEntry; n++)
1028 if (m_pView->IsExpanded(pEntry))
1030 // draw vertical line
1031 aPos1.setX(m_pView->GetTabPos(pEntry, pFirstDynamicTab) + m_nNodeBmpTabDistance +
1032 rExpandedNodeBitmap.GetSizePixel().Width() / 2);
1033 aPos1.setY(nY + nEntryHeight);
1034 pChild = m_pView->FirstChild(pEntry);
1035 assert(pChild && "Child?");
1036 pChild = pChild->LastSibling();
1037 nDistance = static_cast<sal_uInt16>(m_pView->GetVisiblePos(pChild) -
1038 m_pView->GetVisiblePos(pEntry));
1039 aPos2 = aPos1;
1040 aPos2.AdjustY((nDistance * nEntryHeight) - (nEntryHeightDIV2 + 2));
1041 rRenderContext.DrawLine(aPos1, aPos2);
1043 // visible in control?
1044 if (n >= nOffs && !m_pTree->IsAtRootDepth(pEntry))
1046 // draw horizontal line
1047 aPos1.setX(m_pView->GetTabPos(m_pView->GetParent(pEntry), pFirstDynamicTab)
1048 + m_nNodeBmpTabDistance
1049 + rExpandedNodeBitmap.GetSizePixel().Width() / 2);
1050 aPos1.setY(nY + nEntryHeightDIV2);
1051 aPos2 = aPos1;
1052 aPos2.AdjustX(m_pView->GetIndent() / 2);
1053 rRenderContext.DrawLine(aPos1, aPos2);
1055 nY += nEntryHeight;
1056 pEntry = m_pView->NextVisible(pEntry);
1059 rRenderContext.Pop();
1062 void SvImpLBox::PositionScrollBars( Size& rSize, sal_uInt16 nMask )
1064 tools::Long nOverlap = 0;
1066 Size aVerSize( m_nVerSBarWidth, rSize.Height() );
1067 Size aHorSize( rSize.Width(), m_nHorSBarHeight );
1069 if( nMask & 0x0001 )
1070 aHorSize.AdjustWidth( -m_nVerSBarWidth );
1071 if( nMask & 0x0002 )
1072 aVerSize.AdjustHeight( -m_nHorSBarHeight );
1074 aVerSize.AdjustHeight(2 * nOverlap );
1075 Point aVerPos( rSize.Width() - aVerSize.Width() + nOverlap, -nOverlap );
1076 m_aVerSBar->SetPosSizePixel( aVerPos, aVerSize );
1078 aHorSize.AdjustWidth(2 * nOverlap );
1079 Point aHorPos( -nOverlap, rSize.Height() - aHorSize.Height() + nOverlap );
1081 m_aHorSBar->SetPosSizePixel( aHorPos, aHorSize );
1083 if( nMask & 0x0001 )
1084 rSize.setWidth( aVerPos.X() );
1085 if( nMask & 0x0002 )
1086 rSize.setHeight( aHorPos.Y() );
1088 if( (nMask & (0x0001|0x0002)) == (0x0001|0x0002) )
1089 m_aScrBarBox->Show();
1090 else
1091 m_aScrBarBox->Hide();
1094 void SvImpLBox::AdjustScrollBars( Size& rSize )
1096 tools::Long nEntryHeight = m_pView->GetEntryHeight();
1097 if( !nEntryHeight )
1098 return;
1100 sal_uInt16 nResult = 0;
1102 Size aOSize( m_pView->Control::GetOutputSizePixel() );
1104 const WinBits nWindowStyle = m_pView->GetStyle();
1105 bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
1106 bool bHorBar = false;
1107 tools::Long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
1108 Point aOrigin( m_pView->GetMapMode().GetOrigin() );
1109 aOrigin.setX( aOrigin.X() * -1 );
1110 nMaxRight += aOrigin.X() - 1;
1111 tools::Long nVis = m_nMostRight - aOrigin.X();
1112 if( (nWindowStyle & (WB_AUTOHSCROLL|WB_HSCROLL)) &&
1113 (nVis < m_nMostRight || nMaxRight < m_nMostRight) )
1115 bHorBar = true;
1118 // number of entries that are not collapsed
1119 sal_uLong nTotalCount = m_pView->GetVisibleCount();
1121 // number of entries visible within the view
1122 m_nVisibleCount = aOSize.Height() / nEntryHeight;
1124 // do we need a vertical scrollbar?
1125 if( bVerSBar || nTotalCount > m_nVisibleCount )
1127 nResult = 1;
1128 nMaxRight -= m_nVerSBarWidth;
1129 if( !bHorBar )
1131 if( (nWindowStyle & (WB_AUTOHSCROLL|WB_HSCROLL)) &&
1132 (nVis < m_nMostRight || nMaxRight < m_nMostRight) )
1133 bHorBar = true;
1137 // do we need a horizontal scrollbar?
1138 if( bHorBar )
1140 nResult |= 0x0002;
1141 // the number of entries visible within the view has to be recalculated
1142 // because the horizontal scrollbar is now visible.
1143 m_nVisibleCount = (aOSize.Height() - m_nHorSBarHeight) / nEntryHeight;
1144 // we might actually need a vertical scrollbar now
1145 if( !(nResult & 0x0001) &&
1146 ((nTotalCount > m_nVisibleCount) || bVerSBar) )
1148 nResult = 3;
1152 PositionScrollBars( aOSize, nResult );
1154 // adapt Range, VisibleRange etc.
1156 // refresh output size, in case we have to scroll
1157 tools::Rectangle aRect;
1158 aRect.SetSize( aOSize );
1159 m_aSelEng.SetVisibleArea( aRect );
1161 // vertical scrollbar
1162 tools::Long nTemp = static_cast<tools::Long>(m_nVisibleCount);
1163 nTemp--;
1164 if( nTemp != m_aVerSBar->GetVisibleSize() )
1166 if( !m_bInVScrollHdl )
1168 m_aVerSBar->SetPageSize( nTemp - 1 );
1169 m_aVerSBar->SetVisibleSize( nTemp );
1171 else
1173 m_nFlags |= LBoxFlags::EndScrollSetVisSize;
1174 m_nNextVerVisSize = nTemp;
1178 // horizontal scrollbar
1179 nTemp = m_aHorSBar->GetThumbPos();
1180 m_aHorSBar->SetVisibleSize( aOSize.Width() );
1181 tools::Long nNewThumbPos = m_aHorSBar->GetThumbPos();
1182 Range aRange( m_aHorSBar->GetRange() );
1183 if( aRange.Max() < m_nMostRight+25 )
1185 aRange.Max() = m_nMostRight+25;
1186 m_aHorSBar->SetRange( aRange );
1189 if( nTemp != nNewThumbPos )
1191 nTemp = nNewThumbPos - nTemp;
1192 if( m_pView->IsEditingActive() )
1194 m_pView->EndEditing( true ); // Cancel
1195 m_pView->PaintImmediately();
1197 m_pView->nFocusWidth = -1;
1198 KeyLeftRight( nTemp );
1201 if( nResult & 0x0001 )
1202 m_aVerSBar->Show();
1203 else
1204 m_aVerSBar->Hide();
1206 if( nResult & 0x0002 )
1207 m_aHorSBar->Show();
1208 else
1210 m_aHorSBar->Hide();
1212 rSize = aOSize;
1215 void SvImpLBox::InitScrollBarBox()
1217 m_aScrBarBox->SetSizePixel( Size(m_nVerSBarWidth, m_nHorSBarHeight) );
1218 Size aSize( m_pView->Control::GetOutputSizePixel() );
1219 m_aScrBarBox->SetPosPixel( Point(aSize.Width()-m_nVerSBarWidth, aSize.Height()-m_nHorSBarHeight));
1222 void SvImpLBox::Resize()
1224 m_aOutputSize = m_pView->Control::GetOutputSizePixel();
1225 if( m_aOutputSize.IsEmpty() )
1226 return;
1227 m_nFlags |= LBoxFlags::InResize;
1228 InitScrollBarBox();
1230 if( m_pView->GetEntryHeight())
1232 AdjustScrollBars( m_aOutputSize );
1233 UpdateAll(false);
1235 // HACK, as in floating and docked windows the scrollbars might not be drawn
1236 // correctly/not be drawn at all after resizing!
1237 if( m_aHorSBar->IsVisible())
1238 m_aHorSBar->Invalidate();
1239 if( m_aVerSBar->IsVisible())
1240 m_aVerSBar->Invalidate();
1241 m_nFlags &= ~LBoxFlags::InResize;
1244 void SvImpLBox::FillView()
1246 if( !m_pStartEntry )
1248 sal_uLong nVisibleViewCount = m_pView->GetVisibleCount();
1249 tools::Long nTempThumb = m_aVerSBar->GetThumbPos();
1250 if( nTempThumb < 0 )
1251 nTempThumb = 0;
1252 else if( o3tl::make_unsigned(nTempThumb) >= nVisibleViewCount )
1253 nTempThumb = nVisibleViewCount == 0 ? 0 : nVisibleViewCount - 1;
1254 m_pStartEntry = m_pView->GetEntryAtVisPos(nTempThumb);
1256 if( !m_pStartEntry )
1257 return;
1259 sal_uInt16 nLast = static_cast<sal_uInt16>(m_pView->GetVisiblePos(m_pView->LastVisible()));
1260 sal_uInt16 nThumb = static_cast<sal_uInt16>(m_pView->GetVisiblePos( m_pStartEntry ));
1261 sal_uLong nCurDispEntries = nLast-nThumb+1;
1262 if( nCurDispEntries >= m_nVisibleCount )
1263 return;
1265 ShowCursor( false );
1266 // fill window by moving the thumb up incrementally
1267 bool bFound = false;
1268 SvTreeListEntry* pTemp = m_pStartEntry;
1269 while( nCurDispEntries < m_nVisibleCount && pTemp )
1271 pTemp = m_pView->PrevVisible(m_pStartEntry);
1272 if( pTemp )
1274 nThumb--;
1275 m_pStartEntry = pTemp;
1276 nCurDispEntries++;
1277 bFound = true;
1280 if( bFound )
1282 m_aVerSBar->SetThumbPos( nThumb );
1283 ShowCursor( true ); // recalculate focus rectangle
1284 m_pView->Invalidate();
1289 void SvImpLBox::ShowVerSBar()
1291 bool bVerBar = ( m_pView->GetStyle() & WB_VSCROLL ) != 0;
1292 sal_uLong nVis = 0;
1293 if( !bVerBar )
1294 nVis = m_pView->GetVisibleCount();
1295 if( bVerBar || (m_nVisibleCount && nVis > static_cast<sal_uLong>(m_nVisibleCount-1)) )
1297 if( !m_aVerSBar->IsVisible() )
1299 m_pView->nFocusWidth = -1;
1300 AdjustScrollBars( m_aOutputSize );
1301 if( GetUpdateMode() )
1302 m_aVerSBar->Invalidate();
1305 else
1307 if( m_aVerSBar->IsVisible() )
1309 m_pView->nFocusWidth = -1;
1310 AdjustScrollBars( m_aOutputSize );
1314 tools::Long nMaxRight = GetOutputSize().Width();
1315 Point aPos( m_pView->GetMapMode().GetOrigin() );
1316 aPos.setX( aPos.X() * -1 ); // convert document coordinates
1317 nMaxRight = nMaxRight + aPos.X() - 1;
1318 if( nMaxRight < m_nMostRight )
1320 if( !m_aHorSBar->IsVisible() )
1322 m_pView->nFocusWidth = -1;
1323 AdjustScrollBars( m_aOutputSize );
1324 if( GetUpdateMode() )
1325 m_aHorSBar->Invalidate();
1327 else
1329 Range aRange( m_aHorSBar->GetRange() );
1330 if( aRange.Max() < m_nMostRight+25 )
1332 aRange.Max() = m_nMostRight+25;
1333 m_aHorSBar->SetRange( aRange );
1335 else
1337 m_pView->nFocusWidth = -1;
1338 AdjustScrollBars( m_aOutputSize );
1342 else
1344 if( m_aHorSBar->IsVisible() )
1346 m_pView->nFocusWidth = -1;
1347 AdjustScrollBars( m_aOutputSize );
1353 void SvImpLBox::SyncVerThumb()
1355 if( m_pStartEntry )
1357 tools::Long nEntryPos = m_pView->GetVisiblePos( m_pStartEntry );
1358 m_aVerSBar->SetThumbPos( nEntryPos );
1360 else
1361 m_aVerSBar->SetThumbPos( 0 );
1364 bool SvImpLBox::IsEntryInView( SvTreeListEntry* pEntry ) const
1366 // parent collapsed
1367 if( !m_pView->IsEntryVisible(pEntry) )
1368 return false;
1369 tools::Long nY = GetEntryLine( pEntry );
1370 if( nY < 0 )
1371 return false;
1372 tools::Long nMax = m_nVisibleCount * m_pView->GetEntryHeight();
1373 return nY < nMax;
1377 tools::Long SvImpLBox::GetEntryLine(const SvTreeListEntry* pEntry) const
1379 if(!m_pStartEntry )
1380 return -1; // invisible position
1382 tools::Long nFirstVisPos = m_pView->GetVisiblePos( m_pStartEntry );
1383 tools::Long nEntryVisPos = m_pView->GetVisiblePos( pEntry );
1384 nFirstVisPos = nEntryVisPos - nFirstVisPos;
1385 nFirstVisPos *= m_pView->GetEntryHeight();
1386 return nFirstVisPos;
1389 void SvImpLBox::SetEntryHeight()
1391 SetNodeBmpWidth( GetExpandedNodeBmp() );
1392 SetNodeBmpWidth( GetCollapsedNodeBmp() );
1393 if(!m_pView->HasViewData()) // are we within the Clear?
1395 Size aSize = m_pView->Control::GetOutputSizePixel();
1396 AdjustScrollBars( aSize );
1398 else
1400 Resize();
1401 if( GetUpdateMode() )
1402 m_pView->Invalidate();
1407 // ***********************************************************************
1408 // Callback Functions
1409 // ***********************************************************************
1411 void SvImpLBox::EntryExpanded( SvTreeListEntry* pEntry )
1413 // SelAllDestrAnch( false, true ); //DeselectAll();
1414 if( !GetUpdateMode() )
1415 return;
1417 ShowCursor( false );
1418 tools::Long nY = GetEntryLine( pEntry );
1419 if( IsLineVisible(nY) )
1421 InvalidateEntriesFrom( nY );
1422 FindMostRight( pEntry );
1424 m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1 ) );
1425 // if we expanded before the thumb, the thumb's position has to be
1426 // corrected
1427 SyncVerThumb();
1428 ShowVerSBar();
1429 ShowCursor( true );
1432 void SvImpLBox::EntryCollapsed( SvTreeListEntry* pEntry )
1434 if( !m_pView->IsEntryVisible( pEntry ) )
1435 return;
1437 ShowCursor( false );
1439 if( !m_pMostRightEntry || m_pTree->IsChild( pEntry,m_pMostRightEntry ) )
1441 FindMostRight();
1444 if( m_pStartEntry )
1446 tools::Long nOldThumbPos = m_aVerSBar->GetThumbPos();
1447 sal_uLong nVisList = m_pView->GetVisibleCount();
1448 m_aVerSBar->SetRange( Range(0, nVisList-1) );
1449 tools::Long nNewThumbPos = m_aVerSBar->GetThumbPos();
1450 if( nNewThumbPos != nOldThumbPos )
1452 m_pStartEntry = m_pView->First();
1453 sal_uInt16 nDistance = static_cast<sal_uInt16>(nNewThumbPos);
1454 if( nDistance )
1455 m_pStartEntry = m_pView->NextVisible(m_pStartEntry, nDistance);
1456 if( GetUpdateMode() )
1457 m_pView->Invalidate();
1459 else
1460 SyncVerThumb();
1461 ShowVerSBar();
1463 // has the cursor been collapsed?
1464 if( m_pTree->IsChild( pEntry, m_pCursor ) )
1465 SetCursor( pEntry );
1466 if( GetUpdateMode() )
1467 ShowVerSBar();
1468 ShowCursor( true );
1469 if( GetUpdateMode() && m_pCursor )
1470 m_pView->Select( m_pCursor );
1473 void SvImpLBox::CollapsingEntry( SvTreeListEntry* pEntry )
1475 if( !m_pView->IsEntryVisible( pEntry ) || !m_pStartEntry )
1476 return;
1478 SelAllDestrAnch( false ); // deselect all
1480 // is the collapsed cursor visible?
1481 tools::Long nY = GetEntryLine( pEntry );
1482 if( IsLineVisible(nY) )
1484 if( GetUpdateMode() )
1485 InvalidateEntriesFrom( nY );
1487 else
1489 if( m_pTree->IsChild(pEntry, m_pStartEntry) )
1491 m_pStartEntry = pEntry;
1492 if( GetUpdateMode() )
1493 m_pView->Invalidate();
1499 void SvImpLBox::SetNodeBmpWidth( const Image& rBmp )
1501 const Size aSize( rBmp.GetSizePixel() );
1502 m_nNodeBmpWidth = aSize.Width();
1505 void SvImpLBox::SetNodeBmpTabDistance()
1507 m_nNodeBmpTabDistance = -m_pView->GetIndent();
1508 if( m_pView->nContextBmpWidthMax )
1510 // only if the first dynamic tab is centered (we currently assume that)
1511 Size aSize = GetExpandedNodeBmp().GetSizePixel();
1512 m_nNodeBmpTabDistance -= aSize.Width() / 2;
1517 // corrects the cursor when using SingleSelection
1519 void SvImpLBox::EntrySelected( SvTreeListEntry* pEntry, bool bSelect )
1521 if( m_nFlags & LBoxFlags::IgnoreSelect )
1522 return;
1524 m_nFlags &= ~LBoxFlags::DeselectAll;
1525 if( bSelect &&
1526 m_aSelEng.GetSelectionMode() == SelectionMode::Single &&
1527 pEntry != m_pCursor )
1529 SetCursor( pEntry );
1530 DBG_ASSERT(m_pView->GetSelectionCount()==1,"selection count?");
1533 if( GetUpdateMode() && m_pView->IsEntryVisible(pEntry) )
1535 tools::Long nY = GetEntryLine( pEntry );
1536 if( IsLineVisible( nY ) )
1538 ShowCursor(false);
1539 InvalidateEntry(pEntry);
1540 ShowCursor(true);
1546 void SvImpLBox::RemovingEntry( SvTreeListEntry* pEntry )
1548 CallEventListeners( VclEventId::ListboxItemRemoved , pEntry );
1550 DestroyAnchor();
1552 if( !m_pView->IsEntryVisible( pEntry ) )
1554 // if parent is collapsed => bye!
1555 m_nFlags |= LBoxFlags::RemovedEntryInvisible;
1556 return;
1559 if( pEntry == m_pMostRightEntry || (
1560 pEntry->HasChildren() && m_pView->IsExpanded(pEntry) &&
1561 m_pTree->IsChild(pEntry, m_pMostRightEntry)))
1563 m_nFlags |= LBoxFlags::RemovedRecalcMostRight;
1566 SvTreeListEntry* pOldStartEntry = m_pStartEntry;
1568 SvTreeListEntry* pParent = m_pView->GetModel()->GetParent(pEntry);
1570 if (pParent && m_pView->GetModel()->GetChildList(pParent).size() == 1)
1572 DBG_ASSERT( m_pView->IsExpanded( pParent ), "Parent not expanded");
1573 pParent->SetFlags( pParent->GetFlags() | SvTLEntryFlags::NO_NODEBMP);
1574 InvalidateEntry( pParent );
1577 if( m_pCursor && m_pTree->IsChild( pEntry, m_pCursor) )
1578 m_pCursor = pEntry;
1579 if( m_pStartEntry && m_pTree->IsChild(pEntry,m_pStartEntry) )
1580 m_pStartEntry = pEntry;
1582 SvTreeListEntry* pTemp;
1583 if( m_pCursor && m_pCursor == pEntry )
1585 if( m_bSimpleTravel )
1586 m_pView->Select( m_pCursor, false );
1587 ShowCursor( false ); // focus rectangle gone
1588 // NextSibling, because we also delete the children of the cursor
1589 pTemp = m_pCursor->NextSibling();
1590 if( !pTemp )
1591 pTemp = m_pView->PrevVisible(m_pCursor);
1593 SetCursor( pTemp, true );
1595 if( m_pStartEntry && m_pStartEntry == pEntry )
1597 pTemp = m_pStartEntry->NextSibling();
1598 if( !pTemp )
1599 pTemp = m_pView->PrevVisible(m_pStartEntry);
1600 m_pStartEntry = pTemp;
1602 if( GetUpdateMode())
1604 // if it is the last one, we have to invalidate it, so the lines are
1605 // drawn correctly (in this case they're deleted)
1606 if( m_pStartEntry && (m_pStartEntry != pOldStartEntry || pEntry == m_pView->GetModel()->Last()) )
1608 m_aVerSBar->SetThumbPos( m_pView->GetVisiblePos( m_pStartEntry ));
1609 m_pView->Invalidate( GetVisibleArea() );
1611 else
1612 InvalidateEntriesFrom( GetEntryLine( pEntry ) );
1616 void SvImpLBox::EntryRemoved()
1618 if( m_nFlags & LBoxFlags::RemovedEntryInvisible )
1620 m_nFlags &= ~LBoxFlags::RemovedEntryInvisible;
1621 return;
1623 if( !m_pStartEntry )
1624 m_pStartEntry = m_pTree->First();
1625 if( !m_pCursor )
1626 SetCursor( m_pStartEntry, true );
1628 if( m_pCursor && (m_bSimpleTravel || !m_pView->GetSelectionCount() ))
1629 m_pView->Select( m_pCursor );
1631 if( GetUpdateMode())
1633 if( m_nFlags & LBoxFlags::RemovedRecalcMostRight )
1634 FindMostRight();
1635 m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1 ) );
1636 FillView();
1637 if( m_pStartEntry )
1638 // if something above the thumb was deleted
1639 m_aVerSBar->SetThumbPos( m_pView->GetVisiblePos( m_pStartEntry) );
1641 ShowVerSBar();
1642 if( m_pCursor && m_pView->HasFocus() && !m_pView->IsSelected(m_pCursor) )
1644 if( m_pView->GetSelectionCount() )
1646 // is a neighboring entry selected?
1647 SvTreeListEntry* pNextCursor = m_pView->PrevVisible( m_pCursor );
1648 if( !pNextCursor || !m_pView->IsSelected( pNextCursor ))
1649 pNextCursor = m_pView->NextVisible( m_pCursor );
1650 if( !pNextCursor || !m_pView->IsSelected( pNextCursor ))
1651 // no neighbor selected: use first selected
1652 pNextCursor = m_pView->FirstSelected();
1653 SetCursor( pNextCursor );
1654 MakeVisible( m_pCursor );
1656 else
1657 m_pView->Select( m_pCursor );
1659 ShowCursor( true );
1661 m_nFlags &= ~LBoxFlags::RemovedRecalcMostRight;
1665 void SvImpLBox::MovingEntry( SvTreeListEntry* pEntry )
1667 bool bDeselAll(m_nFlags & LBoxFlags::DeselectAll);
1668 SelAllDestrAnch( false ); // DeselectAll();
1669 if( !bDeselAll )
1670 m_nFlags &= ~LBoxFlags::DeselectAll;
1672 if( pEntry == m_pCursor )
1673 ShowCursor( false );
1674 if( IsEntryInView( pEntry ) )
1675 m_pView->Invalidate();
1676 if( pEntry != m_pStartEntry )
1677 return;
1679 SvTreeListEntry* pNew = nullptr;
1680 if( !pEntry->HasChildren() )
1682 pNew = m_pView->NextVisible(m_pStartEntry);
1683 if( !pNew )
1684 pNew = m_pView->PrevVisible(m_pStartEntry);
1686 else
1688 pNew = pEntry->NextSibling();
1689 if( !pNew )
1690 pNew = pEntry->PrevSibling();
1692 m_pStartEntry = pNew;
1695 void SvImpLBox::EntryMoved( SvTreeListEntry* pEntry )
1697 UpdateContextBmpWidthVectorFromMovedEntry( pEntry );
1699 if ( !m_pStartEntry )
1700 // this might happen if the only entry in the view is moved to its very same position
1701 // #i97346#
1702 m_pStartEntry = m_pView->First();
1704 m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1));
1705 sal_uInt16 nFirstPos = static_cast<sal_uInt16>(m_pTree->GetAbsPos( m_pStartEntry ));
1706 sal_uInt16 nNewPos = static_cast<sal_uInt16>(m_pTree->GetAbsPos( pEntry ));
1707 FindMostRight();
1708 if( nNewPos < nFirstPos ) // HACK!
1709 m_pStartEntry = pEntry;
1710 SyncVerThumb();
1711 if( pEntry == m_pCursor )
1713 if( m_pView->IsEntryVisible( m_pCursor ) )
1714 ShowCursor( true );
1715 else
1717 SvTreeListEntry* pParent = pEntry;
1718 do {
1719 pParent = m_pTree->GetParent( pParent );
1721 while( !m_pView->IsEntryVisible( pParent ) );
1722 SetCursor( pParent );
1725 if( IsEntryInView( pEntry ) )
1726 m_pView->Invalidate();
1730 void SvImpLBox::EntryInserted( SvTreeListEntry* pEntry )
1732 if( !GetUpdateMode() )
1733 return;
1735 SvTreeListEntry* pParent = m_pTree->GetParent(pEntry);
1736 if (pParent && m_pTree->GetChildList(pParent).size() == 1)
1737 // draw plus sign
1738 m_pTree->InvalidateEntry( pParent );
1740 if( !m_pView->IsEntryVisible( pEntry ) )
1741 return;
1742 bool bDeselAll(m_nFlags & LBoxFlags::DeselectAll);
1743 if( bDeselAll )
1744 SelAllDestrAnch( false );
1745 else
1746 DestroyAnchor();
1747 // nFlags &= (~LBoxFlags::DeselectAll);
1748 // ShowCursor( false ); // if cursor is moved lower
1749 tools::Long nY = GetEntryLine( pEntry );
1750 bool bEntryVisible = IsLineVisible( nY );
1751 if( bEntryVisible )
1753 ShowCursor( false ); // if cursor is moved lower
1754 nY -= m_pView->GetEntryHeight(); // because of lines
1755 InvalidateEntriesFrom( nY );
1757 else if( m_pStartEntry && nY < GetEntryLine(m_pStartEntry) )
1759 // Check if the view is filled completely. If not, then adjust
1760 // pStartEntry and the Cursor (automatic scrolling).
1761 sal_uInt16 nLast = static_cast<sal_uInt16>(m_pView->GetVisiblePos(m_pView->LastVisible()));
1762 sal_uInt16 nThumb = static_cast<sal_uInt16>(m_pView->GetVisiblePos( m_pStartEntry ));
1763 sal_uInt16 nCurDispEntries = nLast-nThumb+1;
1764 if( nCurDispEntries < m_nVisibleCount )
1766 // set at the next paint event
1767 m_pStartEntry = nullptr;
1768 SetCursor( nullptr );
1769 m_pView->Invalidate();
1772 else if( !m_pStartEntry )
1773 m_pView->Invalidate();
1775 SetMostRight( pEntry );
1776 m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1));
1777 SyncVerThumb(); // if something was inserted before the thumb
1778 ShowVerSBar();
1779 ShowCursor( true );
1780 if( m_pStartEntry != m_pView->First() && (m_nFlags & LBoxFlags::Filling) )
1781 m_pView->PaintImmediately();
1785 // ********************************************************************
1786 // Event handler
1787 // ********************************************************************
1790 // ****** Control the control animation
1792 bool SvImpLBox::ButtonDownCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry)
1794 SvLBoxItem* pItem = m_pView->GetItem(pEntry,rMEvt.GetPosPixel().X(),&m_pActiveTab);
1795 if (pItem && pItem->GetType() == SvLBoxItemType::Button)
1797 m_pActiveButton = static_cast<SvLBoxButton*>(pItem);
1798 m_pActiveEntry = pEntry;
1799 if( m_pCursor == m_pActiveEntry )
1800 m_pView->HideFocus();
1801 m_pView->CaptureMouse();
1802 m_pActiveButton->SetStateHilighted( true );
1803 InvalidateEntry(m_pActiveEntry);
1804 return true;
1806 else
1807 m_pActiveButton = nullptr;
1808 return false;
1811 bool SvImpLBox::MouseMoveCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry const * pEntry)
1813 if( m_pActiveButton )
1815 tools::Long nMouseX = rMEvt.GetPosPixel().X();
1816 if( pEntry == m_pActiveEntry &&
1817 m_pView->GetItem(m_pActiveEntry, nMouseX) == m_pActiveButton )
1819 if( !m_pActiveButton->IsStateHilighted() )
1821 m_pActiveButton->SetStateHilighted(true );
1822 InvalidateEntry(m_pActiveEntry);
1825 else
1827 if( m_pActiveButton->IsStateHilighted() )
1829 m_pActiveButton->SetStateHilighted(false );
1830 InvalidateEntry(m_pActiveEntry);
1833 return true;
1835 return false;
1838 bool SvImpLBox::ButtonUpCheckCtrl( const MouseEvent& rMEvt )
1840 if( m_pActiveButton )
1842 m_pView->ReleaseMouse();
1843 SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
1844 m_pActiveButton->SetStateHilighted( false );
1845 tools::Long nMouseX = rMEvt.GetPosPixel().X();
1846 if (pEntry == m_pActiveEntry && m_pView->GetItem(m_pActiveEntry, nMouseX) == m_pActiveButton)
1847 m_pActiveButton->ClickHdl(m_pActiveEntry);
1848 InvalidateEntry(m_pActiveEntry);
1849 if (m_pCursor == m_pActiveEntry)
1850 ShowCursor(true);
1851 m_pActiveButton = nullptr;
1852 m_pActiveEntry = nullptr;
1853 m_pActiveTab = nullptr;
1854 return true;
1856 return false;
1859 // ******* Control plus/minus button for expanding/collapsing
1861 // false == no expand/collapse button hit
1862 bool SvImpLBox::IsNodeButton( const Point& rPosPixel, const SvTreeListEntry* pEntry ) const
1864 if( !pEntry->HasChildren() && !pEntry->HasChildrenOnDemand() )
1865 return false;
1867 SvLBoxTab* pFirstDynamicTab = m_pView->GetFirstDynamicTab();
1868 if( !pFirstDynamicTab )
1869 return false;
1871 tools::Long nMouseX = rPosPixel.X();
1872 // convert to document coordinates
1873 Point aOrigin( m_pView->GetMapMode().GetOrigin() );
1874 nMouseX -= aOrigin.X();
1876 tools::Long nX = m_pView->GetTabPos( pEntry, pFirstDynamicTab);
1877 nX += m_nNodeBmpTabDistance;
1878 if( nMouseX < nX )
1879 return false;
1880 nX += m_nNodeBmpWidth;
1881 return nMouseX <= nX;
1884 // false == hit no node button
1885 bool SvImpLBox::ButtonDownCheckExpand( const MouseEvent& rMEvt, SvTreeListEntry* pEntry )
1887 bool bRet = false;
1889 if ( m_pView->IsEditingActive() && pEntry == m_pView->pEdEntry )
1890 // inplace editing -> nothing to do
1891 bRet = true;
1892 else if ( IsNodeButton( rMEvt.GetPosPixel(), pEntry ) )
1894 if ( m_pView->IsExpanded( pEntry ) )
1896 m_pView->EndEditing( true );
1897 m_pView->Collapse( pEntry );
1899 else
1901 // you can expand an entry, which is in editing
1902 m_pView->Expand( pEntry );
1904 bRet = true;
1907 return bRet;
1910 void SvImpLBox::MouseButtonDown( const MouseEvent& rMEvt )
1912 if ( !rMEvt.IsLeft() && !rMEvt.IsRight())
1913 return;
1915 m_aEditIdle.Stop();
1916 Point aPos( rMEvt.GetPosPixel());
1918 if( aPos.X() > m_aOutputSize.Width() || aPos.Y() > m_aOutputSize.Height() )
1919 return;
1921 if( !m_pCursor )
1922 m_pCursor = m_pStartEntry;
1923 m_nFlags &= ~LBoxFlags::Filling;
1924 m_pView->GrabFocus();
1925 //fdo#82270 Grabbing focus can invalidate the entries, re-fetch
1926 SvTreeListEntry* pEntry = GetEntry(aPos);
1927 // the entry can still be invalid!
1928 if( !pEntry || !m_pView->GetViewData( pEntry ))
1929 return;
1931 tools::Long nY = GetEntryLine( pEntry );
1932 // Node-Button?
1933 if( ButtonDownCheckExpand( rMEvt, pEntry ) )
1934 return;
1936 if( !EntryReallyHit(pEntry,aPos,nY))
1937 return;
1939 SvLBoxItem* pXItem = m_pView->GetItem( pEntry, aPos.X() );
1940 if( pXItem )
1942 SvLBoxTab* pXTab = m_pView->GetTab( pEntry, pXItem );
1943 if ( !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && pXTab->IsEditable()
1944 && pEntry == m_pView->FirstSelected() && nullptr == m_pView->NextSelected( pEntry ) )
1945 // #i8234# FirstSelected() and NextSelected() ensures, that inplace editing is only triggered, when only one entry is selected
1946 m_nFlags |= LBoxFlags::StartEditTimer;
1947 if ( !m_pView->IsSelected( pEntry ) )
1948 m_nFlags &= ~LBoxFlags::StartEditTimer;
1952 if( (rMEvt.GetClicks() % 2) == 0)
1954 m_nFlags &= ~LBoxFlags::StartEditTimer;
1955 m_pView->pHdlEntry = pEntry;
1956 if( !m_pView->DoubleClickHdl() )
1958 // Handler signals nothing to be done anymore, bail out, 'this' may
1959 // even be dead and destroyed.
1960 return;
1962 else
1964 // if the entry was deleted within the handler
1965 pEntry = GetClickedEntry( aPos );
1966 if( !pEntry )
1967 return;
1968 if( pEntry != m_pView->pHdlEntry )
1970 // select anew & bye
1971 if( !m_bSimpleTravel && !m_aSelEng.IsAlwaysAdding())
1972 SelAllDestrAnch( false ); // DeselectAll();
1973 SetCursor( pEntry );
1975 return;
1977 if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() )
1979 if( m_pView->IsExpanded(pEntry) )
1980 m_pView->Collapse( pEntry );
1981 else
1982 m_pView->Expand( pEntry );
1983 if( pEntry == m_pCursor ) // only if Entryitem was clicked
1984 // (Nodebutton is not an Entryitem!)
1985 m_pView->Select( m_pCursor );
1986 return;
1990 else
1992 // CheckButton? (TreeListBox: Check + Info)
1993 if( ButtonDownCheckCtrl(rMEvt, pEntry) )
1994 return;
1995 // Inplace-Editing?
1997 if ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE
1998 && !rMEvt.IsRight() ) // tdf#128824
1999 m_aSelEng.SelMouseButtonDown( rMEvt );
2002 void SvImpLBox::MouseButtonUp( const MouseEvent& rMEvt)
2004 if ( !ButtonUpCheckCtrl( rMEvt ) && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
2005 m_aSelEng.SelMouseButtonUp( rMEvt );
2006 if( m_nFlags & LBoxFlags::StartEditTimer )
2008 m_nFlags &= ~LBoxFlags::StartEditTimer;
2009 m_aEditClickPos = rMEvt.GetPosPixel();
2010 m_aEditIdle.Start();
2013 if (m_pView->mbActivateOnSingleClick)
2015 Point aPos(rMEvt.GetPosPixel());
2016 SvTreeListEntry* pEntry = GetEntry(aPos);
2017 // tdf#143245 ActivateOnSingleClick only
2018 // if the 'up' is at the active entry
2019 // typically selected by the 'down'
2020 if (!pEntry || pEntry != m_pCursor)
2021 return;
2022 m_pView->DoubleClickHdl();
2026 void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
2028 Point aPos = rMEvt.GetPosPixel();
2029 SvTreeListEntry* pEntry = GetClickedEntry(aPos);
2030 if ( !MouseMoveCheckCtrl( rMEvt, pEntry ) && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
2032 m_aSelEng.SelMouseMove(rMEvt);
2033 if (m_pView->mbHoverSelection)
2035 if (aPos.X() < 0 || aPos.Y() < 0 || aPos.X() > m_aOutputSize.Width() || aPos.Y() > m_aOutputSize.Height())
2036 pEntry = nullptr;
2037 else
2038 pEntry = GetEntry(aPos);
2039 if (!pEntry)
2040 m_pView->SelectAll(false);
2041 else if (!m_pView->IsSelected(pEntry) && IsSelectable(pEntry))
2042 m_pView->Select(pEntry);
2047 void SvImpLBox::ExpandAll()
2049 sal_uInt16 nRefDepth = m_pTree->GetDepth(m_pCursor);
2050 SvTreeListEntry* pCur = m_pTree->Next(m_pCursor);
2051 while (pCur && m_pTree->GetDepth(pCur) > nRefDepth)
2053 if (pCur->HasChildren() && !m_pView->IsExpanded(pCur))
2054 m_pView->Expand(pCur);
2055 pCur = m_pTree->Next(pCur);
2059 void SvImpLBox::CollapseTo(SvTreeListEntry* pParentToCollapse)
2061 // collapse all parents until we get to the given parent to collapse
2062 if (!pParentToCollapse)
2063 return;
2065 sal_uInt16 nRefDepth;
2066 // special case explorer: if the root only has a single
2067 // entry, don't collapse the root entry
2068 if (m_pTree->GetChildList(nullptr).size() < 2)
2070 nRefDepth = 1;
2071 pParentToCollapse = m_pCursor;
2072 while (m_pTree->GetParent(pParentToCollapse)
2073 && m_pTree->GetDepth(m_pTree->GetParent(pParentToCollapse)) > 0)
2075 pParentToCollapse = m_pTree->GetParent(pParentToCollapse);
2078 else
2079 nRefDepth = m_pTree->GetDepth(pParentToCollapse);
2081 if (m_pView->IsExpanded(pParentToCollapse))
2082 m_pView->Collapse(pParentToCollapse);
2083 SvTreeListEntry* pCur = m_pTree->Next(pParentToCollapse);
2084 while (pCur && m_pTree->GetDepth(pCur) > nRefDepth)
2086 if (pCur->HasChildren() && m_pView->IsExpanded(pCur))
2087 m_pView->Collapse(pCur);
2088 pCur = m_pTree->Next(pCur);
2092 bool SvImpLBox::KeyInput( const KeyEvent& rKEvt)
2094 m_aEditIdle.Stop();
2095 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
2097 if( rKeyCode.IsMod2() )
2098 return false; // don't evaluate Alt key
2100 m_nFlags &= ~LBoxFlags::Filling;
2102 if( !m_pCursor )
2103 m_pCursor = m_pStartEntry;
2104 if( !m_pCursor )
2105 return false;
2107 bool bKeyUsed = true;
2109 sal_uInt16 nDelta = static_cast<sal_uInt16>(m_aVerSBar->GetPageSize());
2110 sal_uInt16 aCode = rKeyCode.GetCode();
2112 bool bShift = rKeyCode.IsShift();
2113 bool bMod1 = rKeyCode.IsMod1();
2115 SvTreeListEntry* pNewCursor;
2117 switch( aCode )
2119 case KEY_UP:
2120 if( !IsEntryInView( m_pCursor ) )
2121 MakeVisible( m_pCursor );
2123 pNewCursor = m_pCursor;
2126 pNewCursor = m_pView->PrevVisible(pNewCursor);
2127 } while( pNewCursor && !IsSelectable(pNewCursor) );
2129 // if there is no next entry, take the current one
2130 // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
2131 // the cursor key
2132 if (!pNewCursor)
2133 pNewCursor = m_pCursor;
2135 m_aSelEng.CursorPosChanging( bShift, bMod1 );
2136 SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
2137 if( !IsEntryInView( pNewCursor ) )
2138 KeyUp( false );
2139 break;
2141 case KEY_DOWN:
2142 if( !IsEntryInView( m_pCursor ) )
2143 MakeVisible( m_pCursor );
2145 pNewCursor = m_pCursor;
2148 pNewCursor = m_pView->NextVisible(pNewCursor);
2149 } while( pNewCursor && !IsSelectable(pNewCursor) );
2151 // if there is no next entry, take the current one
2152 // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
2153 // the cursor key
2154 // 06.09.20001 - 83416 - frank.schoenheit@sun.com
2155 if ( !pNewCursor && m_pCursor )
2156 pNewCursor = m_pCursor;
2158 if( pNewCursor )
2160 m_aSelEng.CursorPosChanging( bShift, bMod1 );
2161 if( IsEntryInView( pNewCursor ) )
2162 SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
2163 else
2165 if( m_pCursor )
2166 m_pView->Select( m_pCursor, false );
2167 KeyDown( false );
2168 SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
2171 else
2172 KeyDown( false ); // because scrollbar range might still
2173 // allow scrolling
2174 break;
2176 case KEY_RIGHT:
2178 if( m_bSubLstOpLR )
2180 // only try to expand if sublist is expandable,
2181 // otherwise ignore the key press
2182 if( IsExpandable() && !m_pView->IsExpanded( m_pCursor ) )
2183 m_pView->Expand( m_pCursor );
2185 else if (m_aHorSBar->IsVisible())
2187 tools::Long nThumb = m_aHorSBar->GetThumbPos();
2188 nThumb += m_aHorSBar->GetLineSize();
2189 tools::Long nOldThumb = m_aHorSBar->GetThumbPos();
2190 m_aHorSBar->SetThumbPos( nThumb );
2191 nThumb = nOldThumb;
2192 nThumb -= m_aHorSBar->GetThumbPos();
2193 nThumb *= -1;
2194 if( nThumb )
2196 KeyLeftRight( nThumb );
2199 else
2200 bKeyUsed = false;
2201 break;
2204 case KEY_LEFT:
2206 if (m_aHorSBar->IsVisible())
2208 tools::Long nThumb = m_aHorSBar->GetThumbPos();
2209 nThumb -= m_aHorSBar->GetLineSize();
2210 tools::Long nOldThumb = m_aHorSBar->GetThumbPos();
2211 m_aHorSBar->SetThumbPos( nThumb );
2212 nThumb = nOldThumb;
2213 nThumb -= m_aHorSBar->GetThumbPos();
2214 if( nThumb )
2216 KeyLeftRight( -nThumb );
2218 else if( m_bSubLstOpLR )
2220 if( IsExpandable() && m_pView->IsExpanded( m_pCursor ) )
2221 m_pView->Collapse( m_pCursor );
2222 else
2224 pNewCursor = m_pView->GetParent( m_pCursor );
2225 if( pNewCursor )
2226 SetCursor( pNewCursor );
2230 else if( m_bSubLstOpLR )
2232 if( IsExpandable() && m_pView->IsExpanded( m_pCursor ) )
2233 m_pView->Collapse( m_pCursor );
2234 else
2236 pNewCursor = m_pView->GetParent( m_pCursor );
2237 if( pNewCursor )
2238 SetCursor( pNewCursor );
2241 else
2242 bKeyUsed = false;
2243 break;
2246 case KEY_PAGEUP:
2247 if( !bMod1 )
2249 pNewCursor = m_pView->PrevVisible(m_pCursor, nDelta);
2251 while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
2253 pNewCursor = m_pView->NextVisible(pNewCursor);
2254 nDelta--;
2257 if( nDelta )
2259 DBG_ASSERT(pNewCursor && pNewCursor!=m_pCursor, "Cursor?");
2260 m_aSelEng.CursorPosChanging( bShift, bMod1 );
2261 if( IsEntryInView( pNewCursor ) )
2262 SetCursor( pNewCursor );
2263 else
2265 SetCursor( pNewCursor );
2266 KeyUp( true );
2270 else
2271 bKeyUsed = false;
2272 break;
2274 case KEY_PAGEDOWN:
2275 if( !bMod1 )
2277 pNewCursor= m_pView->NextVisible(m_pCursor, nDelta);
2279 while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
2281 pNewCursor = m_pView->PrevVisible(pNewCursor);
2282 nDelta--;
2285 if( nDelta && pNewCursor )
2287 DBG_ASSERT(pNewCursor && pNewCursor!=m_pCursor, "Cursor?");
2288 m_aSelEng.CursorPosChanging( bShift, bMod1 );
2289 if( IsEntryInView( pNewCursor ) )
2290 SetCursor( pNewCursor );
2291 else
2293 SetCursor( pNewCursor );
2294 KeyDown( true );
2297 else
2298 KeyDown( false ); // see also: KEY_DOWN
2300 else
2301 bKeyUsed = false;
2302 break;
2304 case KEY_SPACE:
2305 if ( m_pView->GetSelectionMode() != SelectionMode::NONE )
2307 if ( bMod1 )
2309 if ( m_pView->GetSelectionMode() == SelectionMode::Multiple && !bShift )
2310 // toggle selection
2311 m_pView->Select( m_pCursor, !m_pView->IsSelected( m_pCursor ) );
2313 else if ( !bShift /*&& !bMod1*/ )
2315 if ( m_aSelEng.IsAddMode() )
2317 // toggle selection
2318 m_pView->Select( m_pCursor, !m_pView->IsSelected( m_pCursor ) );
2320 else if ( !m_pView->IsSelected( m_pCursor ) )
2322 SelAllDestrAnch( false );
2323 m_pView->Select( m_pCursor );
2325 else
2326 bKeyUsed = false;
2328 else
2329 bKeyUsed = false;
2331 else
2332 bKeyUsed = false;
2333 break;
2335 case KEY_RETURN:
2336 bKeyUsed = !m_pView->DoubleClickHdl();
2337 break;
2339 case KEY_F2:
2340 if( !bShift && !bMod1 )
2342 m_aEditClickPos = Point( -1, -1 );
2343 EditTimerCall( nullptr );
2345 else
2346 bKeyUsed = false;
2347 break;
2349 case KEY_F8:
2350 if( bShift && m_pView->GetSelectionMode()==SelectionMode::Multiple &&
2351 !(m_nStyle & WB_SIMPLEMODE))
2353 if( m_aSelEng.IsAlwaysAdding() )
2354 m_aSelEng.AddAlways( false );
2355 else
2356 m_aSelEng.AddAlways( true );
2358 else
2359 bKeyUsed = false;
2360 break;
2362 case KEY_ADD:
2363 if (!m_pView->IsExpanded(m_pCursor))
2364 m_pView->Expand(m_pCursor);
2365 if (bMod1)
2366 ExpandAll();
2367 break;
2369 case KEY_A:
2370 if( bMod1 )
2371 SelAllDestrAnch( true );
2372 else
2373 bKeyUsed = false;
2374 break;
2376 case KEY_SUBTRACT:
2377 if (m_pView->IsExpanded(m_pCursor))
2378 m_pView->Collapse(m_pCursor);
2379 if (bMod1)
2380 CollapseTo(m_pTree->GetRootLevelParent(m_pCursor));
2381 break;
2383 case KEY_MULTIPLY:
2384 if( bMod1 )
2386 // only try to expand/collapse if sublist is expandable,
2387 // otherwise ignore the key press
2388 if( IsExpandable() )
2390 if (!m_pView->IsAllExpanded(m_pCursor))
2392 m_pView->Expand(m_pCursor);
2393 ExpandAll();
2395 else
2396 CollapseTo(m_pCursor);
2399 else
2400 bKeyUsed = false;
2401 break;
2403 case KEY_DIVIDE :
2404 if( bMod1 )
2405 SelAllDestrAnch( true );
2406 else
2407 bKeyUsed = false;
2408 break;
2410 case KEY_COMMA :
2411 if( bMod1 )
2412 SelAllDestrAnch( false );
2413 else
2414 bKeyUsed = false;
2415 break;
2417 case KEY_HOME :
2418 pNewCursor = m_pView->GetModel()->First();
2420 while( pNewCursor && !IsSelectable(pNewCursor) )
2422 pNewCursor = m_pView->NextVisible(pNewCursor);
2425 if( pNewCursor && pNewCursor != m_pCursor )
2427 // SelAllDestrAnch( false );
2428 m_aSelEng.CursorPosChanging( bShift, bMod1 );
2429 SetCursor( pNewCursor );
2430 if( !IsEntryInView( pNewCursor ) )
2431 MakeVisible( pNewCursor );
2433 else
2434 bKeyUsed = false;
2435 break;
2437 case KEY_END :
2438 pNewCursor = m_pView->GetModel()->Last();
2440 while( pNewCursor && !IsSelectable(pNewCursor) )
2442 pNewCursor = m_pView->PrevVisible(pNewCursor);
2445 if( pNewCursor && pNewCursor != m_pCursor)
2447 // SelAllDestrAnch( false );
2448 m_aSelEng.CursorPosChanging( bShift, bMod1 );
2449 SetCursor( pNewCursor );
2450 if( !IsEntryInView( pNewCursor ) )
2451 MakeVisible( pNewCursor );
2453 else
2454 bKeyUsed = false;
2455 break;
2457 case KEY_ESCAPE:
2458 case KEY_TAB:
2459 case KEY_DELETE:
2460 case KEY_BACKSPACE:
2461 // must not be handled because this quits dialogs and does other magic things...
2462 // if there are other single keys which should not be handled, they can be added here
2463 bKeyUsed = false;
2464 break;
2466 default:
2467 // is there any reason why we should eat the events here? The only place where this is called
2468 // is from SvTreeListBox::KeyInput. If we set bKeyUsed to true here, then the key input
2469 // is just silenced. However, we want SvLBox::KeyInput to get a chance, to do the QuickSelection
2470 // handling.
2471 // (The old code here which intentionally set bKeyUsed to sal_True said this was because of "quick search"
2472 // handling, but actually there was no quick search handling anymore. We just re-implemented it.)
2473 // #i31275# / 2009-06-16 / frank.schoenheit@sun.com
2474 bKeyUsed = false;
2475 break;
2477 return bKeyUsed;
2480 void SvImpLBox::GetFocus()
2482 if( m_pCursor )
2484 m_pView->SetEntryFocus( m_pCursor, true );
2485 ShowCursor( true );
2486 // auskommentiert wg. deselectall
2487 // if( bSimpleTravel && !pView->IsSelected(pCursor) )
2488 // pView->Select( pCursor, true );
2490 if( m_nStyle & WB_HIDESELECTION )
2492 SvTreeListEntry* pEntry = m_pView->FirstSelected();
2493 while( pEntry )
2495 InvalidateEntry( pEntry );
2496 pEntry = m_pView->NextSelected( pEntry );
2501 void SvImpLBox::LoseFocus()
2503 m_aEditIdle.Stop();
2504 if( m_pCursor )
2505 m_pView->SetEntryFocus( m_pCursor,false );
2506 ShowCursor( false );
2508 if( m_nStyle & WB_HIDESELECTION )
2510 SvTreeListEntry* pEntry = m_pView ? m_pView->FirstSelected() : nullptr;
2511 while( pEntry )
2513 InvalidateEntry( pEntry );
2514 pEntry = m_pView->NextSelected( pEntry );
2520 // ********************************************************************
2521 // SelectionEngine
2522 // ********************************************************************
2524 void SvImpLBox::SelectEntry( SvTreeListEntry* pEntry, bool bSelect )
2526 m_pView->Select( pEntry, bSelect );
2529 ImpLBSelEng::ImpLBSelEng( SvImpLBox* pImpl, SvTreeListBox* pV )
2531 pImp = pImpl;
2532 pView = pV;
2535 ImpLBSelEng::~ImpLBSelEng()
2539 void ImpLBSelEng::BeginDrag()
2541 pImp->BeginDrag();
2544 void ImpLBSelEng::CreateAnchor()
2546 pImp->m_pAnchor = pImp->m_pCursor;
2549 void ImpLBSelEng::DestroyAnchor()
2551 pImp->m_pAnchor = nullptr;
2554 void ImpLBSelEng::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
2556 SvTreeListEntry* pNewCursor = pImp->MakePointVisible( rPoint );
2557 if( pNewCursor )
2559 // at SimpleTravel, the SetCursor is selected and the select handler is
2560 // called
2561 //if( !bDontSelectAtCursor && !pImp->bSimpleTravel )
2562 // pImp->SelectEntry( pNewCursor, true );
2563 pImp->SetCursor( pNewCursor, bDontSelectAtCursor );
2567 bool ImpLBSelEng::IsSelectionAtPoint( const Point& rPoint )
2569 SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
2570 if( pEntry )
2571 return pView->IsSelected(pEntry);
2572 return false;
2575 void ImpLBSelEng::DeselectAtPoint( const Point& rPoint )
2577 SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
2578 if( !pEntry )
2579 return;
2580 pImp->SelectEntry( pEntry, false );
2583 void ImpLBSelEng::DeselectAll()
2585 pImp->SelAllDestrAnch( false, false ); // don't reset SelectionEngine!
2586 pImp->m_nFlags &= ~LBoxFlags::DeselectAll;
2589 // ***********************************************************************
2590 // Selection
2591 // ***********************************************************************
2593 void SvImpLBox::SetAnchorSelection(SvTreeListEntry* pOldCursor,SvTreeListEntry* pNewCursor)
2595 SvTreeListEntry* pEntry;
2596 sal_uLong nAnchorVisPos = m_pView->GetVisiblePos( m_pAnchor );
2597 sal_uLong nOldVisPos = m_pView->GetVisiblePos( pOldCursor );
2598 sal_uLong nNewVisPos = m_pView->GetVisiblePos( pNewCursor );
2600 if( nOldVisPos > nAnchorVisPos ||
2601 ( nAnchorVisPos==nOldVisPos && nNewVisPos > nAnchorVisPos) )
2603 if( nNewVisPos > nOldVisPos )
2605 pEntry = pOldCursor;
2606 while( pEntry && pEntry != pNewCursor )
2608 m_pView->Select( pEntry );
2609 pEntry = m_pView->NextVisible(pEntry);
2611 if( pEntry )
2612 m_pView->Select( pEntry );
2613 return;
2616 if( nNewVisPos < nAnchorVisPos )
2618 pEntry = m_pAnchor;
2619 while( pEntry && pEntry != pOldCursor )
2621 m_pView->Select( pEntry, false );
2622 pEntry = m_pView->NextVisible(pEntry);
2624 if( pEntry )
2625 m_pView->Select( pEntry, false );
2627 pEntry = pNewCursor;
2628 while( pEntry && pEntry != m_pAnchor )
2630 m_pView->Select( pEntry );
2631 pEntry = m_pView->NextVisible(pEntry);
2633 if( pEntry )
2634 m_pView->Select( pEntry );
2635 return;
2638 if( nNewVisPos < nOldVisPos )
2640 pEntry = m_pView->NextVisible(pNewCursor);
2641 while( pEntry && pEntry != pOldCursor )
2643 m_pView->Select( pEntry, false );
2644 pEntry = m_pView->NextVisible(pEntry);
2646 if( pEntry )
2647 m_pView->Select( pEntry, false );
2648 return;
2651 else
2653 if( nNewVisPos < nOldVisPos ) // enlarge selection
2655 pEntry = pNewCursor;
2656 while( pEntry && pEntry != pOldCursor )
2658 m_pView->Select( pEntry );
2659 pEntry = m_pView->NextVisible(pEntry);
2661 if( pEntry )
2662 m_pView->Select( pEntry );
2663 return;
2666 if( nNewVisPos > nAnchorVisPos )
2668 pEntry = pOldCursor;
2669 while( pEntry && pEntry != m_pAnchor )
2671 m_pView->Select( pEntry, false );
2672 pEntry = m_pView->NextVisible(pEntry);
2674 if( pEntry )
2675 m_pView->Select( pEntry, false );
2676 pEntry = m_pAnchor;
2677 while( pEntry && pEntry != pNewCursor )
2679 m_pView->Select( pEntry );
2680 pEntry = m_pView->NextVisible(pEntry);
2682 if( pEntry )
2683 m_pView->Select( pEntry );
2684 return;
2687 if( nNewVisPos > nOldVisPos )
2689 pEntry = pOldCursor;
2690 while( pEntry && pEntry != pNewCursor )
2692 m_pView->Select( pEntry, false );
2693 pEntry = m_pView->NextVisible(pEntry);
2695 return;
2700 void SvImpLBox::SelAllDestrAnch(
2701 bool bSelect, bool bDestroyAnchor, bool bSingleSelToo )
2703 SvTreeListEntry* pEntry;
2704 m_nFlags &= ~LBoxFlags::DeselectAll;
2705 if( bSelect && m_bSimpleTravel )
2707 if( m_pCursor && !m_pView->IsSelected( m_pCursor ))
2709 m_pView->Select( m_pCursor );
2711 return;
2713 if( !bSelect && m_pView->GetSelectionCount() == 0 )
2715 if( m_bSimpleTravel && ( !GetUpdateMode() || !m_pCursor) )
2716 m_nFlags |= LBoxFlags::DeselectAll;
2717 return;
2719 if( bSelect && m_pView->GetSelectionCount() == m_pView->GetEntryCount())
2720 return;
2721 if( !bSingleSelToo && m_bSimpleTravel )
2722 return;
2724 if( !bSelect && m_pView->GetSelectionCount()==1 && m_pCursor &&
2725 m_pView->IsSelected( m_pCursor ))
2727 m_pView->Select( m_pCursor, false );
2728 if( bDestroyAnchor )
2729 DestroyAnchor(); // delete anchor & reset SelectionEngine
2730 else
2731 m_pAnchor = nullptr; // always delete internal anchor
2732 return;
2735 if( m_bSimpleTravel && !m_pCursor && !GetUpdateMode() )
2736 m_nFlags |= LBoxFlags::DeselectAll;
2738 ShowCursor( false );
2739 bool bUpdate = GetUpdateMode();
2741 m_nFlags |= LBoxFlags::IgnoreSelect; // EntryInserted should not do anything
2742 pEntry = m_pTree->First();
2743 while( pEntry )
2745 if( m_pView->Select( pEntry, bSelect ) )
2747 if( bUpdate && m_pView->IsEntryVisible(pEntry) )
2749 tools::Long nY = GetEntryLine( pEntry );
2750 if( IsLineVisible( nY ) )
2751 InvalidateEntry(pEntry);
2754 pEntry = m_pTree->Next( pEntry );
2756 m_nFlags &= ~LBoxFlags::IgnoreSelect;
2758 if( bDestroyAnchor )
2759 DestroyAnchor(); // delete anchor & reset SelectionEngine
2760 else
2761 m_pAnchor = nullptr; // always delete internal anchor
2762 ShowCursor( true );
2765 void SvImpLBox::SetSelectionMode( SelectionMode eSelMode )
2767 m_aSelEng.SetSelectionMode( eSelMode);
2768 if( eSelMode == SelectionMode::Single )
2769 m_bSimpleTravel = true;
2770 else
2771 m_bSimpleTravel = false;
2772 if( (m_nStyle & WB_SIMPLEMODE) && (eSelMode == SelectionMode::Multiple) )
2773 m_aSelEng.AddAlways( true );
2776 // ***********************************************************************
2777 // Drag & Drop
2778 // ***********************************************************************
2780 void SvImpLBox::SetDragDropMode( DragDropMode eDDMode )
2782 if( eDDMode != DragDropMode::NONE )
2784 m_aSelEng.ExpandSelectionOnMouseMove( false );
2785 m_aSelEng.EnableDrag( true );
2787 else
2789 m_aSelEng.ExpandSelectionOnMouseMove();
2790 m_aSelEng.EnableDrag( false );
2794 void SvImpLBox::BeginDrag()
2796 m_nFlags &= ~LBoxFlags::Filling;
2797 m_pView->StartDrag( 0, m_aSelEng.GetMousePosPixel() );
2800 void SvImpLBox::PaintDDCursor(SvTreeListEntry* pEntry, bool bShow)
2802 if (pEntry)
2805 SvViewDataEntry* pViewData = m_pView->GetViewData(pEntry);
2806 pViewData->SetDragTarget(bShow);
2807 #ifdef MACOSX
2808 // in MacOS we need to draw directly (as we are synchronous) or no invalidation happens
2809 m_pView->PaintEntry1(*pEntry, GetEntryLine(pEntry), *m_pView);
2810 #else
2811 InvalidateEntry(pEntry);
2812 #endif
2816 void SvImpLBox::Command( const CommandEvent& rCEvt )
2818 CommandEventId nCommand = rCEvt.GetCommand();
2820 if( nCommand == CommandEventId::ContextMenu )
2821 m_aEditIdle.Stop();
2823 // scroll mouse event?
2824 if (nCommand == CommandEventId::Wheel ||
2825 nCommand == CommandEventId::StartAutoScroll ||
2826 nCommand == CommandEventId::AutoScroll ||
2827 nCommand == CommandEventId::Gesture)
2829 if (m_pView->HandleScrollCommand(rCEvt, m_aHorSBar.get(), m_aVerSBar.get()))
2830 return;
2833 const Point& rPos = rCEvt.GetMousePosPixel();
2834 if( rPos.X() < m_aOutputSize.Width() && rPos.Y() < m_aOutputSize.Height() )
2835 m_aSelEng.Command( rCEvt );
2838 tools::Rectangle SvImpLBox::GetVisibleArea() const
2840 Point aPos( m_pView->GetMapMode().GetOrigin() );
2841 aPos.setX( aPos.X() * -1 );
2842 tools::Rectangle aRect( aPos, m_aOutputSize );
2843 return aRect;
2846 void SvImpLBox::Invalidate()
2848 m_pView->SetClipRegion();
2851 void SvImpLBox::SetCurEntry( SvTreeListEntry* pEntry )
2853 if ( ( m_aSelEng.GetSelectionMode() != SelectionMode::Single )
2854 && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE )
2856 SelAllDestrAnch( false );
2857 if ( pEntry )
2858 MakeVisible( pEntry );
2859 SetCursor( pEntry );
2860 if ( pEntry && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
2861 m_pView->Select( pEntry );
2864 IMPL_LINK_NOARG(SvImpLBox, EditTimerCall, Timer *, void)
2866 if( !m_pView->IsInplaceEditingEnabled() )
2867 return;
2869 bool bIsMouseTriggered = m_aEditClickPos.X() >= 0;
2870 if ( bIsMouseTriggered )
2872 Point aCurrentMousePos = m_pView->GetPointerPosPixel();
2873 if ( ( std::abs( aCurrentMousePos.X() - m_aEditClickPos.X() ) > 5 )
2874 || ( std::abs( aCurrentMousePos.Y() - m_aEditClickPos.Y() ) > 5 )
2877 return;
2881 SvTreeListEntry* pEntry = GetCurEntry();
2882 if( pEntry )
2884 ShowCursor( false );
2885 m_pView->ImplEditEntry( pEntry );
2886 ShowCursor( true );
2890 bool SvImpLBox::RequestHelp( const HelpEvent& rHEvt )
2892 if( rHEvt.GetMode() & HelpEventMode::QUICK )
2894 Point aPos( m_pView->ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
2895 if( !GetVisibleArea().IsInside( aPos ))
2896 return false;
2898 SvTreeListEntry* pEntry = GetEntry( aPos );
2899 if( pEntry )
2901 // recalculate text rectangle
2902 SvLBoxTab* pTab;
2903 SvLBoxItem* pItem = m_pView->GetItem( pEntry, aPos.X(), &pTab );
2904 if (!pItem || pItem->GetType() != SvLBoxItemType::String)
2905 return false;
2907 aPos = GetEntryPosition( pEntry );
2908 aPos.setX( m_pView->GetTabPos( pEntry, pTab ) ); //pTab->GetPos();
2909 Size aSize(pItem->GetWidth(m_pView, pEntry), pItem->GetHeight(m_pView, pEntry));
2910 SvLBoxTab* pNextTab = NextTab( pTab );
2911 bool bItemClipped = false;
2912 // is the item cut off by its right neighbor?
2913 if( pNextTab && m_pView->GetTabPos(pEntry,pNextTab) < aPos.X()+aSize.Width() )
2915 aSize.setWidth( pNextTab->GetPos() - pTab->GetPos() );
2916 bItemClipped = true;
2918 tools::Rectangle aItemRect( aPos, aSize );
2920 tools::Rectangle aViewRect( GetVisibleArea() );
2922 if( bItemClipped || !aViewRect.IsInside( aItemRect ) )
2924 // clip the right edge of the item at the edge of the view
2925 //if( aItemRect.Right() > aViewRect.Right() )
2926 // aItemRect.Right() = aViewRect.Right();
2928 Point aPt = m_pView->OutputToScreenPixel( aItemRect.TopLeft() );
2929 aItemRect.SetLeft( aPt.X() );
2930 aItemRect.SetTop( aPt.Y() );
2931 aPt = m_pView->OutputToScreenPixel( aItemRect.BottomRight() );
2932 aItemRect.SetRight( aPt.X() );
2933 aItemRect.SetBottom( aPt.Y() );
2935 Help::ShowQuickHelp( m_pView, aItemRect,
2936 static_cast<SvLBoxString*>(pItem)->GetText(), QuickHelpFlags::Left | QuickHelpFlags::VCenter );
2937 return true;
2941 return false;
2944 SvLBoxTab* SvImpLBox::NextTab( SvLBoxTab const * pTab )
2946 sal_uInt16 nTabCount = m_pView->TabCount();
2947 if( nTabCount <= 1 )
2948 return nullptr;
2949 for( int nTab=0; nTab < (nTabCount-1); nTab++)
2951 if( m_pView->aTabs[nTab].get() == pTab )
2952 return m_pView->aTabs[nTab+1].get();
2954 return nullptr;
2957 void SvImpLBox::SetUpdateMode( bool bMode )
2959 if( m_bUpdateMode != bMode )
2961 m_bUpdateMode = bMode;
2962 if( m_bUpdateMode )
2963 UpdateAll( false );
2967 bool SvImpLBox::SetMostRight( SvTreeListEntry* pEntry )
2969 if( m_pView->nTreeFlags & SvTreeFlags::RECALCTABS )
2971 m_nFlags |= LBoxFlags::IgnoreChangedTabs;
2972 m_pView->SetTabs();
2973 m_nFlags &= ~LBoxFlags::IgnoreChangedTabs;
2976 sal_uInt16 nLastTab = m_pView->aTabs.size() - 1;
2977 sal_uInt16 nLastItem = pEntry->ItemCount() - 1;
2978 if( !m_pView->aTabs.empty() && nLastItem != USHRT_MAX )
2980 if( nLastItem < nLastTab )
2981 nLastTab = nLastItem;
2983 SvLBoxTab* pTab = m_pView->aTabs[ nLastTab ].get();
2984 SvLBoxItem& rItem = pEntry->GetItem( nLastTab );
2986 tools::Long nTabPos = m_pView->GetTabPos( pEntry, pTab );
2988 tools::Long nMaxRight = GetOutputSize().Width();
2989 Point aPos( m_pView->GetMapMode().GetOrigin() );
2990 aPos.setX( aPos.X() * -1 ); // conversion document coordinates
2991 nMaxRight = nMaxRight + aPos.X() - 1;
2993 tools::Long nNextTab = nTabPos < nMaxRight ? nMaxRight : nMaxRight + 50;
2994 tools::Long nTabWidth = nNextTab - nTabPos + 1;
2995 auto nItemSize = rItem.GetWidth(m_pView,pEntry);
2996 tools::Long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );
2998 tools::Long nRight = nTabPos + nOffset + nItemSize;
2999 if( nRight > m_nMostRight )
3001 m_nMostRight = nRight;
3002 m_pMostRightEntry = pEntry;
3003 return true;
3006 return false;
3009 void SvImpLBox::FindMostRight()
3011 m_nMostRight = -1;
3012 m_pMostRightEntry = nullptr;
3013 if( !m_pView->GetModel() )
3014 return;
3016 SvTreeListEntry* pEntry = m_pView->FirstVisible();
3017 while( pEntry )
3019 SetMostRight( pEntry );
3020 pEntry = m_pView->NextVisible( pEntry );
3024 void SvImpLBox::FindMostRight( SvTreeListEntry* pParent )
3026 if( !pParent )
3027 FindMostRight();
3028 else
3029 FindMostRight_Impl( pParent );
3032 void SvImpLBox::FindMostRight_Impl( SvTreeListEntry* pParent )
3034 SvTreeListEntries& rList = m_pTree->GetChildList( pParent );
3036 size_t nCount = rList.size();
3037 for( size_t nCur = 0; nCur < nCount; nCur++ )
3039 SvTreeListEntry* pChild = rList[nCur].get();
3040 SetMostRight( pChild );
3041 if( pChild->HasChildren() && m_pView->IsExpanded( pChild ))
3042 FindMostRight_Impl( pChild );
3046 void SvImpLBox::NotifyTabsChanged()
3048 if( GetUpdateMode() && !(m_nFlags & LBoxFlags::IgnoreChangedTabs ) &&
3049 m_nCurUserEvent == nullptr )
3051 m_nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpLBox,MyUserEvent));
3055 bool SvImpLBox::IsExpandable() const
3057 return m_pCursor->HasChildren() || m_pCursor->HasChildrenOnDemand();
3060 IMPL_LINK(SvImpLBox, MyUserEvent, void*, pArg, void )
3062 m_nCurUserEvent = nullptr;
3063 if( !pArg )
3065 m_pView->Invalidate();
3066 m_pView->PaintImmediately();
3068 else
3070 FindMostRight();
3071 ShowVerSBar();
3072 m_pView->Invalidate( GetVisibleArea() );
3077 void SvImpLBox::StopUserEvent()
3079 if( m_nCurUserEvent != nullptr )
3081 Application::RemoveUserEvent( m_nCurUserEvent );
3082 m_nCurUserEvent = nullptr;
3086 void SvImpLBox::implInitDefaultNodeImages()
3088 if ( s_pDefCollapsed )
3089 // assume that all or nothing is initialized
3090 return;
3092 s_pDefCollapsed = new Image(StockImage::Yes, RID_BMP_TREENODE_COLLAPSED);
3093 s_pDefExpanded = new Image(StockImage::Yes, RID_BMP_TREENODE_EXPANDED);
3097 const Image& SvImpLBox::GetDefaultExpandedNodeImage( )
3099 implInitDefaultNodeImages();
3100 return *s_pDefExpanded;
3104 const Image& SvImpLBox::GetDefaultCollapsedNodeImage( )
3106 implInitDefaultNodeImages();
3107 return *s_pDefCollapsed;
3111 void SvImpLBox::CallEventListeners( VclEventId nEvent, void* pData )
3113 if ( m_pView )
3114 m_pView->CallImplEventListeners( nEvent, pData);
3118 bool SvImpLBox::IsSelectable( const SvTreeListEntry* pEntry )
3120 if( pEntry )
3122 SvViewDataEntry* pViewDataNewCur = m_pView->GetViewDataEntry(pEntry);
3123 return (pViewDataNewCur == nullptr) || pViewDataNewCur->IsSelectable();
3125 else
3127 return false;
3131 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */