xref: /trunk/main/sc/source/core/data/attarray.cxx (revision 96fc4b33382e706b7361f08aba6c96207eedf10a)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 
28 
29 //------------------------------------------------------------------------
30 
31 #include "scitems.hxx"
32 #include <svx/algitem.hxx>
33 #include <editeng/boxitem.hxx>
34 #include <editeng/bolnitem.hxx>
35 #include <editeng/frmdiritem.hxx>
36 #include <editeng/shaditem.hxx>
37 #include <svl/poolcach.hxx>
38 #include <editeng/fontitem.hxx>
39 #include <unotools/fontcvt.hxx>
40 
41 #include "attarray.hxx"
42 #include "global.hxx"
43 #include "document.hxx"
44 #include "docpool.hxx"
45 #include "patattr.hxx"
46 #include "stlsheet.hxx"
47 #include "stlpool.hxx"
48 #include "markarr.hxx"
49 #include "rechead.hxx"
50 #include "globstr.hrc"
51 #include "segmenttree.hxx"
52 
53 #undef DBG_INVALIDATE
54 #define DBGOUTPUT(s) \
55     DBG_ERROR( String("Invalidate ") + String(s) + String(": ") \
56                + String(nCol) + String('/') + String(aAdrStart.Row()) + String('/') + String(nTab) \
57                + String(" bis ") \
58                + String(nCol) + String('/') + String(aAdrEnd.Row())   + String('/') + String(nTab) \
59               );
60 
61 // STATIC DATA -----------------------------------------------------------
62 
63 
64 //------------------------------------------------------------------------
65 
66 ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc ) :
67     nCol( nNewCol ),
68     nTab( nNewTab ),
69     pDocument( pDoc )
70 {
71     nCount = nLimit = 1;
72     pData = new ScAttrEntry[1];
73     if (pData)
74     {
75         pData[0].nRow = MAXROW;
76         pData[0].pPattern = pDocument->GetDefPattern();     // ohne Put !!!
77     }
78 }
79 
80 //------------------------------------------------------------------------
81 
82 ScAttrArray::~ScAttrArray()
83 {
84 #ifdef DBG_UTIL
85     TestData();
86 #endif
87 
88     if (pData)
89     {
90         ScDocumentPool* pDocPool = pDocument->GetPool();
91         for (SCSIZE i=0; i<nCount; i++)
92             pDocPool->Remove(*pData[i].pPattern);
93 
94         delete[] pData;
95     }
96 }
97 
98 //------------------------------------------------------------------------
99 #ifdef DBG_UTIL
100 void ScAttrArray::TestData() const
101 {
102 
103     sal_uInt16 nErr = 0;
104     if (pData)
105     {
106         SCSIZE nPos;
107         for (nPos=0; nPos<nCount; nPos++)
108         {
109             if (nPos > 0)
110                 if (pData[nPos].pPattern == pData[nPos-1].pPattern || pData[nPos].nRow <= pData[nPos-1].nRow)
111                     ++nErr;
112             if (pData[nPos].pPattern->Which() != ATTR_PATTERN)
113                 ++nErr;
114         }
115         if ( nPos && pData[nPos-1].nRow != MAXROW )
116             ++nErr;
117     }
118     if (nErr)
119     {
120         ByteString aMsg = ByteString::CreateFromInt32(nErr);
121         aMsg += " errors in attribute array, column ";
122         aMsg += ByteString::CreateFromInt32(nCol);
123         DBG_ERROR( aMsg.GetBuffer() );
124     }
125 }
126 #endif
127 
128 //------------------------------------------------------------------------
129 
130 void ScAttrArray::Reset( const ScPatternAttr* pPattern, sal_Bool bAlloc )
131 {
132     if (pData)
133     {
134         ScDocumentPool*      pDocPool = pDocument->GetPool();
135         const ScPatternAttr* pOldPattern;
136         ScAddress            aAdrStart( nCol, 0, nTab );
137         ScAddress            aAdrEnd  ( nCol, 0, nTab );
138 
139         for (SCSIZE i=0; i<nCount; i++)
140         {
141             // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
142             pOldPattern = pData[i].pPattern;
143             sal_Bool bNumFormatChanged;
144             if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
145                     pPattern->GetItemSet(), pOldPattern->GetItemSet() ) )
146             {
147                 aAdrStart.SetRow( i ? pData[i-1].nRow+1 : 0 );
148                 aAdrEnd  .SetRow( pData[i].nRow );
149                 pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
150 #ifdef DBG_INVALIDATE
151                 DBGOUTPUT("Reset");
152 #endif
153             }
154             // bedingtes Format gesetzt oder geloescht?
155             if ( &pPattern->GetItem(ATTR_CONDITIONAL) != &pOldPattern->GetItem(ATTR_CONDITIONAL) )
156             {
157                 pDocument->ConditionalChanged( ((const SfxUInt32Item&)
158                                 pOldPattern->GetItem(ATTR_CONDITIONAL)).GetValue() );
159                 pDocument->ConditionalChanged( ((const SfxUInt32Item&)
160                                 pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() );
161             }
162             pDocPool->Remove(*pOldPattern);
163         }
164         delete[] pData;
165 
166         if (pDocument->IsStreamValid(nTab))
167             pDocument->SetStreamValid(nTab, sal_False);
168 
169         if (bAlloc)
170         {
171             nCount = nLimit = 1;
172             pData = new ScAttrEntry[1];
173             if (pData)
174             {
175                 ScPatternAttr* pNewPattern = (ScPatternAttr*) &pDocPool->Put(*pPattern);
176                 pData[0].nRow = MAXROW;
177                 pData[0].pPattern = pNewPattern;
178             }
179         }
180         else
181         {
182             nCount = nLimit = 0;
183             pData = NULL;               // muss sofort wieder belegt werden !
184         }
185     }
186 }
187 
188 
189 sal_Bool ScAttrArray::Concat(SCSIZE nPos)
190 {
191     sal_Bool bRet = sal_False;
192     if (pData && (nPos < nCount))
193     {
194         if (nPos > 0)
195         {
196             if (pData[nPos - 1].pPattern == pData[nPos].pPattern)
197             {
198                 pData[nPos - 1].nRow = pData[nPos].nRow;
199                 pDocument->GetPool()->Remove(*pData[nPos].pPattern);
200                 memmove(&pData[nPos], &pData[nPos + 1], (nCount - nPos - 1) * sizeof(ScAttrEntry));
201                 pData[nCount - 1].pPattern = NULL;
202                 pData[nCount - 1].nRow = 0;
203                 nCount--;
204                 nPos--;
205                 bRet = sal_True;
206             }
207         }
208         if (nPos + 1 < nCount)
209         {
210             if (pData[nPos + 1].pPattern == pData[nPos].pPattern)
211             {
212                 pData[nPos].nRow = pData[nPos + 1].nRow;
213                 pDocument->GetPool()->Remove(*pData[nPos].pPattern);
214                 memmove(&pData[nPos + 1], &pData[nPos + 2], (nCount - nPos - 2) * sizeof(ScAttrEntry));
215                 pData[nCount - 1].pPattern = NULL;
216                 pData[nCount - 1].nRow = 0;
217                 nCount--;
218                 bRet = sal_True;
219             }
220         }
221     }
222     return bRet;
223 }
224 
225 //------------------------------------------------------------------------
226 
227 sal_Bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex ) const
228 {
229     long    nLo         = 0;
230     long    nHi         = static_cast<long>(nCount) - 1;
231     long    nStartRow   = 0;
232     long    nEndRow     = 0;
233     long    i           = 0;
234     sal_Bool    bFound      = (nCount == 1);
235     if (pData)
236     {
237         while ( !bFound && nLo <= nHi )
238         {
239             i = (nLo + nHi) / 2;
240             if (i > 0)
241                 nStartRow = (long) pData[i - 1].nRow;
242             else
243                 nStartRow = -1;
244             nEndRow = (long) pData[i].nRow;
245             if (nEndRow < (long) nRow)
246                 nLo = ++i;
247             else
248                 if (nStartRow >= (long) nRow)
249                     nHi = --i;
250                 else
251                     bFound = sal_True;
252         }
253     }
254     else
255         bFound = sal_False;
256 
257     if (bFound)
258         nIndex=(SCSIZE)i;
259     else
260         nIndex=0;
261     return bFound;
262 }
263 
264 
265 const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const
266 {
267     SCSIZE i;
268     if (Search( nRow, i ))
269         return pData[i].pPattern;
270     else
271         return NULL;
272 }
273 
274 
275 const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow,
276         SCROW& rEndRow, SCROW nRow ) const
277 {
278     SCSIZE nIndex;
279     if ( Search( nRow, nIndex ) )
280     {
281         if ( nIndex > 0 )
282             rStartRow = pData[nIndex-1].nRow + 1;
283         else
284             rStartRow = 0;
285         rEndRow = pData[nIndex].nRow;
286         return pData[nIndex].pPattern;
287     }
288     return NULL;
289 }
290 
291 //------------------------------------------------------------------------
292 
293 void ScAttrArray::SetPattern( SCROW nRow, const ScPatternAttr* pPattern, sal_Bool bPutToPool )
294 {
295     SetPatternArea( nRow, nRow, pPattern, bPutToPool );
296 }
297 
298 
299 void ScAttrArray::SetPatternArea(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr *pPattern, sal_Bool bPutToPool )
300 {
301     if (ValidRow(nStartRow) && ValidRow(nEndRow))
302     {
303         if (bPutToPool)
304             pPattern = (const ScPatternAttr*) &pDocument->GetPool()->Put(*pPattern);
305 
306         if ((nStartRow == 0) && (nEndRow == MAXROW))
307             Reset(pPattern);
308         else
309         {
310             SCSIZE nNeeded = nCount + 2;
311             if ( nLimit < nNeeded )
312             {
313                 nLimit += SC_ATTRARRAY_DELTA;
314                 if ( nLimit < nNeeded )
315                     nLimit = nNeeded;
316                 ScAttrEntry* pNewData = new ScAttrEntry[nLimit];
317                 memcpy( pNewData, pData, nCount*sizeof(ScAttrEntry) );
318                 delete[] pData;
319                 pData = pNewData;
320             }
321 
322             ScAddress       aAdrStart( nCol, 0, nTab );
323             ScAddress       aAdrEnd  ( nCol, 0, nTab );
324 
325             SCSIZE ni = 0;      // number of entries in beginning
326             SCSIZE nx = 0;      // track position
327             SCROW ns = 0;      // start row of track position
328             if ( nStartRow > 0 )
329             {
330                 // skip beginning
331                 SCSIZE nIndex;
332                 Search( nStartRow, nIndex );
333                 ni = nIndex;
334 
335                 if ( ni > 0 )
336                 {
337                     nx = ni;
338                     ns = pData[ni-1].nRow+1;
339                 }
340             }
341 
342             // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
343             // oder bedingte Formate neu gesetzt oder geloescht werden
344             while ( ns <= nEndRow )
345             {
346                 const SfxItemSet& rNewSet = pPattern->GetItemSet();
347                 const SfxItemSet& rOldSet = pData[nx].pPattern->GetItemSet();
348 
349                 sal_Bool bNumFormatChanged;
350                 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
351                         rNewSet, rOldSet ) )
352                 {
353                     aAdrStart.SetRow( Max(nStartRow,ns) );
354                     aAdrEnd  .SetRow( Min(nEndRow,pData[nx].nRow) );
355                     pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
356 #ifdef DBG_INVALIDATE
357                     DBGOUTPUT("SetPatternArea");
358 #endif
359                 }
360                 if ( &rNewSet.Get(ATTR_CONDITIONAL) != &rOldSet.Get(ATTR_CONDITIONAL) )
361                 {
362                     pDocument->ConditionalChanged( ((const SfxUInt32Item&)
363                                     rOldSet.Get(ATTR_CONDITIONAL)).GetValue() );
364                     pDocument->ConditionalChanged( ((const SfxUInt32Item&)
365                                     rNewSet.Get(ATTR_CONDITIONAL)).GetValue() );
366                 }
367                 ns = pData[nx].nRow + 1;
368                 nx++;
369             }
370 
371             // continue modifying data array
372 
373             SCSIZE nInsert;     // insert position (MAXROWCOUNT := no insert)
374             sal_Bool bCombined = sal_False;
375             sal_Bool bSplit = sal_False;
376             if ( nStartRow > 0 )
377             {
378                 nInsert = MAXROWCOUNT;
379                 if ( pData[ni].pPattern != pPattern )
380                 {
381                     if ( ni == 0 || (pData[ni-1].nRow < nStartRow - 1) )
382                     {   // may be a split or a simple insert or just a shrink,
383                         // row adjustment is done further down
384                         if ( pData[ni].nRow > nEndRow )
385                             bSplit = sal_True;
386                         ni++;
387                         nInsert = ni;
388                     }
389                     else if ( ni > 0 && pData[ni-1].nRow == nStartRow - 1 )
390                         nInsert = ni;
391                 }
392                 if ( ni > 0 && pData[ni-1].pPattern == pPattern )
393                 {   // combine
394                     pData[ni-1].nRow = nEndRow;
395                     nInsert = MAXROWCOUNT;
396                     bCombined = sal_True;
397                 }
398             }
399             else
400                 nInsert = 0;
401 
402             SCSIZE nj = ni;     // stop position of range to replace
403             while ( nj < nCount && pData[nj].nRow <= nEndRow )
404                 nj++;
405             if ( !bSplit )
406             {
407                 if ( nj < nCount && pData[nj].pPattern == pPattern )
408                 {   // combine
409                     if ( ni > 0 )
410                     {
411                         if ( pData[ni-1].pPattern == pPattern )
412                         {   // adjacent entries
413                             pData[ni-1].nRow = pData[nj].nRow;
414                             nj++;
415                         }
416                         else if ( ni == nInsert )
417                             pData[ni-1].nRow = nStartRow - 1;   // shrink
418                     }
419                     nInsert = MAXROWCOUNT;
420                     bCombined = sal_True;
421                 }
422                 else if ( ni > 0 && ni == nInsert )
423                     pData[ni-1].nRow = nStartRow - 1;   // shrink
424             }
425             ScDocumentPool* pDocPool = pDocument->GetPool();
426             if ( bSplit )
427             {   // duplicate splitted entry in pool
428                 pDocPool->Put( *pData[ni-1].pPattern );
429             }
430             if ( ni < nj )
431             {   // remove middle entries
432                 for ( SCSIZE nk=ni; nk<nj; nk++)
433                 {   // remove entries from pool
434                     pDocPool->Remove( *pData[nk].pPattern );
435                 }
436                 if ( !bCombined )
437                 {   // replace one entry
438                     pData[ni].nRow = nEndRow;
439                     pData[ni].pPattern = pPattern;
440                     ni++;
441                     nInsert = MAXROWCOUNT;
442                 }
443                 if ( ni < nj )
444                 {   // remove entries
445                     memmove( pData + ni, pData + nj, (nCount - nj) * sizeof(ScAttrEntry) );
446                     nCount -= nj - ni;
447                 }
448             }
449 
450             if ( nInsert < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) )
451             {   // insert or append new entry
452                 if ( nInsert <= nCount )
453                 {
454                     if ( !bSplit )
455                         memmove( pData + nInsert + 1, pData + nInsert,
456                             (nCount - nInsert) * sizeof(ScAttrEntry) );
457                     else
458                     {
459                         memmove( pData + nInsert + 2, pData + nInsert,
460                             (nCount - nInsert) * sizeof(ScAttrEntry) );
461                         pData[nInsert+1] = pData[nInsert-1];
462                         nCount++;
463                     }
464                 }
465                 if ( nInsert )
466                     pData[nInsert-1].nRow = nStartRow - 1;
467                 pData[nInsert].nRow = nEndRow;
468                 pData[nInsert].pPattern = pPattern;
469                 nCount++;
470             }
471 
472             if (pDocument->IsStreamValid(nTab))
473                 pDocument->SetStreamValid(nTab, sal_False);
474         }
475     }
476 //  InfoBox(0, String(nCount) + String(" Eintraege") ).Execute();
477 
478 #ifdef DBG_UTIL
479     TestData();
480 #endif
481 }
482 
483 
484 void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, ScStyleSheet* pStyle )
485 {
486     if (ValidRow(nStartRow) && ValidRow(nEndRow))
487     {
488         SCSIZE nPos;
489         SCROW nStart=0;
490         if (!Search( nStartRow, nPos ))
491         {
492             DBG_ERROR("Search-Fehler");
493             return;
494         }
495 
496         ScAddress aAdrStart( nCol, 0, nTab );
497         ScAddress aAdrEnd  ( nCol, 0, nTab );
498 
499         do
500         {
501             const ScPatternAttr* pOldPattern = pData[nPos].pPattern;
502             ScPatternAttr* pNewPattern = new ScPatternAttr(*pOldPattern);
503             pNewPattern->SetStyleSheet(pStyle);
504             SCROW nY1 = nStart;
505             SCROW nY2 = pData[nPos].nRow;
506             nStart = pData[nPos].nRow + 1;
507 
508             if ( *pNewPattern == *pOldPattern )
509             {
510                 // keep the original pattern (might be default)
511                 // pNewPattern is deleted below
512                 nPos++;
513             }
514             else if ( nY1 < nStartRow || nY2 > nEndRow )
515             {
516                 if (nY1 < nStartRow) nY1=nStartRow;
517                 if (nY2 > nEndRow) nY2=nEndRow;
518                 SetPatternArea( nY1, nY2, pNewPattern, sal_True );
519                 Search( nStart, nPos );
520             }
521             else
522             {
523                 // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
524                 // bedingte Formate in Vorlagen gibt es (noch) nicht
525 
526                 const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
527                 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
528 
529                 sal_Bool bNumFormatChanged;
530                 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
531                         rNewSet, rOldSet ) )
532                 {
533                     aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 );
534                     aAdrEnd  .SetRow( pData[nPos].nRow );
535                     pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
536 #ifdef DBG_INVALIDATE
537                     DBGOUTPUT("ApplyStyleArea");
538 #endif
539                 }
540 
541                 pDocument->GetPool()->Remove(*pData[nPos].pPattern);
542                 pData[nPos].pPattern = (const ScPatternAttr*)
543                                             &pDocument->GetPool()->Put(*pNewPattern);
544                 if (Concat(nPos))
545                     Search(nStart, nPos);
546                 else
547                     nPos++;
548             }
549             delete pNewPattern;
550         }
551         while ((nStart <= nEndRow) && (nPos < nCount));
552 
553         if (pDocument->IsStreamValid(nTab))
554             pDocument->SetStreamValid(nTab, sal_False);
555     }
556 
557 #ifdef DBG_UTIL
558     TestData();
559 #endif
560 }
561 
562 
563     // const wird weggecastet, weil es sonst
564     // zu ineffizient/kompliziert wird!
565 #define SET_LINECOLOR(dest,c)                       \
566     if ((dest))                                     \
567     {                                               \
568         ((SvxBorderLine*)(dest))->SetColor((c));    \
569     }
570 
571 #define SET_LINE(dest,src)                              \
572     if ((dest))                                         \
573     {                                                   \
574         SvxBorderLine* pCast = (SvxBorderLine*)(dest);  \
575         pCast->SetOutWidth((src)->GetOutWidth());       \
576         pCast->SetInWidth ((src)->GetInWidth());        \
577         pCast->SetDistance((src)->GetDistance());       \
578     }
579 
580 void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
581                                       const SvxBorderLine* pLine, sal_Bool bColorOnly )
582 {
583     if ( bColorOnly && !pLine )
584         return;
585 
586     if (ValidRow(nStartRow) && ValidRow(nEndRow))
587     {
588         SCSIZE nPos;
589         SCROW nStart=0;
590         if (!Search( nStartRow, nPos ))
591         {
592             DBG_ERROR("Search-Fehler");
593             return;
594         }
595 
596         do
597         {
598             const ScPatternAttr*    pOldPattern = pData[nPos].pPattern;
599             const SfxItemSet&       rOldSet = pOldPattern->GetItemSet();
600             const SfxPoolItem*      pBoxItem = 0;
601             SfxItemState            eState = rOldSet.GetItemState( ATTR_BORDER, sal_True, &pBoxItem );
602             const SfxPoolItem*      pTLBRItem = 0;
603             SfxItemState            eTLBRState = rOldSet.GetItemState( ATTR_BORDER_TLBR, sal_True, &pTLBRItem );
604             const SfxPoolItem*      pBLTRItem = 0;
605             SfxItemState            eBLTRState = rOldSet.GetItemState( ATTR_BORDER_BLTR, sal_True, &pBLTRItem );
606 
607             if ( (SFX_ITEM_SET == eState) || (SFX_ITEM_SET == eTLBRState) || (SFX_ITEM_SET == eBLTRState) )
608             {
609                 ScPatternAttr*  pNewPattern = new ScPatternAttr(*pOldPattern);
610                 SfxItemSet&     rNewSet = pNewPattern->GetItemSet();
611                 SCROW           nY1 = nStart;
612                 SCROW           nY2 = pData[nPos].nRow;
613 
614                 SvxBoxItem*     pNewBoxItem = pBoxItem ? (SvxBoxItem*)pBoxItem->Clone() : 0;
615                 SvxLineItem*    pNewTLBRItem = pTLBRItem ? (SvxLineItem*)pTLBRItem->Clone() : 0;
616                 SvxLineItem*    pNewBLTRItem = pBLTRItem ? (SvxLineItem*)pBLTRItem->Clone() : 0;
617 
618                 // Linienattribute holen und mit Parametern aktualisieren
619 
620                 if ( !pLine )
621                 {
622                     if( pNewBoxItem )
623                     {
624                         if ( pNewBoxItem->GetTop() )    pNewBoxItem->SetLine( NULL, BOX_LINE_TOP );
625                         if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( NULL, BOX_LINE_BOTTOM );
626                         if ( pNewBoxItem->GetLeft() )   pNewBoxItem->SetLine( NULL, BOX_LINE_LEFT );
627                         if ( pNewBoxItem->GetRight() )  pNewBoxItem->SetLine( NULL, BOX_LINE_RIGHT );
628                     }
629                     if( pNewTLBRItem && pNewTLBRItem->GetLine() )
630                         pNewTLBRItem->SetLine( 0 );
631                     if( pNewBLTRItem && pNewBLTRItem->GetLine() )
632                         pNewBLTRItem->SetLine( 0 );
633                 }
634                 else
635                 {
636                     if ( bColorOnly )
637                     {
638                         Color aColor( pLine->GetColor() );
639                         if( pNewBoxItem )
640                         {
641                             SET_LINECOLOR( pNewBoxItem->GetTop(),    aColor );
642                             SET_LINECOLOR( pNewBoxItem->GetBottom(), aColor );
643                             SET_LINECOLOR( pNewBoxItem->GetLeft(),   aColor );
644                             SET_LINECOLOR( pNewBoxItem->GetRight(),   aColor );
645                         }
646                         if( pNewTLBRItem )
647                             SET_LINECOLOR( pNewTLBRItem->GetLine(), aColor );
648                         if( pNewBLTRItem )
649                             SET_LINECOLOR( pNewBLTRItem->GetLine(), aColor );
650                     }
651                     else
652                     {
653                         if( pNewBoxItem )
654                         {
655                             SET_LINE( pNewBoxItem->GetTop(),    pLine );
656                             SET_LINE( pNewBoxItem->GetBottom(), pLine );
657                             SET_LINE( pNewBoxItem->GetLeft(),   pLine );
658                             SET_LINE( pNewBoxItem->GetRight(),   pLine );
659                         }
660                         if( pNewTLBRItem )
661                             SET_LINE( pNewTLBRItem->GetLine(), pLine );
662                         if( pNewBLTRItem )
663                             SET_LINE( pNewBLTRItem->GetLine(), pLine );
664                     }
665                 }
666                 if( pNewBoxItem )   rNewSet.Put( *pNewBoxItem );
667                 if( pNewTLBRItem )  rNewSet.Put( *pNewTLBRItem );
668                 if( pNewBLTRItem )  rNewSet.Put( *pNewBLTRItem );
669 
670                 nStart = pData[nPos].nRow + 1;
671 
672                 if ( nY1 < nStartRow || nY2 > nEndRow )
673                 {
674                     if (nY1 < nStartRow) nY1=nStartRow;
675                     if (nY2 > nEndRow) nY2=nEndRow;
676                     SetPatternArea( nY1, nY2, pNewPattern, sal_True );
677                     Search( nStart, nPos );
678                 }
679                 else
680                 {
681                         //! aus Pool loeschen?
682                     pDocument->GetPool()->Remove(*pData[nPos].pPattern);
683                     pData[nPos].pPattern = (const ScPatternAttr*)
684                                 &pDocument->GetPool()->Put(*pNewPattern);
685 
686                     if (Concat(nPos))
687                         Search(nStart, nPos);
688                     else
689                         nPos++;
690                 }
691                 delete pNewBoxItem;
692                 delete pNewTLBRItem;
693                 delete pNewBLTRItem;
694                 delete pNewPattern;
695             }
696             else
697             {
698                 nStart = pData[nPos].nRow + 1;
699                 nPos++;
700             }
701         }
702         while ((nStart <= nEndRow) && (nPos < nCount));
703     }
704 }
705 
706 #undef SET_LINECOLOR
707 #undef SET_LINE
708 
709 
710 void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache )
711 {
712 #ifdef DBG_UTIL
713     TestData();
714 #endif
715 
716     if (ValidRow(nStartRow) && ValidRow(nEndRow))
717     {
718         SCSIZE nPos;
719         SCROW nStart=0;
720         if (!Search( nStartRow, nPos ))
721         {
722             DBG_ERROR("Search-Fehler");
723             return;
724         }
725 
726         ScAddress aAdrStart( nCol, 0, nTab );
727         ScAddress aAdrEnd  ( nCol, 0, nTab );
728 
729         do
730         {
731             const ScPatternAttr* pOldPattern = pData[nPos].pPattern;
732             const ScPatternAttr* pNewPattern = (const ScPatternAttr*) &pCache->ApplyTo( *pOldPattern, sal_True );
733             ScDocumentPool::CheckRef( *pOldPattern );
734             ScDocumentPool::CheckRef( *pNewPattern );
735             if (pNewPattern != pOldPattern)
736             {
737                 SCROW nY1 = nStart;
738                 SCROW nY2 = pData[nPos].nRow;
739                 nStart = pData[nPos].nRow + 1;
740 
741                 if ( nY1 < nStartRow || nY2 > nEndRow )
742                 {
743                     if (nY1 < nStartRow) nY1=nStartRow;
744                     if (nY2 > nEndRow) nY2=nEndRow;
745                     SetPatternArea( nY1, nY2, pNewPattern );
746                     Search( nStart, nPos );
747                 }
748                 else
749                 {
750                     // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
751 
752                     const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
753                     const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
754 
755                     sal_Bool bNumFormatChanged;
756                     if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
757                             rNewSet, rOldSet ) )
758                     {
759                         aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 );
760                         aAdrEnd  .SetRow( pData[nPos].nRow );
761                         pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
762 #ifdef DBG_INVALIDATE
763                         DBGOUTPUT("ApplyCacheArea");
764 #endif
765                     }
766 
767                     // bedingte Formate neu gesetzt oder geloescht ?
768 
769                     if ( &rNewSet.Get(ATTR_CONDITIONAL) != &rOldSet.Get(ATTR_CONDITIONAL) )
770                     {
771                         pDocument->ConditionalChanged( ((const SfxUInt32Item&)
772                                         rOldSet.Get(ATTR_CONDITIONAL)).GetValue() );
773                         pDocument->ConditionalChanged( ((const SfxUInt32Item&)
774                                         rNewSet.Get(ATTR_CONDITIONAL)).GetValue() );
775                     }
776 
777                     pDocument->GetPool()->Remove(*pData[nPos].pPattern);
778                     pData[nPos].pPattern = pNewPattern;
779                     if (Concat(nPos))
780                         Search(nStart, nPos);
781                     else
782                         ++nPos;
783                 }
784             }
785             else
786             {
787 //!!!!!!!!!!!!!!!!!! mit diesem Remove gibt es Abstuerze (Calc1 Import)
788 //!             pDocument->GetPool()->Remove(*pNewPattern);
789                 nStart = pData[nPos].nRow + 1;
790                 ++nPos;
791             }
792         }
793         while (nStart <= nEndRow);
794 
795         if (pDocument->IsStreamValid(nTab))
796             pDocument->SetStreamValid(nTab, sal_False);
797     }
798 
799 #ifdef DBG_UTIL
800     TestData();
801 #endif
802 }
803 
804 
805 void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource )
806 {
807     const SfxPoolItem* pNewItem;
808     const SfxPoolItem* pOldItem;
809     for (sal_uInt16 nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++)
810     {
811         //  pMergeSet hat keinen Parent
812         SfxItemState eOldState = rMergeSet.GetItemState( nId, sal_False, &pOldItem );
813 
814         if ( eOldState == SFX_ITEM_DEFAULT )                // Default
815         {
816             SfxItemState eNewState = rSource.GetItemState( nId, sal_True, &pNewItem );
817             if ( eNewState == SFX_ITEM_SET )
818             {
819                 if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) )
820                     rMergeSet.InvalidateItem( nId );
821             }
822         }
823         else if ( eOldState == SFX_ITEM_SET )               // Item gesetzt
824         {
825             SfxItemState eNewState = rSource.GetItemState( nId, sal_True, &pNewItem );
826             if ( eNewState == SFX_ITEM_SET )
827             {
828                 if ( pNewItem != pOldItem )                 // beide gepuhlt
829                     rMergeSet.InvalidateItem( nId );
830             }
831             else            // Default
832             {
833                 if ( *pOldItem != rSource.GetPool()->GetDefaultItem(nId) )
834                     rMergeSet.InvalidateItem( nId );
835             }
836         }
837         //  Dontcare bleibt Dontcare
838     }
839 }
840 
841 
842 void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow,
843                                     ScMergePatternState& rState, sal_Bool bDeep ) const
844 {
845     if (ValidRow(nStartRow) && ValidRow(nEndRow))
846     {
847         SCSIZE nPos;
848         SCROW nStart=0;
849         if (!Search( nStartRow, nPos ))
850         {
851             DBG_ERROR("Search-Fehler");
852             return;
853         }
854 
855         do
856         {
857             //  gleiche Patterns muessen nicht mehrfach angesehen werden
858 
859             const ScPatternAttr* pPattern = pData[nPos].pPattern;
860             if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 )
861             {
862                 const SfxItemSet& rThisSet = pPattern->GetItemSet();
863                 if (rState.pItemSet)
864                 {
865                     //  (*ppSet)->MergeValues( rThisSet, sal_False );
866                     //  geht nicht, weil die Vorlagen nicht beruecksichtigt werden
867 
868                     if (bDeep)
869                         lcl_MergeDeep( *rState.pItemSet, rThisSet );
870                     else
871                         rState.pItemSet->MergeValues( rThisSet, sal_False );
872                 }
873                 else
874                 {
875                     //  erstes Pattern - in Set ohne Parent kopieren
876                     rState.pItemSet = new SfxItemSet( *rThisSet.GetPool(), rThisSet.GetRanges() );
877                     rState.pItemSet->Set( rThisSet, bDeep );
878                 }
879 
880                 rState.pOld2 = rState.pOld1;
881                 rState.pOld1 = pPattern;
882             }
883 
884             nStart = pData[nPos].nRow + 1;
885             ++nPos;
886         }
887         while (nStart <= nEndRow);
888     }
889 }
890 
891 
892 
893 //          Umrandung zusammenbauen
894 
895 sal_Bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
896                             sal_uInt8& rModified, const SvxBorderLine*& rpNew )
897 {
898     if (rModified == SC_LINE_DONTCARE)
899         return sal_False;                       // weiter geht's nicht
900 
901     if (rModified == SC_LINE_EMPTY)
902     {
903         rModified = SC_LINE_SET;
904         rpNew = pNewLine;
905         return sal_True;                        // zum ersten mal gesetzt
906     }
907 
908     if (pOldLine == pNewLine)
909     {
910         rpNew = pOldLine;
911         return sal_False;
912     }
913 
914     if (pOldLine && pNewLine)
915         if (*pOldLine == *pNewLine)
916         {
917             rpNew = pOldLine;
918             return sal_False;
919         }
920 
921     rModified = SC_LINE_DONTCARE;
922     rpNew = NULL;
923     return sal_True;                            // andere Linie -> dontcare
924 }
925 
926 
927 void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
928                                 ScLineFlags& rFlags, const ScPatternAttr* pPattern,
929                                 sal_Bool bLeft, SCCOL nDistRight, sal_Bool bTop, SCROW nDistBottom )
930 {
931     //  rechten/unteren Rahmen setzen, wenn Zelle bis zum Ende zusammengefasst:
932     const ScMergeAttr& rMerge = (const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE);
933     if ( rMerge.GetColMerge() == nDistRight + 1 )
934         nDistRight = 0;
935     if ( rMerge.GetRowMerge() == nDistBottom + 1 )
936         nDistBottom = 0;
937 
938     const SvxBoxItem* pCellFrame = (SvxBoxItem*) &pPattern->GetItemSet().Get( ATTR_BORDER );
939     const SvxBorderLine* pLeftAttr   = pCellFrame->GetLeft();
940     const SvxBorderLine* pRightAttr  = pCellFrame->GetRight();
941     const SvxBorderLine* pTopAttr    = pCellFrame->GetTop();
942     const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom();
943     const SvxBorderLine* pNew;
944 
945     if (bTop)
946     {
947         if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew ))
948             pLineOuter->SetLine( pNew, BOX_LINE_TOP );
949     }
950     else
951     {
952         if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew ))
953             pLineInner->SetLine( pNew, BOXINFO_LINE_HORI );
954     }
955 
956     if (nDistBottom == 0)
957     {
958         if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew ))
959             pLineOuter->SetLine( pNew, BOX_LINE_BOTTOM );
960     }
961     else
962     {
963         if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew ))
964             pLineInner->SetLine( pNew, BOXINFO_LINE_HORI );
965     }
966 
967     if (bLeft)
968     {
969         if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew ))
970             pLineOuter->SetLine( pNew, BOX_LINE_LEFT );
971     }
972     else
973     {
974         if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew ))
975             pLineInner->SetLine( pNew, BOXINFO_LINE_VERT );
976     }
977 
978     if (nDistRight == 0)
979     {
980         if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew ))
981             pLineOuter->SetLine( pNew, BOX_LINE_RIGHT );
982     }
983     else
984     {
985         if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew ))
986             pLineInner->SetLine( pNew, BOXINFO_LINE_VERT );
987     }
988 }
989 
990 
991 void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
992                     ScLineFlags& rFlags,
993                     SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight ) const
994 {
995     const ScPatternAttr* pPattern;
996 
997     if (nStartRow == nEndRow)
998     {
999         pPattern = GetPattern( nStartRow );
1000         lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_True, 0 );
1001     }
1002     else
1003     {
1004         pPattern = GetPattern( nStartRow );
1005         lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_True,
1006                             nEndRow-nStartRow );
1007 
1008         SCSIZE nStartIndex;
1009         SCSIZE nEndIndex;
1010         Search( nStartRow+1, nStartIndex );
1011         Search( nEndRow-1, nEndIndex );
1012         for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1013         {
1014             pPattern = (ScPatternAttr*) pData[i].pPattern;
1015             lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_False,
1016                             nEndRow - Min( pData[i].nRow, (SCROW)(nEndRow-1) ) );
1017             // nDistBottom hier immer > 0
1018         }
1019 
1020         pPattern = GetPattern( nEndRow );
1021         lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_False, 0 );
1022     }
1023 }
1024 
1025 //
1026 //  Rahmen anwenden
1027 //
1028 
1029 //  ApplyFrame - auf einen Eintrag im Array
1030 
1031 
1032 sal_Bool ScAttrArray::ApplyFrame( const SvxBoxItem*     pBoxItem,
1033                               const SvxBoxInfoItem* pBoxInfoItem,
1034                               SCROW nStartRow, SCROW nEndRow,
1035                               sal_Bool bLeft, SCCOL nDistRight, sal_Bool bTop, SCROW nDistBottom )
1036 {
1037     DBG_ASSERT( pBoxItem && pBoxInfoItem, "Linienattribute fehlen!" );
1038 
1039     const ScPatternAttr* pPattern = GetPattern( nStartRow );
1040     const SvxBoxItem* pOldFrame = (const SvxBoxItem*)
1041                                   &pPattern->GetItemSet().Get( ATTR_BORDER );
1042 
1043     //  rechten/unteren Rahmen setzen, wenn Zelle bis zum Ende zusammengefasst:
1044     const ScMergeAttr& rMerge = (const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE);
1045     if ( rMerge.GetColMerge() == nDistRight + 1 )
1046         nDistRight = 0;
1047     if ( rMerge.GetRowMerge() == nDistBottom + 1 )
1048         nDistBottom = 0;
1049 
1050     SvxBoxItem aNewFrame( *pOldFrame );
1051 
1052     if ( bLeft ? pBoxInfoItem->IsValid(VALID_LEFT) : pBoxInfoItem->IsValid(VALID_VERT) )
1053         aNewFrame.SetLine( bLeft ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(),
1054             BOX_LINE_LEFT );
1055     if ( (nDistRight==0) ? pBoxInfoItem->IsValid(VALID_RIGHT) : pBoxInfoItem->IsValid(VALID_VERT) )
1056         aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(),
1057             BOX_LINE_RIGHT );
1058     if ( bTop ? pBoxInfoItem->IsValid(VALID_TOP) : pBoxInfoItem->IsValid(VALID_HORI) )
1059         aNewFrame.SetLine( bTop ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(),
1060             BOX_LINE_TOP );
1061     if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(VALID_BOTTOM) : pBoxInfoItem->IsValid(VALID_HORI) )
1062         aNewFrame.SetLine( (nDistBottom==0) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(),
1063             BOX_LINE_BOTTOM );
1064 
1065     if (aNewFrame == *pOldFrame)
1066     {
1067         // nothing to do
1068         return sal_False;
1069     }
1070     else
1071     {
1072         SfxItemPoolCache aCache( pDocument->GetPool(), &aNewFrame );
1073         ApplyCacheArea( nStartRow, nEndRow, &aCache );
1074 
1075 /*      ScPatternAttr* pNewPattern = (ScPatternAttr*) pPattern->Clone();
1076         pNewPattern->GetItemSet().Put( aNewFrame );
1077         SetPatternArea( nStartRow, nEndRow, pNewPattern, sal_True );
1078 */
1079         return sal_True;
1080     }
1081 }
1082 
1083 
1084 void ScAttrArray::ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner,
1085                             SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight )
1086 {
1087     if (nStartRow == nEndRow)
1088         ApplyFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, sal_True, 0 );
1089     else
1090     {
1091         ApplyFrame( pLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight,
1092                         sal_True, nEndRow-nStartRow );
1093 
1094         if ( nEndRow > nStartRow+1 )                // innerer Teil vorhanden?
1095         {
1096             SCSIZE nStartIndex;
1097             SCSIZE nEndIndex;
1098             Search( nStartRow+1, nStartIndex );
1099             Search( nEndRow-1, nEndIndex );
1100             SCROW nTmpStart = nStartRow+1;
1101             SCROW nTmpEnd;
1102             for (SCSIZE i=nStartIndex; i<=nEndIndex;)
1103             {
1104                 nTmpEnd = Min( (SCROW)(nEndRow-1), (SCROW)(pData[i].nRow) );
1105                 sal_Bool bChanged = ApplyFrame( pLineOuter, pLineInner, nTmpStart, nTmpEnd,
1106                                             bLeft, nDistRight, sal_False, nEndRow-nTmpEnd );
1107                 nTmpStart = nTmpEnd+1;
1108                 if (bChanged)
1109                 {
1110                     Search(nTmpStart, i);
1111                     Search(nEndRow-1, nEndIndex);
1112                 }
1113                 else
1114                     i++;
1115             }
1116         }
1117 
1118         ApplyFrame( pLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, sal_False, 0 );
1119     }
1120 }
1121 
1122 
1123 long lcl_LineSize( const SvxBorderLine& rLine )
1124 {
1125     //  nur eine Linie -> halbe Breite, min. 20
1126     //  doppelte Linie -> halber Abstand + eine Linie (je min. 20)
1127 
1128     long nTotal = 0;
1129     sal_uInt16 nWidth = Max( rLine.GetOutWidth(), rLine.GetInWidth() );
1130     sal_uInt16 nDist = rLine.GetDistance();
1131     if (nDist)
1132     {
1133         DBG_ASSERT( rLine.GetOutWidth() && rLine.GetInWidth(),
1134                         "Linie hat Abstand, aber nur eine Breite ???" );
1135 
1136 //      nTotal += ( nDist > 40 ) ? ( nDist / 2 ) : 20;
1137         nTotal += ( nDist > 20 ) ? nDist : 20;
1138         nTotal += ( nWidth > 20 ) ? nWidth : 20;
1139     }
1140     else if (nWidth)
1141 //      nTotal += ( nWidth > 40 ) ? ( nWidth / 2 ) : 20;
1142         nTotal += ( nWidth > 20 ) ? nWidth  : 20;
1143 
1144         //! auch halbieren ???
1145 
1146     return nTotal;
1147 }
1148 
1149 
1150 sal_Bool ScAttrArray::HasLines( SCROW nRow1, SCROW nRow2, Rectangle& rSizes,
1151                                 sal_Bool bLeft, sal_Bool bRight ) const
1152 {
1153     SCSIZE nStartIndex;
1154     SCSIZE nEndIndex;
1155     Search( nRow1, nStartIndex );
1156     Search( nRow2, nEndIndex );
1157     sal_Bool bFound = sal_False;
1158 
1159     const SvxBoxItem* pItem = 0;
1160     const SvxBorderLine* pLine = 0;
1161     long nCmp;
1162 
1163     //  oben
1164 
1165     pItem = (const SvxBoxItem*) &pData[nStartIndex].pPattern->GetItem(ATTR_BORDER);
1166     pLine = pItem->GetTop();
1167     if (pLine)
1168     {
1169         nCmp = lcl_LineSize(*pLine);
1170         if ( nCmp > rSizes.Top() )
1171             rSizes.Top() = nCmp;
1172         bFound = sal_True;
1173     }
1174 
1175     //  unten
1176 
1177     if ( nEndIndex != nStartIndex )
1178         pItem = (const SvxBoxItem*) &pData[nEndIndex].pPattern->GetItem(ATTR_BORDER);
1179     pLine = pItem->GetBottom();
1180     if (pLine)
1181     {
1182         nCmp = lcl_LineSize(*pLine);
1183         if ( nCmp > rSizes.Bottom() )
1184             rSizes.Bottom() = nCmp;
1185         bFound = sal_True;
1186     }
1187 
1188     if ( bLeft || bRight )
1189         for ( SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1190         {
1191             pItem = (const SvxBoxItem*) &pData[i].pPattern->GetItem(ATTR_BORDER);
1192 
1193             //  links
1194 
1195             if (bLeft)
1196             {
1197                 pLine = pItem->GetLeft();
1198                 if (pLine)
1199                 {
1200                     nCmp = lcl_LineSize(*pLine);
1201                     if ( nCmp > rSizes.Left() )
1202                         rSizes.Left() = nCmp;
1203                     bFound = sal_True;
1204                 }
1205             }
1206 
1207             //  rechts
1208 
1209             if (bRight)
1210             {
1211                 pLine = pItem->GetRight();
1212                 if (pLine)
1213                 {
1214                     nCmp = lcl_LineSize(*pLine);
1215                     if ( nCmp > rSizes.Right() )
1216                         rSizes.Right() = nCmp;
1217                     bFound = sal_True;
1218                 }
1219             }
1220         }
1221 
1222     return bFound;
1223 }
1224 
1225 //  Testen, ob Bereich bestimmtes Attribut enthaelt
1226 
1227 bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const
1228 {
1229     SCSIZE nStartIndex;
1230     SCSIZE nEndIndex;
1231     Search( nRow1, nStartIndex );
1232     Search( nRow2, nEndIndex );
1233     bool bFound = false;
1234 
1235     for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++)
1236     {
1237         const ScPatternAttr* pPattern = pData[i].pPattern;
1238         if ( nMask & HASATTR_MERGED )
1239         {
1240             const ScMergeAttr* pMerge =
1241                     (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE );
1242             if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
1243                 bFound = true;
1244         }
1245         if ( nMask & ( HASATTR_OVERLAPPED | HASATTR_NOTOVERLAPPED | HASATTR_AUTOFILTER ) )
1246         {
1247             const ScMergeFlagAttr* pMergeFlag =
1248                     (const ScMergeFlagAttr*) &pPattern->GetItem( ATTR_MERGE_FLAG );
1249             if ( (nMask & HASATTR_OVERLAPPED) && pMergeFlag->IsOverlapped() )
1250                 bFound = true;
1251             if ( (nMask & HASATTR_NOTOVERLAPPED) && !pMergeFlag->IsOverlapped() )
1252                 bFound = true;
1253             if ( (nMask & HASATTR_AUTOFILTER) && pMergeFlag->HasAutoFilter() )
1254                 bFound = true;
1255         }
1256         if ( nMask & HASATTR_LINES )
1257         {
1258             const SvxBoxItem* pBox =
1259                     (const SvxBoxItem*) &pPattern->GetItem( ATTR_BORDER );
1260             if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() )
1261                 bFound = true;
1262         }
1263         if ( nMask & HASATTR_SHADOW )
1264         {
1265             const SvxShadowItem* pShadow =
1266                     (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW );
1267             if ( pShadow->GetLocation() != SVX_SHADOW_NONE )
1268                 bFound = true;
1269         }
1270         if ( nMask & HASATTR_CONDITIONAL )
1271         {
1272             const SfxUInt32Item* pConditional =
1273                     (const SfxUInt32Item*) &pPattern->GetItem( ATTR_CONDITIONAL );
1274             if ( pConditional->GetValue() != 0 )
1275                 bFound = true;
1276         }
1277         if ( nMask & HASATTR_PROTECTED )
1278         {
1279             const ScProtectionAttr* pProtect =
1280                     (const ScProtectionAttr*) &pPattern->GetItem( ATTR_PROTECTION );
1281             if ( pProtect->GetProtection() || pProtect->GetHideCell() )
1282                 bFound = true;
1283         }
1284         if ( nMask & HASATTR_ROTATE )
1285         {
1286             const SfxInt32Item* pRotate =
1287                     (const SfxInt32Item*) &pPattern->GetItem( ATTR_ROTATE_VALUE );
1288             // 90 or 270 degrees is former SvxOrientationItem - only look for other values
1289             // (see ScPatternAttr::GetCellOrientation)
1290             sal_Int32 nAngle = pRotate->GetValue();
1291             if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 )
1292                 bFound = true;
1293         }
1294         if ( nMask & HASATTR_NEEDHEIGHT )
1295         {
1296             if (pPattern->GetCellOrientation() != SVX_ORIENTATION_STANDARD)
1297                 bFound = true;
1298             else if (((const SfxBoolItem&)pPattern->GetItem( ATTR_LINEBREAK )).GetValue())
1299                 bFound = true;
1300             else if ((SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern->
1301                         GetItem( ATTR_HOR_JUSTIFY )).GetValue() == SVX_HOR_JUSTIFY_BLOCK)
1302                 bFound = true;
1303             else if (((const SfxUInt32Item&)pPattern->GetItem( ATTR_CONDITIONAL )).GetValue())
1304                 bFound = true;
1305             else if (((const SfxInt32Item&)pPattern->GetItem( ATTR_ROTATE_VALUE )).GetValue())
1306                 bFound = true;
1307         }
1308         if ( nMask & ( HASATTR_SHADOW_RIGHT | HASATTR_SHADOW_DOWN ) )
1309         {
1310             const SvxShadowItem* pShadow =
1311                     (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW );
1312             SvxShadowLocation eLoc = pShadow->GetLocation();
1313             if ( nMask & HASATTR_SHADOW_RIGHT )
1314                 if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT )
1315                     bFound = true;
1316             if ( nMask & HASATTR_SHADOW_DOWN )
1317                 if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT )
1318                     bFound = true;
1319         }
1320         if ( nMask & HASATTR_RTL )
1321         {
1322             const SvxFrameDirectionItem& rDirection =
1323                     (const SvxFrameDirectionItem&) pPattern->GetItem( ATTR_WRITINGDIR );
1324             if ( rDirection.GetValue() == FRMDIR_HORI_RIGHT_TOP )
1325                 bFound = true;
1326         }
1327         if ( nMask & HASATTR_RIGHTORCENTER )
1328         {
1329             //  called only if the sheet is LTR, so physical=logical alignment can be assumed
1330             SvxCellHorJustify eHorJust = (SvxCellHorJustify)
1331                     ((const SvxHorJustifyItem&) pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue();
1332             if ( eHorJust == SVX_HOR_JUSTIFY_RIGHT || eHorJust == SVX_HOR_JUSTIFY_CENTER )
1333                 bFound = true;
1334         }
1335     }
1336 
1337     return bFound;
1338 }
1339 
1340 //  Bereich um evtl. enthaltene Zusammenfassungen erweitern
1341 //  und evtl. MergeFlag anpassen (bRefresh)
1342 
1343 sal_Bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
1344                                 SCCOL& rPaintCol, SCROW& rPaintRow,
1345                                 sal_Bool bRefresh, sal_Bool bAttrs )
1346 {
1347     const ScPatternAttr* pPattern;
1348     const ScMergeAttr* pItem;
1349     SCSIZE nStartIndex;
1350     SCSIZE nEndIndex;
1351     Search( nStartRow, nStartIndex );
1352     Search( nEndRow, nEndIndex );
1353     sal_Bool bFound = sal_False;
1354 
1355     for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1356     {
1357         pPattern = pData[i].pPattern;
1358         pItem = (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE );
1359         SCsCOL  nCountX = pItem->GetColMerge();
1360         SCsROW  nCountY = pItem->GetRowMerge();
1361         if (nCountX>1 || nCountY>1)
1362         {
1363             SCROW nThisRow = (i>0) ? pData[i-1].nRow+1 : 0;
1364             SCCOL nMergeEndCol = nThisCol + nCountX - 1;
1365             SCROW nMergeEndRow = nThisRow + nCountY - 1;
1366             if (nMergeEndCol > rPaintCol && nMergeEndCol <= MAXCOL)
1367                 rPaintCol = nMergeEndCol;
1368             if (nMergeEndRow > rPaintRow && nMergeEndRow <= MAXROW)
1369                 rPaintRow = nMergeEndRow;
1370             bFound = sal_True;
1371 
1372             if (bAttrs)
1373             {
1374                 const SvxShadowItem* pShadow =
1375                         (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW );
1376                 SvxShadowLocation eLoc = pShadow->GetLocation();
1377                 if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT )
1378                     if ( nMergeEndCol+1 > rPaintCol && nMergeEndCol < MAXCOL )
1379                         rPaintCol = nMergeEndCol+1;
1380                 if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT )
1381                     if ( nMergeEndRow+1 > rPaintRow && nMergeEndRow < MAXROW )
1382                         rPaintRow = nMergeEndRow+1;
1383             }
1384 
1385             if (bRefresh)
1386             {
1387                 if ( nMergeEndCol > nThisCol )
1388                     pDocument->ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, pData[i].nRow,
1389                                 nTab, SC_MF_HOR );
1390                 if ( nMergeEndRow > nThisRow )
1391                     pDocument->ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow,
1392                                 nTab, SC_MF_VER );
1393                 if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow )
1394                     pDocument->ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow,
1395                                 nTab, SC_MF_HOR | SC_MF_VER );
1396 
1397                 Search( nThisRow, i );                  // Daten wurden veraendert
1398                 Search( nStartRow, nStartIndex );
1399                 Search( nEndRow, nEndIndex );
1400             }
1401         }
1402     }
1403 
1404     return bFound;
1405 }
1406 
1407 
1408 sal_Bool ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
1409 {
1410     sal_Bool bFound = sal_False;
1411     const ScPatternAttr* pPattern;
1412     const ScMergeAttr* pItem;
1413     SCSIZE nIndex;
1414 
1415     Search( nStartRow, nIndex );
1416     SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
1417     if (nThisStart < nStartRow)
1418         nThisStart = nStartRow;
1419 
1420     while ( nThisStart <= nEndRow )
1421     {
1422         SCROW nThisEnd = pData[nIndex].nRow;
1423         if (nThisEnd > nEndRow)
1424             nThisEnd = nEndRow;
1425 
1426         pPattern = pData[nIndex].pPattern;
1427         pItem = (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE );
1428         SCsCOL  nCountX = pItem->GetColMerge();
1429         SCsROW  nCountY = pItem->GetRowMerge();
1430         if (nCountX>1 || nCountY>1)
1431         {
1432             const ScMergeAttr* pAttr = (const ScMergeAttr*)
1433                                             &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE );
1434             const ScMergeFlagAttr* pFlagAttr = (const ScMergeFlagAttr*)
1435                                             &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE_FLAG );
1436 
1437             DBG_ASSERT( nCountY==1 || nThisStart==nThisEnd, "was'n hier los?" );
1438 
1439             SCCOL nThisCol = nCol;
1440             SCCOL nMergeEndCol = nThisCol + nCountX - 1;
1441             SCROW nMergeEndRow = nThisEnd + nCountY - 1;
1442 
1443             //! ApplyAttr fuer Bereiche !!!
1444 
1445             for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++)
1446                 pDocument->ApplyAttr( nThisCol, nThisRow, nTab, *pAttr );
1447 
1448             ScPatternAttr*  pNewPattern = new ScPatternAttr( pDocument->GetPool() );
1449             SfxItemSet*     pSet = &pNewPattern->GetItemSet();
1450             pSet->Put( *pFlagAttr );
1451             pDocument->ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow,
1452                                                 nTab, *pNewPattern );
1453             delete pNewPattern;
1454 
1455             Search( nThisEnd, nIndex );                 // Daten wurden veraendert !!!
1456         }
1457 
1458         ++nIndex;
1459         if ( nIndex < nCount )
1460             nThisStart = pData[nIndex-1].nRow+1;
1461         else
1462             nThisStart = MAXROW+1;      // Ende
1463     }
1464 
1465     return bFound;
1466 }
1467 
1468             //      Bereich loeschen, aber Merge-Flags stehenlassen
1469 
1470 void ScAttrArray::DeleteAreaSafe(SCROW nStartRow, SCROW nEndRow)
1471 {
1472     SetPatternAreaSafe( nStartRow, nEndRow, pDocument->GetDefPattern(), sal_True );
1473 }
1474 
1475 
1476 void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow,
1477                         const ScPatternAttr* pWantedPattern, sal_Bool bDefault )
1478 {
1479     const ScPatternAttr*    pOldPattern;
1480     const ScMergeFlagAttr*  pItem;
1481 
1482     SCSIZE  nIndex;
1483     SCROW   nRow;
1484     SCROW   nThisRow;
1485     sal_Bool    bFirstUse = sal_True;
1486 
1487     Search( nStartRow, nIndex );
1488     nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
1489     while ( nThisRow <= nEndRow )
1490     {
1491         pOldPattern = pData[nIndex].pPattern;
1492         if (pOldPattern != pWantedPattern)                          //! else-Zweig ?
1493         {
1494             if (nThisRow < nStartRow) nThisRow = nStartRow;
1495             nRow = pData[nIndex].nRow;
1496             SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow );
1497             pItem = (const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG );
1498 
1499             if (pItem->IsOverlapped() || pItem->HasAutoFilter())
1500             {
1501                 //  #108045# default-constructing a ScPatternAttr for DeleteArea doesn't work
1502                 //  because it would have no cell style information.
1503                 //  Instead, the document's GetDefPattern is copied. Since it is passed as
1504                 //  pWantedPattern, no special treatment of default is needed here anymore.
1505                 ScPatternAttr*  pNewPattern = new ScPatternAttr( *pWantedPattern );
1506                 SfxItemSet*     pSet = &pNewPattern->GetItemSet();
1507                 pSet->Put( *pItem );
1508                 SetPatternArea( nThisRow, nAttrRow, pNewPattern, sal_True );
1509                 delete pNewPattern;
1510             }
1511             else
1512             {
1513                 if ( !bDefault )
1514                 {
1515                     if (bFirstUse)
1516                         bFirstUse = sal_False;
1517                     else
1518                         pDocument->GetPool()->Put( *pWantedPattern );       // im Pool ist es schon!
1519                 }
1520                 SetPatternArea( nThisRow, nAttrRow, pWantedPattern );
1521             }
1522 
1523             Search( nThisRow, nIndex );                 // Daten wurden veraendert !!!
1524         }
1525 
1526         ++nIndex;
1527         nThisRow = pData[nIndex-1].nRow+1;
1528     }
1529 }
1530 
1531 
1532 sal_Bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags )
1533 {
1534     const ScPatternAttr* pOldPattern;
1535 
1536     sal_Int16   nOldValue;
1537     SCSIZE  nIndex;
1538     SCROW   nRow;
1539     SCROW   nThisRow;
1540     sal_Bool    bChanged = sal_False;
1541 
1542     Search( nStartRow, nIndex );
1543     nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
1544     if (nThisRow < nStartRow) nThisRow = nStartRow;
1545 
1546     while ( nThisRow <= nEndRow )
1547     {
1548         pOldPattern = pData[nIndex].pPattern;
1549         nOldValue = ((const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue();
1550         if ( (nOldValue | nFlags) != nOldValue )
1551         {
1552             nRow = pData[nIndex].nRow;
1553             SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow );
1554             ScPatternAttr aNewPattern(*pOldPattern);
1555             aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) );
1556             SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
1557             Search( nThisRow, nIndex );                                 // Daten wurden veraendert !!!
1558             bChanged = sal_True;
1559         }
1560 
1561         ++nIndex;
1562         nThisRow = pData[nIndex-1].nRow+1;
1563     }
1564 
1565     return bChanged;
1566 }
1567 
1568 
1569 sal_Bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags )
1570 {
1571     const ScPatternAttr* pOldPattern;
1572 
1573     sal_Int16   nOldValue;
1574     SCSIZE  nIndex;
1575     SCROW   nRow;
1576     SCROW   nThisRow;
1577     sal_Bool    bChanged = sal_False;
1578 
1579     Search( nStartRow, nIndex );
1580     nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
1581     if (nThisRow < nStartRow) nThisRow = nStartRow;
1582 
1583     while ( nThisRow <= nEndRow )
1584     {
1585         pOldPattern = pData[nIndex].pPattern;
1586         nOldValue = ((const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue();
1587         if ( (nOldValue & ~nFlags) != nOldValue )
1588         {
1589             nRow = pData[nIndex].nRow;
1590             SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow );
1591             ScPatternAttr aNewPattern(*pOldPattern);
1592             aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) );
1593             SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
1594             Search( nThisRow, nIndex );                                 // Daten wurden veraendert !!!
1595             bChanged = sal_True;
1596         }
1597 
1598         ++nIndex;
1599         nThisRow = pData[nIndex-1].nRow+1;
1600     }
1601 
1602     return bChanged;
1603 }
1604 
1605 
1606 void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
1607 {
1608     const ScPatternAttr* pOldPattern;
1609 
1610     SCSIZE  nIndex;
1611     SCROW   nRow;
1612     SCROW   nThisRow;
1613 
1614     Search( nStartRow, nIndex );
1615     nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
1616     if (nThisRow < nStartRow) nThisRow = nStartRow;
1617 
1618     while ( nThisRow <= nEndRow )
1619     {
1620         pOldPattern = pData[nIndex].pPattern;
1621         if ( pOldPattern->HasItemsSet( pWhich ) )
1622         {
1623             ScPatternAttr aNewPattern(*pOldPattern);
1624             aNewPattern.ClearItems( pWhich );
1625 
1626             nRow = pData[nIndex].nRow;
1627             SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow );
1628             SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
1629             Search( nThisRow, nIndex );                                 // Daten wurden veraendert !!!
1630         }
1631 
1632         ++nIndex;
1633         nThisRow = pData[nIndex-1].nRow+1;
1634     }
1635 }
1636 
1637 
1638 void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, sal_Bool bIncrement )
1639 {
1640     SCSIZE nIndex;
1641     Search( nStartRow, nIndex );
1642     SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
1643     if (nThisStart < nStartRow) nThisStart = nStartRow;
1644 
1645     while ( nThisStart <= nEndRow )
1646     {
1647         const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
1648         const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
1649         const SfxPoolItem* pItem;
1650 
1651         sal_Bool bNeedJust = ( rOldSet.GetItemState( ATTR_HOR_JUSTIFY, sal_False, &pItem ) != SFX_ITEM_SET
1652                         || ((const SvxHorJustifyItem*)pItem)->GetValue() != SVX_HOR_JUSTIFY_LEFT );
1653         sal_uInt16 nOldValue = ((const SfxUInt16Item&)rOldSet.Get( ATTR_INDENT )).GetValue();
1654         sal_uInt16 nNewValue = nOldValue;
1655         if ( bIncrement )
1656         {
1657             if ( nNewValue < SC_MAX_INDENT )
1658             {
1659                 nNewValue += SC_INDENT_STEP;
1660                 if ( nNewValue > SC_MAX_INDENT ) nNewValue = SC_MAX_INDENT;
1661             }
1662         }
1663         else
1664         {
1665             if ( nNewValue > 0 )
1666             {
1667                 if ( nNewValue > SC_INDENT_STEP )
1668                     nNewValue -= SC_INDENT_STEP;
1669                 else
1670                     nNewValue = 0;
1671             }
1672         }
1673 
1674         if ( bNeedJust || nNewValue != nOldValue )
1675         {
1676             SCROW nThisEnd = pData[nIndex].nRow;
1677             SCROW nAttrRow = Min( nThisEnd, nEndRow );
1678             ScPatternAttr aNewPattern(*pOldPattern);
1679             aNewPattern.GetItemSet().Put( SfxUInt16Item( ATTR_INDENT, nNewValue ) );
1680             if ( bNeedJust )
1681                 aNewPattern.GetItemSet().Put(
1682                                 SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) );
1683             SetPatternArea( nThisStart, nAttrRow, &aNewPattern, sal_True );
1684 
1685             nThisStart = nThisEnd + 1;
1686             Search( nThisStart, nIndex );               // Daten wurden veraendert !!!
1687         }
1688         else
1689         {
1690             nThisStart = pData[nIndex].nRow + 1;        // weiterzaehlen...
1691             ++nIndex;
1692         }
1693     }
1694 }
1695 
1696 
1697 SCsROW ScAttrArray::GetNextUnprotected( SCsROW nRow, sal_Bool bUp ) const
1698 {
1699     long nRet = nRow;
1700     if (VALIDROW(nRow))
1701     {
1702         SCSIZE nIndex;
1703         Search(nRow, nIndex);
1704         while (((const ScProtectionAttr&)pData[nIndex].pPattern->
1705                 GetItem(ATTR_PROTECTION)).GetProtection())
1706         {
1707             if (bUp)
1708             {
1709                 if (nIndex==0)
1710                     return -1;                  // nichts gefunden
1711                 --nIndex;
1712                 nRet = pData[nIndex].nRow;
1713             }
1714             else
1715             {
1716                 nRet = pData[nIndex].nRow+1;
1717                 ++nIndex;
1718                 if (nIndex>=nCount)
1719                     return MAXROW+1;            // nichts gefunden
1720             }
1721         }
1722     }
1723     return nRet;
1724 }
1725 
1726 void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
1727 {
1728     SCROW nStart = 0;
1729     SCSIZE nPos = 0;
1730     while (nPos < nCount)
1731     {
1732         SCROW nEnd = pData[nPos].nRow;
1733         if (pData[nPos].pPattern->GetStyleSheet() == pStyleSheet)
1734         {
1735 //          for (SCROW nRow = nStart; nRow <= nEnd; nRow++)
1736 //              pUsed[nRow] = sal_True;
1737 
1738             rUsedRows.setTrue(nStart, nEnd);
1739 
1740             if (bReset)
1741             {
1742                 ScPatternAttr* pNewPattern = new ScPatternAttr(*pData[nPos].pPattern);
1743                 pDocument->GetPool()->Remove(*pData[nPos].pPattern);
1744                 pNewPattern->SetStyleSheet( (ScStyleSheet*)
1745                     pDocument->GetStyleSheetPool()->
1746                         Find( ScGlobal::GetRscString(STR_STYLENAME_STANDARD),
1747                               SFX_STYLE_FAMILY_PARA,
1748                               SFXSTYLEBIT_AUTO | SCSTYLEBIT_STANDARD ) );
1749                 pData[nPos].pPattern = (const ScPatternAttr*)
1750                                             &pDocument->GetPool()->Put(*pNewPattern);
1751                 delete pNewPattern;
1752 
1753                 if (Concat(nPos))
1754                 {
1755                     Search(nStart, nPos);
1756                     --nPos;                         // wegen ++ am Ende
1757                 }
1758             }
1759         }
1760         nStart = nEnd + 1;
1761         ++nPos;
1762     }
1763 }
1764 
1765 
1766 sal_Bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle,
1767         sal_Bool bGatherAllStyles ) const
1768 {
1769     sal_Bool    bIsUsed = sal_False;
1770     SCSIZE  nPos    = 0;
1771 
1772     while ( nPos < nCount )
1773     {
1774         const ScStyleSheet* pStyle = pData[nPos].pPattern->GetStyleSheet();
1775         if ( pStyle )
1776         {
1777             pStyle->SetUsage( ScStyleSheet::USED );
1778             if ( pStyle == &rStyle )
1779             {
1780                 if ( !bGatherAllStyles )
1781                     return sal_True;
1782                 bIsUsed = sal_True;
1783             }
1784         }
1785         nPos++;
1786     }
1787 
1788     return bIsUsed;
1789 }
1790 
1791 
1792 sal_Bool ScAttrArray::IsEmpty() const
1793 {
1794     if (nCount == 1)
1795     {
1796         if ( pData[0].pPattern != pDocument->GetDefPattern() )
1797             return sal_False;
1798         else
1799             return sal_True;
1800     }
1801     else
1802         return sal_False;
1803 }
1804 
1805 
1806 //UNUSED2008-05  SCROW ScAttrArray::GetFirstEntryPos() const
1807 //UNUSED2008-05  {
1808 //UNUSED2008-05      DBG_ASSERT( nCount, "nCount = 0" );
1809 //UNUSED2008-05
1810 //UNUSED2008-05      if ( pData[0].pPattern != pDocument->GetDefPattern() )
1811 //UNUSED2008-05          return 0;
1812 //UNUSED2008-05      else
1813 //UNUSED2008-05      {
1814 //UNUSED2008-05          if (nCount==1)
1815 //UNUSED2008-05              return 0;                               // leer
1816 //UNUSED2008-05          else
1817 //UNUSED2008-05              return pData[0].nRow + 1;
1818 //UNUSED2008-05      }
1819 //UNUSED2008-05  }
1820 //UNUSED2008-05
1821 //UNUSED2008-05
1822 //UNUSED2008-05  SCROW ScAttrArray::GetLastEntryPos( sal_Bool bIncludeBottom ) const
1823 //UNUSED2008-05  {
1824 //UNUSED2008-05      DBG_ASSERT( nCount, "nCount == 0" );
1825 //UNUSED2008-05
1826 //UNUSED2008-05      if (bIncludeBottom)
1827 //UNUSED2008-05          bIncludeBottom = ( pData[nCount-1].pPattern != pDocument->GetDefPattern() );
1828 //UNUSED2008-05
1829 //UNUSED2008-05      if (bIncludeBottom)
1830 //UNUSED2008-05          return MAXROW;
1831 //UNUSED2008-05      else
1832 //UNUSED2008-05      {
1833 //UNUSED2008-05          if (nCount<=1)
1834 //UNUSED2008-05              return 0;                               // leer
1835 //UNUSED2008-05          else
1836 //UNUSED2008-05              return pData[nCount-2].nRow;
1837 //UNUSED2008-05      }
1838 //UNUSED2008-05  }
1839 
1840 
1841 sal_Bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
1842 {
1843     DBG_ASSERT( nCount, "nCount == 0" );
1844 
1845     sal_Bool bFound = sal_False;
1846     SCSIZE nStart = 0;
1847 
1848     // Skip first entry if more than 1 row.
1849     // Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr.
1850 
1851     SCSIZE nVisStart = 1;
1852     while ( nVisStart < nCount && pData[nVisStart].pPattern->IsVisibleEqual(*pData[nVisStart-1].pPattern) )
1853         ++nVisStart;
1854     if ( nVisStart >= nCount || pData[nVisStart-1].nRow > 0 )   // more than 1 row?
1855         nStart = nVisStart;
1856 
1857     while ( nStart < nCount && !bFound )
1858     {
1859         if ( pData[nStart].pPattern->IsVisible() )
1860         {
1861             rFirstRow = nStart ? ( pData[nStart-1].nRow + 1 ) : 0;
1862             bFound = sal_True;
1863         }
1864         else
1865             ++nStart;
1866     }
1867 
1868     return bFound;
1869 }
1870 
1871 // size (rows) of a range of attributes after cell content where the search is stopped
1872 // (more than a default page size, 2*42 because it's as good as any number)
1873 
1874 const SCROW SC_VISATTR_STOP = 84;
1875 
1876 sal_Bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const
1877 {
1878     //  #i30830# changed behavior:
1879     //  ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows
1880     //  below the last content cell
1881 
1882     if ( nLastData == MAXROW )
1883     {
1884         rLastRow = MAXROW;      // can't look for attributes below MAXROW
1885         return sal_True;
1886     }
1887 
1888     sal_Bool bFound = sal_False;
1889 
1890     //  loop backwards from the end instead of using Search, assuming that
1891     //  there usually aren't many attributes below the last cell
1892 
1893     SCSIZE nPos = nCount;
1894     while ( nPos > 0 && pData[nPos-1].nRow > nLastData )
1895     {
1896         SCSIZE nEndPos = nPos - 1;
1897         SCSIZE nStartPos = nEndPos;         // find range of visually equal formats
1898         while ( nStartPos > 0 &&
1899                 pData[nStartPos-1].nRow > nLastData &&
1900                 pData[nStartPos-1].pPattern->IsVisibleEqual(*pData[nStartPos].pPattern) )
1901             --nStartPos;
1902 
1903         SCROW nAttrStartRow = ( nStartPos > 0 ) ? ( pData[nStartPos-1].nRow + 1 ) : 0;
1904         if ( nAttrStartRow <= nLastData )
1905             nAttrStartRow = nLastData + 1;
1906         SCROW nAttrSize = pData[nEndPos].nRow + 1 - nAttrStartRow;
1907         if ( nAttrSize >= SC_VISATTR_STOP )
1908         {
1909             bFound = sal_False;        // ignore this range and below
1910         }
1911         else if ( !bFound && pData[nEndPos].pPattern->IsVisible() )
1912         {
1913             rLastRow = pData[nEndPos].nRow;
1914             bFound = sal_True;
1915         }
1916 
1917         nPos = nStartPos;           // look further from the top of the range
1918     }
1919 
1920     return bFound;
1921 }
1922 
1923 sal_Bool ScAttrArray::GetLastAttr( SCROW& rLastRow, SCROW nLastData ) const
1924 {
1925     if ( nLastData == MAXROW )
1926     {
1927         rLastRow = MAXROW;
1928         return sal_True;
1929     }
1930     sal_Bool bFound = sal_False;
1931     SCSIZE nEndPos = nCount - 1;
1932     SCSIZE nStartPos = nEndPos;
1933     while ( nStartPos > 0 && pData[nStartPos-1].nRow > nLastData &&
1934             !pData[nStartPos].pPattern->IsVisible() )
1935         --nStartPos;
1936 
1937     if(nStartPos >= 0 && pData[nStartPos].nRow > nLastData)
1938     {
1939         bFound = sal_True;
1940         rLastRow = pData[nStartPos].nRow;
1941     }
1942     else
1943         rLastRow = nLastData;
1944     return bFound;
1945 }
1946 
1947 
1948 sal_Bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
1949 {
1950     SCSIZE nIndex;
1951     Search( nStartRow, nIndex );
1952     SCROW nThisStart = nStartRow;
1953     sal_Bool bFound = sal_False;
1954     while ( nIndex < nCount && nThisStart <= nEndRow && !bFound )
1955     {
1956         if ( pData[nIndex].pPattern->IsVisible() )
1957             bFound = sal_True;
1958 
1959         nThisStart = pData[nIndex].nRow + 1;
1960         ++nIndex;
1961     }
1962 
1963     return bFound;
1964 }
1965 
1966 
1967 sal_Bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
1968                                     SCROW nStartRow, SCROW nEndRow ) const
1969 {
1970     sal_Bool bEqual = sal_True;
1971     SCSIZE nThisPos = 0;
1972     SCSIZE nOtherPos = 0;
1973     if ( nStartRow > 0 )
1974     {
1975         Search( nStartRow, nThisPos );
1976         rOther.Search( nStartRow, nOtherPos );
1977     }
1978 
1979     while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual )
1980     {
1981         SCROW nThisRow = pData[nThisPos].nRow;
1982         SCROW nOtherRow = rOther.pData[nOtherPos].nRow;
1983         const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern;
1984         const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern;
1985         bEqual = ( pThisPattern == pOtherPattern ||
1986                     pThisPattern->IsVisibleEqual(*pOtherPattern) );
1987 
1988         if ( nThisRow >= nOtherRow )
1989         {
1990             if ( nOtherRow >= nEndRow ) break;
1991             ++nOtherPos;
1992         }
1993         if ( nThisRow <= nOtherRow )
1994         {
1995             if ( nThisRow >= nEndRow ) break;
1996             ++nThisPos;
1997         }
1998     }
1999 
2000     return bEqual;
2001 }
2002 
2003 
2004 sal_Bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
2005 {
2006     //! mit IsVisibleEqual zusammenfassen?
2007 
2008     sal_Bool bEqual = sal_True;
2009     SCSIZE nThisPos = 0;
2010     SCSIZE nOtherPos = 0;
2011     if ( nStartRow > 0 )
2012     {
2013         Search( nStartRow, nThisPos );
2014         rOther.Search( nStartRow, nOtherPos );
2015     }
2016 
2017     while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual )
2018     {
2019         SCROW nThisRow = pData[nThisPos].nRow;
2020         SCROW nOtherRow = rOther.pData[nOtherPos].nRow;
2021         const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern;
2022         const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern;
2023         bEqual = ( pThisPattern == pOtherPattern );
2024 
2025         if ( nThisRow >= nOtherRow )
2026         {
2027             if ( nOtherRow >= nEndRow ) break;
2028             ++nOtherPos;
2029         }
2030         if ( nThisRow <= nOtherRow )
2031         {
2032             if ( nThisRow >= nEndRow ) break;
2033             ++nThisPos;
2034         }
2035     }
2036 
2037     return bEqual;
2038 }
2039 
2040 
2041 sal_Bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
2042 {
2043     //  horizontal zusammengefasste duerfen nicht herausgeschoben werden
2044     //  (ob die ganze Zusammenfassung betroffen ist, ist hier nicht zu erkennen)
2045 
2046     sal_Bool bTest = sal_True;
2047     if (!IsEmpty())
2048     {
2049         SCSIZE nIndex = 0;
2050         if ( nStartRow > 0 )
2051             Search( nStartRow, nIndex );
2052 
2053         for ( ; nIndex < nCount; nIndex++ )
2054         {
2055             if ( ((const ScMergeFlagAttr&)pData[nIndex].pPattern->
2056                         GetItem(ATTR_MERGE_FLAG)).IsHorOverlapped() )
2057             {
2058                 bTest = sal_False;                      // darf nicht herausgeschoben werden
2059                 break;
2060             }
2061             if ( pData[nIndex].nRow >= nEndRow )    // Ende des Bereichs
2062                 break;
2063         }
2064     }
2065     return bTest;
2066 }
2067 
2068 
2069 sal_Bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
2070 {
2071     //  wenn die erste herausgeschobene Zeile vertikal ueberlappt ist,
2072     //  wuerde eine kaputte Zusammenfassung uebrigbleiben
2073 
2074     if (pData)
2075     {
2076         //  MAXROW + 1 - nSize  = erste herausgeschobene Zeile
2077 
2078         SCSIZE nFirstLost = nCount-1;
2079         while ( nFirstLost && pData[nFirstLost-1].nRow >= sal::static_int_cast<SCROW>(MAXROW + 1 - nSize) )
2080             --nFirstLost;
2081 
2082         if ( ((const ScMergeFlagAttr&)pData[nFirstLost].pPattern->
2083                             GetItem(ATTR_MERGE_FLAG)).IsVerOverlapped() )
2084             return sal_False;
2085     }
2086 
2087     return sal_True;
2088 }
2089 
2090 
2091 void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
2092 {
2093     if (!pData)
2094         return;
2095 
2096     SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0;      // Vorgaenger erweitern
2097     SCSIZE nIndex;
2098     Search( nSearch, nIndex );
2099 
2100     //  ein gesetztes ScMergeAttr darf nicht ausgedehnt werden
2101     //  (darum hinterher wieder loeschen)
2102 
2103     sal_Bool bDoMerge = ((const ScMergeAttr&) pData[nIndex].pPattern->GetItem(ATTR_MERGE)).IsMerged();
2104 
2105     SCSIZE nRemove = 0;
2106     SCSIZE i;
2107     for (i = nIndex; i < nCount-1; i++)
2108     {
2109         SCROW nNew = pData[i].nRow + nSize;
2110         if ( nNew >= MAXROW )                   // Ende erreicht ?
2111         {
2112             nNew = MAXROW;
2113             if (!nRemove)
2114                 nRemove = i+1;                  // folgende loeschen
2115         }
2116         pData[i].nRow = nNew;
2117     }
2118 
2119     //  muessen Eintraege am Ende geloescht werden?
2120 
2121     if (nRemove && nRemove < nCount)
2122         DeleteRange( nRemove, nCount-1 );
2123 
2124     if (bDoMerge)           // ausgedehntes ScMergeAttr wieder reparieren
2125     {
2126             //! ApplyAttr fuer Bereiche !!!
2127 
2128         const SfxPoolItem& rDef = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE );
2129         for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
2130             pDocument->ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
2131 
2132         //  im eingefuegten Bereich ist nichts zusammengefasst
2133     }
2134 
2135     // Don't duplicate the merge flags in the inserted row.
2136     // #i108488# SC_MF_SCENARIO has to be allowed.
2137     RemoveFlags( nStartRow, nStartRow+nSize-1, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO | SC_MF_BUTTON );
2138 }
2139 
2140 
2141 void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
2142 {
2143     if (pData)
2144     {
2145         sal_Bool bFirst=sal_True;
2146         SCSIZE nStartIndex = 0;
2147         SCSIZE nEndIndex = 0;
2148         SCSIZE i;
2149 
2150         for ( i = 0; i < nCount-1; i++)
2151             if (pData[i].nRow >= nStartRow && pData[i].nRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
2152             {
2153                 if (bFirst)
2154                 {
2155                     nStartIndex = i;
2156                     bFirst = sal_False;
2157                 }
2158                 nEndIndex = i;
2159             }
2160         if (!bFirst)
2161         {
2162             SCROW nStart;
2163             if (nStartIndex==0)
2164                 nStart = 0;
2165             else
2166                 nStart = pData[nStartIndex-1].nRow + 1;
2167 
2168             if (nStart < nStartRow)
2169             {
2170                 pData[nStartIndex].nRow = nStartRow - 1;
2171                 ++nStartIndex;
2172             }
2173             if (nEndIndex >= nStartIndex)
2174             {
2175                 DeleteRange( nStartIndex, nEndIndex );
2176                 if (nStartIndex > 0)
2177                     if ( pData[nStartIndex-1].pPattern == pData[nStartIndex].pPattern )
2178                         DeleteRange( nStartIndex-1, nStartIndex-1 );
2179             }
2180         }
2181         for (i = 0; i < nCount-1; i++)
2182             if (pData[i].nRow >= nStartRow)
2183                 pData[i].nRow -= nSize;
2184 
2185 //      unten nicht Default-Pattern nachschieben, um Druckbereiche erkennen zu koennen
2186 //      stattdessen nur Merge-Flags loeschen
2187 
2188         RemoveFlags( MAXROW-nSize+1, MAXROW, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO );
2189     }
2190 }
2191 
2192 
2193 void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
2194 {
2195     ScDocumentPool* pDocPool = pDocument->GetPool();
2196     for (SCSIZE i = nStartIndex; i <= nEndIndex; i++)
2197         pDocPool->Remove(*pData[i].pPattern);
2198 
2199     memmove( &pData[nStartIndex], &pData[nEndIndex + 1], (nCount - nEndIndex - 1) * sizeof(ScAttrEntry) );
2200     nCount -= nEndIndex-nStartIndex+1;
2201 }
2202 
2203 
2204 void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
2205 {
2206     RemoveAreaMerge( nStartRow, nEndRow );          // von zusammengefassten auch die Flags loeschen
2207 
2208     if ( !HasAttrib( nStartRow, nEndRow, HASATTR_OVERLAPPED | HASATTR_AUTOFILTER) )
2209         SetPatternArea( nStartRow, nEndRow, pDocument->GetDefPattern() );
2210     else
2211         DeleteAreaSafe( nStartRow, nEndRow );       // Merge-Flags stehenlassen
2212 }
2213 
2214 
2215 void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
2216 {
2217     const ScPatternAttr* pDefPattern = pDocument->GetDefPattern();
2218     const ScPatternAttr* pOldPattern;
2219 
2220     SCSIZE  nIndex;
2221     SCROW   nRow;
2222     SCROW   nThisRow;
2223 
2224     Search( nStartRow, nIndex );
2225     nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
2226     if (nThisRow < nStartRow) nThisRow = nStartRow;
2227 
2228     while ( nThisRow <= nEndRow )
2229     {
2230         pOldPattern = pData[nIndex].pPattern;
2231 
2232         if ( pOldPattern->GetItemSet().Count() )        // harte Attribute ?
2233         {
2234             nRow = pData[nIndex].nRow;
2235             SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow );
2236 
2237             ScPatternAttr aNewPattern(*pOldPattern);
2238             SfxItemSet& rSet = aNewPattern.GetItemSet();
2239             for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
2240                 if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
2241                     rSet.ClearItem(nId);
2242 
2243             if ( aNewPattern == *pDefPattern )
2244                 SetPatternArea( nThisRow, nAttrRow, pDefPattern, sal_False );
2245             else
2246                 SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
2247 
2248             Search( nThisRow, nIndex );                                 // Daten wurden veraendert !!!
2249         }
2250 
2251         ++nIndex;
2252         nThisRow = pData[nIndex-1].nRow+1;
2253     }
2254 }
2255 
2256         // Verschieben innerhalb eines Dokuments
2257 
2258 void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
2259 {
2260     SCROW nStart = nStartRow;
2261     for (SCSIZE i = 0; i < nCount; i++)
2262     {
2263         if ((pData[i].nRow >= nStartRow) && ((i==0) ? sal_True : pData[i-1].nRow < nEndRow))
2264         {
2265             //  Kopieren (bPutToPool=sal_True)
2266             rAttrArray.SetPatternArea( nStart, Min( (SCROW)pData[i].nRow, (SCROW)nEndRow ),
2267                                         pData[i].pPattern, sal_True );
2268         }
2269         nStart = Max( (SCROW)nStart, (SCROW)(pData[i].nRow + 1) );
2270     }
2271     DeleteArea(nStartRow, nEndRow);
2272 }
2273 
2274 
2275         // Kopieren zwischen Dokumenten (Clipboard)
2276 
2277 void ScAttrArray::CopyArea( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray,
2278                                 sal_Int16 nStripFlags )
2279 {
2280     nStartRow -= nDy;       // Source
2281     nEndRow -= nDy;
2282 
2283     SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0);
2284     SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW);
2285 
2286     ScDocumentPool* pSourceDocPool = pDocument->GetPool();
2287     ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool();
2288     sal_Bool bSamePool = (pSourceDocPool==pDestDocPool);
2289 
2290     for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++)
2291     {
2292         if (pData[i].nRow >= nStartRow)
2293         {
2294             const ScPatternAttr* pOldPattern = pData[i].pPattern;
2295             const ScPatternAttr* pNewPattern;
2296 
2297             if (IsDefaultItem( pOldPattern ))
2298             {
2299                 //  am Default muss nichts veraendert werden
2300 
2301                 pNewPattern = (const ScPatternAttr*)
2302                                 &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
2303             }
2304             else if ( nStripFlags )
2305             {
2306                 ScPatternAttr* pTmpPattern = new ScPatternAttr( *pOldPattern );
2307                 sal_Int16 nNewFlags = 0;
2308                 if ( nStripFlags != SC_MF_ALL )
2309                     nNewFlags = ((const ScMergeFlagAttr&)pTmpPattern->GetItem(ATTR_MERGE_FLAG)).
2310                                 GetValue() & ~nStripFlags;
2311 
2312                 if ( nNewFlags )
2313                     pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
2314                 else
2315                     pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG );
2316 
2317                 if (bSamePool)
2318                     pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pTmpPattern);
2319                 else
2320                     pNewPattern = pTmpPattern->PutInPool( rAttrArray.pDocument, pDocument );
2321                 delete pTmpPattern;
2322             }
2323             else
2324             {
2325                 if (bSamePool)
2326                     pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern);
2327                 else
2328                     pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument );
2329             }
2330 
2331             rAttrArray.SetPatternArea(nDestStart,
2332                             Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern);
2333         }
2334 
2335         // when pasting from clipboard and skipping filtered rows, the adjusted end position
2336         // can be negative
2337         nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1));
2338     }
2339 }
2340 
2341         // Flags stehenlassen
2342         //! mit CopyArea zusammenfassen !!!
2343 
2344 void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray )
2345 {
2346     nStartRow -= nDy;       // Source
2347     nEndRow -= nDy;
2348 
2349     SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0);
2350     SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW);
2351 
2352     if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HASATTR_OVERLAPPED ) )
2353     {
2354         CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
2355         return;
2356     }
2357 
2358     ScDocumentPool* pSourceDocPool = pDocument->GetPool();
2359     ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool();
2360     sal_Bool bSamePool = (pSourceDocPool==pDestDocPool);
2361 
2362     for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++)
2363     {
2364         if (pData[i].nRow >= nStartRow)
2365         {
2366             const ScPatternAttr* pOldPattern = pData[i].pPattern;
2367             const ScPatternAttr* pNewPattern;
2368 
2369             if (bSamePool)
2370                 pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern);
2371             else
2372                 pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument );
2373 
2374             rAttrArray.SetPatternAreaSafe(nDestStart,
2375                             Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern, sal_False);
2376         }
2377 
2378         // when pasting from clipboard and skipping filtered rows, the adjusted end position
2379         // can be negative
2380         nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1));
2381     }
2382 }
2383 
2384 
2385 SCsROW ScAttrArray::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle,
2386                                     sal_Bool bUp, ScMarkArray* pMarkArray )
2387 {
2388     sal_Bool bFound = sal_False;
2389 
2390     if (pMarkArray)
2391     {
2392         nRow = pMarkArray->GetNextMarked( nRow, bUp );
2393         if (!VALIDROW(nRow))
2394             return nRow;
2395     }
2396 
2397     SCSIZE nIndex;
2398     Search(nRow, nIndex);
2399     const ScPatternAttr* pPattern = pData[nIndex].pPattern;
2400 
2401     while (nIndex < nCount && !bFound)
2402     {
2403         if (pPattern->GetStyleSheet() == pSearchStyle)
2404         {
2405             if (pMarkArray)
2406             {
2407                 nRow = pMarkArray->GetNextMarked( nRow, bUp );
2408                 SCROW nStart = nIndex ? pData[nIndex-1].nRow+1 : 0;
2409                 if (nRow >= nStart && nRow <= pData[nIndex].nRow)
2410                     bFound = sal_True;
2411             }
2412             else
2413                 bFound = sal_True;
2414         }
2415 
2416         if (!bFound)
2417         {
2418             if (bUp)
2419             {
2420                 if (nIndex==0)
2421                 {
2422                     nIndex = nCount;
2423                     nRow = -1;
2424                 }
2425                 else
2426                 {
2427                     --nIndex;
2428                     nRow = pData[nIndex].nRow;
2429                     pPattern = pData[nIndex].pPattern;
2430                 }
2431             }
2432             else
2433             {
2434                 nRow = pData[nIndex].nRow+1;
2435                 ++nIndex;
2436                 if (nIndex<nCount)
2437                     pPattern = pData[nIndex].pPattern;
2438             }
2439         }
2440     }
2441 
2442     DBG_ASSERT( bFound || !ValidRow(nRow), "interner Fehler in ScAttrArray::SearchStyle" );
2443 
2444     return nRow;
2445 }
2446 
2447 
2448 sal_Bool ScAttrArray::SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow,
2449                         const ScStyleSheet* pSearchStyle, sal_Bool bUp, ScMarkArray* pMarkArray )
2450 {
2451     SCsROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
2452     if (VALIDROW(nStartRow))
2453     {
2454         SCSIZE nIndex;
2455         Search(nStartRow,nIndex);
2456 
2457         rRow = nStartRow;
2458         if (bUp)
2459         {
2460             if (nIndex>0)
2461                 rEndRow = pData[nIndex-1].nRow + 1;
2462             else
2463                 rEndRow = 0;
2464             if (pMarkArray)
2465             {
2466                 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, sal_True );
2467                 if (nMarkEnd>rEndRow)
2468                     rEndRow = nMarkEnd;
2469             }
2470         }
2471         else
2472         {
2473             rEndRow = pData[nIndex].nRow;
2474             if (pMarkArray)
2475             {
2476                 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, sal_False );
2477                 if (nMarkEnd<rEndRow)
2478                     rEndRow = nMarkEnd;
2479             }
2480         }
2481 
2482         return sal_True;
2483     }
2484     else
2485         return sal_False;
2486 }
2487 
2488 //------------------------------------------------------------------------
2489 //
2490 //                          Laden / Speichern
2491 //
2492 
2493 
2494 #if 0
2495 void ScAttrArray::Save( SvStream& /* rStream */ ) const
2496 {
2497 #if SC_ROWLIMIT_STREAM_ACCESS
2498 #error address types changed!
2499     ScWriteHeader aHdr( rStream, 8 );
2500 
2501     ScDocumentPool* pDocPool = pDocument->GetPool();
2502 
2503     sal_uInt16 nSaveCount = nCount;
2504     SCROW nSaveMaxRow = pDocument->GetSrcMaxRow();
2505     if ( nSaveMaxRow != MAXROW )
2506     {
2507         if ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow )
2508         {
2509             pDocument->SetLostData();           // Warnung ausgeben
2510             do
2511                 --nSaveCount;
2512             while ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow );
2513         }
2514     }
2515 
2516     rStream << nSaveCount;
2517 
2518     const SfxPoolItem* pItem;
2519     for (SCSIZE i=0; i<nSaveCount; i++)
2520     {
2521         rStream << Min( pData[i].nRow, nSaveMaxRow );
2522 
2523         const ScPatternAttr* pPattern = pData[i].pPattern;
2524         pDocPool->StoreSurrogate( rStream, pPattern );
2525 
2526         //  sal_False, weil ATTR_CONDITIONAL (noch) nicht in Vorlagen:
2527         if (pPattern->GetItemSet().GetItemState(ATTR_CONDITIONAL,sal_False,&pItem) == SFX_ITEM_SET)
2528             pDocument->SetConditionalUsed( ((const SfxUInt32Item*)pItem)->GetValue() );
2529 
2530         if (pPattern->GetItemSet().GetItemState(ATTR_VALIDDATA,sal_False,&pItem) == SFX_ITEM_SET)
2531             pDocument->SetValidationUsed( ((const SfxUInt32Item*)pItem)->GetValue() );
2532     }
2533 #endif // SC_ROWLIMIT_STREAM_ACCESS
2534 }
2535 
2536 
2537 void ScAttrArray::Load( SvStream& /* rStream */ )
2538 {
2539 #if SC_ROWLIMIT_STREAM_ACCESS
2540 #error address types changed!
2541     ScDocumentPool* pDocPool = pDocument->GetPool();
2542 
2543     ScReadHeader aHdr( rStream );
2544 
2545     sal_uInt16 nNewCount;
2546     rStream >> nNewCount;
2547     if ( nNewCount > MAXROW+1 )                     // wuerde das Array zu gross?
2548     {
2549         pDocument->SetLostData();
2550         rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
2551         return;
2552     }
2553 
2554     Reset( pDocument->GetDefPattern(), sal_False );     // loeschen
2555     pData = new ScAttrEntry[nNewCount];             // neu anlegen
2556     for (SCSIZE i=0; i<nNewCount; i++)
2557     {
2558         rStream >> pData[i].nRow;
2559 
2560         sal_uInt16 nWhich = ATTR_PATTERN;
2561         const ScPatternAttr* pNewPattern = (const ScPatternAttr*)
2562                                            pDocPool->LoadSurrogate( rStream, nWhich, ATTR_PATTERN );
2563         if (!pNewPattern)
2564         {
2565             // da is was schiefgelaufen
2566             DBG_ERROR("ScAttrArray::Load: Surrogat nicht im Pool");
2567             pNewPattern = pDocument->GetDefPattern();
2568         }
2569         ScDocumentPool::CheckRef( *pNewPattern );
2570         pData[i].pPattern = pNewPattern;
2571 
2572         // LoadSurrogate erhoeht auch die Ref
2573     }
2574     nCount = nLimit = nNewCount;
2575 
2576     if ( nCount > 1 && pData[nCount-2].nRow >= MAXROW ) // faengt ein Attribut hinter MAXROW an?
2577     {
2578         pDocument->SetLostData();
2579         rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
2580         return;
2581     }
2582 
2583     if ( pDocument->GetSrcMaxRow() != MAXROW )          // Ende anpassen?
2584     {
2585         //  Ende immer auf MAXROW umsetzen (nur auf 32 Bit)
2586 
2587         DBG_ASSERT( pData[nCount-1].nRow == pDocument->GetSrcMaxRow(), "Attribut-Ende ?!?" );
2588         pData[nCount-1].nRow = MAXROW;
2589     }
2590 #endif // SC_ROWLIMIT_STREAM_ACCESS
2591 }
2592 #endif
2593 
2594 
2595 //UNUSED2008-05  void ScAttrArray::ConvertFontsAfterLoad()
2596 //UNUSED2008-05  {
2597 //UNUSED2008-05      ScFontToSubsFontConverter_AutoPtr xFontConverter;
2598 //UNUSED2008-05      const sal_uLong nFlags = FONTTOSUBSFONT_IMPORT | FONTTOSUBSFONT_ONLYOLDSOSYMBOLFONTS;
2599 //UNUSED2008-05      SCSIZE   nIndex = 0;
2600 //UNUSED2008-05      SCROW  nThisRow = 0;
2601 //UNUSED2008-05
2602 //UNUSED2008-05      while ( nThisRow <= MAXROW )
2603 //UNUSED2008-05      {
2604 //UNUSED2008-05          const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
2605 //UNUSED2008-05          const SfxPoolItem* pItem;
2606 //UNUSED2008-05          if( pOldPattern->GetItemSet().GetItemState( ATTR_FONT, sal_False, &pItem ) == SFX_ITEM_SET )
2607 //UNUSED2008-05          {
2608 //UNUSED2008-05              const SvxFontItem* pFontItem = (const SvxFontItem*) pItem;
2609 //UNUSED2008-05              const String& rOldName = pFontItem->GetFamilyName();
2610 //UNUSED2008-05              xFontConverter = CreateFontToSubsFontConverter( rOldName, nFlags );
2611 //UNUSED2008-05              if ( xFontConverter )
2612 //UNUSED2008-05              {
2613 //UNUSED2008-05                  String aNewName( GetFontToSubsFontName( xFontConverter ) );
2614 //UNUSED2008-05                  if ( aNewName != rOldName )
2615 //UNUSED2008-05                  {
2616 //UNUSED2008-05                      SCROW nAttrRow = pData[nIndex].nRow;
2617 //UNUSED2008-05                      SvxFontItem aNewItem( pFontItem->GetFamily(), aNewName,
2618 //UNUSED2008-05                          pFontItem->GetStyleName(), pFontItem->GetPitch(),
2619 //UNUSED2008-05                          RTL_TEXTENCODING_DONTKNOW, ATTR_FONT );
2620 //UNUSED2008-05                      ScPatternAttr aNewPattern( *pOldPattern );
2621 //UNUSED2008-05                      aNewPattern.GetItemSet().Put( aNewItem );
2622 //UNUSED2008-05                      SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
2623 //UNUSED2008-05                      Search( nThisRow, nIndex );     //! data changed
2624 //UNUSED2008-05                  }
2625 //UNUSED2008-05              }
2626 //UNUSED2008-05          }
2627 //UNUSED2008-05          ++nIndex;
2628 //UNUSED2008-05          nThisRow = pData[nIndex-1].nRow+1;
2629 //UNUSED2008-05      }
2630 //UNUSED2008-05  }
2631 
2632