Bump version to 5.0-14
[LibreOffice.git] / svtools / source / contnr / svimpbox.cxx
blobad1c81f1c54ba4a00dde3954bbea545d190538eb
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 <vcl/svapp.hxx>
21 #include <vcl/salnativewidgets.hxx>
22 #include <vcl/help.hxx>
23 #include <vcl/settings.hxx>
25 #include <cstdlib>
26 #include <stack>
28 #include <svtools/treelistbox.hxx>
29 #include <svtools/svlbitm.hxx>
30 #include <svimpbox.hxx>
31 #include <rtl/instance.hxx>
32 #include <svtools/svtresid.hxx>
33 #include <tools/wintypes.hxx>
34 #include <svtools/svtools.hrc>
35 #include <comphelper/processfactory.hxx>
36 #include <comphelper/string.hxx>
38 #include <svtools/treelistentry.hxx>
39 #include <svtools/viewdataentry.hxx>
41 #define NODE_BMP_TABDIST_NOTVALID -2000000
42 #define FIRST_ENTRY_TAB 1
44 // #i27063# (pl), #i32300# (pb) never access VCL after DeInitVCL - also no destructors
45 Image* SvImpLBox::s_pDefCollapsed = NULL;
46 Image* SvImpLBox::s_pDefExpanded = NULL;
47 sal_Int32 SvImpLBox::s_nImageRefCount = 0;
49 SvImpLBox::SvImpLBox( SvTreeListBox* pLBView, SvTreeList* pLBTree, WinBits nWinStyle)
50 : aVerSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_VSCROLL))
51 , aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
52 , aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
53 , aOutputSize(0, 0)
54 , aSelEng(pLBView, (FunctionSet*)0)
55 , aFctSet(this, &aSelEng, pLBView)
56 , nNextVerVisSize(0)
57 , nExtendedWinBits(0)
58 , bAreChildrenTransient(true)
59 , m_pStringSorter(NULL)
61 osl_atomic_increment(&s_nImageRefCount);
62 pView = pLBView;
63 pTree = pLBTree;
64 aSelEng.SetFunctionSet( (FunctionSet*)&aFctSet );
65 aSelEng.ExpandSelectionOnMouseMove( false );
66 SetStyle( nWinStyle );
67 SetSelectionMode( SINGLE_SELECTION );
68 SetDragDropMode( DragDropMode::NONE );
70 aVerSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollUpDownHdl ) );
71 aHorSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollLeftRightHdl ) );
72 aHorSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
73 aVerSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
74 aVerSBar->SetRange( Range(0,0) );
75 aVerSBar->Hide();
76 aHorSBar->SetRange( Range(0,0) );
77 aHorSBar->SetPageSize( 24 ); // pixels
78 aHorSBar->SetLineSize( 8 ); // pixels
80 nHorSBarHeight = (short)aHorSBar->GetSizePixel().Height();
81 nVerSBarWidth = (short)aVerSBar->GetSizePixel().Width();
83 pStartEntry = 0;
84 pCursor = 0;
85 pAnchor = 0;
86 nVisibleCount = 0; // number of rows of data in control
87 nNodeBmpTabDistance = NODE_BMP_TABDIST_NOTVALID;
88 nYoffsNodeBmp = 0;
89 nNodeBmpWidth = 0;
91 bAsyncBeginDrag = false;
92 aAsyncBeginDragIdle.SetPriority( SchedulerPriority::HIGHEST );
93 aAsyncBeginDragIdle.SetIdleHdl( LINK(this,SvImpLBox,BeginDragHdl));
94 // button animation in listbox
95 pActiveButton = 0;
96 pActiveEntry = 0;
97 pActiveTab = 0;
99 nFlags = 0;
100 nCurTabPos = FIRST_ENTRY_TAB;
102 aEditIdle.SetPriority( SchedulerPriority::LOWEST );
103 aEditIdle.SetIdleHdl( LINK(this,SvImpLBox,EditTimerCall) );
105 nMostRight = -1;
106 pMostRightEntry = 0;
107 nCurUserEvent = 0;
109 bUpdateMode = true;
110 bInVScrollHdl = false;
111 nFlags |= F_FILLING;
113 bSubLstOpRet = bSubLstOpLR = bContextMenuHandling = bIsCellFocusEnabled = false;
116 SvImpLBox::~SvImpLBox()
118 aEditIdle.Stop();
119 StopUserEvent();
121 delete m_pStringSorter;
122 if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
124 DELETEZ(s_pDefCollapsed);
125 DELETEZ(s_pDefExpanded);
127 aVerSBar.disposeAndClear();
128 aHorSBar.disposeAndClear();
129 aScrBarBox.disposeAndClear();
132 void SvImpLBox::UpdateStringSorter()
134 const ::com::sun::star::lang::Locale& rNewLocale = Application::GetSettings().GetLanguageTag().getLocale();
136 if( m_pStringSorter )
138 // different Locale from the older one, drop it and force recreate
139 const ::com::sun::star::lang::Locale &aLocale = m_pStringSorter->getLocale();
140 if( aLocale.Language != rNewLocale.Language ||
141 aLocale.Country != rNewLocale.Country ||
142 aLocale.Variant != rNewLocale.Variant )
144 delete m_pStringSorter;
145 m_pStringSorter = NULL;
149 if( !m_pStringSorter )
151 m_pStringSorter = new comphelper::string::NaturalStringSorter(
152 ::comphelper::getProcessComponentContext(),
153 rNewLocale);
157 // #97680# ----------------------
158 short SvImpLBox::UpdateContextBmpWidthVector( SvTreeListEntry* pEntry, short nWidth )
160 DBG_ASSERT( pView->pModel, "View and Model aren't valid!" );
162 sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
163 // initialize vector if necessary
164 std::vector< short >::size_type nSize = aContextBmpWidthVector.size();
165 while ( nDepth > nSize )
167 aContextBmpWidthVector.resize( nSize + 1 );
168 aContextBmpWidthVector.at( nSize ) = nWidth;
169 ++nSize;
171 if( aContextBmpWidthVector.size() == nDepth )
173 aContextBmpWidthVector.resize( nDepth + 1 );
174 aContextBmpWidthVector.at( nDepth ) = 0;
176 short nContextBmpWidth = aContextBmpWidthVector[ nDepth ];
177 if( nContextBmpWidth < nWidth )
179 aContextBmpWidthVector.at( nDepth ) = nWidth;
180 return nWidth;
182 else
183 return nContextBmpWidth;
186 void SvImpLBox::UpdateContextBmpWidthVectorFromMovedEntry( SvTreeListEntry* pEntry )
188 DBG_ASSERT( pEntry, "Moved Entry is invalid!" );
190 SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry->GetFirstItem( SV_ITEM_ID_LBOXCONTEXTBMP ) );
191 short nExpWidth = (short)pBmpItem->GetBitmap1().GetSizePixel().Width();
192 short nColWidth = (short)pBmpItem->GetBitmap2().GetSizePixel().Width();
193 short nMax = std::max(nExpWidth, nColWidth);
194 UpdateContextBmpWidthVector( pEntry, nMax );
196 if( pEntry->HasChildren() ) // recursive call, whether expanded or not
198 SvTreeListEntry* pChild = pView->FirstChild( pEntry );
199 DBG_ASSERT( pChild, "The first child is invalid!" );
202 UpdateContextBmpWidthVectorFromMovedEntry( pChild );
203 pChild = pView->Next( pChild );
204 } while ( pChild );
208 void SvImpLBox::UpdateContextBmpWidthMax( SvTreeListEntry* pEntry )
210 sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
211 if( aContextBmpWidthVector.size() < 1 )
212 return;
213 short nWidth = aContextBmpWidthVector[ nDepth ];
214 if( nWidth != pView->nContextBmpWidthMax ) {
215 pView->nContextBmpWidthMax = nWidth;
216 nFlags |= F_IGNORE_CHANGED_TABS;
217 pView->SetTabs();
218 nFlags &= ~F_IGNORE_CHANGED_TABS;
222 void SvImpLBox::CalcCellFocusRect( SvTreeListEntry* pEntry, Rectangle& rRect )
224 if ( pEntry && bIsCellFocusEnabled )
226 if ( nCurTabPos > FIRST_ENTRY_TAB )
228 SvLBoxItem* pItem = pCursor->GetItem( nCurTabPos );
229 rRect.Left() = pView->GetTab( pCursor, pItem )->GetPos();
231 if (pCursor->ItemCount() > static_cast<size_t>(nCurTabPos+1))
233 SvLBoxItem* pNextItem = pCursor->GetItem( nCurTabPos + 1 );
234 long nRight = pView->GetTab( pCursor, pNextItem )->GetPos() - 1;
235 if ( nRight < rRect.Right() )
236 rRect.Right() = nRight;
241 void SvImpLBox::SetStyle( WinBits i_nWinStyle )
243 m_nStyle = i_nWinStyle;
244 if ( ( m_nStyle & WB_SIMPLEMODE) && ( aSelEng.GetSelectionMode() == MULTIPLE_SELECTION ) )
245 aSelEng.AddAlways( true );
248 void SvImpLBox::SetExtendedWindowBits( ExtendedWinBits _nBits )
250 nExtendedWinBits = _nBits;
253 // don't touch the model any more
254 void SvImpLBox::Clear()
256 StopUserEvent();
257 pStartEntry = 0;
258 pAnchor = 0;
260 pActiveButton = 0;
261 pActiveEntry = 0;
262 pActiveTab = 0;
264 nMostRight = -1;
265 pMostRightEntry = 0;
267 // don't touch the cursor any more
268 if( pCursor )
270 if( pView->HasFocus() )
271 pView->HideFocus();
272 pCursor = 0;
274 aVerSBar->Hide();
275 aVerSBar->SetThumbPos( 0 );
276 Range aRange( 0, 0 );
277 aVerSBar->SetRange( aRange );
278 aOutputSize = pView->Control::GetOutputSizePixel();
279 nFlags &= ~(F_VER_SBARSIZE_WITH_HBAR | F_HOR_SBARSIZE_WITH_VBAR );
280 aHorSBar->Hide();
281 aHorSBar->SetThumbPos( 0 );
282 MapMode aMapMode( pView->GetMapMode());
283 aMapMode.SetOrigin( Point(0,0) );
284 pView->Control::SetMapMode( aMapMode );
285 aHorSBar->SetRange( aRange );
286 aHorSBar->SetSizePixel(Size(aOutputSize.Width(),nHorSBarHeight));
287 pView->SetClipRegion();
288 if( GetUpdateMode() )
289 pView->Invalidate( GetVisibleArea() );
290 nFlags |= F_FILLING;
291 if( !aHorSBar->IsVisible() && !aVerSBar->IsVisible() )
292 aScrBarBox->Hide();
294 aContextBmpWidthVector.clear();
296 CallEventListeners( VCLEVENT_LISTBOX_ITEMREMOVED, NULL );
299 // *********************************************************************
300 // Paint, navigate, scroll
301 // *********************************************************************
303 IMPL_LINK_NOARG(SvImpLBox, EndScrollHdl)
305 if( nFlags & F_ENDSCROLL_SET_VIS_SIZE )
307 aVerSBar->SetVisibleSize( nNextVerVisSize );
308 nFlags &= ~F_ENDSCROLL_SET_VIS_SIZE;
310 EndScroll();
311 return 0;
314 // handler for vertical scrollbar
316 IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar )
318 DBG_ASSERT(!bInVScrollHdl,"Scroll handler out-paces itself!");
319 long nDelta = pScrollBar->GetDelta();
320 if( !nDelta )
321 return 0;
323 nFlags &= (~F_FILLING);
325 bInVScrollHdl = true;
327 if( pView->IsEditingActive() )
329 pView->EndEditing( true ); // Cancel
330 pView->Update();
332 BeginScroll();
334 if( nDelta > 0 )
336 if( nDelta == 1 )
337 CursorDown();
338 else
339 PageDown( (sal_uInt16) nDelta );
341 else
343 nDelta *= (-1);
344 if( nDelta == 1 )
345 CursorUp();
346 else
347 PageUp( (sal_uInt16) nDelta );
349 bInVScrollHdl = false;
350 return 0;
354 void SvImpLBox::CursorDown()
356 if (!pStartEntry)
357 return;
359 SvTreeListEntry* pNextFirstToDraw = pView->NextVisible(pStartEntry);
360 if( pNextFirstToDraw )
362 nFlags &= (~F_FILLING);
363 ShowCursor( false );
364 pView->Update();
365 pStartEntry = pNextFirstToDraw;
366 Rectangle aArea( GetVisibleArea() );
367 pView->Scroll( 0, -(pView->GetEntryHeight()), aArea, SCROLL_NOCHILDREN );
368 pView->Update();
369 ShowCursor( true );
370 pView->NotifyScrolled();
374 void SvImpLBox::CursorUp()
376 if (!pStartEntry)
377 return;
379 SvTreeListEntry* pPrevFirstToDraw = pView->PrevVisible(pStartEntry);
380 if( pPrevFirstToDraw )
382 nFlags &= (~F_FILLING);
383 long nEntryHeight = pView->GetEntryHeight();
384 ShowCursor( false );
385 pView->Update();
386 pStartEntry = pPrevFirstToDraw;
387 Rectangle aArea( GetVisibleArea() );
388 aArea.Bottom() -= nEntryHeight;
389 pView->Scroll( 0, nEntryHeight, aArea, SCROLL_NOCHILDREN );
390 pView->Update();
391 ShowCursor( true );
392 pView->NotifyScrolled();
396 void SvImpLBox::PageDown( sal_uInt16 nDelta )
398 sal_uInt16 nRealDelta = nDelta;
400 if( !nDelta )
401 return;
403 if (!pStartEntry)
404 return;
406 SvTreeListEntry* pNext = pView->NextVisible(pStartEntry, nRealDelta);
407 if( pNext == pStartEntry )
408 return;
410 ShowCursor( false );
412 nFlags &= (~F_FILLING);
413 pView->Update();
414 pStartEntry = pNext;
416 if( nRealDelta >= nVisibleCount )
418 pView->Invalidate( GetVisibleArea() );
419 pView->Update();
421 else
423 long nScroll = nRealDelta * (-1);
424 Rectangle aArea( GetVisibleArea() );
425 nScroll = pView->GetEntryHeight() * static_cast<long>(nRealDelta);
426 nScroll = -nScroll;
427 pView->Update();
428 pView->Scroll( 0, nScroll, aArea, SCROLL_NOCHILDREN );
429 pView->Update();
430 pView->NotifyScrolled();
433 ShowCursor( true );
436 void SvImpLBox::PageUp( sal_uInt16 nDelta )
438 sal_uInt16 nRealDelta = nDelta;
439 if( !nDelta )
440 return;
442 if (!pStartEntry)
443 return;
445 SvTreeListEntry* pPrev = pView->PrevVisible(pStartEntry, nRealDelta);
446 if( pPrev == pStartEntry )
447 return;
449 nFlags &= (~F_FILLING);
450 ShowCursor( false );
452 pView->Update();
453 pStartEntry = pPrev;
454 if( nRealDelta >= nVisibleCount )
456 pView->Invalidate( GetVisibleArea() );
457 pView->Update();
459 else
461 long nEntryHeight = pView->GetEntryHeight();
462 Rectangle aArea( GetVisibleArea() );
463 pView->Update();
464 pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, SCROLL_NOCHILDREN );
465 pView->Update();
466 pView->NotifyScrolled();
469 ShowCursor( true );
472 void SvImpLBox::KeyUp( bool bPageUp, bool bNotifyScroll )
474 if( !aVerSBar->IsVisible() )
475 return;
477 long nDelta;
478 if( bPageUp )
479 nDelta = aVerSBar->GetPageSize();
480 else
481 nDelta = 1;
483 long nThumbPos = aVerSBar->GetThumbPos();
485 if( nThumbPos < nDelta )
486 nDelta = nThumbPos;
488 if( nDelta <= 0 )
489 return;
491 nFlags &= (~F_FILLING);
492 if( bNotifyScroll )
493 BeginScroll();
495 aVerSBar->SetThumbPos( nThumbPos - nDelta );
496 if( bPageUp )
497 PageUp( (short)nDelta );
498 else
499 CursorUp();
501 if( bNotifyScroll )
502 EndScroll();
506 void SvImpLBox::KeyDown( bool bPageDown, bool bNotifyScroll )
508 if( !aVerSBar->IsVisible() )
509 return;
511 long nDelta;
512 if( bPageDown )
513 nDelta = aVerSBar->GetPageSize();
514 else
515 nDelta = 1;
517 long nThumbPos = aVerSBar->GetThumbPos();
518 long nVisibleSize = aVerSBar->GetVisibleSize();
519 long nRange = aVerSBar->GetRange().Len();
521 long nTmp = nThumbPos+nVisibleSize;
522 while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
523 nDelta--;
525 if( nDelta <= 0 )
526 return;
528 nFlags &= (~F_FILLING);
529 if( bNotifyScroll )
530 BeginScroll();
532 aVerSBar->SetThumbPos( nThumbPos+nDelta );
533 if( bPageDown )
534 PageDown( (short)nDelta );
535 else
536 CursorDown();
538 if( bNotifyScroll )
539 EndScroll();
544 void SvImpLBox::InvalidateEntriesFrom( long nY ) const
546 if( !(nFlags & F_IN_PAINT ))
548 Rectangle aRect( GetVisibleArea() );
549 aRect.Top() = nY;
550 pView->Invalidate( aRect );
554 void SvImpLBox::InvalidateEntry( long nY ) const
556 if( !(nFlags & F_IN_PAINT ))
558 Rectangle aRect( GetVisibleArea() );
559 long nMaxBottom = aRect.Bottom();
560 aRect.Top() = nY;
561 aRect.Bottom() = nY; aRect.Bottom() += pView->GetEntryHeight();
562 if( aRect.Top() > nMaxBottom )
563 return;
564 if( aRect.Bottom() > nMaxBottom )
565 aRect.Bottom() = nMaxBottom;
566 pView->Invalidate( aRect );
570 void SvImpLBox::InvalidateEntry( SvTreeListEntry* pEntry )
572 if( GetUpdateMode() )
574 long nPrev = nMostRight;
575 SetMostRight( pEntry );
576 if( nPrev < nMostRight )
577 ShowVerSBar();
579 if( !(nFlags & F_IN_PAINT ))
581 bool bHasFocusRect = false;
582 if( pEntry==pCursor && pView->HasFocus() )
584 bHasFocusRect = true;
585 ShowCursor( false );
587 InvalidateEntry( GetEntryLine( pEntry ) );
588 if( bHasFocusRect )
589 ShowCursor( true );
594 void SvImpLBox::RecalcFocusRect()
596 if( pView->HasFocus() && pCursor )
598 pView->HideFocus();
599 long nY = GetEntryLine( pCursor );
600 Rectangle aRect = pView->GetFocusRect( pCursor, nY );
601 CalcCellFocusRect( pCursor, aRect );
602 vcl::Region aOldClip( pView->GetClipRegion());
603 vcl::Region aClipRegion( GetClipRegionRect() );
604 pView->SetClipRegion( aClipRegion );
605 pView->ShowFocus( aRect );
606 pView->SetClipRegion( aOldClip );
611 // Sets cursor. When using SingleSelection, the selection is adjusted.
614 void SvImpLBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
616 SvViewDataEntry* pViewDataNewCur = 0;
617 if( pEntry )
618 pViewDataNewCur= pView->GetViewDataEntry(pEntry);
619 if( pEntry &&
620 pEntry == pCursor &&
621 pViewDataNewCur &&
622 pViewDataNewCur->HasFocus() &&
623 pViewDataNewCur->IsSelected())
625 return;
628 // if this cursor is not selectable, find first visible that is and use it
629 while( pEntry && pViewDataNewCur && !pViewDataNewCur->IsSelectable() )
631 pEntry = pView->NextVisible(pEntry);
632 pViewDataNewCur = pEntry ? pView->GetViewDataEntry(pEntry) : 0;
635 SvTreeListEntry* pOldCursor = pCursor;
636 if( pCursor && pEntry != pCursor )
638 pView->SetEntryFocus( pCursor, false );
639 if( bSimpleTravel )
640 pView->Select( pCursor, false );
641 pView->HideFocus();
643 pCursor = pEntry;
644 if( pCursor )
646 if (pViewDataNewCur)
647 pViewDataNewCur->SetFocus( true );
648 if(!bForceNoSelect && bSimpleTravel && !(nFlags & F_DESEL_ALL) && GetUpdateMode())
650 pView->Select( pCursor, true );
651 CallEventListeners( VCLEVENT_LISTBOX_TREEFOCUS, pCursor );
653 // multiple selection: select in cursor move if we're not in
654 // Add mode (Ctrl-F8)
655 else if( GetUpdateMode() &&
656 pView->GetSelectionMode() == MULTIPLE_SELECTION &&
657 !(nFlags & F_DESEL_ALL) && !aSelEng.IsAddMode() &&
658 !bForceNoSelect )
660 pView->Select( pCursor, true );
661 CallEventListeners( VCLEVENT_LISTBOX_TREEFOCUS, pCursor );
663 else
665 ShowCursor( true );
666 if (bForceNoSelect && GetUpdateMode())
668 CallEventListeners( VCLEVENT_LISTBOX_TREEFOCUS, pCursor);
672 if( pAnchor )
674 DBG_ASSERT(aSelEng.GetSelectionMode() != SINGLE_SELECTION,"Mode?");
675 SetAnchorSelection( pOldCursor, pCursor );
678 nFlags &= (~F_DESEL_ALL);
680 pView->OnCurrentEntryChanged();
683 void SvImpLBox::ShowCursor( bool bShow )
685 if( !bShow || !pCursor || !pView->HasFocus() )
687 vcl::Region aOldClip( pView->GetClipRegion());
688 vcl::Region aClipRegion( GetClipRegionRect() );
689 pView->SetClipRegion( aClipRegion );
690 pView->HideFocus();
691 pView->SetClipRegion( aOldClip );
693 else
695 long nY = GetEntryLine( pCursor );
696 Rectangle aRect = pView->GetFocusRect( pCursor, nY );
697 CalcCellFocusRect( pCursor, aRect );
698 vcl::Region aOldClip( pView->GetClipRegion());
699 vcl::Region aClipRegion( GetClipRegionRect() );
700 pView->SetClipRegion( aClipRegion );
701 pView->ShowFocus( aRect );
702 pView->SetClipRegion( aOldClip );
708 void SvImpLBox::UpdateAll(
709 bool bInvalidateCompleteView, bool bUpdateVerScrollBar )
711 if( bUpdateVerScrollBar )
712 FindMostRight(0);
713 aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
714 SyncVerThumb();
715 FillView();
716 ShowVerSBar();
717 if( bSimpleTravel && pCursor && pView->HasFocus() )
718 pView->Select( pCursor, true );
719 ShowCursor( true );
720 if( bInvalidateCompleteView )
721 pView->Invalidate();
722 else
723 pView->Invalidate( GetVisibleArea() );
726 IMPL_LINK( SvImpLBox, ScrollLeftRightHdl, ScrollBar *, pScrollBar )
728 long nDelta = pScrollBar->GetDelta();
729 if( nDelta )
731 if( pView->IsEditingActive() )
733 pView->EndEditing( true ); // Cancel
734 pView->Update();
736 pView->nFocusWidth = -1;
737 KeyLeftRight( nDelta );
739 return 0;
742 void SvImpLBox::KeyLeftRight( long nDelta )
744 if( !(nFlags & F_IN_RESIZE) )
745 pView->Update();
746 BeginScroll();
747 nFlags &= (~F_FILLING);
748 ShowCursor( false );
750 // neuen Origin berechnen
751 long nPos = aHorSBar->GetThumbPos();
752 Point aOrigin( -nPos, 0 );
754 MapMode aMapMode( pView->GetMapMode() );
755 aMapMode.SetOrigin( aOrigin );
756 pView->SetMapMode( aMapMode );
758 if( !(nFlags & F_IN_RESIZE) )
760 Rectangle aRect( GetVisibleArea() );
761 pView->Scroll( -nDelta, 0, aRect, SCROLL_NOCHILDREN );
763 else
764 pView->Invalidate();
765 RecalcFocusRect();
766 ShowCursor( true );
767 pView->NotifyScrolled();
771 // returns the last entry if position is just past the last entry
772 SvTreeListEntry* SvImpLBox::GetClickedEntry( const Point& rPoint ) const
774 DBG_ASSERT( pView->GetModel(), "SvImpLBox::GetClickedEntry: how can this ever happen? Please tell me (frank.schoenheit@sun.com) how to reproduce!" );
775 if ( !pView->GetModel() )
776 // this is quite impossible. Nevertheless, stack traces from the crash reporter
777 // suggest it isn't. Okay, make it safe, and wait for somebody to reproduce it
778 // reliably :-\ ....
779 // #122359# / 2005-05-23 / frank.schoenheit@sun.com
780 return NULL;
781 if( pView->GetEntryCount() == 0 || !pStartEntry || !pView->GetEntryHeight())
782 return 0;
784 sal_uInt16 nClickedEntry = (sal_uInt16)(rPoint.Y() / pView->GetEntryHeight() );
785 sal_uInt16 nTemp = nClickedEntry;
786 SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
787 return pEntry;
791 // checks if the entry was hit "the right way"
792 // (Focusrect+ ContextBitmap bei TreeListBox)
794 bool SvImpLBox::EntryReallyHit(SvTreeListEntry* pEntry, const Point& rPosPixel, long nLine)
796 bool bRet;
797 // we are not too exact when it comes to "special" entries
798 // (with CheckButtons etc.)
799 if( pEntry->ItemCount() >= 3 )
800 return true;
802 Rectangle aRect( pView->GetFocusRect( pEntry, nLine ));
803 aRect.Right() = GetOutputSize().Width() - pView->GetMapMode().GetOrigin().X();
805 SvLBoxContextBmp* pBmp = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP));
806 aRect.Left() -= pBmp->GetSize(pView,pEntry).Width();
807 aRect.Left() -= 4; // a little tolerance
809 Point aPos( rPosPixel );
810 aPos -= pView->GetMapMode().GetOrigin();
811 if( aRect.IsInside( aPos ) )
812 bRet = true;
813 else
814 bRet = false;
815 return bRet;
819 // returns 0 if position is just past the last entry
820 SvTreeListEntry* SvImpLBox::GetEntry( const Point& rPoint ) const
822 if( (pView->GetEntryCount() == 0) || !pStartEntry ||
823 (rPoint.Y() > aOutputSize.Height())
824 || !pView->GetEntryHeight())
825 return 0;
827 sal_uInt16 nClickedEntry = (sal_uInt16)(rPoint.Y() / pView->GetEntryHeight() );
828 sal_uInt16 nTemp = nClickedEntry;
829 SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
830 if( nTemp != nClickedEntry )
831 pEntry = 0;
832 return pEntry;
836 SvTreeListEntry* SvImpLBox::MakePointVisible(const Point& rPoint, bool bNotifyScroll)
838 if( !pCursor )
839 return 0;
840 long nY = rPoint.Y();
841 SvTreeListEntry* pEntry = 0;
842 long nMax = aOutputSize.Height();
843 if( nY < 0 || nY >= nMax ) // aOutputSize.Height() )
845 if( nY < 0 )
846 pEntry = pView->PrevVisible(pCursor);
847 else
848 pEntry = pView->NextVisible(pCursor);
850 if( pEntry && pEntry != pCursor )
851 pView->SetEntryFocus( pCursor, false );
853 if( nY < 0 )
854 KeyUp( false, bNotifyScroll );
855 else
856 KeyDown( false, bNotifyScroll );
858 else
860 pEntry = GetClickedEntry( rPoint );
861 if( !pEntry )
863 sal_uInt16 nSteps = 0xFFFF;
864 // TODO: LastVisible is not yet implemented!
865 pEntry = pView->NextVisible(pStartEntry, nSteps);
867 if( pEntry )
869 if( pEntry != pCursor &&
870 aSelEng.GetSelectionMode() == SINGLE_SELECTION
872 pView->Select( pCursor, false );
875 return pEntry;
878 Rectangle SvImpLBox::GetClipRegionRect() const
880 Point aOrigin( pView->GetMapMode().GetOrigin() );
881 aOrigin.X() *= -1; // conversion document coordinates
882 Rectangle aClipRect( aOrigin, aOutputSize );
883 aClipRect.Bottom()++;
884 return aClipRect;
888 void SvImpLBox::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
890 if (!pView->GetVisibleCount())
891 return;
893 nFlags |= F_IN_PAINT;
895 if (nFlags & F_FILLING)
897 SvTreeListEntry* pFirst = pView->First();
898 if (pFirst != pStartEntry)
900 ShowCursor(false);
901 pStartEntry = pView->First();
902 aVerSBar->SetThumbPos( 0 );
903 StopUserEvent();
904 ShowCursor(true);
905 nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
906 reinterpret_cast<void*>(1));
907 return;
911 if (!pStartEntry)
913 pStartEntry = pView->First();
916 if (nNodeBmpTabDistance == NODE_BMP_TABDIST_NOTVALID)
917 SetNodeBmpTabDistance();
919 long nRectHeight = rRect.GetHeight();
920 long nEntryHeight = pView->GetEntryHeight();
922 // calculate area for the entries we want to draw
923 sal_uInt16 nStartLine = static_cast<sal_uInt16>(rRect.Top() / nEntryHeight);
924 sal_uInt16 nCount = static_cast<sal_uInt16>(nRectHeight / nEntryHeight);
925 nCount += 2; // don't miss a row
927 long nY = nStartLine * nEntryHeight;
928 SvTreeListEntry* pEntry = pStartEntry;
929 while (nStartLine && pEntry)
931 pEntry = pView->NextVisible(pEntry);
932 nStartLine--;
935 vcl::Region aClipRegion(GetClipRegionRect());
937 // first draw the lines, then clip them!
938 rRenderContext.SetClipRegion();
939 if (m_nStyle & (WB_HASLINES | WB_HASLINESATROOT))
940 DrawNet(rRenderContext);
942 rRenderContext.SetClipRegion(aClipRegion);
944 for(sal_uInt16 n=0; n< nCount && pEntry; n++)
946 /*long nMaxRight=*/
947 pView->PaintEntry1(pEntry, nY, rRenderContext, SvLBoxTabFlags::ALL, true );
948 nY += nEntryHeight;
949 pEntry = pView->NextVisible(pEntry);
952 if (!pCursor && ((nExtendedWinBits & EWB_NO_AUTO_CURENTRY) == 0))
954 // do not select if multiselection or explicit set
955 bool bNotSelect = (aSelEng.GetSelectionMode() == MULTIPLE_SELECTION ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
956 SetCursor(pStartEntry, bNotSelect);
959 nFlags &= (~F_DESEL_ALL);
960 rRenderContext.SetClipRegion();
961 if (!(nFlags & F_PAINTED))
963 nFlags |= F_PAINTED;
965 nFlags &= (~F_IN_PAINT);
968 void SvImpLBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
970 if( !pEntry )
971 return;
973 bool bInView = IsEntryInView( pEntry );
975 if( bInView && (!bMoveToTop || pStartEntry == pEntry) )
976 return; // is already visible
978 if( pStartEntry || (m_nStyle & WB_FORCE_MAKEVISIBLE) )
979 nFlags &= (~F_FILLING);
980 if( !bInView )
982 if( !pView->IsEntryVisible(pEntry) ) // Parent(s) collapsed?
984 SvTreeListEntry* pParent = pView->GetParent( pEntry );
985 while( pParent )
987 if( !pView->IsExpanded( pParent ) )
989 #ifdef DBG_UTIL
990 bool bRet =
991 #endif
992 pView->Expand( pParent );
993 DBG_ASSERT(bRet,"Not expanded!");
995 pParent = pView->GetParent( pParent );
997 // do the parent's children fit into the view or do we have to scroll?
998 if( IsEntryInView( pEntry ) && !bMoveToTop )
999 return; // no need to scroll
1003 pStartEntry = pEntry;
1004 ShowCursor( false );
1005 FillView();
1006 aVerSBar->SetThumbPos( (long)(pView->GetVisiblePos( pStartEntry )) );
1007 ShowCursor( true );
1008 pView->Invalidate();
1011 void SvImpLBox::ScrollToAbsPos( long nPos )
1013 if( pView->GetVisibleCount() == 0 )
1014 return;
1015 long nLastEntryPos = pView->GetAbsPos( pView->Last() );
1017 if( nPos < 0 )
1018 nPos = 0;
1019 else if( nPos > nLastEntryPos )
1020 nPos = nLastEntryPos;
1022 SvTreeListEntry* pEntry = pView->GetEntryAtAbsPos( nPos );
1023 if( !pEntry || pEntry == pStartEntry )
1024 return;
1026 if( pStartEntry || (m_nStyle & WB_FORCE_MAKEVISIBLE) )
1027 nFlags &= (~F_FILLING);
1029 if( pView->IsEntryVisible(pEntry) )
1031 pStartEntry = pEntry;
1032 ShowCursor( false );
1033 aVerSBar->SetThumbPos( nPos );
1034 ShowCursor( true );
1035 if (GetUpdateMode())
1036 pView->Invalidate();
1040 void SvImpLBox::DrawNet(vcl::RenderContext& rRenderContext)
1042 if (pView->GetVisibleCount() < 2 && !pStartEntry->HasChildrenOnDemand() &&
1043 !pStartEntry->HasChildren())
1045 return;
1048 // for platforms that don't have nets, DrawNativeControl does nothing and returns true
1049 // so that SvImpLBox::DrawNet() doesn't draw anything either
1050 if (rRenderContext.IsNativeControlSupported(CTRL_LISTNET, PART_ENTIRE_CONTROL))
1052 ImplControlValue aControlValue;
1053 ControlState nState = ControlState::ENABLED;
1054 if (rRenderContext.DrawNativeControl(CTRL_LISTNET, PART_ENTIRE_CONTROL,
1055 Rectangle(), nState, aControlValue, OUString()))
1057 return;
1062 long nEntryHeight = pView->GetEntryHeight();
1063 long nEntryHeightDIV2 = nEntryHeight / 2;
1064 if( nEntryHeightDIV2 && !(nEntryHeight & 0x0001))
1065 nEntryHeightDIV2--;
1067 SvTreeListEntry* pChild;
1068 SvTreeListEntry* pEntry = pStartEntry;
1070 SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
1071 while (pTree->GetDepth( pEntry ) > 0)
1073 pEntry = pView->GetParent(pEntry);
1075 sal_uInt16 nOffs = static_cast<sal_uInt16>(pView->GetVisiblePos(pStartEntry) - pView->GetVisiblePos(pEntry));
1076 long nY = 0;
1077 nY -= (nOffs * nEntryHeight);
1079 DBG_ASSERT(pFirstDynamicTab,"No Tree!");
1081 rRenderContext.Push(PushFlags::LINECOLOR);
1083 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1084 Color aCol = rStyleSettings.GetFaceColor();
1086 if (aCol.IsRGBEqual(rRenderContext.GetBackground().GetColor()))
1087 aCol = rStyleSettings.GetShadowColor();
1088 rRenderContext.SetLineColor(aCol);
1089 Point aPos1, aPos2;
1090 sal_uInt16 nDistance;
1091 sal_uLong nMax = nVisibleCount + nOffs + 1;
1093 const Image& rExpandedNodeBitmap = GetExpandedNodeBmp();
1095 for (sal_uLong n=0; n< nMax && pEntry; n++)
1097 if (pView->IsExpanded(pEntry))
1099 aPos1.X() = pView->GetTabPos(pEntry, pFirstDynamicTab);
1100 // if it is not a context bitmap, go a little to the right below the
1101 // first text (node bitmap, too)
1102 if (!pView->nContextBmpWidthMax)
1103 aPos1.X() += rExpandedNodeBitmap.GetSizePixel().Width() / 2;
1105 aPos1.Y() = nY;
1106 aPos1.Y() += nEntryHeightDIV2;
1108 pChild = pView->FirstChild( pEntry );
1109 DBG_ASSERT(pChild,"Child?");
1110 pChild = SvTreeList::LastSibling( pChild );
1111 nDistance = static_cast<sal_uInt16>(pView->GetVisiblePos(pChild) - pView->GetVisiblePos(pEntry));
1112 aPos2 = aPos1;
1113 aPos2.Y() += nDistance * nEntryHeight;
1114 rRenderContext.DrawLine(aPos1, aPos2);
1116 // visible in control?
1117 if (n >= nOffs && ((m_nStyle & WB_HASLINESATROOT) || !pTree->IsAtRootDepth(pEntry)))
1119 // can we recycle aPos1?
1120 if (!pView->IsExpanded(pEntry))
1122 // nope
1123 aPos1.X() = pView->GetTabPos(pEntry, pFirstDynamicTab);
1124 // if it is not a context bitmap, go a little to the right below
1125 // the first text (node bitmap, too)
1126 if (!pView->nContextBmpWidthMax)
1127 aPos1.X() += rExpandedNodeBitmap.GetSizePixel().Width() / 2;
1128 aPos1.Y() = nY;
1129 aPos1.Y() += nEntryHeightDIV2;
1130 aPos2.X() = aPos1.X();
1132 aPos2.Y() = aPos1.Y();
1133 aPos2.X() -= pView->GetIndent();
1134 rRenderContext.DrawLine(aPos1, aPos2);
1136 nY += nEntryHeight;
1137 pEntry = pView->NextVisible(pEntry);
1139 if (m_nStyle & WB_HASLINESATROOT)
1141 pEntry = pView->First();
1142 aPos1.X() = pView->GetTabPos(pEntry, pFirstDynamicTab);
1143 // if it is not a context bitmap, go a little to the right below the
1144 // first text (node bitmap, too)
1145 if (!pView->nContextBmpWidthMax)
1146 aPos1.X() += rExpandedNodeBitmap.GetSizePixel().Width() / 2;
1147 aPos1.X() -= pView->GetIndent();
1148 aPos1.Y() = GetEntryLine( pEntry );
1149 aPos1.Y() += nEntryHeightDIV2;
1150 pChild = SvTreeList::LastSibling( pEntry );
1151 aPos2.X() = aPos1.X();
1152 aPos2.Y() = GetEntryLine( pChild );
1153 aPos2.Y() += nEntryHeightDIV2;
1154 rRenderContext.DrawLine(aPos1, aPos2);
1156 rRenderContext.Pop();
1159 void SvImpLBox::PositionScrollBars( Size& rSize, sal_uInt16 nMask )
1161 long nOverlap = 0;
1163 Size aVerSize( nVerSBarWidth, rSize.Height() );
1164 Size aHorSize( rSize.Width(), nHorSBarHeight );
1166 if( nMask & 0x0001 )
1167 aHorSize.Width() -= nVerSBarWidth;
1168 if( nMask & 0x0002 )
1169 aVerSize.Height() -= nHorSBarHeight;
1171 aVerSize.Height() += 2 * nOverlap;
1172 Point aVerPos( rSize.Width() - aVerSize.Width() + nOverlap, -nOverlap );
1173 aVerSBar->SetPosSizePixel( aVerPos, aVerSize );
1175 aHorSize.Width() += 2 * nOverlap;
1176 Point aHorPos( -nOverlap, rSize.Height() - aHorSize.Height() + nOverlap );
1178 aHorSBar->SetPosSizePixel( aHorPos, aHorSize );
1180 if( nMask & 0x0001 )
1181 rSize.Width() = aVerPos.X();
1182 if( nMask & 0x0002 )
1183 rSize.Height() = aHorPos.Y();
1185 if( (nMask & (0x0001|0x0002)) == (0x0001|0x0002) )
1186 aScrBarBox->Show();
1187 else
1188 aScrBarBox->Hide();
1191 // nResult: Bit0 == VerSBar Bit1 == HorSBar
1192 sal_uInt16 SvImpLBox::AdjustScrollBars( Size& rSize )
1194 long nEntryHeight = pView->GetEntryHeight();
1195 if( !nEntryHeight )
1196 return 0;
1198 sal_uInt16 nResult = 0;
1200 Size aOSize( pView->Control::GetOutputSizePixel() );
1202 const WinBits nWindowStyle = pView->GetStyle();
1203 bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
1204 bool bHorBar = false;
1205 long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
1206 Point aOrigin( pView->GetMapMode().GetOrigin() );
1207 aOrigin.X() *= -1;
1208 nMaxRight += aOrigin.X() - 1;
1209 long nVis = nMostRight - aOrigin.X();
1210 if( (nWindowStyle & WB_HSCROLL) &&
1211 (nVis < nMostRight || nMaxRight < nMostRight) )
1213 bHorBar = true;
1216 // number of entries that are not collapsed
1217 sal_uLong nTotalCount = pView->GetVisibleCount();
1219 // number of entries visible within the view
1220 nVisibleCount = aOSize.Height() / nEntryHeight;
1222 // do we need a vertical scrollbar?
1223 if( bVerSBar || nTotalCount > nVisibleCount )
1225 nResult = 1;
1226 nFlags |= F_HOR_SBARSIZE_WITH_VBAR;
1227 nMaxRight -= nVerSBarWidth;
1228 if( !bHorBar )
1230 if( (nWindowStyle & WB_HSCROLL) &&
1231 (nVis < nMostRight || nMaxRight < nMostRight) )
1232 bHorBar = true;
1236 // do we need a horizontal scrollbar?
1237 if( bHorBar )
1239 nResult |= 0x0002;
1240 // the number of entries visible within the view has to be recalculated
1241 // because the horizontal scrollbar is now visible.
1242 nVisibleCount = (aOSize.Height() - nHorSBarHeight) / nEntryHeight;
1243 // we might actually need a vertical scrollbar now
1244 if( !(nResult & 0x0001) &&
1245 ((nTotalCount > nVisibleCount) || bVerSBar) )
1247 nResult = 3;
1248 nFlags |= F_VER_SBARSIZE_WITH_HBAR;
1252 PositionScrollBars( aOSize, nResult );
1254 // adapt Range, VisibleRange etc.
1256 // refresh output size, in case we have to scroll
1257 Rectangle aRect;
1258 aRect.SetSize( aOSize );
1259 aSelEng.SetVisibleArea( aRect );
1261 // vertical scrollbar
1262 long nTemp = (long)nVisibleCount;
1263 nTemp--;
1264 if( nTemp != aVerSBar->GetVisibleSize() )
1266 if( !bInVScrollHdl )
1268 aVerSBar->SetPageSize( nTemp - 1 );
1269 aVerSBar->SetVisibleSize( nTemp );
1271 else
1273 nFlags |= F_ENDSCROLL_SET_VIS_SIZE;
1274 nNextVerVisSize = nTemp;
1278 // horizontal scrollbar
1279 nTemp = aHorSBar->GetThumbPos();
1280 aHorSBar->SetVisibleSize( aOSize.Width() );
1281 long nNewThumbPos = aHorSBar->GetThumbPos();
1282 Range aRange( aHorSBar->GetRange() );
1283 if( aRange.Max() < nMostRight+25 )
1285 aRange.Max() = nMostRight+25;
1286 aHorSBar->SetRange( aRange );
1289 if( nTemp != nNewThumbPos )
1291 nTemp = nNewThumbPos - nTemp;
1292 if( pView->IsEditingActive() )
1294 pView->EndEditing( true ); // Cancel
1295 pView->Update();
1297 pView->nFocusWidth = -1;
1298 KeyLeftRight( nTemp );
1301 if( nResult & 0x0001 )
1302 aVerSBar->Show();
1303 else
1304 aVerSBar->Hide();
1306 if( nResult & 0x0002 )
1307 aHorSBar->Show();
1308 else
1310 aHorSBar->Hide();
1312 rSize = aOSize;
1313 return nResult;
1316 void SvImpLBox::InitScrollBarBox()
1318 aScrBarBox->SetSizePixel( Size(nVerSBarWidth, nHorSBarHeight) );
1319 Size aSize( pView->Control::GetOutputSizePixel() );
1320 aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth, aSize.Height()-nHorSBarHeight));
1323 void SvImpLBox::Resize()
1325 aOutputSize = pView->Control::GetOutputSizePixel();
1326 if( aOutputSize.Width() <= 0 || aOutputSize.Height() <= 0 )
1327 return;
1328 nFlags |= F_IN_RESIZE;
1329 InitScrollBarBox();
1331 if( pView->GetEntryHeight())
1333 AdjustScrollBars( aOutputSize );
1334 UpdateAll(false);
1336 // HACK, as in floating and docked windows the scrollbars might not be drawn
1337 // correctly/not be drawn at all after resizing!
1338 if( aHorSBar->IsVisible())
1339 aHorSBar->Invalidate();
1340 if( aVerSBar->IsVisible())
1341 aVerSBar->Invalidate();
1342 nFlags &= (~(F_IN_RESIZE | F_PAINTED));
1345 void SvImpLBox::FillView()
1347 if( !pStartEntry )
1349 sal_uInt16 nVisibleViewCount = (sal_uInt16)(pView->GetVisibleCount());
1350 sal_uInt16 nTempThumb = (sal_uInt16)aVerSBar->GetThumbPos();
1351 if( nTempThumb >= nVisibleViewCount )
1352 nTempThumb = nVisibleViewCount - 1;
1353 pStartEntry = pView->GetEntryAtVisPos(nTempThumb);
1355 if( pStartEntry )
1357 sal_uInt16 nLast = (sal_uInt16)(pView->GetVisiblePos(pView->LastVisible()));
1358 sal_uInt16 nThumb = (sal_uInt16)(pView->GetVisiblePos( pStartEntry ));
1359 sal_uInt16 nCurDispEntries = nLast-nThumb+1;
1360 if( nCurDispEntries < nVisibleCount )
1362 ShowCursor( false );
1363 // fill window by moving the thumb up incrementally
1364 bool bFound = false;
1365 SvTreeListEntry* pTemp = pStartEntry;
1366 while( nCurDispEntries < nVisibleCount && pTemp )
1368 pTemp = pView->PrevVisible(pStartEntry);
1369 if( pTemp )
1371 nThumb--;
1372 pStartEntry = pTemp;
1373 nCurDispEntries++;
1374 bFound = true;
1377 if( bFound )
1379 aVerSBar->SetThumbPos( nThumb );
1380 ShowCursor( true ); // recalculate focus rectangle
1381 pView->Invalidate();
1390 void SvImpLBox::ShowVerSBar()
1392 bool bVerBar = ( pView->GetStyle() & WB_VSCROLL ) != 0;
1393 sal_uLong nVis = 0;
1394 if( !bVerBar )
1395 nVis = pView->GetVisibleCount();
1396 if( bVerBar || (nVisibleCount && nVis > (sal_uLong)(nVisibleCount-1)) )
1398 if( !aVerSBar->IsVisible() )
1400 pView->nFocusWidth = -1;
1401 AdjustScrollBars( aOutputSize );
1402 if( GetUpdateMode() )
1403 aVerSBar->Update();
1406 else
1408 if( aVerSBar->IsVisible() )
1410 pView->nFocusWidth = -1;
1411 AdjustScrollBars( aOutputSize );
1415 long nMaxRight = GetOutputSize().Width();
1416 Point aPos( pView->GetMapMode().GetOrigin() );
1417 aPos.X() *= -1; // convert document coordinates
1418 nMaxRight = nMaxRight + aPos.X() - 1;
1419 if( nMaxRight < nMostRight )
1421 if( !aHorSBar->IsVisible() )
1423 pView->nFocusWidth = -1;
1424 AdjustScrollBars( aOutputSize );
1425 if( GetUpdateMode() )
1426 aHorSBar->Update();
1428 else
1430 Range aRange( aHorSBar->GetRange() );
1431 if( aRange.Max() < nMostRight+25 )
1433 aRange.Max() = nMostRight+25;
1434 aHorSBar->SetRange( aRange );
1436 else
1438 pView->nFocusWidth = -1;
1439 AdjustScrollBars( aOutputSize );
1443 else
1445 if( aHorSBar->IsVisible() )
1447 pView->nFocusWidth = -1;
1448 AdjustScrollBars( aOutputSize );
1454 void SvImpLBox::SyncVerThumb()
1456 if( pStartEntry )
1458 long nEntryPos = pView->GetVisiblePos( pStartEntry );
1459 aVerSBar->SetThumbPos( nEntryPos );
1461 else
1462 aVerSBar->SetThumbPos( 0 );
1465 bool SvImpLBox::IsEntryInView( SvTreeListEntry* pEntry ) const
1467 // parent collapsed
1468 if( !pView->IsEntryVisible(pEntry) )
1469 return false;
1470 long nY = GetEntryLine( pEntry );
1471 if( nY < 0 )
1472 return false;
1473 long nMax = nVisibleCount * pView->GetEntryHeight();
1474 if( nY >= nMax )
1475 return false;
1476 return true;
1480 long SvImpLBox::GetEntryLine( SvTreeListEntry* pEntry ) const
1482 if(!pStartEntry )
1483 return -1; // invisible position
1485 long nFirstVisPos = pView->GetVisiblePos( pStartEntry );
1486 long nEntryVisPos = pView->GetVisiblePos( pEntry );
1487 nFirstVisPos = nEntryVisPos - nFirstVisPos;
1488 nFirstVisPos *= pView->GetEntryHeight();
1489 return nFirstVisPos;
1492 void SvImpLBox::SetEntryHeight( short /* nHeight */ )
1494 SetNodeBmpYOffset( GetExpandedNodeBmp() );
1495 SetNodeBmpYOffset( GetCollapsedNodeBmp() );
1496 if(!pView->HasViewData()) // are we within the Clear?
1498 Size aSize = pView->Control::GetOutputSizePixel();
1499 AdjustScrollBars( aSize );
1501 else
1503 Resize();
1504 if( GetUpdateMode() )
1505 pView->Invalidate();
1511 // ***********************************************************************
1512 // Callback Functions
1513 // ***********************************************************************
1515 void SvImpLBox::EntryExpanded( SvTreeListEntry* pEntry )
1517 // SelAllDestrAnch( false, true ); //DeselectAll();
1518 if( GetUpdateMode() )
1520 ShowCursor( false );
1521 long nY = GetEntryLine( pEntry );
1522 if( IsLineVisible(nY) )
1524 InvalidateEntriesFrom( nY );
1525 FindMostRight( pEntry, 0 );
1527 aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
1528 // if we expanded before the thumb, the thumb's position has to be
1529 // corrected
1530 SyncVerThumb();
1531 ShowVerSBar();
1532 ShowCursor( true );
1536 void SvImpLBox::EntryCollapsed( SvTreeListEntry* pEntry )
1538 if( !pView->IsEntryVisible( pEntry ) )
1539 return;
1541 ShowCursor( false );
1543 if( !pMostRightEntry || pTree->IsChild( pEntry,pMostRightEntry ) )
1545 FindMostRight(0);
1548 if( pStartEntry )
1550 long nOldThumbPos = aVerSBar->GetThumbPos();
1551 sal_uLong nVisList = pView->GetVisibleCount();
1552 aVerSBar->SetRange( Range(0, nVisList-1) );
1553 long nNewThumbPos = aVerSBar->GetThumbPos();
1554 if( nNewThumbPos != nOldThumbPos )
1556 pStartEntry = pView->First();
1557 sal_uInt16 nDistance = (sal_uInt16)nNewThumbPos;
1558 if( nDistance )
1559 pStartEntry = pView->NextVisible(pStartEntry, nDistance);
1560 if( GetUpdateMode() )
1561 pView->Invalidate();
1563 else
1564 SyncVerThumb();
1565 ShowVerSBar();
1567 // has the cursor been collapsed?
1568 if( pTree->IsChild( pEntry, pCursor ) )
1569 SetCursor( pEntry );
1570 if( GetUpdateMode() )
1571 ShowVerSBar();
1572 ShowCursor( true );
1573 if( GetUpdateMode() && pCursor )
1574 pView->Select( pCursor, true );
1577 void SvImpLBox::CollapsingEntry( SvTreeListEntry* pEntry )
1579 if( !pView->IsEntryVisible( pEntry ) || !pStartEntry )
1580 return;
1582 SelAllDestrAnch( false, true ); // deselect all
1584 // is the collapsed cursor visible?
1585 long nY = GetEntryLine( pEntry );
1586 if( IsLineVisible(nY) )
1588 if( GetUpdateMode() )
1589 InvalidateEntriesFrom( nY );
1591 else
1593 if( pTree->IsChild(pEntry, pStartEntry) )
1595 pStartEntry = pEntry;
1596 if( GetUpdateMode() )
1597 pView->Invalidate();
1603 void SvImpLBox::SetNodeBmpYOffset( const Image& rBmp )
1605 Size aSize;
1606 nYoffsNodeBmp = pView->GetHeightOffset( rBmp, aSize );
1607 nNodeBmpWidth = aSize.Width();
1610 void SvImpLBox::SetNodeBmpTabDistance()
1612 nNodeBmpTabDistance = -pView->GetIndent();
1613 if( pView->nContextBmpWidthMax )
1615 // only if the first dynamic tab is centered (we currently assume that)
1616 Size aSize = GetExpandedNodeBmp().GetSizePixel();
1617 nNodeBmpTabDistance -= aSize.Width() / 2;
1622 // corrects the cursor when using SingleSelection
1624 void SvImpLBox::EntrySelected( SvTreeListEntry* pEntry, bool bSelect )
1626 if( nFlags & F_IGNORE_SELECT )
1627 return;
1629 nFlags &= (~F_DESEL_ALL);
1630 if( bSelect &&
1631 aSelEng.GetSelectionMode() == SINGLE_SELECTION &&
1632 pEntry != pCursor )
1634 SetCursor( pEntry );
1635 DBG_ASSERT(pView->GetSelectionCount()==1,"selection count?");
1638 if( GetUpdateMode() && pView->IsEntryVisible(pEntry) )
1640 long nY = GetEntryLine( pEntry );
1641 if( IsLineVisible( nY ) )
1643 ShowCursor(false);
1644 InvalidateEntry(pEntry);
1645 ShowCursor(true);
1651 void SvImpLBox::RemovingEntry( SvTreeListEntry* pEntry )
1653 CallEventListeners( VCLEVENT_LISTBOX_ITEMREMOVED , pEntry );
1655 DestroyAnchor();
1657 if( !pView->IsEntryVisible( pEntry ) )
1659 // if parent is collapsed => bye!
1660 nFlags |= F_REMOVED_ENTRY_INVISIBLE;
1661 return;
1664 if( pEntry == pMostRightEntry || (
1665 pEntry->HasChildren() && pView->IsExpanded(pEntry) &&
1666 pTree->IsChild(pEntry, pMostRightEntry)))
1668 nFlags |= F_REMOVED_RECALC_MOST_RIGHT;
1671 SvTreeListEntry* pOldStartEntry = pStartEntry;
1673 SvTreeListEntry* pParent = pView->GetModel()->GetParent(pEntry);
1675 if (pParent && pView->GetModel()->GetChildList(pParent).size() == 1)
1677 DBG_ASSERT( pView->IsExpanded( pParent ), "Parent not expanded");
1678 pParent->SetFlags( pParent->GetFlags() | SvTLEntryFlags::NO_NODEBMP);
1679 InvalidateEntry( pParent );
1682 if( pCursor && pTree->IsChild( pEntry, pCursor) )
1683 pCursor = pEntry;
1684 if( pStartEntry && pTree->IsChild(pEntry,pStartEntry) )
1685 pStartEntry = pEntry;
1687 SvTreeListEntry* pTemp;
1688 if( pCursor && pCursor == pEntry )
1690 if( bSimpleTravel )
1691 pView->Select( pCursor, false );
1692 ShowCursor( false ); // focus rectangle gone
1693 // NextSibling, because we also delete the children of the cursor
1694 pTemp = SvTreeListBox::NextSibling( pCursor );
1695 if( !pTemp )
1696 pTemp = pView->PrevVisible(pCursor);
1698 SetCursor( pTemp, true );
1700 if( pStartEntry && pStartEntry == pEntry )
1702 pTemp = SvTreeListBox::NextSibling( pStartEntry );
1703 if( !pTemp )
1704 pTemp = pView->PrevVisible(pStartEntry);
1705 pStartEntry = pTemp;
1707 if( GetUpdateMode())
1709 // if it is the last one, we have to invalidate it, so the lines are
1710 // drawn correctly (in this case they're deleted)
1711 if( pStartEntry && (pStartEntry != pOldStartEntry || pEntry == pView->GetModel()->Last()) )
1713 aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry ));
1714 pView->Invalidate( GetVisibleArea() );
1716 else
1717 InvalidateEntriesFrom( GetEntryLine( pEntry ) );
1721 void SvImpLBox::EntryRemoved()
1723 if( nFlags & F_REMOVED_ENTRY_INVISIBLE )
1725 nFlags &= (~F_REMOVED_ENTRY_INVISIBLE);
1726 return;
1728 if( !pStartEntry )
1729 pStartEntry = pTree->First();
1730 if( !pCursor )
1731 SetCursor( pStartEntry, true );
1733 if( pCursor && (bSimpleTravel || !pView->GetSelectionCount() ))
1734 pView->Select( pCursor, true );
1736 if( GetUpdateMode())
1738 if( nFlags & F_REMOVED_RECALC_MOST_RIGHT )
1739 FindMostRight(0);
1740 aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
1741 FillView();
1742 if( pStartEntry )
1743 // if something above the thumb was deleted
1744 aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry) );
1746 ShowVerSBar();
1747 if( pCursor && pView->HasFocus() && !pView->IsSelected(pCursor) )
1749 if( pView->GetSelectionCount() )
1751 // is a neighboring entry selected?
1752 SvTreeListEntry* pNextCursor = pView->PrevVisible( pCursor );
1753 if( !pNextCursor || !pView->IsSelected( pNextCursor ))
1754 pNextCursor = pView->NextVisible( pCursor );
1755 if( !pNextCursor || !pView->IsSelected( pNextCursor ))
1756 // no neighbor selected: use first selected
1757 pNextCursor = pView->FirstSelected();
1758 SetCursor( pNextCursor );
1759 MakeVisible( pCursor );
1761 else
1762 pView->Select( pCursor, true );
1764 ShowCursor( true );
1766 nFlags &= (~F_REMOVED_RECALC_MOST_RIGHT);
1770 void SvImpLBox::MovingEntry( SvTreeListEntry* pEntry )
1772 int bDeselAll = nFlags & F_DESEL_ALL;
1773 SelAllDestrAnch( false, true ); // DeselectAll();
1774 if( !bDeselAll )
1775 nFlags &= (~F_DESEL_ALL);
1777 if( pEntry == pCursor )
1778 ShowCursor( false );
1779 if( IsEntryInView( pEntry ) )
1780 pView->Invalidate();
1781 if( pEntry == pStartEntry )
1783 SvTreeListEntry* pNew = 0;
1784 if( !pEntry->HasChildren() )
1786 pNew = pView->NextVisible(pStartEntry);
1787 if( !pNew )
1788 pNew = pView->PrevVisible(pStartEntry);
1790 else
1792 pNew = SvTreeList::NextSibling( pEntry );
1793 if( !pNew )
1794 pNew = SvTreeList::PrevSibling( pEntry );
1796 pStartEntry = pNew;
1800 void SvImpLBox::EntryMoved( SvTreeListEntry* pEntry )
1802 UpdateContextBmpWidthVectorFromMovedEntry( pEntry );
1804 if ( !pStartEntry )
1805 // this might happen if the only entry in the view is moved to its very same position
1806 // #i97346#
1807 pStartEntry = pView->First();
1809 aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
1810 sal_uInt16 nFirstPos = (sal_uInt16)pTree->GetAbsPos( pStartEntry );
1811 sal_uInt16 nNewPos = (sal_uInt16)pTree->GetAbsPos( pEntry );
1812 FindMostRight(0);
1813 if( nNewPos < nFirstPos ) // HACK!
1814 pStartEntry = pEntry;
1815 SyncVerThumb();
1816 if( pEntry == pCursor )
1818 if( pView->IsEntryVisible( pCursor ) )
1819 ShowCursor( true );
1820 else
1822 SvTreeListEntry* pParent = pEntry;
1823 do {
1824 pParent = pTree->GetParent( pParent );
1826 while( !pView->IsEntryVisible( pParent ) );
1827 SetCursor( pParent );
1830 if( IsEntryInView( pEntry ) )
1831 pView->Invalidate();
1836 void SvImpLBox::EntryInserted( SvTreeListEntry* pEntry )
1838 if( GetUpdateMode() )
1840 SvTreeListEntry* pParent = pTree->GetParent(pEntry);
1841 if (pParent && pTree->GetChildList(pParent).size() == 1)
1842 // draw plus sign
1843 pTree->InvalidateEntry( pParent );
1845 if( !pView->IsEntryVisible( pEntry ) )
1846 return;
1847 int bDeselAll = nFlags & F_DESEL_ALL;
1848 if( bDeselAll )
1849 SelAllDestrAnch( false, true );
1850 else
1851 DestroyAnchor();
1852 // nFlags &= (~F_DESEL_ALL);
1853 // ShowCursor( false ); // if cursor is moved lower
1854 long nY = GetEntryLine( pEntry );
1855 bool bEntryVisible = IsLineVisible( nY );
1856 if( bEntryVisible )
1858 ShowCursor( false ); // if cursor is moved lower
1859 nY -= pView->GetEntryHeight(); // because of lines
1860 InvalidateEntriesFrom( nY );
1862 else if( pStartEntry && nY < GetEntryLine(pStartEntry) )
1864 // Check if the view is filled completely. If not, then adjust
1865 // pStartEntry and the Cursor (automatic scrolling).
1866 sal_uInt16 nLast = (sal_uInt16)(pView->GetVisiblePos(pView->LastVisible()));
1867 sal_uInt16 nThumb = (sal_uInt16)(pView->GetVisiblePos( pStartEntry ));
1868 sal_uInt16 nCurDispEntries = nLast-nThumb+1;
1869 if( nCurDispEntries < nVisibleCount )
1871 // set at the next paint event
1872 pStartEntry = 0;
1873 SetCursor( 0 );
1874 pView->Invalidate();
1877 else if( !pStartEntry )
1878 pView->Invalidate();
1880 SetMostRight( pEntry );
1881 aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
1882 SyncVerThumb(); // if something was inserted before the thumb
1883 ShowVerSBar();
1884 ShowCursor( true );
1885 if( pStartEntry != pView->First() && (nFlags & F_FILLING) )
1886 pView->Update();
1892 // ********************************************************************
1893 // Event handler
1894 // ********************************************************************
1897 // ****** Control the control animation
1899 bool SvImpLBox::ButtonDownCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry, long /*nY*/)
1901 SvLBoxItem* pItem = pView->GetItem(pEntry,rMEvt.GetPosPixel().X(),&pActiveTab);
1902 if (pItem && pItem->GetType() == SV_ITEM_ID_LBOXBUTTON)
1904 pActiveButton = static_cast<SvLBoxButton*>(pItem);
1905 pActiveEntry = pEntry;
1906 if( pCursor == pActiveEntry )
1907 pView->HideFocus();
1908 pView->CaptureMouse();
1909 pActiveButton->SetStateHilighted( true );
1910 InvalidateEntry(pActiveEntry);
1911 return true;
1913 else
1914 pActiveButton = 0;
1915 return false;
1918 bool SvImpLBox::MouseMoveCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry)
1920 if( pActiveButton )
1922 long nMouseX = rMEvt.GetPosPixel().X();
1923 if( pEntry == pActiveEntry &&
1924 pView->GetItem(pActiveEntry, nMouseX) == pActiveButton )
1926 if( !pActiveButton->IsStateHilighted() )
1928 pActiveButton->SetStateHilighted(true );
1929 InvalidateEntry(pActiveEntry);
1932 else
1934 if( pActiveButton->IsStateHilighted() )
1936 pActiveButton->SetStateHilighted(false );
1937 InvalidateEntry(pActiveEntry);
1940 return true;
1942 return false;
1945 bool SvImpLBox::ButtonUpCheckCtrl( const MouseEvent& rMEvt )
1947 if( pActiveButton )
1949 pView->ReleaseMouse();
1950 SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
1951 pActiveButton->SetStateHilighted( false );
1952 long nMouseX = rMEvt.GetPosPixel().X();
1953 if (pEntry == pActiveEntry && pView->GetItem(pActiveEntry, nMouseX) == pActiveButton)
1954 pActiveButton->ClickHdl(pView, pActiveEntry);
1955 InvalidateEntry(pActiveEntry);
1956 if (pCursor == pActiveEntry)
1957 ShowCursor(true);
1958 pActiveButton = 0;
1959 pActiveEntry = 0;
1960 pActiveTab = 0;
1961 return true;
1963 return false;
1966 // ******* Control plus/minus button for expanding/collapsing
1968 // false == no expand/collapse button hit
1969 bool SvImpLBox::IsNodeButton( const Point& rPosPixel, SvTreeListEntry* pEntry ) const
1971 if( !pEntry->HasChildren() && !pEntry->HasChildrenOnDemand() )
1972 return false;
1974 SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
1975 if( !pFirstDynamicTab )
1976 return false;
1978 long nMouseX = rPosPixel.X();
1979 // convert to document coordinates
1980 Point aOrigin( pView->GetMapMode().GetOrigin() );
1981 nMouseX -= aOrigin.X();
1983 long nX = pView->GetTabPos( pEntry, pFirstDynamicTab);
1984 nX += nNodeBmpTabDistance;
1985 if( nMouseX < nX )
1986 return false;
1987 nX += nNodeBmpWidth;
1988 if( nMouseX > nX )
1989 return false;
1990 return true;
1993 // false == hit no node button
1994 bool SvImpLBox::ButtonDownCheckExpand( const MouseEvent& rMEvt, SvTreeListEntry* pEntry, long /* nY */ )
1996 bool bRet = false;
1998 if ( pView->IsEditingActive() && pEntry == pView->pEdEntry )
1999 // inplace editing -> nothing to do
2000 bRet = true;
2001 else if ( IsNodeButton( rMEvt.GetPosPixel(), pEntry ) )
2003 if ( pView->IsExpanded( pEntry ) )
2005 pView->EndEditing( true );
2006 pView->Collapse( pEntry );
2008 else
2010 // you can expand an entry, which is in editing
2011 pView->Expand( pEntry );
2013 bRet = true;
2016 return bRet;
2019 void SvImpLBox::MouseButtonDown( const MouseEvent& rMEvt )
2021 if ( !rMEvt.IsLeft() && !rMEvt.IsRight())
2022 return;
2024 aEditIdle.Stop();
2025 Point aPos( rMEvt.GetPosPixel());
2027 if( aPos.X() > aOutputSize.Width() || aPos.Y() > aOutputSize.Height() )
2028 return;
2030 SvTreeListEntry* pEntry = GetEntry( aPos );
2031 if ( pEntry != pCursor )
2032 // new entry selected -> reset current tab position to first tab
2033 nCurTabPos = FIRST_ENTRY_TAB;
2034 nFlags &= (~F_FILLING);
2035 pView->GrabFocus();
2036 //fdo#82270 Grabbing focus can invalidate the entries, re-fetch
2037 pEntry = GetEntry(aPos);
2038 // the entry can still be invalid!
2039 if( !pEntry || !pView->GetViewData( pEntry ))
2040 return;
2042 long nY = GetEntryLine( pEntry );
2043 // Node-Button?
2044 if( ButtonDownCheckExpand( rMEvt, pEntry, nY ) )
2045 return;
2047 if( !EntryReallyHit(pEntry,aPos,nY))
2048 return;
2050 SvLBoxItem* pXItem = pView->GetItem( pEntry, aPos.X() );
2051 if( pXItem )
2053 SvLBoxTab* pXTab = pView->GetTab( pEntry, pXItem );
2054 if ( !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && pXTab->IsEditable()
2055 && pEntry == pView->FirstSelected() && NULL == pView->NextSelected( pEntry ) )
2056 // #i8234# FirstSelected() and NextSelected() ensures, that inplace editing is only triggered, when only one entry is selected
2057 nFlags |= F_START_EDITTIMER;
2058 if ( !pView->IsSelected( pEntry ) )
2059 nFlags &= ~F_START_EDITTIMER;
2063 if( (rMEvt.GetClicks() % 2) == 0 )
2065 nFlags &= (~F_START_EDITTIMER);
2066 pView->pHdlEntry = pEntry;
2067 if( pView->DoubleClickHdl() )
2069 // if the entry was deleted within the handler
2070 pEntry = GetClickedEntry( aPos );
2071 if( !pEntry )
2072 return;
2073 if( pEntry != pView->pHdlEntry )
2075 // select anew & bye
2076 if( !bSimpleTravel && !aSelEng.IsAlwaysAdding())
2077 SelAllDestrAnch( false, true ); // DeselectAll();
2078 SetCursor( pEntry );
2080 return;
2082 if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() )
2084 if( pView->IsExpanded(pEntry) )
2085 pView->Collapse( pEntry );
2086 else
2087 pView->Expand( pEntry );
2088 if( pEntry == pCursor ) // only if Entryitem was clicked
2089 // (Nodebutton is not an Entryitem!)
2090 pView->Select( pCursor, true );
2091 return;
2095 else
2097 // CheckButton? (TreeListBox: Check + Info)
2098 if( ButtonDownCheckCtrl(rMEvt, pEntry, nY) )
2099 return;
2100 // Inplace-Editing?
2102 if ( aSelEng.GetSelectionMode() != NO_SELECTION )
2103 aSelEng.SelMouseButtonDown( rMEvt );
2106 void SvImpLBox::MouseButtonUp( const MouseEvent& rMEvt)
2108 if ( !ButtonUpCheckCtrl( rMEvt ) && ( aSelEng.GetSelectionMode() != NO_SELECTION ) )
2109 aSelEng.SelMouseButtonUp( rMEvt );
2110 EndScroll();
2111 if( nFlags & F_START_EDITTIMER )
2113 nFlags &= (~F_START_EDITTIMER);
2114 aEditClickPos = rMEvt.GetPosPixel();
2115 aEditIdle.Start();
2118 return;
2121 void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
2123 SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
2124 if ( !MouseMoveCheckCtrl( rMEvt, pEntry ) && ( aSelEng.GetSelectionMode() != NO_SELECTION ) )
2125 aSelEng.SelMouseMove( rMEvt );
2126 return;
2129 bool SvImpLBox::KeyInput( const KeyEvent& rKEvt)
2131 aEditIdle.Stop();
2132 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
2134 if( rKeyCode.IsMod2() )
2135 return false; // don't evaluate Alt key
2137 nFlags &= (~F_FILLING);
2139 if( !pCursor )
2140 pCursor = pStartEntry;
2141 if( !pCursor )
2142 return false;
2144 bool bKeyUsed = true;
2146 sal_uInt16 nDelta = (sal_uInt16)aVerSBar->GetPageSize();
2147 sal_uInt16 aCode = rKeyCode.GetCode();
2149 bool bShift = rKeyCode.IsShift();
2150 bool bMod1 = rKeyCode.IsMod1();
2152 SvTreeListEntry* pNewCursor;
2154 const WinBits nWindowStyle = pView->GetStyle();
2155 switch( aCode )
2157 case KEY_UP:
2158 if( !IsEntryInView( pCursor ) )
2159 MakeVisible( pCursor );
2161 pNewCursor = pCursor;
2164 pNewCursor = pView->PrevVisible(pNewCursor);
2165 } while( pNewCursor && !IsSelectable(pNewCursor) );
2167 if ( pNewCursor )
2168 // new entry selected -> reset current tab position to first tab
2169 nCurTabPos = FIRST_ENTRY_TAB;
2170 // if there is no next entry, take the current one
2171 // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
2172 // the cursor key
2173 if ( !pNewCursor && pCursor )
2174 pNewCursor = pCursor;
2176 if( pNewCursor )
2178 aSelEng.CursorPosChanging( bShift, bMod1 );
2179 SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
2180 if( !IsEntryInView( pNewCursor ) )
2181 KeyUp( false );
2183 break;
2185 case KEY_DOWN:
2186 if( !IsEntryInView( pCursor ) )
2187 MakeVisible( pCursor );
2189 pNewCursor = pCursor;
2192 pNewCursor = pView->NextVisible(pNewCursor);
2193 } while( pNewCursor && !IsSelectable(pNewCursor) );
2195 if ( pNewCursor )
2196 // new entry selected -> reset current tab position to first tab
2197 nCurTabPos = FIRST_ENTRY_TAB;
2199 // if there is no next entry, take the current one
2200 // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
2201 // the cursor key
2202 // 06.09.20001 - 83416 - frank.schoenheit@sun.com
2203 if ( !pNewCursor && pCursor )
2204 pNewCursor = pCursor;
2206 if( pNewCursor )
2208 aSelEng.CursorPosChanging( bShift, bMod1 );
2209 if( IsEntryInView( pNewCursor ) )
2210 SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
2211 else
2213 if( pCursor )
2214 pView->Select( pCursor, false );
2215 KeyDown( false );
2216 SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
2219 else
2220 KeyDown( false ); // because scrollbar range might still
2221 // allow scrolling
2222 break;
2224 case KEY_RIGHT:
2226 if( bSubLstOpLR )
2228 // only try to expand if sublist is expandable,
2229 // otherwise ignore the key press
2230 if( IsNowExpandable() )
2231 pView->Expand( pCursor );
2233 else if ( bIsCellFocusEnabled && pCursor )
2235 if ( nCurTabPos < ( pView->TabCount() - 1 /*!2*/ ) )
2237 ++nCurTabPos;
2238 ShowCursor( true );
2239 CallEventListeners( VCLEVENT_LISTBOX_SELECT, pCursor );
2242 else if( nWindowStyle & WB_HSCROLL )
2244 long nThumb = aHorSBar->GetThumbPos();
2245 nThumb += aHorSBar->GetLineSize();
2246 long nOldThumb = aHorSBar->GetThumbPos();
2247 aHorSBar->SetThumbPos( nThumb );
2248 nThumb = nOldThumb;
2249 nThumb -= aHorSBar->GetThumbPos();
2250 nThumb *= -1;
2251 if( nThumb )
2253 KeyLeftRight( nThumb );
2254 EndScroll();
2257 else
2258 bKeyUsed = false;
2259 break;
2262 case KEY_LEFT:
2264 if ( bIsCellFocusEnabled && pCursor )
2266 if ( nCurTabPos > FIRST_ENTRY_TAB )
2268 --nCurTabPos;
2269 ShowCursor( true );
2270 CallEventListeners( VCLEVENT_LISTBOX_SELECT, pCursor );
2273 else if ( nWindowStyle & WB_HSCROLL )
2275 long nThumb = aHorSBar->GetThumbPos();
2276 nThumb -= aHorSBar->GetLineSize();
2277 long nOldThumb = aHorSBar->GetThumbPos();
2278 aHorSBar->SetThumbPos( nThumb );
2279 nThumb = nOldThumb;
2280 nThumb -= aHorSBar->GetThumbPos();
2281 if( nThumb )
2283 KeyLeftRight( -nThumb );
2284 EndScroll();
2286 else if( bSubLstOpLR )
2288 if( IsExpandable() && pView->IsExpanded( pCursor ) )
2289 pView->Collapse( pCursor );
2290 else
2292 pNewCursor = pView->GetParent( pCursor );
2293 if( pNewCursor )
2294 SetCursor( pNewCursor );
2298 else if( bSubLstOpLR )
2300 if( IsExpandable() && pView->IsExpanded( pCursor ) )
2301 pView->Collapse( pCursor );
2302 else
2304 pNewCursor = pView->GetParent( pCursor );
2305 if( pNewCursor )
2306 SetCursor( pNewCursor );
2309 else
2310 bKeyUsed = false;
2311 break;
2314 case KEY_PAGEUP:
2315 if( !bMod1 )
2317 pNewCursor = pView->PrevVisible(pCursor, nDelta);
2319 while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
2321 pNewCursor = pView->NextVisible(pNewCursor);
2322 nDelta--;
2325 if( nDelta )
2327 DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
2328 aSelEng.CursorPosChanging( bShift, bMod1 );
2329 if( IsEntryInView( pNewCursor ) )
2330 SetCursor( pNewCursor );
2331 else
2333 SetCursor( pNewCursor );
2334 KeyUp( true );
2338 else
2339 bKeyUsed = false;
2340 break;
2342 case KEY_PAGEDOWN:
2343 if( !bMod1 )
2345 pNewCursor= pView->NextVisible(pCursor, nDelta);
2347 while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
2349 pNewCursor = pView->PrevVisible(pNewCursor);
2350 nDelta--;
2353 if( nDelta && pNewCursor )
2355 DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
2356 aSelEng.CursorPosChanging( bShift, bMod1 );
2357 if( IsEntryInView( pNewCursor ) )
2358 SetCursor( pNewCursor );
2359 else
2361 SetCursor( pNewCursor );
2362 KeyDown( true );
2365 else
2366 KeyDown( false ); // see also: KEY_DOWN
2368 else
2369 bKeyUsed = false;
2370 break;
2372 case KEY_SPACE:
2373 if ( pView->GetSelectionMode() != NO_SELECTION )
2375 if ( bMod1 )
2377 if ( pView->GetSelectionMode() == MULTIPLE_SELECTION && !bShift )
2378 // toggle selection
2379 pView->Select( pCursor, !pView->IsSelected( pCursor ) );
2381 else if ( !bShift /*&& !bMod1*/ )
2383 if ( aSelEng.IsAddMode() )
2385 // toggle selection
2386 pView->Select( pCursor, !pView->IsSelected( pCursor ) );
2388 else if ( !pView->IsSelected( pCursor ) )
2390 SelAllDestrAnch( false );
2391 pView->Select( pCursor, true );
2393 else
2394 bKeyUsed = false;
2396 else
2397 bKeyUsed = false;
2399 else
2400 bKeyUsed = false;
2401 break;
2403 case KEY_RETURN:
2404 if( bSubLstOpRet && IsExpandable() )
2406 if( pView->IsExpanded( pCursor ) )
2407 pView->Collapse( pCursor );
2408 else
2409 pView->Expand( pCursor );
2411 else
2412 bKeyUsed = false;
2413 break;
2415 case KEY_F2:
2416 if( !bShift && !bMod1 )
2418 aEditClickPos = Point( -1, -1 );
2419 EditTimerCall( 0 );
2421 else
2422 bKeyUsed = false;
2423 break;
2425 case KEY_F8:
2426 if( bShift && pView->GetSelectionMode()==MULTIPLE_SELECTION &&
2427 !(m_nStyle & WB_SIMPLEMODE))
2429 if( aSelEng.IsAlwaysAdding() )
2430 aSelEng.AddAlways( false );
2431 else
2432 aSelEng.AddAlways( true );
2434 else
2435 bKeyUsed = false;
2436 break;
2438 case KEY_ADD:
2439 if( pCursor )
2441 if( !pView->IsExpanded(pCursor))
2442 pView->Expand( pCursor );
2443 if( bMod1 )
2445 sal_uInt16 nRefDepth = pTree->GetDepth( pCursor );
2446 SvTreeListEntry* pCur = pTree->Next( pCursor );
2447 while( pCur && pTree->GetDepth(pCur) > nRefDepth )
2449 if( pCur->HasChildren() && !pView->IsExpanded(pCur))
2450 pView->Expand( pCur );
2451 pCur = pTree->Next( pCur );
2455 else
2456 bKeyUsed = false;
2457 break;
2459 case KEY_A:
2460 if( bMod1 )
2461 SelAllDestrAnch( true );
2462 else
2463 bKeyUsed = false;
2464 break;
2466 case KEY_SUBTRACT:
2467 if( pCursor )
2469 if( pView->IsExpanded(pCursor))
2470 pView->Collapse( pCursor );
2471 if( bMod1 )
2473 // collapse all parents until we get to the root
2474 SvTreeListEntry* pParentToCollapse = pTree->GetRootLevelParent(pCursor);
2475 if( pParentToCollapse )
2477 sal_uInt16 nRefDepth;
2478 // special case explorer: if the root only has a single
2479 // entry, don't collapse the root entry
2480 if (pTree->GetChildList(0).size() < 2)
2482 nRefDepth = 1;
2483 pParentToCollapse = pCursor;
2484 while( pTree->GetParent(pParentToCollapse) &&
2485 pTree->GetDepth( pTree->GetParent(pParentToCollapse)) > 0)
2487 pParentToCollapse = pTree->GetParent(pParentToCollapse);
2490 else
2491 nRefDepth = 0;
2493 if( pView->IsExpanded(pParentToCollapse) )
2494 pView->Collapse( pParentToCollapse );
2495 SvTreeListEntry* pCur = pTree->Next( pParentToCollapse );
2496 while( pCur && pTree->GetDepth(pCur) > nRefDepth )
2498 if( pCur->HasChildren() && pView->IsExpanded(pCur) )
2499 pView->Collapse( pCur );
2500 pCur = pTree->Next( pCur );
2505 else
2506 bKeyUsed = false;
2507 break;
2509 case KEY_DIVIDE :
2510 if( bMod1 )
2511 SelAllDestrAnch( true );
2512 else
2513 bKeyUsed = false;
2514 break;
2516 case KEY_COMMA :
2517 if( bMod1 )
2518 SelAllDestrAnch( false );
2519 else
2520 bKeyUsed = false;
2521 break;
2523 case KEY_HOME :
2524 pNewCursor = pView->GetModel()->First();
2526 while( pNewCursor && !IsSelectable(pNewCursor) )
2528 pNewCursor = pView->NextVisible(pNewCursor);
2531 if( pNewCursor && pNewCursor != pCursor )
2533 // SelAllDestrAnch( false );
2534 aSelEng.CursorPosChanging( bShift, bMod1 );
2535 SetCursor( pNewCursor );
2536 if( !IsEntryInView( pNewCursor ) )
2537 MakeVisible( pNewCursor );
2539 else
2540 bKeyUsed = false;
2541 break;
2543 case KEY_END :
2544 pNewCursor = pView->GetModel()->Last();
2546 while( pNewCursor && !IsSelectable(pNewCursor) )
2548 pNewCursor = pView->PrevVisible(pNewCursor);
2551 if( pNewCursor && pNewCursor != pCursor)
2553 // SelAllDestrAnch( false );
2554 aSelEng.CursorPosChanging( bShift, bMod1 );
2555 SetCursor( pNewCursor );
2556 if( !IsEntryInView( pNewCursor ) )
2557 MakeVisible( pNewCursor );
2559 else
2560 bKeyUsed = false;
2561 break;
2563 case KEY_ESCAPE:
2564 case KEY_TAB:
2565 case KEY_DELETE:
2566 case KEY_BACKSPACE:
2567 // must not be handled because this quits dialogs and does other magic things...
2568 // if there are other single keys which should not be handled, they can be added here
2569 bKeyUsed = false;
2570 break;
2572 default:
2573 // is there any reason why we should eat the events here? The only place where this is called
2574 // is from SvTreeListBox::KeyInput. If we set bKeyUsed to true here, then the key input
2575 // is just silenced. However, we want SvLBox::KeyInput to get a chance, to do the QuickSelection
2576 // handling.
2577 // (The old code here which intentionally set bKeyUsed to sal_True said this was because of "quick search"
2578 // handling, but actually there was no quick search handling anymore. We just re-implemented it.)
2579 // #i31275# / 2009-06-16 / frank.schoenheit@sun.com
2580 bKeyUsed = false;
2581 break;
2583 return bKeyUsed;
2586 void SvImpLBox::GetFocus()
2588 if( pCursor )
2590 pView->SetEntryFocus( pCursor, true );
2591 ShowCursor( true );
2592 // auskommentiert wg. deselectall
2593 // if( bSimpleTravel && !pView->IsSelected(pCursor) )
2594 // pView->Select( pCursor, true );
2596 if( m_nStyle & WB_HIDESELECTION )
2598 SvTreeListEntry* pEntry = pView->FirstSelected();
2599 while( pEntry )
2601 InvalidateEntry( pEntry );
2602 pEntry = pView->NextSelected( pEntry );
2607 void SvImpLBox::LoseFocus()
2609 aEditIdle.Stop();
2610 if( pCursor )
2611 pView->SetEntryFocus( pCursor,false );
2612 ShowCursor( false );
2614 if( m_nStyle & WB_HIDESELECTION )
2616 SvTreeListEntry* pEntry = pView ? pView->FirstSelected() : NULL;
2617 while( pEntry )
2619 InvalidateEntry( pEntry );
2620 pEntry = pView->NextSelected( pEntry );
2626 // ********************************************************************
2627 // SelectionEngine
2628 // ********************************************************************
2630 void SvImpLBox::SelectEntry( SvTreeListEntry* pEntry, bool bSelect )
2632 pView->Select( pEntry, bSelect );
2635 ImpLBSelEng::ImpLBSelEng( SvImpLBox* pImpl, SelectionEngine* pSEng,
2636 SvTreeListBox* pV )
2638 pImp = pImpl;
2639 pSelEng = pSEng;
2640 pView = pV;
2643 ImpLBSelEng::~ImpLBSelEng()
2647 void ImpLBSelEng::BeginDrag()
2649 pImp->BeginDrag();
2652 void ImpLBSelEng::CreateAnchor()
2654 pImp->pAnchor = pImp->pCursor;
2657 void ImpLBSelEng::DestroyAnchor()
2659 pImp->pAnchor = 0;
2662 bool ImpLBSelEng::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
2664 SvTreeListEntry* pNewCursor = pImp->MakePointVisible( rPoint );
2665 if( pNewCursor != pImp->pCursor )
2666 pImp->BeginScroll();
2668 if( pNewCursor )
2670 // at SimpleTravel, the SetCursor is selected and the select handler is
2671 // called
2672 //if( !bDontSelectAtCursor && !pImp->bSimpleTravel )
2673 // pImp->SelectEntry( pNewCursor, true );
2674 pImp->SetCursor( pNewCursor, bDontSelectAtCursor );
2675 return true;
2677 return false;
2680 bool ImpLBSelEng::IsSelectionAtPoint( const Point& rPoint )
2682 SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
2683 if( pEntry )
2684 return pView->IsSelected(pEntry);
2685 return false;
2688 void ImpLBSelEng::DeselectAtPoint( const Point& rPoint )
2690 SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
2691 if( !pEntry )
2692 return;
2693 pImp->SelectEntry( pEntry, false );
2696 void ImpLBSelEng::DeselectAll()
2698 pImp->SelAllDestrAnch( false, false ); // don't reset SelectionEngine!
2699 pImp->nFlags &= (~F_DESEL_ALL);
2702 // ***********************************************************************
2703 // Selection
2704 // ***********************************************************************
2706 void SvImpLBox::SetAnchorSelection(SvTreeListEntry* pOldCursor,SvTreeListEntry* pNewCursor)
2708 SvTreeListEntry* pEntry;
2709 sal_uLong nAnchorVisPos = pView->GetVisiblePos( pAnchor );
2710 sal_uLong nOldVisPos = pView->GetVisiblePos( pOldCursor );
2711 sal_uLong nNewVisPos = pView->GetVisiblePos( pNewCursor );
2713 if( nOldVisPos > nAnchorVisPos ||
2714 ( nAnchorVisPos==nOldVisPos && nNewVisPos > nAnchorVisPos) )
2716 if( nNewVisPos > nOldVisPos )
2718 pEntry = pOldCursor;
2719 while( pEntry && pEntry != pNewCursor )
2721 pView->Select( pEntry, true );
2722 pEntry = pView->NextVisible(pEntry);
2724 if( pEntry )
2725 pView->Select( pEntry, true );
2726 return;
2729 if( nNewVisPos < nAnchorVisPos )
2731 pEntry = pAnchor;
2732 while( pEntry && pEntry != pOldCursor )
2734 pView->Select( pEntry, false );
2735 pEntry = pView->NextVisible(pEntry);
2737 if( pEntry )
2738 pView->Select( pEntry, false );
2740 pEntry = pNewCursor;
2741 while( pEntry && pEntry != pAnchor )
2743 pView->Select( pEntry, true );
2744 pEntry = pView->NextVisible(pEntry);
2746 if( pEntry )
2747 pView->Select( pEntry, true );
2748 return;
2751 if( nNewVisPos < nOldVisPos )
2753 pEntry = pNewCursor;
2754 pEntry = pView->NextVisible(pEntry);
2755 while( pEntry && pEntry != pOldCursor )
2757 pView->Select( pEntry, false );
2758 pEntry = pView->NextVisible(pEntry);
2760 if( pEntry )
2761 pView->Select( pEntry, false );
2762 return;
2765 else
2767 if( nNewVisPos < nOldVisPos ) // enlarge selection
2769 pEntry = pNewCursor;
2770 while( pEntry && pEntry != pOldCursor )
2772 pView->Select( pEntry, true );
2773 pEntry = pView->NextVisible(pEntry);
2775 if( pEntry )
2776 pView->Select( pEntry, true );
2777 return;
2780 if( nNewVisPos > nAnchorVisPos )
2782 pEntry = pOldCursor;
2783 while( pEntry && pEntry != pAnchor )
2785 pView->Select( pEntry, false );
2786 pEntry = pView->NextVisible(pEntry);
2788 if( pEntry )
2789 pView->Select( pEntry, false );
2790 pEntry = pAnchor;
2791 while( pEntry && pEntry != pNewCursor )
2793 pView->Select( pEntry, true );
2794 pEntry = pView->NextVisible(pEntry);
2796 if( pEntry )
2797 pView->Select( pEntry, true );
2798 return;
2801 if( nNewVisPos > nOldVisPos )
2803 pEntry = pOldCursor;
2804 while( pEntry && pEntry != pNewCursor )
2806 pView->Select( pEntry, false );
2807 pEntry = pView->NextVisible(pEntry);
2809 return;
2814 void SvImpLBox::SelAllDestrAnch(
2815 bool bSelect, bool bDestroyAnchor, bool bSingleSelToo )
2817 SvTreeListEntry* pEntry;
2818 nFlags &= (~F_DESEL_ALL);
2819 if( bSelect && bSimpleTravel )
2821 if( pCursor && !pView->IsSelected( pCursor ))
2823 pView->Select( pCursor, true );
2825 return;
2827 if( !bSelect && pView->GetSelectionCount() == 0 )
2829 if( bSimpleTravel && ( !GetUpdateMode() || !pCursor) )
2830 nFlags |= F_DESEL_ALL;
2831 return;
2833 if( bSelect && pView->GetSelectionCount() == pView->GetEntryCount())
2834 return;
2835 if( !bSingleSelToo && bSimpleTravel )
2836 return;
2838 if( !bSelect && pView->GetSelectionCount()==1 && pCursor &&
2839 pView->IsSelected( pCursor ))
2841 pView->Select( pCursor, false );
2842 if( bDestroyAnchor )
2843 DestroyAnchor(); // delete anchor & reset SelectionEngine
2844 else
2845 pAnchor = 0; // always delete internal anchor
2846 return;
2849 if( bSimpleTravel && !pCursor && !GetUpdateMode() )
2850 nFlags |= F_DESEL_ALL;
2852 ShowCursor( false );
2853 bool bUpdate = GetUpdateMode();
2855 nFlags |= F_IGNORE_SELECT; // EntryInserted should not do anything
2856 pEntry = pTree->First();
2857 while( pEntry )
2859 if( pView->Select( pEntry, bSelect ) )
2861 if( bUpdate && pView->IsEntryVisible(pEntry) )
2863 long nY = GetEntryLine( pEntry );
2864 if( IsLineVisible( nY ) )
2865 InvalidateEntry(pEntry);
2868 pEntry = pTree->Next( pEntry );
2870 nFlags &= ~F_IGNORE_SELECT;
2872 if( bDestroyAnchor )
2873 DestroyAnchor(); // delete anchor & reset SelectionEngine
2874 else
2875 pAnchor = 0; // always delete internal anchor
2876 ShowCursor( true );
2879 void SvImpLBox::SetSelectionMode( SelectionMode eSelMode )
2881 aSelEng.SetSelectionMode( eSelMode);
2882 if( eSelMode == SINGLE_SELECTION )
2883 bSimpleTravel = true;
2884 else
2885 bSimpleTravel = false;
2886 if( (m_nStyle & WB_SIMPLEMODE) && (eSelMode == MULTIPLE_SELECTION) )
2887 aSelEng.AddAlways( true );
2890 // ***********************************************************************
2891 // Drag & Drop
2892 // ***********************************************************************
2894 void SvImpLBox::SetDragDropMode( DragDropMode eDDMode )
2896 if( eDDMode != DragDropMode::NONE && eDDMode != DragDropMode::APP_DROP )
2898 aSelEng.ExpandSelectionOnMouseMove( false );
2899 aSelEng.EnableDrag( true );
2901 else
2903 aSelEng.ExpandSelectionOnMouseMove( true );
2904 aSelEng.EnableDrag( false );
2908 void SvImpLBox::BeginDrag()
2910 nFlags &= (~F_FILLING);
2911 if( !bAsyncBeginDrag )
2913 BeginScroll();
2914 pView->StartDrag( 0, aSelEng.GetMousePosPixel() );
2915 EndScroll();
2917 else
2919 aAsyncBeginDragPos = aSelEng.GetMousePosPixel();
2920 aAsyncBeginDragIdle.Start();
2924 IMPL_LINK_NOARG_TYPED(SvImpLBox, BeginDragHdl, Idle *, void)
2926 pView->StartDrag( 0, aAsyncBeginDragPos );
2929 void SvImpLBox::PaintDDCursor( SvTreeListEntry* pInsertionPos )
2931 long nY;
2932 if( pInsertionPos )
2934 nY = GetEntryLine( pInsertionPos );
2935 nY += pView->GetEntryHeight();
2937 else
2938 nY = 1;
2939 RasterOp eOldOp = pView->GetRasterOp();
2940 pView->SetRasterOp( ROP_INVERT );
2941 Color aOldLineColor = pView->GetLineColor();
2942 pView->SetLineColor( Color( COL_BLACK ) );
2943 pView->DrawLine( Point( 0, nY ), Point( aOutputSize.Width(), nY ) );
2944 pView->SetLineColor( aOldLineColor );
2945 pView->SetRasterOp( eOldOp );
2948 // Delete all submenus of a PopupMenu, recursively
2949 static void lcl_DeleteSubPopups(PopupMenu* pPopup)
2951 for(sal_uInt16 i = 0; i < pPopup->GetItemCount(); i++)
2953 PopupMenu* pSubPopup = pPopup->GetPopupMenu( pPopup->GetItemId( i ));
2954 if(pSubPopup)
2956 lcl_DeleteSubPopups(pSubPopup);
2957 delete pSubPopup;
2962 void SvImpLBox::Command( const CommandEvent& rCEvt )
2964 CommandEventId nCommand = rCEvt.GetCommand();
2966 if( nCommand == CommandEventId::ContextMenu )
2967 aEditIdle.Stop();
2969 // scroll mouse event?
2970 if( ( ( nCommand == CommandEventId::Wheel ) || ( nCommand == CommandEventId::StartAutoScroll ) || ( nCommand == CommandEventId::AutoScroll ) )
2971 && pView->HandleScrollCommand( rCEvt, aHorSBar.get(), aVerSBar.get() ) )
2972 return;
2974 if( bContextMenuHandling && nCommand == CommandEventId::ContextMenu )
2976 Point aPopupPos;
2977 bool bClickedIsFreePlace = false;
2978 std::stack<SvTreeListEntry*> aSelRestore;
2980 if( rCEvt.IsMouseEvent() )
2981 { // change selection, if mouse position doesn't fit to selection
2983 aPopupPos = rCEvt.GetMousePosPixel();
2985 SvTreeListEntry* pClickedEntry = GetEntry( aPopupPos );
2986 if( pClickedEntry )
2987 { // mouse in non empty area
2988 bool bClickedIsSelected = false;
2990 // collect the currently selected entries
2991 SvTreeListEntry* pSelected = pView->FirstSelected();
2992 while( pSelected )
2994 bClickedIsSelected |= ( pClickedEntry == pSelected );
2995 pSelected = pView->NextSelected( pSelected );
2998 // if the entry which the user clicked at is not selected
2999 if( !bClickedIsSelected )
3000 { // deselect all other and select the clicked one
3001 pView->SelectAll( false );
3002 pView->SetCursor( pClickedEntry );
3005 else if( aSelEng.GetSelectionMode() == SINGLE_SELECTION )
3007 bClickedIsFreePlace = true;
3008 sal_Int32 nSelectedEntries = pView->GetSelectionCount();
3009 SvTreeListEntry* pSelected = pView->FirstSelected();
3010 for(sal_uInt16 nSel = 0; nSel < nSelectedEntries; nSel++ )
3012 aSelRestore.push(pSelected);
3013 pSelected = pView->NextSelected( pSelected );
3015 pView->SelectAll( false );
3017 else
3018 { // deselect all
3019 pView->SelectAll( false );
3024 else
3025 { // key event (or at least no mouse event)
3026 sal_Int32 nSelectionCount = pView->GetSelectionCount();
3028 if( nSelectionCount )
3029 { // now always take first visible as base for positioning the menu
3030 SvTreeListEntry* pSelected = pView->FirstSelected();
3031 while( pSelected )
3033 if( IsEntryInView( pSelected ) )
3034 break;
3036 pSelected = pView->NextSelected( pSelected );
3039 if( !pSelected )
3041 // no one was visible
3042 pSelected = pView->FirstSelected();
3043 pView->MakeVisible( pSelected );
3046 aPopupPos = pView->GetFocusRect( pSelected, pView->GetEntryPosition( pSelected ).Y() ).Center();
3048 else
3049 aPopupPos = Point( 0, 0 );
3052 PopupMenu* pPopup = pView->CreateContextMenu();
3054 if( pPopup )
3056 // do action for selected entry in popup menu
3057 sal_uInt16 nMenuAction = pPopup->Execute( pView, aPopupPos );
3058 if ( nMenuAction )
3059 pView->ExcecuteContextMenuAction( nMenuAction );
3060 lcl_DeleteSubPopups(pPopup);
3061 delete pPopup;
3064 if( bClickedIsFreePlace )
3066 while(!aSelRestore.empty())
3068 SvTreeListEntry* pEntry = aSelRestore.top();
3069 //#i19717# the entry is maybe already deleted
3070 bool bFound = false;
3071 for(sal_uLong nEntry = 0; nEntry < pView->GetEntryCount(); nEntry++)
3072 if(pEntry == pView->GetEntry(nEntry))
3074 bFound = true;
3075 break;
3077 if(bFound)
3078 SetCurEntry( pEntry );
3079 aSelRestore.pop();
3083 #ifndef NOCOMMAND
3084 else
3086 const Point& rPos = rCEvt.GetMousePosPixel();
3087 if( rPos.X() < aOutputSize.Width() && rPos.Y() < aOutputSize.Height() )
3088 aSelEng.Command( rCEvt );
3090 #endif
3093 void SvImpLBox::BeginScroll()
3095 if( !(nFlags & F_IN_SCROLLING))
3097 nFlags |= F_IN_SCROLLING;
3101 void SvImpLBox::EndScroll()
3103 if( nFlags & F_IN_SCROLLING)
3105 pView->NotifyEndScroll();
3106 nFlags &= (~F_IN_SCROLLING);
3111 Rectangle SvImpLBox::GetVisibleArea() const
3113 Point aPos( pView->GetMapMode().GetOrigin() );
3114 aPos.X() *= -1;
3115 Rectangle aRect( aPos, aOutputSize );
3116 return aRect;
3119 void SvImpLBox::Invalidate()
3121 pView->SetClipRegion();
3124 void SvImpLBox::SetCurEntry( SvTreeListEntry* pEntry )
3126 if ( ( aSelEng.GetSelectionMode() != SINGLE_SELECTION )
3127 && ( aSelEng.GetSelectionMode() != NO_SELECTION )
3129 SelAllDestrAnch( false, true, false );
3130 if ( pEntry )
3131 MakeVisible( pEntry );
3132 SetCursor( pEntry );
3133 if ( pEntry && ( aSelEng.GetSelectionMode() != NO_SELECTION ) )
3134 pView->Select( pEntry, true );
3137 IMPL_LINK_NOARG_TYPED(SvImpLBox, EditTimerCall, Idle *, void)
3139 if( pView->IsInplaceEditingEnabled() )
3141 bool bIsMouseTriggered = aEditClickPos.X() >= 0;
3142 if ( bIsMouseTriggered )
3144 Point aCurrentMousePos = pView->GetPointerPosPixel();
3145 if ( ( std::abs( aCurrentMousePos.X() - aEditClickPos.X() ) > 5 )
3146 || ( std::abs( aCurrentMousePos.Y() - aEditClickPos.Y() ) > 5 )
3149 return;
3153 SvTreeListEntry* pEntry = GetCurEntry();
3154 if( pEntry )
3156 ShowCursor( false );
3157 pView->ImplEditEntry( pEntry );
3158 ShowCursor( true );
3163 bool SvImpLBox::RequestHelp( const HelpEvent& rHEvt )
3165 if( rHEvt.GetMode() & HelpEventMode::QUICK )
3167 Point aPos( pView->ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
3168 if( !GetVisibleArea().IsInside( aPos ))
3169 return false;
3171 SvTreeListEntry* pEntry = GetEntry( aPos );
3172 if( pEntry )
3174 // recalculate text rectangle
3175 SvLBoxTab* pTab;
3176 SvLBoxItem* pItem = pView->GetItem( pEntry, aPos.X(), &pTab );
3177 if (!pItem || pItem->GetType() != SV_ITEM_ID_LBOXSTRING)
3178 return false;
3180 aPos = GetEntryPosition( pEntry );
3181 aPos.X() = pView->GetTabPos( pEntry, pTab ); //pTab->GetPos();
3182 Size aSize( pItem->GetSize( pView, pEntry ) );
3183 SvLBoxTab* pNextTab = NextTab( pTab );
3184 bool bItemClipped = false;
3185 // is the item cut off by its right neighbor?
3186 if( pNextTab && pView->GetTabPos(pEntry,pNextTab) < aPos.X()+aSize.Width() )
3188 aSize.Width() = pNextTab->GetPos() - pTab->GetPos();
3189 bItemClipped = true;
3191 Rectangle aItemRect( aPos, aSize );
3193 Rectangle aViewRect( GetVisibleArea() );
3195 if( bItemClipped || !aViewRect.IsInside( aItemRect ) )
3197 // clip the right edge of the item at the edge of the view
3198 //if( aItemRect.Right() > aViewRect.Right() )
3199 // aItemRect.Right() = aViewRect.Right();
3201 Point aPt = pView->OutputToScreenPixel( aItemRect.TopLeft() );
3202 aItemRect.Left() = aPt.X();
3203 aItemRect.Top() = aPt.Y();
3204 aPt = pView->OutputToScreenPixel( aItemRect.BottomRight() );
3205 aItemRect.Right() = aPt.X();
3206 aItemRect.Bottom() = aPt.Y();
3208 Help::ShowQuickHelp( pView, aItemRect,
3209 static_cast<SvLBoxString*>(pItem)->GetText(), QuickHelpFlags::Left | QuickHelpFlags::VCenter );
3210 return true;
3214 return false;
3217 SvLBoxTab* SvImpLBox::NextTab( SvLBoxTab* pTab )
3219 sal_uInt16 nTabCount = pView->TabCount();
3220 if( nTabCount <= 1 )
3221 return 0;
3222 for( sal_uInt16 nTab=0; nTab < (nTabCount-1); nTab++)
3224 if( pView->aTabs[nTab]==pTab )
3225 return (SvLBoxTab*)(pView->aTabs[nTab+1]);
3227 return 0;
3230 void SvImpLBox::EndSelection()
3232 DestroyAnchor();
3233 nFlags &= ~F_START_EDITTIMER;
3236 void SvImpLBox::SetUpdateMode( bool bMode )
3238 if( bUpdateMode != bMode )
3240 bUpdateMode = bMode;
3241 if( bUpdateMode )
3242 UpdateAll( false );
3246 bool SvImpLBox::SetMostRight( SvTreeListEntry* pEntry )
3248 if( pView->nTreeFlags & SvTreeFlags::RECALCTABS )
3250 nFlags |= F_IGNORE_CHANGED_TABS;
3251 pView->SetTabs();
3252 nFlags &= ~F_IGNORE_CHANGED_TABS;
3255 sal_uInt16 nLastTab = pView->aTabs.size() - 1;
3256 sal_uInt16 nLastItem = pEntry->ItemCount() - 1;
3257 if( !pView->aTabs.empty() && nLastItem != USHRT_MAX )
3259 if( nLastItem < nLastTab )
3260 nLastTab = nLastItem;
3262 SvLBoxTab* pTab = pView->aTabs[ nLastTab ];
3263 SvLBoxItem* pItem = pEntry->GetItem( nLastTab );
3265 long nTabPos = pView->GetTabPos( pEntry, pTab );
3267 long nMaxRight = GetOutputSize().Width();
3268 Point aPos( pView->GetMapMode().GetOrigin() );
3269 aPos.X() *= -1; // conversion document coordinates
3270 nMaxRight = nMaxRight + aPos.X() - 1;
3272 long nNextTab = nTabPos < nMaxRight ? nMaxRight : nMaxRight + 50;
3273 long nTabWidth = nNextTab - nTabPos + 1;
3274 long nItemSize = pItem->GetSize(pView,pEntry).Width();
3275 long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );
3277 long nRight = nTabPos + nOffset + nItemSize;
3278 if( nRight > nMostRight )
3280 nMostRight = nRight;
3281 pMostRightEntry = pEntry;
3282 return true;
3285 return false;
3288 void SvImpLBox::FindMostRight( SvTreeListEntry* pEntryToIgnore )
3290 nMostRight = -1;
3291 pMostRightEntry = 0;
3292 if( !pView->GetModel() )
3293 return;
3295 SvTreeListEntry* pEntry = pView->FirstVisible();
3296 while( pEntry )
3298 if( pEntry != pEntryToIgnore )
3299 SetMostRight( pEntry );
3300 pEntry = pView->NextVisible( pEntry );
3304 void SvImpLBox::FindMostRight( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
3306 if( !pParent )
3307 FindMostRight( pEntryToIgnore );
3308 else
3309 FindMostRight_Impl( pParent, pEntryToIgnore );
3312 void SvImpLBox::FindMostRight_Impl( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
3314 SvTreeListEntries& rList = pTree->GetChildList( pParent );
3316 size_t nCount = rList.size();
3317 for( size_t nCur = 0; nCur < nCount; nCur++ )
3319 SvTreeListEntry* pChild = &rList[nCur];
3320 if( pChild != pEntryToIgnore )
3322 SetMostRight( pChild );
3323 if( pChild->HasChildren() && pView->IsExpanded( pChild ))
3324 FindMostRight_Impl( pChild, pEntryToIgnore );
3329 void SvImpLBox::NotifyTabsChanged()
3331 if( GetUpdateMode() && !(nFlags & F_IGNORE_CHANGED_TABS ) &&
3332 nCurUserEvent == 0 )
3334 nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpLBox,MyUserEvent),(void*)0);
3338 bool SvImpLBox::IsExpandable() const
3340 return pCursor->HasChildren() || pCursor->HasChildrenOnDemand();
3343 bool SvImpLBox::IsNowExpandable() const
3345 return IsExpandable() && !pView->IsExpanded( pCursor );
3348 IMPL_LINK(SvImpLBox, MyUserEvent, void*, pArg )
3350 nCurUserEvent = 0;
3351 if( !pArg )
3353 pView->Invalidate();
3354 pView->Update();
3356 else
3358 FindMostRight( 0 );
3359 ShowVerSBar();
3360 pView->Invalidate( GetVisibleArea() );
3362 return 0;
3366 void SvImpLBox::StopUserEvent()
3368 if( nCurUserEvent != 0 )
3370 Application::RemoveUserEvent( nCurUserEvent );
3371 nCurUserEvent = 0;
3375 void SvImpLBox::ShowFocusRect( const SvTreeListEntry* pEntry )
3377 if( pEntry )
3379 long nY = GetEntryLine( const_cast<SvTreeListEntry*>(pEntry) );
3380 Rectangle aRect = pView->GetFocusRect( const_cast<SvTreeListEntry*>(pEntry), nY );
3381 vcl::Region aOldClip( pView->GetClipRegion());
3382 vcl::Region aClipRegion( GetClipRegionRect() );
3383 pView->SetClipRegion( aClipRegion );
3384 pView->ShowFocus( aRect );
3385 pView->SetClipRegion( aOldClip );
3388 else
3390 pView->HideFocus();
3395 void SvImpLBox::implInitDefaultNodeImages()
3397 if ( s_pDefCollapsed )
3398 // assume that all or nothing is initialized
3399 return;
3401 s_pDefCollapsed = new Image( SvtResId( RID_IMG_TREENODE_COLLAPSED ) );
3402 s_pDefExpanded = new Image( SvtResId( RID_IMG_TREENODE_EXPANDED ) );
3406 const Image& SvImpLBox::GetDefaultExpandedNodeImage( )
3408 implInitDefaultNodeImages();
3409 return *s_pDefExpanded;
3413 const Image& SvImpLBox::GetDefaultCollapsedNodeImage( )
3415 implInitDefaultNodeImages();
3416 return *s_pDefCollapsed;
3420 void SvImpLBox::CallEventListeners( sal_uLong nEvent, void* pData )
3422 if ( pView )
3423 pView->CallImplEventListeners( nEvent, pData);
3428 bool SvImpLBox::SetCurrentTabPos( sal_uInt16 _nNewPos )
3430 bool bRet = false;
3432 if ( pView && _nNewPos < ( pView->TabCount() - 2 ) )
3434 nCurTabPos = _nNewPos;
3435 ShowCursor( true );
3436 bRet = true;
3439 return bRet;
3444 bool SvImpLBox::IsSelectable( const SvTreeListEntry* pEntry )
3446 if( pEntry )
3448 SvViewDataEntry* pViewDataNewCur = pView->GetViewDataEntry(const_cast<SvTreeListEntry*>(pEntry));
3449 return (pViewDataNewCur == 0) || pViewDataNewCur->IsSelectable();
3451 else
3453 return false;
3457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */