Bump version to 21.06.18.1
[LibreOffice.git] / tools / source / memtools / multisel.cxx
blob4f915da258a4e739903c2f084d99a2b395faab09
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <tools/multisel.hxx>
23 #include <rtl/ustrbuf.hxx>
25 void MultiSelection::ImplClear()
27 // no selected indexes
28 nSelCount = 0;
29 aSels.clear();
32 sal_Int32 MultiSelection::ImplFindSubSelection( sal_Int32 nIndex ) const
34 // iterate through the sub selections
35 sal_Int32 n = 0;
36 for ( ;
37 n < sal_Int32(aSels.size()) && nIndex > aSels[ n ].Max();
38 ++n ) {} /* empty loop */
39 return n;
42 void MultiSelection::ImplMergeSubSelections( sal_Int32 nPos1, sal_Int32 nPos2 )
44 // didn't a sub selection at nPos2 exist?
45 if ( nPos2 >= sal_Int32(aSels.size()) )
46 return;
48 // did the sub selections touch each other?
49 if ( (aSels[ nPos1 ].Max() + 1) == aSels[ nPos2 ].Min() )
51 // merge them
52 aSels[ nPos1 ].Max() = aSels[ nPos2 ].Max();
53 aSels.erase( aSels.begin() + nPos2 );
57 MultiSelection::MultiSelection():
58 aTotRange( 0, -1 ),
59 nCurSubSel(0),
60 nCurIndex(0),
61 nSelCount(0),
62 bCurValid(false)
66 void MultiSelection::Reset()
68 aTotRange = Range(0, -1);
69 bCurValid = false;
70 // clear the old sub selections
71 ImplClear();
74 MultiSelection::MultiSelection( const MultiSelection& rOrig ) :
75 aTotRange(rOrig.aTotRange),
76 nSelCount(rOrig.nSelCount),
77 bCurValid(rOrig.bCurValid)
79 if ( bCurValid )
81 nCurSubSel = rOrig.nCurSubSel;
82 nCurIndex = rOrig.nCurIndex;
84 else
86 nCurSubSel = 0;
87 nCurIndex = 0;
90 // copy the sub selections
91 for (const Range & rSel : rOrig.aSels)
92 aSels.push_back( rSel );
95 MultiSelection::MultiSelection( const Range& rRange ):
96 aTotRange(rRange),
97 nCurSubSel(0),
98 nCurIndex(0),
99 nSelCount(0),
100 bCurValid(false)
104 MultiSelection::~MultiSelection()
108 MultiSelection& MultiSelection::operator= ( const MultiSelection& rOrig )
110 aTotRange = rOrig.aTotRange;
111 bCurValid = rOrig.bCurValid;
112 if ( bCurValid )
114 nCurSubSel = rOrig.nCurSubSel;
115 nCurIndex = rOrig.nCurIndex;
118 // clear the old and copy the sub selections
119 ImplClear();
120 for (const Range& rSel : rOrig.aSels)
121 aSels.push_back( rSel );
122 nSelCount = rOrig.nSelCount;
124 return *this;
127 void MultiSelection::SelectAll( bool bSelect )
129 ImplClear();
130 if ( bSelect )
132 aSels.push_back( aTotRange );
133 nSelCount = aTotRange.Len();
137 bool MultiSelection::Select( sal_Int32 nIndex, bool bSelect )
139 DBG_ASSERT( aTotRange.IsInside(nIndex), "selected index out of range" );
141 // out of range?
142 if ( !aTotRange.IsInside(nIndex) )
143 return false;
145 // find the virtual target position
146 sal_Int32 nSubSelPos = ImplFindSubSelection( nIndex );
148 if ( bSelect )
150 // is it included in the found sub selection?
151 if ( nSubSelPos < sal_Int32(aSels.size()) && aSels[ nSubSelPos ].IsInside( nIndex ) )
152 // already selected, nothing to do
153 return false;
155 // it will become selected
156 ++nSelCount;
158 // is it at the end of the previous sub selection
159 if ( nSubSelPos > 0 &&
160 aSels[ nSubSelPos-1 ].Max() == (nIndex-1) )
162 // expand the previous sub selection
163 aSels[ nSubSelPos-1 ].Max() = nIndex;
165 // try to merge the previous sub selection
166 ImplMergeSubSelections( nSubSelPos-1, nSubSelPos );
168 // is it at the beginning of the found sub selection
169 else if ( nSubSelPos < sal_Int32(aSels.size())
170 && aSels[ nSubSelPos ].Min() == (nIndex+1)
172 // expand the found sub selection
173 aSels[ nSubSelPos ].Min() = nIndex;
174 else
176 // create a new sub selection
177 if ( nSubSelPos < sal_Int32(aSels.size()) ) {
178 aSels.insert( aSels.begin() + nSubSelPos, Range( nIndex, nIndex ) );
179 } else {
180 aSels.push_back( Range( nIndex, nIndex ) );
182 if ( bCurValid && nCurSubSel >= nSubSelPos )
183 ++nCurSubSel;
186 else
188 // is it excluded from the found sub selection?
189 if ( nSubSelPos >= sal_Int32(aSels.size())
190 || !aSels[ nSubSelPos ].IsInside( nIndex )
192 // not selected, nothing to do
193 return false;
196 // it will become deselected
197 --nSelCount;
199 // is it the only index in the found sub selection?
200 if ( aSels[ nSubSelPos ].Len() == 1 )
202 // remove the complete sub selection
203 aSels.erase( aSels.begin() + nSubSelPos );
204 return true;
207 // is it at the beginning of the found sub selection?
208 if ( aSels[ nSubSelPos ].Min() == nIndex )
209 ++aSels[ nSubSelPos ].Min();
210 // is it at the end of the found sub selection?
211 else if ( aSels[ nSubSelPos ].Max() == nIndex )
212 --aSels[ nSubSelPos ].Max();
213 // it is in the middle of the found sub selection?
214 else
216 // split the sub selection
217 if ( nSubSelPos < sal_Int32(aSels.size()) ) {
218 aSels.insert( aSels.begin() + nSubSelPos, Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
219 } else {
220 aSels.push_back( Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
222 aSels[ nSubSelPos+1 ].Min() = nIndex + 1;
226 return true;
229 void MultiSelection::Select( const Range& rIndexRange, bool bSelect )
231 sal_Int32 nOld;
233 sal_Int32 nTmpMin = rIndexRange.Min();
234 sal_Int32 nTmpMax = rIndexRange.Max();
235 sal_Int32 nCurMin = FirstSelected();
236 sal_Int32 nCurMax = LastSelected();
237 DBG_ASSERT(aTotRange.IsInside(nTmpMax), "selected index out of range" );
238 DBG_ASSERT(aTotRange.IsInside(nTmpMin), "selected index out of range" );
240 // replace whole selection?
241 if( aSels.empty() || (nTmpMin <= nCurMin && nTmpMax >= nCurMax ) )
243 ImplClear();
244 if ( bSelect )
246 aSels.push_back( rIndexRange );
247 nSelCount = rIndexRange.Len();
249 return;
251 // expand on left side?
252 if( nTmpMax < nCurMin )
254 if( bSelect )
256 // extend first range?
257 if( nCurMin > (nTmpMax+1) )
259 aSels.insert( aSels.begin(), rIndexRange );
260 nSelCount += rIndexRange.Len();
262 else
264 auto & rRange = aSels.front();
265 nOld = rRange.Min();
266 rRange.Min() = nTmpMin;
267 nSelCount += ( nOld - nTmpMin );
269 bCurValid = false;
271 return;
273 // expand on right side?
274 else if( nTmpMin > nCurMax )
276 if( bSelect )
278 // extend last range?
279 if( nTmpMin > (nCurMax+1) )
281 aSels.push_back( rIndexRange );
282 nSelCount += rIndexRange.Len();
284 else
286 auto & rRange = aSels.back();
287 nOld = rRange.Max();
288 rRange.Max() = nTmpMax;
289 nSelCount += ( nTmpMax - nOld );
291 bCurValid = false;
293 return;
296 // TODO here is potential for optimization
297 while( nTmpMin <= nTmpMax )
299 Select( nTmpMin, bSelect );
300 nTmpMin++;
304 bool MultiSelection::IsSelected( sal_Int32 nIndex ) const
306 // find the virtual target position
307 sal_Int32 nSubSelPos = ImplFindSubSelection( nIndex );
309 return nSubSelPos < sal_Int32(aSels.size()) && aSels[ nSubSelPos ].IsInside(nIndex);
312 void MultiSelection::Insert( sal_Int32 nIndex, sal_Int32 nCount )
314 // find the virtual target position
315 sal_Int32 nSubSelPos = ImplFindSubSelection( nIndex );
317 // did we need to shift the sub selections?
318 if ( nSubSelPos < sal_Int32(aSels.size()) )
319 { // did we insert an unselected into an existing sub selection?
320 if ( aSels[ nSubSelPos ].Min() != nIndex
321 && aSels[ nSubSelPos ].IsInside(nIndex)
322 ) { // split the sub selection
323 if ( nSubSelPos < sal_Int32(aSels.size()) ) {
324 aSels.insert( aSels.begin() + nSubSelPos, Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
325 } else {
326 aSels.push_back( Range( aSels[ nSubSelPos ].Min(), nIndex-1 ) );
328 ++nSubSelPos;
329 aSels[ nSubSelPos ].Min() = nIndex;
332 // shift the sub selections behind the inserting position
333 for ( sal_Int32 nPos = nSubSelPos; nPos < sal_Int32(aSels.size()); ++nPos )
335 aSels[ nPos ].Min() += nCount;
336 aSels[ nPos ].Max() += nCount;
340 bCurValid = false;
341 aTotRange.Max() += nCount;
344 void MultiSelection::Remove( sal_Int32 nIndex )
346 // find the virtual target position
347 sal_Int32 nSubSelPos = ImplFindSubSelection( nIndex );
349 // did we remove from an existing sub selection?
350 if ( nSubSelPos < sal_Int32(aSels.size())
351 && aSels[ nSubSelPos ].IsInside(nIndex)
353 // does this sub selection only contain the index to be deleted
354 if ( aSels[ nSubSelPos ].Len() == 1 ) {
355 // completely remove the sub selection
356 aSels.erase( aSels.begin() + nSubSelPos );
357 } else {
358 // shorten this sub selection
359 --( aSels[ nSubSelPos++ ].Max() );
362 // adjust the selected counter
363 --nSelCount;
366 // shift the sub selections behind the removed index
367 for ( sal_Int32 nPos = nSubSelPos; nPos < sal_Int32(aSels.size()); ++nPos )
369 --( aSels[ nPos ].Min() );
370 --( aSels[ nPos ].Max() );
373 bCurValid = false;
374 aTotRange.Max() -= 1;
377 sal_Int32 MultiSelection::FirstSelected()
379 nCurSubSel = 0;
381 bCurValid = !aSels.empty();
382 if ( !bCurValid )
383 return SFX_ENDOFSELECTION;
385 nCurIndex = aSels[ 0 ].Min();
386 return nCurIndex;
389 sal_Int32 MultiSelection::LastSelected()
391 nCurSubSel = aSels.size() - 1;
392 bCurValid = !aSels.empty();
394 if ( !bCurValid )
395 return SFX_ENDOFSELECTION;
397 nCurIndex = aSels[ nCurSubSel ].Max();
398 return nCurIndex;
401 sal_Int32 MultiSelection::NextSelected()
403 if ( !bCurValid )
404 return SFX_ENDOFSELECTION;
406 // is the next index in the current sub selection too?
407 if ( nCurIndex < aSels[ nCurSubSel ].Max() )
408 return ++nCurIndex;
410 // are there further sub selections?
411 if ( ++nCurSubSel >= sal_Int32(aSels.size()) )
412 // we are at the end!
413 return SFX_ENDOFSELECTION;
415 nCurIndex = aSels[ nCurSubSel ].Min();
416 return nCurIndex;
419 void MultiSelection::SetTotalRange( const Range& rTotRange )
421 aTotRange = rTotRange;
423 // adjust lower boundary
424 Range* pRange = aSels.empty() ? nullptr : &aSels.front();
425 while( pRange )
427 if( pRange->Max() < aTotRange.Min() )
429 aSels.erase( aSels.begin() );
431 else if( pRange->Min() < aTotRange.Min() )
433 pRange->Min() = aTotRange.Min();
434 break;
436 else
437 break;
439 pRange = aSels.empty() ? nullptr : &aSels.front();
442 // adjust upper boundary
443 sal_Int32 nCount = aSels.size();
444 while( nCount )
446 pRange = &aSels[ nCount - 1 ];
447 if( pRange->Min() > aTotRange.Max() )
449 aSels.pop_back();
451 else if( pRange->Max() > aTotRange.Max() )
453 pRange->Max() = aTotRange.Max();
454 break;
456 else
457 break;
459 nCount = aSels.size();
462 // re-calculate selection count
463 nSelCount = 0;
464 for (Range const & rSel : aSels)
465 nSelCount += rSel.Len();
467 bCurValid = false;
468 nCurIndex = 0;
471 // StringRangeEnumerator
473 StringRangeEnumerator::StringRangeEnumerator( const OUString& i_rInput,
474 sal_Int32 i_nMinNumber,
475 sal_Int32 i_nMaxNumber,
476 sal_Int32 i_nLogicalOffset
478 : mnCount( 0 )
479 , mnMin( i_nMinNumber )
480 , mnMax( i_nMaxNumber )
481 , mnOffset( i_nLogicalOffset )
482 , mbValidInput( false )
484 // Parse string only if boundaries are valid.
485 if( mnMin >= 0 && mnMax >= 0 && mnMin <= mnMax )
486 mbValidInput = setRange( i_rInput );
489 bool StringRangeEnumerator::checkValue( sal_Int32 i_nValue, const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
491 if( i_nValue < 0 || i_nValue < mnMin || i_nValue > mnMax )
492 return false;
493 if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
494 return false;
495 return true;
498 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst, sal_Int32 i_nLast, bool bSequence )
500 bool bSuccess = true;
501 if( bSequence )
503 // Check if the range is completely outside of possible pages range
504 if ((i_nFirst < mnMin && i_nLast < mnMin) ||
505 (i_nFirst > mnMax && i_nLast > mnMax))
506 return false;
507 if( i_nFirst < mnMin )
508 i_nFirst = mnMin;
509 if( i_nFirst > mnMax )
510 i_nFirst = mnMax;
511 if( i_nLast < mnMin )
512 i_nLast = mnMin;
513 if( i_nLast > mnMax )
514 i_nLast = mnMax;
515 if( checkValue( i_nFirst ) && checkValue( i_nLast ) )
517 maSequence.push_back( Range( i_nFirst, i_nLast ) );
518 sal_Int32 nNumber = i_nLast - i_nFirst;
519 nNumber = nNumber < 0 ? -nNumber : nNumber;
520 mnCount += nNumber + 1;
522 else
523 bSuccess = false;
525 else
527 if( checkValue( i_nFirst ) )
529 maSequence.push_back( Range( i_nFirst, i_nFirst ) );
530 mnCount++;
532 else if( checkValue( i_nLast ) )
534 maSequence.push_back( Range( i_nLast, i_nLast ) );
535 mnCount++;
537 else
538 bSuccess = false;
541 return bSuccess;
544 void StringRangeEnumerator::insertJoinedRanges(
545 const std::vector< sal_Int32 >& rNumbers )
547 size_t nCount = rNumbers.size();
548 if( nCount == 0 )
549 return;
551 if( nCount == 1 )
553 insertRange( rNumbers[0], -1, false );
554 return;
557 for( size_t i = 0; i < nCount - 1; i++ )
559 sal_Int32 nFirst = rNumbers[i];
560 sal_Int32 nLast = rNumbers[i + 1];
561 if( i > 0 )
563 if ( nFirst > nLast ) nFirst--;
564 else if( nFirst < nLast ) nFirst++;
567 insertRange( nFirst, nLast, nFirst != nLast );
571 bool StringRangeEnumerator::setRange( const OUString& i_rNewRange )
573 mnCount = 0;
574 maSequence.clear();
576 const sal_Unicode* pInput = i_rNewRange.getStr();
577 OUStringBuffer aNumberBuf( 16 );
578 std::vector< sal_Int32 > aNumbers;
579 bool bSequence = false;
580 while( *pInput )
582 while( *pInput >= '0' && *pInput <= '9' )
583 aNumberBuf.append( *pInput++ );
584 if( !aNumberBuf.isEmpty() )
586 sal_Int32 nNumber = aNumberBuf.makeStringAndClear().toInt32() + mnOffset;
587 aNumbers.push_back( nNumber );
588 bSequence = false;
591 if( *pInput == '-' )
593 bSequence = true;
594 if( aNumbers.empty() )
596 // push out-of-range small value, to exclude ranges totally outside of possible range
597 aNumbers.push_back( mnMin-1 );
600 else if( *pInput == ',' || *pInput == ';' )
602 if( bSequence && !aNumbers.empty() )
604 // push out-of-range large value, to exclude ranges totally outside of possible range
605 aNumbers.push_back( mnMax+1 );
607 insertJoinedRanges( aNumbers );
609 aNumbers.clear();
610 bSequence = false;
612 else if( *pInput && *pInput != ' ' )
613 return false; // parse error
615 if( *pInput )
616 pInput++;
618 // insert last entries
619 if( bSequence && !aNumbers.empty() )
621 // push out-of-range large value, to exclude ranges totally outside of possible range
622 aNumbers.push_back( mnMax+1 );
624 insertJoinedRanges( aNumbers );
626 return true;
629 bool StringRangeEnumerator::hasValue( sal_Int32 i_nValue, const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
631 if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
632 return false;
633 size_t n = maSequence.size();
634 for( size_t i= 0; i < n; ++i )
636 const StringRangeEnumerator::Range rRange( maSequence[i] );
637 if( rRange.nFirst < rRange.nLast )
639 if( i_nValue >= rRange.nFirst && i_nValue <= rRange.nLast )
640 return true;
642 else
644 if( i_nValue >= rRange.nLast && i_nValue <= rRange.nFirst )
645 return true;
648 return false;
651 StringRangeEnumerator::Iterator& StringRangeEnumerator::Iterator::operator++()
653 if( nRangeIndex >= 0 && nCurrent >= 0 && pEnumerator )
655 const StringRangeEnumerator::Range& rRange( pEnumerator->maSequence[nRangeIndex] );
656 bool bRangeChange = false;
657 if( rRange.nLast < rRange.nFirst )
659 // backward range
660 if( nCurrent > rRange.nLast )
661 nCurrent--;
662 else
663 bRangeChange = true;
665 else
667 // forward range
668 if( nCurrent < rRange.nLast )
669 nCurrent++;
670 else
671 bRangeChange = true;
673 if( bRangeChange )
675 nRangeIndex++;
676 if( size_t(nRangeIndex) == pEnumerator->maSequence.size() )
678 // reached the end
679 nRangeIndex = nCurrent = -1;
681 else
682 nCurrent = pEnumerator->maSequence[nRangeIndex].nFirst;
684 if( nRangeIndex != -1 && nCurrent != -1 )
686 if( ! pEnumerator->checkValue( nCurrent, pPossibleValues ) )
687 return ++(*this);
690 return *this;
694 bool StringRangeEnumerator::Iterator::operator==( const Iterator& i_rCompare ) const
696 return i_rCompare.pEnumerator == pEnumerator && i_rCompare.nRangeIndex == nRangeIndex && i_rCompare.nCurrent == nCurrent;
699 StringRangeEnumerator::Iterator StringRangeEnumerator::begin( const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
701 StringRangeEnumerator::Iterator it( this,
702 i_pPossibleValues,
703 maSequence.empty() ? -1 : 0,
704 maSequence.empty() ? -1 : maSequence[0].nFirst );
705 if( ! checkValue(*it, i_pPossibleValues ) )
706 ++it;
707 return it;
710 StringRangeEnumerator::Iterator StringRangeEnumerator::end( const o3tl::sorted_vector< sal_Int32 >* i_pPossibleValues ) const
712 return StringRangeEnumerator::Iterator( this, i_pPossibleValues, -1, -1 );
715 bool StringRangeEnumerator::getRangesFromString( const OUString& i_rPageRange,
716 std::vector< sal_Int32 >& o_rPageVector,
717 sal_Int32 i_nMinNumber,
718 sal_Int32 i_nMaxNumber,
719 sal_Int32 i_nLogicalOffset,
720 o3tl::sorted_vector< sal_Int32 > const * i_pPossibleValues
723 o_rPageVector.clear();
725 StringRangeEnumerator aEnum( i_rPageRange, i_nMinNumber, i_nMaxNumber, i_nLogicalOffset ) ;
727 //Even if the input range wasn't completely valid, return what ranges could
728 //be extracted from the input.
729 o_rPageVector.reserve( static_cast< size_t >( aEnum.size() ) );
730 for( StringRangeEnumerator::Iterator it = aEnum.begin( i_pPossibleValues );
731 it != aEnum.end( i_pPossibleValues ); ++it )
733 o_rPageVector.push_back( *it );
736 return aEnum.mbValidInput;
739 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */