1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 /*
21     TODO:
22         - delete anchor in SelectionEngine when selecting manually
23         - SelectAll( false ) => only repaint the deselected entries
24 */
25 
26 #include <vcl/treelistbox.hxx>
27 #include <vcl/accessiblefactory.hxx>
28 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
29 #include <vcl/svapp.hxx>
30 #include <vcl/accel.hxx>
31 #include <vcl/settings.hxx>
32 #include <vcl/commandevent.hxx>
33 #include <vcl/uitest/uiobject.hxx>
34 #include <sot/formats.hxx>
35 #include <unotools/accessiblestatesethelper.hxx>
36 #include <rtl/instance.hxx>
37 #include <comphelper/string.hxx>
38 #include <sal/log.hxx>
39 #include <tools/debug.hxx>
40 
41 #include <vcl/svlbitm.hxx>
42 #include <vcl/treelistentry.hxx>
43 #include <vcl/viewdataentry.hxx>
44 #include <svimpbox.hxx>
45 
46 #include <set>
47 #include <string.h>
48 #include <vector>
49 
50 using namespace css::accessibility;
51 
52 // Drag&Drop
53 static VclPtr<SvTreeListBox> g_pDDSource;
54 static VclPtr<SvTreeListBox> g_pDDTarget;
55 
56 #define SVLBOX_ACC_RETURN 1
57 #define SVLBOX_ACC_ESCAPE 2
58 
59 // ***************************************************************
60 
61 class MyEdit_Impl : public Edit
62 {
63     SvInplaceEdit2* pOwner;
64 public:
65                  MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* pOwner );
~MyEdit_Impl()66     virtual     ~MyEdit_Impl() override { disposeOnce(); }
dispose()67     virtual void dispose() override { pOwner = nullptr; Edit::dispose(); }
68     virtual void KeyInput( const KeyEvent& rKEvt ) override;
69     virtual void LoseFocus() override;
70 };
71 
MyEdit_Impl(vcl::Window * pParent,SvInplaceEdit2 * _pOwner)72 MyEdit_Impl::MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* _pOwner ) :
73 
74     Edit( pParent, WB_LEFT ),
75 
76     pOwner( _pOwner )
77 
78 {
79 }
80 
KeyInput(const KeyEvent & rKEvt)81 void MyEdit_Impl::KeyInput( const KeyEvent& rKEvt )
82 {
83     if( !pOwner->KeyInput( rKEvt ))
84         Edit::KeyInput( rKEvt );
85 }
86 
LoseFocus()87 void MyEdit_Impl::LoseFocus()
88 {
89     if (pOwner)
90         pOwner->LoseFocus();
91 }
92 
SvInplaceEdit2(vcl::Window * pParent,const Point & rPos,const Size & rSize,const OUString & rData,const Link<SvInplaceEdit2 &,void> & rNotifyEditEnd,const Selection & rSelection)93 SvInplaceEdit2::SvInplaceEdit2
94 (
95     vcl::Window* pParent, const Point& rPos,
96     const Size& rSize,
97     const OUString& rData,
98     const Link<SvInplaceEdit2&,void>& rNotifyEditEnd,
99     const Selection& rSelection
100 ) :
101 
102     aCallBackHdl       ( rNotifyEditEnd ),
103     bCanceled           ( false ),
104     bAlreadyInCallBack  ( false )
105 
106 {
107 
108     pEdit = VclPtr<MyEdit_Impl>::Create( pParent, this );
109 
110     vcl::Font aFont( pParent->GetFont() );
111     aFont.SetTransparent( false );
112     Color aColor( pParent->GetBackground().GetColor() );
113     aFont.SetFillColor(aColor );
114     pEdit->SetFont( aFont );
115     pEdit->SetBackground( pParent->GetBackground() );
116     pEdit->SetPosPixel( rPos );
117     pEdit->SetSizePixel( rSize );
118     pEdit->SetText( rData );
119     pEdit->SetSelection( rSelection );
120     pEdit->SaveValue();
121 
122     aAccReturn.InsertItem( SVLBOX_ACC_RETURN, vcl::KeyCode(KEY_RETURN) );
123     aAccEscape.InsertItem( SVLBOX_ACC_ESCAPE, vcl::KeyCode(KEY_ESCAPE) );
124 
125     aAccReturn.SetActivateHdl( LINK( this, SvInplaceEdit2, ReturnHdl_Impl) );
126     aAccEscape.SetActivateHdl( LINK( this, SvInplaceEdit2, EscapeHdl_Impl) );
127     Application::InsertAccel( &aAccReturn );
128     Application::InsertAccel( &aAccEscape );
129 
130     pEdit->Show();
131     pEdit->GrabFocus();
132 }
133 
~SvInplaceEdit2()134 SvInplaceEdit2::~SvInplaceEdit2()
135 {
136     if( !bAlreadyInCallBack )
137     {
138         Application::RemoveAccel( &aAccReturn );
139         Application::RemoveAccel( &aAccEscape );
140     }
141     pEdit.disposeAndClear();
142 }
143 
GetSavedValue() const144 OUString const & SvInplaceEdit2::GetSavedValue() const
145 {
146     return pEdit->GetSavedValue();
147 }
148 
Hide()149 void SvInplaceEdit2::Hide()
150 {
151     pEdit->Hide();
152 }
153 
154 
IMPL_LINK_NOARG(SvInplaceEdit2,ReturnHdl_Impl,Accelerator &,void)155 IMPL_LINK_NOARG(SvInplaceEdit2, ReturnHdl_Impl, Accelerator&, void)
156 {
157     bCanceled = false;
158     CallCallBackHdl_Impl();
159 }
160 
IMPL_LINK_NOARG(SvInplaceEdit2,EscapeHdl_Impl,Accelerator &,void)161 IMPL_LINK_NOARG(SvInplaceEdit2, EscapeHdl_Impl, Accelerator&, void)
162 {
163     bCanceled = true;
164     CallCallBackHdl_Impl();
165 }
166 
KeyInput(const KeyEvent & rKEvt)167 bool SvInplaceEdit2::KeyInput( const KeyEvent& rKEvt )
168 {
169     vcl::KeyCode aCode = rKEvt.GetKeyCode();
170     sal_uInt16 nCode = aCode.GetCode();
171 
172     switch ( nCode )
173     {
174         case KEY_ESCAPE:
175             bCanceled = true;
176             CallCallBackHdl_Impl();
177             return true;
178 
179         case KEY_RETURN:
180             bCanceled = false;
181             CallCallBackHdl_Impl();
182             return true;
183     }
184     return false;
185 }
186 
StopEditing(bool bCancel)187 void SvInplaceEdit2::StopEditing( bool bCancel )
188 {
189     if ( !bAlreadyInCallBack )
190     {
191         bCanceled = bCancel;
192         CallCallBackHdl_Impl();
193     }
194 }
195 
LoseFocus()196 void SvInplaceEdit2::LoseFocus()
197 {
198     if ( !bAlreadyInCallBack
199     && ((!Application::GetFocusWindow()) || !pEdit->IsChild( Application::GetFocusWindow()) )
200     )
201     {
202         bCanceled = false;
203         aIdle.SetPriority(TaskPriority::REPAINT);
204         aIdle.SetInvokeHandler(LINK(this,SvInplaceEdit2,Timeout_Impl));
205         aIdle.SetDebugName( "svtools::SvInplaceEdit2 aIdle" );
206         aIdle.Start();
207     }
208 }
209 
IMPL_LINK_NOARG(SvInplaceEdit2,Timeout_Impl,Timer *,void)210 IMPL_LINK_NOARG(SvInplaceEdit2, Timeout_Impl, Timer *, void)
211 {
212     CallCallBackHdl_Impl();
213 }
214 
CallCallBackHdl_Impl()215 void SvInplaceEdit2::CallCallBackHdl_Impl()
216 {
217     aIdle.Stop();
218     if ( !bAlreadyInCallBack )
219     {
220         bAlreadyInCallBack = true;
221         Application::RemoveAccel( &aAccReturn );
222         Application::RemoveAccel( &aAccEscape );
223         pEdit->Hide();
224         aCallBackHdl.Call( *this );
225     }
226 }
227 
GetText() const228 OUString SvInplaceEdit2::GetText() const
229 {
230     return pEdit->GetText();
231 }
232 
233 // ***************************************************************
234 // class SvLBoxTab
235 // ***************************************************************
236 
237 
SvLBoxTab()238 SvLBoxTab::SvLBoxTab()
239 {
240     nPos = 0;
241     nFlags = SvLBoxTabFlags::NONE;
242 }
243 
SvLBoxTab(long nPosition,SvLBoxTabFlags nTabFlags)244 SvLBoxTab::SvLBoxTab( long nPosition, SvLBoxTabFlags nTabFlags )
245 {
246     nPos = nPosition;
247     nFlags = nTabFlags;
248 }
249 
SvLBoxTab(const SvLBoxTab & rTab)250 SvLBoxTab::SvLBoxTab( const SvLBoxTab& rTab )
251 {
252     nPos = rTab.nPos;
253     nFlags = rTab.nFlags;
254 }
255 
~SvLBoxTab()256 SvLBoxTab::~SvLBoxTab()
257 {
258 }
259 
260 
CalcOffset(long nItemWidth,long nTabWidth)261 long SvLBoxTab::CalcOffset( long nItemWidth, long nTabWidth )
262 {
263     long nOffset = 0;
264     if ( nFlags & SvLBoxTabFlags::ADJUST_RIGHT )
265     {
266         nOffset = nTabWidth - nItemWidth;
267         if( nOffset < 0 )
268             nOffset = 0;
269     }
270     else if ( nFlags & SvLBoxTabFlags::ADJUST_CENTER )
271     {
272         if( nFlags & SvLBoxTabFlags::FORCE )
273         {
274             // correct implementation of centering
275             nOffset = ( nTabWidth - nItemWidth ) / 2;
276             if( nOffset < 0 )
277                 nOffset = 0;
278         }
279         else
280         {
281             // historically grown, wrong calculation of tabs which is needed by
282             // Abo-Tabbox, Tools/Options/Customize etc.
283             nItemWidth++;
284             nOffset = -( nItemWidth / 2 );
285         }
286     }
287     return nOffset;
288 }
289 
290 // ***************************************************************
291 // class SvLBoxItem
292 // ***************************************************************
293 
294 
SvLBoxItem()295 SvLBoxItem::SvLBoxItem()
296     : mbDisabled(false)
297 {
298 }
299 
~SvLBoxItem()300 SvLBoxItem::~SvLBoxItem()
301 {
302 }
303 
GetWidth(const SvTreeListBox * pView,const SvTreeListEntry * pEntry) const304 int SvLBoxItem::GetWidth(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const
305 {
306     const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this );
307     int nWidth = pViewData->mnWidth;
308     if (nWidth == -1)
309     {
310         nWidth = CalcWidth(pView);
311         const_cast<SvViewDataItem*>(pViewData)->mnWidth = nWidth;
312     }
313     return nWidth;
314 }
315 
GetHeight(const SvTreeListBox * pView,const SvTreeListEntry * pEntry) const316 int SvLBoxItem::GetHeight(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const
317 {
318     const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this );
319     return pViewData->mnHeight;
320 }
321 
GetWidth(const SvTreeListBox * pView,const SvViewDataEntry * pData,sal_uInt16 nItemPos)322 int SvLBoxItem::GetWidth(const SvTreeListBox* pView, const SvViewDataEntry* pData, sal_uInt16 nItemPos)
323 {
324     const SvViewDataItem& rIData = pData->GetItem(nItemPos);
325     int nWidth = rIData.mnWidth;
326     if (nWidth == -1)
327     {
328         nWidth = CalcWidth(pView);
329         const_cast<SvViewDataItem&>(rIData).mnWidth = nWidth;
330     }
331     return nWidth;
332 }
333 
GetHeight(const SvViewDataEntry * pData,sal_uInt16 nItemPos)334 int SvLBoxItem::GetHeight(const SvViewDataEntry* pData, sal_uInt16 nItemPos)
335 {
336     const SvViewDataItem& rIData = pData->GetItem(nItemPos);
337     return rIData.mnHeight;
338 }
339 
CalcWidth(const SvTreeListBox *) const340 int SvLBoxItem::CalcWidth(const SvTreeListBox* /*pView*/) const
341 {
342     return 0;
343 }
344 
345 struct SvTreeListBoxImpl
346 {
347     bool m_bIsEmptyTextAllowed:1;
348     bool m_bEntryMnemonicsEnabled:1;
349     bool m_bDoingQuickSelection:1;
350 
351     vcl::MnemonicEngine m_aMnemonicEngine;
352     vcl::QuickSelectionEngine m_aQuickSelectionEngine;
353 
SvTreeListBoxImplSvTreeListBoxImpl354     explicit SvTreeListBoxImpl(SvTreeListBox& _rBox) :
355         m_bIsEmptyTextAllowed(true),
356         m_bEntryMnemonicsEnabled(false),
357         m_bDoingQuickSelection(false),
358         m_aMnemonicEngine(_rBox),
359         m_aQuickSelectionEngine(_rBox) {}
360 };
361 
362 
SvTreeListBox(vcl::Window * pParent,WinBits nWinStyle)363 SvTreeListBox::SvTreeListBox(vcl::Window* pParent, WinBits nWinStyle) :
364     Control(pParent, nWinStyle | WB_CLIPCHILDREN),
365     DropTargetHelper(this),
366     DragSourceHelper(this),
367     mpImpl(new SvTreeListBoxImpl(*this)),
368     mbContextBmpExpanded(false),
369     mbAlternatingRowColors(false),
370     mbUpdateAlternatingRows(false),
371     mbQuickSearch(false),
372     eSelMode(SelectionMode::NONE),
373     nMinWidthInChars(0),
374     mbCenterAndClipText(false)
375 {
376     nImpFlags = SvTreeListBoxFlags::NONE;
377     pTargetEntry = nullptr;
378     nDragDropMode = DragDropMode::NONE;
379     pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
380     pHdlEntry = nullptr;
381     eSelMode = SelectionMode::Single;
382     nDragDropMode = DragDropMode::NONE;
383     SetType(WindowType::TREELISTBOX);
384 
385     InitTreeView();
386     pImpl->SetModel( pModel.get() );
387 
388     SetSublistOpenWithLeftRight();
389 }
390 
Clear()391 void SvTreeListBox::Clear()
392 {
393     if (pModel)
394         pModel->Clear();  // Model calls SvTreeListBox::ModelHasCleared()
395 }
396 
EnableEntryMnemonics()397 void SvTreeListBox::EnableEntryMnemonics()
398 {
399     if ( IsEntryMnemonicsEnabled() )
400         return;
401 
402     mpImpl->m_bEntryMnemonicsEnabled = true;
403     Invalidate();
404 }
405 
IsEntryMnemonicsEnabled() const406 bool SvTreeListBox::IsEntryMnemonicsEnabled() const
407 {
408     return mpImpl->m_bEntryMnemonicsEnabled;
409 }
410 
IMPL_LINK(SvTreeListBox,CloneHdl_Impl,SvTreeListEntry *,pEntry,SvTreeListEntry *)411 IMPL_LINK( SvTreeListBox, CloneHdl_Impl, SvTreeListEntry*, pEntry, SvTreeListEntry* )
412 {
413     return CloneEntry(pEntry);
414 }
415 
Insert(SvTreeListEntry * pEntry,SvTreeListEntry * pParent,sal_uLong nPos)416 sal_uLong SvTreeListBox::Insert( SvTreeListEntry* pEntry, SvTreeListEntry* pParent, sal_uLong nPos )
417 {
418     sal_uLong nInsPos = pModel->Insert( pEntry, pParent, nPos );
419     pEntry->SetBackColor( GetBackground().GetColor() );
420     SetAlternatingRowColors( mbAlternatingRowColors );
421     return nInsPos;
422 }
423 
Insert(SvTreeListEntry * pEntry,sal_uLong nRootPos)424 sal_uLong SvTreeListBox::Insert( SvTreeListEntry* pEntry,sal_uLong nRootPos )
425 {
426     sal_uLong nInsPos = pModel->Insert( pEntry, nRootPos );
427     pEntry->SetBackColor( GetBackground().GetColor() );
428     SetAlternatingRowColors( mbAlternatingRowColors );
429     return nInsPos;
430 }
431 
ExpandingHdl()432 bool SvTreeListBox::ExpandingHdl()
433 {
434     return !aExpandingHdl.IsSet() || aExpandingHdl.Call( this );
435 }
436 
ExpandedHdl()437 void SvTreeListBox::ExpandedHdl()
438 {
439     aExpandedHdl.Call( this );
440 }
441 
SelectHdl()442 void SvTreeListBox::SelectHdl()
443 {
444     aSelectHdl.Call( this );
445 }
446 
DeselectHdl()447 void SvTreeListBox::DeselectHdl()
448 {
449     aDeselectHdl.Call( this );
450 }
451 
DoubleClickHdl()452 bool SvTreeListBox::DoubleClickHdl()
453 {
454     return !aDoubleClickHdl.IsSet() || aDoubleClickHdl.Call(this);
455 }
456 
457 
CheckDragAndDropMode(SvTreeListBox const * pSource,sal_Int8 nAction)458 bool SvTreeListBox::CheckDragAndDropMode( SvTreeListBox const * pSource, sal_Int8 nAction )
459 {
460     if ( pSource == this )
461     {
462         if ( !(nDragDropMode & (DragDropMode::CTRL_MOVE | DragDropMode::CTRL_COPY) ) )
463             return false; // D&D locked within list
464         if( DND_ACTION_MOVE == nAction )
465         {
466             if ( !(nDragDropMode & DragDropMode::CTRL_MOVE) )
467                  return false; // no local move
468         }
469         else
470         {
471             if ( !(nDragDropMode & DragDropMode::CTRL_COPY))
472                 return false; // no local copy
473         }
474     }
475     else
476     {
477         if ( !(nDragDropMode & DragDropMode::APP_DROP ) )
478             return false; // no drop
479         if ( DND_ACTION_MOVE == nAction )
480         {
481             if ( !(nDragDropMode & DragDropMode::APP_MOVE) )
482                 return false; // no global move
483         }
484         else
485         {
486             if ( !(nDragDropMode & DragDropMode::APP_COPY))
487                 return false; // no global copy
488         }
489     }
490     return true;
491 }
492 
493 
494 /*
495     NotifyMoving/Copying
496     ====================
497 
498     default behavior:
499 
500     1. target doesn't have children
501         - entry becomes sibling of target. entry comes after target
502           (->Window: below the target)
503     2. target is an expanded parent
504         - entry inserted at the beginning of the target childlist
505     3. target is a collapsed parent
506         - entry is inserted at the end of the target childlist
507 */
NotifyMoving(SvTreeListEntry * pTarget,SvTreeListEntry * pEntry,SvTreeListEntry * & rpNewParent,sal_uLong & rNewChildPos)508 TriState SvTreeListBox::NotifyMoving(
509     SvTreeListEntry*  pTarget,       // D&D dropping position in GetModel()
510     SvTreeListEntry*  pEntry,        // entry that we want to move, from
511                                  // GetSourceListBox()->GetModel()
512     SvTreeListEntry*& rpNewParent,   // new target parent
513     sal_uLong&        rNewChildPos)  // position in childlist of target parent
514 {
515     DBG_ASSERT(pEntry,"NotifyMoving:SourceEntry?");
516     if( !pTarget )
517     {
518         rpNewParent = nullptr;
519         rNewChildPos = 0;
520         return TRISTATE_TRUE;
521     }
522     if ( !pTarget->HasChildren() && !pTarget->HasChildrenOnDemand() )
523     {
524         // case 1
525         rpNewParent = GetParent( pTarget );
526         rNewChildPos = SvTreeList::GetRelPos( pTarget ) + 1;
527         rNewChildPos += nCurEntrySelPos;
528         nCurEntrySelPos++;
529     }
530     else
531     {
532         // cases 2 & 3
533         rpNewParent = pTarget;
534         if( IsExpanded(pTarget))
535             rNewChildPos = 0;
536         else
537             rNewChildPos = TREELIST_APPEND;
538     }
539     return TRISTATE_TRUE;
540 }
541 
NotifyCopying(SvTreeListEntry * pTarget,SvTreeListEntry * pEntry,SvTreeListEntry * & rpNewParent,sal_uLong & rNewChildPos)542 TriState SvTreeListBox::NotifyCopying(
543     SvTreeListEntry*  pTarget,       // D&D dropping position in GetModel()
544     SvTreeListEntry*  pEntry,        // entry that we want to move, from
545                                  // GetSourceListBox()->GetModel()
546     SvTreeListEntry*& rpNewParent,   // new target parent
547     sal_uLong&        rNewChildPos)  // position in childlist of target parent
548 {
549     return NotifyMoving(pTarget,pEntry,rpNewParent,rNewChildPos);
550 }
551 
FirstChild(SvTreeListEntry * pParent) const552 SvTreeListEntry* SvTreeListBox::FirstChild( SvTreeListEntry* pParent ) const
553 {
554     return pModel->FirstChild(pParent);
555 }
556 
557 // return: all entries copied
CopySelection(SvTreeListBox * pSource,SvTreeListEntry * pTarget)558 bool SvTreeListBox::CopySelection( SvTreeListBox* pSource, SvTreeListEntry* pTarget )
559 {
560     nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying
561     bool bSuccess = true;
562     std::vector<SvTreeListEntry*> aList;
563     bool bClone = ( pSource->GetModel() != GetModel() );
564     Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() );
565     pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
566 
567     // cache selection to simplify iterating over the selection when doing a D&D
568     // exchange within the same listbox
569     SvTreeListEntry* pSourceEntry = pSource->FirstSelected();
570     while ( pSourceEntry )
571     {
572         // children are copied automatically
573         pSource->SelectChildren( pSourceEntry, false );
574         aList.push_back( pSourceEntry );
575         pSourceEntry = pSource->NextSelected( pSourceEntry );
576     }
577 
578     for (auto const& elem : aList)
579     {
580         pSourceEntry = elem;
581         SvTreeListEntry* pNewParent = nullptr;
582         sal_uLong nInsertionPos = TREELIST_APPEND;
583         TriState nOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos);
584         if ( nOk )
585         {
586             if ( bClone )
587             {
588                 sal_uLong nCloneCount = 0;
589                 pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount);
590                 pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos);
591             }
592             else
593             {
594                 sal_uLong nListPos = pModel->Copy(pSourceEntry, pNewParent, nInsertionPos);
595                 pSourceEntry = GetEntry( pNewParent, nListPos );
596             }
597         }
598         else
599             bSuccess = false;
600 
601         if (nOk == TRISTATE_INDET)  // HACK: make visible moved entry
602             MakeVisible( pSourceEntry );
603     }
604     pModel->SetCloneLink( aCloneLink );
605     return bSuccess;
606 }
607 
608 // return: all entries were moved
MoveSelectionCopyFallbackPossible(SvTreeListBox * pSource,SvTreeListEntry * pTarget,bool bAllowCopyFallback)609 bool SvTreeListBox::MoveSelectionCopyFallbackPossible( SvTreeListBox* pSource, SvTreeListEntry* pTarget, bool bAllowCopyFallback )
610 {
611     nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying
612     bool bSuccess = true;
613     std::vector<SvTreeListEntry*> aList;
614     bool bClone = ( pSource->GetModel() != GetModel() );
615     Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() );
616     if ( bClone )
617         pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
618 
619     SvTreeListEntry* pSourceEntry = pSource->FirstSelected();
620     while ( pSourceEntry )
621     {
622         // children are automatically moved
623         pSource->SelectChildren( pSourceEntry, false );
624         aList.push_back( pSourceEntry );
625         pSourceEntry = pSource->NextSelected( pSourceEntry );
626     }
627 
628     for (auto const& elem : aList)
629     {
630         pSourceEntry = elem;
631         SvTreeListEntry* pNewParent = nullptr;
632         sal_uLong nInsertionPos = TREELIST_APPEND;
633         TriState nOk = NotifyMoving(pTarget,pSourceEntry,pNewParent,nInsertionPos);
634         TriState nCopyOk = nOk;
635         if ( !nOk && bAllowCopyFallback )
636         {
637             nInsertionPos = TREELIST_APPEND;
638             nCopyOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos);
639         }
640 
641         if ( nOk || nCopyOk )
642         {
643             if ( bClone )
644             {
645                 sal_uLong nCloneCount = 0;
646                 pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount);
647                 pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos);
648             }
649             else
650             {
651                 if ( nOk )
652                     pModel->Move(pSourceEntry, pNewParent, nInsertionPos);
653                 else
654                     pModel->Copy(pSourceEntry, pNewParent, nInsertionPos);
655             }
656         }
657         else
658             bSuccess = false;
659 
660         if (nOk == TRISTATE_INDET)  // HACK: make moved entry visible
661             MakeVisible( pSourceEntry );
662     }
663     pModel->SetCloneLink( aCloneLink );
664     return bSuccess;
665 }
666 
RemoveSelection()667 void SvTreeListBox::RemoveSelection()
668 {
669     std::vector<const SvTreeListEntry*> aList;
670     // cache selection, as the implementation deselects everything on the first
671     // remove
672     SvTreeListEntry* pEntry = FirstSelected();
673     while ( pEntry )
674     {
675         aList.push_back( pEntry );
676         if ( pEntry->HasChildren() )
677             // remove deletes all children automatically
678             SelectChildren(pEntry, false);
679         pEntry = NextSelected( pEntry );
680     }
681 
682     for (auto const& elem : aList)
683         pModel->Remove(elem);
684 }
685 
RemoveEntry(SvTreeListEntry const * pEntry)686 void SvTreeListBox::RemoveEntry(SvTreeListEntry const * pEntry)
687 {
688     pModel->Remove(pEntry);
689 }
690 
RecalcViewData()691 void SvTreeListBox::RecalcViewData()
692 {
693     SvTreeListEntry* pEntry = First();
694     while( pEntry )
695     {
696         sal_uInt16 nCount = pEntry->ItemCount();
697         sal_uInt16 nCurPos = 0;
698         while ( nCurPos < nCount )
699         {
700             SvLBoxItem& rItem = pEntry->GetItem( nCurPos );
701             rItem.InitViewData( this, pEntry );
702             nCurPos++;
703         }
704         pEntry = Next( pEntry );
705     }
706 }
707 
ImplShowTargetEmphasis(SvTreeListEntry * pEntry,bool bShow)708 void SvTreeListBox::ImplShowTargetEmphasis( SvTreeListEntry* pEntry, bool bShow)
709 {
710     if ( bShow && (nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
711         return;
712     if ( !bShow && !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
713         return;
714     pImpl->PaintDDCursor( pEntry, bShow);
715     if( bShow )
716         nImpFlags |= SvTreeListBoxFlags::TARGEMPH_VIS;
717     else
718         nImpFlags &= ~SvTreeListBoxFlags::TARGEMPH_VIS;
719 }
720 
OnCurrentEntryChanged()721 void SvTreeListBox::OnCurrentEntryChanged()
722 {
723     if ( !mpImpl->m_bDoingQuickSelection )
724         mpImpl->m_aQuickSelectionEngine.Reset();
725 }
726 
GetEntry(SvTreeListEntry * pParent,sal_uLong nPos) const727 SvTreeListEntry* SvTreeListBox::GetEntry( SvTreeListEntry* pParent, sal_uLong nPos ) const
728 {
729     return pModel->GetEntry(pParent, nPos);
730 }
731 
GetEntry(sal_uLong nRootPos) const732 SvTreeListEntry* SvTreeListBox::GetEntry( sal_uLong nRootPos ) const
733 {
734     return pModel->GetEntry(nRootPos);
735 }
736 
GetEntryFromPath(const::std::deque<sal_Int32> & _rPath) const737 SvTreeListEntry* SvTreeListBox::GetEntryFromPath( const ::std::deque< sal_Int32 >& _rPath ) const
738 {
739 
740     SvTreeListEntry* pEntry = nullptr;
741     SvTreeListEntry* pParent = nullptr;
742     for (auto const& elem : _rPath)
743     {
744         pEntry = GetEntry( pParent, elem );
745         if ( !pEntry )
746             break;
747         pParent = pEntry;
748     }
749 
750     return pEntry;
751 }
752 
FillEntryPath(SvTreeListEntry * pEntry,::std::deque<sal_Int32> & _rPath) const753 void SvTreeListBox::FillEntryPath( SvTreeListEntry* pEntry, ::std::deque< sal_Int32 >& _rPath ) const
754 {
755 
756     if ( !pEntry )
757         return;
758 
759     SvTreeListEntry* pParentEntry = GetParent( pEntry );
760     while ( true )
761     {
762         sal_uLong i, nCount = GetLevelChildCount( pParentEntry );
763         for ( i = 0; i < nCount; ++i )
764         {
765             SvTreeListEntry* pTemp = GetEntry( pParentEntry, i );
766             DBG_ASSERT( pEntry, "invalid entry" );
767             if ( pEntry == pTemp )
768             {
769                 _rPath.push_front( static_cast<sal_Int32>(i) );
770                 break;
771             }
772         }
773 
774         if ( pParentEntry )
775         {
776             pEntry = pParentEntry;
777             pParentEntry = GetParent( pParentEntry );
778         }
779         else
780             break;
781     }
782 }
783 
GetParent(const SvTreeListEntry * pEntry) const784 const SvTreeListEntry* SvTreeListBox::GetParent( const SvTreeListEntry* pEntry ) const
785 {
786     return pModel->GetParent(pEntry);
787 }
788 
GetParent(SvTreeListEntry * pEntry) const789 SvTreeListEntry* SvTreeListBox::GetParent( SvTreeListEntry* pEntry ) const
790 {
791     return pModel->GetParent(pEntry);
792 }
793 
GetRootLevelParent(SvTreeListEntry * pEntry) const794 SvTreeListEntry* SvTreeListBox::GetRootLevelParent( SvTreeListEntry* pEntry ) const
795 {
796     return pModel->GetRootLevelParent(pEntry);
797 }
798 
GetChildCount(SvTreeListEntry const * pParent) const799 sal_uLong SvTreeListBox::GetChildCount( SvTreeListEntry const * pParent ) const
800 {
801     return pModel->GetChildCount(pParent);
802 }
803 
GetLevelChildCount(SvTreeListEntry * _pParent) const804 sal_uLong SvTreeListBox::GetLevelChildCount( SvTreeListEntry* _pParent ) const
805 {
806 
807     //if _pParent is 0, then pEntry is the first child of the root.
808     SvTreeListEntry* pEntry = FirstChild( _pParent );
809 
810     if( !pEntry )//there is only root, root don't have children
811         return 0;
812 
813     if( !_pParent )//root and children of root
814         return pEntry->pParent->m_Children.size();
815 
816     return _pParent->m_Children.size();
817 }
818 
GetViewDataEntry(SvTreeListEntry const * pEntry) const819 SvViewDataEntry* SvTreeListBox::GetViewDataEntry( SvTreeListEntry const * pEntry ) const
820 {
821     return const_cast<SvViewDataEntry*>(SvListView::GetViewData(pEntry));
822 }
823 
GetViewDataItem(SvTreeListEntry const * pEntry,SvLBoxItem const * pItem)824 SvViewDataItem* SvTreeListBox::GetViewDataItem(SvTreeListEntry const * pEntry, SvLBoxItem const * pItem)
825 {
826     return const_cast<SvViewDataItem*>(static_cast<const SvTreeListBox*>(this)->GetViewDataItem(pEntry, pItem));
827 }
828 
GetViewDataItem(const SvTreeListEntry * pEntry,const SvLBoxItem * pItem) const829 const SvViewDataItem* SvTreeListBox::GetViewDataItem(const SvTreeListEntry* pEntry, const SvLBoxItem* pItem) const
830 {
831     const SvViewDataEntry* pEntryData = SvListView::GetViewData(pEntry);
832     assert(pEntryData && "Entry not in View");
833     sal_uInt16 nItemPos = pEntry->GetPos(pItem);
834     return &pEntryData->GetItem(nItemPos);
835 }
836 
InitViewData(SvViewDataEntry * pData,SvTreeListEntry * pEntry)837 void SvTreeListBox::InitViewData( SvViewDataEntry* pData, SvTreeListEntry* pEntry )
838 {
839     SvTreeListEntry* pInhEntry = pEntry;
840     SvViewDataEntry* pEntryData = pData;
841 
842     pEntryData->Init(pInhEntry->ItemCount());
843     sal_uInt16 nCount = pInhEntry->ItemCount();
844     sal_uInt16 nCurPos = 0;
845     while( nCurPos < nCount )
846     {
847         SvLBoxItem& rItem = pInhEntry->GetItem( nCurPos );
848         SvViewDataItem& rItemData = pEntryData->GetItem(nCurPos);
849         rItem.InitViewData( this, pInhEntry, &rItemData );
850         nCurPos++;
851     }
852 }
853 
EnableSelectionAsDropTarget(bool bEnable)854 void SvTreeListBox::EnableSelectionAsDropTarget( bool bEnable )
855 {
856     sal_uInt16 nRefDepth;
857     SvTreeListEntry* pTemp;
858 
859     SvTreeListEntry* pSelEntry = FirstSelected();
860     while( pSelEntry )
861     {
862         if ( !bEnable )
863         {
864             pSelEntry->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP;
865             nRefDepth = pModel->GetDepth( pSelEntry );
866             pTemp = Next( pSelEntry );
867             while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth )
868             {
869                 pTemp->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP;
870                 pTemp = Next( pTemp );
871             }
872         }
873         else
874         {
875             pSelEntry->nEntryFlags &= ~SvTLEntryFlags::DISABLE_DROP;
876             nRefDepth = pModel->GetDepth( pSelEntry );
877             pTemp = Next( pSelEntry );
878             while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth )
879             {
880                 pTemp->nEntryFlags &= ~SvTLEntryFlags::DISABLE_DROP;
881                 pTemp = Next( pTemp );
882             }
883         }
884         pSelEntry = NextSelected( pSelEntry );
885     }
886 }
887 
888 // ******************************************************************
889 // InplaceEditing
890 // ******************************************************************
891 
EditText(const OUString & rStr,const tools::Rectangle & rRect,const Selection & rSel)892 void SvTreeListBox::EditText( const OUString& rStr, const tools::Rectangle& rRect,
893     const Selection& rSel )
894 {
895     pEdCtrl.reset();
896     nImpFlags |= SvTreeListBoxFlags::IN_EDT;
897     nImpFlags &= ~SvTreeListBoxFlags::EDTEND_CALLED;
898     HideFocus();
899     pEdCtrl.reset( new SvInplaceEdit2(
900         this, rRect.TopLeft(), rRect.GetSize(), rStr,
901         LINK( this, SvTreeListBox, TextEditEndedHdl_Impl ),
902         rSel ) );
903 }
904 
IMPL_LINK_NOARG(SvTreeListBox,TextEditEndedHdl_Impl,SvInplaceEdit2 &,void)905 IMPL_LINK_NOARG(SvTreeListBox, TextEditEndedHdl_Impl, SvInplaceEdit2&, void)
906 {
907     if ( nImpFlags & SvTreeListBoxFlags::EDTEND_CALLED ) // avoid nesting
908         return;
909     nImpFlags |= SvTreeListBoxFlags::EDTEND_CALLED;
910     OUString aStr;
911     if ( !pEdCtrl->EditingCanceled() )
912         aStr = pEdCtrl->GetText();
913     else
914         aStr = pEdCtrl->GetSavedValue();
915     if ( mpImpl->m_bIsEmptyTextAllowed || !aStr.isEmpty() )
916         EditedText( aStr );
917     // Hide may only be called after the new text was put into the entry, so
918     // that we don't call the selection handler in the GetFocus of the listbox
919     // with the old entry text.
920     pEdCtrl->Hide();
921     nImpFlags &= ~SvTreeListBoxFlags::IN_EDT;
922     GrabFocus();
923 }
924 
CancelTextEditing()925 void SvTreeListBox::CancelTextEditing()
926 {
927     if ( pEdCtrl )
928         pEdCtrl->StopEditing( true );
929     nImpFlags &= ~SvTreeListBoxFlags::IN_EDT;
930 }
931 
EndEditing(bool bCancel)932 void SvTreeListBox::EndEditing( bool bCancel )
933 {
934     if( pEdCtrl )
935         pEdCtrl->StopEditing( bCancel );
936     nImpFlags &= ~SvTreeListBoxFlags::IN_EDT;
937 }
938 
939 
FirstSearchEntry(OUString & _rEntryText) const940 const void* SvTreeListBox::FirstSearchEntry( OUString& _rEntryText ) const
941 {
942     SvTreeListEntry* pEntry = GetCurEntry();
943     if ( pEntry )
944         pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( NextSearchEntry( pEntry, _rEntryText ) ) );
945     else
946     {
947         pEntry = FirstSelected();
948         if ( !pEntry )
949             pEntry = First();
950     }
951 
952     if ( pEntry )
953         _rEntryText = GetEntryText( pEntry );
954 
955     return pEntry;
956 }
957 
NextSearchEntry(const void * _pCurrentSearchEntry,OUString & _rEntryText) const958 const void* SvTreeListBox::NextSearchEntry( const void* _pCurrentSearchEntry, OUString& _rEntryText ) const
959 {
960     SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pCurrentSearchEntry ) );
961 
962     if  (   (   ( GetChildCount( pEntry ) > 0 )
963             ||  ( pEntry->HasChildrenOnDemand() )
964             )
965         &&  !IsExpanded( pEntry )
966         )
967     {
968         pEntry = pEntry->NextSibling();
969     }
970     else
971     {
972         pEntry = Next( pEntry );
973     }
974 
975     if ( !pEntry )
976         pEntry = First();
977 
978     if ( pEntry )
979         _rEntryText = GetEntryText( pEntry );
980 
981     return pEntry;
982 }
983 
SelectSearchEntry(const void * _pEntry)984 void SvTreeListBox::SelectSearchEntry( const void* _pEntry )
985 {
986     SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pEntry ) );
987     DBG_ASSERT( pEntry, "SvTreeListBox::SelectSearchEntry: invalid entry!" );
988     if ( !pEntry )
989         return;
990 
991     SelectAll( false );
992     SetCurEntry( pEntry );
993     Select( pEntry );
994 }
995 
ExecuteSearchEntry(const void *) const996 void SvTreeListBox::ExecuteSearchEntry( const void* /*_pEntry*/ ) const
997 {
998     // nothing to do here, we have no "execution"
999 }
1000 
CurrentEntry(OUString & _out_entryText) const1001 vcl::StringEntryIdentifier SvTreeListBox::CurrentEntry( OUString& _out_entryText ) const
1002 {
1003     // always accept the current entry if there is one
1004     SvTreeListEntry* pCurrentEntry( GetCurEntry() );
1005     if ( pCurrentEntry )
1006     {
1007         _out_entryText = GetEntryText( pCurrentEntry );
1008         return pCurrentEntry;
1009     }
1010     return FirstSearchEntry( _out_entryText );
1011 }
1012 
NextEntry(vcl::StringEntryIdentifier _currentEntry,OUString & _out_entryText) const1013 vcl::StringEntryIdentifier SvTreeListBox::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
1014 {
1015     return NextSearchEntry( _currentEntry, _out_entryText );
1016 }
1017 
SelectEntry(vcl::StringEntryIdentifier _entry)1018 void SvTreeListBox::SelectEntry( vcl::StringEntryIdentifier _entry )
1019 {
1020     SelectSearchEntry( _entry );
1021 }
1022 
HandleKeyInput(const KeyEvent & _rKEvt)1023 bool SvTreeListBox::HandleKeyInput( const KeyEvent& _rKEvt )
1024 {
1025     if ( _rKEvt.GetKeyCode().IsMod1() )
1026         return false;
1027 
1028     if  (   IsEntryMnemonicsEnabled()
1029         &&  mpImpl->m_aMnemonicEngine.HandleKeyEvent( _rKEvt )
1030         )
1031         return true;
1032 
1033     if (mbQuickSearch)
1034     {
1035         mpImpl->m_bDoingQuickSelection = true;
1036         const bool bHandled = mpImpl->m_aQuickSelectionEngine.HandleKeyEvent( _rKEvt );
1037         mpImpl->m_bDoingQuickSelection = false;
1038         if ( bHandled )
1039             return true;
1040     }
1041 
1042     return false;
1043 }
1044 
EditingCanceled() const1045 bool SvTreeListBox::EditingCanceled() const
1046 {
1047     return pEdCtrl && pEdCtrl->EditingCanceled();
1048 }
1049 
1050 
1051 //JP 28.3.2001: new Drag & Drop API
AcceptDrop(const AcceptDropEvent & rEvt)1052 sal_Int8 SvTreeListBox::AcceptDrop( const AcceptDropEvent& rEvt )
1053 {
1054     sal_Int8 nRet = DND_ACTION_NONE;
1055 
1056     if (rEvt.mbLeaving || !CheckDragAndDropMode(g_pDDSource, rEvt.mnAction))
1057     {
1058         ImplShowTargetEmphasis( pTargetEntry, false );
1059     }
1060     else if( nDragDropMode == DragDropMode::NONE )
1061     {
1062         SAL_WARN( "svtools.contnr", "SvTreeListBox::QueryDrop(): no target" );
1063     }
1064     else
1065     {
1066         SvTreeListEntry* pEntry = GetDropTarget( rEvt.maPosPixel );
1067         if( !IsDropFormatSupported( SotClipboardFormatId::TREELISTBOX ) )
1068         {
1069             SAL_WARN( "svtools.contnr", "SvTreeListBox::QueryDrop(): no format" );
1070         }
1071         else
1072         {
1073             DBG_ASSERT(g_pDDSource, "SvTreeListBox::QueryDrop(): SourceBox == 0");
1074             if (!( pEntry && g_pDDSource->GetModel() == GetModel()
1075                     && DND_ACTION_MOVE == rEvt.mnAction
1076                     && (pEntry->nEntryFlags & SvTLEntryFlags::DISABLE_DROP)))
1077             {
1078                 if( NotifyAcceptDrop( pEntry ))
1079                     nRet = rEvt.mnAction;
1080             }
1081         }
1082 
1083         // **** draw emphasis ****
1084         if( DND_ACTION_NONE == nRet )
1085                ImplShowTargetEmphasis( pTargetEntry, false );
1086         else if( pEntry != pTargetEntry || !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
1087         {
1088             ImplShowTargetEmphasis( pTargetEntry, false );
1089             pTargetEntry = pEntry;
1090             ImplShowTargetEmphasis( pTargetEntry, true );
1091         }
1092     }
1093     return nRet;
1094 }
1095 
ExecuteDrop(const ExecuteDropEvent & rEvt,SvTreeListBox * pSourceView)1096 sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt, SvTreeListBox* pSourceView )
1097 {
1098     assert(pSourceView);
1099     pSourceView->EnableSelectionAsDropTarget();
1100 
1101     ImplShowTargetEmphasis( pTargetEntry, false );
1102     g_pDDTarget = this;
1103 
1104     TransferableDataHelper aData( rEvt.maDropEvent.Transferable );
1105 
1106     sal_Int8 nRet;
1107     if( aData.HasFormat( SotClipboardFormatId::TREELISTBOX ))
1108         nRet = rEvt.mnAction;
1109     else
1110         nRet = DND_ACTION_NONE;
1111 
1112     if( DND_ACTION_NONE != nRet )
1113     {
1114         nRet = DND_ACTION_NONE;
1115 
1116         SvTreeListEntry* pTarget = pTargetEntry; // may be 0!
1117 
1118         if( DND_ACTION_COPY == rEvt.mnAction )
1119         {
1120             if (CopySelection(g_pDDSource, pTarget))
1121                 nRet = rEvt.mnAction;
1122         }
1123         else if( DND_ACTION_MOVE == rEvt.mnAction )
1124         {
1125             if (MoveSelectionCopyFallbackPossible( g_pDDSource, pTarget, false ))
1126                 nRet = rEvt.mnAction;
1127         }
1128         else if( DND_ACTION_COPYMOVE == rEvt.mnAction )
1129         {
1130             if (MoveSelectionCopyFallbackPossible(g_pDDSource, pTarget, true))
1131                 nRet = rEvt.mnAction;
1132         }
1133     }
1134     return nRet;
1135 }
1136 
ExecuteDrop(const ExecuteDropEvent & rEvt)1137 sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt )
1138 {
1139     return ExecuteDrop( rEvt, g_pDDSource );
1140 }
1141 
1142 /**
1143  * This sets the global variables used to determine the
1144  * in-process drag source.
1145  */
SetupDragOrigin()1146 void SvTreeListBox::SetupDragOrigin()
1147 {
1148     g_pDDSource = this;
1149     g_pDDTarget = nullptr;
1150 }
1151 
StartDrag(sal_Int8,const Point & rPosPixel)1152 void SvTreeListBox::StartDrag( sal_Int8, const Point& rPosPixel )
1153 {
1154 
1155     Point aEventPos( rPosPixel );
1156     MouseEvent aMouseEvt( aEventPos, 1, MouseEventModifiers::SELECT, MOUSE_LEFT );
1157     MouseButtonUp( aMouseEvt );
1158 
1159     nOldDragMode = GetDragDropMode();
1160     if ( nOldDragMode == DragDropMode::NONE )
1161         return;
1162 
1163     ReleaseMouse();
1164 
1165     SvTreeListEntry* pEntry = GetEntry( rPosPixel ); // GetDropTarget( rPos );
1166     if( !pEntry )
1167     {
1168         DragFinished( DND_ACTION_NONE );
1169         return;
1170     }
1171 
1172     rtl::Reference<TransferDataContainer> pContainer = new TransferDataContainer;
1173     nDragDropMode = NotifyStartDrag( *pContainer, pEntry );
1174     if( nDragDropMode == DragDropMode::NONE || 0 == GetSelectionCount() )
1175     {
1176         nDragDropMode = nOldDragMode;
1177         DragFinished( DND_ACTION_NONE );
1178         return;
1179     }
1180 
1181     SetupDragOrigin();
1182 
1183     // apparently some (unused) content is needed
1184     pContainer->CopyAnyData( SotClipboardFormatId::TREELISTBOX,
1185                              "unused", SAL_N_ELEMENTS("unused") );
1186 
1187     bool bOldUpdateMode = Control::IsUpdateMode();
1188     Control::SetUpdateMode( true );
1189     Update();
1190     Control::SetUpdateMode( bOldUpdateMode );
1191 
1192     // Disallow using the selection and its children as drop targets.
1193     // Important: If the selection of the SourceListBox is changed in the
1194     // DropHandler, the entries have to be allowed as drop targets again:
1195     // (GetSourceListBox()->EnableSelectionAsDropTarget( true, true );)
1196     EnableSelectionAsDropTarget( false );
1197 
1198     pContainer->StartDrag( this, DND_ACTION_COPYMOVE | DND_ACTION_LINK, GetDragFinishedHdl() );
1199 }
1200 
DragFinished(sal_Int8 nAction)1201 void SvTreeListBox::DragFinished( sal_Int8
1202 #ifndef UNX
1203 nAction
1204 #endif
1205 )
1206 {
1207     EnableSelectionAsDropTarget();
1208 
1209 #ifndef UNX
1210     if (   (nAction == DND_ACTION_MOVE)
1211         && (   (g_pDDTarget && (g_pDDTarget->GetModel() != GetModel()))
1212             || !g_pDDTarget))
1213     {
1214         RemoveSelection();
1215     }
1216 #endif
1217 
1218     ImplShowTargetEmphasis( pTargetEntry, false );
1219     g_pDDSource = nullptr;
1220     g_pDDTarget = nullptr;
1221     pTargetEntry = nullptr;
1222     nDragDropMode = nOldDragMode;
1223 }
1224 
NotifyStartDrag(TransferDataContainer &,SvTreeListEntry *)1225 DragDropMode SvTreeListBox::NotifyStartDrag( TransferDataContainer&, SvTreeListEntry* )
1226 {
1227     return DragDropMode(0xffff);
1228 }
1229 
NotifyAcceptDrop(SvTreeListEntry *)1230 bool SvTreeListBox::NotifyAcceptDrop( SvTreeListEntry* )
1231 {
1232     return true;
1233 }
1234 
1235 // Handler and methods for Drag - finished handler.
1236 // The with get GetDragFinishedHdl() get link can set on the
1237 // TransferDataContainer. This link is a callback for the DragFinished
1238 // call. AddBox method is called from the GetDragFinishedHdl() and the
1239 // remove is called in link callback and in the destructor. So it can't
1240 // called to a deleted object.
1241 
1242 namespace
1243 {
1244     struct SortLBoxes : public rtl::Static<std::set<sal_uLong>, SortLBoxes> {};
1245 }
1246 
AddBoxToDDList_Impl(const SvTreeListBox & rB)1247 void SvTreeListBox::AddBoxToDDList_Impl( const SvTreeListBox& rB )
1248 {
1249     sal_uLong nVal = reinterpret_cast<sal_uLong>(&rB);
1250     SortLBoxes::get().insert( nVal );
1251 }
1252 
RemoveBoxFromDDList_Impl(const SvTreeListBox & rB)1253 void SvTreeListBox::RemoveBoxFromDDList_Impl( const SvTreeListBox& rB )
1254 {
1255     sal_uLong nVal = reinterpret_cast<sal_uLong>(&rB);
1256     SortLBoxes::get().erase( nVal );
1257 }
1258 
IMPL_LINK(SvTreeListBox,DragFinishHdl_Impl,sal_Int8,nAction,void)1259 IMPL_LINK( SvTreeListBox, DragFinishHdl_Impl, sal_Int8, nAction, void )
1260 {
1261     sal_uLong nVal = reinterpret_cast<sal_uLong>(this);
1262     std::set<sal_uLong> &rSortLBoxes = SortLBoxes::get();
1263     std::set<sal_uLong>::const_iterator it = rSortLBoxes.find(nVal);
1264     if( it != rSortLBoxes.end() )
1265     {
1266         DragFinished( nAction );
1267         rSortLBoxes.erase( it );
1268     }
1269 }
1270 
GetDragFinishedHdl() const1271 Link<sal_Int8,void> SvTreeListBox::GetDragFinishedHdl() const
1272 {
1273     AddBoxToDDList_Impl( *this );
1274     return LINK( const_cast<SvTreeListBox*>(this), SvTreeListBox, DragFinishHdl_Impl );
1275 }
1276 
1277 /*
1278     Bugs/TODO
1279 
1280     - calculate rectangle when editing in-place (bug with some fonts)
1281     - SetSpaceBetweenEntries: offset is not taken into account in SetEntryHeight
1282 */
1283 
1284 #define SV_LBOX_DEFAULT_INDENT_PIXEL 20
1285 
InitTreeView()1286 void SvTreeListBox::InitTreeView()
1287 {
1288     pCheckButtonData = nullptr;
1289     pEdEntry = nullptr;
1290     pEdItem = nullptr;
1291     nEntryHeight = 0;
1292     pEdCtrl = nullptr;
1293     nFirstSelTab = 0;
1294     nLastSelTab = 0;
1295     nFocusWidth = -1;
1296     mnCheckboxItemWidth = 0;
1297 
1298     nTreeFlags = SvTreeFlags::RECALCTABS;
1299     nIndent = SV_LBOX_DEFAULT_INDENT_PIXEL;
1300     nEntryHeightOffs = SV_ENTRYHEIGHTOFFS_PIXEL;
1301     pImpl.reset( new SvImpLBox( this, GetModel(), GetStyle() ) );
1302 
1303     mbContextBmpExpanded = true;
1304     nContextBmpWidthMax = 0;
1305 
1306     SetFont( GetFont() );
1307     AdjustEntryHeightAndRecalc();
1308 
1309     SetSpaceBetweenEntries( 0 );
1310     SetLineColor();
1311     InitSettings();
1312     ImplInitStyle();
1313     SetTabs();
1314 }
1315 
GetEntryAltText(SvTreeListEntry *) const1316 OUString SvTreeListBox::GetEntryAltText( SvTreeListEntry* ) const
1317 {
1318     return OUString();
1319 }
1320 
GetEntryLongDescription(SvTreeListEntry *) const1321 OUString SvTreeListBox::GetEntryLongDescription( SvTreeListEntry* ) const
1322 {
1323     return OUString();
1324 }
1325 
SearchEntryTextWithHeadTitle(SvTreeListEntry * pEntry)1326 OUString SvTreeListBox::SearchEntryTextWithHeadTitle( SvTreeListEntry* pEntry )
1327 {
1328     assert(pEntry);
1329     OUStringBuffer sRet;
1330 
1331     sal_uInt16 nCount = pEntry->ItemCount();
1332     sal_uInt16 nCur = 0;
1333     while( nCur < nCount )
1334     {
1335         SvLBoxItem& rItem = pEntry->GetItem( nCur );
1336         if ( (rItem.GetType() == SvLBoxItemType::String) &&
1337              !static_cast<SvLBoxString&>( rItem ).GetText().isEmpty() )
1338         {
1339             sRet.append(static_cast<SvLBoxString&>( rItem ).GetText()).append(",");
1340         }
1341         nCur++;
1342     }
1343 
1344     if (!sRet.isEmpty())
1345         sRet = sRet.copy(0, sRet.getLength() - 1);
1346     return sRet.makeStringAndClear();
1347 }
1348 
~SvTreeListBox()1349 SvTreeListBox::~SvTreeListBox()
1350 {
1351     disposeOnce();
1352 }
1353 
dispose()1354 void SvTreeListBox::dispose()
1355 {
1356     if( pImpl )
1357     {
1358         pImpl->CallEventListeners( VclEventId::ObjectDying );
1359         pImpl.reset();
1360     }
1361     if( mpImpl )
1362     {
1363         ClearTabList();
1364 
1365         pEdCtrl.reset();
1366 
1367         SvListView::dispose();
1368 
1369         SvTreeListBox::RemoveBoxFromDDList_Impl( *this );
1370 
1371         if (this == g_pDDSource)
1372             g_pDDSource = nullptr;
1373         if (this == g_pDDTarget)
1374             g_pDDTarget = nullptr;
1375         mpImpl.reset();
1376     }
1377 
1378     DropTargetHelper::dispose();
1379     DragSourceHelper::dispose();
1380     Control::dispose();
1381 }
1382 
SetNoAutoCurEntry(bool b)1383 void SvTreeListBox::SetNoAutoCurEntry( bool b )
1384 {
1385     pImpl->SetNoAutoCurEntry( b );
1386 }
1387 
SetSublistOpenWithReturn()1388 void SvTreeListBox::SetSublistOpenWithReturn()
1389 {
1390     pImpl->m_bSubLstOpRet = true;
1391 }
1392 
SetSublistOpenWithLeftRight()1393 void SvTreeListBox::SetSublistOpenWithLeftRight()
1394 {
1395     pImpl->m_bSubLstOpLR = true;
1396 }
1397 
SetSublistDontOpenWithDoubleClick(bool bDontOpen)1398 void SvTreeListBox::SetSublistDontOpenWithDoubleClick(bool bDontOpen)
1399 {
1400     pImpl->m_bSubLstOpDblClick = !bDontOpen;
1401 }
1402 
Resize()1403 void SvTreeListBox::Resize()
1404 {
1405     if( IsEditingActive() )
1406         EndEditing( true );
1407 
1408     Control::Resize();
1409 
1410     pImpl->Resize();
1411     nFocusWidth = -1;
1412     pImpl->ShowCursor( false );
1413     pImpl->ShowCursor( true );
1414 }
1415 
1416 /* Cases:
1417 
1418    A) entries have bitmaps
1419        0. no buttons
1420        1. node buttons (can optionally also be on root items)
1421        2. node buttons (can optionally also be on root items) + CheckButton
1422        3. CheckButton
1423    B) entries don't have bitmaps  (=>via WindowBits because of D&D!)
1424        0. no buttons
1425        1. node buttons (can optionally also be on root items)
1426        2. node buttons (can optionally also be on root items) + CheckButton
1427        3. CheckButton
1428 */
1429 
1430 #define NO_BUTTONS              0
1431 #define NODE_BUTTONS            1
1432 #define NODE_AND_CHECK_BUTTONS  2
1433 #define CHECK_BUTTONS           3
1434 
1435 #define TABFLAGS_TEXT (SvLBoxTabFlags::DYNAMIC |        \
1436                        SvLBoxTabFlags::ADJUST_LEFT |    \
1437                        SvLBoxTabFlags::EDITABLE |       \
1438                        SvLBoxTabFlags::SHOW_SELECTION)
1439 
1440 #define TABFLAGS_CONTEXTBMP (SvLBoxTabFlags::DYNAMIC | SvLBoxTabFlags::ADJUST_CENTER)
1441 
1442 #define TABFLAGS_CHECKBTN (SvLBoxTabFlags::DYNAMIC |        \
1443                            SvLBoxTabFlags::ADJUST_CENTER)
1444 
1445 #define TAB_STARTPOS    2
1446 
1447 // take care of GetTextOffset when doing changes
SetTabs()1448 void SvTreeListBox::SetTabs()
1449 {
1450     if( IsEditingActive() )
1451         EndEditing( true );
1452     nTreeFlags &= ~SvTreeFlags::RECALCTABS;
1453     nFocusWidth = -1;
1454     const WinBits nStyle( GetStyle() );
1455     bool bHasButtons = (nStyle & WB_HASBUTTONS)!=0;
1456     bool bHasButtonsAtRoot = (nStyle & (WB_HASLINESATROOT |
1457                                               WB_HASBUTTONSATROOT))!=0;
1458     long nStartPos = TAB_STARTPOS;
1459     long nNodeWidthPixel = GetExpandedNodeBmp().GetSizePixel().Width();
1460 
1461     // pCheckButtonData->Width() knows nothing about the native checkbox width,
1462     // so we have mnCheckboxItemWidth which becomes valid when something is added.
1463     long nCheckWidth = 0;
1464     if( nTreeFlags & SvTreeFlags::CHKBTN )
1465         nCheckWidth = mnCheckboxItemWidth;
1466     long nCheckWidthDIV2 = nCheckWidth / 2;
1467 
1468     long nContextWidth = nContextBmpWidthMax;
1469     long nContextWidthDIV2 = nContextWidth / 2;
1470 
1471     ClearTabList();
1472 
1473     int nCase = NO_BUTTONS;
1474     if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
1475     {
1476         if( bHasButtons )
1477             nCase = NODE_BUTTONS;
1478     }
1479     else
1480     {
1481         if( bHasButtons )
1482             nCase = NODE_AND_CHECK_BUTTONS;
1483         else
1484             nCase = CHECK_BUTTONS;
1485     }
1486 
1487     switch( nCase )
1488     {
1489         case NO_BUTTONS :
1490             nStartPos += nContextWidthDIV2;  // because of centering
1491             AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
1492             nStartPos += nContextWidthDIV2;  // right edge of context bitmap
1493             // only set a distance if there are bitmaps
1494             if( nContextBmpWidthMax )
1495                 nStartPos += 5; // distance context bitmap to text
1496             AddTab( nStartPos, TABFLAGS_TEXT );
1497             break;
1498 
1499         case NODE_BUTTONS :
1500             if( bHasButtonsAtRoot )
1501                 nStartPos += ( nIndent + (nNodeWidthPixel/2) );
1502             else
1503                 nStartPos += nContextWidthDIV2;
1504             AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
1505             nStartPos += nContextWidthDIV2;  // right edge of context bitmap
1506             // only set a distance if there are bitmaps
1507             if( nContextBmpWidthMax )
1508                 nStartPos += 5; // distance context bitmap to text
1509             AddTab( nStartPos, TABFLAGS_TEXT );
1510             break;
1511 
1512         case NODE_AND_CHECK_BUTTONS :
1513             if( bHasButtonsAtRoot )
1514                 nStartPos += ( nIndent + nNodeWidthPixel );
1515             else
1516                 nStartPos += nCheckWidthDIV2;
1517             AddTab( nStartPos, TABFLAGS_CHECKBTN );
1518             nStartPos += nCheckWidthDIV2;  // right edge of CheckButton
1519             nStartPos += 3;  // distance CheckButton to context bitmap
1520             nStartPos += nContextWidthDIV2;  // center of context bitmap
1521             AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
1522             nStartPos += nContextWidthDIV2;  // right edge of context bitmap
1523             // only set a distance if there are bitmaps
1524             if( nContextBmpWidthMax )
1525                 nStartPos += 5; // distance context bitmap to text
1526             AddTab( nStartPos, TABFLAGS_TEXT );
1527             break;
1528 
1529         case CHECK_BUTTONS :
1530             nStartPos += nCheckWidthDIV2;
1531             AddTab( nStartPos, TABFLAGS_CHECKBTN );
1532             nStartPos += nCheckWidthDIV2;  // right edge of CheckButton
1533             nStartPos += 3;  // distance CheckButton to context bitmap
1534             nStartPos += nContextWidthDIV2;  // center of context bitmap
1535             AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
1536             nStartPos += nContextWidthDIV2;  // right edge of context bitmap
1537             // only set a distance if there are bitmaps
1538             if( nContextBmpWidthMax )
1539                 nStartPos += 5; // distance context bitmap to text
1540             AddTab( nStartPos, TABFLAGS_TEXT );
1541             break;
1542     }
1543     pImpl->NotifyTabsChanged();
1544 }
1545 
InitEntry(SvTreeListEntry * pEntry,const OUString & aStr,const Image & aCollEntryBmp,const Image & aExpEntryBmp)1546 void SvTreeListBox::InitEntry(SvTreeListEntry* pEntry,
1547     const OUString& aStr, const Image& aCollEntryBmp, const Image& aExpEntryBmp)
1548 {
1549     if( nTreeFlags & SvTreeFlags::CHKBTN )
1550     {
1551         pEntry->AddItem(std::make_unique<SvLBoxButton>(pCheckButtonData));
1552     }
1553 
1554     pEntry->AddItem(std::make_unique<SvLBoxContextBmp>( aCollEntryBmp,aExpEntryBmp, mbContextBmpExpanded));
1555 
1556     pEntry->AddItem(std::make_unique<SvLBoxString>(aStr));
1557 }
1558 
GetEntryText(SvTreeListEntry * pEntry) const1559 OUString SvTreeListBox::GetEntryText(SvTreeListEntry* pEntry) const
1560 {
1561     assert(pEntry);
1562     SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String));
1563     assert(pItem);
1564     return pItem->GetText();
1565 }
1566 
GetExpandedEntryBmp(const SvTreeListEntry * pEntry)1567 const Image& SvTreeListBox::GetExpandedEntryBmp(const SvTreeListEntry* pEntry)
1568 {
1569     assert(pEntry);
1570     const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
1571     assert(pItem);
1572     return pItem->GetBitmap2( );
1573 }
1574 
GetCollapsedEntryBmp(const SvTreeListEntry * pEntry)1575 const Image& SvTreeListBox::GetCollapsedEntryBmp( const SvTreeListEntry* pEntry )
1576 {
1577     assert(pEntry);
1578     const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
1579     assert(pItem);
1580     return pItem->GetBitmap1( );
1581 }
1582 
IMPL_LINK(SvTreeListBox,CheckButtonClick,SvLBoxButtonData *,pData,void)1583 IMPL_LINK( SvTreeListBox, CheckButtonClick, SvLBoxButtonData *, pData, void )
1584 {
1585     pHdlEntry = pData->GetActEntry();
1586     CheckButtonHdl();
1587 }
1588 
InsertEntry(const OUString & rText,SvTreeListEntry * pParent,bool bChildrenOnDemand,sal_uLong nPos,void * pUser)1589 SvTreeListEntry* SvTreeListBox::InsertEntry(
1590     const OUString& rText,
1591     SvTreeListEntry* pParent,
1592     bool bChildrenOnDemand, sal_uLong nPos,
1593     void* pUser
1594 )
1595 {
1596     nTreeFlags |= SvTreeFlags::MANINS;
1597 
1598     const Image& rDefExpBmp = pImpl->GetDefaultEntryExpBmp( );
1599     const Image& rDefColBmp = pImpl->GetDefaultEntryColBmp( );
1600 
1601     aCurInsertedExpBmp = rDefExpBmp;
1602     aCurInsertedColBmp = rDefColBmp;
1603 
1604     SvTreeListEntry* pEntry = new SvTreeListEntry;
1605     pEntry->SetUserData( pUser );
1606     InitEntry( pEntry, rText, rDefColBmp, rDefExpBmp );
1607     pEntry->EnableChildrenOnDemand( bChildrenOnDemand );
1608 
1609     if( !pParent )
1610         Insert( pEntry, nPos );
1611     else
1612         Insert( pEntry, pParent, nPos );
1613 
1614     aPrevInsertedExpBmp = rDefExpBmp;
1615     aPrevInsertedColBmp = rDefColBmp;
1616 
1617     nTreeFlags &= ~SvTreeFlags::MANINS;
1618 
1619     return pEntry;
1620 }
1621 
InsertEntry(const OUString & rText,const Image & aExpEntryBmp,const Image & aCollEntryBmp,SvTreeListEntry * pParent,bool bChildrenOnDemand,sal_uLong nPos,void * pUser)1622 SvTreeListEntry* SvTreeListBox::InsertEntry( const OUString& rText,
1623     const Image& aExpEntryBmp, const Image& aCollEntryBmp,
1624     SvTreeListEntry* pParent, bool bChildrenOnDemand, sal_uLong nPos, void* pUser )
1625 {
1626     nTreeFlags |= SvTreeFlags::MANINS;
1627 
1628     aCurInsertedExpBmp = aExpEntryBmp;
1629     aCurInsertedColBmp = aCollEntryBmp;
1630 
1631     SvTreeListEntry* pEntry = new SvTreeListEntry;
1632     pEntry->SetUserData( pUser );
1633     InitEntry( pEntry, rText, aCollEntryBmp, aExpEntryBmp );
1634 
1635     pEntry->EnableChildrenOnDemand( bChildrenOnDemand );
1636 
1637     if( !pParent )
1638         Insert( pEntry, nPos );
1639     else
1640         Insert( pEntry, pParent, nPos );
1641 
1642     aPrevInsertedExpBmp = aExpEntryBmp;
1643     aPrevInsertedColBmp = aCollEntryBmp;
1644 
1645     nTreeFlags &= ~SvTreeFlags::MANINS;
1646 
1647     return pEntry;
1648 }
1649 
SetEntryText(SvTreeListEntry * pEntry,const OUString & rStr)1650 void SvTreeListBox::SetEntryText(SvTreeListEntry* pEntry, const OUString& rStr)
1651 {
1652     SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String));
1653     assert(pItem);
1654     pItem->SetText(rStr);
1655     pItem->InitViewData( this, pEntry );
1656     GetModel()->InvalidateEntry( pEntry );
1657 }
1658 
SetExpandedEntryBmp(SvTreeListEntry * pEntry,const Image & aBmp)1659 void SvTreeListBox::SetExpandedEntryBmp( SvTreeListEntry* pEntry, const Image& aBmp )
1660 {
1661     SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
1662 
1663     assert(pItem);
1664     pItem->SetBitmap2( aBmp );
1665 
1666     GetModel()->InvalidateEntry( pEntry );
1667     SetEntryHeight( pEntry );
1668     Size aSize = aBmp.GetSizePixel();
1669     short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) );
1670     if( nWidth > nContextBmpWidthMax )
1671     {
1672         nContextBmpWidthMax = nWidth;
1673         SetTabs();
1674     }
1675 }
1676 
SetCollapsedEntryBmp(SvTreeListEntry * pEntry,const Image & aBmp)1677 void SvTreeListBox::SetCollapsedEntryBmp(SvTreeListEntry* pEntry,const Image& aBmp )
1678 {
1679     SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
1680 
1681     assert(pItem);
1682     pItem->SetBitmap1( aBmp );
1683 
1684     GetModel()->InvalidateEntry( pEntry );
1685     SetEntryHeight( pEntry );
1686     Size aSize = aBmp.GetSizePixel();
1687     short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) );
1688     if( nWidth > nContextBmpWidthMax )
1689     {
1690         nContextBmpWidthMax = nWidth;
1691         SetTabs();
1692     }
1693 }
1694 
CheckBoxInserted(SvTreeListEntry * pEntry)1695 void SvTreeListBox::CheckBoxInserted(SvTreeListEntry* pEntry)
1696 {
1697     SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
1698     if( pItem )
1699     {
1700         auto nWidth = pItem->GetWidth(this, pEntry);
1701         if( mnCheckboxItemWidth < nWidth )
1702         {
1703             mnCheckboxItemWidth = nWidth;
1704             nTreeFlags |= SvTreeFlags::RECALCTABS;
1705         }
1706     }
1707 }
1708 
ImpEntryInserted(SvTreeListEntry * pEntry)1709 void SvTreeListBox::ImpEntryInserted( SvTreeListEntry* pEntry )
1710 {
1711 
1712     SvTreeListEntry* pParent = pModel->GetParent( pEntry );
1713     if( pParent )
1714     {
1715         SvTLEntryFlags nFlags = pParent->GetFlags();
1716         nFlags &= ~SvTLEntryFlags::NO_NODEBMP;
1717         pParent->SetFlags( nFlags );
1718     }
1719 
1720     if(!((nTreeFlags & SvTreeFlags::MANINS) &&
1721          (aPrevInsertedExpBmp == aCurInsertedExpBmp)  &&
1722          (aPrevInsertedColBmp == aCurInsertedColBmp) ))
1723     {
1724         Size aSize = GetCollapsedEntryBmp( pEntry ).GetSizePixel();
1725         if( aSize.Width() > nContextBmpWidthMax )
1726         {
1727             nContextBmpWidthMax = static_cast<short>(aSize.Width());
1728             nTreeFlags |= SvTreeFlags::RECALCTABS;
1729         }
1730         aSize = GetExpandedEntryBmp( pEntry ).GetSizePixel();
1731         if( aSize.Width() > nContextBmpWidthMax )
1732         {
1733             nContextBmpWidthMax = static_cast<short>(aSize.Width());
1734             nTreeFlags |= SvTreeFlags::RECALCTABS;
1735         }
1736     }
1737     SetEntryHeight( pEntry );
1738 
1739     if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
1740         return;
1741 
1742     CheckBoxInserted(pEntry);
1743 }
1744 
SetCheckButtonState(SvTreeListEntry * pEntry,SvButtonState eState)1745 void SvTreeListBox::SetCheckButtonState( SvTreeListEntry* pEntry, SvButtonState eState)
1746 {
1747     if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
1748         return;
1749 
1750     SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
1751     if(!pItem)
1752         return ;
1753     switch( eState )
1754     {
1755         case SvButtonState::Checked:
1756             pItem->SetStateChecked();
1757             break;
1758 
1759         case SvButtonState::Unchecked:
1760             pItem->SetStateUnchecked();
1761             break;
1762 
1763         case SvButtonState::Tristate:
1764             pItem->SetStateTristate();
1765             break;
1766     }
1767     InvalidateEntry( pEntry );
1768 }
1769 
GetCheckButtonState(SvTreeListEntry * pEntry) const1770 SvButtonState SvTreeListBox::GetCheckButtonState( SvTreeListEntry* pEntry ) const
1771 {
1772     SvButtonState eState = SvButtonState::Unchecked;
1773     if( pEntry && ( nTreeFlags & SvTreeFlags::CHKBTN ) )
1774     {
1775         SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
1776         if(!pItem)
1777             return SvButtonState::Tristate;
1778         SvItemStateFlags nButtonFlags = pItem->GetButtonFlags();
1779         eState = SvLBoxButtonData::ConvertToButtonState( nButtonFlags );
1780     }
1781     return eState;
1782 }
1783 
CheckButtonHdl()1784 void SvTreeListBox::CheckButtonHdl()
1785 {
1786     aCheckButtonHdl.Call( this );
1787     if ( pCheckButtonData )
1788         pImpl->CallEventListeners( VclEventId::CheckboxToggle, static_cast<void*>(pCheckButtonData->GetActEntry()) );
1789 }
1790 
1791 
1792 // TODO: Currently all data is cloned so that they conform to the default tree
1793 // view format. Actually, the model should be used as a reference here. This
1794 // leads to us _not_ calling SvTreeListEntry::Clone, but only its base class
1795 // SvTreeListEntry.
1796 
1797 
CloneEntry(SvTreeListEntry * pSource)1798 SvTreeListEntry* SvTreeListBox::CloneEntry( SvTreeListEntry* pSource )
1799 {
1800     OUString aStr;
1801     Image aCollEntryBmp;
1802     Image aExpEntryBmp;
1803 
1804     SvLBoxString* pStringItem = static_cast<SvLBoxString*>(pSource->GetFirstItem(SvLBoxItemType::String));
1805     if( pStringItem )
1806         aStr = pStringItem->GetText();
1807     SvLBoxContextBmp* pBmpItem = static_cast<SvLBoxContextBmp*>(pSource->GetFirstItem(SvLBoxItemType::ContextBmp));
1808     if( pBmpItem )
1809     {
1810         aCollEntryBmp = pBmpItem->GetBitmap1( );
1811         aExpEntryBmp  = pBmpItem->GetBitmap2( );
1812     }
1813     SvTreeListEntry* pClone = new SvTreeListEntry;
1814     InitEntry( pClone, aStr, aCollEntryBmp, aExpEntryBmp );
1815     pClone->SvTreeListEntry::Clone( pSource );
1816     pClone->EnableChildrenOnDemand( pSource->HasChildrenOnDemand() );
1817     pClone->SetUserData( pSource->GetUserData() );
1818 
1819     return pClone;
1820 }
1821 
SetIndent(short nNewIndent)1822 void SvTreeListBox::SetIndent( short nNewIndent )
1823 {
1824     nIndent = nNewIndent;
1825     SetTabs();
1826     if( IsUpdateMode() )
1827         Invalidate();
1828 }
1829 
GetDefaultExpandedEntryBmp() const1830 const Image& SvTreeListBox::GetDefaultExpandedEntryBmp( ) const
1831 {
1832     return pImpl->GetDefaultEntryExpBmp( );
1833 }
1834 
GetDefaultCollapsedEntryBmp() const1835 const Image& SvTreeListBox::GetDefaultCollapsedEntryBmp( ) const
1836 {
1837     return pImpl->GetDefaultEntryColBmp( );
1838 }
1839 
SetDefaultExpandedEntryBmp(const Image & aBmp)1840 void SvTreeListBox::SetDefaultExpandedEntryBmp( const Image& aBmp )
1841 {
1842     Size aSize = aBmp.GetSizePixel();
1843     if( aSize.Width() > nContextBmpWidthMax )
1844         nContextBmpWidthMax = static_cast<short>(aSize.Width());
1845     SetTabs();
1846 
1847     pImpl->SetDefaultEntryExpBmp( aBmp );
1848 }
1849 
SetDefaultCollapsedEntryBmp(const Image & aBmp)1850 void SvTreeListBox::SetDefaultCollapsedEntryBmp( const Image& aBmp )
1851 {
1852     Size aSize = aBmp.GetSizePixel();
1853     if( aSize.Width() > nContextBmpWidthMax )
1854         nContextBmpWidthMax = static_cast<short>(aSize.Width());
1855     SetTabs();
1856 
1857     pImpl->SetDefaultEntryColBmp( aBmp );
1858 }
1859 
EnableCheckButton(SvLBoxButtonData * pData)1860 void SvTreeListBox::EnableCheckButton( SvLBoxButtonData* pData )
1861 {
1862     if( !pData )
1863         nTreeFlags &= ~SvTreeFlags::CHKBTN;
1864     else
1865     {
1866         SetCheckButtonData( pData );
1867         nTreeFlags |= SvTreeFlags::CHKBTN;
1868         pData->SetLink( LINK(this, SvTreeListBox, CheckButtonClick));
1869     }
1870 
1871     SetTabs();
1872     if( IsUpdateMode() )
1873         Invalidate();
1874 }
1875 
SetCheckButtonData(SvLBoxButtonData * pData)1876 void SvTreeListBox::SetCheckButtonData( SvLBoxButtonData* pData )
1877 {
1878     if ( pData )
1879         pCheckButtonData = pData;
1880 }
1881 
GetDefaultExpandedNodeImage()1882 const Image& SvTreeListBox::GetDefaultExpandedNodeImage( )
1883 {
1884     return SvImpLBox::GetDefaultExpandedNodeImage( );
1885 }
1886 
GetDefaultCollapsedNodeImage()1887 const Image& SvTreeListBox::GetDefaultCollapsedNodeImage( )
1888 {
1889     return SvImpLBox::GetDefaultCollapsedNodeImage( );
1890 }
1891 
SetNodeBitmaps(const Image & rCollapsedNodeBmp,const Image & rExpandedNodeBmp)1892 void SvTreeListBox::SetNodeBitmaps( const Image& rCollapsedNodeBmp, const Image& rExpandedNodeBmp )
1893 {
1894     SetExpandedNodeBmp( rExpandedNodeBmp );
1895     SetCollapsedNodeBmp( rCollapsedNodeBmp );
1896     SetTabs();
1897 }
1898 
EditingEntry(SvTreeListEntry *,Selection &)1899 bool SvTreeListBox::EditingEntry( SvTreeListEntry*, Selection& )
1900 {
1901     return true;
1902 }
1903 
EditedEntry(SvTreeListEntry *,const OUString &)1904 bool SvTreeListBox::EditedEntry( SvTreeListEntry* /*pEntry*/,const OUString& /*rNewText*/)
1905 {
1906     return true;
1907 }
1908 
EnableInplaceEditing(bool bOn)1909 void SvTreeListBox::EnableInplaceEditing( bool bOn )
1910 {
1911     if (bOn)
1912         nImpFlags |= SvTreeListBoxFlags::EDT_ENABLED;
1913     else
1914         nImpFlags &= ~SvTreeListBoxFlags::EDT_ENABLED;
1915 }
1916 
KeyInput(const KeyEvent & rKEvt)1917 void SvTreeListBox::KeyInput( const KeyEvent& rKEvt )
1918 {
1919     // under OS/2, we get key up/down even while editing
1920     if( IsEditingActive() )
1921         return;
1922 
1923     if( !pImpl->KeyInput( rKEvt ) )
1924     {
1925         bool bHandled = HandleKeyInput( rKEvt );
1926         if ( !bHandled )
1927             Control::KeyInput( rKEvt );
1928     }
1929 }
1930 
RequestingChildren(SvTreeListEntry * pParent)1931 void SvTreeListBox::RequestingChildren( SvTreeListEntry* pParent )
1932 {
1933     if( !pParent->HasChildren() )
1934         InsertEntry( "<dummy>", pParent );
1935 }
1936 
GetFocus()1937 void SvTreeListBox::GetFocus()
1938 {
1939     //If there is no item in the tree, draw focus.
1940     if( !First())
1941     {
1942         Invalidate();
1943     }
1944     pImpl->GetFocus();
1945     Control::GetFocus();
1946 
1947     SvTreeListEntry* pEntry = FirstSelected();
1948     if ( !pEntry )
1949     {
1950         pEntry = pImpl->GetCurrentEntry();
1951     }
1952     if (pImpl->m_pCursor)
1953     {
1954         if (pEntry != pImpl->m_pCursor)
1955             pEntry = pImpl->m_pCursor;
1956     }
1957     if ( pEntry )
1958         pImpl->CallEventListeners( VclEventId::ListboxTreeFocus, pEntry );
1959 
1960 }
1961 
LoseFocus()1962 void SvTreeListBox::LoseFocus()
1963 {
1964     // If there is no item in the tree, delete visual focus.
1965     if ( !First() )
1966         Invalidate();
1967     if ( pImpl )
1968         pImpl->LoseFocus();
1969     Control::LoseFocus();
1970 }
1971 
ModelHasCleared()1972 void SvTreeListBox::ModelHasCleared()
1973 {
1974     pImpl->m_pCursor = nullptr; // else we crash in GetFocus when editing in-place
1975     pEdCtrl.reset();
1976     pImpl->Clear();
1977     nFocusWidth = -1;
1978 
1979     nContextBmpWidthMax = 0;
1980     SetDefaultExpandedEntryBmp( GetDefaultExpandedEntryBmp() );
1981     SetDefaultCollapsedEntryBmp( GetDefaultCollapsedEntryBmp() );
1982 
1983     if( !(nTreeFlags & SvTreeFlags::FIXEDHEIGHT ))
1984         nEntryHeight = 0;
1985     AdjustEntryHeight();
1986     AdjustEntryHeight( GetDefaultExpandedEntryBmp() );
1987     AdjustEntryHeight( GetDefaultCollapsedEntryBmp() );
1988 
1989     SvListView::ModelHasCleared();
1990 }
1991 
ScrollOutputArea(short nDeltaEntries)1992 void SvTreeListBox::ScrollOutputArea( short nDeltaEntries )
1993 {
1994     if( !nDeltaEntries || !pImpl->m_aVerSBar->IsVisible() )
1995         return;
1996 
1997     long nThumb = pImpl->m_aVerSBar->GetThumbPos();
1998     long nMax = pImpl->m_aVerSBar->GetRange().Max();
1999 
2000     if( nDeltaEntries < 0 )
2001     {
2002         // move window up
2003         nDeltaEntries *= -1;
2004         long nVis = pImpl->m_aVerSBar->GetVisibleSize();
2005         long nTemp = nThumb + nVis;
2006         if( nDeltaEntries > (nMax - nTemp) )
2007             nDeltaEntries = static_cast<short>(nMax - nTemp);
2008         pImpl->PageDown( static_cast<sal_uInt16>(nDeltaEntries) );
2009     }
2010     else
2011     {
2012         if( nDeltaEntries > nThumb )
2013             nDeltaEntries = static_cast<short>(nThumb);
2014         pImpl->PageUp( static_cast<sal_uInt16>(nDeltaEntries) );
2015     }
2016     pImpl->SyncVerThumb();
2017     NotifyEndScroll();
2018 }
2019 
ScrollToAbsPos(long nPos)2020 void SvTreeListBox::ScrollToAbsPos( long nPos )
2021 {
2022     pImpl->ScrollToAbsPos( nPos );
2023 }
2024 
SetSelectionMode(SelectionMode eSelectMode)2025 void SvTreeListBox::SetSelectionMode( SelectionMode eSelectMode )
2026 {
2027     eSelMode = eSelectMode;
2028     pImpl->SetSelectionMode( eSelectMode );
2029 }
2030 
SetDragDropMode(DragDropMode nDDMode)2031 void SvTreeListBox::SetDragDropMode( DragDropMode nDDMode )
2032 {
2033     nDragDropMode = nDDMode;
2034     pImpl->SetDragDropMode( nDDMode );
2035 }
2036 
SetEntryHeight(SvTreeListEntry const * pEntry)2037 void SvTreeListBox::SetEntryHeight( SvTreeListEntry const * pEntry )
2038 {
2039     short nHeightMax=0;
2040     sal_uInt16 nCount = pEntry->ItemCount();
2041     sal_uInt16 nCur = 0;
2042     SvViewDataEntry* pViewData = GetViewDataEntry( pEntry );
2043     while( nCur < nCount )
2044     {
2045         auto nHeight = SvLBoxItem::GetHeight(pViewData, nCur);
2046         if( nHeight > nHeightMax )
2047             nHeightMax = nHeight;
2048         nCur++;
2049     }
2050 
2051     if( nHeightMax > nEntryHeight )
2052     {
2053         nEntryHeight = nHeightMax;
2054         Control::SetFont( GetFont() );
2055         pImpl->SetEntryHeight();
2056     }
2057 }
2058 
SetEntryHeight(short nHeight,bool bForce)2059 void SvTreeListBox::SetEntryHeight( short nHeight, bool bForce )
2060 {
2061     if( nHeight > nEntryHeight || bForce )
2062     {
2063         nEntryHeight = nHeight;
2064         if( nEntryHeight )
2065             nTreeFlags |= SvTreeFlags::FIXEDHEIGHT;
2066         else
2067             nTreeFlags &= ~SvTreeFlags::FIXEDHEIGHT;
2068         Control::SetFont( GetFont() );
2069         pImpl->SetEntryHeight();
2070     }
2071 }
2072 
SetEntryWidth(short nWidth)2073 void SvTreeListBox::SetEntryWidth( short nWidth )
2074 {
2075     nEntryWidth = nWidth;
2076 }
2077 
AdjustEntryHeight(const Image & rBmp)2078 void SvTreeListBox::AdjustEntryHeight( const Image& rBmp )
2079 {
2080     const Size aSize( rBmp.GetSizePixel() );
2081     if( aSize.Height() > nEntryHeight )
2082     {
2083         nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs;
2084         pImpl->SetEntryHeight();
2085     }
2086 }
2087 
AdjustEntryHeight()2088 void SvTreeListBox::AdjustEntryHeight()
2089 {
2090     Size aSize( GetTextWidth(OUString('X')), GetTextHeight() );
2091     if( aSize.Height()  >  nEntryHeight )
2092     {
2093         nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs;
2094         pImpl->SetEntryHeight();
2095     }
2096 }
2097 
Expand(SvTreeListEntry * pParent)2098 bool SvTreeListBox::Expand( SvTreeListEntry* pParent )
2099 {
2100     pHdlEntry = pParent;
2101     bool bExpanded = false;
2102     SvTLEntryFlags nFlags;
2103 
2104     if( pParent->HasChildrenOnDemand() )
2105         RequestingChildren( pParent );
2106     bool bExpandAllowed = pParent->HasChildren() && ExpandingHdl();
2107     // double check if the expander callback ended up removing all children
2108     if (pParent->HasChildren())
2109     {
2110         if (bExpandAllowed)
2111         {
2112             bExpanded = true;
2113             ExpandListEntry( pParent );
2114             pImpl->EntryExpanded( pParent );
2115             pHdlEntry = pParent;
2116             ExpandedHdl();
2117             SetAlternatingRowColors( mbAlternatingRowColors );
2118         }
2119         nFlags = pParent->GetFlags();
2120         nFlags &= ~SvTLEntryFlags::NO_NODEBMP;
2121         nFlags |= SvTLEntryFlags::HAD_CHILDREN;
2122         pParent->SetFlags( nFlags );
2123     }
2124     else
2125     {
2126         nFlags = pParent->GetFlags();
2127         nFlags |= SvTLEntryFlags::NO_NODEBMP;
2128         pParent->SetFlags( nFlags );
2129         GetModel()->InvalidateEntry( pParent ); // repaint
2130     }
2131 
2132     // #i92103#
2133     if ( bExpanded )
2134     {
2135         pImpl->CallEventListeners( VclEventId::ItemExpanded, pParent );
2136     }
2137 
2138     return bExpanded;
2139 }
2140 
Collapse(SvTreeListEntry * pParent)2141 bool SvTreeListBox::Collapse( SvTreeListEntry* pParent )
2142 {
2143     pHdlEntry = pParent;
2144     bool bCollapsed = false;
2145 
2146     if( ExpandingHdl() )
2147     {
2148         bCollapsed = true;
2149         pImpl->CollapsingEntry( pParent );
2150         CollapseListEntry( pParent );
2151         pImpl->EntryCollapsed( pParent );
2152         pHdlEntry = pParent;
2153         ExpandedHdl();
2154         SetAlternatingRowColors( mbAlternatingRowColors );
2155     }
2156 
2157     // #i92103#
2158     if ( bCollapsed )
2159     {
2160         pImpl->CallEventListeners( VclEventId::ItemCollapsed, pParent );
2161     }
2162 
2163     return bCollapsed;
2164 }
2165 
Select(SvTreeListEntry * pEntry,bool bSelect)2166 bool SvTreeListBox::Select( SvTreeListEntry* pEntry, bool bSelect )
2167 {
2168     DBG_ASSERT(pEntry,"Select: Null-Ptr");
2169     bool bRetVal = SelectListEntry( pEntry, bSelect );
2170     DBG_ASSERT(IsSelected(pEntry)==bSelect,"Select failed");
2171     if( bRetVal )
2172     {
2173         pImpl->EntrySelected( pEntry, bSelect );
2174         pHdlEntry = pEntry;
2175         if( bSelect )
2176         {
2177             SelectHdl();
2178             CallEventListeners( VclEventId::ListboxTreeSelect, pEntry);
2179         }
2180         else
2181             DeselectHdl();
2182     }
2183     return bRetVal;
2184 }
2185 
SelectChildren(SvTreeListEntry * pParent,bool bSelect)2186 sal_uLong SvTreeListBox::SelectChildren( SvTreeListEntry* pParent, bool bSelect )
2187 {
2188     pImpl->DestroyAnchor();
2189     sal_uLong nRet = 0;
2190     if( !pParent->HasChildren() )
2191         return 0;
2192     sal_uInt16 nRefDepth = pModel->GetDepth( pParent );
2193     SvTreeListEntry* pChild = FirstChild( pParent );
2194     do {
2195         nRet++;
2196         Select( pChild, bSelect );
2197         pChild = Next( pChild );
2198     } while( pChild && pModel->GetDepth( pChild ) > nRefDepth );
2199     return nRet;
2200 }
2201 
SelectAll(bool bSelect,bool)2202 void SvTreeListBox::SelectAll( bool bSelect, bool )
2203 {
2204     pImpl->SelAllDestrAnch(
2205         bSelect,
2206         true,       // delete anchor,
2207         true );     // even when using SelectionMode::Single, deselect the cursor
2208 }
2209 
ModelHasInsertedTree(SvTreeListEntry * pEntry)2210 void SvTreeListBox::ModelHasInsertedTree( SvTreeListEntry* pEntry )
2211 {
2212     sal_uInt16 nRefDepth = pModel->GetDepth( pEntry );
2213     SvTreeListEntry* pTmp = pEntry;
2214     do
2215     {
2216         ImpEntryInserted( pTmp );
2217         pTmp = Next( pTmp );
2218     } while( pTmp && nRefDepth < pModel->GetDepth( pTmp ) );
2219     pImpl->TreeInserted( pEntry );
2220 }
2221 
ModelHasInserted(SvTreeListEntry * pEntry)2222 void SvTreeListBox::ModelHasInserted( SvTreeListEntry* pEntry )
2223 {
2224     ImpEntryInserted( pEntry );
2225     pImpl->EntryInserted( pEntry );
2226 }
2227 
ModelIsMoving(SvTreeListEntry * pSource)2228 void SvTreeListBox::ModelIsMoving(SvTreeListEntry* pSource )
2229 {
2230     pImpl->MovingEntry( pSource );
2231 }
2232 
ModelHasMoved(SvTreeListEntry * pSource)2233 void SvTreeListBox::ModelHasMoved( SvTreeListEntry* pSource )
2234 {
2235     pImpl->EntryMoved( pSource );
2236 }
2237 
ModelIsRemoving(SvTreeListEntry * pEntry)2238 void SvTreeListBox::ModelIsRemoving( SvTreeListEntry* pEntry )
2239 {
2240     if(pEdEntry == pEntry)
2241         pEdEntry = nullptr;
2242 
2243     pImpl->RemovingEntry( pEntry );
2244 }
2245 
ModelHasRemoved(SvTreeListEntry * pEntry)2246 void SvTreeListBox::ModelHasRemoved( SvTreeListEntry* pEntry  )
2247 {
2248     if ( pEntry == pHdlEntry)
2249         pHdlEntry = nullptr;
2250     pImpl->EntryRemoved();
2251 }
2252 
SetCollapsedNodeBmp(const Image & rBmp)2253 void SvTreeListBox::SetCollapsedNodeBmp( const Image& rBmp)
2254 {
2255     AdjustEntryHeight( rBmp );
2256     pImpl->SetCollapsedNodeBmp( rBmp );
2257 }
2258 
SetExpandedNodeBmp(const Image & rBmp)2259 void SvTreeListBox::SetExpandedNodeBmp( const Image& rBmp )
2260 {
2261     AdjustEntryHeight( rBmp );
2262     pImpl->SetExpandedNodeBmp( rBmp );
2263 }
2264 
2265 
SetFont(const vcl::Font & rFont)2266 void SvTreeListBox::SetFont( const vcl::Font& rFont )
2267 {
2268     vcl::Font aTempFont( rFont );
2269     vcl::Font aOrigFont( GetFont() );
2270     aTempFont.SetTransparent( true );
2271     if (aTempFont == aOrigFont)
2272         return;
2273     Control::SetFont( aTempFont );
2274 
2275     aTempFont.SetColor(aOrigFont.GetColor());
2276     aTempFont.SetFillColor(aOrigFont.GetFillColor());
2277     aTempFont.SetTransparent(aOrigFont.IsTransparent());
2278 
2279     if (aTempFont == aOrigFont)
2280         return;
2281 
2282     AdjustEntryHeightAndRecalc();
2283 }
2284 
AdjustEntryHeightAndRecalc()2285 void SvTreeListBox::AdjustEntryHeightAndRecalc()
2286 {
2287     AdjustEntryHeight();
2288     // always invalidate, else things go wrong in SetEntryHeight
2289     RecalcViewData();
2290 }
2291 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)2292 void SvTreeListBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
2293 {
2294     Control::Paint(rRenderContext, rRect);
2295     if (nTreeFlags & SvTreeFlags::RECALCTABS)
2296         SetTabs();
2297     pImpl->Paint(rRenderContext, rRect);
2298 
2299     //Add visual focus draw
2300     if (First())
2301         return;
2302 
2303     if (HasFocus())
2304     {
2305         long nHeight = rRenderContext.GetTextHeight();
2306         tools::Rectangle aRect(Point(0, 0), Size(GetSizePixel().Width(), nHeight));
2307         ShowFocus(aRect);
2308     }
2309     else
2310     {
2311         HideFocus();
2312     }
2313 }
2314 
MouseButtonDown(const MouseEvent & rMEvt)2315 void SvTreeListBox::MouseButtonDown( const MouseEvent& rMEvt )
2316 {
2317     pImpl->MouseButtonDown( rMEvt );
2318 }
2319 
MouseButtonUp(const MouseEvent & rMEvt)2320 void SvTreeListBox::MouseButtonUp( const MouseEvent& rMEvt )
2321 {
2322     pImpl->MouseButtonUp( rMEvt );
2323 }
2324 
MouseMove(const MouseEvent & rMEvt)2325 void SvTreeListBox::MouseMove( const MouseEvent& rMEvt )
2326 {
2327     pImpl->MouseMove( rMEvt );
2328 }
2329 
2330 
SetUpdateMode(bool bUpdate)2331 void SvTreeListBox::SetUpdateMode( bool bUpdate )
2332 {
2333     pImpl->SetUpdateMode( bUpdate );
2334     mbUpdateAlternatingRows = bUpdate;
2335     SetAlternatingRowColors( mbAlternatingRowColors );
2336 }
2337 
SetSpaceBetweenEntries(short nOffsLogic)2338 void SvTreeListBox::SetSpaceBetweenEntries( short nOffsLogic )
2339 {
2340     if( nOffsLogic != nEntryHeightOffs )
2341     {
2342         nEntryHeight = nEntryHeight - nEntryHeightOffs;
2343         nEntryHeightOffs = nOffsLogic;
2344         nEntryHeight = nEntryHeight + nOffsLogic;
2345         AdjustEntryHeightAndRecalc();
2346         pImpl->SetEntryHeight();
2347     }
2348 }
2349 
SetCursor(SvTreeListEntry * pEntry,bool bForceNoSelect)2350 void SvTreeListBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
2351 {
2352     pImpl->SetCursor(pEntry, bForceNoSelect);
2353 }
2354 
SetCurEntry(SvTreeListEntry * pEntry)2355 void SvTreeListBox::SetCurEntry( SvTreeListEntry* pEntry )
2356 {
2357     pImpl->SetCurEntry( pEntry );
2358 }
2359 
GetExpandedNodeBmp() const2360 Image const & SvTreeListBox::GetExpandedNodeBmp( ) const
2361 {
2362     return pImpl->GetExpandedNodeBmp( );
2363 }
2364 
GetEntryPosition(SvTreeListEntry * pEntry) const2365 Point SvTreeListBox::GetEntryPosition( SvTreeListEntry* pEntry ) const
2366 {
2367     return pImpl->GetEntryPosition( pEntry );
2368 }
2369 
MakeVisible(SvTreeListEntry * pEntry)2370 void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry )
2371 {
2372     pImpl->MakeVisible(pEntry);
2373 }
2374 
MakeVisible(SvTreeListEntry * pEntry,bool bMoveToTop)2375 void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
2376 {
2377     pImpl->MakeVisible( pEntry, bMoveToTop );
2378 }
2379 
ModelHasEntryInvalidated(SvTreeListEntry * pEntry)2380 void SvTreeListBox::ModelHasEntryInvalidated( SvTreeListEntry* pEntry )
2381 {
2382 
2383     // reinitialize the separate items of the entries
2384     sal_uInt16 nCount = pEntry->ItemCount();
2385     for( sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++ )
2386     {
2387         SvLBoxItem& rItem = pEntry->GetItem( nIdx );
2388         rItem.InitViewData( this, pEntry );
2389     }
2390 
2391     // repaint
2392     pImpl->InvalidateEntry( pEntry );
2393 }
2394 
EditItemText(SvTreeListEntry * pEntry,SvLBoxString * pItem,const Selection & rSelection)2395 void SvTreeListBox::EditItemText(SvTreeListEntry* pEntry, SvLBoxString* pItem, const Selection& rSelection)
2396 {
2397     assert(pEntry && pItem);
2398     if( IsSelected( pEntry ))
2399     {
2400         pImpl->ShowCursor( false );
2401         SelectListEntry( pEntry, false );
2402         pImpl->InvalidateEntry(pEntry);
2403         SelectListEntry( pEntry, true );
2404         pImpl->ShowCursor( true );
2405     }
2406     pEdEntry = pEntry;
2407     pEdItem = pItem;
2408     SvLBoxTab* pTab = GetTab( pEntry, pItem );
2409     DBG_ASSERT(pTab,"EditItemText:Tab not found");
2410 
2411     auto nItemHeight( pItem->GetHeight(this, pEntry) );
2412     Point aPos = GetEntryPosition( pEntry );
2413     aPos.AdjustY(( nEntryHeight - nItemHeight ) / 2 );
2414     aPos.setX( GetTabPos( pEntry, pTab ) );
2415     long nOutputWidth = pImpl->GetOutputSize().Width();
2416     Size aSize( nOutputWidth - aPos.X(), nItemHeight );
2417     sal_uInt16 nPos = std::find_if( aTabs.begin(), aTabs.end(),
2418                         [pTab](const std::unique_ptr<SvLBoxTab>& p) { return p.get() == pTab; })
2419                       - aTabs.begin();
2420     if( nPos+1 < static_cast<sal_uInt16>(aTabs.size()) )
2421     {
2422         SvLBoxTab* pRightTab = aTabs[ nPos + 1 ].get();
2423         long nRight = GetTabPos( pEntry, pRightTab );
2424         if( nRight <= nOutputWidth )
2425             aSize.setWidth( nRight - aPos.X() );
2426     }
2427     Point aOrigin( GetMapMode().GetOrigin() );
2428     aPos += aOrigin; // convert to win coordinates
2429     aSize.AdjustWidth( -(aOrigin.X()) );
2430     tools::Rectangle aRect( aPos, aSize );
2431     EditText( pItem->GetText(), aRect, rSelection );
2432 }
2433 
EditEntry(SvTreeListEntry * pEntry)2434 void SvTreeListBox::EditEntry( SvTreeListEntry* pEntry )
2435 {
2436     pImpl->m_aEditClickPos = Point( -1, -1 );
2437     ImplEditEntry( pEntry );
2438 }
2439 
ImplEditEntry(SvTreeListEntry * pEntry)2440 void SvTreeListBox::ImplEditEntry( SvTreeListEntry* pEntry )
2441 {
2442     if( IsEditingActive() )
2443         EndEditing();
2444     if( !pEntry )
2445         pEntry = GetCurEntry();
2446     if( !pEntry )
2447         return;
2448 
2449     long nClickX = pImpl->m_aEditClickPos.X();
2450     bool bIsMouseTriggered = nClickX >= 0;
2451 
2452     SvLBoxString* pItem = nullptr;
2453     sal_uInt16 nCount = pEntry->ItemCount();
2454     long nTabPos, nNextTabPos = 0;
2455     for( sal_uInt16 i = 0 ; i < nCount ; i++ )
2456     {
2457         SvLBoxItem& rTmpItem = pEntry->GetItem( i );
2458         if (rTmpItem.GetType() != SvLBoxItemType::String)
2459             continue;
2460 
2461         SvLBoxTab* pTab = GetTab( pEntry, &rTmpItem );
2462         nNextTabPos = -1;
2463         if( i < nCount - 1 )
2464         {
2465             SvLBoxItem& rNextItem = pEntry->GetItem( i + 1 );
2466             SvLBoxTab* pNextTab = GetTab( pEntry, &rNextItem );
2467             nNextTabPos = pNextTab->GetPos();
2468         }
2469 
2470         if( pTab && pTab->IsEditable() )
2471         {
2472             nTabPos = pTab->GetPos();
2473             if( !bIsMouseTriggered || (nClickX > nTabPos && (nNextTabPos == -1 || nClickX < nNextTabPos ) ) )
2474             {
2475                 pItem = static_cast<SvLBoxString*>( &rTmpItem );
2476                 break;
2477             }
2478         }
2479     }
2480 
2481     Selection aSel( SELECTION_MIN, SELECTION_MAX );
2482     if( pItem && EditingEntry( pEntry, aSel ) )
2483     {
2484         SelectAll( false );
2485         MakeVisible( pEntry );
2486         EditItemText( pEntry, pItem, aSel );
2487     }
2488 }
2489 
AreChildrenTransient() const2490 bool SvTreeListBox::AreChildrenTransient() const
2491 {
2492     return pImpl->AreChildrenTransient();
2493 }
2494 
EditedText(const OUString & rStr)2495 void SvTreeListBox::EditedText( const OUString& rStr )
2496 
2497 {
2498     if(pEdEntry) // we have to check if this entry is null that means that it is removed while editing
2499     {
2500         if( EditedEntry( pEdEntry, rStr ) )
2501         {
2502             static_cast<SvLBoxString*>(pEdItem)->SetText( rStr );
2503             pModel->InvalidateEntry( pEdEntry );
2504         }
2505         if( GetSelectionCount() == 0 )
2506             Select( pEdEntry );
2507         if( GetSelectionMode() == SelectionMode::Multiple && !GetCurEntry() )
2508             SetCurEntry( pEdEntry );
2509     }
2510 }
2511 
GetDropTarget(const Point & rPos)2512 SvTreeListEntry* SvTreeListBox::GetDropTarget( const Point& rPos )
2513 {
2514     // scroll
2515     if( rPos.Y() < 12 )
2516     {
2517         ImplShowTargetEmphasis(pTargetEntry, false);
2518         ScrollOutputArea( +1 );
2519     }
2520     else
2521     {
2522         Size aSize( pImpl->GetOutputSize() );
2523         if( rPos.Y() > aSize.Height() - 12 )
2524         {
2525             ImplShowTargetEmphasis(pTargetEntry, false);
2526             ScrollOutputArea( -1 );
2527         }
2528     }
2529 
2530     SvTreeListEntry* pTarget = pImpl->GetEntry( rPos );
2531     // when dropping in a vacant space, use the last entry
2532     if( !pTarget )
2533         return LastVisible();
2534     else if( (GetDragDropMode() & DragDropMode::ENABLE_TOP) &&
2535              pTarget == First() && rPos.Y() < 6 )
2536         return nullptr;
2537 
2538     return pTarget;
2539 }
2540 
2541 
GetEntry(const Point & rPos,bool bHit) const2542 SvTreeListEntry* SvTreeListBox::GetEntry( const Point& rPos, bool bHit ) const
2543 {
2544     SvTreeListEntry* pEntry = pImpl->GetEntry( rPos );
2545     if( pEntry && bHit )
2546     {
2547         long nLine = pImpl->GetEntryLine( pEntry );
2548         if( !(pImpl->EntryReallyHit( pEntry, rPos, nLine)) )
2549             return nullptr;
2550     }
2551     return pEntry;
2552 }
2553 
GetCurEntry() const2554 SvTreeListEntry* SvTreeListBox::GetCurEntry() const
2555 {
2556     return pImpl ? pImpl->GetCurEntry() : nullptr;
2557 }
2558 
ImplInitStyle()2559 void SvTreeListBox::ImplInitStyle()
2560 {
2561     const WinBits nWindowStyle = GetStyle();
2562 
2563     nTreeFlags |= SvTreeFlags::RECALCTABS;
2564     if (nWindowStyle & WB_SORT)
2565     {
2566         GetModel()->SetSortMode(SortAscending);
2567         GetModel()->SetCompareHdl(LINK(this, SvTreeListBox, DefaultCompare));
2568     }
2569     else
2570     {
2571         GetModel()->SetSortMode(SortNone);
2572         GetModel()->SetCompareHdl(Link<const SvSortData&,sal_Int32>());
2573     }
2574     pImpl->SetStyle(nWindowStyle);
2575     pImpl->Resize();
2576     Invalidate();
2577 }
2578 
InvalidateEntry(SvTreeListEntry * pEntry)2579 void SvTreeListBox::InvalidateEntry(SvTreeListEntry* pEntry)
2580 {
2581     DBG_ASSERT(pEntry,"InvalidateEntry:No Entry");
2582     if (pEntry)
2583     {
2584         GetModel()->InvalidateEntry(pEntry);
2585     }
2586 }
2587 
PaintEntry1(SvTreeListEntry & rEntry,long nLine,vcl::RenderContext & rRenderContext)2588 void SvTreeListBox::PaintEntry1(SvTreeListEntry& rEntry, long nLine, vcl::RenderContext& rRenderContext)
2589 {
2590     tools::Rectangle aRect; // multi purpose
2591 
2592     bool bHorSBar = pImpl->HasHorScrollBar();
2593     PreparePaint(rRenderContext, rEntry);
2594 
2595     pImpl->UpdateContextBmpWidthMax(&rEntry);
2596 
2597     if (nTreeFlags & SvTreeFlags::RECALCTABS)
2598         SetTabs();
2599 
2600     short nTempEntryHeight = GetEntryHeight();
2601     long nWidth = pImpl->GetOutputSize().Width();
2602 
2603     // Did we turn on the scrollbar within PreparePaints? If yes, we have to set
2604     // the ClipRegion anew.
2605     if (!bHorSBar && pImpl->HasHorScrollBar())
2606         rRenderContext.SetClipRegion(vcl::Region(pImpl->GetClipRegionRect()));
2607 
2608     Point aEntryPos(rRenderContext.GetMapMode().GetOrigin());
2609     aEntryPos.setX( aEntryPos.X() * -1 ); // conversion document coordinates
2610     long nMaxRight = nWidth + aEntryPos.X() - 1;
2611 
2612     Color aBackupTextColor(rRenderContext.GetTextColor());
2613     vcl::Font aBackupFont(rRenderContext.GetFont());
2614     Color aBackupColor = rRenderContext.GetFillColor();
2615 
2616     bool bCurFontIsSel = false;
2617     // if a ClipRegion was set from outside, we don't have to reset it
2618     const WinBits nWindowStyle = GetStyle();
2619     const bool bHideSelection = (nWindowStyle & WB_HIDESELECTION) !=0 && !HasFocus();
2620     const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
2621 
2622     vcl::Font aHighlightFont(rRenderContext.GetFont());
2623     const Color aHighlightTextColor(rSettings.GetHighlightTextColor());
2624     aHighlightFont.SetColor(aHighlightTextColor);
2625 
2626     Size aRectSize(0, nTempEntryHeight);
2627 
2628     SvViewDataEntry* pViewDataEntry = GetViewDataEntry( &rEntry );
2629 
2630     const size_t nTabCount = aTabs.size();
2631     const size_t nItemCount = rEntry.ItemCount();
2632     size_t nCurTab = 0;
2633     size_t nCurItem = 0;
2634 
2635     while (nCurTab < nTabCount && nCurItem < nItemCount)
2636     {
2637         SvLBoxTab* pTab = aTabs[nCurTab].get();
2638         const size_t nNextTab = nCurTab + 1;
2639         SvLBoxTab* pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr;
2640         SvLBoxItem& rItem = rEntry.GetItem(nCurItem);
2641 
2642         SvLBoxTabFlags nFlags = pTab->nFlags;
2643         Size aSize(rItem.GetWidth(this, pViewDataEntry, nCurItem),
2644                    SvLBoxItem::GetHeight(pViewDataEntry, nCurItem));
2645         long nTabPos = GetTabPos(&rEntry, pTab);
2646 
2647         long nNextTabPos;
2648         if (pNextTab)
2649             nNextTabPos = GetTabPos(&rEntry, pNextTab);
2650         else
2651         {
2652             nNextTabPos = nMaxRight;
2653             if (nTabPos > nMaxRight)
2654                 nNextTabPos += 50;
2655         }
2656 
2657         long nX;
2658         if( pTab->nFlags & SvLBoxTabFlags::ADJUST_RIGHT )
2659             // avoid cutting the right edge off the tab separation
2660             nX = nTabPos + pTab->CalcOffset(aSize.Width(), (nNextTabPos - SV_TAB_BORDER - 1) - nTabPos);
2661         else
2662             nX = nTabPos + pTab->CalcOffset(aSize.Width(), nNextTabPos - nTabPos);
2663 
2664         aEntryPos.setX( nX );
2665         aEntryPos.setY( nLine );
2666 
2667         // set background pattern/color
2668 
2669         Wallpaper aWallpaper = rRenderContext.GetBackground();
2670 
2671         bool bSelTab = bool(nFlags & SvLBoxTabFlags::SHOW_SELECTION);
2672 
2673         if (pViewDataEntry->IsHighlighted() && bSelTab)
2674         {
2675             Color aNewWallColor = rSettings.GetHighlightColor();
2676             // if the face color is bright then the deactivate color is also bright
2677             // -> so you can't see any deactivate selection
2678             if (bHideSelection && !rSettings.GetFaceColor().IsBright()
2679                && aWallpaper.GetColor().IsBright() != rSettings.GetDeactiveColor().IsBright())
2680             {
2681                 aNewWallColor = rSettings.GetDeactiveColor();
2682             }
2683             // set font color to highlight
2684             if (!bCurFontIsSel)
2685             {
2686                 rRenderContext.SetTextColor(aHighlightTextColor);
2687                 rRenderContext.SetFont(aHighlightFont);
2688                 bCurFontIsSel = true;
2689             }
2690             aWallpaper.SetColor(aNewWallColor);
2691         }
2692         else  // no selection
2693         {
2694             if (bCurFontIsSel || rEntry.GetTextColor())
2695             {
2696                 bCurFontIsSel = false;
2697                 if (const auto & xCustomTextColor = rEntry.GetTextColor())
2698                     rRenderContext.SetTextColor(*xCustomTextColor);
2699                 else
2700                     rRenderContext.SetTextColor(aBackupTextColor);
2701                 rRenderContext.SetFont(aBackupFont);
2702             }
2703             else
2704             {
2705                 aWallpaper.SetColor(rEntry.GetBackColor());
2706             }
2707         }
2708 
2709         // draw background
2710         if (!(nTreeFlags & SvTreeFlags::USESEL))
2711         {
2712             // only draw the area that is used by the item
2713             aRectSize.setWidth( aSize.Width() );
2714             aRect.SetPos(aEntryPos);
2715             aRect.SetSize(aRectSize);
2716         }
2717         else
2718         {
2719             // draw from the current to the next tab
2720             if (nCurTab != 0)
2721                 aRect.SetLeft( nTabPos );
2722             else
2723                 // if we're in the 0th tab, always draw from column 0 --
2724                 // else we get problems with centered tabs
2725                 aRect.SetLeft( 0 );
2726             aRect.SetTop( nLine );
2727             aRect.SetBottom( nLine + nTempEntryHeight - 1 );
2728             if (pNextTab)
2729             {
2730                 long nRight;
2731                 nRight = GetTabPos(&rEntry, pNextTab) - 1;
2732                 if (nRight > nMaxRight)
2733                     nRight = nMaxRight;
2734                 aRect.SetRight( nRight );
2735             }
2736             else
2737             {
2738                 aRect.SetRight( nMaxRight );
2739             }
2740         }
2741         // A custom selection that starts at a tab position > 0, do not fill
2742         // the background of the 0th item, else e.g. we might not be able to
2743         // realize tab listboxes with lines.
2744         if (!(nCurTab == 0 && (nTreeFlags & SvTreeFlags::USESEL) && nFirstSelTab))
2745         {
2746             Color aBackgroundColor = aWallpaper.GetColor();
2747             if (aBackgroundColor != COL_TRANSPARENT)
2748             {
2749                 rRenderContext.SetFillColor(aBackgroundColor);
2750                 // this case may occur for smaller horizontal resizes
2751                 if (aRect.Left() < aRect.Right())
2752                     rRenderContext.DrawRect(aRect);
2753             }
2754         }
2755         // draw item
2756         // center vertically
2757         aEntryPos.AdjustY((nTempEntryHeight - aSize.Height()) / 2 );
2758         pViewDataEntry->SetPaintRectangle(aRect);
2759 
2760         rItem.Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry);
2761 
2762         // division line between tabs
2763         if (pNextTab && rItem.GetType() == SvLBoxItemType::String &&
2764             // not at the right edge of the window!
2765             aRect.Right() < nMaxRight)
2766         {
2767             aRect.SetLeft( aRect.Right() - SV_TAB_BORDER );
2768             rRenderContext.DrawRect(aRect);
2769         }
2770 
2771         rRenderContext.SetFillColor(aBackupColor);
2772 
2773         nCurItem++;
2774         nCurTab++;
2775     }
2776 
2777     if (pViewDataEntry->IsDragTarget())
2778     {
2779         rRenderContext.Push();
2780         rRenderContext.SetLineColor(rSettings.GetDeactiveColor());
2781         rRenderContext.SetFillColor(rSettings.GetDeactiveColor());
2782         rRenderContext.DrawRect(tools::Rectangle(Point(0, nLine + nTempEntryHeight - 2), Size(nWidth, 2)));
2783         rRenderContext.Pop();
2784     }
2785 
2786     if (bCurFontIsSel || rEntry.GetTextColor())
2787     {
2788         rRenderContext.SetTextColor(aBackupTextColor);
2789         rRenderContext.SetFont(aBackupFont);
2790     }
2791 
2792     sal_uInt16 nFirstDynTabPos(0);
2793     SvLBoxTab* pFirstDynamicTab = GetFirstDynamicTab(nFirstDynTabPos);
2794     long nDynTabPos = GetTabPos(&rEntry, pFirstDynamicTab);
2795     nDynTabPos += pImpl->m_nNodeBmpTabDistance;
2796     nDynTabPos += pImpl->m_nNodeBmpWidth / 2;
2797     nDynTabPos += 4; // 4 pixels of buffer, so the node bitmap is not too close
2798                      // to the next tab
2799 
2800     if( !((!(rEntry.GetFlags() & SvTLEntryFlags::NO_NODEBMP)) &&
2801         (nWindowStyle & WB_HASBUTTONS) && pFirstDynamicTab &&
2802         (rEntry.HasChildren() || rEntry.HasChildrenOnDemand())))
2803         return;
2804 
2805     // find first tab and check if the node bitmap extends into it
2806     sal_uInt16 nNextTab = nFirstDynTabPos;
2807     SvLBoxTab* pNextTab;
2808     do
2809     {
2810         nNextTab++;
2811         pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr;
2812     } while (pNextTab && pNextTab->IsDynamic());
2813 
2814     if (!(!pNextTab || (GetTabPos( &rEntry, pNextTab ) > nDynTabPos)))
2815         return;
2816 
2817     if (!((nWindowStyle & WB_HASBUTTONSATROOT) || pModel->GetDepth(&rEntry) > 0))
2818         return;
2819 
2820     Point aPos(GetTabPos(&rEntry, pFirstDynamicTab), nLine);
2821     aPos.AdjustX(pImpl->m_nNodeBmpTabDistance );
2822 
2823     const Image* pImg = nullptr;
2824 
2825     if (IsExpanded(&rEntry))
2826         pImg = &pImpl->GetExpandedNodeBmp();
2827     else
2828     {
2829         if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() &&
2830             (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)) &&
2831             pImpl->GetDontKnowNodeBmp().GetSizePixel().Width())
2832         {
2833             pImg = &pImpl->GetDontKnowNodeBmp( );
2834         }
2835         else
2836         {
2837             pImg = &pImpl->GetCollapsedNodeBmp( );
2838         }
2839     }
2840     aPos.AdjustY((nTempEntryHeight - pImg->GetSizePixel().Height()) / 2 );
2841 
2842     DrawImageFlags nStyle = DrawImageFlags::NONE;
2843     if (!IsEnabled())
2844         nStyle |= DrawImageFlags::Disable;
2845 
2846     //native
2847     bool bNativeOK = false;
2848     if (rRenderContext.IsNativeControlSupported(ControlType::ListNode, ControlPart::Entire))
2849     {
2850         ImplControlValue aControlValue;
2851         tools::Rectangle aCtrlRegion(aPos,  pImg->GetSizePixel());
2852         ControlState nState = ControlState::NONE;
2853 
2854         if (IsEnabled())
2855             nState |= ControlState::ENABLED;
2856 
2857         if (IsExpanded(&rEntry))
2858             aControlValue.setTristateVal(ButtonValue::On); //expanded node
2859         else
2860         {
2861             if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() &&
2862                 (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)) &&
2863                 pImpl->GetDontKnowNodeBmp().GetSizePixel().Width())
2864             {
2865                 aControlValue.setTristateVal( ButtonValue::DontKnow ); //don't know
2866             }
2867             else
2868             {
2869                 aControlValue.setTristateVal( ButtonValue::Off ); //collapsed node
2870             }
2871         }
2872 
2873         bNativeOK = rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion, nState, aControlValue, OUString());
2874     }
2875 
2876     if (!bNativeOK)
2877     {
2878         rRenderContext.DrawImage(aPos, *pImg ,nStyle);
2879     }
2880 }
2881 
PreparePaint(vcl::RenderContext &,SvTreeListEntry &)2882 void SvTreeListBox::PreparePaint(vcl::RenderContext& /*rRenderContext*/, SvTreeListEntry& /*rEntry*/)
2883 {
2884 }
2885 
GetFocusRect(SvTreeListEntry * pEntry,long nLine)2886 tools::Rectangle SvTreeListBox::GetFocusRect( SvTreeListEntry* pEntry, long nLine )
2887 {
2888     pImpl->UpdateContextBmpWidthMax( pEntry );
2889 
2890     Size aSize;
2891     tools::Rectangle aRect;
2892     aRect.SetTop( nLine );
2893     aSize.setHeight( GetEntryHeight() );
2894 
2895     long nRealWidth = pImpl->GetOutputSize().Width();
2896     nRealWidth -= GetMapMode().GetOrigin().X();
2897 
2898     sal_uInt16 nCurTab;
2899     SvLBoxTab* pTab = GetFirstTab( SvLBoxTabFlags::SHOW_SELECTION, nCurTab );
2900     long nTabPos = 0;
2901     if( pTab )
2902         nTabPos = GetTabPos( pEntry, pTab );
2903     long nNextTabPos;
2904     if( pTab && nCurTab < aTabs.size() - 1 )
2905     {
2906         SvLBoxTab* pNextTab = aTabs[ nCurTab + 1 ].get();
2907         nNextTabPos = GetTabPos( pEntry, pNextTab );
2908     }
2909     else
2910     {
2911         nNextTabPos = nRealWidth;
2912         if( nTabPos > nRealWidth )
2913             nNextTabPos += 50;
2914     }
2915 
2916     bool bUserSelection = bool( nTreeFlags & SvTreeFlags::USESEL );
2917     if( !bUserSelection )
2918     {
2919         if( pTab && nCurTab < pEntry->ItemCount() )
2920         {
2921             SvLBoxItem& rItem = pEntry->GetItem( nCurTab );
2922             aSize.setWidth(rItem.GetWidth(this, pEntry));
2923             if( !aSize.Width() )
2924                 aSize.setWidth( 15 );
2925             long nX = nTabPos; //GetTabPos( pEntry, pTab );
2926             // alignment
2927             nX += pTab->CalcOffset( aSize.Width(), nNextTabPos - nTabPos );
2928             aRect.SetLeft( nX );
2929             // make sure that first and last letter aren't cut off slightly
2930             aRect.SetSize( aSize );
2931             if( aRect.Left() > 0 )
2932                 aRect.AdjustLeft( -1 );
2933             aRect.AdjustRight( 1 );
2934         }
2935     }
2936     else
2937     {
2938         // if SelTab != 0, we have to calculate also
2939         if( nFocusWidth == -1 || nFirstSelTab )
2940         {
2941             SvLBoxTab* pLastTab = nullptr; // default to select whole width
2942 
2943             sal_uInt16 nLastTab;
2944             GetLastTab(SvLBoxTabFlags::SHOW_SELECTION,nLastTab);
2945             nLastTab++;
2946             if( nLastTab < aTabs.size() ) // is there another one?
2947                 pLastTab = aTabs[ nLastTab ].get();
2948 
2949             aSize.setWidth( pLastTab ? pLastTab->GetPos() : 0x0fffffff );
2950             nFocusWidth = static_cast<short>(aSize.Width());
2951             if( pTab )
2952                 nFocusWidth = nFocusWidth - static_cast<short>(nTabPos); //pTab->GetPos();
2953         }
2954         else
2955         {
2956             aSize.setWidth( nFocusWidth );
2957             if( pTab )
2958             {
2959                 if( nCurTab )
2960                     aSize.AdjustWidth(nTabPos );
2961                 else
2962                     aSize.AdjustWidth(pTab->GetPos() ); // Tab0 always from the leftmost position
2963             }
2964         }
2965         // if selection starts with 0th tab, draw from column 0 on
2966         if( nCurTab != 0 )
2967         {
2968             aRect.SetLeft( nTabPos );
2969             aSize.AdjustWidth( -nTabPos );
2970         }
2971         aRect.SetSize( aSize );
2972     }
2973     // adjust right edge because of clipping
2974     if( aRect.Right() >= nRealWidth )
2975     {
2976         aRect.SetRight( nRealWidth-1 );
2977         nFocusWidth = static_cast<short>(aRect.GetWidth());
2978     }
2979     return aRect;
2980 }
2981 
2982 
GetTabPos(SvTreeListEntry * pEntry,SvLBoxTab * pTab)2983 sal_IntPtr SvTreeListBox::GetTabPos( SvTreeListEntry* pEntry, SvLBoxTab* pTab)
2984 {
2985     assert(pTab);
2986     sal_IntPtr nPos = pTab->GetPos();
2987     if( pTab->IsDynamic() )
2988     {
2989         sal_uInt16 nDepth = pModel->GetDepth( pEntry );
2990         nDepth = nDepth * static_cast<sal_uInt16>(nIndent);
2991         nPos += static_cast<sal_IntPtr>(nDepth);
2992     }
2993     return nPos;
2994 }
2995 
GetItem_Impl(SvTreeListEntry * pEntry,long nX,SvLBoxTab ** ppTab)2996 SvLBoxItem* SvTreeListBox::GetItem_Impl( SvTreeListEntry* pEntry, long nX,
2997     SvLBoxTab** ppTab )
2998 {
2999     SvLBoxItem* pItemClicked = nullptr;
3000     sal_uInt16 nTabCount = aTabs.size();
3001     sal_uInt16 nItemCount = pEntry->ItemCount();
3002     SvLBoxTab* pTab = aTabs.front().get();
3003     SvLBoxItem* pItem = &pEntry->GetItem(0);
3004     sal_uInt16 nNextItem = 1;
3005     nX -= GetMapMode().GetOrigin().X();
3006     long nRealWidth = pImpl->GetOutputSize().Width();
3007     nRealWidth -= GetMapMode().GetOrigin().X();
3008 
3009     while( true )
3010     {
3011         SvLBoxTab* pNextTab=nNextItem<nTabCount ? aTabs[nNextItem].get() : nullptr;
3012         long nStart = GetTabPos( pEntry, pTab );
3013 
3014         long nNextTabPos;
3015         if( pNextTab )
3016             nNextTabPos = GetTabPos( pEntry, pNextTab );
3017         else
3018         {
3019             nNextTabPos = nRealWidth;
3020             if( nStart > nRealWidth )
3021                 nNextTabPos += 50;
3022         }
3023 
3024         auto nItemWidth(pItem->GetWidth(this, pEntry));
3025         nStart += pTab->CalcOffset(nItemWidth, nNextTabPos - nStart);
3026         auto nLen = nItemWidth;
3027         if( pNextTab )
3028         {
3029             long nTabWidth = GetTabPos( pEntry, pNextTab ) - nStart;
3030             if( nTabWidth < nLen )
3031                 nLen = nTabWidth;
3032         }
3033 
3034         if( nX >= nStart && nX < (nStart+nLen ) )
3035         {
3036             pItemClicked = pItem;
3037             if( ppTab )
3038             {
3039                 *ppTab = pTab;
3040                 break;
3041             }
3042         }
3043         if( nNextItem >= nItemCount || nNextItem >= nTabCount)
3044             break;
3045         pTab = aTabs[ nNextItem ].get();
3046         pItem = &pEntry->GetItem( nNextItem );
3047         nNextItem++;
3048     }
3049     return pItemClicked;
3050 }
3051 
getPreferredDimensions(std::vector<long> & rWidths) const3052 long SvTreeListBox::getPreferredDimensions(std::vector<long> &rWidths) const
3053 {
3054     long nHeight = 0;
3055     rWidths.clear();
3056     SvTreeListEntry* pEntry = First();
3057     while (pEntry)
3058     {
3059         sal_uInt16 nCount = pEntry->ItemCount();
3060         sal_uInt16 nCurPos = 0;
3061         if (nCount > rWidths.size())
3062             rWidths.resize(nCount);
3063         while (nCurPos < nCount)
3064         {
3065             SvLBoxItem& rItem = pEntry->GetItem( nCurPos );
3066             auto nWidth = rItem.GetWidth(this, pEntry);
3067             if (nWidth)
3068             {
3069                 nWidth += SV_TAB_BORDER * 2;
3070                 if (nWidth > rWidths[nCurPos])
3071                    rWidths[nCurPos] = nWidth;
3072             }
3073             ++nCurPos;
3074         }
3075         pEntry = Next( pEntry );
3076         nHeight += GetEntryHeight();
3077     }
3078     return nHeight;
3079 }
3080 
GetOptimalSize() const3081 Size SvTreeListBox::GetOptimalSize() const
3082 {
3083     std::vector<long> aWidths;
3084     Size aRet(0, getPreferredDimensions(aWidths));
3085     for (long aWidth : aWidths)
3086         aRet.AdjustWidth(aWidth );
3087     if (GetStyle() & WB_BORDER)
3088     {
3089         aRet.AdjustWidth(StyleSettings::GetBorderSize() * 2 );
3090         aRet.AdjustHeight(StyleSettings::GetBorderSize() * 2 );
3091     }
3092     long nMinWidth = nMinWidthInChars * approximate_char_width();
3093     aRet.setWidth( std::max(aRet.Width(), nMinWidth) );
3094 
3095     if (GetStyle() & WB_VSCROLL)
3096         aRet.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize());
3097 
3098     return aRet;
3099 }
3100 
SetAlternatingRowColors(bool bEnable)3101 void SvTreeListBox::SetAlternatingRowColors( bool bEnable )
3102 {
3103     if( !mbUpdateAlternatingRows )
3104     {
3105         mbAlternatingRowColors = bEnable;
3106         return;
3107     }
3108 
3109     if( bEnable )
3110     {
3111         SvTreeListEntry* pEntry = pModel->First();
3112         for(size_t i = 0; pEntry; ++i)
3113         {
3114             pEntry->SetBackColor( i % 2 == 0 ? GetBackground().GetColor() : GetSettings().GetStyleSettings().GetAlternatingRowColor());
3115             SvTreeListEntry *pNextEntry = nullptr;
3116             if( IsExpanded( pEntry ) )
3117                 pNextEntry = pModel->FirstChild( pEntry );
3118             else
3119                 pNextEntry = pEntry->NextSibling();
3120 
3121             if( !pNextEntry )
3122                 pEntry = pModel->Next( pEntry );
3123             else
3124                 pEntry = pNextEntry;
3125         }
3126     }
3127     else if( mbAlternatingRowColors )
3128         for(SvTreeListEntry* pEntry = pModel->First(); pEntry; pEntry = pModel->Next(pEntry))
3129             pEntry->SetBackColor( GetBackground().GetColor() );
3130 
3131     mbAlternatingRowColors = bEnable;
3132     pImpl->UpdateAll(true);
3133 }
3134 
SetForceMakeVisible(bool bEnable)3135 void SvTreeListBox::SetForceMakeVisible( bool bEnable )
3136 {
3137     pImpl->SetForceMakeVisible(bEnable);
3138 }
3139 
GetItem(SvTreeListEntry * pEntry,long nX,SvLBoxTab ** ppTab)3140 SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,long nX,SvLBoxTab** ppTab)
3141 {
3142     return GetItem_Impl( pEntry, nX, ppTab );
3143 }
3144 
GetItem(SvTreeListEntry * pEntry,long nX)3145 SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,long nX )
3146 {
3147     SvLBoxTab* pDummyTab;
3148     return GetItem_Impl( pEntry, nX, &pDummyTab );
3149 }
3150 
AddTab(long nTabPos,SvLBoxTabFlags nFlags)3151 void SvTreeListBox::AddTab(long nTabPos, SvLBoxTabFlags nFlags )
3152 {
3153     nFocusWidth = -1;
3154     SvLBoxTab* pTab = new SvLBoxTab( nTabPos, nFlags );
3155     aTabs.emplace_back( pTab );
3156     if( nTreeFlags & SvTreeFlags::USESEL )
3157     {
3158         sal_uInt16 nPos = aTabs.size() - 1;
3159         if( nPos >= nFirstSelTab && nPos <= nLastSelTab )
3160             pTab->nFlags |= SvLBoxTabFlags::SHOW_SELECTION;
3161         else
3162             // string items usually have to be selected -- turn this off
3163             // explicitly
3164             pTab->nFlags &= ~SvLBoxTabFlags::SHOW_SELECTION;
3165     }
3166 }
3167 
3168 
GetFirstDynamicTab(sal_uInt16 & rPos) const3169 SvLBoxTab* SvTreeListBox::GetFirstDynamicTab( sal_uInt16& rPos ) const
3170 {
3171     sal_uInt16 nCurTab = 0;
3172     sal_uInt16 nTabCount = aTabs.size();
3173     while( nCurTab < nTabCount )
3174     {
3175         SvLBoxTab* pTab = aTabs[nCurTab].get();
3176         if( pTab->nFlags & SvLBoxTabFlags::DYNAMIC )
3177         {
3178             rPos = nCurTab;
3179             return pTab;
3180         }
3181         nCurTab++;
3182     }
3183     return nullptr;
3184 }
3185 
GetFirstDynamicTab() const3186 SvLBoxTab* SvTreeListBox::GetFirstDynamicTab() const
3187 {
3188     sal_uInt16 nDummy;
3189     return GetFirstDynamicTab( nDummy );
3190 }
3191 
GetTab(SvTreeListEntry const * pEntry,SvLBoxItem const * pItem) const3192 SvLBoxTab* SvTreeListBox::GetTab( SvTreeListEntry const * pEntry, SvLBoxItem const * pItem) const
3193 {
3194     sal_uInt16 nPos = pEntry->GetPos( pItem );
3195     return aTabs[ nPos ].get();
3196 }
3197 
ClearTabList()3198 void SvTreeListBox::ClearTabList()
3199 {
3200     aTabs.clear();
3201 }
3202 
3203 
GetOutputSizePixel() const3204 Size SvTreeListBox::GetOutputSizePixel() const
3205 {
3206     Size aSize = pImpl->GetOutputSize();
3207     return aSize;
3208 }
3209 
NotifyEndScroll()3210 void SvTreeListBox::NotifyEndScroll()
3211 {
3212 }
3213 
NotifyScrolled()3214 void SvTreeListBox::NotifyScrolled()
3215 {
3216     aScrolledHdl.Call( this );
3217 }
3218 
Invalidate(InvalidateFlags nInvalidateFlags)3219 void SvTreeListBox::Invalidate( InvalidateFlags nInvalidateFlags )
3220 {
3221     if (!pImpl)
3222         return;
3223     if( nFocusWidth == -1 )
3224         // to make sure that the control doesn't show the wrong focus rectangle
3225         // after painting
3226         pImpl->RecalcFocusRect();
3227     Control::Invalidate( nInvalidateFlags );
3228     pImpl->Invalidate();
3229 }
3230 
Invalidate(const tools::Rectangle & rRect,InvalidateFlags nInvalidateFlags)3231 void SvTreeListBox::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nInvalidateFlags )
3232 {
3233     if( nFocusWidth == -1 )
3234         // to make sure that the control doesn't show the wrong focus rectangle
3235         // after painting
3236         pImpl->RecalcFocusRect();
3237     Control::Invalidate( rRect, nInvalidateFlags );
3238 }
3239 
3240 
SetHighlightRange(sal_uInt16 nStart,sal_uInt16 nEnd)3241 void SvTreeListBox::SetHighlightRange( sal_uInt16 nStart, sal_uInt16 nEnd)
3242 {
3243 
3244     sal_uInt16 nTemp;
3245     nTreeFlags |= SvTreeFlags::USESEL;
3246     if( nStart > nEnd )
3247     {
3248         nTemp = nStart;
3249         nStart = nEnd;
3250         nEnd = nTemp;
3251     }
3252     // select all tabs that lie within the area
3253     nTreeFlags |= SvTreeFlags::RECALCTABS;
3254     nFirstSelTab = nStart;
3255     nLastSelTab = nEnd;
3256     pImpl->RecalcFocusRect();
3257 }
3258 
Command(const CommandEvent & rCEvt)3259 void SvTreeListBox::Command(const CommandEvent& rCEvt)
3260 {
3261     if (!aPopupMenuHdl.Call(rCEvt))
3262         pImpl->Command(rCEvt);
3263     //pass at least alt press/release to parent impl
3264     if (rCEvt.GetCommand() == CommandEventId::ModKeyChange)
3265         Control::Command(rCEvt);
3266 }
3267 
RemoveParentKeepChildren(SvTreeListEntry * pParent)3268 void SvTreeListBox::RemoveParentKeepChildren( SvTreeListEntry* pParent )
3269 {
3270     assert(pParent);
3271     SvTreeListEntry* pNewParent = GetParent( pParent );
3272     if( pParent->HasChildren())
3273     {
3274         SvTreeListEntry* pChild = FirstChild( pParent );
3275         while( pChild )
3276         {
3277             pModel->Move( pChild, pNewParent, TREELIST_APPEND );
3278             pChild = FirstChild( pParent );
3279         }
3280     }
3281     pModel->Remove( pParent );
3282 }
3283 
GetFirstTab(SvLBoxTabFlags nFlagMask,sal_uInt16 & rPos)3284 SvLBoxTab* SvTreeListBox::GetFirstTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rPos )
3285 {
3286     sal_uInt16 nTabCount = aTabs.size();
3287     for( sal_uInt16 nPos = 0; nPos < nTabCount; nPos++ )
3288     {
3289         SvLBoxTab* pTab = aTabs[ nPos ].get();
3290         if( pTab->nFlags & nFlagMask )
3291         {
3292             rPos = nPos;
3293             return pTab;
3294         }
3295     }
3296     rPos = 0xffff;
3297     return nullptr;
3298 }
3299 
GetLastTab(SvLBoxTabFlags nFlagMask,sal_uInt16 & rTabPos)3300 void SvTreeListBox::GetLastTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rTabPos )
3301 {
3302     sal_uInt16 nPos = static_cast<sal_uInt16>(aTabs.size());
3303     while( nPos )
3304     {
3305         --nPos;
3306         SvLBoxTab* pTab = aTabs[ nPos ].get();
3307         if( pTab->nFlags & nFlagMask )
3308         {
3309             rTabPos = nPos;
3310             return;
3311         }
3312     }
3313     rTabPos = 0xffff;
3314 }
3315 
RequestHelp(const HelpEvent & rHEvt)3316 void SvTreeListBox::RequestHelp( const HelpEvent& rHEvt )
3317 {
3318     if( !pImpl->RequestHelp( rHEvt ) )
3319         Control::RequestHelp( rHEvt );
3320 }
3321 
DefaultCompare(const SvLBoxString * pLeftText,const SvLBoxString * pRightText)3322 sal_Int32 SvTreeListBox::DefaultCompare(const SvLBoxString* pLeftText, const SvLBoxString* pRightText)
3323 {
3324     OUString aLeft = pLeftText ? pLeftText->GetText() : OUString();
3325     OUString aRight = pRightText ? pRightText->GetText() : OUString();
3326     pImpl->UpdateStringSorter();
3327     return pImpl->m_pStringSorter->compare(aLeft, aRight);
3328 }
3329 
IMPL_LINK(SvTreeListBox,DefaultCompare,const SvSortData &,rData,sal_Int32)3330 IMPL_LINK( SvTreeListBox, DefaultCompare, const SvSortData&, rData, sal_Int32 )
3331 {
3332     const SvTreeListEntry* pLeft = rData.pLeft;
3333     const SvTreeListEntry* pRight = rData.pRight;
3334     const SvLBoxString* pLeftText = static_cast<const SvLBoxString*>(pLeft->GetFirstItem(SvLBoxItemType::String));
3335     const SvLBoxString* pRightText = static_cast<const SvLBoxString*>(pRight->GetFirstItem(SvLBoxItemType::String));
3336     return DefaultCompare(pLeftText, pRightText);
3337 }
3338 
ModelNotification(SvListAction nActionId,SvTreeListEntry * pEntry1,SvTreeListEntry * pEntry2,sal_uLong nPos)3339 void SvTreeListBox::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1,
3340                         SvTreeListEntry* pEntry2, sal_uLong nPos )
3341 {
3342     SolarMutexGuard aSolarGuard;
3343 
3344     if( nActionId == SvListAction::CLEARING )
3345         CancelTextEditing();
3346 
3347     SvListView::ModelNotification( nActionId, pEntry1, pEntry2, nPos );
3348     switch( nActionId )
3349     {
3350         case SvListAction::INSERTED:
3351         {
3352             SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry1->GetFirstItem( SvLBoxItemType::ContextBmp ) );
3353             if ( !pBmpItem )
3354                 break;
3355             const Image& rBitmap1( pBmpItem->GetBitmap1() );
3356             const Image& rBitmap2( pBmpItem->GetBitmap2() );
3357             short nMaxWidth = short( std::max( rBitmap1.GetSizePixel().Width(), rBitmap2.GetSizePixel().Width() ) );
3358             nMaxWidth = pImpl->UpdateContextBmpWidthVector( pEntry1, nMaxWidth );
3359             if( nMaxWidth > nContextBmpWidthMax )
3360             {
3361                 nContextBmpWidthMax = nMaxWidth;
3362                 SetTabs();
3363             }
3364             if (get_width_request() == -1)
3365                 queue_resize();
3366         }
3367         break;
3368 
3369         case SvListAction::RESORTING:
3370             SetUpdateMode( false );
3371             break;
3372 
3373         case SvListAction::RESORTED:
3374             // after a selection: show first entry and also keep the selection
3375             MakeVisible( pModel->First(), true );
3376             SetUpdateMode( true );
3377             break;
3378 
3379         case SvListAction::CLEARED:
3380             if( IsUpdateMode() )
3381                 Update();
3382             break;
3383 
3384         default: break;
3385     }
3386 }
3387 
EndSelection()3388 void SvTreeListBox::EndSelection()
3389 {
3390     pImpl->EndSelection();
3391 }
3392 
GetVScroll()3393 ScrollBar *SvTreeListBox::GetVScroll()
3394 {
3395     return pImpl->m_aVerSBar.get();
3396 }
3397 
GetHScroll()3398 ScrollBar *SvTreeListBox::GetHScroll()
3399 {
3400     return pImpl->m_aHorSBar.get();
3401 }
3402 
EnableAsyncDrag(bool b)3403 void SvTreeListBox::EnableAsyncDrag( bool b )
3404 {
3405     pImpl->EnableAsyncDrag( b );
3406 }
3407 
GetFirstEntryInView() const3408 SvTreeListEntry* SvTreeListBox::GetFirstEntryInView() const
3409 {
3410     return GetEntry( Point() );
3411 }
3412 
GetNextEntryInView(SvTreeListEntry * pEntry) const3413 SvTreeListEntry* SvTreeListBox::GetNextEntryInView(SvTreeListEntry* pEntry ) const
3414 {
3415     SvTreeListEntry* pNext = NextVisible( pEntry );
3416     if( pNext )
3417     {
3418         Point aPos( GetEntryPosition(pNext) );
3419         const Size& rSize = pImpl->GetOutputSize();
3420         if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() )
3421             return nullptr;
3422     }
3423     return pNext;
3424 }
3425 
GetPrevEntryInView(SvTreeListEntry * pEntry) const3426 SvTreeListEntry* SvTreeListBox::GetPrevEntryInView(SvTreeListEntry* pEntry ) const
3427 {
3428     SvTreeListEntry* pPrev = PrevVisible( pEntry );
3429     if( pPrev )
3430     {
3431         Point aPos( GetEntryPosition(pPrev) );
3432         const Size& rSize = pImpl->GetOutputSize();
3433         if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() )
3434             return nullptr;
3435     }
3436     return pPrev;
3437 }
3438 
GetLastEntryInView() const3439 SvTreeListEntry* SvTreeListBox::GetLastEntryInView() const
3440 {
3441     SvTreeListEntry* pEntry = GetFirstEntryInView();
3442     SvTreeListEntry* pNext = nullptr;
3443     while( pEntry )
3444     {
3445         pNext = NextVisible( pEntry );
3446         if( pNext )
3447         {
3448           Point aPos( GetEntryPosition(pNext) );
3449           const Size& rSize = pImpl->GetOutputSize();
3450           if( aPos.Y() < 0 || aPos.Y() + GetEntryHeight() >= rSize.Height() )
3451               break;
3452           else
3453               pEntry = pNext;
3454         }
3455         else
3456             break;
3457     }
3458     return pEntry;
3459 }
3460 
ShowFocusRect(const SvTreeListEntry * pEntry)3461 void SvTreeListBox::ShowFocusRect( const SvTreeListEntry* pEntry )
3462 {
3463     pImpl->ShowFocusRect( pEntry );
3464 }
3465 
DataChanged(const DataChangedEvent & rDCEvt)3466 void SvTreeListBox::DataChanged( const DataChangedEvent& rDCEvt )
3467 {
3468     if( (rDCEvt.GetType()==DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
3469     {
3470         nEntryHeight = 0;   // _together_ with true of 1. par (bFont) of InitSettings() a zero-height
3471                             //  forces complete recalc of heights!
3472         InitSettings();
3473         Invalidate();
3474     }
3475     else
3476         Control::DataChanged( rDCEvt );
3477 }
3478 
StateChanged(StateChangedType eType)3479 void SvTreeListBox::StateChanged( StateChangedType eType )
3480 {
3481     if( eType == StateChangedType::Enable )
3482         Invalidate( InvalidateFlags::Children );
3483 
3484     Control::StateChanged( eType );
3485 
3486     if ( eType == StateChangedType::Style )
3487         ImplInitStyle();
3488 }
3489 
ApplySettings(vcl::RenderContext & rRenderContext)3490 void SvTreeListBox::ApplySettings(vcl::RenderContext& rRenderContext)
3491 {
3492     SetPointFont(rRenderContext, GetPointFont(*this));
3493 
3494     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
3495     rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
3496     rRenderContext.SetTextFillColor();
3497     rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
3498 
3499     // always try to re-create default-SvLBoxButtonData
3500     if (pCheckButtonData && pCheckButtonData->HasDefaultImages())
3501         pCheckButtonData->SetDefaultImages(this);
3502 }
3503 
InitSettings()3504 void SvTreeListBox::InitSettings()
3505 {
3506     const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
3507     vcl::Font aFont = rStyleSettings.GetFieldFont();
3508     aFont.SetColor(rStyleSettings.GetWindowTextColor());
3509     SetPointFont(*this, aFont);
3510     AdjustEntryHeightAndRecalc();
3511 
3512     SetTextColor(rStyleSettings.GetFieldTextColor());
3513     SetTextFillColor();
3514 
3515     SetBackground(rStyleSettings.GetFieldColor());
3516 
3517     // always try to re-create default-SvLBoxButtonData
3518     if( pCheckButtonData && pCheckButtonData->HasDefaultImages() )
3519         pCheckButtonData->SetDefaultImages(this);
3520 }
3521 
IsCellFocusEnabled() const3522 bool SvTreeListBox::IsCellFocusEnabled() const
3523 {
3524     return pImpl->IsCellFocusEnabled();
3525 }
3526 
SetCurrentTabPos(sal_uInt16 _nNewPos)3527 bool SvTreeListBox::SetCurrentTabPos( sal_uInt16 _nNewPos )
3528 {
3529     return pImpl->SetCurrentTabPos( _nNewPos );
3530 }
3531 
GetCurrentTabPos() const3532 sal_uInt16 SvTreeListBox::GetCurrentTabPos() const
3533 {
3534     return pImpl->GetCurrentTabPos();
3535 }
3536 
CreateContextMenu()3537 VclPtr<PopupMenu> SvTreeListBox::CreateContextMenu()
3538 {
3539     return nullptr;
3540 }
3541 
ExecuteContextMenuAction(sal_uInt16)3542 void SvTreeListBox::ExecuteContextMenuAction( sal_uInt16 )
3543 {
3544     SAL_INFO( "svtools.contnr", "SvTreeListBox::ExecuteContextMenuAction(): now there's happening nothing!" );
3545 }
3546 
EnableContextMenuHandling()3547 void SvTreeListBox::EnableContextMenuHandling()
3548 {
3549     assert(pImpl && "-SvTreeListBox::EnableContextMenuHandling(): No implementation!");
3550     pImpl->m_bContextMenuHandling = true;
3551 }
3552 
CreateAccessible()3553 css::uno::Reference< XAccessible > SvTreeListBox::CreateAccessible()
3554 {
3555     vcl::Window* pParent = GetAccessibleParentWindow();
3556     DBG_ASSERT( pParent, "SvTreeListBox::CreateAccessible - accessible parent not found" );
3557 
3558     css::uno::Reference< XAccessible > xAccessible;
3559     if ( pParent )
3560     {
3561         css::uno::Reference< XAccessible > xAccParent = pParent->GetAccessible();
3562         if ( xAccParent.is() )
3563         {
3564             // need to be done here to get the vclxwindow later on in the accessible
3565             css::uno::Reference< css::awt::XWindowPeer > xTemp(GetComponentInterface());
3566             xAccessible = pImpl->m_aFactoryAccess.getFactory().createAccessibleTreeListBox( *this, xAccParent );
3567         }
3568     }
3569     return xAccessible;
3570 }
3571 
FillAccessibleEntryStateSet(SvTreeListEntry * pEntry,::utl::AccessibleStateSetHelper & rStateSet) const3572 void SvTreeListBox::FillAccessibleEntryStateSet( SvTreeListEntry* pEntry, ::utl::AccessibleStateSetHelper& rStateSet ) const
3573 {
3574     assert(pEntry && "SvTreeListBox::FillAccessibleEntryStateSet: invalid entry");
3575 
3576     if ( pEntry->HasChildrenOnDemand() || pEntry->HasChildren() )
3577     {
3578         rStateSet.AddState( AccessibleStateType::EXPANDABLE );
3579         if ( IsExpanded( pEntry ) )
3580             rStateSet.AddState( sal_Int16(AccessibleStateType::EXPANDED) );
3581     }
3582 
3583     if ( GetCheckButtonState( pEntry ) == SvButtonState::Checked )
3584         rStateSet.AddState( AccessibleStateType::CHECKED );
3585     if ( IsEntryVisible( pEntry ) )
3586         rStateSet.AddState( AccessibleStateType::VISIBLE );
3587     if ( IsSelected( pEntry ) )
3588         rStateSet.AddState( AccessibleStateType::SELECTED );
3589     if ( IsEnabled() )
3590     {
3591         rStateSet.AddState( AccessibleStateType::ENABLED );
3592         rStateSet.AddState( AccessibleStateType::FOCUSABLE );
3593         rStateSet.AddState( AccessibleStateType::SELECTABLE );
3594         SvViewDataEntry* pViewDataNewCur = GetViewDataEntry(pEntry);
3595         if (pViewDataNewCur && pViewDataNewCur->HasFocus())
3596             rStateSet.AddState( AccessibleStateType::FOCUSED );
3597     }
3598 }
3599 
GetBoundingRect(SvTreeListEntry * pEntry)3600 tools::Rectangle SvTreeListBox::GetBoundingRect( SvTreeListEntry* pEntry )
3601 {
3602     Point aPos = GetEntryPosition( pEntry );
3603     tools::Rectangle aRect = GetFocusRect( pEntry, aPos.Y() );
3604     return aRect;
3605 }
3606 
CallImplEventListeners(VclEventId nEvent,void * pData)3607 void SvTreeListBox::CallImplEventListeners(VclEventId nEvent, void* pData)
3608 {
3609     CallEventListeners(nEvent, pData);
3610 }
3611 
set_min_width_in_chars(sal_Int32 nChars)3612 void SvTreeListBox::set_min_width_in_chars(sal_Int32 nChars)
3613 {
3614     nMinWidthInChars = nChars;
3615     queue_resize();
3616 }
3617 
set_property(const OString & rKey,const OUString & rValue)3618 bool SvTreeListBox::set_property(const OString &rKey, const OUString &rValue)
3619 {
3620     if (rKey == "min-width-chars")
3621     {
3622         set_min_width_in_chars(rValue.toInt32());
3623     }
3624     else if (rKey == "enable-tree-lines")
3625     {
3626         auto nStyle = GetStyle();
3627         nStyle &= ~(WB_HASLINES | WB_HASLINESATROOT);
3628         if (toBool(rValue))
3629             nStyle |= (WB_HASLINES | WB_HASLINESATROOT);
3630         SetStyle(nStyle);
3631     }
3632     else if (rKey == "show-expanders")
3633     {
3634         auto nStyle = GetStyle();
3635         nStyle &= ~(WB_HASBUTTONS | WB_HASBUTTONSATROOT);
3636         if (toBool(rValue))
3637             nStyle |= (WB_HASBUTTONS | WB_HASBUTTONSATROOT);
3638         SetStyle(nStyle);
3639     }
3640     else if (rKey == "rules-hint")
3641     {
3642         SetAlternatingRowColors(toBool(rValue));
3643     }
3644     else if (rKey == "enable-search")
3645     {
3646         SetQuickSearch(toBool(rValue));
3647     }
3648     else if (rKey == "reorderable")
3649     {
3650         if (toBool(rValue))
3651             SetDragDropMode(DragDropMode::CTRL_MOVE | DragDropMode::ENABLE_TOP);
3652     }
3653     else
3654         return Control::set_property(rKey, rValue);
3655     return true;
3656 }
3657 
GetUITestFactory() const3658 FactoryFunction SvTreeListBox::GetUITestFactory() const
3659 {
3660     return TreeListUIObject::create;
3661 }
3662 
3663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3664