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
30 for ( size_t i
= 0, n
= aSels
.size(); i
< n
; ++i
) {
36 size_t MultiSelection::ImplFindSubSelection( long nIndex
) const
38 // iterate through the sub selections
41 n
< aSels
.size() && nIndex
> aSels
[ n
]->Max();
42 ++n
) {} /* empty loop */
46 bool MultiSelection::ImplMergeSubSelections( size_t nPos1
, size_t nPos2
)
48 // didn't a sub selection at nPos2 exist?
49 if ( nPos2
>= aSels
.size() )
52 // did the sub selections touch each other?
53 if ( (aSels
[ nPos1
]->Max() + 1) == aSels
[ nPos2
]->Min() )
56 aSels
[ nPos1
]->Max() = aSels
[ nPos2
]->Max();
57 ImpSelList::iterator it
= aSels
.begin();
58 ::std::advance( it
, nPos2
);
67 MultiSelection::MultiSelection():
78 MultiSelection::MultiSelection( const MultiSelection
& rOrig
) :
79 aTotRange(rOrig
.aTotRange
),
80 nSelCount(rOrig
.nSelCount
),
81 bCurValid(rOrig
.bCurValid
),
86 nCurSubSel
= rOrig
.nCurSubSel
;
87 nCurIndex
= rOrig
.nCurIndex
;
88 bInverseCur
= rOrig
.bInverseCur
;
97 // copy the sub selections
98 for ( size_t n
= 0; n
< rOrig
.aSels
.size(); ++n
)
99 aSels
.push_back( new Range( *rOrig
.aSels
[ n
] ) );
102 MultiSelection::MultiSelection( const Range
& rRange
):
113 MultiSelection::~MultiSelection()
115 for ( size_t i
= 0, n
= aSels
.size(); i
< n
; ++i
)
120 MultiSelection
& MultiSelection::operator= ( const MultiSelection
& rOrig
)
122 aTotRange
= rOrig
.aTotRange
;
123 bCurValid
= rOrig
.bCurValid
;
126 nCurSubSel
= rOrig
.nCurSubSel
;
127 nCurIndex
= rOrig
.nCurIndex
;
130 // clear the old and copy the sub selections
132 for ( size_t n
= 0; n
< rOrig
.aSels
.size(); ++n
)
133 aSels
.push_back( new Range( *rOrig
.aSels
[ n
] ) );
134 nSelCount
= rOrig
.nSelCount
;
139 bool MultiSelection::operator== ( MultiSelection
& rWith
)
141 if ( aTotRange
!= rWith
.aTotRange
|| nSelCount
!= rWith
.nSelCount
||
142 aSels
.size() != rWith
.aSels
.size() )
145 // compare the sub selections
146 for ( size_t n
= 0; n
< aSels
.size(); ++n
)
147 if ( *aSels
[ n
] != *rWith
.aSels
[ n
] )
152 void MultiSelection::SelectAll( bool bSelect
)
157 aSels
.push_back( new Range(aTotRange
) );
158 nSelCount
= aTotRange
.Len();
162 bool MultiSelection::Select( long nIndex
, bool bSelect
)
164 DBG_ASSERT( aTotRange
.IsInside(nIndex
), "selected index out of range" );
167 if ( !aTotRange
.IsInside(nIndex
) )
170 // find the virtual target position
171 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
175 // is it included in the found sub selection?
176 if ( nSubSelPos
< aSels
.size() && aSels
[ nSubSelPos
]->IsInside( nIndex
) )
177 // already selected, nothing to do
180 // it will become selected
183 // is it at the end of the previous sub selection
184 if ( nSubSelPos
> 0 &&
185 aSels
[ nSubSelPos
-1 ]->Max() == (nIndex
-1) )
187 // expand the previous sub selection
188 aSels
[ nSubSelPos
-1 ]->Max() = nIndex
;
190 // try to merge the previous sub selection
191 ImplMergeSubSelections( nSubSelPos
-1, nSubSelPos
);
193 // is it at the beginning of the found sub selection
194 else if ( nSubSelPos
< aSels
.size()
195 && aSels
[ nSubSelPos
]->Min() == (nIndex
+1)
197 // expand the found sub selection
198 aSels
[ nSubSelPos
]->Min() = nIndex
;
201 // create a new sub selection
202 if ( nSubSelPos
< aSels
.size() ) {
203 ImpSelList::iterator it
= aSels
.begin();
204 ::std::advance( it
, nSubSelPos
);
205 aSels
.insert( it
, new Range( nIndex
, nIndex
) );
207 aSels
.push_back( new Range( nIndex
, nIndex
) );
209 if ( bCurValid
&& nCurSubSel
>= nSubSelPos
)
215 // is it excluded from the found sub selection?
216 if ( nSubSelPos
>= aSels
.size()
217 || !aSels
[ nSubSelPos
]->IsInside( nIndex
)
219 // not selected, nothing to do
223 // it will become deselected
226 // is it the only index in the found sub selection?
227 if ( aSels
[ nSubSelPos
]->Len() == 1 )
229 // remove the complete sub selection
230 ImpSelList::iterator it
= aSels
.begin();
231 ::std::advance( it
, nSubSelPos
);
237 // is it at the beginning of the found sub selection?
238 if ( aSels
[ nSubSelPos
]->Min() == nIndex
)
239 ++aSels
[ nSubSelPos
]->Min();
240 // is it at the end of the found sub selection?
241 else if ( aSels
[ nSubSelPos
]->Max() == nIndex
)
242 --aSels
[ nSubSelPos
]->Max();
243 // it is in the middle of the found sub selection?
246 // split the sub selection
247 if ( nSubSelPos
< aSels
.size() ) {
248 ImpSelList::iterator it
= aSels
.begin();
249 ::std::advance( it
, nSubSelPos
);
250 aSels
.insert( it
, new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
252 aSels
.push_back( new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
254 aSels
[ nSubSelPos
+1 ]->Min() = nIndex
+ 1;
261 void MultiSelection::Select( const Range
& rIndexRange
, bool bSelect
)
266 sal_uIntPtr nTmpMin
= rIndexRange
.Min();
267 sal_uIntPtr nTmpMax
= rIndexRange
.Max();
268 sal_uIntPtr nCurMin
= FirstSelected();
269 sal_uIntPtr nCurMax
= LastSelected();
270 DBG_ASSERT(aTotRange
.IsInside(nTmpMax
), "selected index out of range" );
271 DBG_ASSERT(aTotRange
.IsInside(nTmpMin
), "selected index out of range" );
273 // replace whole selection?
274 if( nTmpMin
<= nCurMin
&& nTmpMax
>= nCurMax
)
279 aSels
.push_back( new Range(rIndexRange
) );
280 nSelCount
= rIndexRange
.Len();
284 // expand on left side?
285 if( nTmpMax
< nCurMin
)
289 // extend first range?
290 if( nCurMin
> (nTmpMax
+1) )
292 pRange
= new Range( rIndexRange
);
293 aSels
.insert( aSels
.begin() , pRange
);
294 nSelCount
+= pRange
->Len();
298 pRange
= aSels
.front();
299 nOld
= pRange
->Min();
300 pRange
->Min() = (long)nTmpMin
;
301 nSelCount
+= ( nOld
- nTmpMin
);
307 // expand on right side?
308 else if( nTmpMin
> nCurMax
)
312 // extend last range?
313 if( nTmpMin
> (nCurMax
+1) )
315 pRange
= new Range( rIndexRange
);
316 aSels
.push_back( pRange
);
317 nSelCount
+= pRange
->Len();
321 pRange
= aSels
.back();
322 nOld
= pRange
->Max();
323 pRange
->Max() = (long)nTmpMax
;
324 nSelCount
+= ( nTmpMax
- nOld
);
331 // TODO here is potential for optimization
332 while( nTmpMin
<= nTmpMax
)
334 Select( nTmpMin
, bSelect
);
339 bool MultiSelection::IsSelected( long nIndex
) const
341 // find the virtual target position
342 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
344 return nSubSelPos
< aSels
.size() && aSels
[ nSubSelPos
]->IsInside(nIndex
);
347 void MultiSelection::Insert( long nIndex
, long nCount
)
349 // find the virtual target position
350 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
352 // did we need to shift the sub selections?
353 if ( nSubSelPos
< aSels
.size() )
354 { // did we insert an unselected into an existing sub selection?
356 && aSels
[ nSubSelPos
]->Min() != nIndex
357 && aSels
[ nSubSelPos
]->IsInside(nIndex
)
358 ) { // split the sub selection
359 if ( nSubSelPos
< aSels
.size() ) {
360 ImpSelList::iterator it
= aSels
.begin();
361 ::std::advance( it
, nSubSelPos
);
362 aSels
.insert( it
, new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
364 aSels
.push_back( new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
367 aSels
[ nSubSelPos
]->Min() = nIndex
;
370 // did we append an selected to an existing sub selection?
373 && aSels
[ nSubSelPos
]->Max() == nIndex
-1
374 ) // expand the previous sub selection
375 aSels
[ nSubSelPos
-1 ]->Max() += nCount
;
377 // did we insert an selected into an existing sub selection?
379 && aSels
[ nSubSelPos
]->Min() == nIndex
380 ) { // expand the sub selection
381 aSels
[ nSubSelPos
]->Max() += nCount
;
385 // shift the sub selections behind the inserting position
386 for ( size_t nPos
= nSubSelPos
; nPos
< aSels
.size(); ++nPos
)
388 aSels
[ nPos
]->Min() += nCount
;
389 aSels
[ nPos
]->Max() += nCount
;
394 aTotRange
.Max() += nCount
;
399 void MultiSelection::Remove( long nIndex
)
401 // find the virtual target position
402 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
404 // did we remove from an existing sub selection?
405 if ( nSubSelPos
< aSels
.size()
406 && aSels
[ nSubSelPos
]->IsInside(nIndex
)
408 // does this sub selection only contain the index to be deleted
409 if ( aSels
[ nSubSelPos
]->Len() == 1 ) {
410 // completely remove the sub selection
411 ImpSelList::iterator it
= aSels
.begin();
412 ::std::advance( it
, nSubSelPos
);
416 // shorten this sub selection
417 --( aSels
[ nSubSelPos
++ ]->Max() );
420 // adjust the selected counter
424 // shift the sub selections behind the removed index
425 for ( size_t nPos
= nSubSelPos
; nPos
< aSels
.size(); ++nPos
)
427 --( aSels
[ nPos
]->Min() );
428 --( aSels
[ nPos
]->Max() );
432 aTotRange
.Max() -= 1;
435 long MultiSelection::ImplFwdUnselected()
438 return SFX_ENDOFSELECTION
;
440 if ( ( nCurSubSel
< aSels
.size() )
441 && ( aSels
[ nCurSubSel
]->Min() <= nCurIndex
)
443 nCurIndex
= aSels
[ nCurSubSel
++ ]->Max() + 1;
445 if ( nCurIndex
<= aTotRange
.Max() )
448 return SFX_ENDOFSELECTION
;
451 long MultiSelection::FirstSelected( bool bInverse
)
453 bInverseCur
= bInverse
;
458 bCurValid
= nSelCount
< sal_uIntPtr(aTotRange
.Len());
462 return ImplFwdUnselected();
467 bCurValid
= !aSels
.empty();
469 return nCurIndex
= aSels
[ 0 ]->Min();
472 return SFX_ENDOFSELECTION
;
475 long MultiSelection::LastSelected()
477 nCurSubSel
= aSels
.size() - 1;
478 bCurValid
= !aSels
.empty();
481 return nCurIndex
= aSels
[ nCurSubSel
]->Max();
483 return SFX_ENDOFSELECTION
;
486 long MultiSelection::NextSelected()
489 return SFX_ENDOFSELECTION
;
494 return ImplFwdUnselected();
498 // is the next index in the current sub selection too?
499 if ( nCurIndex
< aSels
[ nCurSubSel
]->Max() )
502 // are there further sub selections?
503 if ( ++nCurSubSel
< aSels
.size() )
504 return nCurIndex
= aSels
[ nCurSubSel
]->Min();
506 // we are at the end!
507 return SFX_ENDOFSELECTION
;
511 void MultiSelection::SetTotalRange( const Range
& rTotRange
)
513 aTotRange
= rTotRange
;
515 // adjust lower boundary
516 Range
* pRange
= aSels
.empty() ? NULL
: aSels
.front();
519 if( pRange
->Max() < aTotRange
.Min() )
522 aSels
.erase( aSels
.begin() );
524 else if( pRange
->Min() < aTotRange
.Min() )
526 pRange
->Min() = aTotRange
.Min();
532 pRange
= aSels
.empty() ? NULL
: aSels
.front();
535 // adjust upper boundary
536 size_t nCount
= aSels
.size();
539 pRange
= aSels
[ nCount
- 1 ];
540 if( pRange
->Min() > aTotRange
.Max() )
545 else if( pRange
->Max() > aTotRange
.Max() )
547 pRange
->Max() = aTotRange
.Max();
553 nCount
= aSels
.size();
556 // re-calculate selection count
558 for ( size_t i
= 0, n
= aSels
.size(); i
< n
; ++ i
)
559 nSelCount
+= aSels
[i
]->Len();
565 // StringRangeEnumerator
567 StringRangeEnumerator::StringRangeEnumerator( const OUString
& i_rInput
,
568 sal_Int32 i_nMinNumber
,
569 sal_Int32 i_nMaxNumber
,
570 sal_Int32 i_nLogicalOffset
573 , mnMin( i_nMinNumber
)
574 , mnMax( i_nMaxNumber
)
575 , mnOffset( i_nLogicalOffset
)
576 , mbValidInput( false )
578 // Parse string only if boundaries are valid.
579 if( mnMin
>= 0 && mnMax
>= 0 && mnMin
<= mnMax
)
580 mbValidInput
= setRange( i_rInput
);
583 bool StringRangeEnumerator::checkValue( sal_Int32 i_nValue
, const std::set
< sal_Int32
>* i_pPossibleValues
) const
585 if( i_nValue
< 0 || i_nValue
< mnMin
|| i_nValue
> mnMax
)
587 if( i_pPossibleValues
&& i_pPossibleValues
->find( i_nValue
) == i_pPossibleValues
->end() )
592 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst
, sal_Int32 i_nLast
, bool bSequence
, bool bMayAdjust
)
594 bool bSuccess
= true;
599 if( i_nFirst
< mnMin
)
601 if( i_nFirst
> mnMax
)
603 if( i_nLast
< mnMin
)
605 if( i_nLast
> mnMax
)
608 if( checkValue( i_nFirst
) && checkValue( i_nLast
) )
610 maSequence
.push_back( Range( i_nFirst
, i_nLast
) );
611 sal_Int32 nNumber
= i_nLast
- i_nFirst
;
612 nNumber
= nNumber
< 0 ? -nNumber
: nNumber
;
613 mnCount
+= nNumber
+ 1;
620 if( checkValue( i_nFirst
) )
622 maSequence
.push_back( Range( i_nFirst
, i_nFirst
) );
625 else if( checkValue( i_nLast
) )
627 maSequence
.push_back( Range( i_nLast
, i_nLast
) );
637 bool StringRangeEnumerator::insertJoinedRanges(
638 const std::vector
< sal_Int32
>& rNumbers
, bool i_bStrict
)
640 size_t nCount
= rNumbers
.size();
645 return insertRange( rNumbers
[0], -1, false, ! i_bStrict
);
647 for( size_t i
= 0; i
< nCount
- 1; i
++ )
649 sal_Int32 nFirst
= rNumbers
[i
];
650 sal_Int32 nLast
= rNumbers
[i
+ 1];
653 if ( nFirst
> nLast
) nFirst
--;
654 else if( nFirst
< nLast
) nFirst
++;
657 if ( ! insertRange( nFirst
, nLast
, nFirst
!= nLast
, ! i_bStrict
) && i_bStrict
)
664 bool StringRangeEnumerator::setRange( const OUString
& i_rNewRange
, bool i_bStrict
)
669 const sal_Unicode
* pInput
= i_rNewRange
.getStr();
670 OUStringBuffer
aNumberBuf( 16 );
671 std::vector
< sal_Int32
> aNumbers
;
672 bool bSequence
= false;
675 while( *pInput
>= '0' && *pInput
<= '9' )
676 aNumberBuf
.append( *pInput
++ );
677 if( !aNumberBuf
.isEmpty() )
679 sal_Int32 nNumber
= aNumberBuf
.makeStringAndClear().toInt32() + mnOffset
;
680 aNumbers
.push_back( nNumber
);
687 if( aNumbers
.empty() )
688 aNumbers
.push_back( mnMin
);
690 else if( *pInput
== ',' || *pInput
== ';' )
692 if( bSequence
&& !aNumbers
.empty() )
693 aNumbers
.push_back( mnMax
);
694 if( ! insertJoinedRanges( aNumbers
, i_bStrict
) && i_bStrict
)
700 else if( *pInput
&& *pInput
!= ' ' )
701 return false; // parse error
706 // insert last entries
707 if( bSequence
&& !aNumbers
.empty() )
708 aNumbers
.push_back( mnMax
);
709 if( ! insertJoinedRanges( aNumbers
, i_bStrict
) && i_bStrict
)
715 bool StringRangeEnumerator::hasValue( sal_Int32 i_nValue
, const std::set
< sal_Int32
>* i_pPossibleValues
) const
717 if( i_pPossibleValues
&& i_pPossibleValues
->find( i_nValue
) == i_pPossibleValues
->end() )
719 size_t n
= maSequence
.size();
720 for( size_t i
= 0; i
< n
; ++i
)
722 const StringRangeEnumerator::Range
rRange( maSequence
[i
] );
723 if( rRange
.nFirst
< rRange
.nLast
)
725 if( i_nValue
>= rRange
.nFirst
&& i_nValue
<= rRange
.nLast
)
730 if( i_nValue
>= rRange
.nLast
&& i_nValue
<= rRange
.nFirst
)
737 StringRangeEnumerator::Iterator
& StringRangeEnumerator::Iterator::operator++()
739 if( nRangeIndex
>= 0 && nCurrent
>= 0 && pEnumerator
)
741 const StringRangeEnumerator::Range
& rRange( pEnumerator
->maSequence
[nRangeIndex
] );
742 bool bRangeChange
= false;
743 if( rRange
.nLast
< rRange
.nFirst
)
746 if( nCurrent
> rRange
.nLast
)
754 if( nCurrent
< rRange
.nLast
)
762 if( size_t(nRangeIndex
) == pEnumerator
->maSequence
.size() )
765 nRangeIndex
= nCurrent
= -1;
768 nCurrent
= pEnumerator
->maSequence
[nRangeIndex
].nFirst
;
770 if( nRangeIndex
!= -1 && nCurrent
!= -1 )
772 if( ! pEnumerator
->checkValue( nCurrent
, pPossibleValues
) )
780 bool StringRangeEnumerator::Iterator::operator==( const Iterator
& i_rCompare
) const
782 return i_rCompare
.pEnumerator
== pEnumerator
&& i_rCompare
.nRangeIndex
== nRangeIndex
&& i_rCompare
.nCurrent
== nCurrent
;
785 StringRangeEnumerator::Iterator
StringRangeEnumerator::begin( const std::set
< sal_Int32
>* i_pPossibleValues
) const
787 StringRangeEnumerator::Iterator
it( this,
789 maSequence
.empty() ? -1 : 0,
790 maSequence
.empty() ? -1 : maSequence
[0].nFirst
);
791 if( ! checkValue(*it
, i_pPossibleValues
) )
796 StringRangeEnumerator::Iterator
StringRangeEnumerator::end( const std::set
< sal_Int32
>* i_pPossibleValues
) const
798 return StringRangeEnumerator::Iterator( this, i_pPossibleValues
, -1, -1 );
801 bool StringRangeEnumerator::getRangesFromString( const OUString
& i_rPageRange
,
802 std::vector
< sal_Int32
>& o_rPageVector
,
803 sal_Int32 i_nMinNumber
,
804 sal_Int32 i_nMaxNumber
,
805 sal_Int32 i_nLogicalOffset
,
806 std::set
< sal_Int32
>* i_pPossibleValues
809 o_rPageVector
.clear();
811 StringRangeEnumerator
aEnum( i_rPageRange
, i_nMinNumber
, i_nMaxNumber
, i_nLogicalOffset
) ;
813 //Even if the input range wasn't completely valid, return what ranges could
814 //be extracted from the input.
815 o_rPageVector
.reserve( static_cast< size_t >( aEnum
.size() ) );
816 for( StringRangeEnumerator::Iterator it
= aEnum
.begin( i_pPossibleValues
);
817 it
!= aEnum
.end( i_pPossibleValues
); ++it
)
819 o_rPageVector
.push_back( *it
);
822 return aEnum
.isValidInput();
825 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */