1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
32 sal_Int32
MultiSelection::ImplFindSubSelection( sal_Int32 nIndex
) const
34 // iterate through the sub selections
37 n
< sal_Int32(aSels
.size()) && nIndex
> aSels
[ n
].Max();
38 ++n
) {} /* empty loop */
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()) )
48 // did the sub selections touch each other?
49 if ( (aSels
[ nPos1
].Max() + 1) == aSels
[ nPos2
].Min() )
52 aSels
[ nPos1
].Max() = aSels
[ nPos2
].Max();
53 aSels
.erase( aSels
.begin() + nPos2
);
57 MultiSelection::MultiSelection():
66 void MultiSelection::Reset()
68 aTotRange
= Range(0, -1);
70 // clear the old sub selections
74 MultiSelection::MultiSelection( const MultiSelection
& rOrig
) :
75 aTotRange(rOrig
.aTotRange
),
76 nSelCount(rOrig
.nSelCount
),
77 bCurValid(rOrig
.bCurValid
)
81 nCurSubSel
= rOrig
.nCurSubSel
;
82 nCurIndex
= rOrig
.nCurIndex
;
90 // copy the sub selections
91 for (const Range
& rSel
: rOrig
.aSels
)
92 aSels
.push_back( rSel
);
95 MultiSelection::MultiSelection( const Range
& rRange
):
104 MultiSelection::~MultiSelection()
108 MultiSelection
& MultiSelection::operator= ( const MultiSelection
& rOrig
)
110 aTotRange
= rOrig
.aTotRange
;
111 bCurValid
= rOrig
.bCurValid
;
114 nCurSubSel
= rOrig
.nCurSubSel
;
115 nCurIndex
= rOrig
.nCurIndex
;
118 // clear the old and copy the sub selections
120 for (const Range
& rSel
: rOrig
.aSels
)
121 aSels
.push_back( rSel
);
122 nSelCount
= rOrig
.nSelCount
;
127 void MultiSelection::SelectAll( bool 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" );
142 if ( !aTotRange
.IsInside(nIndex
) )
145 // find the virtual target position
146 sal_Int32 nSubSelPos
= ImplFindSubSelection( nIndex
);
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
155 // it will become selected
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
;
176 // create a new sub selection
177 if ( nSubSelPos
< sal_Int32(aSels
.size()) ) {
178 aSels
.insert( aSels
.begin() + nSubSelPos
, Range( nIndex
, nIndex
) );
180 aSels
.push_back( Range( nIndex
, nIndex
) );
182 if ( bCurValid
&& nCurSubSel
>= nSubSelPos
)
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
196 // it will become deselected
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
);
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?
216 // split the sub selection
217 if ( nSubSelPos
< sal_Int32(aSels
.size()) ) {
218 aSels
.insert( aSels
.begin() + nSubSelPos
, Range( aSels
[ nSubSelPos
].Min(), nIndex
-1 ) );
220 aSels
.push_back( Range( aSels
[ nSubSelPos
].Min(), nIndex
-1 ) );
222 aSels
[ nSubSelPos
+1 ].Min() = nIndex
+ 1;
229 void MultiSelection::Select( const Range
& rIndexRange
, bool bSelect
)
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
) )
246 aSels
.push_back( rIndexRange
);
247 nSelCount
= rIndexRange
.Len();
251 // expand on left side?
252 if( nTmpMax
< nCurMin
)
256 // extend first range?
257 if( nCurMin
> (nTmpMax
+1) )
259 aSels
.insert( aSels
.begin(), rIndexRange
);
260 nSelCount
+= rIndexRange
.Len();
264 auto & rRange
= aSels
.front();
266 rRange
.Min() = nTmpMin
;
267 nSelCount
+= ( nOld
- nTmpMin
);
273 // expand on right side?
274 else if( nTmpMin
> nCurMax
)
278 // extend last range?
279 if( nTmpMin
> (nCurMax
+1) )
281 aSels
.push_back( rIndexRange
);
282 nSelCount
+= rIndexRange
.Len();
286 auto & rRange
= aSels
.back();
288 rRange
.Max() = nTmpMax
;
289 nSelCount
+= ( nTmpMax
- nOld
);
296 // TODO here is potential for optimization
297 while( nTmpMin
<= nTmpMax
)
299 Select( nTmpMin
, bSelect
);
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 ) );
326 aSels
.push_back( Range( aSels
[ nSubSelPos
].Min(), nIndex
-1 ) );
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
;
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
);
358 // shorten this sub selection
359 --( aSels
[ nSubSelPos
++ ].Max() );
362 // adjust the selected counter
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() );
374 aTotRange
.Max() -= 1;
377 sal_Int32
MultiSelection::FirstSelected()
381 bCurValid
= !aSels
.empty();
383 return SFX_ENDOFSELECTION
;
385 nCurIndex
= aSels
[ 0 ].Min();
389 sal_Int32
MultiSelection::LastSelected()
391 nCurSubSel
= aSels
.size() - 1;
392 bCurValid
= !aSels
.empty();
395 return SFX_ENDOFSELECTION
;
397 nCurIndex
= aSels
[ nCurSubSel
].Max();
401 sal_Int32
MultiSelection::NextSelected()
404 return SFX_ENDOFSELECTION
;
406 // is the next index in the current sub selection too?
407 if ( nCurIndex
< aSels
[ nCurSubSel
].Max() )
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();
419 void MultiSelection::SetTotalRange( const Range
& rTotRange
)
421 aTotRange
= rTotRange
;
423 // adjust lower boundary
424 Range
* pRange
= aSels
.empty() ? nullptr : &aSels
.front();
427 if( pRange
->Max() < aTotRange
.Min() )
429 aSels
.erase( aSels
.begin() );
431 else if( pRange
->Min() < aTotRange
.Min() )
433 pRange
->Min() = aTotRange
.Min();
439 pRange
= aSels
.empty() ? nullptr : &aSels
.front();
442 // adjust upper boundary
443 sal_Int32 nCount
= aSels
.size();
446 pRange
= &aSels
[ nCount
- 1 ];
447 if( pRange
->Min() > aTotRange
.Max() )
451 else if( pRange
->Max() > aTotRange
.Max() )
453 pRange
->Max() = aTotRange
.Max();
459 nCount
= aSels
.size();
462 // re-calculate selection count
464 for (Range
const & rSel
: aSels
)
465 nSelCount
+= rSel
.Len();
471 // StringRangeEnumerator
473 StringRangeEnumerator::StringRangeEnumerator( const OUString
& i_rInput
,
474 sal_Int32 i_nMinNumber
,
475 sal_Int32 i_nMaxNumber
,
476 sal_Int32 i_nLogicalOffset
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
)
493 if( i_pPossibleValues
&& i_pPossibleValues
->find( i_nValue
) == i_pPossibleValues
->end() )
498 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst
, sal_Int32 i_nLast
, bool bSequence
)
500 bool bSuccess
= true;
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
))
507 if( i_nFirst
< mnMin
)
509 if( i_nFirst
> mnMax
)
511 if( i_nLast
< mnMin
)
513 if( 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;
527 if( checkValue( i_nFirst
) )
529 maSequence
.push_back( Range( i_nFirst
, i_nFirst
) );
532 else if( checkValue( i_nLast
) )
534 maSequence
.push_back( Range( i_nLast
, i_nLast
) );
544 void StringRangeEnumerator::insertJoinedRanges(
545 const std::vector
< sal_Int32
>& rNumbers
)
547 size_t nCount
= rNumbers
.size();
553 insertRange( rNumbers
[0], -1, false );
557 for( size_t i
= 0; i
< nCount
- 1; i
++ )
559 sal_Int32 nFirst
= rNumbers
[i
];
560 sal_Int32 nLast
= rNumbers
[i
+ 1];
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
)
576 const sal_Unicode
* pInput
= i_rNewRange
.getStr();
577 OUStringBuffer
aNumberBuf( 16 );
578 std::vector
< sal_Int32
> aNumbers
;
579 bool bSequence
= false;
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
);
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
);
612 else if( *pInput
&& *pInput
!= ' ' )
613 return false; // parse error
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
);
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() )
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
)
644 if( i_nValue
>= rRange
.nLast
&& i_nValue
<= rRange
.nFirst
)
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
)
660 if( nCurrent
> rRange
.nLast
)
668 if( nCurrent
< rRange
.nLast
)
676 if( size_t(nRangeIndex
) == pEnumerator
->maSequence
.size() )
679 nRangeIndex
= nCurrent
= -1;
682 nCurrent
= pEnumerator
->maSequence
[nRangeIndex
].nFirst
;
684 if( nRangeIndex
!= -1 && nCurrent
!= -1 )
686 if( ! pEnumerator
->checkValue( nCurrent
, pPossibleValues
) )
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,
703 maSequence
.empty() ? -1 : 0,
704 maSequence
.empty() ? -1 : maSequence
[0].nFirst
);
705 if( ! checkValue(*it
, i_pPossibleValues
) )
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: */