/* -*- 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 .
*/
SwChartLockController_Helper::~SwChartLockController_Helper()
{ if (m_pDoc) // still connected?
suppress_fun_call_w_exception(Disconnect());
}
void SwChartLockController_Helper::StartOrContinueLocking()
{ if (!m_bIsLocked)
LockAllCharts();
m_aUnlockTimer.Start(); // start or continue time of locking
}
/** * rCellRangeName needs to be of one of the following formats: * - e.g. "A2:E5" or * - e.g. "Table1.A2:E5"
*/ bool FillRangeDescriptor(
SwRangeDescriptor &rDesc,
std::u16string_view rCellRangeName )
{
sal_Int32 nToken = std::u16string_view::npos == rCellRangeName.find('.') ? 0 : 1;
std::u16string_view aCellRangeNoTableName( o3tl::getToken(rCellRangeName, nToken, '.' ) );
OUString aTLName( o3tl::getToken(aCellRangeNoTableName, 0, ':') ); // name of top left cell
OUString aBRName( o3tl::getToken(aCellRangeNoTableName, 1, ':') ); // name of bottom right cell if(aTLName.isEmpty() || aBRName.isEmpty()) returnfalse;
staticbool GetTableAndCellsFromRangeRep(
std::u16string_view rRangeRepresentation,
UIName &rTableName,
OUString &rStartCell,
OUString &rEndCell, bool bSortStartEndCells = true )
{ // parse range representation for table name and cell/range names // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
OUString aTableName; // table name
OUString aStartCell; // name of top left cell
OUString aEndCell; // name of bottom right cell
size_t nIdx = rRangeRepresentation.find( '.' ); if (nIdx != std::u16string_view::npos)
{
aTableName = rRangeRepresentation.substr( 0, nIdx );
std::u16string_view aRange = rRangeRepresentation.substr( nIdx + 1 ); // cell range
size_t nPos = aRange.find( ':' ); if (nPos != std::u16string_view::npos) // a cell-range like "Table1.A2:D4"
{
aStartCell = aRange.substr( 0, nPos );
aEndCell = aRange.substr( nPos + 1 );
// need to switch start and end cell ? // (does not check for normalization here) if (bSortStartEndCells && 1 == sw_CompareCellsByColFirst( aStartCell, aEndCell ))
std::swap(aStartCell, aEndCell);
} else// a single cell like in "Table1.B3"
{
aStartCell = aEndCell = aRange;
}
}
staticvoid GetFormatAndCreateCursorFromRangeRep( const SwDoc *pDoc,
std::u16string_view rRangeRepresentation, // must be a single range (i.e. so called sub-range)
SwFrameFormat **ppTableFormat, // will be set to the table format of the table used in the range representation
std::shared_ptr<SwUnoCursor>& rpUnoCursor ) // will be set to cursor spanning the cell range (cursor will be created!)
{
UIName aTableName; // table name
OUString aStartCell; // name of top left cell
OUString aEndCell; // name of bottom right cell bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
aTableName, aStartCell, aEndCell );
if (!bNamesFound)
{ if (ppTableFormat)
*ppTableFormat = nullptr;
rpUnoCursor.reset();
} else
{
SwFrameFormat *pTableFormat = nullptr;
// is the correct table format already provided? if (*ppTableFormat != nullptr && (*ppTableFormat)->GetName() == aTableName)
pTableFormat = *ppTableFormat; else
GetTableByName( *pDoc, aTableName, &pTableFormat, nullptr );
*ppTableFormat = pTableFormat;
rpUnoCursor.reset(); // default result in case of failure
SwTable *pTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr; // create new SwUnoCursor spanning the specified range //! see also SwXTextTable::GetRangeByName // #i80314# // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)> const SwTableBox* pTLBox =
pTable ? pTable->GetTableBox( aStartCell, true ) : nullptr; if(pTLBox)
{ const SwStartNode* pSttNd = pTLBox->GetSttNd();
SwPosition aPos(*pSttNd);
// set cursor to top left box of range auto pUnoCursor = pTableFormat->GetDoc().CreateUnoCursor(aPos, true);
pUnoCursor->Move( fnMoveForward, GoInNode );
pUnoCursor->SetRemainInSection( false );
for (sal_Int32 i = 0; i < nLen; ++i)
{
sal_Int32 nIdxOfSmallest = i;
GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
aSmallestTableName, aSmallestStartCell, aSmallestEndCell ); if (aSmallestEndCell.isEmpty())
aSmallestEndCell = aSmallestStartCell;
for (sal_Int32 k = i+1; k < nLen; ++k)
{ // get cell names for sub range
UIName aTableName;
OUString aStartCell;
OUString aEndCell;
GetTableAndCellsFromRangeRep( pSubRanges[k],
aTableName, aStartCell, aEndCell ); if (aEndCell.isEmpty())
aEndCell = aStartCell;
// compare cell ranges ( is the new one smaller? ) if (-1 == sw_CompareCellRanges( aStartCell, aEndCell,
aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
{
nIdxOfSmallest = k;
aSmallestTableName = std::move(aTableName);
aSmallestStartCell = aStartCell;
aSmallestEndCell = aEndCell;
}
}
// move smallest element to the start of the not sorted area const OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
pSubRanges[ i ] = aTmp;
}
}
if (!m_pDoc) throw uno::RuntimeException(u"Not connected to a document."_ustr);
// get arguments
OUString aRangeRepresentation;
uno::Sequence< sal_Int32 > aSequenceMapping; bool bFirstIsLabel = false; bool bDtaSrcIsColumns = true; // true : DataSource will be sequence of columns // false: DataSource will be sequence of rows
uno::Sequence< OUString > aSubRanges; // get sub-ranges and check that they all are from the very same table bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, true );
if (!bOk && m_pDoc && !aChartOleObjectName.isEmpty() )
{ //try to correct the range here //work around wrong writer ranges ( see Issue 58464 )
UIName aChartTableName;
if( !aChartTableName.isEmpty() )
{ //the wrong range is still shifted one row down //thus the first row is missing and an invalid row at the end is added. //Therefore we need to shift the range one row up
SwRangeDescriptor aDesc; if (aRangeRepresentation.isEmpty()) return nullptr; // we can't handle this thus returning an empty references
aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left
FillRangeDescriptor( aDesc, aRangeRepresentation );
aDesc.Normalize();
if (aDesc.nTop <= 0) // no chance to shift the range one row up? return nullptr; // we can't handle this thus returning an empty references
aDesc.nTop -= 1;
aDesc.nBottom -= 1;
OUString aNewStartCell( sw_GetCellName( aDesc.nLeft, aDesc.nTop ) );
OUString aNewEndCell( sw_GetCellName( aDesc.nRight, aDesc.nBottom ) );
aRangeRepresentation = GetRangeRepFromTableAndCells(
aChartTableName, aNewStartCell, aNewEndCell, true );
bOk = GetSubranges( aRangeRepresentation, aSubRanges, true );
}
} if (!bOk) // different tables used, or incorrect range specifiers throw lang::IllegalArgumentException();
SortSubranges( aSubRanges, bDtaSrcIsColumns );
// get table format for that single table from above
SwFrameFormat *pTableFormat = nullptr; // pointer to table format
std::shared_ptr<SwUnoCursor> pUnoCursor; // here required to check if the cells in the range do actually exist if (aSubRanges.hasElements())
GetFormatAndCreateCursorFromRangeRep( m_pDoc, aSubRanges[0], &pTableFormat, pUnoCursor );
if (!pTableFormat || !pUnoCursor) throw lang::IllegalArgumentException();
SwTable* pTable = SwTable::FindTable(pTableFormat); if (pTable->IsTableComplex()) return nullptr; // we can't handle this thus returning an empty references
// get a character map in the size of the table to mark // all the ranges to use in
sal_Int32 nRows = pTable->GetTabLines().size();
sal_Int32 nCols = 0; // As per tdf#149718 one should know that some cells can be merged together. // Therefore, the number of columns (boxes in each row) are not necessarily // equal. Here, we calculate the maximum number of columns in all rows. for (sal_Int32 i = 0; i < nRows; ++i)
nCols = std::max(nCols, static_cast<sal_Int32>(pTable->GetTabLines()[i]->GetTabBoxes().size()));
std::vector<std::vector<char>> aMap(nRows); for (sal_Int32 i = 0; i < nRows; ++i)
aMap[i].resize(nCols);
// iterate over subranges and mark used cells in above map //!! by proceeding this way we automatically get rid of //!! multiple listed or overlapping cell ranges which should //!! just be ignored silently for (const OUString& rSubRange : aSubRanges)
{
UIName aTableName;
OUString aStartCell, aEndCell; bool bOk2 = GetTableAndCellsFromRangeRep(
rSubRange, aTableName, aStartCell, aEndCell );
OSL_ENSURE(bOk2, "failed to get table and start/end cells");
for (oi = 0; oi < oiEnd; ++oi)
{
ii = 0; while (ii < iiEnd)
{ char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
// label should be used but is not yet found? if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
{
aLabelIdx[oi] = ii;
rChar = 'L'; // setting a different char for labels here // makes the test for the data sequence below // easier
}
// find data sequence if (rChar == 'x' && aDataStartIdx[oi] == -1)
{
aDataStartIdx[oi] = ii;
// get length of data sequence
sal_Int32 nL = 0; while (ii< iiEnd && 'x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
{
++nL; ++ii;
}
aDataLen[oi] = nL;
// check that there is no other separate sequence of data // to be found because that is not supported while (ii < iiEnd)
{ if ('x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii])) throw lang::IllegalArgumentException();
++ii;
}
} else
++ii;
}
}
// make some other consistency checks while calculating // the number of XLabeledDataSequence to build: // - labels should always be used or not at all // - the data sequences should have equal non-zero length
sal_Int32 nNumLDS = 0; if (oiEnd > 0)
{ for (oi = 0; oi < oiEnd; ++oi)
{ // row/col used at all? if (aDataStartIdx[oi] != -1 &&
(!bFirstIsLabel || aLabelIdx[oi] != -1))
{
++nNumLDS;
}
}
} if (nNumLDS == 0) throw lang::IllegalArgumentException();
// now we should have all necessary data to build a proper DataSource // thus if we came this far there should be no further problem if (bTestOnly) return nullptr; // have createDataSourcePossible return true
// create data source from found label and data sequences
uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aLabelSeqs(nNumLDS);
uno::Reference<chart2::data::XDataSequence>* pLabelSeqs = aLabelSeqs.getArray();
uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aDataSeqs(nNumLDS);
uno::Reference<chart2::data::XDataSequence>* pDataSeqs = aDataSeqs.getArray();
sal_Int32 nSeqsIdx = 0; for (oi = 0; oi < oiEnd; ++oi)
{ // row/col not used? (see if-statement above where nNumLDS was counted) if (!(aDataStartIdx[oi] != -1 &&
(!bFirstIsLabel || aLabelIdx[oi] != -1))) continue;
// get cursors spanning the cell ranges for label and data
std::shared_ptr<SwUnoCursor> pLabelUnoCursor;
std::shared_ptr<SwUnoCursor> pDataUnoCursor;
GetFormatAndCreateCursorFromRangeRep(m_pDoc, aLabelRange, &pTableFormat, pLabelUnoCursor);
GetFormatAndCreateCursorFromRangeRep(m_pDoc, aDataRange, &pTableFormat, pDataUnoCursor);
// create XDataSequence's from cursors if (pLabelUnoCursor)
pLabelSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pLabelUnoCursor);
OSL_ENSURE(pDataUnoCursor, "pointer to data sequence missing"); if (pDataUnoCursor)
pDataSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pDataUnoCursor); if (pLabelUnoCursor || pDataUnoCursor)
++nSeqsIdx;
}
OSL_ENSURE(nSeqsIdx == nNumLDS, "mismatch between sequence size and num,ber of entries");
// build data source from data and label sequences
uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aLDS(nNumLDS);
uno::Reference<chart2::data::XLabeledDataSequence>* pLDS = aLDS.getArray(); for (sal_Int32 i = 0; i < nNumLDS; ++i)
{
rtl::Reference<SwChartLabeledDataSequence> pLabeledDtaSeq = new SwChartLabeledDataSequence;
pLabeledDtaSeq->setLabel(pLabelSeqs[i]);
pLabeledDtaSeq->setValues(pDataSeqs[i]);
pLDS[i] = pLabeledDtaSeq;
}
// apply 'SequenceMapping' if it was provided if (aSequenceMapping.hasElements())
{
uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aOld_LDS(aLDS);
uno::Reference<chart2::data::XLabeledDataSequence>* pOld_LDS = aOld_LDS.getArray();
sal_Int32 nNewCnt = 0; for (sal_Int32 nIdx : aSequenceMapping)
{ // check that index to be used is valid // and has not yet been used if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
{
pLDS[nNewCnt++] = pOld_LDS[nIdx];
// mark index as being used already (avoids duplicate entries)
pOld_LDS[nIdx].clear();
}
} // add not yet used 'old' sequences to new one for (sal_Int32 i = 0; i < nNumLDS; ++i)
{ if (pOld_LDS[i].is())
pLDS[nNewCnt++] = pOld_LDS[i];
}
OSL_ENSURE(nNewCnt == nNumLDS, "unexpected size of resulting sequence");
}
/** * Fix for #i79009 * we need to return a property that has the same value as the property * 'CellRangeRepresentation' but for all rows which are increased by one. * E.g. Table1.A1:D5 -> Table1,A2:D6 * Since the problem is only for old charts which did not support multiple * we do not need to provide that property/string if the 'CellRangeRepresentation' * contains multiple ranges.
*/
OUString SwChartDataProvider::GetBrokenCellRangeForExport(
std::u16string_view rCellRangeRepresentation )
{ // check that we do not have multiple ranges if (std::u16string_view::npos == rCellRangeRepresentation.find( ';' ))
{ // get current cell and table names
UIName aTableName;
OUString aStartCell, aEndCell;
GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
aTableName, aStartCell, aEndCell, false );
sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
SwXTextTable::GetCellPosition( aStartCell, nStartCol, nStartRow );
SwXTextTable::GetCellPosition( aEndCell, nEndCol, nEndRow );
// get new cell names
++nStartRow;
++nEndRow;
aStartCell = sw_GetCellName( nStartCol, nStartRow );
aEndCell = sw_GetCellName( nEndCol, nEndRow );
OUString aCellRanges;
sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither
sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have // the expected size of 1 (i.e. if FirstCellAsLabel can // be determined) // -1: don't know yet, 0: not used, 1: always a single labe cell, ... // -2: neither/failed for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1)
{
uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] ); if( !xLabeledDataSequence.is() )
{
OSL_FAIL("got NULL for XLabeledDataSequence from Data source"); continue;
} const uno::Reference< chart2::data::XDataSequence > xCurLabel = xLabeledDataSequence->getLabel(); const uno::Reference< chart2::data::XDataSequence > xCurValues = xLabeledDataSequence->getValues();
// get sequence lengths for label and values. // (0 length is Ok)
sal_Int32 nCurLabelSeqLen = -1;
sal_Int32 nCurValuesSeqLen = -1; if (xCurLabel.is())
nCurLabelSeqLen = xCurLabel->getData().getLength(); if (xCurValues.is())
nCurValuesSeqLen = xCurValues->getData().getLength();
// check for consistent use of 'first cell as label' if (nLabelSeqLen == -1) // set initial value to compare with below further on
nLabelSeqLen = nCurLabelSeqLen; if (nLabelSeqLen != nCurLabelSeqLen)
nLabelSeqLen = -2; // failed / no consistent use of label cells
// get table and cell names for label and values data sequences // (start and end cell will be sorted, i.e. start cell <= end cell)
UIName aLabelTableName;
OUString aLabelStartCell, aLabelEndCell;
UIName aValuesTableName;
OUString aValuesStartCell, aValuesEndCell;
OUString aLabelRange, aValuesRange; if (xCurLabel.is())
aLabelRange = xCurLabel->getSourceRangeRepresentation(); if (xCurValues.is())
aValuesRange = xCurValues->getSourceRangeRepresentation(); if ((!aLabelRange.isEmpty() && !GetTableAndCellsFromRangeRep( aLabelRange,
aLabelTableName, aLabelStartCell, aLabelEndCell )) ||
!GetTableAndCellsFromRangeRep( aValuesRange,
aValuesTableName, aValuesStartCell, aValuesEndCell ))
{ return aResult; // failed -> return empty property sequence
}
// make sure all sequences use the same table if (aTableName.isEmpty())
aTableName = aValuesTableName; // get initial value to compare with if (aTableName.isEmpty() ||
aTableName != aValuesTableName ||
(!aLabelTableName.isEmpty() && aTableName != aLabelTableName))
{ return aResult; // failed -> return empty property sequence
}
// try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting // first and last cell used in both sequences
sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
{
OSL_ENSURE( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1, "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
nDirection = 0; // default direction for a single cell should be 'columns'
} else// more than one cell is available (in values and label together!)
{ if (nFirstCol == nLastCol && nFirstRow != nLastRow)
nDirection = 1; elseif (nFirstCol != nLastCol && nFirstRow == nLastRow)
nDirection = 0; else
{
OSL_FAIL( "trying to determine 'DataRowSource': unexpected case found" );
nDirection = -2;
}
} // check for consistent direction of data source if (nDtaSrcIsColumns == -1) // set initial value to compare with below
nDtaSrcIsColumns = nDirection; if (nDtaSrcIsColumns != nDirection)
{
nDtaSrcIsColumns = -2; // failed
}
if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
{ // build data to obtain 'SequenceMapping' later on
#if OSL_DEBUG_LEVEL > 0 // do some extra sanity checking that the length of the sequences // matches their range representation
{
sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1; if (xCurLabel.is())
{
SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow);
OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
(nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()), "label sequence length does not match range representation!" );
} if (xCurValues.is())
{
SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow);
OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
(nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()), "value sequence length does not match range representation!" );
}
} #endif
} // for
// build value for 'CellRangeRepresentation'
const OUString aCellRangeBase = aTableName.toString() + ".";
OUString aCurRange; for (sal_Int32 i = 0; i < nTableRows; ++i)
{ for (sal_Int32 k = 0; k < nTableCols; ++k)
{ if (aMap[i][k] != '\0') // top-left cell of a sub-range found
{ // find rectangular sub-range to use
sal_Int32 nRowIndex1 = i; // row index
sal_Int32 nColIndex1 = k; // column index
sal_Int32 nRowSubLen = 0;
sal_Int32 nColSubLen = 0; while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
++nRowSubLen; // be aware of shifted sequences! // (according to the checks done prior the length should be ok) while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
&& aMap[i + nRowSubLen-1][nColIndex1] != '\0')
{
++nColIndex1;
++nColSubLen;
}
OUString aStartCell( sw_GetCellName( k, i ) );
OUString aEndCell( sw_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
aCurRange = aCellRangeBase + aStartCell + ":" + aEndCell; if (!aCellRanges.isEmpty())
aCellRanges += ";";
aCellRanges += aCurRange;
// clear already found sub-range from map for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2) for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2)
aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
}
}
} // to be nice to the user we now sort the cell ranges according to // rows or columns depending on the direction used in the data source
uno::Sequence< OUString > aSortedRanges;
GetSubranges( aCellRanges, aSortedRanges, false/*sub ranges should already be normalized*/ );
SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
OUString aSortedCellRanges; for (const OUString& rSortedRange : aSortedRanges)
{ if (!aSortedCellRanges.isEmpty())
aSortedCellRanges += ";";
aSortedCellRanges += rSortedRange;
}
// build value for 'SequenceMapping'
uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping ); auto [begin, end] = asNonConstRange(aSortedMapping);
std::sort(begin, end); bool bNeedSequenceMapping = false; for (sal_Int32 i = 0; i < aSequenceMapping.getLength(); ++i)
{ auto it = std::find( std::cbegin(aSortedMapping), std::cend(aSortedMapping),
aSequenceMapping[i] );
pSequenceMapping[i] = std::distance(std::cbegin(aSortedMapping), it);
if (i != aSequenceMapping[i])
bNeedSequenceMapping = true;
}
// check if 'SequenceMapping' is actually not required... // (don't write unnecessary properties to the XML file) if (!bNeedSequenceMapping)
aSequenceMapping.realloc(0);
// build resulting properties
OSL_ENSURE(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/, "unexpected value for 'nLabelSeqLen'" ); bool bFirstCellIsLabel = false; // default value if 'nLabelSeqLen' could not properly determined if (nLabelSeqLen > 0) // == 0 means no label sequence in use
bFirstCellIsLabel = true;
SwFrameFormat *pTableFormat = nullptr; // pointer to table format
std::shared_ptr<SwUnoCursor> pUnoCursor; // pointer to new created cursor spanning the cell range
GetFormatAndCreateCursorFromRangeRep( m_pDoc, rRangeRepresentation,
&pTableFormat, pUnoCursor ); if (!pTableFormat || !pUnoCursor) throw lang::IllegalArgumentException();
// check that cursors point and mark are in a single row or column.
OUString aCellRange( GetCellRangeName( *pTableFormat, *pUnoCursor ) );
SwRangeDescriptor aDesc;
FillRangeDescriptor( aDesc, aCellRange ); if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight) throw lang::IllegalArgumentException();
uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
{ // note: it is no error to return nothing here return uno::Reference< sheet::XRangeSelection >();
}
// iterate over all data-sequences for that table... auto aIt( rVec.begin() ); while (aIt != rVec.end())
{ bool bNowEmpty = false; bool bSeqDisposed = false;
// check if weak reference is still valid...
rtl::Reference< SwChartDataSequence > pDataSeq(*aIt); if (pDataSeq.is())
{ // then delete that table box (check if implementation cursor needs to be adjusted) try
{
bNowEmpty = pDataSeq->DeleteBox( rBox );
} catch (const lang::DisposedException&)
{
bNowEmpty = true;
bSeqDisposed = true;
}
}
if (bNowEmpty)
{
aIt = rVec.erase( aIt ); if (pDataSeq && !bSeqDisposed)
pDataSeq->dispose(); // the current way to tell chart that sth. got removed
} else
++aIt;
}
}
void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
{
OSL_ENSURE( pTable, "table pointer is NULL" ); if (!pTable) return;
if (!m_bDisposed)
pTable->GetFrameFormat()->GetDoc().getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
//! make a copy of the STL container! //! This is necessary since calling 'dispose' will implicitly remove an element //! of the original container, and thus any iterator in the original container //! would become invalid. const Vec_DataSequenceRef_t aVec( m_aDataSequences[ pTable ] );
for (const unotools::WeakReference<SwChartDataSequence>& rItem : aVec)
{
rtl::Reference< SwChartDataSequence > xRef(rItem); if (xRef.is())
{
xRef->dispose();
}
}
}
/** * SwChartDataProvider::AddRowCols tries to notify charts of added columns * or rows and extends the value sequence respectively (if possible). * If those can be added to the end of existing value data-sequences those * sequences get modified accordingly and will send a modification * notification (calling 'setModified * * Since this function is a work-around for non existent Writer core functionality * (no arbitrary multi-selection in tables that can be used to define a * data-sequence) this function will be somewhat unreliable. * For example we will only try to adapt value sequences. For this we assume * that a sequence of length 1 is a label sequence and those with length >= 2 * we presume to be value sequences. Also new cells can only be added in the * direction the value sequence is already pointing (rows / cols) and at the * start or end of the values data-sequence. * Nothing needs to be done if the new cells are in between the table cursors * point and mark since data-sequence are considered to consist of all cells * between those. * New rows/cols need to be added already to the table before calling * this function.
*/ void SwChartDataProvider::AddRowCols( const SwTable &rTable, const SwSelBoxes& rBoxes,
sal_uInt16 nLines, bool bBehind )
{ if (rTable.IsTableComplex()) return;
bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/ if (nFirstCol == nLastCol && nFirstRow != nLastRow)
bAddCols = true; if (nFirstCol != nLastCol && nFirstRow != nLastRow) return;
//get range of indices in col/rows for new cells
sal_Int32 nFirstNewCol = nFirstCol;
sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines; if (bAddCols)
{
OSL_ENSURE( nFirstCol == nLastCol, "column indices seem broken" );
nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines;
nFirstNewRow = nFirstRow;
}
// iterate over all data-sequences for the table const Vec_DataSequenceRef_t &rVec = m_aDataSequences[ &rTable ]; for (const unotools::WeakReference<SwChartDataSequence>& rItem : rVec)
{
rtl::Reference< SwChartDataSequence > pDataSeq(rItem); if (pDataSeq.is())
{ const sal_Int32 nLen = pDataSeq->getTextualData().getLength(); if (nLen > 1) // value data-sequence ?
{
SwRangeDescriptor aDesc;
pDataSeq->FillRangeDesc( aDesc );
if (rRangeRepresentation.isEmpty()) return OUString();
OUStringBuffer aRes;
// multiple ranges are delimited by a ';' like in // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
SwTable* pFirstFoundTable = nullptr; // to check that only one table will be used
sal_Int32 nPos = 0; do { const OUString aRange( rRangeRepresentation.getToken(0, ';', nPos) );
SwFrameFormat *pTableFormat = nullptr; // pointer to table format
std::shared_ptr<SwUnoCursor> pCursor;
GetFormatAndCreateCursorFromRangeRep( m_pDoc, aRange, &pTableFormat, pCursor ); if (!pTableFormat) throw lang::IllegalArgumentException();
SwTable* pTable = SwTable::FindTable( pTableFormat ); if (pTable->IsTableComplex()) throw uno::RuntimeException(u"Table too complex."_ustr);
// check that there is only one table used in all ranges if (!pFirstFoundTable)
pFirstFoundTable = pTable; if (pTable != pFirstFoundTable) throw lang::IllegalArgumentException();
sal_Int32 nCol, nRow;
SwXTextTable::GetCellPosition( aStartCell, nCol, nRow ); if (nCol < 0 || nRow < 0) throw uno::RuntimeException(u"Cell not found."_ustr);
//!! following objects/functions are implemented in XMLRangeHelper.?xx //!! which is a copy of the respective file from chart2 !!
XMLRangeHelper::CellRange aCellRange;
aCellRange.aTableName = aTableName.toString();
aCellRange.aUpperLeft.nColumn = nCol;
aCellRange.aUpperLeft.nRow = nRow;
aCellRange.aUpperLeft.bIsEmpty = false; if (aStartCell != aEndCell && !aEndCell.isEmpty())
{
SwXTextTable::GetCellPosition( aEndCell, nCol, nRow ); if (nCol < 0 || nRow < 0) throw uno::RuntimeException(u"Cell not found."_ustr);
aCellRange.aLowerRight.nColumn = nCol;
aCellRange.aLowerRight.nRow = nRow;
aCellRange.aLowerRight.bIsEmpty = false;
}
OUString aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) ); if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
aRes.append(" ");
aRes.append(aTmp);
} while (nPos>0);
// multiple ranges are delimited by a ' ' like in // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
OUString aFirstFoundTable; // to check that only one table will be used
sal_Int32 nPos = 0; do
{
OUString aRange( rXMLRange.getToken(0, ' ', nPos) );
//!! following objects and function are implemented in XMLRangeHelper.?xx //!! which is a copy of the respective file from chart2 !!
XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
// check that there is only one table used in all ranges if (aFirstFoundTable.isEmpty())
aFirstFoundTable = aCellRange.aTableName; if (aCellRange.aTableName != aFirstFoundTable) throw lang::IllegalArgumentException();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.