forked from amazingfate/loongoffice
For a default column, now ScAttrArray does not store the default format. So the case of default pattern from 0 to MAXROW is represented as nCount = 0 and pData = nullptr in ScAttrArray. A new ScAttrArray object (aNextColAttrArray) is introduced as a member of ScTable. This is used to store the formatting of *unallocated* columns (whose indices are from aCol.size() to MAXCOL). In next patches for this bug, I plan to refactor table*.cxx functions related to formatting such that : 1) In formatting setter functions, if colspan of the input range spans the colrange(aCol.size() to MAXCOL) then instead of allocating columns, apply that formatting to aNextColAttrArray. 2) In formatting getter (const) functions, if requested colspan has some intersection with the colrange(aCol.size() to MAXCOL) then use the formatting info stored in aNextColAttrArray to calculate the formatting of the input range. 3) In general setter (non-const) functions if we really need to allocate new columns (example, when data is entered), use the formatting info stored in aNextColAttrArray to create pAttrArray of the new column. Change-Id: Ieb56f853209b396d92fdb2c27e39361703576423 Reviewed-on: https://gerrit.libreoffice.org/27828 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Eike Rathke <erack@redhat.com> Tested-by: Eike Rathke <erack@redhat.com>
2714 lines
91 KiB
C++
2714 lines
91 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include "attarray.hxx"
|
|
#include "scitems.hxx"
|
|
#include <svx/algitem.hxx>
|
|
#include <editeng/boxitem.hxx>
|
|
#include <editeng/lineitem.hxx>
|
|
#include <editeng/frmdiritem.hxx>
|
|
#include <editeng/shaditem.hxx>
|
|
#include <editeng/editobj.hxx>
|
|
#include <editeng/justifyitem.hxx>
|
|
#include <svl/poolcach.hxx>
|
|
#include <editeng/fontitem.hxx>
|
|
#include <unotools/fontcvt.hxx>
|
|
|
|
#include "global.hxx"
|
|
#include "document.hxx"
|
|
#include "docpool.hxx"
|
|
#include "patattr.hxx"
|
|
#include "stlsheet.hxx"
|
|
#include "stlpool.hxx"
|
|
#include "markarr.hxx"
|
|
#include "rechead.hxx"
|
|
#include "globstr.hrc"
|
|
#include "segmenttree.hxx"
|
|
#include "editdataarray.hxx"
|
|
#include "formulacell.hxx"
|
|
#include "cellvalue.hxx"
|
|
#include "editutil.hxx"
|
|
#include <rtl/strbuf.hxx>
|
|
#include <memory>
|
|
|
|
using ::editeng::SvxBorderLine;
|
|
|
|
ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc, ScAttrArray* pNextColAttrArray, bool bCreateEmpty ) :
|
|
nCol( nNewCol ),
|
|
nTab( nNewTab ),
|
|
pDocument( pDoc ),
|
|
nCount(0),
|
|
nLimit(0),
|
|
pData(nullptr)
|
|
{
|
|
if ( nCol != -1 && !bCreateEmpty && pNextColAttrArray )
|
|
{
|
|
nCount = pNextColAttrArray->nCount;
|
|
nLimit = pNextColAttrArray->nCount;
|
|
if ( nCount )
|
|
{
|
|
bool bNumFormatChanged;
|
|
ScAddress aAdrStart( nCol, 0, nTab );
|
|
ScAddress aAdrEnd( nCol, 0, nTab );
|
|
pData = new ScAttrEntry[nCount];
|
|
for ( size_t nIdx = 0; nIdx < nCount; ++nIdx )
|
|
{
|
|
pData[nIdx].nRow = pNextColAttrArray->pData[nIdx].nRow;
|
|
ScPatternAttr aNewPattern( *(pNextColAttrArray->pData[nIdx].pPattern) );
|
|
pData[nIdx].pPattern = static_cast<const ScPatternAttr*>( &pDocument->GetPool()->Put( aNewPattern ) );
|
|
bNumFormatChanged = false;
|
|
if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
|
|
pData[nIdx].pPattern->GetItemSet(), pDocument->GetDefPattern()->GetItemSet() ) )
|
|
{
|
|
aAdrStart.SetRow( nIdx ? pData[nIdx-1].nRow+1 : 0 );
|
|
aAdrEnd.SetRow( pData[nIdx].nRow );
|
|
pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ScAttrArray::~ScAttrArray()
|
|
{
|
|
#if DEBUG_SC_TESTATTRARRAY
|
|
TestData();
|
|
#endif
|
|
|
|
ScDocumentPool* pDocPool = pDocument->GetPool();
|
|
for (SCSIZE i=0; i<nCount; i++)
|
|
pDocPool->Remove(*pData[i].pPattern);
|
|
|
|
delete[] pData;
|
|
}
|
|
|
|
#if DEBUG_SC_TESTATTRARRAY
|
|
void ScAttrArray::TestData() const
|
|
{
|
|
|
|
sal_uInt16 nErr = 0;
|
|
SCSIZE nPos;
|
|
for (nPos=0; nPos<nCount; nPos++)
|
|
{
|
|
if (nPos > 0)
|
|
if (pData[nPos].pPattern == pData[nPos-1].pPattern || pData[nPos].nRow <= pData[nPos-1].nRow)
|
|
++nErr;
|
|
if (pData[nPos].pPattern->Which() != ATTR_PATTERN)
|
|
++nErr;
|
|
}
|
|
if ( nPos && pData[nPos-1].nRow != MAXROW )
|
|
++nErr;
|
|
|
|
if (nErr)
|
|
{
|
|
OStringBuffer aMsg;
|
|
aMsg.append(static_cast<sal_Int32>(nErr));
|
|
aMsg.append(" errors in attribute array, column ");
|
|
aMsg.append(static_cast<sal_Int32>(nCol));
|
|
OSL_FAIL(aMsg.getStr());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void ScAttrArray::SetDefaultIfNotInit( SCSIZE nNeeded )
|
|
{
|
|
if ( pData )
|
|
return;
|
|
|
|
SCSIZE nNewLimit = ( SC_ATTRARRAY_DELTA > nNeeded ) ? SC_ATTRARRAY_DELTA : nNeeded;
|
|
pData = new ScAttrEntry[nNewLimit];
|
|
pData[0].nRow = MAXROW;
|
|
pData[0].pPattern = pDocument->GetDefPattern(); // no put
|
|
nCount = 1;
|
|
nLimit = nNewLimit;
|
|
}
|
|
|
|
void ScAttrArray::Reset( const ScPatternAttr* pPattern )
|
|
{
|
|
ScDocumentPool* pDocPool = pDocument->GetPool();
|
|
ScAddress aAdrStart( nCol, 0, nTab );
|
|
ScAddress aAdrEnd ( nCol, 0, nTab );
|
|
|
|
for (SCSIZE i=0; i<nCount; i++)
|
|
{
|
|
// ensure that attributing changes text width of cell
|
|
const ScPatternAttr* pOldPattern = pData[i].pPattern;
|
|
if ( nCol != -1 )
|
|
{
|
|
bool bNumFormatChanged;
|
|
if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
|
|
pPattern->GetItemSet(), pOldPattern->GetItemSet() ) )
|
|
{
|
|
aAdrStart.SetRow( i ? pData[i-1].nRow+1 : 0 );
|
|
aAdrEnd .SetRow( pData[i].nRow );
|
|
pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
|
|
}
|
|
}
|
|
pDocPool->Remove(*pOldPattern);
|
|
}
|
|
delete[] pData;
|
|
|
|
if (pDocument->IsStreamValid(nTab))
|
|
pDocument->SetStreamValid(nTab, false);
|
|
|
|
nCount = nLimit = 1;
|
|
pData = new ScAttrEntry[1];
|
|
const ScPatternAttr* pNewPattern = static_cast<const ScPatternAttr*>( &pDocPool->Put(*pPattern) );
|
|
pData[0].nRow = MAXROW;
|
|
pData[0].pPattern = pNewPattern;
|
|
}
|
|
|
|
bool ScAttrArray::Concat(SCSIZE nPos)
|
|
{
|
|
bool bRet = false;
|
|
if (nPos < nCount)
|
|
{
|
|
if (nPos > 0)
|
|
{
|
|
if (pData[nPos - 1].pPattern == pData[nPos].pPattern)
|
|
{
|
|
pData[nPos - 1].nRow = pData[nPos].nRow;
|
|
pDocument->GetPool()->Remove(*pData[nPos].pPattern);
|
|
memmove(&pData[nPos], &pData[nPos + 1], (nCount - nPos - 1) * sizeof(ScAttrEntry));
|
|
pData[nCount - 1].pPattern = nullptr;
|
|
pData[nCount - 1].nRow = 0;
|
|
nCount--;
|
|
nPos--;
|
|
bRet = true;
|
|
}
|
|
}
|
|
if (nPos + 1 < nCount)
|
|
{
|
|
if (pData[nPos + 1].pPattern == pData[nPos].pPattern)
|
|
{
|
|
pData[nPos].nRow = pData[nPos + 1].nRow;
|
|
pDocument->GetPool()->Remove(*pData[nPos].pPattern);
|
|
memmove(&pData[nPos + 1], &pData[nPos + 2], (nCount - nPos - 2) * sizeof(ScAttrEntry));
|
|
pData[nCount - 1].pPattern = nullptr;
|
|
pData[nCount - 1].nRow = 0;
|
|
nCount--;
|
|
bRet = true;
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex ) const
|
|
{
|
|
long nHi = static_cast<long>(nCount) - 1;
|
|
long i = 0;
|
|
bool bFound = (nCount == 1);
|
|
long nLo = 0;
|
|
long nStartRow = 0;
|
|
while ( !bFound && nLo <= nHi )
|
|
{
|
|
i = (nLo + nHi) / 2;
|
|
if (i > 0)
|
|
nStartRow = (long) pData[i - 1].nRow;
|
|
else
|
|
nStartRow = -1;
|
|
const long nEndRow = (long) pData[i].nRow;
|
|
if (nEndRow < (long) nRow)
|
|
nLo = ++i;
|
|
else
|
|
if (nStartRow >= (long) nRow)
|
|
nHi = --i;
|
|
else
|
|
bFound = true;
|
|
}
|
|
|
|
if (bFound)
|
|
nIndex=(SCSIZE)i;
|
|
else
|
|
nIndex=0;
|
|
return bFound;
|
|
}
|
|
|
|
const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const
|
|
{
|
|
if ( !pData )
|
|
{
|
|
if ( !ValidRow( nRow ) )
|
|
return nullptr;
|
|
return pDocument->GetDefPattern();
|
|
}
|
|
SCSIZE i;
|
|
if (Search( nRow, i ))
|
|
return pData[i].pPattern;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow,
|
|
SCROW& rEndRow, SCROW nRow ) const
|
|
{
|
|
if ( !pData )
|
|
{
|
|
if ( !ValidRow( nRow ) )
|
|
return nullptr;
|
|
rStartRow = 0;
|
|
rEndRow = MAXROW;
|
|
return pDocument->GetDefPattern();
|
|
}
|
|
SCSIZE nIndex;
|
|
if ( Search( nRow, nIndex ) )
|
|
{
|
|
if ( nIndex > 0 )
|
|
rStartRow = pData[nIndex-1].nRow + 1;
|
|
else
|
|
rStartRow = 0;
|
|
rEndRow = pData[nIndex].nRow;
|
|
return pData[nIndex].pPattern;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ScAttrArray::AddCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
|
|
{
|
|
if(!ValidRow(nStartRow) || !ValidRow(nEndRow))
|
|
return;
|
|
|
|
if(nEndRow < nStartRow)
|
|
return;
|
|
|
|
SCROW nTempStartRow = nStartRow;
|
|
SCROW nTempEndRow = nEndRow;
|
|
|
|
do
|
|
{
|
|
const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
|
|
|
|
std::unique_ptr<ScPatternAttr> pNewPattern;
|
|
if(pPattern)
|
|
{
|
|
pNewPattern.reset( new ScPatternAttr(*pPattern) );
|
|
SCROW nPatternStartRow;
|
|
SCROW nPatternEndRow;
|
|
GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
|
|
|
|
nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
|
|
const SfxPoolItem* pItem = nullptr;
|
|
pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pItem );
|
|
std::vector< sal_uInt32 > aCondFormatData;
|
|
if(pItem)
|
|
aCondFormatData = static_cast<const ScCondFormatItem*>(pItem)->GetCondFormatData();
|
|
aCondFormatData.push_back(nIndex);
|
|
|
|
ScCondFormatItem aItem;
|
|
aItem.SetCondFormatData( aCondFormatData );
|
|
pNewPattern->GetItemSet().Put( aItem );
|
|
}
|
|
else
|
|
{
|
|
pNewPattern.reset( new ScPatternAttr( pDocument->GetPool() ) );
|
|
ScCondFormatItem aItem;
|
|
aItem.AddCondFormatData(nIndex);
|
|
pNewPattern->GetItemSet().Put( aItem );
|
|
nTempEndRow = nEndRow;
|
|
}
|
|
|
|
SetPatternArea( nTempStartRow, nTempEndRow, pNewPattern.get(), true );
|
|
nTempStartRow = nTempEndRow + 1;
|
|
}
|
|
while(nTempEndRow < nEndRow);
|
|
|
|
}
|
|
|
|
void ScAttrArray::RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
|
|
{
|
|
if(!ValidRow(nStartRow) || !ValidRow(nEndRow))
|
|
return;
|
|
|
|
if(nEndRow < nStartRow)
|
|
return;
|
|
|
|
SCROW nTempStartRow = nStartRow;
|
|
SCROW nTempEndRow = nEndRow;
|
|
|
|
do
|
|
{
|
|
const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
|
|
|
|
if(pPattern)
|
|
{
|
|
ScPatternAttr aPattern( *pPattern );
|
|
SCROW nPatternStartRow;
|
|
SCROW nPatternEndRow;
|
|
GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
|
|
|
|
nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
|
|
const SfxPoolItem* pItem = nullptr;
|
|
pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pItem );
|
|
if(pItem)
|
|
{
|
|
std::vector< sal_uInt32 > aCondFormatData = static_cast<const ScCondFormatItem*>(pItem)->GetCondFormatData();
|
|
std::vector<sal_uInt32>::iterator itr = std::find(aCondFormatData.begin(), aCondFormatData.end(), nIndex);
|
|
if(itr != aCondFormatData.end() || nIndex == 0)
|
|
{
|
|
ScCondFormatItem aItem;
|
|
if (nIndex == 0)
|
|
aCondFormatData.clear();
|
|
else
|
|
aCondFormatData.erase(itr);
|
|
aItem.SetCondFormatData( aCondFormatData );
|
|
aPattern.GetItemSet().Put( aItem );
|
|
SetPatternArea( nTempStartRow, nTempEndRow, &aPattern, true );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
nTempStartRow = nTempEndRow + 1;
|
|
}
|
|
while(nTempEndRow < nEndRow);
|
|
|
|
}
|
|
|
|
void ScAttrArray::SetPattern( SCROW nRow, const ScPatternAttr* pPattern, bool bPutToPool )
|
|
{
|
|
SetPatternArea( nRow, nRow, pPattern, bPutToPool );
|
|
}
|
|
|
|
void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow,
|
|
const ScPatternAttr* pPattern, ScEditDataArray* pDataArray )
|
|
{
|
|
assert( nCol != -1 );
|
|
for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
|
|
{
|
|
ScAddress aPos(nCol, nRow, nTab);
|
|
ScRefCellValue aCell(*pDocument, aPos);
|
|
if (aCell.meType != CELLTYPE_EDIT || !aCell.mpEditText)
|
|
continue;
|
|
|
|
EditTextObject* pOldData = nullptr;
|
|
if (pDataArray)
|
|
pOldData = aCell.mpEditText->Clone();
|
|
|
|
// Direct modification of cell content - something to watch out for if
|
|
// we decide to share edit text instances in the future.
|
|
ScEditUtil::RemoveCharAttribs(const_cast<EditTextObject&>(*aCell.mpEditText), *pPattern);
|
|
|
|
if (pDataArray)
|
|
{
|
|
EditTextObject* pNewData = aCell.mpEditText->Clone();
|
|
pDataArray->AddItem(nTab, nCol, nRow, pOldData, pNewData);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ScAttrArray::Reserve( SCSIZE nReserve )
|
|
{
|
|
if ( !pData && nReserve )
|
|
{
|
|
if( ScAttrEntry* pNewData = new (std::nothrow) ScAttrEntry[nReserve] )
|
|
{
|
|
nLimit = nReserve;
|
|
nCount = 1;
|
|
pData = pNewData;
|
|
pData[0].nRow = MAXROW;
|
|
pData[0].pPattern = pDocument->GetDefPattern(); // no put
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
else if ( nLimit < nReserve )
|
|
{
|
|
if( ScAttrEntry* pNewData = new (std::nothrow) ScAttrEntry[nReserve] )
|
|
{
|
|
nLimit = nReserve;
|
|
memcpy( pNewData, pData, nCount*sizeof(ScAttrEntry) );
|
|
delete[] pData;
|
|
pData = pNewData;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void ScAttrArray::SetPatternArea(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr *pPattern,
|
|
bool bPutToPool, ScEditDataArray* pDataArray )
|
|
{
|
|
if (ValidRow(nStartRow) && ValidRow(nEndRow))
|
|
{
|
|
if (bPutToPool)
|
|
pPattern = static_cast<const ScPatternAttr*>(&pDocument->GetPool()->Put(*pPattern));
|
|
|
|
if ((nStartRow == 0) && (nEndRow == MAXROW))
|
|
Reset(pPattern);
|
|
else
|
|
{
|
|
SCSIZE nNeeded = nCount + 2;
|
|
SetDefaultIfNotInit( nNeeded );
|
|
if ( nLimit < nNeeded )
|
|
{
|
|
nLimit += SC_ATTRARRAY_DELTA;
|
|
if ( nLimit < nNeeded )
|
|
nLimit = nNeeded;
|
|
ScAttrEntry* pNewData = new ScAttrEntry[nLimit];
|
|
memcpy( pNewData, pData, nCount*sizeof(ScAttrEntry) );
|
|
delete[] pData;
|
|
pData = pNewData;
|
|
}
|
|
|
|
ScAddress aAdrStart( nCol, 0, nTab );
|
|
ScAddress aAdrEnd ( nCol, 0, nTab );
|
|
|
|
SCSIZE ni = 0; // number of entries in beginning
|
|
SCSIZE nx = 0; // track position
|
|
SCROW ns = 0; // start row of track position
|
|
if ( nStartRow > 0 )
|
|
{
|
|
// skip beginning
|
|
SCSIZE nIndex;
|
|
Search( nStartRow, nIndex );
|
|
ni = nIndex;
|
|
|
|
if ( ni > 0 )
|
|
{
|
|
nx = ni;
|
|
ns = pData[ni-1].nRow+1;
|
|
}
|
|
}
|
|
|
|
// ensure that attributing changes text width of cell
|
|
// otherwise, conditional formats need to be reset or deleted
|
|
while ( ns <= nEndRow )
|
|
{
|
|
if ( nCol != -1 )
|
|
{
|
|
const SfxItemSet& rNewSet = pPattern->GetItemSet();
|
|
const SfxItemSet& rOldSet = pData[nx].pPattern->GetItemSet();
|
|
bool bNumFormatChanged;
|
|
if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
|
|
rNewSet, rOldSet ) )
|
|
{
|
|
aAdrStart.SetRow( std::max(nStartRow,ns) );
|
|
aAdrEnd .SetRow( std::min(nEndRow,pData[nx].nRow) );
|
|
pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
|
|
}
|
|
}
|
|
ns = pData[nx].nRow + 1;
|
|
nx++;
|
|
}
|
|
|
|
// continue modifying data array
|
|
|
|
SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert)
|
|
bool bCombined = false;
|
|
bool bSplit = false;
|
|
if ( nStartRow > 0 )
|
|
{
|
|
nInsert = MAXROWCOUNT;
|
|
if ( pData[ni].pPattern != pPattern )
|
|
{
|
|
if ( ni == 0 || (pData[ni-1].nRow < nStartRow - 1) )
|
|
{ // may be a split or a simple insert or just a shrink,
|
|
// row adjustment is done further down
|
|
if ( pData[ni].nRow > nEndRow )
|
|
bSplit = true;
|
|
ni++;
|
|
nInsert = ni;
|
|
}
|
|
else if ( ni > 0 && pData[ni-1].nRow == nStartRow - 1 )
|
|
nInsert = ni;
|
|
}
|
|
if ( ni > 0 && pData[ni-1].pPattern == pPattern )
|
|
{ // combine
|
|
pData[ni-1].nRow = nEndRow;
|
|
nInsert = MAXROWCOUNT;
|
|
bCombined = true;
|
|
}
|
|
}
|
|
else
|
|
nInsert = 0;
|
|
|
|
SCSIZE nj = ni; // stop position of range to replace
|
|
while ( nj < nCount && pData[nj].nRow <= nEndRow )
|
|
nj++;
|
|
if ( !bSplit )
|
|
{
|
|
if ( nj < nCount && pData[nj].pPattern == pPattern )
|
|
{ // combine
|
|
if ( ni > 0 )
|
|
{
|
|
if ( pData[ni-1].pPattern == pPattern )
|
|
{ // adjacent entries
|
|
pData[ni-1].nRow = pData[nj].nRow;
|
|
nj++;
|
|
}
|
|
else if ( ni == nInsert )
|
|
pData[ni-1].nRow = nStartRow - 1; // shrink
|
|
}
|
|
nInsert = MAXROWCOUNT;
|
|
bCombined = true;
|
|
}
|
|
else if ( ni > 0 && ni == nInsert )
|
|
pData[ni-1].nRow = nStartRow - 1; // shrink
|
|
}
|
|
ScDocumentPool* pDocPool = pDocument->GetPool();
|
|
if ( bSplit )
|
|
{ // duplicate splitted entry in pool
|
|
pDocPool->Put( *pData[ni-1].pPattern );
|
|
}
|
|
if ( ni < nj )
|
|
{ // remove middle entries
|
|
for ( SCSIZE nk=ni; nk<nj; nk++)
|
|
{ // remove entries from pool
|
|
pDocPool->Remove( *pData[nk].pPattern );
|
|
}
|
|
if ( !bCombined )
|
|
{ // replace one entry
|
|
pData[ni].nRow = nEndRow;
|
|
pData[ni].pPattern = pPattern;
|
|
ni++;
|
|
nInsert = MAXROWCOUNT;
|
|
}
|
|
if ( ni < nj )
|
|
{ // remove entries
|
|
memmove( pData + ni, pData + nj, (nCount - nj) * sizeof(ScAttrEntry) );
|
|
nCount -= nj - ni;
|
|
}
|
|
}
|
|
|
|
if ( nInsert < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) )
|
|
{ // insert or append new entry
|
|
if ( nInsert <= nCount )
|
|
{
|
|
if ( !bSplit )
|
|
memmove( pData + nInsert + 1, pData + nInsert,
|
|
(nCount - nInsert) * sizeof(ScAttrEntry) );
|
|
else
|
|
{
|
|
memmove( pData + nInsert + 2, pData + nInsert,
|
|
(nCount - nInsert) * sizeof(ScAttrEntry) );
|
|
pData[nInsert+1] = pData[nInsert-1];
|
|
nCount++;
|
|
}
|
|
}
|
|
if ( nInsert )
|
|
pData[nInsert-1].nRow = nStartRow - 1;
|
|
pData[nInsert].nRow = nEndRow;
|
|
pData[nInsert].pPattern = pPattern;
|
|
|
|
// Remove character attributes from these cells if the pattern
|
|
// is applied during normal session.
|
|
if (pDataArray && nCol != -1)
|
|
RemoveCellCharAttribs(nStartRow, nEndRow, pPattern, pDataArray);
|
|
|
|
nCount++;
|
|
}
|
|
|
|
if (pDocument->IsStreamValid(nTab))
|
|
pDocument->SetStreamValid(nTab, false);
|
|
}
|
|
}
|
|
|
|
#if DEBUG_SC_TESTATTRARRAY
|
|
TestData();
|
|
#endif
|
|
}
|
|
|
|
void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, ScStyleSheet* pStyle )
|
|
{
|
|
if (ValidRow(nStartRow) && ValidRow(nEndRow))
|
|
{
|
|
SetDefaultIfNotInit();
|
|
SCSIZE nPos;
|
|
SCROW nStart=0;
|
|
if (!Search( nStartRow, nPos ))
|
|
{
|
|
OSL_FAIL("Search Failure");
|
|
return;
|
|
}
|
|
|
|
ScAddress aAdrStart( nCol, 0, nTab );
|
|
ScAddress aAdrEnd ( nCol, 0, nTab );
|
|
|
|
do
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[nPos].pPattern;
|
|
std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
|
|
pNewPattern->SetStyleSheet(pStyle);
|
|
SCROW nY1 = nStart;
|
|
SCROW nY2 = pData[nPos].nRow;
|
|
nStart = pData[nPos].nRow + 1;
|
|
|
|
if ( *pNewPattern == *pOldPattern )
|
|
{
|
|
// keep the original pattern (might be default)
|
|
// pNewPattern is deleted below
|
|
nPos++;
|
|
}
|
|
else if ( nY1 < nStartRow || nY2 > nEndRow )
|
|
{
|
|
if (nY1 < nStartRow) nY1=nStartRow;
|
|
if (nY2 > nEndRow) nY2=nEndRow;
|
|
SetPatternArea( nY1, nY2, pNewPattern.get(), true );
|
|
Search( nStart, nPos );
|
|
}
|
|
else
|
|
{
|
|
if ( nCol != -1 )
|
|
{
|
|
// ensure attributing changes text width of cell; otherwise
|
|
// there aren't (yet) template format changes
|
|
const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
|
|
const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
|
|
|
|
bool bNumFormatChanged;
|
|
if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
|
|
rNewSet, rOldSet ) )
|
|
{
|
|
aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 );
|
|
aAdrEnd .SetRow( pData[nPos].nRow );
|
|
pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
|
|
}
|
|
}
|
|
|
|
pDocument->GetPool()->Remove(*pData[nPos].pPattern);
|
|
pData[nPos].pPattern = static_cast<const ScPatternAttr*>(
|
|
&pDocument->GetPool()->Put(*pNewPattern));
|
|
if (Concat(nPos))
|
|
Search(nStart, nPos);
|
|
else
|
|
nPos++;
|
|
}
|
|
}
|
|
while ((nStart <= nEndRow) && (nPos < nCount));
|
|
|
|
if (pDocument->IsStreamValid(nTab))
|
|
pDocument->SetStreamValid(nTab, false);
|
|
}
|
|
|
|
#if DEBUG_SC_TESTATTRARRAY
|
|
TestData();
|
|
#endif
|
|
}
|
|
|
|
// const cast, otherwise it will be too inefficient/complicated
|
|
#define SET_LINECOLOR(dest,c) \
|
|
if ((dest)) \
|
|
{ \
|
|
const_cast<SvxBorderLine*>(dest)->SetColor((c)); \
|
|
}
|
|
|
|
#define SET_LINE(dest,src) \
|
|
if ((dest)) \
|
|
{ \
|
|
SvxBorderLine* pCast = const_cast<SvxBorderLine*>(dest); \
|
|
pCast->SetBorderLineStyle( (src)->GetBorderLineStyle() ); \
|
|
pCast->SetWidth( (src)->GetWidth( ) ); \
|
|
}
|
|
|
|
void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
|
|
const SvxBorderLine* pLine, bool bColorOnly )
|
|
{
|
|
if ( bColorOnly && !pLine )
|
|
return;
|
|
|
|
if (ValidRow(nStartRow) && ValidRow(nEndRow))
|
|
{
|
|
SCSIZE nPos;
|
|
SCROW nStart=0;
|
|
SetDefaultIfNotInit();
|
|
if (!Search( nStartRow, nPos ))
|
|
{
|
|
OSL_FAIL("Search failure");
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[nPos].pPattern;
|
|
const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
|
|
const SfxPoolItem* pBoxItem = nullptr;
|
|
SfxItemState eState = rOldSet.GetItemState( ATTR_BORDER, true, &pBoxItem );
|
|
const SfxPoolItem* pTLBRItem = nullptr;
|
|
SfxItemState eTLBRState = rOldSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem );
|
|
const SfxPoolItem* pBLTRItem = nullptr;
|
|
SfxItemState eBLTRState = rOldSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem );
|
|
|
|
if ( (SfxItemState::SET == eState) || (SfxItemState::SET == eTLBRState) || (SfxItemState::SET == eBLTRState) )
|
|
{
|
|
std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
|
|
SfxItemSet& rNewSet = pNewPattern->GetItemSet();
|
|
SCROW nY1 = nStart;
|
|
SCROW nY2 = pData[nPos].nRow;
|
|
|
|
SvxBoxItem* pNewBoxItem = pBoxItem ? static_cast<SvxBoxItem*>(pBoxItem->Clone()) : nullptr;
|
|
SvxLineItem* pNewTLBRItem = pTLBRItem ? static_cast<SvxLineItem*>(pTLBRItem->Clone()) : nullptr;
|
|
SvxLineItem* pNewBLTRItem = pBLTRItem ? static_cast<SvxLineItem*>(pBLTRItem->Clone()) : nullptr;
|
|
|
|
// fetch line and update attributes with parameters
|
|
|
|
if ( !pLine )
|
|
{
|
|
if( pNewBoxItem )
|
|
{
|
|
if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::TOP );
|
|
if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::BOTTOM );
|
|
if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::LEFT );
|
|
if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::RIGHT );
|
|
}
|
|
if( pNewTLBRItem && pNewTLBRItem->GetLine() )
|
|
pNewTLBRItem->SetLine( nullptr );
|
|
if( pNewBLTRItem && pNewBLTRItem->GetLine() )
|
|
pNewBLTRItem->SetLine( nullptr );
|
|
}
|
|
else
|
|
{
|
|
if ( bColorOnly )
|
|
{
|
|
Color aColor( pLine->GetColor() );
|
|
if( pNewBoxItem )
|
|
{
|
|
SET_LINECOLOR( pNewBoxItem->GetTop(), aColor );
|
|
SET_LINECOLOR( pNewBoxItem->GetBottom(), aColor );
|
|
SET_LINECOLOR( pNewBoxItem->GetLeft(), aColor );
|
|
SET_LINECOLOR( pNewBoxItem->GetRight(), aColor );
|
|
}
|
|
if( pNewTLBRItem )
|
|
SET_LINECOLOR( pNewTLBRItem->GetLine(), aColor );
|
|
if( pNewBLTRItem )
|
|
SET_LINECOLOR( pNewBLTRItem->GetLine(), aColor );
|
|
}
|
|
else
|
|
{
|
|
if( pNewBoxItem )
|
|
{
|
|
SET_LINE( pNewBoxItem->GetTop(), pLine );
|
|
SET_LINE( pNewBoxItem->GetBottom(), pLine );
|
|
SET_LINE( pNewBoxItem->GetLeft(), pLine );
|
|
SET_LINE( pNewBoxItem->GetRight(), pLine );
|
|
}
|
|
if( pNewTLBRItem )
|
|
SET_LINE( pNewTLBRItem->GetLine(), pLine );
|
|
if( pNewBLTRItem )
|
|
SET_LINE( pNewBLTRItem->GetLine(), pLine );
|
|
}
|
|
}
|
|
if( pNewBoxItem ) rNewSet.Put( *pNewBoxItem );
|
|
if( pNewTLBRItem ) rNewSet.Put( *pNewTLBRItem );
|
|
if( pNewBLTRItem ) rNewSet.Put( *pNewBLTRItem );
|
|
|
|
nStart = pData[nPos].nRow + 1;
|
|
|
|
if ( nY1 < nStartRow || nY2 > nEndRow )
|
|
{
|
|
if (nY1 < nStartRow) nY1=nStartRow;
|
|
if (nY2 > nEndRow) nY2=nEndRow;
|
|
SetPatternArea( nY1, nY2, pNewPattern.get(), true );
|
|
Search( nStart, nPos );
|
|
}
|
|
else
|
|
{
|
|
// remove from pool ?
|
|
pDocument->GetPool()->Remove(*pData[nPos].pPattern);
|
|
pData[nPos].pPattern = static_cast<const ScPatternAttr*>(
|
|
&pDocument->GetPool()->Put(*pNewPattern) );
|
|
|
|
if (Concat(nPos))
|
|
Search(nStart, nPos);
|
|
else
|
|
nPos++;
|
|
}
|
|
delete pNewBoxItem;
|
|
delete pNewTLBRItem;
|
|
delete pNewBLTRItem;
|
|
}
|
|
else
|
|
{
|
|
nStart = pData[nPos].nRow + 1;
|
|
nPos++;
|
|
}
|
|
}
|
|
while ((nStart <= nEndRow) && (nPos < nCount));
|
|
}
|
|
}
|
|
|
|
#undef SET_LINECOLOR
|
|
#undef SET_LINE
|
|
|
|
void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache, ScEditDataArray* pDataArray )
|
|
{
|
|
#if DEBUG_SC_TESTATTRARRAY
|
|
TestData();
|
|
#endif
|
|
|
|
if (ValidRow(nStartRow) && ValidRow(nEndRow))
|
|
{
|
|
SCSIZE nPos;
|
|
SCROW nStart=0;
|
|
SetDefaultIfNotInit();
|
|
if (!Search( nStartRow, nPos ))
|
|
{
|
|
OSL_FAIL("Search Failure");
|
|
return;
|
|
}
|
|
|
|
ScAddress aAdrStart( nCol, 0, nTab );
|
|
ScAddress aAdrEnd ( nCol, 0, nTab );
|
|
|
|
do
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[nPos].pPattern;
|
|
const ScPatternAttr* pNewPattern = static_cast<const ScPatternAttr*>( &pCache->ApplyTo( *pOldPattern ) );
|
|
if (pNewPattern != pOldPattern)
|
|
{
|
|
SCROW nY1 = nStart;
|
|
SCROW nY2 = pData[nPos].nRow;
|
|
nStart = pData[nPos].nRow + 1;
|
|
|
|
if ( nY1 < nStartRow || nY2 > nEndRow )
|
|
{
|
|
if (nY1 < nStartRow) nY1=nStartRow;
|
|
if (nY2 > nEndRow) nY2=nEndRow;
|
|
SetPatternArea( nY1, nY2, pNewPattern, false, pDataArray );
|
|
Search( nStart, nPos );
|
|
}
|
|
else
|
|
{
|
|
if ( nCol != -1 )
|
|
{
|
|
// ensure attributing changes text-width of cell
|
|
|
|
const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
|
|
const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
|
|
|
|
bool bNumFormatChanged;
|
|
if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
|
|
rNewSet, rOldSet ) )
|
|
{
|
|
aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 );
|
|
aAdrEnd .SetRow( pData[nPos].nRow );
|
|
pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
|
|
}
|
|
}
|
|
|
|
pDocument->GetPool()->Remove(*pData[nPos].pPattern);
|
|
pData[nPos].pPattern = pNewPattern;
|
|
if (Concat(nPos))
|
|
Search(nStart, nPos);
|
|
else
|
|
++nPos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nStart = pData[nPos].nRow + 1;
|
|
++nPos;
|
|
}
|
|
}
|
|
while (nStart <= nEndRow);
|
|
|
|
if (pDocument->IsStreamValid(nTab))
|
|
pDocument->SetStreamValid(nTab, false);
|
|
}
|
|
|
|
#if DEBUG_SC_TESTATTRARRAY
|
|
TestData();
|
|
#endif
|
|
}
|
|
|
|
void ScAttrArray::SetAttrEntries(ScAttrEntry* pNewData, SCSIZE nSize)
|
|
{
|
|
ScDocumentPool* pDocPool = pDocument->GetPool();
|
|
for (SCSIZE i=0; i<nCount; i++)
|
|
pDocPool->Remove(*pData[i].pPattern);
|
|
|
|
delete[] pData;
|
|
|
|
pData = pNewData;
|
|
nCount = nLimit = nSize;
|
|
}
|
|
|
|
static void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource )
|
|
{
|
|
const SfxPoolItem* pNewItem;
|
|
const SfxPoolItem* pOldItem;
|
|
for (sal_uInt16 nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++)
|
|
{
|
|
// pMergeSet has no parent
|
|
SfxItemState eOldState = rMergeSet.GetItemState( nId, false, &pOldItem );
|
|
|
|
if ( eOldState == SfxItemState::DEFAULT )
|
|
{
|
|
SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
|
|
if ( eNewState == SfxItemState::SET )
|
|
{
|
|
if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) )
|
|
rMergeSet.InvalidateItem( nId );
|
|
}
|
|
}
|
|
else if ( eOldState == SfxItemState::SET ) // Item set
|
|
{
|
|
SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
|
|
if ( eNewState == SfxItemState::SET )
|
|
{
|
|
if ( pNewItem != pOldItem ) // Both pulled
|
|
rMergeSet.InvalidateItem( nId );
|
|
}
|
|
else // Default
|
|
{
|
|
if ( *pOldItem != rSource.GetPool()->GetDefaultItem(nId) )
|
|
rMergeSet.InvalidateItem( nId );
|
|
}
|
|
}
|
|
// Dontcare remains Dontcare
|
|
}
|
|
}
|
|
|
|
void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow,
|
|
ScMergePatternState& rState, bool bDeep ) const
|
|
{
|
|
if (ValidRow(nStartRow) && ValidRow(nEndRow))
|
|
{
|
|
SCSIZE nPos = 0;
|
|
SCROW nStart=0;
|
|
if ( pData && !Search( nStartRow, nPos ) )
|
|
{
|
|
OSL_FAIL("Search failure");
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
// similar patterns must not be repeated
|
|
const ScPatternAttr* pPattern = nullptr;
|
|
if ( pData )
|
|
pPattern = pData[nPos].pPattern;
|
|
else
|
|
pPattern = pDocument->GetDefPattern();
|
|
if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 )
|
|
{
|
|
const SfxItemSet& rThisSet = pPattern->GetItemSet();
|
|
if (rState.pItemSet)
|
|
{
|
|
rState.mbValidPatternId = false;
|
|
if (bDeep)
|
|
lcl_MergeDeep( *rState.pItemSet, rThisSet );
|
|
else
|
|
rState.pItemSet->MergeValues( rThisSet );
|
|
}
|
|
else
|
|
{
|
|
// first pattern - copied from parent
|
|
rState.pItemSet = new SfxItemSet( *rThisSet.GetPool(), rThisSet.GetRanges() );
|
|
rState.pItemSet->Set( rThisSet, bDeep );
|
|
rState.mnPatternId = pPattern->GetKey();
|
|
}
|
|
|
|
rState.pOld2 = rState.pOld1;
|
|
rState.pOld1 = pPattern;
|
|
}
|
|
|
|
if ( pData )
|
|
nStart = pData[nPos].nRow + 1;
|
|
else
|
|
nStart = MAXROW + 1;
|
|
++nPos;
|
|
}
|
|
while (nStart <= nEndRow);
|
|
}
|
|
}
|
|
|
|
// assemble border
|
|
|
|
static bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
|
|
sal_uInt8& rModified, const SvxBorderLine*& rpNew )
|
|
{
|
|
if (rModified == SC_LINE_DONTCARE)
|
|
return false; // don't go again
|
|
|
|
if (rModified == SC_LINE_EMPTY)
|
|
{
|
|
rModified = SC_LINE_SET;
|
|
rpNew = pNewLine;
|
|
return true; // initial value
|
|
}
|
|
|
|
if (pOldLine == pNewLine)
|
|
{
|
|
rpNew = pOldLine;
|
|
return false;
|
|
}
|
|
|
|
if (pOldLine && pNewLine)
|
|
if (*pOldLine == *pNewLine)
|
|
{
|
|
rpNew = pOldLine;
|
|
return false;
|
|
}
|
|
|
|
rModified = SC_LINE_DONTCARE;
|
|
rpNew = nullptr;
|
|
return true; // another line -> don't care
|
|
}
|
|
|
|
static void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
|
|
ScLineFlags& rFlags, const ScPatternAttr* pPattern,
|
|
bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
|
|
{
|
|
// right/bottom border set when connected together
|
|
const ScMergeAttr& rMerge = static_cast<const ScMergeAttr&>(pPattern->GetItem(ATTR_MERGE));
|
|
if ( rMerge.GetColMerge() == nDistRight + 1 )
|
|
nDistRight = 0;
|
|
if ( rMerge.GetRowMerge() == nDistBottom + 1 )
|
|
nDistBottom = 0;
|
|
|
|
const SvxBoxItem* pCellFrame = static_cast<const SvxBoxItem*>( &pPattern->GetItemSet().Get( ATTR_BORDER ) );
|
|
const SvxBorderLine* pLeftAttr = pCellFrame->GetLeft();
|
|
const SvxBorderLine* pRightAttr = pCellFrame->GetRight();
|
|
const SvxBorderLine* pTopAttr = pCellFrame->GetTop();
|
|
const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom();
|
|
const SvxBorderLine* pNew;
|
|
|
|
if (bTop)
|
|
{
|
|
if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew ))
|
|
pLineOuter->SetLine( pNew, SvxBoxItemLine::TOP );
|
|
}
|
|
else
|
|
{
|
|
if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew ))
|
|
pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
|
|
}
|
|
|
|
if (nDistBottom == 0)
|
|
{
|
|
if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew ))
|
|
pLineOuter->SetLine( pNew, SvxBoxItemLine::BOTTOM );
|
|
}
|
|
else
|
|
{
|
|
if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew ))
|
|
pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
|
|
}
|
|
|
|
if (bLeft)
|
|
{
|
|
if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew ))
|
|
pLineOuter->SetLine( pNew, SvxBoxItemLine::LEFT );
|
|
}
|
|
else
|
|
{
|
|
if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew ))
|
|
pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
|
|
}
|
|
|
|
if (nDistRight == 0)
|
|
{
|
|
if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew ))
|
|
pLineOuter->SetLine( pNew, SvxBoxItemLine::RIGHT );
|
|
}
|
|
else
|
|
{
|
|
if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew ))
|
|
pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
|
|
}
|
|
}
|
|
|
|
void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
|
|
ScLineFlags& rFlags,
|
|
SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight ) const
|
|
{
|
|
const ScPatternAttr* pPattern;
|
|
|
|
if (nStartRow == nEndRow)
|
|
{
|
|
pPattern = GetPattern( nStartRow );
|
|
lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true, 0 );
|
|
}
|
|
else if ( pData ) // non-default pattern
|
|
{
|
|
pPattern = GetPattern( nStartRow );
|
|
lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true,
|
|
nEndRow-nStartRow );
|
|
|
|
SCSIZE nStartIndex;
|
|
SCSIZE nEndIndex;
|
|
Search( nStartRow+1, nStartIndex );
|
|
Search( nEndRow-1, nEndIndex );
|
|
for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
|
|
{
|
|
pPattern = pData[i].pPattern;
|
|
lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false,
|
|
nEndRow - std::min( pData[i].nRow, (SCROW)(nEndRow-1) ) );
|
|
// nDistBottom here always > 0
|
|
}
|
|
|
|
pPattern = GetPattern( nEndRow );
|
|
lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, 0 );
|
|
}
|
|
else
|
|
{
|
|
lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pDocument->GetDefPattern(), bLeft, nDistRight, true, 0 );
|
|
}
|
|
}
|
|
|
|
// apply border
|
|
|
|
// ApplyFrame - on an entry into the array
|
|
|
|
bool ScAttrArray::ApplyFrame( const SvxBoxItem* pBoxItem,
|
|
const SvxBoxInfoItem* pBoxInfoItem,
|
|
SCROW nStartRow, SCROW nEndRow,
|
|
bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
|
|
{
|
|
OSL_ENSURE( pBoxItem && pBoxInfoItem, "Missing line attributes!" );
|
|
|
|
const ScPatternAttr* pPattern = GetPattern( nStartRow );
|
|
const SvxBoxItem* pOldFrame = static_cast<const SvxBoxItem*>(
|
|
&pPattern->GetItemSet().Get( ATTR_BORDER ));
|
|
|
|
// right/bottom border set when connected together
|
|
const ScMergeAttr& rMerge = static_cast<const ScMergeAttr&>(pPattern->GetItem(ATTR_MERGE));
|
|
if ( rMerge.GetColMerge() == nDistRight + 1 )
|
|
nDistRight = 0;
|
|
if ( rMerge.GetRowMerge() == nDistBottom + 1 )
|
|
nDistBottom = 0;
|
|
|
|
SvxBoxItem aNewFrame( *pOldFrame );
|
|
bool bRTL=pDocument->IsLayoutRTL(nTab);
|
|
// fdo#37464 check if the sheet are RTL then replace right <=> left
|
|
if (bRTL)
|
|
{
|
|
if( bLeft && nDistRight==0)
|
|
{
|
|
if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
|
|
aNewFrame.SetLine( pBoxItem->GetLeft(), SvxBoxItemLine::RIGHT );
|
|
if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
|
|
aNewFrame.SetLine( pBoxItem->GetRight(), SvxBoxItemLine::LEFT );
|
|
}
|
|
else
|
|
{
|
|
if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
|
|
aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(),
|
|
SvxBoxItemLine::RIGHT );
|
|
if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
|
|
aNewFrame.SetLine( bLeft ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(),
|
|
SvxBoxItemLine::LEFT );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
|
|
aNewFrame.SetLine( bLeft ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(),
|
|
SvxBoxItemLine::LEFT );
|
|
if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
|
|
aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(),
|
|
SvxBoxItemLine::RIGHT );
|
|
}
|
|
if ( bTop ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
|
|
aNewFrame.SetLine( bTop ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(),
|
|
SvxBoxItemLine::TOP );
|
|
if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
|
|
aNewFrame.SetLine( (nDistBottom==0) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(),
|
|
SvxBoxItemLine::BOTTOM );
|
|
|
|
if (aNewFrame == *pOldFrame)
|
|
{
|
|
// nothing to do
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
SfxItemPoolCache aCache( pDocument->GetPool(), &aNewFrame );
|
|
ApplyCacheArea( nStartRow, nEndRow, &aCache );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void ScAttrArray::ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner,
|
|
SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight )
|
|
{
|
|
if (nStartRow == nEndRow)
|
|
ApplyFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0 );
|
|
else if ( pData )
|
|
{
|
|
ApplyFrame( pLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight,
|
|
true, nEndRow-nStartRow );
|
|
|
|
if ( nEndRow > nStartRow+1 ) // inner part available?
|
|
{
|
|
SCSIZE nStartIndex;
|
|
SCSIZE nEndIndex;
|
|
Search( nStartRow+1, nStartIndex );
|
|
Search( nEndRow-1, nEndIndex );
|
|
SCROW nTmpStart = nStartRow+1;
|
|
SCROW nTmpEnd;
|
|
for (SCSIZE i=nStartIndex; i<=nEndIndex;)
|
|
{
|
|
nTmpEnd = std::min( (SCROW)(nEndRow-1), (SCROW)(pData[i].nRow) );
|
|
bool bChanged = ApplyFrame( pLineOuter, pLineInner, nTmpStart, nTmpEnd,
|
|
bLeft, nDistRight, false, nEndRow-nTmpEnd );
|
|
nTmpStart = nTmpEnd+1;
|
|
if (bChanged)
|
|
{
|
|
Search(nTmpStart, i);
|
|
Search(nEndRow-1, nEndIndex);
|
|
}
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
ApplyFrame( pLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, false, 0 );
|
|
}
|
|
else
|
|
{
|
|
ApplyFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0 );
|
|
}
|
|
}
|
|
|
|
// Test if field contains specific attribute
|
|
|
|
bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) const
|
|
{
|
|
if ( !pData )
|
|
return false;
|
|
|
|
SCSIZE nStartIndex;
|
|
SCSIZE nEndIndex;
|
|
Search( nRow1, nStartIndex );
|
|
if (nRow1 != nRow2)
|
|
Search( nRow2, nEndIndex );
|
|
else
|
|
nEndIndex = nStartIndex;
|
|
bool bFound = false;
|
|
|
|
for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++)
|
|
{
|
|
const ScPatternAttr* pPattern = pData[i].pPattern;
|
|
if ( nMask & HasAttrFlags::Merged )
|
|
{
|
|
const ScMergeAttr* pMerge =
|
|
static_cast<const ScMergeAttr*>( &pPattern->GetItem( ATTR_MERGE ) );
|
|
if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & ( HasAttrFlags::Overlapped | HasAttrFlags::NotOverlapped | HasAttrFlags::AutoFilter ) )
|
|
{
|
|
const ScMergeFlagAttr* pMergeFlag =
|
|
static_cast<const ScMergeFlagAttr*>( &pPattern->GetItem( ATTR_MERGE_FLAG ) );
|
|
if ( (nMask & HasAttrFlags::Overlapped) && pMergeFlag->IsOverlapped() )
|
|
bFound = true;
|
|
if ( (nMask & HasAttrFlags::NotOverlapped) && !pMergeFlag->IsOverlapped() )
|
|
bFound = true;
|
|
if ( (nMask & HasAttrFlags::AutoFilter) && pMergeFlag->HasAutoFilter() )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::Lines )
|
|
{
|
|
const SvxBoxItem* pBox =
|
|
static_cast<const SvxBoxItem*>( &pPattern->GetItem( ATTR_BORDER ) );
|
|
if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::Shadow )
|
|
{
|
|
const SvxShadowItem* pShadow =
|
|
static_cast<const SvxShadowItem*>( &pPattern->GetItem( ATTR_SHADOW ) );
|
|
if ( pShadow->GetLocation() != SVX_SHADOW_NONE )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::Conditional )
|
|
{
|
|
bool bContainsCondFormat =
|
|
!static_cast<const ScCondFormatItem&>(pPattern->GetItem( ATTR_CONDITIONAL )).GetCondFormatData().empty();
|
|
if ( bContainsCondFormat )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::Protected )
|
|
{
|
|
const ScProtectionAttr* pProtect =
|
|
static_cast<const ScProtectionAttr*>( &pPattern->GetItem( ATTR_PROTECTION ) );
|
|
bool bFoundTemp = false;
|
|
if ( pProtect->GetProtection() || pProtect->GetHideCell() )
|
|
bFoundTemp = true;
|
|
|
|
bool bContainsCondFormat =
|
|
!static_cast<const ScCondFormatItem&>(pPattern->GetItem( ATTR_CONDITIONAL )).GetCondFormatData().empty();
|
|
if ( bContainsCondFormat && nCol != -1 ) // pDocument->GetCondResult() is valid only for real columns.
|
|
{
|
|
SCROW nRowStartCond = std::max<SCROW>( nRow1, i ? pData[i-1].nRow + 1: 0 );
|
|
SCROW nRowEndCond = std::min<SCROW>( nRow2, pData[i].nRow );
|
|
bool bFoundCond = false;
|
|
for(SCROW nRowCond = nRowStartCond; nRowCond <= nRowEndCond && !bFoundCond; ++nRowCond)
|
|
{
|
|
const SfxItemSet* pSet = pDocument->GetCondResult( nCol, nRowCond, nTab );
|
|
|
|
const SfxPoolItem* pItem;
|
|
if( pSet && pSet->GetItemState( ATTR_PROTECTION, true, &pItem ) == SfxItemState::SET )
|
|
{
|
|
const ScProtectionAttr* pCondProtect = static_cast<const ScProtectionAttr*>(pItem);
|
|
if( pCondProtect->GetProtection() || pCondProtect->GetHideCell() )
|
|
bFoundCond = true;
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// well it is not true that we found one
|
|
// but existing one + cell where conditional
|
|
// formatting does not remove it
|
|
// => we should use the existing protection setting
|
|
bFoundCond = bFoundTemp;
|
|
}
|
|
}
|
|
bFoundTemp = bFoundCond;
|
|
}
|
|
|
|
if(bFoundTemp)
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::Rotate )
|
|
{
|
|
const SfxInt32Item* pRotate =
|
|
static_cast<const SfxInt32Item*>( &pPattern->GetItem( ATTR_ROTATE_VALUE ) );
|
|
// 90 or 270 degrees is former SvxOrientationItem - only look for other values
|
|
// (see ScPatternAttr::GetCellOrientation)
|
|
sal_Int32 nAngle = pRotate->GetValue();
|
|
if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::NeedHeight )
|
|
{
|
|
if (pPattern->GetCellOrientation() != SVX_ORIENTATION_STANDARD)
|
|
bFound = true;
|
|
else if (static_cast<const SfxBoolItem&>(pPattern->GetItem( ATTR_LINEBREAK )).GetValue())
|
|
bFound = true;
|
|
else if ((SvxCellHorJustify)static_cast<const SvxHorJustifyItem&>(pPattern->
|
|
GetItem( ATTR_HOR_JUSTIFY )).GetValue() == SVX_HOR_JUSTIFY_BLOCK)
|
|
bFound = true;
|
|
|
|
else if (!static_cast<const ScCondFormatItem&>(pPattern->GetItem(ATTR_CONDITIONAL)).GetCondFormatData().empty())
|
|
bFound = true;
|
|
else if (static_cast<const SfxInt32Item&>(pPattern->GetItem( ATTR_ROTATE_VALUE )).GetValue())
|
|
bFound = true;
|
|
}
|
|
if ( nMask & ( HasAttrFlags::ShadowRight | HasAttrFlags::ShadowDown ) )
|
|
{
|
|
const SvxShadowItem* pShadow =
|
|
static_cast<const SvxShadowItem*>( &pPattern->GetItem( ATTR_SHADOW ));
|
|
SvxShadowLocation eLoc = pShadow->GetLocation();
|
|
if ( nMask & HasAttrFlags::ShadowRight )
|
|
if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT )
|
|
bFound = true;
|
|
if ( nMask & HasAttrFlags::ShadowDown )
|
|
if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT )
|
|
bFound = true;
|
|
}
|
|
if ( nMask & HasAttrFlags::RightOrCenter )
|
|
{
|
|
// called only if the sheet is LTR, so physical=logical alignment can be assumed
|
|
SvxCellHorJustify eHorJust = (SvxCellHorJustify)
|
|
static_cast<const SvxHorJustifyItem&>( pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue();
|
|
if ( eHorJust == SVX_HOR_JUSTIFY_RIGHT || eHorJust == SVX_HOR_JUSTIFY_CENTER )
|
|
bFound = true;
|
|
}
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
bool ScAttrArray::IsMerged( SCROW nRow ) const
|
|
{
|
|
if ( pData )
|
|
{
|
|
SCSIZE nIndex;
|
|
Search(nRow, nIndex);
|
|
const ScMergeAttr& rItem =
|
|
static_cast<const ScMergeAttr&>(pData[nIndex].pPattern->GetItem(ATTR_MERGE));
|
|
|
|
return rItem.IsMerged();
|
|
}
|
|
|
|
return static_cast<const ScMergeAttr&>(pDocument->GetDefPattern()->GetItem(ATTR_MERGE)).IsMerged();
|
|
}
|
|
|
|
/**
|
|
* Area around any given summaries expand and adapt any MergeFlag (bRefresh)
|
|
*/
|
|
bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
|
|
SCCOL& rPaintCol, SCROW& rPaintRow,
|
|
bool bRefresh )
|
|
{
|
|
assert( nCol != -1 );
|
|
SetDefaultIfNotInit();
|
|
const ScPatternAttr* pPattern;
|
|
const ScMergeAttr* pItem;
|
|
SCSIZE nStartIndex;
|
|
SCSIZE nEndIndex;
|
|
Search( nStartRow, nStartIndex );
|
|
Search( nEndRow, nEndIndex );
|
|
bool bFound = false;
|
|
|
|
for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
|
|
{
|
|
pPattern = pData[i].pPattern;
|
|
pItem = static_cast<const ScMergeAttr*>( &pPattern->GetItem( ATTR_MERGE ) );
|
|
SCsCOL nCountX = pItem->GetColMerge();
|
|
SCsROW nCountY = pItem->GetRowMerge();
|
|
if (nCountX>1 || nCountY>1)
|
|
{
|
|
SCROW nThisRow = (i>0) ? pData[i-1].nRow+1 : 0;
|
|
SCCOL nMergeEndCol = nThisCol + nCountX - 1;
|
|
SCROW nMergeEndRow = nThisRow + nCountY - 1;
|
|
if (nMergeEndCol > rPaintCol && nMergeEndCol <= MAXCOL)
|
|
rPaintCol = nMergeEndCol;
|
|
if (nMergeEndRow > rPaintRow && nMergeEndRow <= MAXROW)
|
|
rPaintRow = nMergeEndRow;
|
|
bFound = true;
|
|
|
|
if (bRefresh)
|
|
{
|
|
if ( nMergeEndCol > nThisCol )
|
|
pDocument->ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, pData[i].nRow,
|
|
nTab, ScMF::Hor );
|
|
if ( nMergeEndRow > nThisRow )
|
|
pDocument->ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow,
|
|
nTab, ScMF::Ver );
|
|
if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow )
|
|
pDocument->ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow,
|
|
nTab, ScMF::Hor | ScMF::Ver );
|
|
|
|
Search( nThisRow, i ); // Data changed
|
|
Search( nStartRow, nStartIndex );
|
|
Search( nEndRow, nEndIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
void ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
|
|
{
|
|
assert( nCol != -1 );
|
|
SetDefaultIfNotInit();
|
|
const ScPatternAttr* pPattern;
|
|
const ScMergeAttr* pItem;
|
|
SCSIZE nIndex;
|
|
|
|
Search( nStartRow, nIndex );
|
|
SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nThisStart < nStartRow)
|
|
nThisStart = nStartRow;
|
|
|
|
while ( nThisStart <= nEndRow )
|
|
{
|
|
SCROW nThisEnd = pData[nIndex].nRow;
|
|
if (nThisEnd > nEndRow)
|
|
nThisEnd = nEndRow;
|
|
|
|
pPattern = pData[nIndex].pPattern;
|
|
pItem = static_cast<const ScMergeAttr*>( &pPattern->GetItem( ATTR_MERGE ) );
|
|
SCsCOL nCountX = pItem->GetColMerge();
|
|
SCsROW nCountY = pItem->GetRowMerge();
|
|
if (nCountX>1 || nCountY>1)
|
|
{
|
|
const ScMergeAttr* pAttr = static_cast<const ScMergeAttr*>(
|
|
&pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ) );
|
|
const ScMergeFlagAttr* pFlagAttr = static_cast<const ScMergeFlagAttr*>(
|
|
&pDocument->GetPool()->GetDefaultItem( ATTR_MERGE_FLAG ));
|
|
|
|
OSL_ENSURE( nCountY==1 || nThisStart==nThisEnd, "What's up?" );
|
|
|
|
SCCOL nThisCol = nCol;
|
|
SCCOL nMergeEndCol = nThisCol + nCountX - 1;
|
|
SCROW nMergeEndRow = nThisEnd + nCountY - 1;
|
|
|
|
// ApplyAttr for areas
|
|
for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++)
|
|
pDocument->ApplyAttr( nThisCol, nThisRow, nTab, *pAttr );
|
|
|
|
std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( pDocument->GetPool() ));
|
|
SfxItemSet* pSet = &pNewPattern->GetItemSet();
|
|
pSet->Put( *pFlagAttr );
|
|
pDocument->ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow,
|
|
nTab, *pNewPattern );
|
|
pNewPattern.reset();
|
|
|
|
Search( nThisEnd, nIndex ); // data changed
|
|
}
|
|
|
|
++nIndex;
|
|
if ( nIndex < nCount )
|
|
nThisStart = pData[nIndex-1].nRow+1;
|
|
else
|
|
nThisStart = MAXROW+1; // End
|
|
}
|
|
}
|
|
|
|
void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow,
|
|
const ScPatternAttr* pWantedPattern, bool bDefault )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
const ScPatternAttr* pOldPattern;
|
|
const ScMergeFlagAttr* pItem;
|
|
|
|
SCSIZE nIndex;
|
|
SCROW nRow;
|
|
SCROW nThisRow;
|
|
bool bFirstUse = true;
|
|
|
|
Search( nStartRow, nIndex );
|
|
nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
while ( nThisRow <= nEndRow )
|
|
{
|
|
pOldPattern = pData[nIndex].pPattern;
|
|
if (pOldPattern != pWantedPattern) // FIXME: else-branch?
|
|
{
|
|
if (nThisRow < nStartRow) nThisRow = nStartRow;
|
|
nRow = pData[nIndex].nRow;
|
|
SCROW nAttrRow = std::min( (SCROW)nRow, (SCROW)nEndRow );
|
|
pItem = static_cast<const ScMergeFlagAttr*>( &pOldPattern->GetItem( ATTR_MERGE_FLAG ) );
|
|
|
|
if (pItem->IsOverlapped() || pItem->HasAutoFilter())
|
|
{
|
|
// default-constructing a ScPatternAttr for DeleteArea doesn't work
|
|
// because it would have no cell style information.
|
|
// Instead, the document's GetDefPattern is copied. Since it is passed as
|
|
// pWantedPattern, no special treatment of default is needed here anymore.
|
|
std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( *pWantedPattern ));
|
|
SfxItemSet* pSet = &pNewPattern->GetItemSet();
|
|
pSet->Put( *pItem );
|
|
SetPatternArea( nThisRow, nAttrRow, pNewPattern.get(), true );
|
|
}
|
|
else
|
|
{
|
|
if ( !bDefault )
|
|
{
|
|
if (bFirstUse)
|
|
bFirstUse = false;
|
|
else
|
|
// it's in the pool
|
|
pDocument->GetPool()->Put( *pWantedPattern );
|
|
}
|
|
SetPatternArea( nThisRow, nAttrRow, pWantedPattern );
|
|
}
|
|
|
|
Search( nThisRow, nIndex ); // data changed
|
|
}
|
|
|
|
++nIndex;
|
|
nThisRow = pData[nIndex-1].nRow+1;
|
|
}
|
|
}
|
|
|
|
bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
const ScPatternAttr* pOldPattern;
|
|
|
|
ScMF nOldValue;
|
|
SCSIZE nIndex;
|
|
SCROW nRow;
|
|
SCROW nThisRow;
|
|
bool bChanged = false;
|
|
|
|
Search( nStartRow, nIndex );
|
|
nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nThisRow < nStartRow) nThisRow = nStartRow;
|
|
|
|
while ( nThisRow <= nEndRow )
|
|
{
|
|
pOldPattern = pData[nIndex].pPattern;
|
|
nOldValue = static_cast<const ScMergeFlagAttr*>( &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue();
|
|
if ( (nOldValue | nFlags) != nOldValue )
|
|
{
|
|
nRow = pData[nIndex].nRow;
|
|
SCROW nAttrRow = std::min( (SCROW)nRow, (SCROW)nEndRow );
|
|
ScPatternAttr aNewPattern(*pOldPattern);
|
|
aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) );
|
|
SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true );
|
|
Search( nThisRow, nIndex ); // data changed
|
|
bChanged = true;
|
|
}
|
|
|
|
++nIndex;
|
|
nThisRow = pData[nIndex-1].nRow+1;
|
|
}
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
const ScPatternAttr* pOldPattern;
|
|
|
|
ScMF nOldValue;
|
|
SCSIZE nIndex;
|
|
SCROW nRow;
|
|
SCROW nThisRow;
|
|
bool bChanged = false;
|
|
|
|
Search( nStartRow, nIndex );
|
|
nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nThisRow < nStartRow) nThisRow = nStartRow;
|
|
|
|
while ( nThisRow <= nEndRow )
|
|
{
|
|
pOldPattern = pData[nIndex].pPattern;
|
|
nOldValue = static_cast<const ScMergeFlagAttr*>(&pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue();
|
|
if ( (nOldValue & ~nFlags) != nOldValue )
|
|
{
|
|
nRow = pData[nIndex].nRow;
|
|
SCROW nAttrRow = std::min( (SCROW)nRow, (SCROW)nEndRow );
|
|
ScPatternAttr aNewPattern(*pOldPattern);
|
|
aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) );
|
|
SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true );
|
|
Search( nThisRow, nIndex ); // data changed
|
|
bChanged = true;
|
|
}
|
|
|
|
++nIndex;
|
|
nThisRow = pData[nIndex-1].nRow+1;
|
|
}
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
SCSIZE nIndex;
|
|
SCROW nRow;
|
|
SCROW nThisRow;
|
|
|
|
Search( nStartRow, nIndex );
|
|
nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nThisRow < nStartRow) nThisRow = nStartRow;
|
|
|
|
while ( nThisRow <= nEndRow )
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
|
|
if ( pOldPattern->HasItemsSet( pWhich ) )
|
|
{
|
|
ScPatternAttr aNewPattern(*pOldPattern);
|
|
aNewPattern.ClearItems( pWhich );
|
|
|
|
nRow = pData[nIndex].nRow;
|
|
SCROW nAttrRow = std::min( (SCROW)nRow, (SCROW)nEndRow );
|
|
SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true );
|
|
Search( nThisRow, nIndex ); // data changed
|
|
}
|
|
|
|
++nIndex;
|
|
nThisRow = pData[nIndex-1].nRow+1;
|
|
}
|
|
}
|
|
|
|
void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, bool bIncrement )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
SCSIZE nIndex;
|
|
Search( nStartRow, nIndex );
|
|
SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nThisStart < nStartRow) nThisStart = nStartRow;
|
|
|
|
while ( nThisStart <= nEndRow )
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
|
|
const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
|
|
const SfxPoolItem* pItem;
|
|
|
|
bool bNeedJust = ( rOldSet.GetItemState( ATTR_HOR_JUSTIFY, false, &pItem ) != SfxItemState::SET
|
|
|| (static_cast<const SvxHorJustifyItem*>(pItem)->GetValue() != SVX_HOR_JUSTIFY_LEFT &&
|
|
static_cast<const SvxHorJustifyItem*>(pItem)->GetValue() != SVX_HOR_JUSTIFY_RIGHT ));
|
|
sal_uInt16 nOldValue = static_cast<const SfxUInt16Item&>(rOldSet.Get( ATTR_INDENT )).GetValue();
|
|
sal_uInt16 nNewValue = nOldValue;
|
|
// To keep Increment indent from running outside the cell1659
|
|
long nColWidth = (long)pDocument->GetColWidth(nCol,nTab);
|
|
if ( bIncrement )
|
|
{
|
|
if ( nNewValue < nColWidth-SC_INDENT_STEP )
|
|
{
|
|
nNewValue += SC_INDENT_STEP;
|
|
if ( nNewValue > nColWidth-SC_INDENT_STEP ) nNewValue = nColWidth-SC_INDENT_STEP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( nNewValue > 0 )
|
|
{
|
|
if ( nNewValue > SC_INDENT_STEP )
|
|
nNewValue -= SC_INDENT_STEP;
|
|
else
|
|
nNewValue = 0;
|
|
}
|
|
}
|
|
|
|
if ( bNeedJust || nNewValue != nOldValue )
|
|
{
|
|
SCROW nThisEnd = pData[nIndex].nRow;
|
|
SCROW nAttrRow = std::min( nThisEnd, nEndRow );
|
|
ScPatternAttr aNewPattern(*pOldPattern);
|
|
aNewPattern.GetItemSet().Put( SfxUInt16Item( ATTR_INDENT, nNewValue ) );
|
|
if ( bNeedJust )
|
|
aNewPattern.GetItemSet().Put(
|
|
SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) );
|
|
SetPatternArea( nThisStart, nAttrRow, &aNewPattern, true );
|
|
|
|
nThisStart = nThisEnd + 1;
|
|
Search( nThisStart, nIndex ); // data changed
|
|
}
|
|
else
|
|
{
|
|
nThisStart = pData[nIndex].nRow + 1;
|
|
++nIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
SCsROW ScAttrArray::GetNextUnprotected( SCsROW nRow, bool bUp ) const
|
|
{
|
|
long nRet = nRow;
|
|
if (ValidRow(nRow))
|
|
{
|
|
if ( !pData )
|
|
{
|
|
if ( bUp )
|
|
return -1;
|
|
else
|
|
return MAXROW+1;
|
|
}
|
|
|
|
SCSIZE nIndex;
|
|
Search(nRow, nIndex);
|
|
while (static_cast<const ScProtectionAttr&>(pData[nIndex].pPattern->
|
|
GetItem(ATTR_PROTECTION)).GetProtection())
|
|
{
|
|
if (bUp)
|
|
{
|
|
if (nIndex==0)
|
|
return -1; // not found
|
|
--nIndex;
|
|
nRet = pData[nIndex].nRow;
|
|
}
|
|
else
|
|
{
|
|
nRet = pData[nIndex].nRow+1;
|
|
++nIndex;
|
|
if (nIndex>=nCount)
|
|
return MAXROW+1; // not found
|
|
}
|
|
}
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
SCROW nStart = 0;
|
|
SCSIZE nPos = 0;
|
|
while (nPos < nCount)
|
|
{
|
|
SCROW nEnd = pData[nPos].nRow;
|
|
if (pData[nPos].pPattern->GetStyleSheet() == pStyleSheet)
|
|
{
|
|
rUsedRows.setTrue(nStart, nEnd);
|
|
|
|
if (bReset)
|
|
{
|
|
std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pData[nPos].pPattern));
|
|
pDocument->GetPool()->Remove(*pData[nPos].pPattern);
|
|
pNewPattern->SetStyleSheet( static_cast<ScStyleSheet*>(
|
|
pDocument->GetStyleSheetPool()->
|
|
Find( ScGlobal::GetRscString(STR_STYLENAME_STANDARD),
|
|
SfxStyleFamily::Para,
|
|
SFXSTYLEBIT_AUTO | SCSTYLEBIT_STANDARD ) ) );
|
|
pData[nPos].pPattern = static_cast<const ScPatternAttr*>(
|
|
&pDocument->GetPool()->Put(*pNewPattern));
|
|
pNewPattern.reset();
|
|
|
|
if (Concat(nPos))
|
|
{
|
|
Search(nStart, nPos);
|
|
--nPos; // because ++ at end
|
|
}
|
|
}
|
|
}
|
|
nStart = nEnd + 1;
|
|
++nPos;
|
|
}
|
|
}
|
|
|
|
bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
|
|
{
|
|
if ( !pData )
|
|
{
|
|
const ScStyleSheet* pStyle = pDocument->GetDefPattern()->GetStyleSheet();
|
|
if ( pStyle )
|
|
{
|
|
pStyle->SetUsage( ScStyleSheet::USED );
|
|
if ( pStyle == &rStyle )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool bIsUsed = false;
|
|
SCSIZE nPos = 0;
|
|
|
|
while ( nPos < nCount )
|
|
{
|
|
const ScStyleSheet* pStyle = pData[nPos].pPattern->GetStyleSheet();
|
|
if ( pStyle )
|
|
{
|
|
pStyle->SetUsage( ScStyleSheet::USED );
|
|
if ( pStyle == &rStyle )
|
|
{
|
|
bIsUsed = true;
|
|
}
|
|
}
|
|
nPos++;
|
|
}
|
|
|
|
return bIsUsed;
|
|
}
|
|
|
|
bool ScAttrArray::IsEmpty() const
|
|
{
|
|
if ( !pData )
|
|
return true;
|
|
|
|
if (nCount == 1)
|
|
{
|
|
if ( pData[0].pPattern != pDocument->GetDefPattern() )
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
|
|
{
|
|
if ( !pData )
|
|
return false;
|
|
|
|
OSL_ENSURE( nCount, "nCount == 0" );
|
|
|
|
bool bFound = false;
|
|
SCSIZE nStart = 0;
|
|
|
|
// Skip first entry if more than 1 row.
|
|
// Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr.
|
|
|
|
SCSIZE nVisStart = 1;
|
|
while ( nVisStart < nCount && pData[nVisStart].pPattern->IsVisibleEqual(*pData[nVisStart-1].pPattern) )
|
|
++nVisStart;
|
|
if ( nVisStart >= nCount || pData[nVisStart-1].nRow > 0 ) // more than 1 row?
|
|
nStart = nVisStart;
|
|
|
|
while ( nStart < nCount && !bFound )
|
|
{
|
|
if ( pData[nStart].pPattern->IsVisible() )
|
|
{
|
|
rFirstRow = nStart ? ( pData[nStart-1].nRow + 1 ) : 0;
|
|
bFound = true;
|
|
}
|
|
else
|
|
++nStart;
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
// Number of rows after the search will be stopped
|
|
// (more than a default page size, 2*42 because it's as good as any number)
|
|
|
|
const SCROW SC_VISATTR_STOP = 84;
|
|
|
|
bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const
|
|
{
|
|
if ( !pData )
|
|
{
|
|
rLastRow = nLastData;
|
|
return false;
|
|
}
|
|
|
|
OSL_ENSURE( nCount, "nCount == 0" );
|
|
|
|
// #i30830# changed behavior:
|
|
// ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows
|
|
// below the last content cell
|
|
|
|
if ( nLastData == MAXROW )
|
|
{
|
|
rLastRow = MAXROW; // can't look for attributes below MAXROW
|
|
return true;
|
|
}
|
|
|
|
// Quick check: last data row in or immediately preceding a run that is the
|
|
// last attribution down to the end, e.g. default style or column style.
|
|
SCSIZE nPos = nCount - 1;
|
|
SCROW nStartRow = (nPos ? pData[nPos-1].nRow + 1 : 0);
|
|
if (nStartRow <= nLastData + 1)
|
|
{
|
|
// Ignore here a few rows if data happens to end within
|
|
// SC_VISATTR_STOP rows before MAXROW.
|
|
rLastRow = nLastData;
|
|
return false;
|
|
}
|
|
|
|
// Find a run below last data row.
|
|
bool bFound = false;
|
|
Search( nLastData, nPos );
|
|
while ( nPos < nCount )
|
|
{
|
|
// find range of visually equal formats
|
|
SCSIZE nEndPos = nPos;
|
|
while ( nEndPos < nCount-1 &&
|
|
pData[nEndPos].pPattern->IsVisibleEqual( *pData[nEndPos+1].pPattern))
|
|
++nEndPos;
|
|
SCROW nAttrStartRow = ( nPos > 0 ) ? ( pData[nPos-1].nRow + 1 ) : 0;
|
|
if ( nAttrStartRow <= nLastData )
|
|
nAttrStartRow = nLastData + 1;
|
|
SCROW nAttrSize = pData[nEndPos].nRow + 1 - nAttrStartRow;
|
|
if ( pData[nEndPos].pPattern->IsVisible() )
|
|
{
|
|
rLastRow = pData[nEndPos].nRow;
|
|
bFound = true;
|
|
}
|
|
// We are not ignoring range for current column,
|
|
// if it is larger than SC_VISATTR_STOP, because it is still in default page size range.
|
|
// We are not checking next columns, due to performance reasons.
|
|
if ( nAttrSize >= SC_VISATTR_STOP )
|
|
break;
|
|
nPos = nEndPos + 1;
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
|
|
{
|
|
if ( !pData )
|
|
return pDocument->GetDefPattern()->IsVisible();
|
|
|
|
SCSIZE nIndex;
|
|
Search( nStartRow, nIndex );
|
|
SCROW nThisStart = nStartRow;
|
|
bool bFound = false;
|
|
while ( nIndex < nCount && nThisStart <= nEndRow && !bFound )
|
|
{
|
|
if ( pData[nIndex].pPattern->IsVisible() )
|
|
bFound = true;
|
|
|
|
nThisStart = pData[nIndex].nRow + 1;
|
|
++nIndex;
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
|
|
SCROW nStartRow, SCROW nEndRow ) const
|
|
{
|
|
if ( !pData && !rOther.pData )
|
|
{
|
|
const ScPatternAttr* pDefPattern1 = pDocument->GetDefPattern();
|
|
const ScPatternAttr* pDefPattern2 = rOther.pDocument->GetDefPattern();
|
|
return ( pDefPattern1 == pDefPattern2 || pDefPattern1->IsVisibleEqual( *pDefPattern2 ) );
|
|
}
|
|
|
|
{
|
|
const ScAttrArray* pNonDefault = nullptr;
|
|
const ScPatternAttr* pDefPattern = nullptr;
|
|
bool bDefNonDefCase = false;
|
|
if ( !pData && rOther.pData )
|
|
{
|
|
pNonDefault = &rOther;
|
|
pDefPattern = pDocument->GetDefPattern();
|
|
bDefNonDefCase = true;
|
|
}
|
|
else if ( pData && !rOther.pData )
|
|
{
|
|
pNonDefault = this;
|
|
pDefPattern = rOther.pDocument->GetDefPattern();
|
|
bDefNonDefCase = true;
|
|
}
|
|
|
|
if ( bDefNonDefCase )
|
|
{
|
|
bool bEqual = true;
|
|
SCSIZE nPos = 0;
|
|
if ( nStartRow > 0 )
|
|
pNonDefault->Search( nStartRow, nPos );
|
|
|
|
while ( nPos < pNonDefault->nCount && bEqual )
|
|
{
|
|
const ScPatternAttr* pNonDefPattern = pNonDefault->pData[nPos].pPattern;
|
|
bEqual = ( pNonDefPattern == pDefPattern ||
|
|
pNonDefPattern->IsVisibleEqual( *pDefPattern ) );
|
|
|
|
if ( pNonDefault->pData[nPos].nRow >= nEndRow ) break;
|
|
++nPos;
|
|
}
|
|
return bEqual;
|
|
}
|
|
}
|
|
|
|
bool bEqual = true;
|
|
SCSIZE nThisPos = 0;
|
|
SCSIZE nOtherPos = 0;
|
|
if ( nStartRow > 0 )
|
|
{
|
|
Search( nStartRow, nThisPos );
|
|
rOther.Search( nStartRow, nOtherPos );
|
|
}
|
|
|
|
while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual )
|
|
{
|
|
SCROW nThisRow = pData[nThisPos].nRow;
|
|
SCROW nOtherRow = rOther.pData[nOtherPos].nRow;
|
|
const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern;
|
|
const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern;
|
|
bEqual = ( pThisPattern == pOtherPattern ||
|
|
pThisPattern->IsVisibleEqual(*pOtherPattern) );
|
|
|
|
if ( nThisRow >= nOtherRow )
|
|
{
|
|
if ( nOtherRow >= nEndRow ) break;
|
|
++nOtherPos;
|
|
}
|
|
if ( nThisRow <= nOtherRow )
|
|
{
|
|
if ( nThisRow >= nEndRow ) break;
|
|
++nThisPos;
|
|
}
|
|
}
|
|
|
|
return bEqual;
|
|
}
|
|
|
|
bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
|
|
{
|
|
// summarised with IsVisibleEqual
|
|
if ( !pData && !rOther.pData )
|
|
{
|
|
const ScPatternAttr* pDefPattern1 = pDocument->GetDefPattern();
|
|
const ScPatternAttr* pDefPattern2 = rOther.pDocument->GetDefPattern();
|
|
return ( pDefPattern1 == pDefPattern2 );
|
|
}
|
|
|
|
{
|
|
const ScAttrArray* pNonDefault = nullptr;
|
|
const ScPatternAttr* pDefPattern = nullptr;
|
|
bool bDefNonDefCase = false;
|
|
if ( !pData && rOther.pData )
|
|
{
|
|
pNonDefault = &rOther;
|
|
pDefPattern = pDocument->GetDefPattern();
|
|
bDefNonDefCase = true;
|
|
}
|
|
else if ( pData && !rOther.pData )
|
|
{
|
|
pNonDefault = this;
|
|
pDefPattern = rOther.pDocument->GetDefPattern();
|
|
bDefNonDefCase = true;
|
|
}
|
|
|
|
if ( bDefNonDefCase )
|
|
{
|
|
bool bEqual = true;
|
|
SCSIZE nPos = 0;
|
|
if ( nStartRow > 0 )
|
|
pNonDefault->Search( nStartRow, nPos );
|
|
|
|
while ( nPos < pNonDefault->nCount && bEqual )
|
|
{
|
|
const ScPatternAttr* pNonDefPattern = pNonDefault->pData[nPos].pPattern;
|
|
bEqual = ( pNonDefPattern == pDefPattern );
|
|
|
|
if ( pNonDefault->pData[nPos].nRow >= nEndRow ) break;
|
|
++nPos;
|
|
}
|
|
return bEqual;
|
|
}
|
|
}
|
|
|
|
bool bEqual = true;
|
|
SCSIZE nThisPos = 0;
|
|
SCSIZE nOtherPos = 0;
|
|
if ( nStartRow > 0 )
|
|
{
|
|
Search( nStartRow, nThisPos );
|
|
rOther.Search( nStartRow, nOtherPos );
|
|
}
|
|
|
|
while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual )
|
|
{
|
|
SCROW nThisRow = pData[nThisPos].nRow;
|
|
SCROW nOtherRow = rOther.pData[nOtherPos].nRow;
|
|
const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern;
|
|
const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern;
|
|
bEqual = ( pThisPattern == pOtherPattern );
|
|
|
|
if ( nThisRow >= nOtherRow )
|
|
{
|
|
if ( nOtherRow >= nEndRow ) break;
|
|
++nOtherPos;
|
|
}
|
|
if ( nThisRow <= nOtherRow )
|
|
{
|
|
if ( nThisRow >= nEndRow ) break;
|
|
++nThisPos;
|
|
}
|
|
}
|
|
|
|
return bEqual;
|
|
}
|
|
|
|
bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
|
|
{
|
|
// Horizontal aggregate are not allowed to be moved out; if whole summary,
|
|
// here is not recognized
|
|
|
|
bool bTest = true;
|
|
if (!IsEmpty())
|
|
{
|
|
SCSIZE nIndex = 0;
|
|
if ( nStartRow > 0 )
|
|
Search( nStartRow, nIndex );
|
|
|
|
for ( ; nIndex < nCount; nIndex++ )
|
|
{
|
|
if ( static_cast<const ScMergeFlagAttr&>(pData[nIndex].pPattern->
|
|
GetItem(ATTR_MERGE_FLAG)).IsHorOverlapped() )
|
|
{
|
|
bTest = false; // may not be pushed out
|
|
break;
|
|
}
|
|
if ( pData[nIndex].nRow >= nEndRow ) // end of range
|
|
break;
|
|
}
|
|
}
|
|
return bTest;
|
|
}
|
|
|
|
bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
|
|
{
|
|
// if 1st row pushed out is vertically overlapped, summary would be broken
|
|
|
|
// MAXROW + 1 - nSize = 1st row pushed out
|
|
|
|
if ( !pData )
|
|
return !static_cast<const ScMergeFlagAttr&>(pDocument->GetDefPattern()->
|
|
GetItem(ATTR_MERGE_FLAG)).IsVerOverlapped();
|
|
|
|
SCSIZE nFirstLost = nCount-1;
|
|
while ( nFirstLost && pData[nFirstLost-1].nRow >= sal::static_int_cast<SCROW>(MAXROW + 1 - nSize) )
|
|
--nFirstLost;
|
|
|
|
if ( static_cast<const ScMergeFlagAttr&>(pData[nFirstLost].pPattern->
|
|
GetItem(ATTR_MERGE_FLAG)).IsVerOverlapped() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
if (!pData)
|
|
return;
|
|
|
|
SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // expand predecessor
|
|
SCSIZE nIndex;
|
|
Search( nSearch, nIndex );
|
|
|
|
// set ScMergeAttr may not be extended (so behind delete again)
|
|
|
|
bool bDoMerge = static_cast<const ScMergeAttr&>( pData[nIndex].pPattern->GetItem(ATTR_MERGE)).IsMerged();
|
|
|
|
assert( !bDoMerge || nCol != -1 );
|
|
|
|
SCSIZE nRemove = 0;
|
|
SCSIZE i;
|
|
for (i = nIndex; i < nCount-1; i++)
|
|
{
|
|
SCROW nNew = pData[i].nRow + nSize;
|
|
if ( nNew >= MAXROW ) // at end?
|
|
{
|
|
nNew = MAXROW;
|
|
if (!nRemove)
|
|
nRemove = i+1; // remove the following?
|
|
}
|
|
pData[i].nRow = nNew;
|
|
}
|
|
|
|
// Remove entries at end ?
|
|
|
|
if (nRemove && nRemove < nCount)
|
|
DeleteRange( nRemove, nCount-1 );
|
|
|
|
if (bDoMerge) // extensively repair (again) ScMergeAttr
|
|
{
|
|
// ApplyAttr for areas
|
|
|
|
const SfxPoolItem& rDef = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE );
|
|
for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
|
|
pDocument->ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
|
|
|
|
// reply inserts in this area not summarized
|
|
}
|
|
|
|
// Don't duplicate the merge flags in the inserted row.
|
|
// #i108488# ScMF::Scenario has to be allowed.
|
|
RemoveFlags( nStartRow, nStartRow+nSize-1, ScMF::Hor | ScMF::Ver | ScMF::Auto | ScMF::Button );
|
|
}
|
|
|
|
void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
bool bFirst=true;
|
|
SCSIZE nStartIndex = 0;
|
|
SCSIZE nEndIndex = 0;
|
|
SCSIZE i;
|
|
|
|
for ( i = 0; i < nCount-1; i++)
|
|
if (pData[i].nRow >= nStartRow && pData[i].nRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
|
|
{
|
|
if (bFirst)
|
|
{
|
|
nStartIndex = i;
|
|
bFirst = false;
|
|
}
|
|
nEndIndex = i;
|
|
}
|
|
if (!bFirst)
|
|
{
|
|
SCROW nStart;
|
|
if (nStartIndex==0)
|
|
nStart = 0;
|
|
else
|
|
nStart = pData[nStartIndex-1].nRow + 1;
|
|
|
|
if (nStart < nStartRow)
|
|
{
|
|
pData[nStartIndex].nRow = nStartRow - 1;
|
|
++nStartIndex;
|
|
}
|
|
if (nEndIndex >= nStartIndex)
|
|
{
|
|
DeleteRange( nStartIndex, nEndIndex );
|
|
if (nStartIndex > 0)
|
|
if ( pData[nStartIndex-1].pPattern == pData[nStartIndex].pPattern )
|
|
DeleteRange( nStartIndex-1, nStartIndex-1 );
|
|
}
|
|
}
|
|
for (i = 0; i < nCount-1; i++)
|
|
if (pData[i].nRow >= nStartRow)
|
|
pData[i].nRow -= nSize;
|
|
|
|
// Below does not follow the pattern to detect pressure ranges;
|
|
// instead, only remove merge flags.
|
|
RemoveFlags( MAXROW-nSize+1, MAXROW, ScMF::Hor | ScMF::Ver | ScMF::Auto );
|
|
}
|
|
|
|
void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
|
|
{
|
|
SetDefaultIfNotInit();
|
|
ScDocumentPool* pDocPool = pDocument->GetPool();
|
|
for (SCSIZE i = nStartIndex; i <= nEndIndex; i++)
|
|
pDocPool->Remove(*pData[i].pPattern);
|
|
|
|
memmove( &pData[nStartIndex], &pData[nEndIndex + 1], (nCount - nEndIndex - 1) * sizeof(ScAttrEntry) );
|
|
nCount -= nEndIndex-nStartIndex+1;
|
|
}
|
|
|
|
void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
|
|
{
|
|
SetDefaultIfNotInit();
|
|
if ( nCol != -1 )
|
|
RemoveAreaMerge( nStartRow, nEndRow ); // remove from combined flags
|
|
|
|
if ( !HasAttrib( nStartRow, nEndRow, HasAttrFlags::Overlapped | HasAttrFlags::AutoFilter) )
|
|
SetPatternArea( nStartRow, nEndRow, pDocument->GetDefPattern() );
|
|
else
|
|
SetPatternAreaSafe( nStartRow, nEndRow, pDocument->GetDefPattern(), true ); // leave merge flags
|
|
}
|
|
|
|
void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
|
|
{
|
|
SetDefaultIfNotInit();
|
|
const ScPatternAttr* pDefPattern = pDocument->GetDefPattern();
|
|
|
|
SCSIZE nIndex;
|
|
SCROW nRow;
|
|
SCROW nThisRow;
|
|
|
|
Search( nStartRow, nIndex );
|
|
nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nThisRow < nStartRow) nThisRow = nStartRow;
|
|
|
|
while ( nThisRow <= nEndRow )
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
|
|
|
|
if ( pOldPattern->GetItemSet().Count() ) // hard attributes ?
|
|
{
|
|
nRow = pData[nIndex].nRow;
|
|
SCROW nAttrRow = std::min( (SCROW)nRow, (SCROW)nEndRow );
|
|
|
|
ScPatternAttr aNewPattern(*pOldPattern);
|
|
SfxItemSet& rSet = aNewPattern.GetItemSet();
|
|
for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
|
|
if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
|
|
rSet.ClearItem(nId);
|
|
|
|
if ( aNewPattern == *pDefPattern )
|
|
SetPatternArea( nThisRow, nAttrRow, pDefPattern );
|
|
else
|
|
SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true );
|
|
|
|
Search( nThisRow, nIndex ); // data changed
|
|
}
|
|
|
|
++nIndex;
|
|
nThisRow = pData[nIndex-1].nRow+1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move within a document
|
|
*/
|
|
void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
|
|
{
|
|
SetDefaultIfNotInit();
|
|
SCROW nStart = nStartRow;
|
|
for (SCSIZE i = 0; i < nCount; i++)
|
|
{
|
|
if ((pData[i].nRow >= nStartRow) && (i == 0 || pData[i-1].nRow < nEndRow))
|
|
{
|
|
// copy (bPutToPool=TRUE)
|
|
rAttrArray.SetPatternArea( nStart, std::min( (SCROW)pData[i].nRow, (SCROW)nEndRow ),
|
|
pData[i].pPattern, true );
|
|
}
|
|
nStart = std::max( (SCROW)nStart, (SCROW)(pData[i].nRow + 1) );
|
|
}
|
|
DeleteArea(nStartRow, nEndRow);
|
|
}
|
|
|
|
/**
|
|
* Copy between documents (Clipboard)
|
|
*/
|
|
void ScAttrArray::CopyArea(
|
|
SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags) const
|
|
{
|
|
nStartRow -= nDy; // Source
|
|
nEndRow -= nDy;
|
|
|
|
SCROW nDestStart = std::max((long)((long)nStartRow + nDy), (long) 0);
|
|
SCROW nDestEnd = std::min((long)((long)nEndRow + nDy), (long) MAXROW);
|
|
|
|
ScDocumentPool* pSourceDocPool = pDocument->GetPool();
|
|
ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool();
|
|
bool bSamePool = (pSourceDocPool==pDestDocPool);
|
|
|
|
if ( !pData )
|
|
{
|
|
const ScPatternAttr* pNewPattern = static_cast<const ScPatternAttr*>(
|
|
&pDestDocPool->GetDefaultItem( ATTR_PATTERN ));
|
|
rAttrArray.SetPatternArea(nDestStart, nDestEnd, pNewPattern);
|
|
return;
|
|
}
|
|
|
|
for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++)
|
|
{
|
|
if (pData[i].nRow >= nStartRow)
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[i].pPattern;
|
|
const ScPatternAttr* pNewPattern;
|
|
|
|
if (IsDefaultItem( pOldPattern ))
|
|
{
|
|
// default: nothing changed
|
|
|
|
pNewPattern = static_cast<const ScPatternAttr*>(
|
|
&pDestDocPool->GetDefaultItem( ATTR_PATTERN ));
|
|
}
|
|
else if ( nStripFlags != ScMF::NONE )
|
|
{
|
|
std::unique_ptr<ScPatternAttr> pTmpPattern(new ScPatternAttr( *pOldPattern ));
|
|
ScMF nNewFlags = ScMF::NONE;
|
|
if ( nStripFlags != ScMF::All )
|
|
nNewFlags = static_cast<const ScMergeFlagAttr&>(pTmpPattern->GetItem(ATTR_MERGE_FLAG)).
|
|
GetValue() & ~nStripFlags;
|
|
|
|
if ( nNewFlags != ScMF::NONE )
|
|
pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
|
|
else
|
|
pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG );
|
|
|
|
if (bSamePool)
|
|
pNewPattern = static_cast<const ScPatternAttr*>( &pDestDocPool->Put(*pTmpPattern) );
|
|
else
|
|
pNewPattern = pTmpPattern->PutInPool( rAttrArray.pDocument, pDocument );
|
|
}
|
|
else
|
|
{
|
|
if (bSamePool)
|
|
pNewPattern = static_cast<const ScPatternAttr*>( &pDestDocPool->Put(*pOldPattern) );
|
|
else
|
|
pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument );
|
|
}
|
|
|
|
rAttrArray.SetPatternArea(nDestStart,
|
|
std::min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern);
|
|
}
|
|
|
|
// when pasting from clipboard and skipping filtered rows, the adjusted
|
|
// end position can be negative
|
|
nDestStart = std::max((long)nDestStart, (long)(pData[i].nRow + nDy + 1));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Leave flags
|
|
* summarized with CopyArea
|
|
*/
|
|
void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray )
|
|
{
|
|
nStartRow -= nDy; // Source
|
|
nEndRow -= nDy;
|
|
|
|
SCROW nDestStart = std::max((long)((long)nStartRow + nDy), (long) 0);
|
|
SCROW nDestEnd = std::min((long)((long)nEndRow + nDy), (long) MAXROW);
|
|
|
|
if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HasAttrFlags::Overlapped ) )
|
|
{
|
|
CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
|
|
return;
|
|
}
|
|
|
|
ScDocumentPool* pSourceDocPool = pDocument->GetPool();
|
|
ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool();
|
|
bool bSamePool = (pSourceDocPool==pDestDocPool);
|
|
|
|
if ( !pData )
|
|
{
|
|
const ScPatternAttr* pNewPattern;
|
|
if (bSamePool)
|
|
pNewPattern = static_cast<const ScPatternAttr*>(
|
|
&pDestDocPool->Put(*pDocument->GetDefPattern()));
|
|
else
|
|
pNewPattern = pDocument->GetDefPattern()->PutInPool( rAttrArray.pDocument, pDocument );
|
|
|
|
rAttrArray.SetPatternAreaSafe(nDestStart, nDestEnd, pNewPattern, false);
|
|
return;
|
|
}
|
|
|
|
|
|
for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++)
|
|
{
|
|
if (pData[i].nRow >= nStartRow)
|
|
{
|
|
const ScPatternAttr* pOldPattern = pData[i].pPattern;
|
|
const ScPatternAttr* pNewPattern;
|
|
|
|
if (bSamePool)
|
|
pNewPattern = static_cast<const ScPatternAttr*>( &pDestDocPool->Put(*pOldPattern) );
|
|
else
|
|
pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument );
|
|
|
|
rAttrArray.SetPatternAreaSafe(nDestStart,
|
|
std::min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern, false);
|
|
}
|
|
|
|
// when pasting from clipboard and skipping filtered rows, the adjusted
|
|
// end position can be negative
|
|
nDestStart = std::max((long)nDestStart, (long)(pData[i].nRow + nDy + 1));
|
|
}
|
|
}
|
|
|
|
SCsROW ScAttrArray::SearchStyle(
|
|
SCsROW nRow, const ScStyleSheet* pSearchStyle, bool bUp,
|
|
const ScMarkArray* pMarkArray) const
|
|
{
|
|
bool bFound = false;
|
|
|
|
if (pMarkArray)
|
|
{
|
|
nRow = pMarkArray->GetNextMarked( nRow, bUp );
|
|
if (!ValidRow(nRow))
|
|
return nRow;
|
|
}
|
|
|
|
if ( !pData )
|
|
{
|
|
if (pDocument->GetDefPattern()->GetStyleSheet() == pSearchStyle)
|
|
return nRow;
|
|
|
|
nRow = bUp ? -1 : MAXROW + 1;
|
|
return nRow;
|
|
}
|
|
|
|
SCSIZE nIndex;
|
|
Search(nRow, nIndex);
|
|
const ScPatternAttr* pPattern = pData[nIndex].pPattern;
|
|
|
|
while (nIndex < nCount && !bFound)
|
|
{
|
|
if (pPattern->GetStyleSheet() == pSearchStyle)
|
|
{
|
|
if (pMarkArray)
|
|
{
|
|
nRow = pMarkArray->GetNextMarked( nRow, bUp );
|
|
SCROW nStart = nIndex ? pData[nIndex-1].nRow+1 : 0;
|
|
if (nRow >= nStart && nRow <= pData[nIndex].nRow)
|
|
bFound = true;
|
|
}
|
|
else
|
|
bFound = true;
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
if (bUp)
|
|
{
|
|
if (nIndex==0)
|
|
{
|
|
nIndex = nCount;
|
|
nRow = -1;
|
|
}
|
|
else
|
|
{
|
|
--nIndex;
|
|
nRow = pData[nIndex].nRow;
|
|
pPattern = pData[nIndex].pPattern;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nRow = pData[nIndex].nRow+1;
|
|
++nIndex;
|
|
if (nIndex<nCount)
|
|
pPattern = pData[nIndex].pPattern;
|
|
}
|
|
}
|
|
}
|
|
|
|
OSL_ENSURE( bFound || !ValidRow(nRow), "Internal failure in ScAttrArray::SearchStyle" );
|
|
|
|
return nRow;
|
|
}
|
|
|
|
bool ScAttrArray::SearchStyleRange(
|
|
SCsROW& rRow, SCsROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
|
|
const ScMarkArray* pMarkArray) const
|
|
{
|
|
SCsROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
|
|
if (ValidRow(nStartRow))
|
|
{
|
|
if ( !pData )
|
|
{
|
|
rRow = nStartRow;
|
|
if (bUp)
|
|
{
|
|
rEndRow = 0;
|
|
if (pMarkArray)
|
|
{
|
|
SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
|
|
if (nMarkEnd>rEndRow)
|
|
rEndRow = nMarkEnd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rEndRow = MAXROW;
|
|
if (pMarkArray)
|
|
{
|
|
SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
|
|
if (nMarkEnd<rEndRow)
|
|
rEndRow = nMarkEnd;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SCSIZE nIndex;
|
|
Search(nStartRow,nIndex);
|
|
|
|
rRow = nStartRow;
|
|
if (bUp)
|
|
{
|
|
if (nIndex>0)
|
|
rEndRow = pData[nIndex-1].nRow + 1;
|
|
else
|
|
rEndRow = 0;
|
|
if (pMarkArray)
|
|
{
|
|
SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
|
|
if (nMarkEnd>rEndRow)
|
|
rEndRow = nMarkEnd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rEndRow = pData[nIndex].nRow;
|
|
if (pMarkArray)
|
|
{
|
|
SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
|
|
if (nMarkEnd<rEndRow)
|
|
rEndRow = nMarkEnd;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW nEndRow ) const
|
|
{
|
|
if ( !pData )
|
|
return 1;
|
|
|
|
SCSIZE nIndex1, nIndex2;
|
|
|
|
if( !Search( nStartRow, nIndex1 ) )
|
|
return 0;
|
|
|
|
if( !Search( nEndRow, nIndex2 ) )
|
|
nIndex2 = nCount - 1;
|
|
|
|
return nIndex2 - nIndex1 + 1;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|