Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / tools / source / memtools / multisel.cxx
blob8fd0c61aeb27bfb91fc6eaae3e8206fe48e172e4
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 nCurIndex = aSels[ 0 ].Min();
385 return SFX_ENDOFSELECTION;
388 sal_Int32 MultiSelection::LastSelected()
390 nCurSubSel = aSels.size() - 1;
391 bCurValid = !aSels.empty();
393 if ( bCurValid )
394 return nCurIndex = aSels[ nCurSubSel ].Max();
396 return SFX_ENDOFSELECTION;
399 sal_Int32 MultiSelection::NextSelected()
401 if ( !bCurValid )
402 return SFX_ENDOFSELECTION;
404 // is the next index in the current sub selection too?
405 if ( nCurIndex < aSels[ nCurSubSel ].Max() )
406 return ++nCurIndex;
408 // are there further sub selections?
409 if ( ++nCurSubSel < sal_Int32(aSels.size()) )
410 return nCurIndex = aSels[ nCurSubSel ].Min();
412 // we are at the end!
413 return SFX_ENDOFSELECTION;
416 void MultiSelection::SetTotalRange( const Range& rTotRange )
418 aTotRange = rTotRange;
420 // adjust lower boundary
421 Range* pRange = aSels.empty() ? nullptr : &aSels.front();
422 while( pRange )
424 if( pRange->Max() < aTotRange.Min() )
426 aSels.erase( aSels.begin() );
428 else if( pRange->Min() < aTotRange.Min() )
430 pRange->Min() = aTotRange.Min();
431 break;
433 else
434 break;
436 pRange = aSels.empty() ? nullptr : &aSels.front();
439 // adjust upper boundary
440 sal_Int32 nCount = aSels.size();
441 while( nCount )
443 pRange = &aSels[ nCount - 1 ];
444 if( pRange->Min() > aTotRange.Max() )
446 aSels.pop_back();
448 else if( pRange->Max() > aTotRange.Max() )
450 pRange->Max() = aTotRange.Max();
451 break;
453 else
454 break;
456 nCount = aSels.size();
459 // re-calculate selection count
460 nSelCount = 0;
461 for (Range const & rSel : aSels)
462 nSelCount += rSel.Len();
464 bCurValid = false;
465 nCurIndex = 0;
468 // StringRangeEnumerator
470 StringRangeEnumerator::StringRangeEnumerator( const OUString& i_rInput,
471 sal_Int32 i_nMinNumber,
472 sal_Int32 i_nMaxNumber,
473 sal_Int32 i_nLogicalOffset
475 : mnCount( 0 )
476 , mnMin( i_nMinNumber )
477 , mnMax( i_nMaxNumber )
478 , mnOffset( i_nLogicalOffset )
479 , mbValidInput( false )
481 // Parse string only if boundaries are valid.
482 if( mnMin >= 0 && mnMax >= 0 && mnMin <= mnMax )
483 mbValidInput = setRange( i_rInput );
486 bool StringRangeEnumerator::checkValue( sal_Int32 i_nValue, const std::set< sal_Int32 >* i_pPossibleValues ) const
488 if( i_nValue < 0 || i_nValue < mnMin || i_nValue > mnMax )
489 return false;
490 if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
491 return false;
492 return true;
495 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst, sal_Int32 i_nLast, bool bSequence )
497 bool bSuccess = true;
498 if( bSequence )
500 // Check if the range is completely outside of possible pages range
501 if ((i_nFirst < mnMin && i_nLast < mnMin) ||
502 (i_nFirst > mnMax && i_nLast > mnMax))
503 return false;
504 if( i_nFirst < mnMin )
505 i_nFirst = mnMin;
506 if( i_nFirst > mnMax )
507 i_nFirst = mnMax;
508 if( i_nLast < mnMin )
509 i_nLast = mnMin;
510 if( i_nLast > mnMax )
511 i_nLast = mnMax;
512 if( checkValue( i_nFirst ) && checkValue( i_nLast ) )
514 maSequence.push_back( Range( i_nFirst, i_nLast ) );
515 sal_Int32 nNumber = i_nLast - i_nFirst;
516 nNumber = nNumber < 0 ? -nNumber : nNumber;
517 mnCount += nNumber + 1;
519 else
520 bSuccess = false;
522 else
524 if( checkValue( i_nFirst ) )
526 maSequence.push_back( Range( i_nFirst, i_nFirst ) );
527 mnCount++;
529 else if( checkValue( i_nLast ) )
531 maSequence.push_back( Range( i_nLast, i_nLast ) );
532 mnCount++;
534 else
535 bSuccess = false;
538 return bSuccess;
541 void StringRangeEnumerator::insertJoinedRanges(
542 const std::vector< sal_Int32 >& rNumbers )
544 size_t nCount = rNumbers.size();
545 if( nCount == 0 )
546 return;
548 if( nCount == 1 )
550 insertRange( rNumbers[0], -1, false );
551 return;
554 for( size_t i = 0; i < nCount - 1; i++ )
556 sal_Int32 nFirst = rNumbers[i];
557 sal_Int32 nLast = rNumbers[i + 1];
558 if( i > 0 )
560 if ( nFirst > nLast ) nFirst--;
561 else if( nFirst < nLast ) nFirst++;
564 insertRange( nFirst, nLast, nFirst != nLast );
568 bool StringRangeEnumerator::setRange( const OUString& i_rNewRange )
570 mnCount = 0;
571 maSequence.clear();
573 const sal_Unicode* pInput = i_rNewRange.getStr();
574 OUStringBuffer aNumberBuf( 16 );
575 std::vector< sal_Int32 > aNumbers;
576 bool bSequence = false;
577 while( *pInput )
579 while( *pInput >= '0' && *pInput <= '9' )
580 aNumberBuf.append( *pInput++ );
581 if( !aNumberBuf.isEmpty() )
583 sal_Int32 nNumber = aNumberBuf.makeStringAndClear().toInt32() + mnOffset;
584 aNumbers.push_back( nNumber );
585 bSequence = false;
588 if( *pInput == '-' )
590 bSequence = true;
591 if( aNumbers.empty() )
593 // push out-of-range small value, to exclude ranges totally outside of possible range
594 aNumbers.push_back( mnMin-1 );
597 else if( *pInput == ',' || *pInput == ';' )
599 if( bSequence && !aNumbers.empty() )
601 // push out-of-range large value, to exclude ranges totally outside of possible range
602 aNumbers.push_back( mnMax+1 );
604 insertJoinedRanges( aNumbers );
606 aNumbers.clear();
607 bSequence = false;
609 else if( *pInput && *pInput != ' ' )
610 return false; // parse error
612 if( *pInput )
613 pInput++;
615 // insert last entries
616 if( bSequence && !aNumbers.empty() )
618 // push out-of-range large value, to exclude ranges totally outside of possible range
619 aNumbers.push_back( mnMax+1 );
621 insertJoinedRanges( aNumbers );
623 return true;
626 bool StringRangeEnumerator::hasValue( sal_Int32 i_nValue, const std::set< sal_Int32 >* i_pPossibleValues ) const
628 if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
629 return false;
630 size_t n = maSequence.size();
631 for( size_t i= 0; i < n; ++i )
633 const StringRangeEnumerator::Range rRange( maSequence[i] );
634 if( rRange.nFirst < rRange.nLast )
636 if( i_nValue >= rRange.nFirst && i_nValue <= rRange.nLast )
637 return true;
639 else
641 if( i_nValue >= rRange.nLast && i_nValue <= rRange.nFirst )
642 return true;
645 return false;
648 StringRangeEnumerator::Iterator& StringRangeEnumerator::Iterator::operator++()
650 if( nRangeIndex >= 0 && nCurrent >= 0 && pEnumerator )
652 const StringRangeEnumerator::Range& rRange( pEnumerator->maSequence[nRangeIndex] );
653 bool bRangeChange = false;
654 if( rRange.nLast < rRange.nFirst )
656 // backward range
657 if( nCurrent > rRange.nLast )
658 nCurrent--;
659 else
660 bRangeChange = true;
662 else
664 // forward range
665 if( nCurrent < rRange.nLast )
666 nCurrent++;
667 else
668 bRangeChange = true;
670 if( bRangeChange )
672 nRangeIndex++;
673 if( size_t(nRangeIndex) == pEnumerator->maSequence.size() )
675 // reached the end
676 nRangeIndex = nCurrent = -1;
678 else
679 nCurrent = pEnumerator->maSequence[nRangeIndex].nFirst;
681 if( nRangeIndex != -1 && nCurrent != -1 )
683 if( ! pEnumerator->checkValue( nCurrent, pPossibleValues ) )
684 return ++(*this);
687 return *this;
691 bool StringRangeEnumerator::Iterator::operator==( const Iterator& i_rCompare ) const
693 return i_rCompare.pEnumerator == pEnumerator && i_rCompare.nRangeIndex == nRangeIndex && i_rCompare.nCurrent == nCurrent;
696 StringRangeEnumerator::Iterator StringRangeEnumerator::begin( const std::set< sal_Int32 >* i_pPossibleValues ) const
698 StringRangeEnumerator::Iterator it( this,
699 i_pPossibleValues,
700 maSequence.empty() ? -1 : 0,
701 maSequence.empty() ? -1 : maSequence[0].nFirst );
702 if( ! checkValue(*it, i_pPossibleValues ) )
703 ++it;
704 return it;
707 StringRangeEnumerator::Iterator StringRangeEnumerator::end( const std::set< sal_Int32 >* i_pPossibleValues ) const
709 return StringRangeEnumerator::Iterator( this, i_pPossibleValues, -1, -1 );
712 bool StringRangeEnumerator::getRangesFromString( const OUString& i_rPageRange,
713 std::vector< sal_Int32 >& o_rPageVector,
714 sal_Int32 i_nMinNumber,
715 sal_Int32 i_nMaxNumber,
716 sal_Int32 i_nLogicalOffset,
717 std::set< sal_Int32 > const * i_pPossibleValues
720 o_rPageVector.clear();
722 StringRangeEnumerator aEnum( i_rPageRange, i_nMinNumber, i_nMaxNumber, i_nLogicalOffset ) ;
724 //Even if the input range wasn't completely valid, return what ranges could
725 //be extracted from the input.
726 o_rPageVector.reserve( static_cast< size_t >( aEnum.size() ) );
727 for( StringRangeEnumerator::Iterator it = aEnum.begin( i_pPossibleValues );
728 it != aEnum.end( i_pPossibleValues ); ++it )
730 o_rPageVector.push_back( *it );
733 return aEnum.mbValidInput;
736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */