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 .
21 #define private public
25 #include <tools/debug.hxx>
26 #include <tools/multisel.hxx>
28 #include "rtl/ustrbuf.hxx"
38 static void Print( const MultiSelection
* pSel
)
40 DbgOutf( "TotRange: %4ld-%4ld\n",
41 pSel
->aTotRange
.Min(), pSel
->aTotRange
.Max() );
42 if ( pSel
->bCurValid
)
44 DbgOutf( "CurSubSel: %4ld\n", pSel
->nCurSubSel
);
45 DbgOutf( "CurIndex: %4ld\n", pSel
->nCurIndex
);
47 DbgOutf( "SelCount: %4ld\n", pSel
->nSelCount
);
48 DbgOutf( "SubCount: %4ld\n", pSel
->aSels
.Count() );
49 for ( sal_uIntPtr nPos
= 0; nPos
< pSel
->aSels
.Count(); ++nPos
)
51 DbgOutf( "SubSel #%2ld: %4ld-%4ld\n", nPos
,
52 pSel
->aSels
.GetObject(nPos
)->Min(),
53 pSel
->aSels
.GetObject(nPos
)->Max() );
60 void MultiSelection::ImplClear()
62 // no selected indexes
65 for ( size_t i
= 0, n
= aSels
.size(); i
< n
; ++i
) {
71 size_t MultiSelection::ImplFindSubSelection( long nIndex
) const
73 // iterate through the sub selections
76 n
< aSels
.size() && nIndex
> aSels
[ n
]->Max();
77 ++n
) {} /* empty loop */
81 sal_Bool
MultiSelection::ImplMergeSubSelections( size_t nPos1
, size_t nPos2
)
83 // didn't a sub selection at nPos2 exist?
84 if ( nPos2
>= aSels
.size() )
87 // did the sub selections touch each other?
88 if ( (aSels
[ nPos1
]->Max() + 1) == aSels
[ nPos2
]->Min() )
91 aSels
[ nPos1
]->Max() = aSels
[ nPos2
]->Max();
92 ImpSelList::iterator it
= aSels
.begin();
93 ::std::advance( it
, nPos2
);
102 MultiSelection::MultiSelection():
106 bCurValid(sal_False
),
107 bSelectNew(sal_False
)
111 MultiSelection::MultiSelection( const MultiSelection
& rOrig
) :
112 aTotRange(rOrig
.aTotRange
),
113 nSelCount(rOrig
.nSelCount
),
114 bCurValid(rOrig
.bCurValid
),
115 bSelectNew(sal_False
)
119 nCurSubSel
= rOrig
.nCurSubSel
;
120 nCurIndex
= rOrig
.nCurIndex
;
123 // copy the sub selections
124 for ( size_t n
= 0; n
< rOrig
.aSels
.size(); ++n
)
125 aSels
.push_back( new Range( *rOrig
.aSels
[ n
] ) );
128 MultiSelection::MultiSelection( const Range
& rRange
):
132 bCurValid(sal_False
),
133 bSelectNew(sal_False
)
137 MultiSelection::~MultiSelection()
139 for ( size_t i
= 0, n
= aSels
.size(); i
< n
; ++i
)
144 MultiSelection
& MultiSelection::operator= ( const MultiSelection
& rOrig
)
146 aTotRange
= rOrig
.aTotRange
;
147 bCurValid
= rOrig
.bCurValid
;
150 nCurSubSel
= rOrig
.nCurSubSel
;
151 nCurIndex
= rOrig
.nCurIndex
;
154 // clear the old and copy the sub selections
156 for ( size_t n
= 0; n
< rOrig
.aSels
.size(); ++n
)
157 aSels
.push_back( new Range( *rOrig
.aSels
[ n
] ) );
158 nSelCount
= rOrig
.nSelCount
;
163 sal_Bool
MultiSelection::operator== ( MultiSelection
& rWith
)
165 if ( aTotRange
!= rWith
.aTotRange
|| nSelCount
!= rWith
.nSelCount
||
166 aSels
.size() != rWith
.aSels
.size() )
169 // compare the sub seletions
170 for ( size_t n
= 0; n
< aSels
.size(); ++n
)
171 if ( *aSels
[ n
] != *rWith
.aSels
[ n
] )
176 void MultiSelection::SelectAll( sal_Bool bSelect
)
178 DBG(DbgOutf( "::SelectAll(%s)\n", bSelect
? "sal_True" : "sal_False" ));
183 aSels
.push_back( new Range(aTotRange
) );
184 nSelCount
= aTotRange
.Len();
190 sal_Bool
MultiSelection::Select( long nIndex
, sal_Bool bSelect
)
192 DBG_ASSERT( aTotRange
.IsInside(nIndex
), "selected index out of range" );
195 if ( !aTotRange
.IsInside(nIndex
) )
198 // find the virtual target position
199 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
203 // is it included in the found sub selection?
204 if ( nSubSelPos
< aSels
.size() && aSels
[ nSubSelPos
]->IsInside( nIndex
) )
205 // already selected, nothing to do
208 // it will become selected
211 // is it at the end of the previous sub selection
212 if ( nSubSelPos
> 0 &&
213 aSels
[ nSubSelPos
-1 ]->Max() == (nIndex
-1) )
215 // expand the previous sub selection
216 aSels
[ nSubSelPos
-1 ]->Max() = nIndex
;
218 // try to merge the previous sub selection
219 ImplMergeSubSelections( nSubSelPos
-1, nSubSelPos
);
221 // is is at the beginning of the found sub selection
222 else if ( nSubSelPos
< aSels
.size()
223 && aSels
[ nSubSelPos
]->Min() == (nIndex
+1)
225 // expand the found sub selection
226 aSels
[ nSubSelPos
]->Min() = nIndex
;
229 // create a new sub selection
230 if ( nSubSelPos
< aSels
.size() ) {
231 ImpSelList::iterator it
= aSels
.begin();
232 ::std::advance( it
, nSubSelPos
);
233 aSels
.insert( it
, new Range( nIndex
, nIndex
) );
235 aSels
.push_back( new Range( nIndex
, nIndex
) );
237 if ( bCurValid
&& nCurSubSel
>= nSubSelPos
)
243 // is it excluded from the found sub selection?
244 if ( nSubSelPos
>= aSels
.size()
245 || !aSels
[ nSubSelPos
]->IsInside( nIndex
)
247 // not selected, nothing to do
252 // it will become deselected
255 // is it the only index in the found sub selection?
256 if ( aSels
[ nSubSelPos
]->Len() == 1 )
258 // remove the complete sub selection
259 ImpSelList::iterator it
= aSels
.begin();
260 ::std::advance( it
, nSubSelPos
);
267 // is it at the beginning of the found sub selection?
268 if ( aSels
[ nSubSelPos
]->Min() == nIndex
)
269 ++aSels
[ nSubSelPos
]->Min();
270 // is it at the end of the found sub selection?
271 else if ( aSels
[ nSubSelPos
]->Max() == nIndex
)
272 --aSels
[ nSubSelPos
]->Max();
273 // it is in the middle of the found sub selection?
276 // split the sub selection
277 if ( nSubSelPos
< aSels
.size() ) {
278 ImpSelList::iterator it
= aSels
.begin();
279 ::std::advance( it
, nSubSelPos
);
280 aSels
.insert( it
, new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
282 aSels
.push_back( new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
284 aSels
[ nSubSelPos
+1 ]->Min() = nIndex
+ 1;
293 void MultiSelection::Select( const Range
& rIndexRange
, sal_Bool bSelect
)
298 sal_uIntPtr nTmpMin
= rIndexRange
.Min();
299 sal_uIntPtr nTmpMax
= rIndexRange
.Max();
300 sal_uIntPtr nCurMin
= FirstSelected();
301 sal_uIntPtr nCurMax
= LastSelected();
302 DBG_ASSERT(aTotRange
.IsInside(nTmpMax
), "selected index out of range" );
303 DBG_ASSERT(aTotRange
.IsInside(nTmpMin
), "selected index out of range" );
305 // replace whole selection?
306 if( nTmpMin
<= nCurMin
&& nTmpMax
>= nCurMax
)
311 aSels
.push_back( new Range(rIndexRange
) );
312 nSelCount
= rIndexRange
.Len();
316 // expand on left side?
317 if( nTmpMax
< nCurMin
)
321 // extend first range?
322 if( nCurMin
> (nTmpMax
+1) )
324 pRange
= new Range( rIndexRange
);
325 aSels
.insert( aSels
.begin() , pRange
);
326 nSelCount
+= pRange
->Len();
330 pRange
= aSels
.front();
331 nOld
= pRange
->Min();
332 pRange
->Min() = (long)nTmpMin
;
333 nSelCount
+= ( nOld
- nTmpMin
);
335 bCurValid
= sal_False
;
339 // expand on right side?
340 else if( nTmpMin
> nCurMax
)
344 // extend last range?
345 if( nTmpMin
> (nCurMax
+1) )
347 pRange
= new Range( rIndexRange
);
348 aSels
.push_back( pRange
);
349 nSelCount
+= pRange
->Len();
353 pRange
= aSels
.back();
354 nOld
= pRange
->Max();
355 pRange
->Max() = (long)nTmpMax
;
356 nSelCount
+= ( nTmpMax
- nOld
);
358 bCurValid
= sal_False
;
363 // TODO here is potential for optimization
364 while( nTmpMin
<= nTmpMax
)
366 Select( nTmpMin
, bSelect
);
371 sal_Bool
MultiSelection::IsSelected( long nIndex
) const
373 // find the virtual target position
374 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
376 return nSubSelPos
< aSels
.size() && aSels
[ nSubSelPos
]->IsInside(nIndex
);
379 void MultiSelection::Insert( long nIndex
, long nCount
)
381 DBG(DbgOutf( "::Insert(%ld, %ld)\n", nIndex
, nCount
));
383 // find the virtual target position
384 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
386 // did we need to shift the sub selections?
387 if ( nSubSelPos
< aSels
.size() )
388 { // did we insert an unselected into an existing sub selection?
390 && aSels
[ nSubSelPos
]->Min() != nIndex
391 && aSels
[ nSubSelPos
]->IsInside(nIndex
)
392 ) { // split the sub selection
393 if ( nSubSelPos
< aSels
.size() ) {
394 ImpSelList::iterator it
= aSels
.begin();
395 ::std::advance( it
, nSubSelPos
);
396 aSels
.insert( it
, new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
398 aSels
.push_back( new Range( aSels
[ nSubSelPos
]->Min(), nIndex
-1 ) );
401 aSels
[ nSubSelPos
]->Min() = nIndex
;
404 // did we append an selected to an existing sub selection?
407 && aSels
[ nSubSelPos
]->Max() == nIndex
-1
408 ) // expand the previous sub selection
409 aSels
[ nSubSelPos
-1 ]->Max() += nCount
;
411 // did we insert an selected into an existing sub selection?
413 && aSels
[ nSubSelPos
]->Min() == nIndex
414 ) { // expand the sub selection
415 aSels
[ nSubSelPos
]->Max() += nCount
;
419 // shift the sub selections behind the inserting position
420 for ( size_t nPos
= nSubSelPos
; nPos
< aSels
.size(); ++nPos
)
422 aSels
[ nPos
]->Min() += nCount
;
423 aSels
[ nPos
]->Max() += nCount
;
427 bCurValid
= sal_False
;
428 aTotRange
.Max() += nCount
;
435 void MultiSelection::Remove( long nIndex
)
437 DBG(DbgOutf( "::Remove(%ld)\n", nIndex
));
439 // find the virtual target position
440 size_t nSubSelPos
= ImplFindSubSelection( nIndex
);
442 // did we remove from an existing sub selection?
443 if ( nSubSelPos
< aSels
.size()
444 && aSels
[ nSubSelPos
]->IsInside(nIndex
)
446 // does this sub selection only contain the index to be deleted
447 if ( aSels
[ nSubSelPos
]->Len() == 1 ) {
448 // completely remove the sub selection
449 ImpSelList::iterator it
= aSels
.begin();
450 ::std::advance( it
, nSubSelPos
);
454 // shorten this sub selection
455 --( aSels
[ nSubSelPos
++ ]->Max() );
458 // adjust the selected counter
462 // shift the sub selections behind the removed index
463 for ( size_t nPos
= nSubSelPos
; nPos
< aSels
.size(); ++nPos
)
465 --( aSels
[ nPos
]->Min() );
466 --( aSels
[ nPos
]->Max() );
469 bCurValid
= sal_False
;
470 aTotRange
.Max() -= 1;
475 long MultiSelection::ImplFwdUnselected()
478 return SFX_ENDOFSELECTION
;
480 if ( ( nCurSubSel
< aSels
.size() )
481 && ( aSels
[ nCurSubSel
]->Min() <= nCurIndex
)
483 nCurIndex
= aSels
[ nCurSubSel
++ ]->Max() + 1;
485 if ( nCurIndex
<= aTotRange
.Max() )
488 return SFX_ENDOFSELECTION
;
491 long MultiSelection::FirstSelected( sal_Bool bInverse
)
493 bInverseCur
= bInverse
;
498 bCurValid
= nSelCount
< sal_uIntPtr(aTotRange
.Len());
502 return ImplFwdUnselected();
507 bCurValid
= !aSels
.empty();
509 return nCurIndex
= aSels
[ 0 ]->Min();
512 return SFX_ENDOFSELECTION
;
515 long MultiSelection::LastSelected()
517 nCurSubSel
= aSels
.size() - 1;
518 bCurValid
= !aSels
.empty();
521 return nCurIndex
= aSels
[ nCurSubSel
]->Max();
523 return SFX_ENDOFSELECTION
;
526 long MultiSelection::NextSelected()
529 return SFX_ENDOFSELECTION
;
534 return ImplFwdUnselected();
538 // is the next index in the current sub selection too?
539 if ( nCurIndex
< aSels
[ nCurSubSel
]->Max() )
542 // are there further sub selections?
543 if ( ++nCurSubSel
< aSels
.size() )
544 return nCurIndex
= aSels
[ nCurSubSel
]->Min();
546 // we are at the end!
547 return SFX_ENDOFSELECTION
;
551 void MultiSelection::SetTotalRange( const Range
& rTotRange
)
553 aTotRange
= rTotRange
;
555 // adjust lower boundary
556 Range
* pRange
= aSels
.empty() ? NULL
: aSels
.front();
559 if( pRange
->Max() < aTotRange
.Min() )
562 aSels
.erase( aSels
.begin() );
564 else if( pRange
->Min() < aTotRange
.Min() )
566 pRange
->Min() = aTotRange
.Min();
572 pRange
= aSels
.empty() ? NULL
: aSels
.front();
575 // adjust upper boundary
576 size_t nCount
= aSels
.size();
579 pRange
= aSels
[ nCount
- 1 ];
580 if( pRange
->Min() > aTotRange
.Max() )
585 else if( pRange
->Max() > aTotRange
.Max() )
587 pRange
->Max() = aTotRange
.Max();
593 nCount
= aSels
.size();
596 // re-calculate selection count
598 for ( size_t i
= 0, n
= aSels
.size(); i
< n
; ++ i
)
599 nSelCount
+= aSels
[i
]->Len();
601 bCurValid
= sal_False
;
605 // StringRangeEnumerator
607 StringRangeEnumerator::StringRangeEnumerator( const OUString
& i_rInput
,
608 sal_Int32 i_nMinNumber
,
609 sal_Int32 i_nMaxNumber
,
610 sal_Int32 i_nLogicalOffset
613 , mnMin( i_nMinNumber
)
614 , mnMax( i_nMaxNumber
)
615 , mnOffset( i_nLogicalOffset
)
616 , mbValidInput( false )
618 // Parse string only if boundaries are valid.
619 if( mnMin
>= 0 && mnMax
>= 0 && mnMin
<= mnMax
)
620 mbValidInput
= setRange( i_rInput
);
623 bool StringRangeEnumerator::checkValue( sal_Int32 i_nValue
, const std::set
< sal_Int32
>* i_pPossibleValues
) const
625 if( i_nValue
< 0 || i_nValue
< mnMin
|| i_nValue
> mnMax
)
627 if( i_pPossibleValues
&& i_pPossibleValues
->find( i_nValue
) == i_pPossibleValues
->end() )
632 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst
, sal_Int32 i_nLast
, bool bSequence
, bool bMayAdjust
)
634 bool bSuccess
= true;
639 if( i_nFirst
< mnMin
)
641 if( i_nFirst
> mnMax
)
643 if( i_nLast
< mnMin
)
645 if( i_nLast
> mnMax
)
648 if( checkValue( i_nFirst
) && checkValue( i_nLast
) )
650 maSequence
.push_back( Range( i_nFirst
, i_nLast
) );
651 sal_Int32 nNumber
= i_nLast
- i_nFirst
;
652 nNumber
= nNumber
< 0 ? -nNumber
: nNumber
;
653 mnCount
+= nNumber
+ 1;
660 if( checkValue( i_nFirst
) )
662 maSequence
.push_back( Range( i_nFirst
, i_nFirst
) );
665 else if( checkValue( i_nLast
) )
667 maSequence
.push_back( Range( i_nLast
, i_nLast
) );
677 bool StringRangeEnumerator::insertJoinedRanges(
678 const std::vector
< sal_Int32
>& rNumbers
, bool i_bStrict
)
680 size_t nCount
= rNumbers
.size();
685 return insertRange( rNumbers
[0], -1, false, ! i_bStrict
);
687 for( size_t i
= 0; i
< nCount
- 1; i
++ )
689 sal_Int32 nFirst
= rNumbers
[i
];
690 sal_Int32 nLast
= rNumbers
[i
+ 1];
693 if ( nFirst
> nLast
) nFirst
--;
694 else if( nFirst
< nLast
) nFirst
++;
697 if ( ! insertRange( nFirst
, nLast
, nFirst
!= nLast
, ! i_bStrict
) && i_bStrict
)
704 bool StringRangeEnumerator::setRange( const OUString
& i_rNewRange
, bool i_bStrict
)
709 const sal_Unicode
* pInput
= i_rNewRange
.getStr();
710 OUStringBuffer
aNumberBuf( 16 );
711 std::vector
< sal_Int32
> aNumbers
;
712 bool bSequence
= false;
715 while( *pInput
>= sal_Unicode('0') && *pInput
<= sal_Unicode('9') )
716 aNumberBuf
.append( *pInput
++ );
717 if( aNumberBuf
.getLength() )
719 sal_Int32 nNumber
= aNumberBuf
.makeStringAndClear().toInt32() + mnOffset
;
720 aNumbers
.push_back( nNumber
);
724 if( *pInput
== sal_Unicode('-') )
727 if( aNumbers
.empty() )
728 aNumbers
.push_back( mnMin
);
730 else if( *pInput
== sal_Unicode(',') || *pInput
== sal_Unicode(';') )
732 if( bSequence
&& !aNumbers
.empty() )
733 aNumbers
.push_back( mnMax
);
734 if( ! insertJoinedRanges( aNumbers
, i_bStrict
) && i_bStrict
)
740 else if( *pInput
&& *pInput
!= sal_Unicode(' ') )
741 return false; // parse error
746 // insert last entries
747 if( bSequence
&& !aNumbers
.empty() )
748 aNumbers
.push_back( mnMax
);
749 if( ! insertJoinedRanges( aNumbers
, i_bStrict
) && i_bStrict
)
755 bool StringRangeEnumerator::hasValue( sal_Int32 i_nValue
, const std::set
< sal_Int32
>* i_pPossibleValues
) const
757 if( i_pPossibleValues
&& i_pPossibleValues
->find( i_nValue
) == i_pPossibleValues
->end() )
759 size_t n
= maSequence
.size();
760 for( size_t i
= 0; i
< n
; ++i
)
762 const StringRangeEnumerator::Range
rRange( maSequence
[i
] );
763 if( rRange
.nFirst
< rRange
.nLast
)
765 if( i_nValue
>= rRange
.nFirst
&& i_nValue
<= rRange
.nLast
)
770 if( i_nValue
>= rRange
.nLast
&& i_nValue
<= rRange
.nFirst
)
777 StringRangeEnumerator::Iterator
& StringRangeEnumerator::Iterator::operator++()
779 if( nRangeIndex
>= 0 && nCurrent
>= 0 && pEnumerator
)
781 const StringRangeEnumerator::Range
& rRange( pEnumerator
->maSequence
[nRangeIndex
] );
782 bool bRangeChange
= false;
783 if( rRange
.nLast
< rRange
.nFirst
)
786 if( nCurrent
> rRange
.nLast
)
794 if( nCurrent
< rRange
.nLast
)
802 if( size_t(nRangeIndex
) == pEnumerator
->maSequence
.size() )
805 nRangeIndex
= nCurrent
= -1;
808 nCurrent
= pEnumerator
->maSequence
[nRangeIndex
].nFirst
;
810 if( nRangeIndex
!= -1 && nCurrent
!= -1 )
812 if( ! pEnumerator
->checkValue( nCurrent
, pPossibleValues
) )
819 sal_Int32
StringRangeEnumerator::Iterator::operator*() const
824 bool StringRangeEnumerator::Iterator::operator==( const Iterator
& i_rCompare
) const
826 return i_rCompare
.pEnumerator
== pEnumerator
&& i_rCompare
.nRangeIndex
== nRangeIndex
&& i_rCompare
.nCurrent
== nCurrent
;
829 StringRangeEnumerator::Iterator
StringRangeEnumerator::begin( const std::set
< sal_Int32
>* i_pPossibleValues
) const
831 StringRangeEnumerator::Iterator
it( this,
833 maSequence
.empty() ? -1 : 0,
834 maSequence
.empty() ? -1 : maSequence
[0].nFirst
);
835 if( ! checkValue(*it
, i_pPossibleValues
) )
840 StringRangeEnumerator::Iterator
StringRangeEnumerator::end( const std::set
< sal_Int32
>* i_pPossibleValues
) const
842 return StringRangeEnumerator::Iterator( this, i_pPossibleValues
, -1, -1 );
845 bool StringRangeEnumerator::getRangesFromString( const OUString
& i_rPageRange
,
846 std::vector
< sal_Int32
>& o_rPageVector
,
847 sal_Int32 i_nMinNumber
,
848 sal_Int32 i_nMaxNumber
,
849 sal_Int32 i_nLogicalOffset
,
850 std::set
< sal_Int32
>* i_pPossibleValues
853 o_rPageVector
.clear();
855 StringRangeEnumerator
aEnum( i_rPageRange
, i_nMinNumber
, i_nMaxNumber
, i_nLogicalOffset
) ;
857 //Even if the input range wasn't completely valid, return what ranges could
858 //be extracted from the input.
859 o_rPageVector
.reserve( static_cast< size_t >( aEnum
.size() ) );
860 for( StringRangeEnumerator::Iterator it
= aEnum
.begin( i_pPossibleValues
);
861 it
!= aEnum
.end( i_pPossibleValues
); ++it
)
863 o_rPageVector
.push_back( *it
);
866 return aEnum
.isValidInput();
869 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */