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