tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / attarray.cxx
blob852db370cf54c9c128565805f4cb801fe6fed22b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <attarray.hxx>
21 #include <scitems.hxx>
22 #include <editeng/borderline.hxx>
23 #include <editeng/boxitem.hxx>
24 #include <editeng/lineitem.hxx>
25 #include <editeng/shaditem.hxx>
26 #include <editeng/editobj.hxx>
27 #include <editeng/justifyitem.hxx>
28 #include <osl/diagnose.h>
29 #include <poolcach.hxx>
31 #include <global.hxx>
32 #include <document.hxx>
33 #include <docpool.hxx>
34 #include <docsh.hxx>
35 #include <patattr.hxx>
36 #include <stlsheet.hxx>
37 #include <stlpool.hxx>
38 #include <markarr.hxx>
39 #include <globstr.hrc>
40 #include <scresid.hxx>
41 #include <segmenttree.hxx>
42 #include <editdataarray.hxx>
43 #include <cellvalue.hxx>
44 #include <editutil.hxx>
45 #include <mtvelements.hxx>
46 #include <memory>
48 using ::editeng::SvxBorderLine;
50 ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, ScAttrArray* pDefaultColAttrArray ) :
51 nCol( nNewCol ),
52 nTab( nNewTab ),
53 rDocument( rDoc )
55 if ( nCol == -1 || !pDefaultColAttrArray || pDefaultColAttrArray->mvData.empty() )
56 return;
58 ScAddress aAdrStart( nCol, 0, nTab );
59 ScAddress aAdrEnd( nCol, 0, nTab );
60 mvData.resize( pDefaultColAttrArray->mvData.size() );
61 for ( size_t nIdx = 0; nIdx < pDefaultColAttrArray->mvData.size(); ++nIdx )
63 mvData[nIdx].nEndRow = pDefaultColAttrArray->mvData[nIdx].nEndRow;
64 mvData[nIdx].setScPatternAttr(pDefaultColAttrArray->mvData[nIdx].getScPatternAttr());
65 bool bNumFormatChanged = false;
66 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
67 mvData[nIdx].getScPatternAttr()->GetItemSet(),
68 rDocument.getCellAttributeHelper().getDefaultCellAttribute().GetItemSet() ) )
70 aAdrStart.SetRow( nIdx ? mvData[nIdx-1].nEndRow+1 : 0 );
71 aAdrEnd.SetRow( mvData[nIdx].nEndRow );
72 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
77 ScAttrArray::~ScAttrArray()
79 #if DEBUG_SC_TESTATTRARRAY
80 TestData();
81 #endif
84 #if DEBUG_SC_TESTATTRARRAY
85 void ScAttrArray::TestData() const
88 sal_uInt16 nErr = 0;
89 SCSIZE nPos;
90 for (nPos=0; nPos<nCount; nPos++)
92 if (nPos > 0)
93 if (mvData[nPos].pPattern == mvData[nPos-1].pPattern || mvData[nPos].nRow <= mvData[nPos-1].nRow)
94 ++nErr;
96 if ( nPos && mvData[nPos-1].nRow != rDocument.MaxRow() )
97 ++nErr;
99 SAL_WARN_IF( nErr, "sc", nErr << " errors in attribute array, column " << nCol );
101 #endif
103 void ScAttrArray::SetDefaultIfNotInit( SCSIZE nNeeded )
105 if ( !mvData.empty() )
106 return;
108 SCSIZE nNewLimit = std::max<SCSIZE>( SC_ATTRARRAY_DELTA, nNeeded );
109 mvData.reserve( nNewLimit );
110 mvData.emplace_back();
111 mvData[0].nEndRow = rDocument.MaxRow();
112 mvData[0].setScPatternAttr(&rDocument.getCellAttributeHelper().getDefaultCellAttribute()); // no put
115 void ScAttrArray::Reset(const CellAttributeHolder& rPattern)
117 const ScPatternAttr* pPattern(rPattern.getScPatternAttr());
118 if (nullptr == pPattern)
119 return;
121 ScAddress aAdrStart( nCol, 0, nTab );
122 ScAddress aAdrEnd ( nCol, 0, nTab );
124 for (SCSIZE i=0; i<mvData.size(); i++)
126 // ensure that attributing changes text width of cell
127 const ScPatternAttr* pOldPattern(mvData[i].getScPatternAttr());
128 if ( nCol != -1 )
130 bool bNumFormatChanged;
131 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
132 pPattern->GetItemSet(), pOldPattern->GetItemSet() ) )
134 aAdrStart.SetRow( i ? mvData[i-1].nEndRow+1 : 0 );
135 aAdrEnd .SetRow( mvData[i].nEndRow );
136 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
140 mvData.resize(0);
142 rDocument.SetStreamValid(nTab, false);
144 mvData.resize(1);
145 mvData[0].nEndRow = rDocument.MaxRow();
146 mvData[0].setScPatternAttr(pPattern);
149 bool ScAttrArray::Concat(SCSIZE nPos)
151 bool bRet = false;
152 if (nPos < mvData.size())
154 if (nPos > 0)
156 if (ScPatternAttr::areSame(mvData[nPos - 1].getScPatternAttr(), mvData[nPos].getScPatternAttr()))
158 mvData[nPos - 1].nEndRow = mvData[nPos].nEndRow;
159 mvData.erase(mvData.begin() + nPos);
160 nPos--;
161 bRet = true;
164 if (nPos + 1 < mvData.size())
166 if (ScPatternAttr::areSame(mvData[nPos + 1].getScPatternAttr(), mvData[nPos].getScPatternAttr()))
168 mvData[nPos].nEndRow = mvData[nPos + 1].nEndRow;
169 mvData.erase(mvData.begin() + nPos + 1);
170 bRet = true;
174 return bRet;
178 * nCount is the number of runs of different attribute combinations;
179 * no attribute in a column => nCount==1, one attribute somewhere => nCount == 3
180 * (ie. one run with no attribute + one attribute + another run with no attribute)
181 * so a range of identical attributes is only one entry in ScAttrArray.
183 * Iterative implementation of Binary Search
184 * The same implementation was used inside ScMarkArray::Search().
186 * @param oIndexHint, hint for the start of the search, useful when searching twice for successive values
189 bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex, std::optional<SCROW> oIndexHint ) const
191 /* auto it = std::lower_bound(mvData.begin(), mvData.end(), nRow,
192 [] (const ScAttrEntry &r1, SCROW nRow)
193 { return r1.nEndRow < nRow; } );
194 if (it != mvData.end())
195 nIndex = it - mvData.begin();
196 return it != mvData.end(); */
198 if (mvData.size() == 1)
200 nIndex = 0;
201 return true;
204 tools::Long nHi = static_cast<tools::Long>(mvData.size()) - 1;
205 tools::Long i = 0;
206 assert((!oIndexHint || *oIndexHint <= nHi) && "bad index hint");
207 tools::Long nLo = oIndexHint ? *oIndexHint : 0;
209 while ( nLo <= nHi )
211 i = (nLo + nHi) / 2;
213 if (mvData[i].nEndRow < nRow)
215 // If [nRow] greater, ignore left half
216 nLo = i + 1;
218 else if ((i > 0) && (mvData[i - 1].nEndRow >= nRow))
220 // If [nRow] is smaller, ignore right half
221 nHi = i - 1;
223 else
225 // found
226 nIndex=static_cast<SCSIZE>(i);
227 return true;
231 nIndex=0;
232 return false;
235 const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const
237 if ( mvData.empty() )
239 if ( !rDocument.ValidRow(nRow) )
240 return nullptr;
241 return &rDocument.getCellAttributeHelper().getDefaultCellAttribute();
243 SCSIZE i;
244 if (Search( nRow, i ))
245 return mvData[i].getScPatternAttr();
246 else
247 return nullptr;
250 const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow,
251 SCROW& rEndRow, SCROW nRow ) const
253 if ( mvData.empty() )
255 if ( !rDocument.ValidRow( nRow ) )
256 return nullptr;
257 rStartRow = 0;
258 rEndRow = rDocument.MaxRow();
259 return &rDocument.getCellAttributeHelper().getDefaultCellAttribute();
261 SCSIZE nIndex;
262 if ( Search( nRow, nIndex ) )
264 if ( nIndex > 0 )
265 rStartRow = mvData[nIndex-1].nEndRow + 1;
266 else
267 rStartRow = 0;
268 rEndRow = mvData[nIndex].nEndRow;
269 return mvData[nIndex].getScPatternAttr();
271 return nullptr;
274 void ScAttrArray::AddCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
276 if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
277 return;
279 if(nEndRow < nStartRow)
280 return;
282 SCROW nTempStartRow = nStartRow;
283 SCROW nTempEndRow = nEndRow;
287 const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
289 // changed to create pNewPattern only if needed, else use already
290 // existing pPattern. This shows by example how to avoid that special
291 // handling of ScPatternAttr in SC and massive
292 // incarnations/destructions of that Item (which contains an ItemSet)
293 std::unique_ptr<ScPatternAttr> pNewPattern;
294 if(pPattern)
296 SCROW nPatternStartRow;
297 SCROW nPatternEndRow;
298 GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
300 nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
301 if (const ScCondFormatItem* pItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ))
303 ScCondFormatIndexes const & rCondFormatData = pItem->GetCondFormatData();
304 if (rCondFormatData.find(nIndex) == rCondFormatData.end())
306 ScCondFormatIndexes aNewCondFormatData;
307 aNewCondFormatData.reserve(rCondFormatData.size()+1);
308 aNewCondFormatData = rCondFormatData;
309 aNewCondFormatData.insert(nIndex);
310 ScCondFormatItem aItem( std::move(aNewCondFormatData) );
311 pNewPattern.reset( new ScPatternAttr(*pPattern) );
312 pNewPattern->GetItemSet().Put( aItem );
315 else
317 ScCondFormatItem aItem(nIndex);
318 pNewPattern.reset( new ScPatternAttr(*pPattern) );
319 pNewPattern->GetItemSet().Put( aItem );
322 else
324 pNewPattern.reset( new ScPatternAttr( rDocument.getCellAttributeHelper() ) );
325 ScCondFormatItem aItem(nIndex);
326 pNewPattern->GetItemSet().Put( aItem );
327 nTempEndRow = nEndRow;
330 if (pNewPattern)
331 SetPatternArea( nTempStartRow, nTempEndRow, CellAttributeHolder(pNewPattern.release(), true) );
332 else
333 SetPatternArea( nTempStartRow, nTempEndRow, CellAttributeHolder(pPattern) );
335 nTempStartRow = nTempEndRow + 1;
337 while(nTempEndRow < nEndRow);
341 void ScAttrArray::RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
343 if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
344 return;
346 if(nEndRow < nStartRow)
347 return;
349 SCROW nTempStartRow = nStartRow;
350 SCROW nTempEndRow = nEndRow;
354 const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
356 if(pPattern)
358 SCROW nPatternStartRow;
359 SCROW nPatternEndRow;
360 GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
362 nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
363 if (const ScCondFormatItem* pItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ))
365 if (nIndex == 0)
367 ScCondFormatItem aItem;
368 ScPatternAttr* pTemp(new ScPatternAttr(*pPattern));
369 pTemp->GetItemSet().Put( aItem );
370 SetPatternArea( nTempStartRow, nTempEndRow, CellAttributeHolder(pTemp, true) );
372 else
374 ScCondFormatIndexes const & rCondFormatData = pItem->GetCondFormatData();
375 auto itr = rCondFormatData.find(nIndex);
376 if(itr != rCondFormatData.end())
378 ScCondFormatIndexes aNewCondFormatData(rCondFormatData);
379 aNewCondFormatData.erase_at(std::distance(rCondFormatData.begin(), itr));
380 ScCondFormatItem aItem( std::move(aNewCondFormatData) );
381 ScPatternAttr* pTemp(new ScPatternAttr(*pPattern));
382 pTemp->GetItemSet().Put( aItem );
383 SetPatternArea( nTempStartRow, nTempEndRow, CellAttributeHolder(pTemp, true) );
388 else
390 return;
393 nTempStartRow = nTempEndRow + 1;
395 while(nTempEndRow < nEndRow);
399 void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow,
400 const ScPatternAttr* pPattern, ScEditDataArray* pDataArray )
402 assert( nCol != -1 );
403 // cache mdds position, this doesn't modify the mdds container, just EditTextObject's
404 sc::ColumnBlockPosition blockPos;
405 rDocument.InitColumnBlockPosition( blockPos, nTab, nCol );
406 nEndRow = rDocument.GetLastDataRow(nTab, nCol, nCol, nEndRow);
407 for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
409 ScAddress aPos(nCol, nRow, nTab);
410 ScRefCellValue aCell(rDocument, aPos, blockPos);
411 if (aCell.getType() != CELLTYPE_EDIT || !aCell.getEditText())
412 continue;
414 std::unique_ptr<EditTextObject> pOldData;
415 if (pDataArray)
416 pOldData = aCell.getEditText()->Clone();
418 // Direct modification of cell content - something to watch out for if
419 // we decide to share edit text instances in the future.
420 ScEditUtil::RemoveCharAttribs(const_cast<EditTextObject&>(*aCell.getEditText()), *pPattern);
422 if (pDataArray)
424 std::unique_ptr<EditTextObject> pNewData = aCell.getEditText()->Clone();
425 pDataArray->AddItem(nTab, nCol, nRow, std::move(pOldData), std::move(pNewData));
430 bool ScAttrArray::Reserve( SCSIZE nReserve )
432 if ( mvData.empty() && nReserve )
434 try {
435 mvData.reserve(nReserve);
436 mvData.emplace_back();
437 mvData[0].nEndRow = rDocument.MaxRow();
438 mvData[0].setScPatternAttr(&rDocument.getCellAttributeHelper().getDefaultCellAttribute()); // no put
439 return true;
440 } catch (std::bad_alloc const &) {
441 return false;
444 else if ( mvData.capacity() < nReserve )
446 try {
447 mvData.reserve(nReserve);
448 return true;
449 } catch (std::bad_alloc const &) {
450 return false;
453 else
454 return false;
457 const ScPatternAttr* ScAttrArray::SetPatternAreaImpl(
458 SCROW nStartRow, SCROW nEndRow, const CellAttributeHolder& rPattern, ScEditDataArray* pDataArray)
460 const ScPatternAttr* pPattern(rPattern.getScPatternAttr());
461 if (nullptr == pPattern)
462 return nullptr;
464 if (rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow))
466 if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
467 Reset(rPattern);
468 else
470 SCSIZE nNeeded = mvData.size() + 2;
471 SetDefaultIfNotInit( nNeeded );
473 ScAddress aAdrStart( nCol, 0, nTab );
474 ScAddress aAdrEnd ( nCol, 0, nTab );
476 SCSIZE ni = 0; // number of entries in beginning
477 SCSIZE nx = 0; // track position
478 SCROW ns = 0; // start row of track position
479 if ( nStartRow > 0 )
481 // skip beginning
482 SCSIZE nIndex;
483 Search( nStartRow, nIndex );
484 ni = nIndex;
486 if ( ni > 0 )
488 nx = ni;
489 ns = mvData[ni-1].nEndRow+1;
493 // ensure that attributing changes text width of cell
494 // otherwise, conditional formats need to be reset or deleted
495 bool bIsLoading = !rDocument.GetDocumentShell() || rDocument.GetDocumentShell()->IsLoading();
496 while ( ns <= nEndRow )
498 if ( nCol != -1 && !bIsLoading )
500 const SfxItemSet& rNewSet = pPattern->GetItemSet();
501 const SfxItemSet& rOldSet = mvData[nx].getScPatternAttr()->GetItemSet();
502 bool bNumFormatChanged;
503 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
504 rNewSet, rOldSet ) )
506 aAdrStart.SetRow( std::max(nStartRow,ns) );
507 aAdrEnd .SetRow( std::min(nEndRow,mvData[nx].nEndRow) );
508 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
511 ns = mvData[nx].nEndRow + 1;
512 nx++;
515 // continue modifying data array
517 SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert)
518 bool bCombined = false;
519 bool bSplit = false;
520 if ( nStartRow > 0 )
522 nInsert = rDocument.MaxRow() + 1;
523 if ( !ScPatternAttr::areSame(mvData[ni].getScPatternAttr(), pPattern ) )
525 if ( ni == 0 || (mvData[ni-1].nEndRow < nStartRow - 1) )
526 { // may be a split or a simple insert or just a shrink,
527 // row adjustment is done further down
528 if ( mvData[ni].nEndRow > nEndRow )
529 bSplit = true;
530 ni++;
531 nInsert = ni;
533 else if (mvData[ni - 1].nEndRow == nStartRow - 1)
534 nInsert = ni;
536 if ( ni > 0 && ScPatternAttr::areSame(mvData[ni-1].getScPatternAttr(), pPattern) )
537 { // combine
538 mvData[ni-1].nEndRow = nEndRow;
539 nInsert = rDocument.MaxRow() + 1;
540 bCombined = true;
543 else
544 nInsert = 0;
546 SCSIZE nj = ni; // stop position of range to replace
547 while ( nj < mvData.size() && mvData[nj].nEndRow <= nEndRow )
548 nj++;
549 if ( !bSplit )
551 if ( nj < mvData.size() && ScPatternAttr::areSame(mvData[nj].getScPatternAttr(), pPattern ) )
552 { // combine
553 if ( ni > 0 )
555 if ( ScPatternAttr::areSame(mvData[ni-1].getScPatternAttr(), pPattern ) )
556 { // adjacent entries
557 mvData[ni-1].nEndRow = mvData[nj].nEndRow;
558 nj++;
560 else if ( ni == nInsert )
561 mvData[ni-1].nEndRow = nStartRow - 1; // shrink
563 nInsert = rDocument.MaxRow() + 1;
564 bCombined = true;
566 else if ( ni > 0 && ni == nInsert )
567 mvData[ni-1].nEndRow = nStartRow - 1; // shrink
569 if ( ni < nj )
570 { // remove middle entries
571 if ( !bCombined )
572 { // replace one entry
573 mvData[ni].nEndRow = nEndRow;
574 mvData[ni].setScPatternAttr(pPattern);
575 ni++;
576 nInsert = rDocument.MaxRow() + 1;
578 if ( ni < nj )
579 { // remove entries
580 mvData.erase( mvData.begin() + ni, mvData.begin() + nj);
584 if ( nInsert < sal::static_int_cast<SCSIZE>(rDocument.MaxRow() + 1) )
585 { // insert or append new entry
586 if ( nInsert <= mvData.size() )
588 if ( !bSplit )
589 mvData.emplace(mvData.begin() + nInsert);
590 else
592 mvData.insert(mvData.begin() + nInsert, 2, ScAttrEntry());
593 mvData[nInsert+1] = mvData[nInsert-1];
596 if ( nInsert )
597 mvData[nInsert-1].nEndRow = nStartRow - 1;
598 mvData[nInsert].nEndRow = nEndRow;
599 mvData[nInsert].setScPatternAttr(pPattern);
601 // Remove character attributes from these cells if the pattern
602 // is applied during normal session.
603 if (pDataArray && nCol != -1)
604 RemoveCellCharAttribs(nStartRow, nEndRow, pPattern, pDataArray);
607 rDocument.SetStreamValid(nTab, false);
611 #if DEBUG_SC_TESTATTRARRAY
612 TestData();
613 #endif
614 return pPattern;
617 void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle )
619 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
620 return;
622 SetDefaultIfNotInit();
623 SCSIZE nPos;
624 SCROW nStart=0;
625 if (!Search( nStartRow, nPos ))
627 OSL_FAIL("Search Failure");
628 return;
631 ScAddress aAdrStart( nCol, 0, nTab );
632 ScAddress aAdrEnd ( nCol, 0, nTab );
636 const ScPatternAttr* pOldPattern = mvData[nPos].getScPatternAttr();
637 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
638 pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(&rStyle));
639 SCROW nY1 = nStart;
640 SCROW nY2 = mvData[nPos].nEndRow;
641 nStart = mvData[nPos].nEndRow + 1;
643 if ( *pNewPattern == *pOldPattern )
645 // keep the original pattern (might be default)
646 // pNewPattern is deleted below
647 nPos++;
649 else if ( nY1 < nStartRow || nY2 > nEndRow )
651 if (nY1 < nStartRow) nY1=nStartRow;
652 if (nY2 > nEndRow) nY2=nEndRow;
653 SetPatternArea( nY1, nY2, CellAttributeHolder(pNewPattern.release(), true) );
654 Search( nStart, nPos );
656 else
658 if ( nCol != -1 )
660 // ensure attributing changes text width of cell; otherwise
661 // there aren't (yet) template format changes
662 const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
663 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
665 bool bNumFormatChanged;
666 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
667 rNewSet, rOldSet ) )
669 aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
670 aAdrEnd .SetRow( mvData[nPos].nEndRow );
671 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
675 mvData[nPos].setScPatternAttr(pNewPattern.release(), true);
676 if (Concat(nPos))
677 Search(nStart, nPos);
678 else
679 nPos++;
682 while ((nStart <= nEndRow) && (nPos < mvData.size()));
684 rDocument.SetStreamValid(nTab, false);
686 #if DEBUG_SC_TESTATTRARRAY
687 TestData();
688 #endif
691 // const cast, otherwise it will be too inefficient/complicated
692 static void SetLineColor(SvxBorderLine const * dest, Color c)
694 if (dest)
696 const_cast<SvxBorderLine*>(dest)->SetColor(c);
700 static void SetLine(const SvxBorderLine* dest, const SvxBorderLine* src)
702 if (dest)
704 SvxBorderLine* pCast = const_cast<SvxBorderLine*>(dest);
705 pCast->SetBorderLineStyle( src->GetBorderLineStyle() );
706 pCast->SetWidth( src->GetWidth() );
710 void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
711 const SvxBorderLine* pLine, bool bColorOnly )
713 if ( bColorOnly && !pLine )
714 return;
716 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
717 return;
719 SCSIZE nPos;
720 SCROW nStart=0;
721 SetDefaultIfNotInit();
722 if (!Search( nStartRow, nPos ))
724 OSL_FAIL("Search failure");
725 return;
730 const ScPatternAttr* pOldPattern = mvData[nPos].getScPatternAttr();
731 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
732 const SvxBoxItem* pBoxItem = rOldSet.GetItemIfSet( ATTR_BORDER );
733 const SvxLineItem* pTLBRItem = rOldSet.GetItemIfSet( ATTR_BORDER_TLBR );
734 const SvxLineItem* pBLTRItem = rOldSet.GetItemIfSet( ATTR_BORDER_BLTR );
736 if ( pBoxItem || pTLBRItem || pBLTRItem )
738 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
739 SfxItemSet& rNewSet = pNewPattern->GetItemSet();
740 SCROW nY1 = nStart;
741 SCROW nY2 = mvData[nPos].nEndRow;
743 std::unique_ptr<SvxBoxItem> pNewBoxItem( pBoxItem ? pBoxItem->Clone() : nullptr);
744 std::unique_ptr<SvxLineItem> pNewTLBRItem( pTLBRItem ? pTLBRItem->Clone() : nullptr);
745 std::unique_ptr<SvxLineItem> pNewBLTRItem(pBLTRItem ? pBLTRItem->Clone() : nullptr);
747 // fetch line and update attributes with parameters
749 if ( !pLine )
751 if( pNewBoxItem )
753 if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::TOP );
754 if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::BOTTOM );
755 if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::LEFT );
756 if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::RIGHT );
758 if( pNewTLBRItem && pNewTLBRItem->GetLine() )
759 pNewTLBRItem->SetLine( nullptr );
760 if( pNewBLTRItem && pNewBLTRItem->GetLine() )
761 pNewBLTRItem->SetLine( nullptr );
763 else
765 if ( bColorOnly )
767 Color aColor( pLine->GetColor() );
768 if( pNewBoxItem )
770 SetLineColor( pNewBoxItem->GetTop(), aColor );
771 SetLineColor( pNewBoxItem->GetBottom(), aColor );
772 SetLineColor( pNewBoxItem->GetLeft(), aColor );
773 SetLineColor( pNewBoxItem->GetRight(), aColor );
775 if( pNewTLBRItem )
776 SetLineColor( pNewTLBRItem->GetLine(), aColor );
777 if( pNewBLTRItem )
778 SetLineColor( pNewBLTRItem->GetLine(), aColor );
780 else
782 if( pNewBoxItem )
784 SetLine( pNewBoxItem->GetTop(), pLine );
785 SetLine( pNewBoxItem->GetBottom(), pLine );
786 SetLine( pNewBoxItem->GetLeft(), pLine );
787 SetLine( pNewBoxItem->GetRight(), pLine );
789 if( pNewTLBRItem )
790 SetLine( pNewTLBRItem->GetLine(), pLine );
791 if( pNewBLTRItem )
792 SetLine( pNewBLTRItem->GetLine(), pLine );
795 if( pNewBoxItem ) rNewSet.Put( std::move(pNewBoxItem) );
796 if( pNewTLBRItem ) rNewSet.Put( std::move(pNewTLBRItem) );
797 if( pNewBLTRItem ) rNewSet.Put( std::move(pNewBLTRItem) );
799 nStart = mvData[nPos].nEndRow + 1;
801 if ( nY1 < nStartRow || nY2 > nEndRow )
803 if (nY1 < nStartRow) nY1=nStartRow;
804 if (nY2 > nEndRow) nY2=nEndRow;
805 SetPatternArea( nY1, nY2, CellAttributeHolder(pNewPattern.release(), true) );
806 Search( nStart, nPos );
808 else
810 // remove from pool ?
811 mvData[nPos].setScPatternAttr(pNewPattern.release(), true);
813 if (Concat(nPos))
814 Search(nStart, nPos);
815 else
816 nPos++;
819 else
821 nStart = mvData[nPos].nEndRow + 1;
822 nPos++;
825 while ((nStart <= nEndRow) && (nPos < mvData.size()));
828 void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, ScItemPoolCache& rCache, ScEditDataArray* pDataArray, bool* const pIsChanged )
830 #if DEBUG_SC_TESTATTRARRAY
831 TestData();
832 #endif
834 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
835 return;
837 SCSIZE nPos;
838 SCROW nStart=0;
839 SetDefaultIfNotInit();
840 if (!Search( nStartRow, nPos ))
842 OSL_FAIL("Search Failure");
843 return;
846 ScAddress aAdrStart( nCol, 0, nTab );
847 ScAddress aAdrEnd ( nCol, 0, nTab );
851 const CellAttributeHolder& rOldPattern(mvData[nPos].getCellAttributeHolder());
852 const CellAttributeHolder& rNewPattern(rCache.ApplyTo( rOldPattern ));
854 if (!CellAttributeHolder::areSame(&rNewPattern, &rOldPattern))
856 SCROW nY1 = nStart;
857 SCROW nY2 = mvData[nPos].nEndRow;
858 nStart = mvData[nPos].nEndRow + 1;
860 if(pIsChanged)
861 *pIsChanged = true;
863 if ( nY1 < nStartRow || nY2 > nEndRow )
865 if (nY1 < nStartRow) nY1=nStartRow;
866 if (nY2 > nEndRow) nY2=nEndRow;
867 SetPatternArea( nY1, nY2, rNewPattern, pDataArray );
868 Search( nStart, nPos );
870 else
872 if ( nCol != -1 )
874 // ensure attributing changes text-width of cell
876 const SfxItemSet& rNewSet = rNewPattern.getScPatternAttr()->GetItemSet();
877 const SfxItemSet& rOldSet = rOldPattern.getScPatternAttr()->GetItemSet();
879 bool bNumFormatChanged;
880 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
881 rNewSet, rOldSet ) )
883 aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
884 aAdrEnd .SetRow( mvData[nPos].nEndRow );
885 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
889 mvData[nPos].setCellAttributeHolder(rNewPattern);
890 if (Concat(nPos))
891 Search(nStart, nPos);
892 else
893 ++nPos;
896 else
898 nStart = mvData[nPos].nEndRow + 1;
899 ++nPos;
902 while (nStart <= nEndRow);
904 rDocument.SetStreamValid(nTab, false);
906 #if DEBUG_SC_TESTATTRARRAY
907 TestData();
908 #endif
911 void ScAttrArray::SetAttrEntries(std::vector<ScAttrEntry> && vNewData)
913 mvData = std::move(vNewData);
915 #ifdef DBG_UTIL
916 SCROW lastEndRow = -1;
917 for(const auto& entry : mvData)
918 { // Verify that the data is not corrupted.
919 assert(entry.nEndRow > lastEndRow);
920 lastEndRow = entry.nEndRow;
922 #endif
925 static void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource )
927 const SfxPoolItem* pNewItem;
928 const SfxPoolItem* pOldItem;
929 for (sal_uInt16 nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++)
931 // pMergeSet has no parent
932 SfxItemState eOldState = rMergeSet.GetItemState( nId, false, &pOldItem );
934 if ( eOldState == SfxItemState::DEFAULT )
936 SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
937 if ( eNewState == SfxItemState::SET )
939 if ( *pNewItem != rMergeSet.GetPool()->GetUserOrPoolDefaultItem(nId) )
940 rMergeSet.InvalidateItem( nId );
943 else if ( eOldState == SfxItemState::SET ) // Item set
945 SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
946 if ( eNewState == SfxItemState::SET )
948 if ( !SfxPoolItem::areSame(pNewItem, pOldItem) ) // Both pulled
949 rMergeSet.InvalidateItem( nId );
951 else // Default
953 if ( *pOldItem != rSource.GetPool()->GetUserOrPoolDefaultItem(nId) )
954 rMergeSet.InvalidateItem( nId );
957 // Dontcare remains Dontcare
961 void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow,
962 ScMergePatternState& rState, bool bDeep ) const
964 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
965 return;
967 SCSIZE nPos = 0;
968 SCROW nStart=0;
969 if ( !mvData.empty() && !Search( nStartRow, nPos ) )
971 OSL_FAIL("Search failure");
972 return;
977 // similar patterns must not be repeated
978 const ScPatternAttr* pPattern(&rDocument.getCellAttributeHelper().getDefaultCellAttribute());
979 if ( !mvData.empty() )
980 pPattern = mvData[nPos].getScPatternAttr();
982 if ( !ScPatternAttr::areSame(pPattern, rState.aOld1.getScPatternAttr())
983 && !ScPatternAttr::areSame(pPattern, rState.aOld2.getScPatternAttr()) )
985 const SfxItemSet& rThisSet = pPattern->GetItemSet();
986 if (rState.pItemSet)
988 rState.mbValidPatternId = false;
989 if (bDeep)
990 lcl_MergeDeep( *rState.pItemSet, rThisSet );
991 else
992 rState.pItemSet->MergeValues( rThisSet );
994 else
996 // first pattern - copied from parent
997 rState.pItemSet.emplace( *rThisSet.GetPool(), rThisSet.GetRanges() );
998 rState.pItemSet->Set( rThisSet, bDeep );
999 rState.mnPatternId = pPattern->GetPAKey();
1002 rState.aOld2 = rState.aOld1;
1003 rState.aOld1 = pPattern;
1006 if ( !mvData.empty() )
1007 nStart = mvData[nPos].nEndRow + 1;
1008 else
1009 nStart = rDocument.MaxRow() + 1;
1010 ++nPos;
1012 while (nStart <= nEndRow);
1015 // assemble border
1017 static bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
1018 sal_uInt8& rModified, const SvxBorderLine*& rpNew )
1020 if (rModified == SC_LINE_DONTCARE)
1021 return false; // don't go again
1023 if (rModified == SC_LINE_EMPTY)
1025 rModified = SC_LINE_SET;
1026 rpNew = pNewLine;
1027 return true; // initial value
1030 if (pOldLine == pNewLine)
1032 rpNew = pOldLine;
1033 return false;
1036 if (pOldLine && pNewLine)
1037 if (*pOldLine == *pNewLine)
1039 rpNew = pOldLine;
1040 return false;
1043 rModified = SC_LINE_DONTCARE;
1044 rpNew = nullptr;
1045 return true; // another line -> don't care
1048 static void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
1049 ScLineFlags& rFlags, const ScPatternAttr* pPattern,
1050 bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
1052 // right/bottom border set when connected together
1053 const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
1054 if ( rMerge.GetColMerge() == nDistRight + 1 )
1055 nDistRight = 0;
1056 if ( rMerge.GetRowMerge() == nDistBottom + 1 )
1057 nDistBottom = 0;
1059 const SvxBoxItem* pCellFrame = &pPattern->GetItem( ATTR_BORDER );
1060 const SvxBorderLine* pLeftAttr = pCellFrame->GetLeft();
1061 const SvxBorderLine* pRightAttr = pCellFrame->GetRight();
1062 const SvxBorderLine* pTopAttr = pCellFrame->GetTop();
1063 const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom();
1064 const SvxBorderLine* pNew;
1066 if (bTop)
1068 if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew ))
1069 pLineOuter->SetLine( pNew, SvxBoxItemLine::TOP );
1071 else
1073 if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew ))
1074 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
1077 if (nDistBottom == 0)
1079 if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew ))
1080 pLineOuter->SetLine( pNew, SvxBoxItemLine::BOTTOM );
1082 else
1084 if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew ))
1085 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
1088 if (bLeft)
1090 if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew ))
1091 pLineOuter->SetLine( pNew, SvxBoxItemLine::LEFT );
1093 else
1095 if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew ))
1096 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
1099 if (nDistRight == 0)
1101 if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew ))
1102 pLineOuter->SetLine( pNew, SvxBoxItemLine::RIGHT );
1104 else
1106 if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew ))
1107 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
1111 void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
1112 ScLineFlags& rFlags,
1113 SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight ) const
1115 const ScPatternAttr* pPattern;
1117 if (nStartRow == nEndRow)
1119 pPattern = GetPattern( nStartRow );
1120 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true, 0 );
1122 else if ( !mvData.empty() ) // non-default pattern
1124 pPattern = GetPattern( nStartRow );
1125 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true,
1126 nEndRow-nStartRow );
1128 SCSIZE nStartIndex;
1129 SCSIZE nEndIndex;
1130 Search( nStartRow+1, nStartIndex );
1131 Search( nEndRow-1, nEndIndex );
1132 for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1134 pPattern = mvData[i].getScPatternAttr();
1135 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false,
1136 nEndRow - std::min( mvData[i].nEndRow, static_cast<SCROW>(nEndRow-1) ) );
1137 // nDistBottom here always > 0
1140 pPattern = GetPattern( nEndRow );
1141 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, 0 );
1143 else
1145 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, &rDocument.getCellAttributeHelper().getDefaultCellAttribute(), bLeft, nDistRight, true, 0 );
1149 // apply border
1151 // ApplyFrame - on an entry into the array
1153 bool ScAttrArray::ApplyFrame( const SvxBoxItem& rBoxItem,
1154 const SvxBoxInfoItem* pBoxInfoItem,
1155 SCROW nStartRow, SCROW nEndRow,
1156 bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
1158 OSL_ENSURE( pBoxInfoItem, "Missing line attributes!" );
1160 const ScPatternAttr* pPattern = GetPattern( nStartRow );
1161 const SvxBoxItem* pOldFrame = &pPattern->GetItem( ATTR_BORDER );
1163 // right/bottom border set when connected together
1164 const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
1165 if ( rMerge.GetColMerge() == nDistRight + 1 )
1166 nDistRight = 0;
1167 if ( rMerge.GetRowMerge() == nDistBottom + 1 )
1168 nDistBottom = 0;
1170 SvxBoxItem aNewFrame( *pOldFrame );
1171 bool bRTL=rDocument.IsLayoutRTL(nTab);
1172 // fdo#37464 check if the sheet are RTL then replace right <=> left
1173 if (bRTL)
1175 if( bLeft && nDistRight==0)
1177 if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
1178 aNewFrame.SetLine( rBoxItem.GetLeft(), SvxBoxItemLine::RIGHT );
1179 if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
1180 aNewFrame.SetLine( rBoxItem.GetRight(), SvxBoxItemLine::LEFT );
1182 else
1184 if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1185 aNewFrame.SetLine( (nDistRight==0) ? rBoxItem.GetLeft() : pBoxInfoItem->GetVert(),
1186 SvxBoxItemLine::RIGHT );
1187 if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1188 aNewFrame.SetLine( bLeft ? rBoxItem.GetRight() : pBoxInfoItem->GetVert(),
1189 SvxBoxItemLine::LEFT );
1192 else
1194 if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1195 aNewFrame.SetLine( bLeft ? rBoxItem.GetLeft() : pBoxInfoItem->GetVert(),
1196 SvxBoxItemLine::LEFT );
1197 if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1198 aNewFrame.SetLine( (nDistRight==0) ? rBoxItem.GetRight() : pBoxInfoItem->GetVert(),
1199 SvxBoxItemLine::RIGHT );
1201 if ( bTop ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
1202 aNewFrame.SetLine( bTop ? rBoxItem.GetTop() : pBoxInfoItem->GetHori(),
1203 SvxBoxItemLine::TOP );
1204 if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
1205 aNewFrame.SetLine( (nDistBottom==0) ? rBoxItem.GetBottom() : pBoxInfoItem->GetHori(),
1206 SvxBoxItemLine::BOTTOM );
1208 if (aNewFrame == *pOldFrame)
1210 // nothing to do
1211 return false;
1213 else
1215 ScItemPoolCache aCache( rDocument.getCellAttributeHelper(), aNewFrame );
1216 ApplyCacheArea( nStartRow, nEndRow, aCache );
1218 return true;
1222 void ScAttrArray::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
1223 SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight)
1225 SetDefaultIfNotInit();
1226 if (nStartRow == nEndRow)
1227 ApplyFrame(rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0);
1228 else
1230 ApplyFrame(rLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight,
1231 true, nEndRow-nStartRow);
1233 if ( nEndRow > nStartRow+1 ) // inner part available?
1235 SCSIZE nStartIndex;
1236 SCSIZE nEndIndex;
1237 Search( nStartRow+1, nStartIndex );
1238 Search( nEndRow-1, nEndIndex );
1239 SCROW nTmpStart = nStartRow+1;
1240 SCROW nTmpEnd;
1241 for (SCSIZE i=nStartIndex; i<=nEndIndex;)
1243 nTmpEnd = std::min( static_cast<SCROW>(nEndRow-1), mvData[i].nEndRow );
1244 bool bChanged = ApplyFrame(rLineOuter, pLineInner, nTmpStart, nTmpEnd,
1245 bLeft, nDistRight, false, nEndRow - nTmpEnd);
1246 nTmpStart = nTmpEnd+1;
1247 if (bChanged)
1249 Search(nTmpStart, i);
1250 Search(nEndRow-1, nEndIndex);
1252 else
1253 i++;
1257 ApplyFrame(rLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, false, 0);
1261 bool ScAttrArray::HasAttrib_Impl(const ScPatternAttr* pPattern, HasAttrFlags nMask, SCROW nRow1, SCROW nRow2, SCSIZE i) const
1263 bool bFound = false;
1264 if ( nMask & HasAttrFlags::Merged )
1266 const ScMergeAttr* pMerge = &pPattern->GetItem( ATTR_MERGE );
1267 if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
1268 bFound = true;
1270 if ( nMask & ( HasAttrFlags::Overlapped | HasAttrFlags::NotOverlapped | HasAttrFlags::AutoFilter ) )
1272 const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem( ATTR_MERGE_FLAG );
1273 if ( (nMask & HasAttrFlags::Overlapped) && pMergeFlag->IsOverlapped() )
1274 bFound = true;
1275 if ( (nMask & HasAttrFlags::NotOverlapped) && !pMergeFlag->IsOverlapped() )
1276 bFound = true;
1277 if ( (nMask & HasAttrFlags::AutoFilter) && pMergeFlag->HasAutoFilter() )
1278 bFound = true;
1280 if ( nMask & HasAttrFlags::Lines )
1282 const SvxBoxItem* pBox = &pPattern->GetItem( ATTR_BORDER );
1283 if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() )
1284 bFound = true;
1286 if ( nMask & HasAttrFlags::Shadow )
1288 const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
1289 if ( pShadow->GetLocation() != SvxShadowLocation::NONE )
1290 bFound = true;
1292 if ( nMask & HasAttrFlags::Conditional )
1294 if ( !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty())
1295 bFound = true;
1297 if ( nMask & HasAttrFlags::Protected )
1299 const ScProtectionAttr* pProtect = &pPattern->GetItem( ATTR_PROTECTION );
1300 bool bFoundTemp = false;
1301 if ( pProtect->GetProtection() || pProtect->GetHideCell() )
1302 bFoundTemp = true;
1304 bool bContainsCondFormat = !mvData.empty() &&
1305 !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty();
1306 if ( bContainsCondFormat && nCol != -1 ) // rDocument.GetCondResult() is valid only for real columns.
1308 SCROW nRowStartCond = std::max<SCROW>( nRow1, i ? mvData[i-1].nEndRow + 1: 0 );
1309 SCROW nRowEndCond = std::min<SCROW>( nRow2, mvData[i].nEndRow );
1310 bool bFoundCond = false;
1311 for(SCROW nRowCond = nRowStartCond; nRowCond <= nRowEndCond && !bFoundCond; ++nRowCond)
1313 const SfxItemSet* pSet = rDocument.GetCondResult( nCol, nRowCond, nTab );
1315 const ScProtectionAttr* pCondProtect;
1316 if( pSet && (pCondProtect = pSet->GetItemIfSet( ATTR_PROTECTION )) )
1318 if( pCondProtect->GetProtection() || pCondProtect->GetHideCell() )
1319 bFoundCond = true;
1320 else
1321 break;
1323 else
1325 // well it is not true that we found one
1326 // but existing one + cell where conditional
1327 // formatting does not remove it
1328 // => we should use the existing protection setting
1329 bFoundCond = bFoundTemp;
1332 bFoundTemp = bFoundCond;
1335 if(bFoundTemp)
1336 bFound = true;
1338 if ( nMask & HasAttrFlags::Rotate )
1340 const ScRotateValueItem* pRotate = &pPattern->GetItem( ATTR_ROTATE_VALUE );
1341 // 90 or 270 degrees is former SvxOrientationItem - only look for other values
1342 // (see ScPatternAttr::GetCellOrientation)
1343 Degree100 nAngle = pRotate->GetValue();
1344 if ( nAngle && nAngle != 9000_deg100 && nAngle != 27000_deg100 )
1345 bFound = true;
1347 if ( nMask & HasAttrFlags::NeedHeight )
1349 if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
1350 bFound = true;
1351 else if (pPattern->GetItem( ATTR_LINEBREAK ).GetValue())
1352 bFound = true;
1353 else if (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block)
1354 bFound = true;
1356 else if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
1357 bFound = true;
1358 else if (pPattern->GetItem( ATTR_ROTATE_VALUE ).GetValue())
1359 bFound = true;
1361 if ( nMask & ( HasAttrFlags::ShadowRight | HasAttrFlags::ShadowDown ) )
1363 const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
1364 SvxShadowLocation eLoc = pShadow->GetLocation();
1365 if ( nMask & HasAttrFlags::ShadowRight )
1366 if ( eLoc == SvxShadowLocation::TopRight || eLoc == SvxShadowLocation::BottomRight )
1367 bFound = true;
1368 if ( nMask & HasAttrFlags::ShadowDown )
1369 if ( eLoc == SvxShadowLocation::BottomLeft || eLoc == SvxShadowLocation::BottomRight )
1370 bFound = true;
1372 if ( nMask & HasAttrFlags::RightOrCenter )
1374 // called only if the sheet is LTR, so physical=logical alignment can be assumed
1375 SvxCellHorJustify eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
1376 if ( eHorJust == SvxCellHorJustify::Right || eHorJust == SvxCellHorJustify::Center )
1377 bFound = true;
1380 return bFound;
1383 // Test if field contains specific attribute
1384 bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) const
1386 if (mvData.empty())
1388 return HasAttrib_Impl(&rDocument.getCellAttributeHelper().getDefaultCellAttribute(), nMask, 0, rDocument.MaxRow(), 0);
1391 SCSIZE nStartIndex;
1392 SCSIZE nEndIndex;
1393 Search( nRow1, nStartIndex );
1394 if (nRow1 != nRow2)
1395 Search( nRow2, nEndIndex, /*hint*/nStartIndex );
1396 else
1397 nEndIndex = nStartIndex;
1398 bool bFound = false;
1400 for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++)
1402 const ScPatternAttr* pPattern = mvData[i].getScPatternAttr();
1403 bFound = HasAttrib_Impl(pPattern, nMask, nRow1, nRow2, i);
1406 return bFound;
1409 bool ScAttrArray::HasAttrib( SCROW nRow, HasAttrFlags nMask, SCROW* nStartRow, SCROW* nEndRow ) const
1411 if (mvData.empty())
1413 if( nStartRow )
1414 *nStartRow = 0;
1415 if( nEndRow )
1416 *nEndRow = rDocument.MaxRow();
1417 return HasAttrib_Impl(&rDocument.getCellAttributeHelper().getDefaultCellAttribute(), nMask, 0, rDocument.MaxRow(), 0);
1420 SCSIZE nIndex;
1421 Search( nRow, nIndex );
1422 if( nStartRow )
1423 *nStartRow = nIndex > 0 ? mvData[nIndex-1].nEndRow+1 : 0;
1424 if( nEndRow )
1425 *nEndRow = mvData[nIndex].nEndRow;
1426 const ScPatternAttr* pPattern = mvData[nIndex].getScPatternAttr();
1427 return HasAttrib_Impl(pPattern, nMask, nRow, nRow, nIndex);
1430 bool ScAttrArray::IsMerged( SCROW nRow ) const
1432 if ( !mvData.empty() )
1434 SCSIZE nIndex;
1435 Search(nRow, nIndex);
1436 const ScMergeAttr& rItem = mvData[nIndex].getScPatternAttr()->GetItem(ATTR_MERGE);
1438 return rItem.IsMerged();
1441 return rDocument.getCellAttributeHelper().getDefaultCellAttribute().GetItem(ATTR_MERGE).IsMerged();
1445 * Area around any given summaries expand and adapt any MergeFlag (bRefresh)
1447 bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
1448 SCCOL& rPaintCol, SCROW& rPaintRow,
1449 bool bRefresh )
1451 assert( nCol != -1 );
1452 SetDefaultIfNotInit();
1453 const ScPatternAttr* pPattern;
1454 const ScMergeAttr* pItem;
1455 SCSIZE nStartIndex;
1456 SCSIZE nEndIndex;
1457 Search( nStartRow, nStartIndex );
1458 Search( nEndRow, nEndIndex );
1459 bool bFound = false;
1461 for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1463 pPattern = mvData[i].getScPatternAttr();
1464 pItem = &pPattern->GetItem( ATTR_MERGE );
1465 SCCOL nCountX = pItem->GetColMerge();
1466 SCROW nCountY = pItem->GetRowMerge();
1467 if (nCountX>1 || nCountY>1)
1469 SCROW nThisRow = (i>0) ? mvData[i-1].nEndRow+1 : 0;
1470 SCCOL nMergeEndCol = nThisCol + nCountX - 1;
1471 SCROW nMergeEndRow = nThisRow + nCountY - 1;
1472 if (nMergeEndCol > rPaintCol && nMergeEndCol <= rDocument.MaxCol())
1473 rPaintCol = nMergeEndCol;
1474 if (nMergeEndRow > rPaintRow && nMergeEndRow <= rDocument.MaxRow())
1475 rPaintRow = nMergeEndRow;
1476 bFound = true;
1478 if (bRefresh)
1480 if ( nMergeEndCol > nThisCol )
1481 rDocument.ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, mvData[i].nEndRow,
1482 nTab, ScMF::Hor );
1483 if ( nMergeEndRow > nThisRow )
1484 rDocument.ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow,
1485 nTab, ScMF::Ver );
1486 if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow )
1487 rDocument.ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow,
1488 nTab, ScMF::Hor | ScMF::Ver );
1490 Search( nThisRow, i ); // Data changed
1491 Search( nStartRow, nStartIndex );
1492 Search( nEndRow, nEndIndex );
1497 return bFound;
1500 void ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
1502 assert( nCol != -1 );
1503 SetDefaultIfNotInit();
1504 const ScPatternAttr* pPattern;
1505 const ScMergeAttr* pItem;
1506 SCSIZE nIndex;
1508 Search( nStartRow, nIndex );
1509 SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1510 if (nThisStart < nStartRow)
1511 nThisStart = nStartRow;
1513 while ( nThisStart <= nEndRow )
1515 SCROW nThisEnd = mvData[nIndex].nEndRow;
1516 if (nThisEnd > nEndRow)
1517 nThisEnd = nEndRow;
1519 pPattern = mvData[nIndex].getScPatternAttr();
1520 pItem = &pPattern->GetItem( ATTR_MERGE );
1521 SCCOL nCountX = pItem->GetColMerge();
1522 SCROW nCountY = pItem->GetRowMerge();
1523 if (nCountX>1 || nCountY>1)
1525 const ScMergeAttr* pAttr = &rDocument.GetPool()->GetUserOrPoolDefaultItem( ATTR_MERGE );
1526 const ScMergeFlagAttr* pFlagAttr = &rDocument.GetPool()->GetUserOrPoolDefaultItem( ATTR_MERGE_FLAG );
1528 OSL_ENSURE( nCountY==1 || nThisStart==nThisEnd, "What's up?" );
1530 SCCOL nThisCol = nCol;
1531 SCCOL nMergeEndCol = nThisCol + nCountX - 1;
1532 SCROW nMergeEndRow = nThisEnd + nCountY - 1;
1534 // ApplyAttr for areas
1535 for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++)
1536 rDocument.ApplyAttr( nThisCol, nThisRow, nTab, *pAttr );
1538 ScPatternAttr aNewPattern( rDocument.getCellAttributeHelper() );
1539 SfxItemSet* pSet = &aNewPattern.GetItemSet();
1540 pSet->Put( *pFlagAttr );
1541 rDocument.ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow,
1542 nTab, aNewPattern );
1544 Search( nThisEnd, nIndex ); // data changed
1547 ++nIndex;
1548 if ( nIndex < mvData.size() )
1549 nThisStart = mvData[nIndex-1].nEndRow+1;
1550 else
1551 nThisStart = rDocument.MaxRow()+1; // End
1555 void ScAttrArray::SetPatternAreaSafe(SCROW nStartRow, SCROW nEndRow, const CellAttributeHolder& rWantedPattern)
1557 SetDefaultIfNotInit();
1558 const ScMergeFlagAttr* pItem;
1560 SCSIZE nIndex;
1561 SCROW nRow;
1562 SCROW nThisRow;
1564 Search( nStartRow, nIndex );
1565 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1566 while ( nThisRow <= nEndRow )
1568 const CellAttributeHolder& rOldPattern(mvData[nIndex].getCellAttributeHolder());
1569 if (!CellAttributeHolder::areSame(&rOldPattern, &rWantedPattern)) // FIXME: else-branch?
1571 if (nThisRow < nStartRow) nThisRow = nStartRow;
1572 nRow = mvData[nIndex].nEndRow;
1573 SCROW nAttrRow = std::min( nRow, nEndRow );
1574 pItem = &rOldPattern.getScPatternAttr()->GetItem( ATTR_MERGE_FLAG );
1576 if (pItem->IsOverlapped() || pItem->HasAutoFilter())
1578 // default-constructing a ScPatternAttr for DeleteArea doesn't work
1579 // because it would have no cell style information.
1580 // Instead, the document's getCellAttributeHelper().getDefaultCellAttribute() is copied. Since it is passed as
1581 // pWantedPattern, no special treatment of default is needed here anymore.
1582 ScPatternAttr* pNewPattern(new ScPatternAttr(*rWantedPattern.getScPatternAttr()));
1583 pNewPattern->GetItemSet().Put( *pItem );
1584 SetPatternArea( nThisRow, nAttrRow, CellAttributeHolder(pNewPattern, true) );
1586 else
1588 SetPatternArea(nThisRow, nAttrRow, rWantedPattern);
1591 Search( nThisRow, nIndex ); // data changed
1594 ++nIndex;
1595 nThisRow = mvData[nIndex-1].nEndRow+1;
1599 bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
1601 SetDefaultIfNotInit();
1602 const ScPatternAttr* pOldPattern;
1604 ScMF nOldValue;
1605 SCSIZE nIndex;
1606 SCROW nRow;
1607 SCROW nThisRow;
1608 bool bChanged = false;
1610 Search( nStartRow, nIndex );
1611 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1612 if (nThisRow < nStartRow) nThisRow = nStartRow;
1614 while ( nThisRow <= nEndRow )
1616 pOldPattern = mvData[nIndex].getScPatternAttr();
1617 nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
1618 if ( (nOldValue | nFlags) != nOldValue )
1620 nRow = mvData[nIndex].nEndRow;
1621 SCROW nAttrRow = std::min( nRow, nEndRow );
1622 ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
1623 pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) );
1624 SetPatternArea( nThisRow, nAttrRow, CellAttributeHolder(pNewPattern, true) );
1625 Search( nThisRow, nIndex ); // data changed
1626 bChanged = true;
1629 ++nIndex;
1630 nThisRow = mvData[nIndex-1].nEndRow+1;
1633 return bChanged;
1636 bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
1638 SetDefaultIfNotInit();
1639 const ScPatternAttr* pOldPattern;
1641 ScMF nOldValue;
1642 SCSIZE nIndex;
1643 SCROW nRow;
1644 SCROW nThisRow;
1645 bool bChanged = false;
1647 Search( nStartRow, nIndex );
1648 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1649 if (nThisRow < nStartRow) nThisRow = nStartRow;
1651 while ( nThisRow <= nEndRow )
1653 pOldPattern = mvData[nIndex].getScPatternAttr();
1654 nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
1655 if ( (nOldValue & ~nFlags) != nOldValue )
1657 nRow = mvData[nIndex].nEndRow;
1658 SCROW nAttrRow = std::min( nRow, nEndRow );
1659 ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
1660 pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) );
1661 SetPatternArea( nThisRow, nAttrRow, CellAttributeHolder(pNewPattern, true) );
1662 Search( nThisRow, nIndex ); // data changed
1663 bChanged = true;
1666 ++nIndex;
1667 nThisRow = mvData[nIndex-1].nEndRow+1;
1670 return bChanged;
1673 void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
1675 SetDefaultIfNotInit();
1676 SCSIZE nIndex;
1677 SCROW nRow;
1678 SCROW nThisRow;
1680 Search( nStartRow, nIndex );
1681 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1682 if (nThisRow < nStartRow) nThisRow = nStartRow;
1684 while ( nThisRow <= nEndRow )
1686 const ScPatternAttr* pOldPattern = mvData[nIndex].getScPatternAttr();
1687 if ( pOldPattern->HasItemsSet( pWhich ) )
1689 ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
1690 pNewPattern->ClearItems( pWhich );
1692 nRow = mvData[nIndex].nEndRow;
1693 SCROW nAttrRow = std::min( nRow, nEndRow );
1694 SetPatternArea( nThisRow, nAttrRow, CellAttributeHolder(pNewPattern, true) );
1695 Search( nThisRow, nIndex ); // data changed
1698 ++nIndex;
1699 nThisRow = mvData[nIndex-1].nEndRow+1;
1703 void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, bool bIncrement )
1705 SetDefaultIfNotInit();
1706 SCSIZE nIndex;
1707 Search( nStartRow, nIndex );
1708 SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1709 if (nThisStart < nStartRow) nThisStart = nStartRow;
1711 while ( nThisStart <= nEndRow )
1713 const ScPatternAttr* pOldPattern = mvData[nIndex].getScPatternAttr();
1714 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
1715 const SvxHorJustifyItem* pItem;
1717 bool bNeedJust = !( pItem = rOldSet.GetItemIfSet( ATTR_HOR_JUSTIFY, false ) )
1718 || (pItem->GetValue() != SvxCellHorJustify::Left &&
1719 pItem->GetValue() != SvxCellHorJustify::Right );
1720 sal_uInt16 nOldValue = rOldSet.Get( ATTR_INDENT ).GetValue();
1721 sal_uInt16 nNewValue = nOldValue;
1722 // To keep Increment indent from running outside the cell1659
1723 tools::Long nColWidth = static_cast<tools::Long>(
1724 rDocument.GetColWidth(nCol == -1 ? rDocument.MaxCol() : nCol,nTab));
1725 if ( bIncrement )
1727 if ( nNewValue < nColWidth-SC_INDENT_STEP )
1729 nNewValue += SC_INDENT_STEP;
1730 if ( nNewValue > nColWidth-SC_INDENT_STEP )
1731 nNewValue = nColWidth-SC_INDENT_STEP;
1734 else
1736 if ( nNewValue > 0 )
1738 if ( nNewValue > SC_INDENT_STEP )
1739 nNewValue -= SC_INDENT_STEP;
1740 else
1741 nNewValue = 0;
1745 if ( bNeedJust || nNewValue != nOldValue )
1747 SCROW nThisEnd = mvData[nIndex].nEndRow;
1748 SCROW nAttrRow = std::min( nThisEnd, nEndRow );
1749 ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
1750 pNewPattern->GetItemSet().Put( ScIndentItem( nNewValue ) );
1751 if ( bNeedJust )
1752 pNewPattern->GetItemSet().Put(
1753 SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
1754 SetPatternArea( nThisStart, nAttrRow, CellAttributeHolder(pNewPattern, true) );
1756 nThisStart = nThisEnd + 1;
1757 Search( nThisStart, nIndex ); // data changed
1759 else
1761 nThisStart = mvData[nIndex].nEndRow + 1;
1762 ++nIndex;
1767 SCROW ScAttrArray::GetNextUnprotected( SCROW nRow, bool bUp ) const
1769 tools::Long nRet = nRow;
1770 if (rDocument.ValidRow(nRow))
1772 if ( mvData.empty() )
1774 if ( bUp )
1775 return -1;
1776 else
1777 return rDocument.MaxRow()+1;
1780 SCSIZE nIndex;
1781 Search(nRow, nIndex);
1782 while (mvData[nIndex].getScPatternAttr()->GetItem(ATTR_PROTECTION).GetProtection())
1784 if (bUp)
1786 if (nIndex==0)
1787 return -1; // not found
1788 --nIndex;
1789 nRet = mvData[nIndex].nEndRow;
1791 else
1793 nRet = mvData[nIndex].nEndRow+1;
1794 ++nIndex;
1795 if (nIndex >= mvData.size())
1796 return rDocument.MaxRow()+1; // not found
1800 return nRet;
1803 void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
1805 SetDefaultIfNotInit();
1806 SCROW nStart = 0;
1807 SCSIZE nPos = 0;
1808 while (nPos < mvData.size())
1810 bool bIterateNext = true;
1812 SCROW nEnd = mvData[nPos].nEndRow;
1813 if (mvData[nPos].getScPatternAttr()->GetStyleSheet() == pStyleSheet)
1815 rUsedRows.setTrue(nStart, nEnd);
1817 if (bReset)
1819 ScPatternAttr* pNewPattern(new ScPatternAttr(*mvData[nPos].getScPatternAttr()));
1820 pNewPattern->SetStyleSheet( static_cast<ScStyleSheet*>(
1821 rDocument.GetStyleSheetPool()->
1822 Find( ScResId(STR_STYLENAME_STANDARD),
1823 SfxStyleFamily::Para,
1824 SfxStyleSearchBits::Auto | SfxStyleSearchBits::ScStandard ) ) );
1825 mvData[nPos].setScPatternAttr(pNewPattern, true);
1827 if (Concat(nPos))
1829 Search(nStart, nPos);
1830 bIterateNext = false; // because ++ at end otherwise
1834 nStart = nEnd + 1;
1836 if (bIterateNext)
1837 ++nPos;
1841 bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
1843 if ( mvData.empty() )
1845 const ScStyleSheet* pStyle = rDocument.getCellAttributeHelper().getDefaultCellAttribute().GetStyleSheet();
1846 if ( pStyle )
1848 pStyle->SetUsage( ScStyleSheet::Usage::USED );
1849 if ( pStyle == &rStyle )
1850 return true;
1852 return false;
1855 bool bIsUsed = false;
1856 SCSIZE nPos = 0;
1858 while ( nPos < mvData.size() )
1860 const ScStyleSheet* pStyle = mvData[nPos].getScPatternAttr()->GetStyleSheet();
1861 if ( pStyle )
1863 pStyle->SetUsage( ScStyleSheet::Usage::USED );
1864 if ( pStyle == &rStyle )
1866 bIsUsed = true;
1869 nPos++;
1872 return bIsUsed;
1875 bool ScAttrArray::IsEmpty() const
1877 if ( mvData.empty() )
1878 return true;
1880 if (mvData.size() == 1)
1882 return mvData[0].getScPatternAttr()->isDefault();
1884 else
1885 return false;
1888 bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
1890 if ( mvData.empty() )
1891 return false;
1893 bool bFound = false;
1894 SCSIZE nStart = 0;
1896 // Skip first entry if more than 1 row.
1897 // Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr.
1899 SCSIZE nVisStart = 1;
1900 while ( nVisStart < mvData.size() && mvData[nVisStart].getScPatternAttr()->IsVisibleEqual(*mvData[nVisStart-1].getScPatternAttr()) )
1901 ++nVisStart;
1902 if ( nVisStart >= mvData.size() || mvData[nVisStart-1].nEndRow > 0 ) // more than 1 row?
1903 nStart = nVisStart;
1905 while ( nStart < mvData.size() && !bFound )
1907 if ( mvData[nStart].getScPatternAttr()->IsVisible() )
1909 rFirstRow = nStart ? ( mvData[nStart-1].nEndRow + 1 ) : 0;
1910 bFound = true;
1912 else
1913 ++nStart;
1916 return bFound;
1919 // size (rows) of a range of attributes after cell content where the search is stopped
1920 // (more than a default page size, 2*42 because it's as good as any number)
1922 const SCROW SC_VISATTR_STOP = 84;
1924 bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData, bool bSkipEmpty ) const
1926 if ( mvData.empty() )
1928 rLastRow = nLastData;
1929 return false;
1932 // #i30830# changed behavior:
1933 // ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows
1934 // below the last content cell
1936 if ( nLastData == rDocument.MaxRow() )
1938 rLastRow = rDocument.MaxRow(); // can't look for attributes below rDocument.MaxRow()
1939 return true;
1942 // Quick check: last data row in or immediately preceding a run that is the
1943 // last attribution down to the end, e.g. default style or column style.
1944 SCSIZE nPos = mvData.size() - 1;
1945 SCROW nStartRow = (nPos ? mvData[nPos-1].nEndRow + 1 : 0);
1946 if (nStartRow <= nLastData + 1)
1948 // Ignore here a few rows if data happens to end within
1949 // SC_VISATTR_STOP rows before rDocument.MaxRow().
1950 rLastRow = nLastData;
1951 return false;
1953 // tdf#93315: If "Suppress output of empty pages" in Calc Options is not checked, show empty
1954 // (containing only empty data cells) page in the document
1955 bool bFound = false;
1956 if (bSkipEmpty)
1958 Search( nLastData, nPos );
1959 while ( nPos < mvData.size() )
1961 // find range of visually equal formats
1962 SCSIZE nEndPos = nPos;
1963 while ( nEndPos < mvData.size()-1 &&
1964 mvData[nEndPos].getScPatternAttr()->IsVisibleEqual(*mvData[nEndPos+1].getScPatternAttr()))
1965 ++nEndPos;
1966 SCROW nAttrStartRow = ( nPos > 0 ) ? ( mvData[nPos-1].nEndRow + 1) : 0;
1967 if ( nAttrStartRow <= nLastData )
1968 nAttrStartRow = nLastData + 1;
1969 SCROW nAttrSize = mvData[nEndPos].nEndRow + 1 - nAttrStartRow;
1970 if ( nAttrSize >= SC_VISATTR_STOP )
1971 break; // while, ignore this range and below
1972 else if ( mvData[nEndPos].getScPatternAttr()->IsVisible() )
1974 rLastRow = mvData[nEndPos].nEndRow;
1975 bFound = true;
1977 nPos = nEndPos + 1;
1980 else
1982 if ((nPos > 0 && mvData[nPos-1].getScPatternAttr()->IsVisible())
1983 || (nPos > 1 && !mvData[nPos-1].getScPatternAttr()->IsVisibleEqual(*mvData[nPos-2].getScPatternAttr())))
1985 rLastRow = mvData[nPos-1].nEndRow;
1986 return true;
1988 rLastRow = nLastData;
1990 return bFound;
1993 bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
1995 if ( mvData.empty() )
1996 return rDocument.getCellAttributeHelper().getDefaultCellAttribute().IsVisible();
1998 SCSIZE nIndex;
1999 Search( nStartRow, nIndex );
2000 SCROW nThisStart = nStartRow;
2001 bool bFound = false;
2002 while ( nIndex < mvData.size() && nThisStart <= nEndRow && !bFound )
2004 if ( mvData[nIndex].getScPatternAttr()->IsVisible() )
2005 bFound = true;
2007 nThisStart = mvData[nIndex].nEndRow + 1;
2008 ++nIndex;
2011 return bFound;
2014 bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
2015 SCROW nStartRow, SCROW nEndRow ) const
2017 if ( mvData.empty() && rOther.mvData.empty() )
2019 const ScPatternAttr* pDefPattern1(&rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2020 const ScPatternAttr* pDefPattern2(&rOther.rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2021 return ( ScPatternAttr::areSame(pDefPattern1, pDefPattern2) || pDefPattern1->IsVisibleEqual( *pDefPattern2 ) );
2025 const ScAttrArray* pNonDefault = nullptr;
2026 const ScPatternAttr* pDefPattern = nullptr;
2027 bool bDefNonDefCase = false;
2028 if ( mvData.empty() && !rOther.mvData.empty() )
2030 pNonDefault = &rOther;
2031 pDefPattern = &rDocument.getCellAttributeHelper().getDefaultCellAttribute();
2032 bDefNonDefCase = true;
2034 else if ( !mvData.empty() && rOther.mvData.empty() )
2036 pNonDefault = this;
2037 pDefPattern = &rOther.rDocument.getCellAttributeHelper().getDefaultCellAttribute();
2038 bDefNonDefCase = true;
2041 if ( bDefNonDefCase )
2043 bool bEqual = true;
2044 SCSIZE nPos = 0;
2045 if ( nStartRow > 0 )
2046 pNonDefault->Search( nStartRow, nPos );
2048 while ( nPos < pNonDefault->Count() && bEqual )
2050 const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].getScPatternAttr();
2051 bEqual = ScPatternAttr::areSame(pNonDefPattern, pDefPattern) || pNonDefPattern->IsVisibleEqual( *pDefPattern );
2053 if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
2054 ++nPos;
2056 return bEqual;
2060 bool bEqual = true;
2061 SCSIZE nThisPos = 0;
2062 SCSIZE nOtherPos = 0;
2063 if ( nStartRow > 0 )
2065 Search( nStartRow, nThisPos );
2066 rOther.Search( nStartRow, nOtherPos );
2069 while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
2071 SCROW nThisRow = mvData[nThisPos].nEndRow;
2072 SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
2073 const ScPatternAttr* pThisPattern = mvData[nThisPos].getScPatternAttr();
2074 const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].getScPatternAttr();
2075 bEqual = ( ScPatternAttr::areSame(pThisPattern, pOtherPattern) || pThisPattern->IsVisibleEqual(*pOtherPattern) );
2077 if ( nThisRow >= nOtherRow )
2079 if ( nOtherRow >= nEndRow ) break;
2080 ++nOtherPos;
2082 if ( nThisRow <= nOtherRow )
2084 if ( nThisRow >= nEndRow ) break;
2085 ++nThisPos;
2089 return bEqual;
2092 bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
2094 // summarised with IsVisibleEqual
2095 if ( mvData.empty() && rOther.mvData.empty() )
2097 const ScPatternAttr* pDefPattern1(&rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2098 const ScPatternAttr* pDefPattern2(&rOther.rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2099 return ScPatternAttr::areSame(pDefPattern1, pDefPattern2);
2103 const ScAttrArray* pNonDefault = nullptr;
2104 const ScPatternAttr* pDefPattern = nullptr;
2105 bool bDefNonDefCase = false;
2106 if ( mvData.empty() && !rOther.mvData.empty() )
2108 pNonDefault = &rOther;
2109 pDefPattern = &rDocument.getCellAttributeHelper().getDefaultCellAttribute();
2110 bDefNonDefCase = true;
2112 else if ( !mvData.empty() && rOther.mvData.empty() )
2114 pNonDefault = this;
2115 pDefPattern = &rOther.rDocument.getCellAttributeHelper().getDefaultCellAttribute();
2116 bDefNonDefCase = true;
2119 if ( bDefNonDefCase )
2121 bool bEqual = true;
2122 SCSIZE nPos = 0;
2123 if ( nStartRow > 0 )
2124 pNonDefault->Search( nStartRow, nPos );
2126 while ( nPos < pNonDefault->Count() && bEqual )
2128 const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].getScPatternAttr();
2129 bEqual = ScPatternAttr::areSame( pNonDefPattern, pDefPattern );
2131 if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
2132 ++nPos;
2134 return bEqual;
2138 bool bEqual = true;
2139 SCSIZE nThisPos = 0;
2140 SCSIZE nOtherPos = 0;
2141 if ( nStartRow > 0 )
2143 Search( nStartRow, nThisPos );
2144 rOther.Search( nStartRow, nOtherPos );
2147 while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
2149 SCROW nThisRow = mvData[nThisPos].nEndRow;
2150 SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
2151 const ScPatternAttr* pThisPattern = mvData[nThisPos].getScPatternAttr();
2152 const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].getScPatternAttr();
2153 bEqual = ScPatternAttr::areSame( pThisPattern, pOtherPattern );
2155 if ( nThisRow >= nOtherRow )
2157 if ( nOtherRow >= nEndRow ) break;
2158 ++nOtherPos;
2160 if ( nThisRow <= nOtherRow )
2162 if ( nThisRow >= nEndRow ) break;
2163 ++nThisPos;
2167 return bEqual;
2170 bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
2172 // Horizontal aggregate are not allowed to be moved out; if whole summary,
2173 // here is not recognized
2175 bool bTest = true;
2176 if (!IsEmpty())
2178 SCSIZE nIndex = 0;
2179 if ( nStartRow > 0 )
2180 Search( nStartRow, nIndex );
2182 for ( ; nIndex < mvData.size(); nIndex++ )
2184 if ( mvData[nIndex].getScPatternAttr()->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped() )
2186 bTest = false; // may not be pushed out
2187 break;
2189 if ( mvData[nIndex].nEndRow >= nEndRow ) // end of range
2190 break;
2193 return bTest;
2196 bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
2198 // if 1st row pushed out is vertically overlapped, summary would be broken
2200 // rDocument.MaxRow() + 1 - nSize = 1st row pushed out
2202 if ( mvData.empty() )
2203 return !rDocument.getCellAttributeHelper().getDefaultCellAttribute().GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
2205 SCSIZE nFirstLost = mvData.size()-1;
2206 while ( nFirstLost && mvData[nFirstLost-1].nEndRow >= sal::static_int_cast<SCROW>(rDocument.MaxRow() + 1 - nSize) )
2207 --nFirstLost;
2209 return !mvData[nFirstLost].getScPatternAttr()->GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
2212 void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
2214 SetDefaultIfNotInit();
2216 SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // expand predecessor
2217 SCSIZE nIndex;
2218 Search( nSearch, nIndex );
2220 // set ScMergeAttr may not be extended (so behind delete again)
2222 bool bDoMerge = mvData[nIndex].getScPatternAttr()->GetItem(ATTR_MERGE).IsMerged();
2224 assert( !bDoMerge || nCol != -1 );
2226 SCSIZE nRemove = 0;
2227 SCSIZE i;
2228 for (i = nIndex; i < mvData.size()-1; i++)
2230 SCROW nNew = mvData[i].nEndRow + nSize;
2231 if ( nNew >= rDocument.MaxRow() ) // at end?
2233 nNew = rDocument.MaxRow();
2234 if (!nRemove)
2235 nRemove = i+1; // remove the following?
2237 mvData[i].nEndRow = nNew;
2240 // Remove entries at end ?
2242 if (nRemove && nRemove < mvData.size())
2243 DeleteRange( nRemove, mvData.size()-1 );
2245 if (bDoMerge) // extensively repair (again) ScMergeAttr
2247 // ApplyAttr for areas
2249 const SfxPoolItem& rDef = rDocument.GetPool()->GetUserOrPoolDefaultItem( ATTR_MERGE );
2250 for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
2251 rDocument.ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
2253 // reply inserts in this area not summarized
2256 // Don't duplicate the merge flags in the inserted row.
2257 // #i108488# ScMF::Scenario has to be allowed.
2258 RemoveFlags( nStartRow, nStartRow+nSize-1, ScMF::Hor | ScMF::Ver | ScMF::Auto | ScMF::Button );
2261 void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
2263 SetDefaultIfNotInit();
2264 bool bFirst=true;
2265 SCSIZE nStartIndex = 0;
2266 SCSIZE nEndIndex = 0;
2267 SCSIZE i;
2269 for ( i = 0; i < mvData.size()-1; i++)
2270 if (mvData[i].nEndRow >= nStartRow && mvData[i].nEndRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
2272 if (bFirst)
2274 nStartIndex = i;
2275 bFirst = false;
2277 nEndIndex = i;
2279 if (!bFirst)
2281 SCROW nStart;
2282 if (nStartIndex==0)
2283 nStart = 0;
2284 else
2285 nStart = mvData[nStartIndex-1].nEndRow + 1;
2287 if (nStart < nStartRow)
2289 mvData[nStartIndex].nEndRow = nStartRow - 1;
2290 ++nStartIndex;
2292 if (nEndIndex >= nStartIndex)
2294 DeleteRange( nStartIndex, nEndIndex );
2295 if (nStartIndex > 0)
2296 if ( ScPatternAttr::areSame( mvData[nStartIndex-1].getScPatternAttr(), mvData[nStartIndex].getScPatternAttr() ) )
2297 DeleteRange( nStartIndex-1, nStartIndex-1 );
2300 for (i = 0; i < mvData.size()-1; i++)
2301 if (mvData[i].nEndRow >= nStartRow)
2302 mvData[i].nEndRow -= nSize;
2304 // Below does not follow the pattern to detect pressure ranges;
2305 // instead, only remove merge flags.
2306 RemoveFlags( rDocument.MaxRow()-nSize+1, rDocument.MaxRow(), ScMF::Hor | ScMF::Ver | ScMF::Auto );
2309 void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
2311 SetDefaultIfNotInit();
2312 mvData.erase(mvData.begin() + nStartIndex, mvData.begin() + nEndIndex + 1);
2315 void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
2317 SetDefaultIfNotInit();
2318 if ( nCol != -1 )
2319 RemoveAreaMerge( nStartRow, nEndRow ); // remove from combined flags
2321 const CellAttributeHolder aDefHolder(&rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2323 if ( !HasAttrib( nStartRow, nEndRow, HasAttrFlags::Overlapped | HasAttrFlags::AutoFilter) )
2324 SetPatternArea( nStartRow, nEndRow, aDefHolder );
2325 else
2326 SetPatternAreaSafe( nStartRow, nEndRow, aDefHolder ); // leave merge flags
2329 void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
2331 SetDefaultIfNotInit();
2332 const CellAttributeHolder aDefHolder(&rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2334 SCSIZE nIndex;
2335 SCROW nRow;
2336 SCROW nThisRow;
2338 Search( nStartRow, nIndex );
2339 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
2340 if (nThisRow < nStartRow) nThisRow = nStartRow;
2342 while ( nThisRow <= nEndRow )
2344 const ScPatternAttr* pOldPattern = mvData[nIndex].getScPatternAttr();
2346 if ( pOldPattern->GetItemSet().Count() ) // hard attributes ?
2348 nRow = mvData[nIndex].nEndRow;
2349 SCROW nAttrRow = std::min( nRow, nEndRow );
2351 ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
2352 SfxItemSet& rSet = pNewPattern->GetItemSet();
2353 for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
2354 if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
2355 rSet.ClearItem(nId);
2357 if ( *pNewPattern == *aDefHolder.getScPatternAttr() )
2359 delete pNewPattern;
2360 SetPatternArea( nThisRow, nAttrRow, aDefHolder );
2362 else
2364 SetPatternArea( nThisRow, nAttrRow, CellAttributeHolder(pNewPattern, true) );
2367 Search( nThisRow, nIndex ); // data changed
2370 ++nIndex;
2371 nThisRow = mvData[nIndex-1].nEndRow+1;
2376 * Move within a document
2378 void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
2380 SetDefaultIfNotInit();
2381 SCROW nStart = nStartRow;
2382 for (SCSIZE i = 0; i < mvData.size(); i++)
2384 if ((mvData[i].nEndRow >= nStartRow) && (i == 0 || mvData[i-1].nEndRow < nEndRow))
2386 // copy (bPutToPool=TRUE)
2387 rAttrArray.SetPatternArea( nStart, std::min( mvData[i].nEndRow, nEndRow ), mvData[i].getCellAttributeHolder() );
2389 nStart = std::max( nStart, mvData[i].nEndRow + 1 );
2391 DeleteArea(nStartRow, nEndRow);
2395 * Copy between documents (Clipboard)
2397 void ScAttrArray::CopyArea(
2398 SCROW nStartRow, SCROW nEndRow, tools::Long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags) const
2400 nStartRow -= nDy; // Source
2401 nEndRow -= nDy;
2403 SCROW nDestStart = std::max(static_cast<tools::Long>(static_cast<tools::Long>(nStartRow) + nDy), tools::Long(0));
2404 SCROW nDestEnd = std::min(static_cast<tools::Long>(static_cast<tools::Long>(nEndRow) + nDy), tools::Long(rDocument.MaxRow()));
2405 const bool bSameCellAttributeHelper(&rDocument.getCellAttributeHelper() == &rAttrArray.rDocument.getCellAttributeHelper());
2407 const ScPatternAttr* pSourceDefaultPattern = &rDocument.getCellAttributeHelper().getDefaultCellAttribute();
2408 const ScPatternAttr* pDestDefaultPattern = &rAttrArray.rDocument.getCellAttributeHelper().getDefaultCellAttribute();
2409 if ( mvData.empty() )
2411 rAttrArray.SetPatternArea(nDestStart, nDestEnd, pDestDefaultPattern);
2412 return;
2415 for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
2417 if (mvData[i].nEndRow >= nStartRow)
2419 const ScPatternAttr* pOldPattern = mvData[i].getScPatternAttr();
2420 CellAttributeHolder aNewPattern;
2422 if (ScPatternAttr::areSame(pSourceDefaultPattern, pOldPattern ))
2424 // default: nothing changed
2425 aNewPattern.setScPatternAttr(pDestDefaultPattern);
2427 else if ( nStripFlags != ScMF::NONE )
2429 ScPatternAttr* pTmpPattern(new ScPatternAttr(*pOldPattern ));
2430 ScMF nNewFlags = ScMF::NONE;
2431 if ( nStripFlags != ScMF::All )
2432 nNewFlags = pTmpPattern->GetItem(ATTR_MERGE_FLAG).GetValue() & ~nStripFlags;
2434 if ( nNewFlags != ScMF::NONE )
2435 pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
2436 else
2437 pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG );
2439 if (bSameCellAttributeHelper)
2440 aNewPattern.setScPatternAttr(pTmpPattern, true);
2441 else
2443 aNewPattern = pTmpPattern->MigrateToDocument( &rAttrArray.rDocument, &rDocument );
2444 delete pTmpPattern;
2447 else
2449 if (bSameCellAttributeHelper)
2450 aNewPattern.setScPatternAttr(pOldPattern);
2451 else
2452 aNewPattern = pOldPattern->MigrateToDocument( &rAttrArray.rDocument, &rDocument );
2455 rAttrArray.SetPatternArea(nDestStart,
2456 std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), aNewPattern.getScPatternAttr());
2459 // when pasting from clipboard and skipping filtered rows, the adjusted
2460 // end position can be negative
2461 nDestStart = std::max(static_cast<tools::Long>(nDestStart), static_cast<tools::Long>(mvData[i].nEndRow + nDy + 1));
2466 * Leave flags
2467 * summarized with CopyArea
2469 void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, tools::Long nDy, ScAttrArray& rAttrArray )
2471 nStartRow -= nDy; // Source
2472 nEndRow -= nDy;
2474 SCROW nDestStart = std::max(static_cast<tools::Long>(static_cast<tools::Long>(nStartRow) + nDy), tools::Long(0));
2475 SCROW nDestEnd = std::min(static_cast<tools::Long>(static_cast<tools::Long>(nEndRow) + nDy), tools::Long(rDocument.MaxRow()));
2477 if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HasAttrFlags::Overlapped ) )
2479 CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
2480 return;
2483 const bool bSameCellAttributeHelper(&rDocument.getCellAttributeHelper() == &rAttrArray.rDocument.getCellAttributeHelper());
2485 if ( mvData.empty() )
2487 CellAttributeHolder aNewPattern;
2488 if (bSameCellAttributeHelper)
2489 aNewPattern.setScPatternAttr(&rDocument.getCellAttributeHelper().getDefaultCellAttribute());
2490 else
2491 aNewPattern = rDocument.getCellAttributeHelper().getDefaultCellAttribute().MigrateToDocument( &rAttrArray.rDocument, &rDocument );
2493 rAttrArray.SetPatternAreaSafe(nDestStart, nDestEnd, aNewPattern);
2494 return;
2498 for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
2500 if (mvData[i].nEndRow >= nStartRow)
2502 const ScPatternAttr* pOldPattern = mvData[i].getScPatternAttr();
2503 CellAttributeHolder aNewPattern;
2505 if (bSameCellAttributeHelper)
2506 aNewPattern.setScPatternAttr(pOldPattern);
2507 else
2508 aNewPattern = pOldPattern->MigrateToDocument( &rAttrArray.rDocument, &rDocument );
2510 rAttrArray.SetPatternAreaSafe(nDestStart,
2511 std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), aNewPattern);
2514 // when pasting from clipboard and skipping filtered rows, the adjusted
2515 // end position can be negative
2516 nDestStart = std::max(static_cast<tools::Long>(nDestStart), static_cast<tools::Long>(mvData[i].nEndRow + nDy + 1));
2520 SCROW ScAttrArray::SearchStyle(
2521 SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp,
2522 const ScMarkArray* pMarkArray) const
2524 bool bFound = false;
2526 if (pMarkArray)
2528 nRow = pMarkArray->GetNextMarked( nRow, bUp );
2529 if (!rDocument.ValidRow(nRow))
2530 return nRow;
2533 if ( mvData.empty() )
2535 if (rDocument.getCellAttributeHelper().getDefaultCellAttribute().GetStyleSheet() == pSearchStyle)
2536 return nRow;
2538 nRow = bUp ? -1 : rDocument.MaxRow() + 1;
2539 return nRow;
2542 SCSIZE nIndex;
2543 Search(nRow, nIndex);
2544 const ScPatternAttr* pPattern = mvData[nIndex].getScPatternAttr();
2546 while (nIndex < mvData.size() && !bFound)
2548 if (pPattern->GetStyleSheet() == pSearchStyle)
2550 if (pMarkArray)
2552 nRow = pMarkArray->GetNextMarked( nRow, bUp );
2553 SCROW nStart = nIndex ? mvData[nIndex-1].nEndRow+1 : 0;
2554 if (nRow >= nStart && nRow <= mvData[nIndex].nEndRow)
2555 bFound = true;
2557 else
2558 bFound = true;
2561 if (!bFound)
2563 if (bUp)
2565 if (nIndex==0)
2567 nIndex = mvData.size();
2568 nRow = -1;
2570 else
2572 --nIndex;
2573 nRow = mvData[nIndex].nEndRow;
2574 pPattern = mvData[nIndex].getScPatternAttr();
2577 else
2579 nRow = mvData[nIndex].nEndRow+1;
2580 ++nIndex;
2581 if (nIndex<mvData.size())
2582 pPattern = mvData[nIndex].getScPatternAttr();
2587 OSL_ENSURE( bFound || !rDocument.ValidRow(nRow), "Internal failure in ScAttrArray::SearchStyle" );
2589 return nRow;
2592 bool ScAttrArray::SearchStyleRange(
2593 SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
2594 const ScMarkArray* pMarkArray) const
2596 SCROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
2597 if (rDocument.ValidRow(nStartRow))
2599 if ( mvData.empty() )
2601 rRow = nStartRow;
2602 if (bUp)
2604 rEndRow = 0;
2605 if (pMarkArray)
2607 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
2608 if (nMarkEnd>rEndRow)
2609 rEndRow = nMarkEnd;
2612 else
2614 rEndRow = rDocument.MaxRow();
2615 if (pMarkArray)
2617 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
2618 if (nMarkEnd<rEndRow)
2619 rEndRow = nMarkEnd;
2623 return true;
2626 SCSIZE nIndex;
2627 Search(nStartRow,nIndex);
2629 rRow = nStartRow;
2630 if (bUp)
2632 if (nIndex>0)
2633 rEndRow = mvData[nIndex-1].nEndRow + 1;
2634 else
2635 rEndRow = 0;
2636 if (pMarkArray)
2638 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
2639 if (nMarkEnd>rEndRow)
2640 rEndRow = nMarkEnd;
2643 else
2645 rEndRow = mvData[nIndex].nEndRow;
2646 if (pMarkArray)
2648 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
2649 if (nMarkEnd<rEndRow)
2650 rEndRow = nMarkEnd;
2654 return true;
2656 else
2657 return false;
2660 SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW nEndRow ) const
2662 if ( mvData.empty() )
2663 return 1;
2665 SCSIZE nIndex1, nIndex2;
2667 if( !Search( nStartRow, nIndex1 ) )
2668 return 0;
2670 if( !Search( nEndRow, nIndex2 ) )
2671 nIndex2 = mvData.size() - 1;
2673 return nIndex2 - nIndex1 + 1;
2676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */