xref: /trunk/main/tools/source/memtools/multisel.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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