xref: /aoo42x/main/tools/source/memtools/multisel.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_tools.hxx"
30 
31 #define _SV_MULTISEL_CXX
32 
33 #ifdef MI_DEBUG
34 #define private public
35 #include <stdio.h>
36 #endif
37 
38 #include <tools/debug.hxx>
39 #include <tools/multisel.hxx>
40 
41 #include "rtl/ustrbuf.hxx"
42 
43 #ifdef MI_DEBUG
44 #define DBG(x) x
45 #else
46 #define DBG(x)
47 #endif
48 
49 using namespace rtl;
50 
51 //==================================================================
52 
53 #ifdef MI_DEBUG
54 
55 static void Print( const MultiSelection* pSel )
56 {
57 	DbgOutf( "TotRange:     %4ld-%4ld\n",
58 			 pSel->aTotRange.Min(), pSel->aTotRange.Max() );
59 	if ( pSel->bCurValid )
60 	{
61 		DbgOutf( "CurSubSel:    %4ld\n", pSel->nCurSubSel );
62 		DbgOutf( "CurIndex:     %4ld\n", pSel->nCurIndex );
63 	}
64 	DbgOutf( "SelCount:     %4ld\n", pSel->nSelCount );
65 	DbgOutf( "SubCount:     %4ld\n", pSel->aSels.Count() );
66 	for ( sal_uIntPtr nPos = 0; nPos < pSel->aSels.Count(); ++nPos )
67 	{
68 		DbgOutf( "SubSel #%2ld:   %4ld-%4ld\n", nPos,
69 				 pSel->aSels.GetObject(nPos)->Min(),
70 				 pSel->aSels.GetObject(nPos)->Max() );
71 	}
72 	DbgOutf( "\n" );
73 	fclose( pFile );
74 }
75 
76 #endif
77 
78 // -----------------------------------------------------------------------
79 
80 void MultiSelection::ImplClear()
81 {
82 	// no selected indexes
83 	nSelCount = 0;
84 
85 	Range* pRange = aSels.First();
86 	while ( pRange )
87 	{
88 		delete pRange;
89 		pRange = aSels.Next();
90 	}
91 	aSels.Clear();
92 }
93 
94 // -----------------------------------------------------------------------
95 
96 sal_uIntPtr MultiSelection::ImplFindSubSelection( long nIndex ) const
97 {
98 	// iterate through the sub selections
99 	sal_uIntPtr n = 0;
100 	for ( ;
101 		  n < aSels.Count() && nIndex > aSels.GetObject(n)->Max();
102 		  ++n ) {} /* empty loop */
103 	return n;
104 }
105 
106 // -----------------------------------------------------------------------
107 
108 sal_Bool MultiSelection::ImplMergeSubSelections( sal_uIntPtr nPos1, sal_uIntPtr nPos2 )
109 {
110 	// didn't a sub selection at nPos2 exist?
111 	if ( nPos2 >= aSels.Count() )
112 		return sal_False;
113 
114 	// did the sub selections touch each other?
115 	if ( (aSels.GetObject(nPos1)->Max() + 1) == aSels.GetObject(nPos2)->Min() )
116 	{
117 		// merge them
118 		aSels.GetObject(nPos1)->Max() = aSels.GetObject(nPos2)->Max();
119 		delete aSels.Remove(nPos2);
120 		return sal_True;
121 	}
122 
123 	return sal_False;
124 }
125 
126 // -----------------------------------------------------------------------
127 
128 MultiSelection::MultiSelection():
129 	aTotRange( 0, -1 ),
130 	nCurSubSel(0),
131 	nSelCount(0),
132 	bCurValid(sal_False),
133 	bSelectNew(sal_False)
134 {
135 }
136 
137 // -----------------------------------------------------------------------
138 
139 MultiSelection::MultiSelection( const UniString& rString, sal_Unicode cRange, sal_Unicode cSep ):
140 	aTotRange(0,RANGE_MAX),
141 	nCurSubSel(0),
142 	nSelCount(0),
143 	bCurValid(sal_False),
144 	bSelectNew(sal_False)
145 {
146 	// Dies ist nur ein Schnellschuss und sollte bald optimiert,
147 	// an die verschiedenen Systeme (UNIX etc.)
148 	// und die gewuenschte Eingabe-Syntax angepasst werden.
149 
150 	UniString			aStr( rString );
151 	sal_Unicode*		pStr   = aStr.GetBufferAccess();
152 	sal_Unicode*		pOld = pStr;
153 	sal_Bool				bReady = sal_False;
154 	sal_Bool				bUntil = sal_False;
155 	xub_StrLen			nCut   = 0;
156 
157 	// Hier normieren wir den String, sodass nur Ziffern,
158 	// Semikola als Trenn- und Minus als VonBis-Zeichen
159 	// uebrigbleiben, z.B. "99-117;55;34;-17;37-43"
160 	while ( *pOld )
161 	{
162 		switch( *pOld )
163 		{
164 			case '0':
165 			case '1':
166 			case '2':
167 			case '3':
168 			case '4':
169 			case '5':
170 			case '6':
171 			case '7':
172 			case '8':
173 			case '9':
174 				DBG_ASSERT( *pOld != cRange, "digit for range char not allowed" );
175 				DBG_ASSERT( *pOld != cSep, "digit for separator not allowed" );
176 				if( bReady )
177 				{
178 					*pStr++ = ';';
179 					nCut++;
180 					bReady = sal_False;
181 				}
182 				*pStr++ = *pOld;
183 				nCut++;
184 				bUntil = sal_False;
185 				break;
186 
187 			case '-':
188 			case ':':
189 			case '/':
190 				if ( *pOld != cSep )
191 				{
192 					if ( !bUntil )
193 					{
194 						*pStr++ = '-';
195 						nCut++;
196 						bUntil = sal_True;
197 					}
198 					bReady = sal_False;
199 				}
200 				else
201 					bReady = sal_True;
202 				break;
203 
204 			case ' ':
205 				DBG_ASSERT( *pOld != cRange, "SPACE for range char not allowed" );
206 				DBG_ASSERT( *pOld != cSep, "SPACE for separator not allowed" );
207 				bReady = !bUntil;
208 				break;
209 
210 			default:
211 				if ( *pOld == cRange )
212 				{
213 					if ( !bUntil )
214 					{
215 						*pStr++ = '-';
216 						nCut++;
217 						bUntil = sal_True;
218 					}
219 					bReady = sal_False;
220 				}
221 				else
222 					bReady = sal_True;
223 				break;
224 		}
225 
226 		pOld++;
227 	}
228 	aStr.ReleaseBufferAccess( nCut );
229 
230 	// Jetzt wird der normierte String ausgewertet ..
231 	UniString			aNumStr;
232 	Range				aRg( 1, RANGE_MAX );
233 	const sal_Unicode*	pCStr = aStr.GetBuffer();
234 	long				nPage = 1;
235 	long				nNum  = 1;
236 	bUntil = sal_False;
237 	while ( *pCStr )
238 	{
239 		switch ( *pCStr )
240 		{
241 			case '0':
242 			case '1':
243 			case '2':
244 			case '3':
245 			case '4':
246 			case '5':
247 			case '6':
248 			case '7':
249 			case '8':
250 			case '9':
251 				aNumStr += *pCStr;
252 				break;
253 			case ';':
254 				nNum = aNumStr.ToInt32();
255 				if ( bUntil )
256 				{
257 					if ( !aNumStr.Len() )
258 						nNum = RANGE_MAX;
259 					aRg.Min() = nPage;
260 					aRg.Max() = nNum;
261 					aRg.Justify();
262 					Select( aRg );
263 				}
264 				else
265 					Select( nNum );
266 				nPage = 0;
267 				aNumStr.Erase();
268 				bUntil = sal_False;
269 				break;
270 
271 			case '-':
272 				nPage = aNumStr.ToInt32();
273 				aNumStr.Erase();
274 				bUntil = sal_True;
275 				break;
276 		}
277 
278 		pCStr++;
279 	}
280 
281 	nNum = aNumStr.ToInt32();
282 	if ( bUntil )
283 	{
284 		if ( !aNumStr.Len() )
285 			nNum = RANGE_MAX;
286 		aRg.Min() = nPage;
287 		aRg.Max() = nNum;
288 		aRg.Justify();
289 		Select( aRg );
290 	}
291 	else
292 		Select( nNum );
293 }
294 
295 // -----------------------------------------------------------------------
296 
297 MultiSelection::MultiSelection( const MultiSelection& rOrig ) :
298 	aTotRange(rOrig.aTotRange),
299 	nSelCount(rOrig.nSelCount),
300 	bCurValid(rOrig.bCurValid),
301 	bSelectNew(sal_False)
302 {
303 	if ( bCurValid )
304 	{
305 		nCurSubSel = rOrig.nCurSubSel;
306 		nCurIndex = rOrig.nCurIndex;
307 	}
308 
309 	// copy the sub selections
310 	for ( sal_uIntPtr n = 0; n < rOrig.aSels.Count(); ++n )
311 		aSels.Insert( new Range( *rOrig.aSels.GetObject(n) ), LIST_APPEND );
312 }
313 
314 // -----------------------------------------------------------------------
315 
316 MultiSelection::MultiSelection( const Range& rRange ):
317 	aTotRange(rRange),
318 	nCurSubSel(0),
319 	nSelCount(0),
320 	bCurValid(sal_False),
321 	bSelectNew(sal_False)
322 {
323 }
324 
325 // -----------------------------------------------------------------------
326 
327 MultiSelection::~MultiSelection()
328 {
329 	Range* pRange = aSels.First();
330 	while ( pRange )
331 	{
332 		delete pRange;
333 		pRange = aSels.Next();
334 	}
335 }
336 
337 // -----------------------------------------------------------------------
338 
339 MultiSelection& MultiSelection::operator= ( const MultiSelection& rOrig )
340 {
341 	aTotRange = rOrig.aTotRange;
342 	bCurValid = rOrig.bCurValid;
343 	if ( bCurValid )
344 	{
345 		nCurSubSel = rOrig.nCurSubSel;
346 		nCurIndex = rOrig.nCurIndex;
347 	}
348 
349 	// clear the old and copy the sub selections
350 	ImplClear();
351 	for ( sal_uIntPtr n = 0; n < rOrig.aSels.Count(); ++n )
352 		aSels.Insert( new Range( *rOrig.aSels.GetObject(n) ), LIST_APPEND );
353 	nSelCount = rOrig.nSelCount;
354 
355 	return *this;
356 }
357 
358 // -----------------------------------------------------------------------
359 
360 sal_Bool MultiSelection::operator== ( MultiSelection& rWith )
361 {
362 	if ( aTotRange != rWith.aTotRange || nSelCount != rWith.nSelCount ||
363 		 aSels.Count() != rWith.aSels.Count() )
364 		return sal_False;
365 
366 	// compare the sub seletions
367 	for ( sal_uIntPtr n = 0; n < aSels.Count(); ++n )
368 		if ( *aSels.GetObject(n) != *rWith.aSels.GetObject(n) )
369 			return sal_False;
370 	return sal_True;
371 }
372 
373 // -----------------------------------------------------------------------
374 
375 void MultiSelection::SelectAll( sal_Bool bSelect )
376 {
377 	DBG(DbgOutf( "::SelectAll(%s)\n", bSelect ? "sal_True" : "sal_False" ));
378 
379 	ImplClear();
380 	if ( bSelect )
381 	{
382 		aSels.Insert( new Range(aTotRange), LIST_APPEND );
383 		nSelCount = aTotRange.Len();
384 	}
385 
386 	DBG(Print( this ));
387 }
388 
389 // -----------------------------------------------------------------------
390 
391 sal_Bool MultiSelection::Select( long nIndex, sal_Bool bSelect )
392 {
393 	DBG_ASSERT( aTotRange.IsInside(nIndex), "selected index out of range" );
394 
395 	// out of range?
396 	if ( !aTotRange.IsInside(nIndex) )
397 		return sal_False;
398 
399 	// find the virtual target position
400 	sal_uIntPtr nSubSelPos = ImplFindSubSelection( nIndex );
401 
402 	if ( bSelect )
403 	{
404 		// is it included in the found sub selection?
405 		if ( nSubSelPos < aSels.Count() &&
406 			 aSels.GetObject(nSubSelPos)->IsInside( nIndex ) )
407 			// already selected, nothing to do
408 			return sal_False;
409 
410 		// it will become selected
411 		++nSelCount;
412 
413 		// is it at the end of the previous sub selection
414 		if ( nSubSelPos > 0 &&
415 			 aSels.GetObject(nSubSelPos-1)->Max() == (nIndex-1) )
416 		{
417 			// expand the previous sub selection
418 			aSels.GetObject(nSubSelPos-1)->Max() = nIndex;
419 
420 			// try to merge the previous sub selection
421 			ImplMergeSubSelections( nSubSelPos-1, nSubSelPos );
422 		}
423 		// is is at the beginning of the found sub selection
424 		else if ( nSubSelPos < aSels.Count() &&
425 				  aSels.GetObject(nSubSelPos)->Min() == (nIndex+1) )
426 			// expand the found sub selection
427 			aSels.GetObject(nSubSelPos)->Min() = nIndex;
428 		else
429 		{
430 			// create a new sub selection
431 			aSels.Insert( new Range( nIndex, nIndex ), nSubSelPos );
432 			if ( bCurValid && nCurSubSel >= nSubSelPos )
433 				++nCurSubSel;
434 		}
435 	}
436 	else
437 	{
438 		// is it excluded from the found sub selection?
439 		if ( nSubSelPos >= aSels.Count() ||
440 			 !aSels.GetObject(nSubSelPos)->IsInside( nIndex ) )
441 		{
442 			// not selected, nothing to do
443 			DBG(Print( this ));
444 			return sal_False;
445 		}
446 
447 		// it will become deselected
448 		--nSelCount;
449 
450 		// is it the only index in the found sub selection?
451 		if ( aSels.GetObject(nSubSelPos)->Len() == 1 )
452 		{
453 			// remove the complete sub selection
454 			delete aSels.Remove( nSubSelPos );
455 			DBG(Print( this ));
456 			return sal_True;
457 		}
458 
459 		// is it at the beginning of the found sub selection?
460 		if ( aSels.GetObject(nSubSelPos)->Min() == nIndex )
461 			++aSels.GetObject(nSubSelPos)->Min();
462 		// is it at the end of the found sub selection?
463 		else if ( aSels.GetObject(nSubSelPos)->Max() == nIndex )
464 			--aSels.GetObject(nSubSelPos)->Max();
465 		// it is in the middle of the found sub selection?
466 		else
467 		{
468 			// split the sub selection
469 			aSels.Insert(
470 				new Range( aSels.GetObject(nSubSelPos)->Min(), nIndex-1 ),
471 				nSubSelPos );
472 			aSels.GetObject(nSubSelPos+1)->Min() = nIndex + 1;
473 		}
474 	}
475 
476 	DBG(Print( this ));
477 
478 	return sal_True;
479 }
480 
481 // -----------------------------------------------------------------------
482 
483 void MultiSelection::Select( const Range& rIndexRange, sal_Bool bSelect )
484 {
485 	Range* pRange;
486 	long nOld;
487 
488 	sal_uIntPtr nTmpMin = rIndexRange.Min();
489 	sal_uIntPtr nTmpMax = rIndexRange.Max();
490 	sal_uIntPtr nCurMin = FirstSelected();
491 	sal_uIntPtr nCurMax = LastSelected();
492 	DBG_ASSERT(aTotRange.IsInside(nTmpMax), "selected index out of range" );
493 	DBG_ASSERT(aTotRange.IsInside(nTmpMin), "selected index out of range" );
494 
495 	// gesamte Selektion ersetzen ?
496 	if( nTmpMin <= nCurMin && nTmpMax >= nCurMax )
497 	{
498 		ImplClear();
499 		if ( bSelect )
500 		{
501 			aSels.Insert( new Range(rIndexRange), LIST_APPEND );
502 			nSelCount = rIndexRange.Len();
503 		}
504 		return;
505 	}
506 	// links erweitern ?
507 	if( nTmpMax < nCurMin )
508 	{
509 		if( bSelect )
510 		{
511 			// ersten Range erweitern ?
512 			if( nCurMin > (nTmpMax+1)  )
513 			{
514 				pRange = new Range( rIndexRange );
515 				aSels.Insert( pRange, (sal_uIntPtr)0 );
516 				nSelCount += pRange->Len();
517 			}
518 			else
519 			{
520 				pRange = aSels.First();
521 				nOld = pRange->Min();
522 				pRange->Min() = (long)nTmpMin;
523 				nSelCount += ( nOld - nTmpMin );
524 			}
525 			bCurValid = sal_False;
526 		}
527 		return;
528 	}
529 	// rechts erweitern ?
530 	else if( nTmpMin > nCurMax )
531 	{
532 		if( bSelect )
533 		{
534 			// letzten Range erweitern ?
535 			if( nTmpMin > (nCurMax+1) )
536 			{
537 				pRange = new Range( rIndexRange );
538 				aSels.Insert( pRange, LIST_APPEND );
539 				nSelCount += pRange->Len();
540 			}
541 			else
542 			{
543 				pRange = aSels.Last();
544 				nOld = pRange->Max();
545 				pRange->Max() = (long)nTmpMax;
546 				nSelCount += ( nTmpMax - nOld );
547 			}
548 			bCurValid = sal_False;
549 		}
550 		return;
551 	}
552 
553 	//HACK(Hier muss noch optimiert werden)
554 	while( nTmpMin <= nTmpMax )
555 	{
556 		Select( nTmpMin, bSelect );
557 		nTmpMin++;
558 	}
559 }
560 
561 // -----------------------------------------------------------------------
562 
563 sal_Bool MultiSelection::IsSelected( long nIndex ) const
564 {
565 	// find the virtual target position
566 	sal_uIntPtr nSubSelPos = ImplFindSubSelection( nIndex );
567 
568 	return nSubSelPos < aSels.Count() &&
569 		   aSels.GetObject(nSubSelPos)->IsInside(nIndex);
570 }
571 
572 // -----------------------------------------------------------------------
573 
574 void MultiSelection::Insert( long nIndex, long nCount )
575 {
576 	DBG(DbgOutf( "::Insert(%ld, %ld)\n", nIndex, nCount ));
577 
578 	// find the virtual target position
579 	sal_uIntPtr nSubSelPos = ImplFindSubSelection( nIndex );
580 
581 	// did we need to shift the sub selections?
582 	if ( nSubSelPos < aSels.Count() )
583 	{
584 		// did we insert an unselected into an existing sub selection?
585 		if ( !bSelectNew && aSels.GetObject(nSubSelPos)->Min() != nIndex &&
586 				  aSels.GetObject(nSubSelPos)->IsInside(nIndex) )
587 		{
588 			// split the sub selection
589 			aSels.Insert(
590 				new Range( aSels.GetObject(nSubSelPos)->Min(), nIndex-1 ),
591 				nSubSelPos );
592 			++nSubSelPos;
593 			aSels.GetObject(nSubSelPos)->Min() = nIndex;
594 		}
595 
596 		// did we append an selected to an existing sub selection?
597 		else if ( bSelectNew && nSubSelPos > 0 &&
598 			 aSels.GetObject(nSubSelPos)->Max() == nIndex-1 )
599 			// expand the previous sub selection
600 			aSels.GetObject(nSubSelPos-1)->Max() += nCount;
601 
602 		// did we insert an selected into an existing sub selection?
603 		else if ( bSelectNew && aSels.GetObject(nSubSelPos)->Min() == nIndex )
604 		{
605 			// expand the sub selection
606 			aSels.GetObject(nSubSelPos)->Max() += nCount;
607 			++nSubSelPos;
608 		}
609 
610 		// shift the sub selections behind the inserting position
611 		for ( sal_uIntPtr nPos = nSubSelPos; nPos < aSels.Count(); ++nPos )
612 		{
613 			aSels.GetObject(nPos)->Min() += nCount;
614 			aSels.GetObject(nPos)->Max() += nCount;
615 		}
616 	}
617 
618 	bCurValid = sal_False;
619 	aTotRange.Max() += nCount;
620 	if ( bSelectNew )
621 		nSelCount += nCount;
622 
623 	DBG(Print( this ));
624 }
625 
626 // -----------------------------------------------------------------------
627 
628 void MultiSelection::Remove( long nIndex )
629 {
630 	DBG(DbgOutf( "::Remove(%ld)\n", nIndex ));
631 
632 	// find the virtual target position
633 	sal_uIntPtr nSubSelPos = ImplFindSubSelection( nIndex );
634 
635 	// did we remove from an existing sub selection?
636 	if ( nSubSelPos < aSels.Count() &&
637 		 aSels.GetObject(nSubSelPos)->IsInside(nIndex) )
638 	{
639 		// does this sub selection only contain the index to be deleted
640 		if ( aSels.GetObject(nSubSelPos)->Len() == 1 )
641 			// completely remove the sub selection
642 			aSels.Remove(nSubSelPos);
643 		else
644 			// shorten this sub selection
645 			--( aSels.GetObject(nSubSelPos++)->Max() );
646 
647 		// adjust the selected counter
648 		--nSelCount;
649 	}
650 
651 	// shift the sub selections behind the removed index
652 	for ( sal_uIntPtr nPos = nSubSelPos; nPos < aSels.Count(); ++nPos )
653 	{
654 		--( aSels.GetObject(nPos)->Min() );
655 		--( aSels.GetObject(nPos)->Max() );
656 	}
657 
658 	bCurValid = sal_False;
659 	aTotRange.Max() -= 1;
660 
661 	DBG(Print( this ));
662 }
663 
664 // -----------------------------------------------------------------------
665 
666 void MultiSelection::Append( long nCount )
667 {
668 	long nPrevLast = aTotRange.Max();
669 	aTotRange.Max() += nCount;
670 	if ( bSelectNew )
671 	{
672 		nSelCount += nCount;
673 		aSels.Insert( new Range( nPrevLast+1, nPrevLast + nCount ),
674 					  LIST_APPEND );
675 		if ( aSels.Count() > 1 )
676 			ImplMergeSubSelections( aSels.Count() - 2, aSels.Count() );
677 	}
678 }
679 
680 // -----------------------------------------------------------------------
681 
682 long MultiSelection::ImplFwdUnselected()
683 {
684 	if ( !bCurValid )
685 		return SFX_ENDOFSELECTION;
686 
687 	if ( ( nCurSubSel < aSels.Count() ) &&
688 		 ( aSels.GetObject(nCurSubSel)->Min() <= nCurIndex ) )
689 		nCurIndex = aSels.GetObject(nCurSubSel++)->Max() + 1;
690 
691 	if ( nCurIndex <= aTotRange.Max() )
692 		return nCurIndex;
693 	else
694 		return SFX_ENDOFSELECTION;
695 }
696 
697 // -----------------------------------------------------------------------
698 
699 long MultiSelection::ImplBwdUnselected()
700 {
701 	if ( !bCurValid )
702 		return SFX_ENDOFSELECTION;
703 
704 	if ( aSels.GetObject(nCurSubSel)->Max() < nCurIndex )
705 		return nCurIndex;
706 
707 	nCurIndex = aSels.GetObject(nCurSubSel--)->Min() - 1;
708 	if ( nCurIndex >= 0 )
709 		return nCurIndex;
710 	else
711 		return SFX_ENDOFSELECTION;
712 }
713 
714 // -----------------------------------------------------------------------
715 
716 long MultiSelection::FirstSelected( sal_Bool bInverse )
717 {
718 	bInverseCur = bInverse;
719 	nCurSubSel = 0;
720 
721 	if ( bInverseCur )
722 	{
723 		bCurValid = nSelCount < sal_uIntPtr(aTotRange.Len());
724 		if ( bCurValid )
725 		{
726 			nCurIndex = 0;
727 			return ImplFwdUnselected();
728 		}
729 	}
730 	else
731 	{
732 		bCurValid = aSels.Count() > 0;
733 		if ( bCurValid )
734 			return nCurIndex = aSels.GetObject(0)->Min();
735 	}
736 
737 	return SFX_ENDOFSELECTION;
738 }
739 
740 // -----------------------------------------------------------------------
741 
742 long MultiSelection::LastSelected()
743 {
744 	nCurSubSel = aSels.Count() - 1;
745 	bCurValid = aSels.Count() > 0;
746 
747 	if ( bCurValid )
748 		return nCurIndex = aSels.GetObject(nCurSubSel)->Max();
749 
750 	return SFX_ENDOFSELECTION;
751 }
752 
753 // -----------------------------------------------------------------------
754 
755 long MultiSelection::NextSelected()
756 {
757 	if ( !bCurValid )
758 		return SFX_ENDOFSELECTION;
759 
760 	if ( bInverseCur )
761 	{
762 		++nCurIndex;
763 		return ImplFwdUnselected();
764 	}
765 	else
766 	{
767 		// is the next index in the current sub selection too?
768 		if ( nCurIndex < aSels.GetObject(nCurSubSel)->Max() )
769 			return ++nCurIndex;
770 
771 		// are there further sub selections?
772 		if ( ++nCurSubSel < aSels.Count() )
773 			return nCurIndex = aSels.GetObject(nCurSubSel)->Min();
774 
775 		// we are at the end!
776 		return SFX_ENDOFSELECTION;
777 	}
778 }
779 
780 // -----------------------------------------------------------------------
781 
782 long MultiSelection::PrevSelected()
783 {
784 	if ( !bCurValid )
785 		return SFX_ENDOFSELECTION;
786 
787 	if ( bInverseCur )
788 	{
789 		--nCurIndex;
790 		return ImplBwdUnselected();
791 	}
792 	else
793 	{
794 		// is the previous index in the current sub selection too?
795 		if ( nCurIndex > aSels.GetObject(nCurSubSel)->Min() )
796 			return --nCurIndex;
797 
798 		// are there previous sub selections?
799 		if ( nCurSubSel > 0 )
800 		{
801 			--nCurSubSel;
802 			return nCurIndex = aSels.GetObject(nCurSubSel)->Max();
803 		}
804 
805 		// we are at the beginning!
806 		return SFX_ENDOFSELECTION;
807 	}
808 }
809 
810 // -----------------------------------------------------------------------
811 
812 void MultiSelection::SetTotalRange( const Range& rTotRange )
813 {
814 	aTotRange = rTotRange;
815 
816 	// die untere Bereichsgrenze anpassen
817 	Range* pRange = aSels.GetObject( 0 );
818 	while( pRange )
819 	{
820 		if( pRange->Max() < aTotRange.Min() )
821 		{
822 			delete pRange;
823 			aSels.Remove( (sal_uIntPtr)0 );
824 		}
825 		else if( pRange->Min() < aTotRange.Min() )
826 		{
827 			pRange->Min() = aTotRange.Min();
828 			break;
829 		}
830 		else
831 			break;
832 
833 		pRange = aSels.GetObject( 0 );
834 	}
835 
836 	// die obere Bereichsgrenze anpassen
837 	sal_uIntPtr nCount = aSels.Count();
838 	while( nCount )
839 	{
840 		pRange = aSels.GetObject( nCount - 1 );
841 		if( pRange->Min() > aTotRange.Max() )
842 		{
843 			delete pRange;
844 			aSels.Remove( (sal_uIntPtr)(nCount - 1) );
845 		}
846 		else if( pRange->Max() > aTotRange.Max() )
847 		{
848 			pRange->Max() = aTotRange.Max();
849 			break;
850 		}
851 		else
852 			break;
853 
854 		nCount = aSels.Count();
855 	}
856 
857 	// Selection-Count neu berechnen
858 	nSelCount = 0;
859 	pRange = aSels.First();
860 	while( pRange )
861 	{
862 		nSelCount += pRange->Len();
863 		pRange = aSels.Next();
864 	}
865 
866 	bCurValid = sal_False;
867 	nCurIndex = 0;
868 }
869 
870 // -----------------------------------------------------------------------
871 //
872 // StringRangeEnumerator
873 //
874 // -----------------------------------------------------------------------
875 StringRangeEnumerator::StringRangeEnumerator( const rtl::OUString& i_rInput,
876                                               sal_Int32 i_nMinNumber,
877                                               sal_Int32 i_nMaxNumber,
878                                               sal_Int32 i_nLogicalOffset
879                                               )
880     : mnCount( 0 )
881     , mnMin( i_nMinNumber )
882     , mnMax( i_nMaxNumber )
883     , mnOffset( i_nLogicalOffset )
884 {
885     setRange( i_rInput );
886 }
887 
888 bool StringRangeEnumerator::checkValue( sal_Int32 i_nValue, const std::set< sal_Int32 >* i_pPossibleValues ) const
889 {
890     if( mnMin >= 0 && i_nValue < mnMin )
891         return false;
892     if( mnMax >= 0 && i_nValue > mnMax )
893         return false;
894     if( i_nValue < 0 )
895         return false;
896     if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
897         return false;
898     return true;
899 }
900 
901 bool StringRangeEnumerator::insertRange( sal_Int32 i_nFirst, sal_Int32 i_nLast, bool bSequence, bool bMayAdjust )
902 {
903     bool bSuccess = true;
904     if( bSequence )
905     {
906         if( i_nFirst == -1 )
907             i_nFirst = mnMin;
908         if( i_nLast == -1 )
909             i_nLast = mnMax;
910         if( bMayAdjust )
911         {
912             if( i_nFirst < mnMin )
913                 i_nFirst = mnMin;
914             if( i_nFirst > mnMax )
915                 i_nFirst = mnMax;
916             if( i_nLast < mnMin )
917                 i_nLast = mnMin;
918             if( i_nLast > mnMax )
919                 i_nLast = mnMax;
920         }
921         if( checkValue( i_nFirst ) && checkValue( i_nLast ) )
922         {
923             maSequence.push_back( Range( i_nFirst, i_nLast ) );
924             sal_Int32 nNumber = i_nLast - i_nFirst;
925             nNumber = nNumber < 0 ? -nNumber : nNumber;
926             mnCount += nNumber + 1;
927         }
928         else
929             bSuccess = false;
930     }
931     else
932     {
933         if( i_nFirst >= 0 )
934         {
935             if( checkValue( i_nFirst ) )
936             {
937                 maSequence.push_back( Range( i_nFirst, i_nFirst ) );
938                 mnCount++;
939             }
940             else
941                 bSuccess = false;
942         }
943         if( i_nLast >= 0 )
944         {
945             if( checkValue( i_nLast ) )
946             {
947                 maSequence.push_back( Range( i_nLast, i_nLast ) );
948                 mnCount++;
949             }
950             else
951                 bSuccess = false;
952         }
953     }
954 
955     return bSuccess;
956 }
957 
958 bool StringRangeEnumerator::setRange( const rtl::OUString& i_rNewRange, bool i_bStrict )
959 {
960     mnCount = 0;
961     maSequence.clear();
962 
963     // we love special cases
964     if( i_rNewRange.getLength() == 0 )
965     {
966         if( mnMin >= 0 && mnMax >= 0 )
967         {
968             insertRange( mnMin, mnMax, mnMin != mnMax, ! i_bStrict );
969         }
970         return true;
971     }
972 
973     const sal_Unicode* pInput = i_rNewRange.getStr();
974     rtl::OUStringBuffer aNumberBuf( 16 );
975     sal_Int32 nLastNumber = -1, nNumber = -1;
976     bool bSequence = false;
977     bool bSuccess = true;
978     while( *pInput )
979     {
980         while( *pInput >= sal_Unicode('0') && *pInput <= sal_Unicode('9') )
981             aNumberBuf.append( *pInput++ );
982         if( aNumberBuf.getLength() )
983         {
984             if( nNumber != -1 )
985             {
986                 if( bSequence )
987                 {
988                     if( ! insertRange( nLastNumber, nNumber, true, ! i_bStrict ) && i_bStrict )
989                     {
990                         bSuccess = false;
991                         break;
992                     }
993                     nLastNumber = -1;
994                 }
995                 else
996                 {
997                     if( ! insertRange( nNumber, nNumber, false, ! i_bStrict ) && i_bStrict )
998                     {
999                         bSuccess = false;
1000                         break;
1001                     }
1002                 }
1003             }
1004             nNumber = aNumberBuf.makeStringAndClear().toInt32();
1005             nNumber += mnOffset;
1006         }
1007         bool bInsertRange = false;
1008         if( *pInput == sal_Unicode('-') )
1009         {
1010             nLastNumber = nNumber;
1011             nNumber = -1;
1012             bSequence = true;
1013         }
1014         else if( *pInput == ' ' )
1015         {
1016         }
1017         else if( *pInput == sal_Unicode(',') || *pInput == sal_Unicode(';') )
1018             bInsertRange = true;
1019         else if( *pInput )
1020         {
1021 
1022             bSuccess = false;
1023             break; // parse error
1024         }
1025 
1026         if( bInsertRange )
1027         {
1028             if( ! insertRange( nLastNumber, nNumber, bSequence, ! i_bStrict ) && i_bStrict )
1029             {
1030                 bSuccess = false;
1031                 break;
1032             }
1033             nNumber = nLastNumber = -1;
1034             bSequence = false;
1035         }
1036         if( *pInput )
1037             pInput++;
1038     }
1039     // insert last entries
1040     insertRange( nLastNumber, nNumber, bSequence, ! i_bStrict );
1041 
1042     return bSuccess;
1043 }
1044 
1045 bool StringRangeEnumerator::hasValue( sal_Int32 i_nValue, const std::set< sal_Int32 >* i_pPossibleValues ) const
1046 {
1047     if( i_pPossibleValues && i_pPossibleValues->find( i_nValue ) == i_pPossibleValues->end() )
1048         return false;
1049     size_t n = maSequence.size();
1050     for( size_t i= 0; i < n; ++i )
1051     {
1052         const StringRangeEnumerator::Range rRange( maSequence[i] );
1053         if( rRange.nFirst < rRange.nLast )
1054         {
1055             if( i_nValue >= rRange.nFirst && i_nValue <= rRange.nLast )
1056                 return true;
1057         }
1058         else
1059         {
1060             if( i_nValue >= rRange.nLast && i_nValue <= rRange.nFirst )
1061                 return true;
1062         }
1063     }
1064     return false;
1065 }
1066 
1067 StringRangeEnumerator::Iterator& StringRangeEnumerator::Iterator::operator++()
1068 {
1069     if( nRangeIndex >= 0 && nCurrent >= 0 && pEnumerator )
1070     {
1071         const StringRangeEnumerator::Range& rRange( pEnumerator->maSequence[nRangeIndex] );
1072         bool bRangeChange = false;
1073         if( rRange.nLast < rRange.nFirst )
1074         {
1075             // backward range
1076             if( nCurrent > rRange.nLast )
1077                 nCurrent--;
1078             else
1079                 bRangeChange = true;
1080         }
1081         else
1082         {
1083             // forward range
1084             if( nCurrent < rRange.nLast )
1085                 nCurrent++;
1086             else
1087                 bRangeChange = true;
1088         }
1089         if( bRangeChange )
1090         {
1091             nRangeIndex++;
1092             if( size_t(nRangeIndex) == pEnumerator->maSequence.size() )
1093             {
1094                 // reached the end
1095                 nRangeIndex = nCurrent = -1;
1096             }
1097             else
1098                 nCurrent = pEnumerator->maSequence[nRangeIndex].nFirst;
1099         }
1100         if( nRangeIndex != -1 && nCurrent != -1 )
1101         {
1102             if( ! pEnumerator->checkValue( nCurrent, pPossibleValues ) )
1103                 return ++(*this);
1104         }
1105     }
1106     return *this;
1107 }
1108 
1109 sal_Int32 StringRangeEnumerator::Iterator::operator*() const
1110 {
1111     return nCurrent;
1112 }
1113 
1114 bool StringRangeEnumerator::Iterator::operator==( const Iterator& i_rCompare ) const
1115 {
1116     return i_rCompare.pEnumerator == pEnumerator && i_rCompare.nRangeIndex == nRangeIndex && i_rCompare.nCurrent == nCurrent;
1117 }
1118 
1119 StringRangeEnumerator::Iterator StringRangeEnumerator::begin( const std::set< sal_Int32 >* i_pPossibleValues ) const
1120 {
1121     StringRangeEnumerator::Iterator it( this,
1122                                         i_pPossibleValues,
1123                                         maSequence.empty() ? -1 : 0,
1124                                         maSequence.empty() ? -1 : maSequence[0].nFirst );
1125     if( ! checkValue(*it, i_pPossibleValues ) )
1126         ++it;
1127     return it;
1128 }
1129 
1130 StringRangeEnumerator::Iterator StringRangeEnumerator::end( const std::set< sal_Int32 >* i_pPossibleValues ) const
1131 {
1132     return StringRangeEnumerator::Iterator( this, i_pPossibleValues, -1, -1 );
1133 }
1134 
1135 bool StringRangeEnumerator::getRangesFromString( const OUString& i_rPageRange,
1136                                                  std::vector< sal_Int32 >& o_rPageVector,
1137                                                  sal_Int32 i_nMinNumber,
1138                                                  sal_Int32 i_nMaxNumber,
1139                                                  sal_Int32 i_nLogicalOffset,
1140                                                  std::set< sal_Int32 >* i_pPossibleValues
1141                                                )
1142 {
1143     StringRangeEnumerator aEnum;
1144     aEnum.setMin( i_nMinNumber );
1145     aEnum.setMax( i_nMaxNumber );
1146     aEnum.setLogicalOffset( i_nLogicalOffset );
1147 
1148     bool bRes = aEnum.setRange( i_rPageRange );
1149     if( bRes )
1150     {
1151         o_rPageVector.clear();
1152         o_rPageVector.reserve( aEnum.size() );
1153         for( StringRangeEnumerator::Iterator it = aEnum.begin( i_pPossibleValues );
1154              it != aEnum.end( i_pPossibleValues ); ++it )
1155         {
1156             o_rPageVector.push_back( *it );
1157         }
1158     }
1159 
1160     return bRes;
1161 }
1162 
1163