/src/libreoffice/vcl/source/treelist/treelist.cxx
Line | Count | Source |
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 | | #include <vcl/toolkit/treelist.hxx> |
21 | | #include <vcl/toolkit/treelistentry.hxx> |
22 | | #include <vcl/toolkit/viewdataentry.hxx> |
23 | | #include <tools/debug.hxx> |
24 | | #include <osl/diagnose.h> |
25 | | |
26 | | #include <cassert> |
27 | | #include <memory> |
28 | | #include <unordered_map> |
29 | | #include <utility> |
30 | | |
31 | | |
32 | | typedef std::unordered_map<SvTreeListEntry*, std::unique_ptr<SvViewDataEntry>> SvDataTable; |
33 | | |
34 | | struct SvListView::Impl |
35 | | { |
36 | | SvListView & m_rThis; |
37 | | |
38 | | SvDataTable m_DataTable; // Mapping SvTreeListEntry -> ViewData |
39 | | |
40 | | sal_uInt32 m_nVisibleCount; |
41 | | sal_uInt32 m_nSelectionCount; |
42 | | bool m_bVisPositionsValid; |
43 | | |
44 | | explicit Impl(SvListView & rThis) |
45 | 0 | : m_rThis(rThis) |
46 | 0 | , m_nVisibleCount(0) |
47 | 0 | , m_nSelectionCount(0) |
48 | 0 | , m_bVisPositionsValid(false) |
49 | 0 | {} |
50 | | |
51 | | void InitTable(); |
52 | | void RemoveViewData( SvTreeListEntry* pParent ); |
53 | | |
54 | | void ActionMoving(SvTreeListEntry* pEntry); |
55 | | void ActionMoved(); |
56 | | void ActionInserted(SvTreeListEntry* pEntry); |
57 | | void ActionInsertedTree(SvTreeListEntry* pEntry); |
58 | | void ActionRemoving(SvTreeListEntry* pEntry); |
59 | | }; |
60 | | |
61 | | |
62 | | SvTreeList::SvTreeList(SvListView& listView) : |
63 | 0 | mrOwnerListView(listView), |
64 | 0 | mbEnableInvalidate(true) |
65 | 0 | { |
66 | 0 | nEntryCount = 0; |
67 | 0 | bAbsPositionsValid = false; |
68 | 0 | pRootItem.reset(new SvTreeListEntry); |
69 | 0 | eSortMode = SvSortMode::None; |
70 | 0 | } |
71 | | |
72 | | SvTreeList::~SvTreeList() |
73 | 0 | { |
74 | 0 | } |
75 | | |
76 | | void SvTreeList::Broadcast( |
77 | | SvListAction nActionId, |
78 | | SvTreeListEntry* pEntry1, |
79 | | SvTreeListEntry* pEntry2, |
80 | | sal_uInt32 nPos |
81 | | ) |
82 | 0 | { |
83 | 0 | mrOwnerListView.ModelNotification(nActionId, pEntry1, pEntry2, nPos); |
84 | 0 | } |
85 | | |
86 | | // an entry is visible if all parents are expanded |
87 | | bool SvTreeList::IsEntryVisible( const SvListView* pView, SvTreeListEntry* pEntry ) const |
88 | 0 | { |
89 | 0 | assert(pView && pEntry && "IsVisible:Invalid Params"); |
90 | 0 | bool bRetVal = false; |
91 | 0 | do |
92 | 0 | { |
93 | 0 | if ( pEntry == pRootItem.get() ) |
94 | 0 | { |
95 | 0 | bRetVal = true; |
96 | 0 | break; |
97 | 0 | } |
98 | 0 | pEntry = pEntry->pParent; |
99 | 0 | } while( pView->IsExpanded( pEntry ) ); |
100 | 0 | return bRetVal; |
101 | 0 | } |
102 | | |
103 | | sal_uInt16 SvTreeList::GetDepth( const SvTreeListEntry* pEntry ) const |
104 | 0 | { |
105 | 0 | DBG_ASSERT(pEntry && pEntry!=pRootItem.get(),"GetDepth:Bad Entry"); |
106 | 0 | sal_uInt16 nDepth = 0; |
107 | 0 | while( pEntry && pEntry->pParent != pRootItem.get() ) |
108 | 0 | { |
109 | 0 | nDepth++; |
110 | 0 | pEntry = pEntry->pParent; |
111 | 0 | } |
112 | 0 | return nDepth; |
113 | 0 | } |
114 | | |
115 | | bool SvTreeList::IsAtRootDepth( const SvTreeListEntry* pEntry ) const |
116 | 0 | { |
117 | 0 | return pEntry->pParent == pRootItem.get(); |
118 | 0 | } |
119 | | |
120 | | void SvTreeList::Clear() |
121 | 0 | { |
122 | 0 | Broadcast( SvListAction::CLEARING ); |
123 | 0 | pRootItem->ClearChildren(); |
124 | 0 | nEntryCount = 0; |
125 | 0 | Broadcast( SvListAction::CLEARED ); |
126 | 0 | } |
127 | | |
128 | | bool SvTreeList::IsChild(const SvTreeListEntry* pParent, const SvTreeListEntry* pChild) const |
129 | 0 | { |
130 | 0 | if ( !pParent ) |
131 | 0 | pParent = pRootItem.get(); |
132 | |
|
133 | 0 | if (pParent->m_Children.empty()) |
134 | 0 | return false; |
135 | | |
136 | 0 | for (auto const& it : pParent->m_Children) |
137 | 0 | { |
138 | 0 | const SvTreeListEntry* pThis = it.get(); |
139 | 0 | if (pThis == pChild) |
140 | 0 | return true; |
141 | 0 | else |
142 | 0 | { |
143 | 0 | bool bIsChild = IsChild(pThis, pChild); |
144 | 0 | if (bIsChild) |
145 | 0 | return true; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | | namespace { |
152 | | |
153 | | class FindByPointer |
154 | | { |
155 | | const SvTreeListEntry* mpEntry; |
156 | | public: |
157 | 0 | explicit FindByPointer(const SvTreeListEntry* p) : mpEntry(p) {} |
158 | | |
159 | | bool operator() (std::unique_ptr<SvTreeListEntry> const& rpEntry) const |
160 | 0 | { |
161 | 0 | return mpEntry == rpEntry.get(); |
162 | 0 | } |
163 | | }; |
164 | | |
165 | | sal_uInt32 findEntryPosition(const SvTreeListEntries& rDst, const SvTreeListEntry* pEntry) |
166 | 0 | { |
167 | 0 | SvTreeListEntries::const_iterator itPos = std::find_if(rDst.begin(), rDst.end(), FindByPointer(pEntry)); |
168 | 0 | if (itPos == rDst.end()) |
169 | 0 | return static_cast<sal_uInt32>(~0); |
170 | | |
171 | 0 | return static_cast<sal_uInt32>(std::distance(rDst.begin(), itPos)); |
172 | 0 | } |
173 | | |
174 | | } |
175 | | |
176 | | sal_uInt32 SvTreeList::Move(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uInt32 nListPos) |
177 | 0 | { |
178 | | // pDest may be 0! |
179 | 0 | assert(pSrcEntry && "Entry?"); |
180 | 0 | if ( !pTargetParent ) |
181 | 0 | pTargetParent = pRootItem.get(); |
182 | 0 | DBG_ASSERT(pSrcEntry!=pTargetParent,"Move:Source=Target"); |
183 | |
|
184 | 0 | Broadcast( SvListAction::MOVING, pSrcEntry, pTargetParent, nListPos ); |
185 | |
|
186 | 0 | if ( pSrcEntry == pTargetParent ) |
187 | | // You can't move an entry onto itself as the parent. Just return its |
188 | | // position and bail out. |
189 | 0 | return pSrcEntry->GetChildListPos(); |
190 | | |
191 | 0 | bAbsPositionsValid = false; |
192 | |
|
193 | 0 | SvTreeListEntries& rDst = pTargetParent->m_Children; |
194 | 0 | SvTreeListEntries& rSrc = pSrcEntry->pParent->m_Children; |
195 | |
|
196 | 0 | bool bSameParent = pTargetParent == pSrcEntry->pParent; |
197 | | |
198 | | // Find the position of the entry being moved in the source container. |
199 | 0 | SvTreeListEntries::iterator itSrcPos = rSrc.begin(), itEnd = rSrc.end(); |
200 | 0 | for (; itSrcPos != itEnd; ++itSrcPos) |
201 | 0 | { |
202 | 0 | const SvTreeListEntry* p = (*itSrcPos).get(); |
203 | 0 | if (p == pSrcEntry) |
204 | | // Found |
205 | 0 | break; |
206 | 0 | } |
207 | |
|
208 | 0 | if (itSrcPos == itEnd) |
209 | 0 | { |
210 | 0 | OSL_FAIL("Source entry not found! This should never happen."); |
211 | 0 | return pSrcEntry->GetChildListPos(); |
212 | 0 | } |
213 | | |
214 | 0 | if (bSameParent) |
215 | 0 | { |
216 | | // Moving within the same parent. |
217 | |
|
218 | 0 | size_t nSrcPos = std::distance(rSrc.begin(), itSrcPos); |
219 | 0 | if (nSrcPos == nListPos) |
220 | | // Nothing to move here. |
221 | 0 | return pSrcEntry->GetChildListPos(); |
222 | | |
223 | 0 | if (nSrcPos < nListPos) |
224 | | // Destination position shifts left after removing the original. |
225 | 0 | --nListPos; |
226 | | |
227 | | // Release the original. |
228 | 0 | std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos)); |
229 | 0 | assert(pOriginal); |
230 | 0 | rSrc.erase(itSrcPos); |
231 | | |
232 | | // Determine the insertion position. |
233 | 0 | SvTreeListEntries::iterator itDstPos = rSrc.end(); |
234 | 0 | if (nListPos < rSrc.size()) |
235 | 0 | { |
236 | 0 | itDstPos = rSrc.begin(); |
237 | 0 | std::advance(itDstPos, nListPos); |
238 | 0 | } |
239 | 0 | rSrc.insert(itDstPos, std::move(pOriginal)); |
240 | 0 | } |
241 | 0 | else |
242 | 0 | { |
243 | | // Moving from one parent to another. |
244 | 0 | SvTreeListEntries::iterator itDstPos = rDst.end(); |
245 | 0 | if (nListPos < rDst.size()) |
246 | 0 | { |
247 | 0 | itDstPos = rDst.begin(); |
248 | 0 | std::advance(itDstPos, nListPos); |
249 | 0 | } |
250 | 0 | std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos)); |
251 | 0 | assert(pOriginal); |
252 | 0 | rSrc.erase(itSrcPos); |
253 | 0 | rDst.insert(itDstPos, std::move(pOriginal)); |
254 | 0 | } |
255 | | |
256 | | // move parent (do this only now, because we need the parent for |
257 | | // deleting the old child list!) |
258 | 0 | pSrcEntry->pParent = pTargetParent; |
259 | | |
260 | | // correct list position in target list |
261 | 0 | SetListPositions(rDst); |
262 | 0 | if (!bSameParent) |
263 | 0 | SetListPositions(rSrc); |
264 | |
|
265 | 0 | sal_uInt32 nRetVal = findEntryPosition(rDst, pSrcEntry); |
266 | 0 | OSL_ENSURE(nRetVal == pSrcEntry->GetChildListPos(), "ListPos not valid"); |
267 | 0 | Broadcast( SvListAction::MOVED,pSrcEntry,pTargetParent,nRetVal); |
268 | 0 | return nRetVal; |
269 | 0 | } |
270 | | |
271 | | sal_uInt32 SvTreeList::Copy(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uInt32 nListPos) |
272 | 0 | { |
273 | | // pDest may be 0! |
274 | 0 | DBG_ASSERT(pSrcEntry,"Entry?"); |
275 | 0 | if ( !pTargetParent ) |
276 | 0 | pTargetParent = pRootItem.get(); |
277 | |
|
278 | 0 | bAbsPositionsValid = false; |
279 | |
|
280 | 0 | sal_uInt32 nCloneCount = 0; |
281 | 0 | SvTreeListEntry* pClonedEntry = Clone( pSrcEntry, nCloneCount ); |
282 | 0 | nEntryCount += nCloneCount; |
283 | |
|
284 | 0 | SvTreeListEntries& rDst = pTargetParent->m_Children; |
285 | |
|
286 | 0 | pClonedEntry->pParent = pTargetParent; // move parent |
287 | |
|
288 | 0 | if (nListPos < rDst.size()) |
289 | 0 | { |
290 | 0 | SvTreeListEntries::iterator itPos = rDst.begin(); // insertion position. |
291 | 0 | std::advance(itPos, nListPos); |
292 | 0 | rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pClonedEntry)); |
293 | 0 | } |
294 | 0 | else |
295 | 0 | rDst.push_back(std::unique_ptr<SvTreeListEntry>(pClonedEntry)); |
296 | |
|
297 | 0 | SetListPositions(rDst); // correct list position in target list |
298 | |
|
299 | 0 | Broadcast( SvListAction::INSERTED_TREE, pClonedEntry ); |
300 | 0 | sal_uInt32 nRetVal = findEntryPosition(rDst, pClonedEntry); |
301 | 0 | return nRetVal; |
302 | 0 | } |
303 | | |
304 | | void SvTreeList::Move( SvTreeListEntry* pSrcEntry, SvTreeListEntry* pDstEntry ) |
305 | 0 | { |
306 | 0 | SvTreeListEntry* pParent; |
307 | 0 | sal_uInt32 nPos; |
308 | |
|
309 | 0 | if ( !pDstEntry ) |
310 | 0 | { |
311 | 0 | pParent = pRootItem.get(); |
312 | 0 | nPos = 0; |
313 | 0 | } |
314 | 0 | else |
315 | 0 | { |
316 | 0 | pParent = pDstEntry->pParent; |
317 | 0 | nPos = pDstEntry->GetChildListPos(); |
318 | 0 | nPos++; // (On screen:) insert _below_ pDstEntry |
319 | 0 | } |
320 | 0 | Move( pSrcEntry, pParent, nPos ); |
321 | 0 | } |
322 | | |
323 | | void SvTreeList::InsertTree(SvTreeListEntry* pSrcEntry, |
324 | | SvTreeListEntry* pTargetParent,sal_uInt32 nListPos) |
325 | 0 | { |
326 | 0 | DBG_ASSERT(pSrcEntry,"InsertTree:Entry?"); |
327 | 0 | if ( !pSrcEntry ) |
328 | 0 | return; |
329 | | |
330 | 0 | if ( !pTargetParent ) |
331 | 0 | pTargetParent = pRootItem.get(); |
332 | | |
333 | | // take sorting into account |
334 | 0 | GetInsertionPos( pSrcEntry, pTargetParent, nListPos ); |
335 | |
|
336 | 0 | bAbsPositionsValid = false; |
337 | |
|
338 | 0 | pSrcEntry->pParent = pTargetParent; // move parent |
339 | 0 | SvTreeListEntries& rDst = pTargetParent->m_Children; |
340 | |
|
341 | 0 | if (nListPos < rDst.size()) |
342 | 0 | { |
343 | 0 | SvTreeListEntries::iterator itPos = rDst.begin(); |
344 | 0 | std::advance(itPos, nListPos); |
345 | 0 | rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pSrcEntry)); |
346 | 0 | } |
347 | 0 | else |
348 | 0 | rDst.push_back(std::unique_ptr<SvTreeListEntry>(pSrcEntry)); |
349 | |
|
350 | 0 | SetListPositions(rDst); // correct list position in target list |
351 | 0 | nEntryCount += GetChildCount( pSrcEntry ); |
352 | 0 | nEntryCount++; // the parent is new, too |
353 | |
|
354 | 0 | Broadcast(SvListAction::INSERTED_TREE, pSrcEntry ); |
355 | 0 | } |
356 | | |
357 | | SvTreeListEntry* SvTreeList::CloneEntry( SvTreeListEntry* pSource ) const |
358 | 0 | { |
359 | 0 | if( aCloneLink.IsSet() ) |
360 | 0 | return aCloneLink.Call( pSource ); |
361 | 0 | SvTreeListEntry* pEntry = new SvTreeListEntry; |
362 | 0 | pEntry->Clone(pSource); |
363 | 0 | return pEntry; |
364 | 0 | } |
365 | | |
366 | | SvTreeListEntry* SvTreeList::Clone( SvTreeListEntry* pEntry, sal_uInt32& nCloneCount ) const |
367 | 0 | { |
368 | 0 | SvTreeListEntry* pClonedEntry = CloneEntry( pEntry ); |
369 | 0 | nCloneCount = 1; |
370 | 0 | if (!pEntry->m_Children.empty()) |
371 | | // Clone the child entries. |
372 | 0 | CloneChildren(pClonedEntry->m_Children, nCloneCount, pEntry->m_Children, *pClonedEntry); |
373 | |
|
374 | 0 | return pClonedEntry; |
375 | 0 | } |
376 | | |
377 | | void SvTreeList::CloneChildren( |
378 | | SvTreeListEntries& rDst, sal_uInt32& rCloneCount, SvTreeListEntries& rSrc, SvTreeListEntry& rNewParent) const |
379 | 0 | { |
380 | 0 | SvTreeListEntries aClone; |
381 | 0 | for (auto const& elem : rSrc) |
382 | 0 | { |
383 | 0 | SvTreeListEntry& rEntry = *elem; |
384 | 0 | std::unique_ptr<SvTreeListEntry> pNewEntry(CloneEntry(&rEntry)); |
385 | 0 | ++rCloneCount; |
386 | 0 | pNewEntry->pParent = &rNewParent; |
387 | 0 | if (!rEntry.m_Children.empty()) |
388 | | // Clone entries recursively. |
389 | 0 | CloneChildren(pNewEntry->m_Children, rCloneCount, rEntry.m_Children, *pNewEntry); |
390 | |
|
391 | 0 | aClone.push_back(std::move(pNewEntry)); |
392 | 0 | } |
393 | |
|
394 | 0 | rDst.swap(aClone); |
395 | 0 | } |
396 | | |
397 | | sal_uInt32 SvTreeList::GetChildCount( const SvTreeListEntry* pParent ) const |
398 | 0 | { |
399 | 0 | if ( !pParent ) |
400 | 0 | return GetEntryCount(); |
401 | | |
402 | 0 | if (pParent->m_Children.empty()) |
403 | 0 | return 0; |
404 | | |
405 | 0 | sal_uInt32 nCount = 0; |
406 | 0 | sal_uInt16 nRefDepth = GetDepth( pParent ); |
407 | 0 | sal_uInt16 nActDepth = nRefDepth; |
408 | 0 | do |
409 | 0 | { |
410 | 0 | pParent = Next(const_cast<SvTreeListEntry*>(pParent), &nActDepth); |
411 | 0 | nCount++; |
412 | 0 | } while( pParent && nRefDepth < nActDepth ); |
413 | |
|
414 | 0 | assert(nCount > 0 && "given do...while"); |
415 | 0 | return nCount - 1; |
416 | 0 | } |
417 | | |
418 | | sal_uInt32 SvTreeList::GetVisibleChildCount(const SvListView* pView, SvTreeListEntry* pParent) const |
419 | 0 | { |
420 | 0 | assert(pView && "GetVisChildCount:No View"); |
421 | 0 | if ( !pParent ) |
422 | 0 | pParent = pRootItem.get(); |
423 | |
|
424 | 0 | if (!pParent || !pView->IsExpanded(pParent) || pParent->m_Children.empty()) |
425 | 0 | return 0; |
426 | | |
427 | 0 | sal_uInt32 nCount = 0; |
428 | 0 | sal_uInt16 nRefDepth = GetDepth( pParent ); |
429 | 0 | sal_uInt16 nActDepth = nRefDepth; |
430 | 0 | do |
431 | 0 | { |
432 | 0 | pParent = NextVisible( pView, pParent, &nActDepth ); |
433 | 0 | nCount++; |
434 | 0 | } while( pParent && nRefDepth < nActDepth ); |
435 | |
|
436 | 0 | assert(nCount > 0 && "given do...while"); |
437 | 0 | return nCount - 1; |
438 | 0 | } |
439 | | |
440 | | sal_uInt32 SvTreeList::GetChildSelectionCount(const SvListView* pView,SvTreeListEntry* pParent) const |
441 | 0 | { |
442 | 0 | assert(pView && "GetChildSelCount:No View"); |
443 | 0 | if ( !pParent ) |
444 | 0 | pParent = pRootItem.get(); |
445 | |
|
446 | 0 | if (!pParent || pParent->m_Children.empty()) |
447 | 0 | return 0; |
448 | | |
449 | 0 | sal_uInt32 nCount = 0; |
450 | 0 | sal_uInt16 nRefDepth = GetDepth( pParent ); |
451 | 0 | sal_uInt16 nActDepth = nRefDepth; |
452 | 0 | do |
453 | 0 | { |
454 | 0 | pParent = Next( pParent, &nActDepth ); |
455 | 0 | if( pParent && pView->IsSelected( pParent ) && nRefDepth < nActDepth) |
456 | 0 | nCount++; |
457 | 0 | } while( pParent && nRefDepth < nActDepth ); |
458 | |
|
459 | 0 | return nCount; |
460 | 0 | } |
461 | | |
462 | | SvTreeListEntry* SvTreeList::First() const |
463 | 0 | { |
464 | 0 | if ( nEntryCount ) |
465 | 0 | return pRootItem->m_Children[0].get(); |
466 | 0 | else |
467 | 0 | return nullptr; |
468 | 0 | } |
469 | | |
470 | | SvTreeListEntry* SvTreeList::Next( SvTreeListEntry* pActEntry, sal_uInt16* pDepth ) const |
471 | 0 | { |
472 | 0 | DBG_ASSERT( pActEntry && pActEntry->pParent, "SvTreeList::Next: invalid entry/parent!" ); |
473 | 0 | if ( !pActEntry || !pActEntry->pParent ) |
474 | 0 | return nullptr; |
475 | | |
476 | 0 | sal_uInt16 nDepth = 0; |
477 | 0 | bool bWithDepth = false; |
478 | 0 | if ( pDepth ) |
479 | 0 | { |
480 | 0 | nDepth = *pDepth; |
481 | 0 | bWithDepth = true; |
482 | 0 | } |
483 | | |
484 | | // Get the list where the current entry belongs to (from its parent). |
485 | 0 | SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children; |
486 | 0 | sal_uInt32 nActualPos = pActEntry->GetChildListPos(); |
487 | |
|
488 | 0 | if (!pActEntry->m_Children.empty()) |
489 | 0 | { |
490 | | // The current entry has children. Get its first child entry. |
491 | 0 | nDepth++; |
492 | 0 | pActEntry = pActEntry->m_Children[0].get(); |
493 | 0 | if ( bWithDepth ) |
494 | 0 | *pDepth = nDepth; |
495 | 0 | return pActEntry; |
496 | 0 | } |
497 | | |
498 | 0 | if (pActualList->size() > (nActualPos+1)) |
499 | 0 | { |
500 | | // Get the next sibling of the current entry. |
501 | 0 | pActEntry = (*pActualList)[nActualPos+1].get(); |
502 | 0 | if ( bWithDepth ) |
503 | 0 | *pDepth = nDepth; |
504 | 0 | return pActEntry; |
505 | 0 | } |
506 | | |
507 | | // Move up level(s) until we find the level where the next sibling exists. |
508 | 0 | SvTreeListEntry* pParent = pActEntry->pParent; |
509 | 0 | nDepth--; |
510 | 0 | while( pParent != pRootItem.get() && pParent != nullptr ) |
511 | 0 | { |
512 | 0 | DBG_ASSERT(pParent!=nullptr,"TreeData corrupt!"); |
513 | 0 | pActualList = &pParent->pParent->m_Children; |
514 | 0 | nActualPos = pParent->GetChildListPos(); |
515 | 0 | if (pActualList->size() > (nActualPos+1)) |
516 | 0 | { |
517 | 0 | pActEntry = (*pActualList)[nActualPos+1].get(); |
518 | 0 | if ( bWithDepth ) |
519 | 0 | *pDepth = nDepth; |
520 | 0 | return pActEntry; |
521 | 0 | } |
522 | 0 | pParent = pParent->pParent; |
523 | 0 | nDepth--; |
524 | 0 | } |
525 | 0 | return nullptr; |
526 | 0 | } |
527 | | |
528 | | SvTreeListEntry* SvTreeList::Prev( SvTreeListEntry* pActEntry ) const |
529 | 0 | { |
530 | 0 | assert(pActEntry && "Entry?"); |
531 | |
|
532 | 0 | SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children; |
533 | 0 | sal_uInt32 nActualPos = pActEntry->GetChildListPos(); |
534 | |
|
535 | 0 | if ( nActualPos > 0 ) |
536 | 0 | { |
537 | 0 | pActEntry = (*pActualList)[nActualPos-1].get(); |
538 | 0 | while (!pActEntry->m_Children.empty()) |
539 | 0 | { |
540 | 0 | pActualList = &pActEntry->m_Children; |
541 | 0 | pActEntry = pActualList->back().get(); |
542 | 0 | } |
543 | 0 | return pActEntry; |
544 | 0 | } |
545 | 0 | if ( pActEntry->pParent == pRootItem.get() ) |
546 | 0 | return nullptr; |
547 | | |
548 | 0 | pActEntry = pActEntry->pParent; |
549 | |
|
550 | 0 | if ( pActEntry ) |
551 | 0 | { |
552 | 0 | return pActEntry; |
553 | 0 | } |
554 | 0 | return nullptr; |
555 | 0 | } |
556 | | |
557 | | SvTreeListEntry* SvTreeList::Last() const |
558 | 0 | { |
559 | 0 | SvTreeListEntries* pActList = &pRootItem->m_Children; |
560 | 0 | SvTreeListEntry* pEntry = nullptr; |
561 | 0 | while (!pActList->empty()) |
562 | 0 | { |
563 | 0 | pEntry = pActList->back().get(); |
564 | 0 | pActList = &pEntry->m_Children; |
565 | 0 | } |
566 | 0 | return pEntry; |
567 | 0 | } |
568 | | |
569 | | sal_uInt32 SvTreeList::GetVisiblePos( const SvListView* pView, SvTreeListEntry const * pEntry ) const |
570 | 0 | { |
571 | 0 | assert(pView && "View?"); |
572 | 0 | DBG_ASSERT(pEntry,"Entry?"); |
573 | |
|
574 | 0 | if (!pView->m_pImpl->m_bVisPositionsValid) |
575 | 0 | { |
576 | | // to make GetVisibleCount refresh the positions |
577 | 0 | const_cast<SvListView*>(pView)->m_pImpl->m_nVisibleCount = 0; |
578 | 0 | GetVisibleCount( const_cast<SvListView*>(pView) ); |
579 | 0 | } |
580 | 0 | const SvViewDataEntry* pViewData = pView->GetViewData( pEntry ); |
581 | 0 | if (!pViewData) |
582 | 0 | return 0; |
583 | 0 | return pViewData->nVisPos; |
584 | 0 | } |
585 | | |
586 | | sal_uInt32 SvTreeList::GetVisibleCount( SvListView* pView ) const |
587 | 0 | { |
588 | 0 | assert(pView && "GetVisCount:No View"); |
589 | 0 | if( !pView->HasViewData() ) |
590 | 0 | return 0; |
591 | 0 | if (pView->m_pImpl->m_nVisibleCount) |
592 | 0 | return pView->m_pImpl->m_nVisibleCount; |
593 | | |
594 | 0 | sal_uInt32 nPos = 0; |
595 | 0 | SvTreeListEntry* pEntry = First(); // first entry is always visible |
596 | 0 | while ( pEntry ) |
597 | 0 | { |
598 | 0 | if (SvViewDataEntry* pViewData = pView->GetViewData( pEntry )) |
599 | 0 | pViewData->nVisPos = nPos; |
600 | 0 | nPos++; |
601 | 0 | pEntry = NextVisible( pView, pEntry ); |
602 | 0 | } |
603 | | #ifdef DBG_UTIL |
604 | | if( nPos > 10000000 ) |
605 | | { |
606 | | OSL_FAIL("nVisibleCount bad"); |
607 | | } |
608 | | #endif |
609 | 0 | pView->m_pImpl->m_nVisibleCount = nPos; |
610 | 0 | pView->m_pImpl->m_bVisPositionsValid = true; |
611 | 0 | return nPos; |
612 | 0 | } |
613 | | |
614 | | |
615 | | // For performance reasons, this function assumes that the passed entry is |
616 | | // already visible. |
617 | | SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pActEntry,sal_uInt16* pActDepth) const |
618 | 0 | { |
619 | 0 | if ( !pActEntry ) |
620 | 0 | return nullptr; |
621 | | |
622 | 0 | assert(pView && "NextVisible:No View"); |
623 | |
|
624 | 0 | sal_uInt16 nDepth = 0; |
625 | 0 | bool bWithDepth = false; |
626 | 0 | if ( pActDepth ) |
627 | 0 | { |
628 | 0 | nDepth = *pActDepth; |
629 | 0 | bWithDepth = true; |
630 | 0 | } |
631 | |
|
632 | 0 | SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children; |
633 | 0 | sal_uInt32 nActualPos = pActEntry->GetChildListPos(); |
634 | |
|
635 | 0 | if ( pView->IsExpanded(pActEntry) ) |
636 | 0 | { |
637 | 0 | OSL_ENSURE(!pActEntry->m_Children.empty(), "Pass entry is supposed to have child entries."); |
638 | |
|
639 | 0 | nDepth++; |
640 | 0 | pActEntry = pActEntry->m_Children[0].get(); |
641 | 0 | if ( bWithDepth ) |
642 | 0 | *pActDepth = nDepth; |
643 | 0 | return pActEntry; |
644 | 0 | } |
645 | | |
646 | 0 | nActualPos++; |
647 | 0 | if ( pActualList->size() > nActualPos ) |
648 | 0 | { |
649 | 0 | pActEntry = (*pActualList)[nActualPos].get(); |
650 | 0 | if ( bWithDepth ) |
651 | 0 | *pActDepth = nDepth; |
652 | 0 | return pActEntry; |
653 | 0 | } |
654 | | |
655 | 0 | SvTreeListEntry* pParent = pActEntry->pParent; |
656 | 0 | nDepth--; |
657 | 0 | while( pParent != pRootItem.get() ) |
658 | 0 | { |
659 | 0 | pActualList = &pParent->pParent->m_Children; |
660 | 0 | nActualPos = pParent->GetChildListPos(); |
661 | 0 | nActualPos++; |
662 | 0 | if ( pActualList->size() > nActualPos ) |
663 | 0 | { |
664 | 0 | pActEntry = (*pActualList)[nActualPos].get(); |
665 | 0 | if ( bWithDepth ) |
666 | 0 | *pActDepth = nDepth; |
667 | 0 | return pActEntry; |
668 | 0 | } |
669 | 0 | pParent = pParent->pParent; |
670 | 0 | nDepth--; |
671 | 0 | } |
672 | 0 | return nullptr; |
673 | 0 | } |
674 | | |
675 | | |
676 | | // For performance reasons, this function assumes that the passed entry is |
677 | | // already visible. |
678 | | |
679 | | SvTreeListEntry* SvTreeList::PrevVisible(const SvListView* pView, SvTreeListEntry* pActEntry) const |
680 | 0 | { |
681 | 0 | assert(pView && pActEntry && "PrevVis:View/Entry?"); |
682 | |
|
683 | 0 | SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children; |
684 | 0 | sal_uInt32 nActualPos = pActEntry->GetChildListPos(); |
685 | |
|
686 | 0 | if ( nActualPos > 0 ) |
687 | 0 | { |
688 | 0 | pActEntry = (*pActualList)[nActualPos-1].get(); |
689 | 0 | while( pView->IsExpanded(pActEntry) ) |
690 | 0 | { |
691 | 0 | pActualList = &pActEntry->m_Children; |
692 | 0 | pActEntry = pActualList->back().get(); |
693 | 0 | } |
694 | 0 | return pActEntry; |
695 | 0 | } |
696 | | |
697 | 0 | if ( pActEntry->pParent == pRootItem.get() ) |
698 | 0 | return nullptr; |
699 | | |
700 | 0 | pActEntry = pActEntry->pParent; |
701 | 0 | if ( pActEntry ) |
702 | 0 | { |
703 | 0 | return pActEntry; |
704 | 0 | } |
705 | 0 | return nullptr; |
706 | 0 | } |
707 | | |
708 | | SvTreeListEntry* SvTreeList::LastVisible( const SvListView* pView) const |
709 | 0 | { |
710 | 0 | DBG_ASSERT(pView,"LastVis:No View"); |
711 | 0 | SvTreeListEntry* pEntry = Last(); |
712 | 0 | while( pEntry && !IsEntryVisible( pView, pEntry ) ) |
713 | 0 | pEntry = PrevVisible( pView, pEntry ); |
714 | 0 | return pEntry; |
715 | 0 | } |
716 | | |
717 | | SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pEntry,sal_uInt16& nDelta) const |
718 | 0 | { |
719 | 0 | assert(pView && pEntry && "NextVis:Wrong Prms!"); |
720 | 0 | DBG_ASSERT(IsEntryVisible(pView,pEntry), "NextVis:Wrong Vis"); |
721 | |
|
722 | 0 | sal_uInt32 nVisPos = GetVisiblePos( pView, pEntry ); |
723 | | // nDelta entries existent? |
724 | | // example: 0,1,2,3,4,5,6,7,8,9 nVisPos=5 nDelta=7 |
725 | | // nNewDelta = 10-nVisPos-1 == 4 |
726 | 0 | if (nVisPos+nDelta >= pView->m_pImpl->m_nVisibleCount) |
727 | 0 | { |
728 | 0 | nDelta = static_cast<sal_uInt16>(pView->m_pImpl->m_nVisibleCount-nVisPos); |
729 | 0 | nDelta--; |
730 | 0 | } |
731 | 0 | sal_uInt16 nDeltaTmp = nDelta; |
732 | 0 | while( nDeltaTmp ) |
733 | 0 | { |
734 | 0 | pEntry = NextVisible( pView, pEntry ); |
735 | 0 | nDeltaTmp--; |
736 | 0 | DBG_ASSERT(pEntry,"Entry?"); |
737 | 0 | } |
738 | 0 | return pEntry; |
739 | 0 | } |
740 | | |
741 | | SvTreeListEntry* SvTreeList::PrevVisible( const SvListView* pView, SvTreeListEntry* pEntry, sal_uInt16& nDelta ) const |
742 | 0 | { |
743 | 0 | DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"PrevVis:Parms/!Vis"); |
744 | |
|
745 | 0 | sal_uInt32 nVisPos = GetVisiblePos( pView, pEntry ); |
746 | | // nDelta entries existent? |
747 | | // example: 0,1,2,3,4,5,6,7,8,9 nVisPos=8 nDelta=20 |
748 | | // nNewDelta = nNewVisPos |
749 | 0 | if ( nDelta > nVisPos ) |
750 | 0 | nDelta = static_cast<sal_uInt16>(nVisPos); |
751 | 0 | sal_uInt16 nDeltaTmp = nDelta; |
752 | 0 | while( nDeltaTmp ) |
753 | 0 | { |
754 | 0 | pEntry = PrevVisible( pView, pEntry ); |
755 | 0 | nDeltaTmp--; |
756 | 0 | DBG_ASSERT(pEntry,"Entry?"); |
757 | 0 | } |
758 | 0 | return pEntry; |
759 | 0 | } |
760 | | |
761 | | SvTreeListEntry* SvTreeList::FirstSelected( const SvListView* pView) const |
762 | 0 | { |
763 | 0 | DBG_ASSERT(pView,"FirstSel:No View"); |
764 | 0 | if( !pView ) |
765 | 0 | return nullptr; |
766 | 0 | SvTreeListEntry* pActSelEntry = First(); |
767 | 0 | while( pActSelEntry && !pView->IsSelected(pActSelEntry) ) |
768 | 0 | pActSelEntry = NextVisible( pView, pActSelEntry ); |
769 | 0 | return pActSelEntry; |
770 | 0 | } |
771 | | |
772 | | |
773 | | SvTreeListEntry* SvTreeList::FirstChild( SvTreeListEntry* pParent ) const |
774 | 0 | { |
775 | 0 | if ( !pParent ) |
776 | 0 | pParent = pRootItem.get(); |
777 | 0 | SvTreeListEntry* pResult; |
778 | 0 | if (!pParent->m_Children.empty()) |
779 | 0 | pResult = pParent->m_Children[0].get(); |
780 | 0 | else |
781 | 0 | pResult = nullptr; |
782 | 0 | return pResult; |
783 | 0 | } |
784 | | |
785 | | SvTreeListEntry* SvTreeList::NextSelected( const SvListView* pView, SvTreeListEntry* pEntry ) const |
786 | 0 | { |
787 | 0 | assert(pView && pEntry && "NextSel:View/Entry?"); |
788 | 0 | pEntry = Next( pEntry ); |
789 | 0 | while( pEntry && !pView->IsSelected(pEntry) ) |
790 | 0 | pEntry = Next( pEntry ); |
791 | 0 | return pEntry; |
792 | 0 | } |
793 | | |
794 | | sal_uInt32 SvTreeList::Insert( SvTreeListEntry* pEntry,SvTreeListEntry* pParent,sal_uInt32 nPos ) |
795 | 0 | { |
796 | 0 | assert(pEntry && "Entry?"); |
797 | |
|
798 | 0 | if ( !pParent ) |
799 | 0 | pParent = pRootItem.get(); |
800 | |
|
801 | 0 | SvTreeListEntries& rList = pParent->m_Children; |
802 | | |
803 | | // take sorting into account |
804 | 0 | GetInsertionPos( pEntry, pParent, nPos ); |
805 | |
|
806 | 0 | bAbsPositionsValid = false; |
807 | 0 | pEntry->pParent = pParent; |
808 | |
|
809 | 0 | if (nPos < rList.size()) |
810 | 0 | { |
811 | 0 | SvTreeListEntries::iterator itPos = rList.begin(); |
812 | 0 | std::advance(itPos, nPos); |
813 | 0 | rList.insert(itPos, std::unique_ptr<SvTreeListEntry>(pEntry)); |
814 | 0 | } |
815 | 0 | else |
816 | 0 | rList.push_back(std::unique_ptr<SvTreeListEntry>(pEntry)); |
817 | |
|
818 | 0 | nEntryCount++; |
819 | 0 | if (nPos != TREELIST_APPEND && (nPos != (rList.size()-1))) |
820 | 0 | SetListPositions(rList); |
821 | 0 | else |
822 | 0 | pEntry->nListPos = rList.size()-1; |
823 | |
|
824 | 0 | Broadcast( SvListAction::INSERTED, pEntry ); |
825 | 0 | return nPos; // pEntry->nListPos; |
826 | 0 | } |
827 | | |
828 | | sal_uInt32 SvTreeList::GetAbsPos( const SvTreeListEntry* pEntry) const |
829 | 0 | { |
830 | 0 | if ( !bAbsPositionsValid ) |
831 | 0 | const_cast<SvTreeList*>(this)->SetAbsolutePositions(); |
832 | 0 | return pEntry->nAbsPos; |
833 | 0 | } |
834 | | |
835 | | sal_uInt32 SvTreeList::GetRelPos( const SvTreeListEntry* pChild ) |
836 | 0 | { |
837 | 0 | return pChild->GetChildListPos(); |
838 | 0 | } |
839 | | |
840 | | void SvTreeList::SetAbsolutePositions() |
841 | 0 | { |
842 | 0 | sal_uInt32 nPos = 0; |
843 | 0 | SvTreeListEntry* pEntry = First(); |
844 | 0 | while ( pEntry ) |
845 | 0 | { |
846 | 0 | pEntry->nAbsPos = nPos; |
847 | 0 | nPos++; |
848 | 0 | pEntry = Next( pEntry ); |
849 | 0 | } |
850 | 0 | bAbsPositionsValid = true; |
851 | 0 | } |
852 | | |
853 | | void SvListView::ExpandListEntry( SvTreeListEntry* pEntry ) |
854 | 0 | { |
855 | 0 | assert(pEntry && "Expand:View/Entry?"); |
856 | 0 | SvViewDataEntry* pViewData = GetViewData(pEntry); |
857 | 0 | if (!pViewData) |
858 | 0 | return; |
859 | | |
860 | 0 | if (pViewData->IsExpanded()) |
861 | 0 | return; |
862 | | |
863 | 0 | DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Expand: We expected to have child entries."); |
864 | |
|
865 | 0 | pViewData->SetExpanded(true); |
866 | 0 | SvTreeListEntry* pParent = pEntry->pParent; |
867 | | // if parent is visible, invalidate status data |
868 | 0 | if ( IsExpanded( pParent ) ) |
869 | 0 | { |
870 | 0 | m_pImpl->m_bVisPositionsValid = false; |
871 | 0 | m_pImpl->m_nVisibleCount = 0; |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | void SvListView::CollapseListEntry( SvTreeListEntry* pEntry ) |
876 | 0 | { |
877 | 0 | assert(pEntry && "Collapse:View/Entry?"); |
878 | 0 | SvViewDataEntry* pViewData = GetViewData( pEntry ); |
879 | 0 | if (!pViewData) |
880 | 0 | return; |
881 | | |
882 | 0 | if (!pViewData->IsExpanded()) |
883 | 0 | return; |
884 | | |
885 | 0 | DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Collapse: We expected to have child entries."); |
886 | |
|
887 | 0 | pViewData->SetExpanded(false); |
888 | |
|
889 | 0 | SvTreeListEntry* pParent = pEntry->pParent; |
890 | 0 | if ( IsExpanded(pParent) ) |
891 | 0 | { |
892 | 0 | m_pImpl->m_nVisibleCount = 0; |
893 | 0 | m_pImpl->m_bVisPositionsValid = false; |
894 | 0 | } |
895 | 0 | } |
896 | | |
897 | | bool SvListView::SelectListEntry( SvTreeListEntry* pEntry, bool bSelect ) |
898 | 0 | { |
899 | 0 | DBG_ASSERT(pEntry,"Select:View/Entry?"); |
900 | |
|
901 | 0 | SvViewDataEntry* pViewData = GetViewData( pEntry ); |
902 | 0 | if (!pViewData) |
903 | 0 | return false; |
904 | | |
905 | 0 | if ( bSelect ) |
906 | 0 | { |
907 | 0 | if ( pViewData->IsSelected() || !pViewData->IsSelectable() ) |
908 | 0 | return false; |
909 | 0 | else |
910 | 0 | { |
911 | 0 | pViewData->SetSelected(true); |
912 | 0 | m_pImpl->m_nSelectionCount++; |
913 | 0 | } |
914 | 0 | } |
915 | 0 | else |
916 | 0 | { |
917 | 0 | if ( !pViewData->IsSelected() ) |
918 | 0 | return false; |
919 | 0 | else |
920 | 0 | { |
921 | 0 | pViewData->SetSelected(false); |
922 | 0 | m_pImpl->m_nSelectionCount--; |
923 | 0 | } |
924 | 0 | } |
925 | 0 | return true; |
926 | 0 | } |
927 | | |
928 | | bool SvTreeList::Remove( const SvTreeListEntry* pEntry ) |
929 | 0 | { |
930 | 0 | assert(pEntry && "Cannot remove root, use clear"); |
931 | |
|
932 | 0 | if( !pEntry->pParent ) |
933 | 0 | { |
934 | 0 | OSL_FAIL("Removing entry not in model!"); |
935 | | // Under certain circumstances (which?), the explorer deletes entries |
936 | | // from the view that it hasn't inserted into the view. We don't want |
937 | | // to crash, so we catch this case here. |
938 | 0 | return false; |
939 | 0 | } |
940 | | |
941 | 0 | Broadcast(SvListAction::REMOVING, const_cast<SvTreeListEntry*>(pEntry)); |
942 | 0 | sal_uInt32 nRemoved = 1 + GetChildCount(pEntry); |
943 | 0 | bAbsPositionsValid = false; |
944 | |
|
945 | 0 | SvTreeListEntry* pParent = pEntry->pParent; |
946 | 0 | SvTreeListEntries& rList = pParent->m_Children; |
947 | 0 | bool bLastEntry = false; |
948 | | |
949 | | // Since we need the live instance of SvTreeListEntry for broadcasting, |
950 | | // we first need to pop it from the container, broadcast it, then delete |
951 | | // the instance manually at the end. |
952 | |
|
953 | 0 | std::unique_ptr<SvTreeListEntry> pEntryDeleter; |
954 | 0 | if ( pEntry->HasChildListPos() ) |
955 | 0 | { |
956 | 0 | size_t nListPos = pEntry->GetChildListPos(); |
957 | 0 | bLastEntry = (nListPos == (rList.size()-1)); |
958 | 0 | SvTreeListEntries::iterator it = rList.begin(); |
959 | 0 | std::advance(it, nListPos); |
960 | 0 | pEntryDeleter = std::move(*it); |
961 | 0 | rList.erase(it); |
962 | 0 | } |
963 | 0 | else |
964 | 0 | { |
965 | 0 | SvTreeListEntries::iterator it = |
966 | 0 | std::find_if(rList.begin(), rList.end(), FindByPointer(pEntry)); |
967 | 0 | if (it != rList.end()) |
968 | 0 | { |
969 | 0 | pEntryDeleter = std::move(*it); |
970 | 0 | rList.erase(it); |
971 | 0 | } |
972 | 0 | } |
973 | |
|
974 | 0 | if (!rList.empty() && !bLastEntry) |
975 | 0 | SetListPositions(rList); |
976 | |
|
977 | 0 | nEntryCount -= nRemoved; |
978 | 0 | Broadcast(SvListAction::REMOVED, const_cast<SvTreeListEntry*>(pEntry)); |
979 | |
|
980 | 0 | return true; |
981 | 0 | } |
982 | | |
983 | | SvTreeListEntry* SvTreeList::GetEntryAtAbsPos( sal_uInt32 nAbsPos ) const |
984 | 0 | { |
985 | 0 | SvTreeListEntry* pEntry = First(); |
986 | 0 | while ( nAbsPos && pEntry ) |
987 | 0 | { |
988 | 0 | pEntry = Next( pEntry ); |
989 | 0 | nAbsPos--; |
990 | 0 | } |
991 | 0 | return pEntry; |
992 | 0 | } |
993 | | |
994 | | SvTreeListEntry* SvTreeList::GetEntryAtVisPos( const SvListView* pView, sal_uInt32 nVisPos ) const |
995 | 0 | { |
996 | 0 | DBG_ASSERT(pView,"GetEntryAtVisPos:No View"); |
997 | 0 | SvTreeListEntry* pEntry = First(); |
998 | 0 | while ( nVisPos && pEntry ) |
999 | 0 | { |
1000 | 0 | pEntry = NextVisible( pView, pEntry ); |
1001 | 0 | nVisPos--; |
1002 | 0 | } |
1003 | 0 | return pEntry; |
1004 | 0 | } |
1005 | | |
1006 | | void SvTreeList::SetListPositions( SvTreeListEntries& rEntries ) |
1007 | 0 | { |
1008 | 0 | if (rEntries.empty()) |
1009 | 0 | return; |
1010 | | |
1011 | 0 | SvTreeListEntry& rFirst = *rEntries.front(); |
1012 | 0 | if (rFirst.pParent) |
1013 | 0 | rFirst.pParent->InvalidateChildrensListPositions(); |
1014 | 0 | } |
1015 | | |
1016 | | void SvTreeList::EnableInvalidate( bool bEnable ) |
1017 | 0 | { |
1018 | 0 | mbEnableInvalidate = bEnable; |
1019 | 0 | } |
1020 | | |
1021 | | void SvTreeList::InvalidateEntry( SvTreeListEntry* pEntry ) |
1022 | 0 | { |
1023 | 0 | if (!mbEnableInvalidate) |
1024 | 0 | return; |
1025 | | |
1026 | 0 | Broadcast( SvListAction::INVALIDATE_ENTRY, pEntry ); |
1027 | 0 | } |
1028 | | |
1029 | | SvListView::SvListView() |
1030 | 0 | : m_pImpl(new Impl(*this)) |
1031 | 0 | { |
1032 | 0 | pModel.reset(new SvTreeList(*this)); |
1033 | 0 | m_pImpl->InitTable(); |
1034 | 0 | } |
1035 | | |
1036 | | void SvListView::dispose() |
1037 | 0 | { |
1038 | 0 | pModel.reset(); |
1039 | 0 | } |
1040 | | |
1041 | | SvListView::~SvListView() |
1042 | 0 | { |
1043 | 0 | m_pImpl->m_DataTable.clear(); |
1044 | 0 | } |
1045 | | |
1046 | | sal_uInt32 SvListView::GetSelectionCount() const |
1047 | 0 | { return m_pImpl->m_nSelectionCount; } |
1048 | | |
1049 | | bool SvListView::HasViewData() const |
1050 | 0 | { return m_pImpl->m_DataTable.size() > 1; } // There's always a ROOT |
1051 | | |
1052 | | |
1053 | | void SvListView::Impl::InitTable() |
1054 | 0 | { |
1055 | 0 | DBG_ASSERT(m_rThis.pModel,"InitTable:No Model"); |
1056 | 0 | DBG_ASSERT(!m_nSelectionCount && !m_nVisibleCount && !m_bVisPositionsValid, |
1057 | 0 | "InitTable: Not cleared!"); |
1058 | |
|
1059 | 0 | if (!m_DataTable.empty()) |
1060 | 0 | { |
1061 | 0 | DBG_ASSERT(m_DataTable.size() == 1, "InitTable: TableCount != 1"); |
1062 | | // Delete the view data allocated to the Clear in the root. |
1063 | | // Attention: The model belonging to the root entry (and thus the entry |
1064 | | // itself) might already be deleted. |
1065 | 0 | m_DataTable.clear(); |
1066 | 0 | } |
1067 | |
|
1068 | 0 | SvTreeListEntry* pEntry; |
1069 | | |
1070 | | // insert root entry |
1071 | 0 | pEntry = m_rThis.pModel->pRootItem.get(); |
1072 | 0 | std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry); |
1073 | 0 | pViewData->SetExpanded(true); |
1074 | 0 | m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData))); |
1075 | | // now all the other entries |
1076 | 0 | pEntry = m_rThis.pModel->First(); |
1077 | 0 | while( pEntry ) |
1078 | 0 | { |
1079 | 0 | pViewData = std::make_unique<SvViewDataEntry>(); |
1080 | 0 | m_rThis.InitViewData( pViewData.get(), pEntry ); |
1081 | 0 | m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData))); |
1082 | 0 | pEntry = m_rThis.pModel->Next( pEntry ); |
1083 | 0 | } |
1084 | 0 | } |
1085 | | |
1086 | | void SvListView::Clear() |
1087 | 0 | { |
1088 | 0 | m_pImpl->m_DataTable.clear(); |
1089 | 0 | m_pImpl->m_nSelectionCount = 0; |
1090 | 0 | m_pImpl->m_nVisibleCount = 0; |
1091 | 0 | m_pImpl->m_bVisPositionsValid = false; |
1092 | 0 | if( pModel ) |
1093 | 0 | { |
1094 | | // insert root entry |
1095 | 0 | SvTreeListEntry* pEntry = pModel->pRootItem.get(); |
1096 | 0 | std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry); |
1097 | 0 | pViewData->SetExpanded(true); |
1098 | 0 | m_pImpl->m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData))); |
1099 | 0 | } |
1100 | 0 | } |
1101 | | |
1102 | | void SvListView::ModelHasCleared() |
1103 | 0 | { |
1104 | 0 | } |
1105 | | |
1106 | | void SvListView::ModelHasInserted( SvTreeListEntry* ) |
1107 | 0 | { |
1108 | 0 | } |
1109 | | |
1110 | | void SvListView::ModelHasInsertedTree( SvTreeListEntry* ) |
1111 | 0 | { |
1112 | 0 | } |
1113 | | |
1114 | | void SvListView::ModelIsMoving( SvTreeListEntry* /* pSource */ ) |
1115 | 0 | { |
1116 | 0 | } |
1117 | | |
1118 | | |
1119 | | void SvListView::ModelHasMoved( SvTreeListEntry* ) |
1120 | 0 | { |
1121 | 0 | } |
1122 | | |
1123 | | void SvListView::ModelIsRemoving( SvTreeListEntry* ) |
1124 | 0 | { |
1125 | 0 | } |
1126 | | |
1127 | | void SvListView::ModelHasRemoved( SvTreeListEntry* ) |
1128 | 0 | { |
1129 | | //WARNING WARNING WARNING |
1130 | | //The supplied pointer should have been deleted |
1131 | | //before this call. Be careful not to use it!!! |
1132 | 0 | } |
1133 | | |
1134 | | void SvListView::ModelHasEntryInvalidated( SvTreeListEntry*) |
1135 | 0 | { |
1136 | 0 | } |
1137 | | |
1138 | | void SvListView::Impl::ActionMoving( SvTreeListEntry* pEntry ) |
1139 | 0 | { |
1140 | 0 | SvTreeListEntry* pParent = pEntry->pParent; |
1141 | 0 | assert(pParent && "Model not consistent"); |
1142 | 0 | if (pParent != m_rThis.pModel->pRootItem.get() && pParent->m_Children.size() == 1) |
1143 | 0 | { |
1144 | 0 | const auto iter = m_DataTable.find(pParent); |
1145 | 0 | assert(iter != m_DataTable.end()); |
1146 | 0 | SvViewDataEntry* pViewData = iter->second.get(); |
1147 | 0 | pViewData->SetExpanded(false); |
1148 | 0 | } |
1149 | | // preliminary |
1150 | 0 | m_nVisibleCount = 0; |
1151 | 0 | m_bVisPositionsValid = false; |
1152 | 0 | } |
1153 | | |
1154 | | void SvListView::Impl::ActionMoved() |
1155 | 0 | { |
1156 | 0 | m_nVisibleCount = 0; |
1157 | 0 | m_bVisPositionsValid = false; |
1158 | 0 | } |
1159 | | |
1160 | | void SvListView::Impl::ActionInserted( SvTreeListEntry* pEntry ) |
1161 | 0 | { |
1162 | 0 | DBG_ASSERT(pEntry,"Insert:No Entry"); |
1163 | 0 | std::unique_ptr<SvViewDataEntry> pData(new SvViewDataEntry()); |
1164 | 0 | m_rThis.InitViewData( pData.get(), pEntry ); |
1165 | 0 | std::pair<SvDataTable::iterator, bool> aSuccess = |
1166 | 0 | m_DataTable.insert(std::make_pair(pEntry, std::move(pData))); |
1167 | 0 | DBG_ASSERT(aSuccess.second,"Entry already in View"); |
1168 | 0 | if (m_nVisibleCount && m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry)) |
1169 | 0 | { |
1170 | 0 | m_nVisibleCount = 0; |
1171 | 0 | m_bVisPositionsValid = false; |
1172 | 0 | } |
1173 | 0 | } |
1174 | | |
1175 | | void SvListView::Impl::ActionInsertedTree( SvTreeListEntry* pEntry ) |
1176 | 0 | { |
1177 | 0 | if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry)) |
1178 | 0 | { |
1179 | 0 | m_nVisibleCount = 0; |
1180 | 0 | m_bVisPositionsValid = false; |
1181 | 0 | } |
1182 | | // iterate over entry and its children |
1183 | 0 | SvTreeListEntry* pCurEntry = pEntry; |
1184 | 0 | sal_uInt16 nRefDepth = m_rThis.pModel->GetDepth( pCurEntry ); |
1185 | 0 | while( pCurEntry ) |
1186 | 0 | { |
1187 | 0 | DBG_ASSERT(m_DataTable.find(pCurEntry) != m_DataTable.end(),"Entry already in Table"); |
1188 | 0 | std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry()); |
1189 | 0 | m_rThis.InitViewData( pViewData.get(), pEntry ); |
1190 | 0 | m_DataTable.insert(std::make_pair(pCurEntry, std::move(pViewData))); |
1191 | 0 | pCurEntry = m_rThis.pModel->Next( pCurEntry ); |
1192 | 0 | if ( pCurEntry && m_rThis.pModel->GetDepth(pCurEntry) <= nRefDepth) |
1193 | 0 | pCurEntry = nullptr; |
1194 | 0 | } |
1195 | 0 | } |
1196 | | |
1197 | | void SvListView::Impl::RemoveViewData( SvTreeListEntry* pParent ) |
1198 | 0 | { |
1199 | 0 | for (auto const& it : pParent->m_Children) |
1200 | 0 | { |
1201 | 0 | SvTreeListEntry& rEntry = *it; |
1202 | 0 | m_DataTable.erase(&rEntry); |
1203 | 0 | if (rEntry.HasChildren()) |
1204 | 0 | RemoveViewData(&rEntry); |
1205 | 0 | } |
1206 | 0 | } |
1207 | | |
1208 | | |
1209 | | void SvListView::Impl::ActionRemoving( SvTreeListEntry* pEntry ) |
1210 | 0 | { |
1211 | 0 | assert(pEntry && "Remove:No Entry"); |
1212 | 0 | const auto iter = m_DataTable.find(pEntry); |
1213 | 0 | assert(iter != m_DataTable.end()); |
1214 | 0 | SvViewDataEntry* pViewData = iter->second.get(); |
1215 | 0 | sal_uInt32 nSelRemoved = 0; |
1216 | 0 | if ( pViewData->IsSelected() ) |
1217 | 0 | nSelRemoved = 1 + m_rThis.pModel->GetChildSelectionCount(&m_rThis, pEntry); |
1218 | 0 | m_nSelectionCount -= nSelRemoved; |
1219 | 0 | sal_uInt32 nVisibleRemoved = 0; |
1220 | 0 | if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry)) |
1221 | 0 | nVisibleRemoved = 1 + m_rThis.pModel->GetVisibleChildCount(&m_rThis, pEntry); |
1222 | 0 | if( m_nVisibleCount ) |
1223 | 0 | { |
1224 | | #ifdef DBG_UTIL |
1225 | | if (m_nVisibleCount < nVisibleRemoved) |
1226 | | { |
1227 | | OSL_FAIL("nVisibleRemoved bad"); |
1228 | | } |
1229 | | #endif |
1230 | 0 | m_nVisibleCount -= nVisibleRemoved; |
1231 | 0 | } |
1232 | 0 | m_bVisPositionsValid = false; |
1233 | |
|
1234 | 0 | m_DataTable.erase(pEntry); |
1235 | 0 | RemoveViewData( pEntry ); |
1236 | |
|
1237 | 0 | SvTreeListEntry* pCurEntry = pEntry->pParent; |
1238 | 0 | if (pCurEntry && pCurEntry != m_rThis.pModel->pRootItem.get() && pCurEntry->m_Children.size() == 1) |
1239 | 0 | { |
1240 | 0 | SvDataTable::iterator itr = m_DataTable.find(pCurEntry); |
1241 | 0 | assert(itr != m_DataTable.end() && "Entry not in Table"); |
1242 | 0 | pViewData = itr->second.get(); |
1243 | 0 | pViewData->SetExpanded(false); |
1244 | 0 | } |
1245 | 0 | } |
1246 | | |
1247 | | void SvListView::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1, |
1248 | | SvTreeListEntry* /*pEntry2*/, sal_uInt32 /*nPos*/ ) |
1249 | 0 | { |
1250 | |
|
1251 | 0 | switch( nActionId ) |
1252 | 0 | { |
1253 | 0 | case SvListAction::INSERTED: |
1254 | 0 | m_pImpl->ActionInserted( pEntry1 ); |
1255 | 0 | ModelHasInserted( pEntry1 ); |
1256 | 0 | break; |
1257 | 0 | case SvListAction::INSERTED_TREE: |
1258 | 0 | m_pImpl->ActionInsertedTree( pEntry1 ); |
1259 | 0 | ModelHasInsertedTree( pEntry1 ); |
1260 | 0 | break; |
1261 | 0 | case SvListAction::REMOVING: |
1262 | 0 | ModelIsRemoving( pEntry1 ); |
1263 | 0 | m_pImpl->ActionRemoving( pEntry1 ); |
1264 | 0 | break; |
1265 | 0 | case SvListAction::REMOVED: |
1266 | 0 | ModelHasRemoved( pEntry1 ); |
1267 | 0 | break; |
1268 | 0 | case SvListAction::MOVING: |
1269 | 0 | ModelIsMoving( pEntry1 ); |
1270 | 0 | m_pImpl->ActionMoving( pEntry1 ); |
1271 | 0 | break; |
1272 | 0 | case SvListAction::MOVED: |
1273 | 0 | m_pImpl->ActionMoved(); |
1274 | 0 | ModelHasMoved( pEntry1 ); |
1275 | 0 | break; |
1276 | 0 | case SvListAction::CLEARING: |
1277 | 0 | Clear(); |
1278 | 0 | ModelHasCleared(); // sic! for compatibility reasons! |
1279 | 0 | break; |
1280 | 0 | case SvListAction::CLEARED: |
1281 | 0 | break; |
1282 | 0 | case SvListAction::INVALIDATE_ENTRY: |
1283 | | // no action for the base class |
1284 | 0 | ModelHasEntryInvalidated( pEntry1 ); |
1285 | 0 | break; |
1286 | 0 | case SvListAction::RESORTED: |
1287 | 0 | m_pImpl->m_bVisPositionsValid = false; |
1288 | 0 | break; |
1289 | 0 | case SvListAction::RESORTING: |
1290 | 0 | break; |
1291 | 0 | default: |
1292 | 0 | OSL_FAIL("unknown ActionId"); |
1293 | 0 | } |
1294 | 0 | } |
1295 | | |
1296 | | void SvListView::InitViewData( SvViewDataEntry*, SvTreeListEntry* ) |
1297 | 0 | { |
1298 | 0 | } |
1299 | | |
1300 | | bool SvListView::IsExpanded( SvTreeListEntry* pEntry ) const |
1301 | 0 | { |
1302 | 0 | DBG_ASSERT(pEntry,"IsExpanded:No Entry"); |
1303 | 0 | SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(pEntry); |
1304 | 0 | if (itr == m_pImpl->m_DataTable.end()) |
1305 | 0 | return false; |
1306 | 0 | return itr->second->IsExpanded(); |
1307 | 0 | } |
1308 | | |
1309 | | bool SvListView::IsAllExpanded( SvTreeListEntry* pEntry ) const |
1310 | 0 | { |
1311 | 0 | DBG_ASSERT(pEntry,"IsAllExpanded:No Entry"); |
1312 | 0 | if (!IsExpanded(pEntry)) |
1313 | 0 | return false; |
1314 | 0 | const SvTreeListEntries& rChildren = pEntry->GetChildEntries(); |
1315 | 0 | for (auto& rChild : rChildren) |
1316 | 0 | { |
1317 | 0 | if (rChild->HasChildren() || rChild->HasChildrenOnDemand()) |
1318 | 0 | { |
1319 | 0 | if (!IsAllExpanded(rChild.get())) |
1320 | 0 | return false; |
1321 | 0 | } |
1322 | 0 | } |
1323 | 0 | return true; |
1324 | 0 | } |
1325 | | |
1326 | | bool SvListView::IsSelected(const SvTreeListEntry* pEntry) const |
1327 | 0 | { |
1328 | 0 | DBG_ASSERT(pEntry,"IsExpanded:No Entry"); |
1329 | 0 | SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry)); |
1330 | 0 | if (itr == m_pImpl->m_DataTable.end()) |
1331 | 0 | return false; |
1332 | 0 | return itr->second->IsSelected(); |
1333 | 0 | } |
1334 | | |
1335 | | void SvListView::SetEntryFocus( SvTreeListEntry* pEntry, bool bFocus ) |
1336 | 0 | { |
1337 | 0 | DBG_ASSERT(pEntry,"SetEntryFocus:No Entry"); |
1338 | 0 | SvDataTable::iterator itr = m_pImpl->m_DataTable.find(pEntry); |
1339 | 0 | assert(itr != m_pImpl->m_DataTable.end() && "Entry not in Table"); |
1340 | 0 | itr->second->SetFocus(bFocus); |
1341 | 0 | } |
1342 | | |
1343 | | const SvViewDataEntry* SvListView::GetViewData( const SvTreeListEntry* pEntry ) const |
1344 | 0 | { |
1345 | 0 | SvDataTable::const_iterator itr = |
1346 | 0 | m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry)); |
1347 | 0 | assert(itr != m_pImpl->m_DataTable.end() && "Entry not in model or wrong view"); |
1348 | 0 | if (itr == m_pImpl->m_DataTable.end()) |
1349 | 0 | return nullptr; |
1350 | 0 | return itr->second.get(); |
1351 | 0 | } |
1352 | | |
1353 | | SvViewDataEntry* SvListView::GetViewData( SvTreeListEntry* pEntry ) |
1354 | 0 | { |
1355 | 0 | return const_cast<SvViewDataEntry*>(std::as_const(*this).GetViewData(pEntry)); |
1356 | 0 | } |
1357 | | |
1358 | | sal_Int32 SvTreeList::Compare(const SvTreeListEntry* pLeft, const SvTreeListEntry* pRight) const |
1359 | 0 | { |
1360 | 0 | if( aCompareLink.IsSet()) |
1361 | 0 | { |
1362 | 0 | SvSortData aSortData; |
1363 | 0 | aSortData.pLeft = pLeft; |
1364 | 0 | aSortData.pRight = pRight; |
1365 | 0 | return aCompareLink.Call( aSortData ); |
1366 | 0 | } |
1367 | 0 | return 0; |
1368 | 0 | } |
1369 | | |
1370 | | void SvTreeList::Resort() |
1371 | 0 | { |
1372 | 0 | Broadcast( SvListAction::RESORTING ); |
1373 | 0 | bAbsPositionsValid = false; |
1374 | 0 | ResortChildren( pRootItem.get() ); |
1375 | 0 | Broadcast( SvListAction::RESORTED ); |
1376 | 0 | } |
1377 | | |
1378 | | namespace { |
1379 | | |
1380 | | class SortComparator |
1381 | | { |
1382 | | SvTreeList& mrList; |
1383 | | public: |
1384 | | |
1385 | 0 | explicit SortComparator( SvTreeList& rList ) : mrList(rList) {} |
1386 | | |
1387 | | bool operator() (std::unique_ptr<SvTreeListEntry> const& rpLeft, |
1388 | | std::unique_ptr<SvTreeListEntry> const& rpRight) const |
1389 | 0 | { |
1390 | 0 | int nCompare = mrList.Compare(rpLeft.get(), rpRight.get()); |
1391 | 0 | if (nCompare != 0 && mrList.GetSortMode() == SvSortMode::Descending) |
1392 | 0 | { |
1393 | 0 | if( nCompare < 0 ) |
1394 | 0 | nCompare = 1; |
1395 | 0 | else |
1396 | 0 | nCompare = -1; |
1397 | 0 | } |
1398 | 0 | return nCompare < 0; |
1399 | 0 | } |
1400 | | }; |
1401 | | |
1402 | | } |
1403 | | |
1404 | | void SvTreeList::ResortChildren( SvTreeListEntry* pParent ) |
1405 | 0 | { |
1406 | 0 | assert(pParent && "Parent not set"); |
1407 | |
|
1408 | 0 | if (pParent->m_Children.empty()) |
1409 | 0 | return; |
1410 | | |
1411 | 0 | SortComparator aComp(*this); |
1412 | 0 | std::sort(pParent->m_Children.begin(), pParent->m_Children.end(), aComp); |
1413 | | |
1414 | | // Recursively sort child entries. |
1415 | 0 | for (auto const& it : pParent->m_Children) |
1416 | 0 | { |
1417 | 0 | SvTreeListEntry& r = *it; |
1418 | 0 | ResortChildren(&r); |
1419 | 0 | } |
1420 | |
|
1421 | 0 | SetListPositions(pParent->m_Children); // correct list position in target list |
1422 | 0 | } |
1423 | | |
1424 | | void SvTreeList::GetInsertionPos( SvTreeListEntry const * pEntry, SvTreeListEntry* pParent, |
1425 | | sal_uInt32& rPos ) |
1426 | 0 | { |
1427 | 0 | DBG_ASSERT(pEntry,"No Entry"); |
1428 | |
|
1429 | 0 | if( eSortMode == SvSortMode::None ) |
1430 | 0 | return; |
1431 | | |
1432 | 0 | rPos = TREELIST_ENTRY_NOTFOUND; |
1433 | 0 | const SvTreeListEntries& rChildList = GetChildList(pParent); |
1434 | |
|
1435 | 0 | if (rChildList.empty()) |
1436 | 0 | return; |
1437 | | |
1438 | 0 | tools::Long i = 0; |
1439 | 0 | tools::Long j = rChildList.size()-1; |
1440 | 0 | tools::Long k; |
1441 | 0 | sal_Int32 nCompare = 1; |
1442 | |
|
1443 | 0 | do |
1444 | 0 | { |
1445 | 0 | k = (i+j)/2; |
1446 | 0 | const SvTreeListEntry* pTempEntry = rChildList[k].get(); |
1447 | 0 | nCompare = Compare( pEntry, pTempEntry ); |
1448 | 0 | if (nCompare != 0 && eSortMode == SvSortMode::Descending) |
1449 | 0 | { |
1450 | 0 | if( nCompare < 0 ) |
1451 | 0 | nCompare = 1; |
1452 | 0 | else |
1453 | 0 | nCompare = -1; |
1454 | 0 | } |
1455 | 0 | if( nCompare > 0 ) |
1456 | 0 | i = k + 1; |
1457 | 0 | else |
1458 | 0 | j = k - 1; |
1459 | 0 | } while( (nCompare != 0) && (i <= j) ); |
1460 | |
|
1461 | 0 | if( nCompare != 0 ) |
1462 | 0 | { |
1463 | 0 | if (i > static_cast<tools::Long>(rChildList.size()-1)) // not found, end of list |
1464 | 0 | rPos = TREELIST_ENTRY_NOTFOUND; |
1465 | 0 | else |
1466 | 0 | rPos = i; // not found, middle of list |
1467 | 0 | } |
1468 | 0 | else |
1469 | 0 | rPos = k; |
1470 | 0 | } |
1471 | | |
1472 | | SvTreeListEntry* SvTreeList::GetEntry( SvTreeListEntry* pParent, sal_uInt32 nPos ) const |
1473 | 0 | { if ( !pParent ) |
1474 | 0 | pParent = pRootItem.get(); |
1475 | 0 | SvTreeListEntry* pRet = nullptr; |
1476 | 0 | if (nPos < pParent->m_Children.size()) |
1477 | 0 | pRet = pParent->m_Children[nPos].get(); |
1478 | 0 | return pRet; |
1479 | 0 | } |
1480 | | |
1481 | | SvTreeListEntry* SvTreeList::GetEntry( sal_uInt32 nRootPos ) const |
1482 | 0 | { |
1483 | 0 | SvTreeListEntry* pRet = nullptr; |
1484 | 0 | if (nEntryCount && nRootPos < pRootItem->m_Children.size()) |
1485 | 0 | pRet = pRootItem->m_Children[nRootPos].get(); |
1486 | 0 | return pRet; |
1487 | 0 | } |
1488 | | |
1489 | | const SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent ) const |
1490 | 0 | { |
1491 | 0 | if ( !pParent ) |
1492 | 0 | pParent = pRootItem.get(); |
1493 | 0 | return pParent->m_Children; |
1494 | 0 | } |
1495 | | |
1496 | | SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent ) |
1497 | 0 | { |
1498 | 0 | if ( !pParent ) |
1499 | 0 | pParent = pRootItem.get(); |
1500 | 0 | return pParent->m_Children; |
1501 | 0 | } |
1502 | | |
1503 | | const SvTreeListEntry* SvTreeList::GetParent( const SvTreeListEntry* pEntry ) const |
1504 | 0 | { |
1505 | 0 | const SvTreeListEntry* pParent = pEntry->pParent; |
1506 | 0 | if (pParent == pRootItem.get()) |
1507 | 0 | pParent = nullptr; |
1508 | 0 | return pParent; |
1509 | 0 | } |
1510 | | |
1511 | | SvTreeListEntry* SvTreeList::GetParent( SvTreeListEntry* pEntry ) |
1512 | 0 | { |
1513 | 0 | SvTreeListEntry* pParent = pEntry->pParent; |
1514 | 0 | if (pParent == pRootItem.get()) |
1515 | 0 | pParent = nullptr; |
1516 | 0 | return pParent; |
1517 | 0 | } |
1518 | | |
1519 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |