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