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 nCurIndex
= aSels
[ 0 ].Min();
385 return SFX_ENDOFSELECTION
;
388 sal_Int32
MultiSelection::LastSelected()
390 nCurSubSel
= aSels
.size() - 1;
391 bCurValid
= !aSels
.empty();
394 return nCurIndex
= aSels
[ nCurSubSel
].Max();
396 return SFX_ENDOFSELECTION
;
399 sal_Int32
MultiSelection::NextSelected()
402 return SFX_ENDOFSELECTION
;
404 // is the next index in the current sub selection too?
405 if ( nCurIndex
< aSels
[ nCurSubSel
].Max() )
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();
424 if( pRange
->Max() < aTotRange
.Min() )
426 aSels
.erase( aSels
.begin() );
428 else if( pRange
->Min() < aTotRange
.Min() )
430 pRange
->Min() = aTotRange
.Min();
436 pRange
= aSels
.empty() ? nullptr : &aSels
.front();
439 // adjust upper boundary
440 sal_Int32 nCount
= aSels
.size();
443 pRange
= &aSels
[ nCount
- 1 ];
444 if( pRange
->Min() > aTotRange
.Max() )
448 else if( pRange
->Max() > aTotRange
.Max() )
450 pRange
->Max() = aTotRange
.Max();
456 nCount
= aSels
.size();
459 // re-calculate selection count
461 for (Range
const & rSel
: aSels
)
462 nSelCount
+= rSel
.Len();
468 // StringRangeEnumerator
470 StringRangeEnumerator::StringRangeEnumerator( const OUString
& i_rInput
,
471 sal_Int32 i_nMinNumber
,
472 sal_Int32 i_nMaxNumber
,
473 sal_Int32 i_nLogicalOffset
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
)
490 if( i_pPossibleValues
&& i_pPossibleValues
->find( i_nValue
) == i_pPossibleValues
->end() )
495 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst
, sal_Int32 i_nLast
, bool bSequence
)
497 bool bSuccess
= true;
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
))
504 if( i_nFirst
< mnMin
)
506 if( i_nFirst
> mnMax
)
508 if( i_nLast
< mnMin
)
510 if( 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;
524 if( checkValue( i_nFirst
) )
526 maSequence
.push_back( Range( i_nFirst
, i_nFirst
) );
529 else if( checkValue( i_nLast
) )
531 maSequence
.push_back( Range( i_nLast
, i_nLast
) );
541 void StringRangeEnumerator::insertJoinedRanges(
542 const std::vector
< sal_Int32
>& rNumbers
)
544 size_t nCount
= rNumbers
.size();
550 insertRange( rNumbers
[0], -1, false );
554 for( size_t i
= 0; i
< nCount
- 1; i
++ )
556 sal_Int32 nFirst
= rNumbers
[i
];
557 sal_Int32 nLast
= rNumbers
[i
+ 1];
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
)
573 const sal_Unicode
* pInput
= i_rNewRange
.getStr();
574 OUStringBuffer
aNumberBuf( 16 );
575 std::vector
< sal_Int32
> aNumbers
;
576 bool bSequence
= false;
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
);
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
);
609 else if( *pInput
&& *pInput
!= ' ' )
610 return false; // parse error
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
);
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() )
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
)
641 if( i_nValue
>= rRange
.nLast
&& i_nValue
<= rRange
.nFirst
)
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
)
657 if( nCurrent
> rRange
.nLast
)
665 if( nCurrent
< rRange
.nLast
)
673 if( size_t(nRangeIndex
) == pEnumerator
->maSequence
.size() )
676 nRangeIndex
= nCurrent
= -1;
679 nCurrent
= pEnumerator
->maSequence
[nRangeIndex
].nFirst
;
681 if( nRangeIndex
!= -1 && nCurrent
!= -1 )
683 if( ! pEnumerator
->checkValue( nCurrent
, pPossibleValues
) )
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,
700 maSequence
.empty() ? -1 : 0,
701 maSequence
.empty() ? -1 : maSequence
[0].nFirst
);
702 if( ! checkValue(*it
, i_pPossibleValues
) )
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: */