xref: /AOO41X/main/sc/source/core/data/attarray.cxx (revision 54628ca40d27d15cc98fe861da7fff7e60c2f7d6)
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     if(nStartPos == 0 && !pData[nStartPos].pPattern->IsVisible()) // add this condition for handle only default pattern in one colume
1937         rLastRow = nLastData;
1938     else if(nStartPos >= 0 && pData[nStartPos].nRow > nLastData)
1939     {
1940         bFound = sal_True;
1941         rLastRow = pData[nStartPos].nRow;
1942     }
1943     else
1944         rLastRow = nLastData;
1945     return bFound;
1946 }
1947 
1948 
1949 sal_Bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
1950 {
1951     SCSIZE nIndex;
1952     Search( nStartRow, nIndex );
1953     SCROW nThisStart = nStartRow;
1954     sal_Bool bFound = sal_False;
1955     while ( nIndex < nCount && nThisStart <= nEndRow && !bFound )
1956     {
1957         if ( pData[nIndex].pPattern->IsVisible() )
1958             bFound = sal_True;
1959 
1960         nThisStart = pData[nIndex].nRow + 1;
1961         ++nIndex;
1962     }
1963 
1964     return bFound;
1965 }
1966 
1967 
1968 sal_Bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
1969                                     SCROW nStartRow, SCROW nEndRow ) const
1970 {
1971     sal_Bool bEqual = sal_True;
1972     SCSIZE nThisPos = 0;
1973     SCSIZE nOtherPos = 0;
1974     if ( nStartRow > 0 )
1975     {
1976         Search( nStartRow, nThisPos );
1977         rOther.Search( nStartRow, nOtherPos );
1978     }
1979 
1980     while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual )
1981     {
1982         SCROW nThisRow = pData[nThisPos].nRow;
1983         SCROW nOtherRow = rOther.pData[nOtherPos].nRow;
1984         const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern;
1985         const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern;
1986         bEqual = ( pThisPattern == pOtherPattern ||
1987                     pThisPattern->IsVisibleEqual(*pOtherPattern) );
1988 
1989         if ( nThisRow >= nOtherRow )
1990         {
1991             if ( nOtherRow >= nEndRow ) break;
1992             ++nOtherPos;
1993         }
1994         if ( nThisRow <= nOtherRow )
1995         {
1996             if ( nThisRow >= nEndRow ) break;
1997             ++nThisPos;
1998         }
1999     }
2000 
2001     return bEqual;
2002 }
2003 
2004 
2005 sal_Bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
2006 {
2007     //! mit IsVisibleEqual zusammenfassen?
2008 
2009     sal_Bool bEqual = sal_True;
2010     SCSIZE nThisPos = 0;
2011     SCSIZE nOtherPos = 0;
2012     if ( nStartRow > 0 )
2013     {
2014         Search( nStartRow, nThisPos );
2015         rOther.Search( nStartRow, nOtherPos );
2016     }
2017 
2018     while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual )
2019     {
2020         SCROW nThisRow = pData[nThisPos].nRow;
2021         SCROW nOtherRow = rOther.pData[nOtherPos].nRow;
2022         const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern;
2023         const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern;
2024         bEqual = ( pThisPattern == pOtherPattern );
2025 
2026         if ( nThisRow >= nOtherRow )
2027         {
2028             if ( nOtherRow >= nEndRow ) break;
2029             ++nOtherPos;
2030         }
2031         if ( nThisRow <= nOtherRow )
2032         {
2033             if ( nThisRow >= nEndRow ) break;
2034             ++nThisPos;
2035         }
2036     }
2037 
2038     return bEqual;
2039 }
2040 
2041 
2042 sal_Bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
2043 {
2044     //  horizontal zusammengefasste duerfen nicht herausgeschoben werden
2045     //  (ob die ganze Zusammenfassung betroffen ist, ist hier nicht zu erkennen)
2046 
2047     sal_Bool bTest = sal_True;
2048     if (!IsEmpty())
2049     {
2050         SCSIZE nIndex = 0;
2051         if ( nStartRow > 0 )
2052             Search( nStartRow, nIndex );
2053 
2054         for ( ; nIndex < nCount; nIndex++ )
2055         {
2056             if ( ((const ScMergeFlagAttr&)pData[nIndex].pPattern->
2057                         GetItem(ATTR_MERGE_FLAG)).IsHorOverlapped() )
2058             {
2059                 bTest = sal_False;                      // darf nicht herausgeschoben werden
2060                 break;
2061             }
2062             if ( pData[nIndex].nRow >= nEndRow )    // Ende des Bereichs
2063                 break;
2064         }
2065     }
2066     return bTest;
2067 }
2068 
2069 
2070 sal_Bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
2071 {
2072     //  wenn die erste herausgeschobene Zeile vertikal ueberlappt ist,
2073     //  wuerde eine kaputte Zusammenfassung uebrigbleiben
2074 
2075     if (pData)
2076     {
2077         //  MAXROW + 1 - nSize  = erste herausgeschobene Zeile
2078 
2079         SCSIZE nFirstLost = nCount-1;
2080         while ( nFirstLost && pData[nFirstLost-1].nRow >= sal::static_int_cast<SCROW>(MAXROW + 1 - nSize) )
2081             --nFirstLost;
2082 
2083         if ( ((const ScMergeFlagAttr&)pData[nFirstLost].pPattern->
2084                             GetItem(ATTR_MERGE_FLAG)).IsVerOverlapped() )
2085             return sal_False;
2086     }
2087 
2088     return sal_True;
2089 }
2090 
2091 
2092 void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
2093 {
2094     if (!pData)
2095         return;
2096 
2097     SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0;      // Vorgaenger erweitern
2098     SCSIZE nIndex;
2099     Search( nSearch, nIndex );
2100 
2101     //  ein gesetztes ScMergeAttr darf nicht ausgedehnt werden
2102     //  (darum hinterher wieder loeschen)
2103 
2104     sal_Bool bDoMerge = ((const ScMergeAttr&) pData[nIndex].pPattern->GetItem(ATTR_MERGE)).IsMerged();
2105 
2106     SCSIZE nRemove = 0;
2107     SCSIZE i;
2108     for (i = nIndex; i < nCount-1; i++)
2109     {
2110         SCROW nNew = pData[i].nRow + nSize;
2111         if ( nNew >= MAXROW )                   // Ende erreicht ?
2112         {
2113             nNew = MAXROW;
2114             if (!nRemove)
2115                 nRemove = i+1;                  // folgende loeschen
2116         }
2117         pData[i].nRow = nNew;
2118     }
2119 
2120     //  muessen Eintraege am Ende geloescht werden?
2121 
2122     if (nRemove && nRemove < nCount)
2123         DeleteRange( nRemove, nCount-1 );
2124 
2125     if (bDoMerge)           // ausgedehntes ScMergeAttr wieder reparieren
2126     {
2127             //! ApplyAttr fuer Bereiche !!!
2128 
2129         const SfxPoolItem& rDef = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE );
2130         for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
2131             pDocument->ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
2132 
2133         //  im eingefuegten Bereich ist nichts zusammengefasst
2134     }
2135 
2136     // Don't duplicate the merge flags in the inserted row.
2137     // #i108488# SC_MF_SCENARIO has to be allowed.
2138     RemoveFlags( nStartRow, nStartRow+nSize-1, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO | SC_MF_BUTTON );
2139 }
2140 
2141 
2142 void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
2143 {
2144     if (pData)
2145     {
2146         sal_Bool bFirst=sal_True;
2147         SCSIZE nStartIndex = 0;
2148         SCSIZE nEndIndex = 0;
2149         SCSIZE i;
2150 
2151         for ( i = 0; i < nCount-1; i++)
2152             if (pData[i].nRow >= nStartRow && pData[i].nRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
2153             {
2154                 if (bFirst)
2155                 {
2156                     nStartIndex = i;
2157                     bFirst = sal_False;
2158                 }
2159                 nEndIndex = i;
2160             }
2161         if (!bFirst)
2162         {
2163             SCROW nStart;
2164             if (nStartIndex==0)
2165                 nStart = 0;
2166             else
2167                 nStart = pData[nStartIndex-1].nRow + 1;
2168 
2169             if (nStart < nStartRow)
2170             {
2171                 pData[nStartIndex].nRow = nStartRow - 1;
2172                 ++nStartIndex;
2173             }
2174             if (nEndIndex >= nStartIndex)
2175             {
2176                 DeleteRange( nStartIndex, nEndIndex );
2177                 if (nStartIndex > 0)
2178                     if ( pData[nStartIndex-1].pPattern == pData[nStartIndex].pPattern )
2179                         DeleteRange( nStartIndex-1, nStartIndex-1 );
2180             }
2181         }
2182         for (i = 0; i < nCount-1; i++)
2183             if (pData[i].nRow >= nStartRow)
2184                 pData[i].nRow -= nSize;
2185 
2186 //      unten nicht Default-Pattern nachschieben, um Druckbereiche erkennen zu koennen
2187 //      stattdessen nur Merge-Flags loeschen
2188 
2189         RemoveFlags( MAXROW-nSize+1, MAXROW, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO );
2190     }
2191 }
2192 
2193 
2194 void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
2195 {
2196     ScDocumentPool* pDocPool = pDocument->GetPool();
2197     for (SCSIZE i = nStartIndex; i <= nEndIndex; i++)
2198         pDocPool->Remove(*pData[i].pPattern);
2199 
2200     memmove( &pData[nStartIndex], &pData[nEndIndex + 1], (nCount - nEndIndex - 1) * sizeof(ScAttrEntry) );
2201     nCount -= nEndIndex-nStartIndex+1;
2202 }
2203 
2204 
2205 void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
2206 {
2207     RemoveAreaMerge( nStartRow, nEndRow );          // von zusammengefassten auch die Flags loeschen
2208 
2209     if ( !HasAttrib( nStartRow, nEndRow, HASATTR_OVERLAPPED | HASATTR_AUTOFILTER) )
2210         SetPatternArea( nStartRow, nEndRow, pDocument->GetDefPattern() );
2211     else
2212         DeleteAreaSafe( nStartRow, nEndRow );       // Merge-Flags stehenlassen
2213 }
2214 
2215 
2216 void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
2217 {
2218     const ScPatternAttr* pDefPattern = pDocument->GetDefPattern();
2219     const ScPatternAttr* pOldPattern;
2220 
2221     SCSIZE  nIndex;
2222     SCROW   nRow;
2223     SCROW   nThisRow;
2224 
2225     Search( nStartRow, nIndex );
2226     nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
2227     if (nThisRow < nStartRow) nThisRow = nStartRow;
2228 
2229     while ( nThisRow <= nEndRow )
2230     {
2231         pOldPattern = pData[nIndex].pPattern;
2232 
2233         if ( pOldPattern->GetItemSet().Count() )        // harte Attribute ?
2234         {
2235             nRow = pData[nIndex].nRow;
2236             SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow );
2237 
2238             ScPatternAttr aNewPattern(*pOldPattern);
2239             SfxItemSet& rSet = aNewPattern.GetItemSet();
2240             for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
2241                 if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
2242                     rSet.ClearItem(nId);
2243 
2244             if ( aNewPattern == *pDefPattern )
2245                 SetPatternArea( nThisRow, nAttrRow, pDefPattern, sal_False );
2246             else
2247                 SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
2248 
2249             Search( nThisRow, nIndex );                                 // Daten wurden veraendert !!!
2250         }
2251 
2252         ++nIndex;
2253         nThisRow = pData[nIndex-1].nRow+1;
2254     }
2255 }
2256 
2257         // Verschieben innerhalb eines Dokuments
2258 
2259 void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
2260 {
2261     SCROW nStart = nStartRow;
2262     for (SCSIZE i = 0; i < nCount; i++)
2263     {
2264         if ((pData[i].nRow >= nStartRow) && ((i==0) ? sal_True : pData[i-1].nRow < nEndRow))
2265         {
2266             //  Kopieren (bPutToPool=sal_True)
2267             rAttrArray.SetPatternArea( nStart, Min( (SCROW)pData[i].nRow, (SCROW)nEndRow ),
2268                                         pData[i].pPattern, sal_True );
2269         }
2270         nStart = Max( (SCROW)nStart, (SCROW)(pData[i].nRow + 1) );
2271     }
2272     DeleteArea(nStartRow, nEndRow);
2273 }
2274 
2275 
2276         // Kopieren zwischen Dokumenten (Clipboard)
2277 
2278 void ScAttrArray::CopyArea( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray,
2279                                 sal_Int16 nStripFlags )
2280 {
2281     nStartRow -= nDy;       // Source
2282     nEndRow -= nDy;
2283 
2284     SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0);
2285     SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW);
2286 
2287     ScDocumentPool* pSourceDocPool = pDocument->GetPool();
2288     ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool();
2289     sal_Bool bSamePool = (pSourceDocPool==pDestDocPool);
2290 
2291     for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++)
2292     {
2293         if (pData[i].nRow >= nStartRow)
2294         {
2295             const ScPatternAttr* pOldPattern = pData[i].pPattern;
2296             const ScPatternAttr* pNewPattern;
2297 
2298             if (IsDefaultItem( pOldPattern ))
2299             {
2300                 //  am Default muss nichts veraendert werden
2301 
2302                 pNewPattern = (const ScPatternAttr*)
2303                                 &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
2304             }
2305             else if ( nStripFlags )
2306             {
2307                 ScPatternAttr* pTmpPattern = new ScPatternAttr( *pOldPattern );
2308                 sal_Int16 nNewFlags = 0;
2309                 if ( nStripFlags != SC_MF_ALL )
2310                     nNewFlags = ((const ScMergeFlagAttr&)pTmpPattern->GetItem(ATTR_MERGE_FLAG)).
2311                                 GetValue() & ~nStripFlags;
2312 
2313                 if ( nNewFlags )
2314                     pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
2315                 else
2316                     pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG );
2317 
2318                 if (bSamePool)
2319                     pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pTmpPattern);
2320                 else
2321                     pNewPattern = pTmpPattern->PutInPool( rAttrArray.pDocument, pDocument );
2322                 delete pTmpPattern;
2323             }
2324             else
2325             {
2326                 if (bSamePool)
2327                     pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern);
2328                 else
2329                     pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument );
2330             }
2331 
2332             rAttrArray.SetPatternArea(nDestStart,
2333                             Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern);
2334         }
2335 
2336         // when pasting from clipboard and skipping filtered rows, the adjusted end position
2337         // can be negative
2338         nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1));
2339     }
2340 }
2341 
2342         // Flags stehenlassen
2343         //! mit CopyArea zusammenfassen !!!
2344 
2345 void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray )
2346 {
2347     nStartRow -= nDy;       // Source
2348     nEndRow -= nDy;
2349 
2350     SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0);
2351     SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW);
2352 
2353     if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HASATTR_OVERLAPPED ) )
2354     {
2355         CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
2356         return;
2357     }
2358 
2359     ScDocumentPool* pSourceDocPool = pDocument->GetPool();
2360     ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool();
2361     sal_Bool bSamePool = (pSourceDocPool==pDestDocPool);
2362 
2363     for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++)
2364     {
2365         if (pData[i].nRow >= nStartRow)
2366         {
2367             const ScPatternAttr* pOldPattern = pData[i].pPattern;
2368             const ScPatternAttr* pNewPattern;
2369 
2370             if (bSamePool)
2371                 pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern);
2372             else
2373                 pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument );
2374 
2375             rAttrArray.SetPatternAreaSafe(nDestStart,
2376                             Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern, sal_False);
2377         }
2378 
2379         // when pasting from clipboard and skipping filtered rows, the adjusted end position
2380         // can be negative
2381         nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1));
2382     }
2383 }
2384 
2385 
2386 SCsROW ScAttrArray::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle,
2387                                     sal_Bool bUp, ScMarkArray* pMarkArray )
2388 {
2389     sal_Bool bFound = sal_False;
2390 
2391     if (pMarkArray)
2392     {
2393         nRow = pMarkArray->GetNextMarked( nRow, bUp );
2394         if (!VALIDROW(nRow))
2395             return nRow;
2396     }
2397 
2398     SCSIZE nIndex;
2399     Search(nRow, nIndex);
2400     const ScPatternAttr* pPattern = pData[nIndex].pPattern;
2401 
2402     while (nIndex < nCount && !bFound)
2403     {
2404         if (pPattern->GetStyleSheet() == pSearchStyle)
2405         {
2406             if (pMarkArray)
2407             {
2408                 nRow = pMarkArray->GetNextMarked( nRow, bUp );
2409                 SCROW nStart = nIndex ? pData[nIndex-1].nRow+1 : 0;
2410                 if (nRow >= nStart && nRow <= pData[nIndex].nRow)
2411                     bFound = sal_True;
2412             }
2413             else
2414                 bFound = sal_True;
2415         }
2416 
2417         if (!bFound)
2418         {
2419             if (bUp)
2420             {
2421                 if (nIndex==0)
2422                 {
2423                     nIndex = nCount;
2424                     nRow = -1;
2425                 }
2426                 else
2427                 {
2428                     --nIndex;
2429                     nRow = pData[nIndex].nRow;
2430                     pPattern = pData[nIndex].pPattern;
2431                 }
2432             }
2433             else
2434             {
2435                 nRow = pData[nIndex].nRow+1;
2436                 ++nIndex;
2437                 if (nIndex<nCount)
2438                     pPattern = pData[nIndex].pPattern;
2439             }
2440         }
2441     }
2442 
2443     DBG_ASSERT( bFound || !ValidRow(nRow), "interner Fehler in ScAttrArray::SearchStyle" );
2444 
2445     return nRow;
2446 }
2447 
2448 
2449 sal_Bool ScAttrArray::SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow,
2450                         const ScStyleSheet* pSearchStyle, sal_Bool bUp, ScMarkArray* pMarkArray )
2451 {
2452     SCsROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
2453     if (VALIDROW(nStartRow))
2454     {
2455         SCSIZE nIndex;
2456         Search(nStartRow,nIndex);
2457 
2458         rRow = nStartRow;
2459         if (bUp)
2460         {
2461             if (nIndex>0)
2462                 rEndRow = pData[nIndex-1].nRow + 1;
2463             else
2464                 rEndRow = 0;
2465             if (pMarkArray)
2466             {
2467                 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, sal_True );
2468                 if (nMarkEnd>rEndRow)
2469                     rEndRow = nMarkEnd;
2470             }
2471         }
2472         else
2473         {
2474             rEndRow = pData[nIndex].nRow;
2475             if (pMarkArray)
2476             {
2477                 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, sal_False );
2478                 if (nMarkEnd<rEndRow)
2479                     rEndRow = nMarkEnd;
2480             }
2481         }
2482 
2483         return sal_True;
2484     }
2485     else
2486         return sal_False;
2487 }
2488 
2489 //------------------------------------------------------------------------
2490 //
2491 //                          Laden / Speichern
2492 //
2493 
2494 
2495 #if 0
2496 void ScAttrArray::Save( SvStream& /* rStream */ ) const
2497 {
2498 #if SC_ROWLIMIT_STREAM_ACCESS
2499 #error address types changed!
2500     ScWriteHeader aHdr( rStream, 8 );
2501 
2502     ScDocumentPool* pDocPool = pDocument->GetPool();
2503 
2504     sal_uInt16 nSaveCount = nCount;
2505     SCROW nSaveMaxRow = pDocument->GetSrcMaxRow();
2506     if ( nSaveMaxRow != MAXROW )
2507     {
2508         if ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow )
2509         {
2510             pDocument->SetLostData();           // Warnung ausgeben
2511             do
2512                 --nSaveCount;
2513             while ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow );
2514         }
2515     }
2516 
2517     rStream << nSaveCount;
2518 
2519     const SfxPoolItem* pItem;
2520     for (SCSIZE i=0; i<nSaveCount; i++)
2521     {
2522         rStream << Min( pData[i].nRow, nSaveMaxRow );
2523 
2524         const ScPatternAttr* pPattern = pData[i].pPattern;
2525         pDocPool->StoreSurrogate( rStream, pPattern );
2526 
2527         //  sal_False, weil ATTR_CONDITIONAL (noch) nicht in Vorlagen:
2528         if (pPattern->GetItemSet().GetItemState(ATTR_CONDITIONAL,sal_False,&pItem) == SFX_ITEM_SET)
2529             pDocument->SetConditionalUsed( ((const SfxUInt32Item*)pItem)->GetValue() );
2530 
2531         if (pPattern->GetItemSet().GetItemState(ATTR_VALIDDATA,sal_False,&pItem) == SFX_ITEM_SET)
2532             pDocument->SetValidationUsed( ((const SfxUInt32Item*)pItem)->GetValue() );
2533     }
2534 #endif // SC_ROWLIMIT_STREAM_ACCESS
2535 }
2536 
2537 
2538 void ScAttrArray::Load( SvStream& /* rStream */ )
2539 {
2540 #if SC_ROWLIMIT_STREAM_ACCESS
2541 #error address types changed!
2542     ScDocumentPool* pDocPool = pDocument->GetPool();
2543 
2544     ScReadHeader aHdr( rStream );
2545 
2546     sal_uInt16 nNewCount;
2547     rStream >> nNewCount;
2548     if ( nNewCount > MAXROW+1 )                     // wuerde das Array zu gross?
2549     {
2550         pDocument->SetLostData();
2551         rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
2552         return;
2553     }
2554 
2555     Reset( pDocument->GetDefPattern(), sal_False );     // loeschen
2556     pData = new ScAttrEntry[nNewCount];             // neu anlegen
2557     for (SCSIZE i=0; i<nNewCount; i++)
2558     {
2559         rStream >> pData[i].nRow;
2560 
2561         sal_uInt16 nWhich = ATTR_PATTERN;
2562         const ScPatternAttr* pNewPattern = (const ScPatternAttr*)
2563                                            pDocPool->LoadSurrogate( rStream, nWhich, ATTR_PATTERN );
2564         if (!pNewPattern)
2565         {
2566             // da is was schiefgelaufen
2567             DBG_ERROR("ScAttrArray::Load: Surrogat nicht im Pool");
2568             pNewPattern = pDocument->GetDefPattern();
2569         }
2570         ScDocumentPool::CheckRef( *pNewPattern );
2571         pData[i].pPattern = pNewPattern;
2572 
2573         // LoadSurrogate erhoeht auch die Ref
2574     }
2575     nCount = nLimit = nNewCount;
2576 
2577     if ( nCount > 1 && pData[nCount-2].nRow >= MAXROW ) // faengt ein Attribut hinter MAXROW an?
2578     {
2579         pDocument->SetLostData();
2580         rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
2581         return;
2582     }
2583 
2584     if ( pDocument->GetSrcMaxRow() != MAXROW )          // Ende anpassen?
2585     {
2586         //  Ende immer auf MAXROW umsetzen (nur auf 32 Bit)
2587 
2588         DBG_ASSERT( pData[nCount-1].nRow == pDocument->GetSrcMaxRow(), "Attribut-Ende ?!?" );
2589         pData[nCount-1].nRow = MAXROW;
2590     }
2591 #endif // SC_ROWLIMIT_STREAM_ACCESS
2592 }
2593 #endif
2594 
2595 
2596 //UNUSED2008-05  void ScAttrArray::ConvertFontsAfterLoad()
2597 //UNUSED2008-05  {
2598 //UNUSED2008-05      ScFontToSubsFontConverter_AutoPtr xFontConverter;
2599 //UNUSED2008-05      const sal_uLong nFlags = FONTTOSUBSFONT_IMPORT | FONTTOSUBSFONT_ONLYOLDSOSYMBOLFONTS;
2600 //UNUSED2008-05      SCSIZE   nIndex = 0;
2601 //UNUSED2008-05      SCROW  nThisRow = 0;
2602 //UNUSED2008-05
2603 //UNUSED2008-05      while ( nThisRow <= MAXROW )
2604 //UNUSED2008-05      {
2605 //UNUSED2008-05          const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
2606 //UNUSED2008-05          const SfxPoolItem* pItem;
2607 //UNUSED2008-05          if( pOldPattern->GetItemSet().GetItemState( ATTR_FONT, sal_False, &pItem ) == SFX_ITEM_SET )
2608 //UNUSED2008-05          {
2609 //UNUSED2008-05              const SvxFontItem* pFontItem = (const SvxFontItem*) pItem;
2610 //UNUSED2008-05              const String& rOldName = pFontItem->GetFamilyName();
2611 //UNUSED2008-05              xFontConverter = CreateFontToSubsFontConverter( rOldName, nFlags );
2612 //UNUSED2008-05              if ( xFontConverter )
2613 //UNUSED2008-05              {
2614 //UNUSED2008-05                  String aNewName( GetFontToSubsFontName( xFontConverter ) );
2615 //UNUSED2008-05                  if ( aNewName != rOldName )
2616 //UNUSED2008-05                  {
2617 //UNUSED2008-05                      SCROW nAttrRow = pData[nIndex].nRow;
2618 //UNUSED2008-05                      SvxFontItem aNewItem( pFontItem->GetFamily(), aNewName,
2619 //UNUSED2008-05                          pFontItem->GetStyleName(), pFontItem->GetPitch(),
2620 //UNUSED2008-05                          RTL_TEXTENCODING_DONTKNOW, ATTR_FONT );
2621 //UNUSED2008-05                      ScPatternAttr aNewPattern( *pOldPattern );
2622 //UNUSED2008-05                      aNewPattern.GetItemSet().Put( aNewItem );
2623 //UNUSED2008-05                      SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
2624 //UNUSED2008-05                      Search( nThisRow, nIndex );     //! data changed
2625 //UNUSED2008-05                  }
2626 //UNUSED2008-05              }
2627 //UNUSED2008-05          }
2628 //UNUSED2008-05          ++nIndex;
2629 //UNUSED2008-05          nThisRow = pData[nIndex-1].nRow+1;
2630 //UNUSED2008-05      }
2631 //UNUSED2008-05  }
2632 
2633