/* -*- 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 <algorithm>
#include <com/sun/star/chart/ChartDataRowSource.hpp>
#include <com/sun/star/chart2/data/LabelOrigin.hpp>
#include <cppuhelper/interfacecontainer.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <osl/mutex.hxx>
#include <vcl/svapp.hxx>
#include <svl/zforlist.hxx>
#include "XMLRangeHelper.hxx"
#include <unochart.hxx>
#include <swtable.hxx>
#include <unoprnms.hxx>
#include <unomap.hxx>
#include <unomid.h>
#include <unocrsr.hxx>
#include <unotbl.hxx>
#include <doc.hxx>
#include <IDocumentChartDataProviderAccess.hxx>
#include <frmfmt.hxx>
#include <docsh.hxx>
#include <ndole.hxx>
#include <swtypes.hxx>
#include <strings.hrc>
#include <docary.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/string.hxx>
#include <svl/itemprop.hxx>
using namespace ::com::sun::star;
void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
{
if (!pDoc)
return;
SwOLENode *pONd;
SwStartNode *pStNd;
SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
{
++aIdx;
if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
pONd->GetOLEObj().GetObject().IsChart() )
{
// Load the object and set modified
uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
{
try
{
uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
xModif->setModified( true );
}
catch ( uno::Exception& )
{
}
}
}
aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
}
}
SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
pDoc( pDocument )
, bIsLocked( false )
{
aUnlockTimer.SetTimeout( 1500 );
aUnlockTimer.SetInvokeHandler( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
aUnlockTimer.SetDebugName( "sw::SwChartLockController_Helper aUnlockTimer" );
}
SwChartLockController_Helper::~SwChartLockController_Helper() COVERITY_NOEXCEPT_FALSE
{
if (pDoc) // still connected?
Disconnect();
}
void SwChartLockController_Helper::StartOrContinueLocking()
{
if (!bIsLocked)
LockAllCharts();
aUnlockTimer.Start(); // start or continue time of locking
}
void SwChartLockController_Helper::Disconnect()
{
aUnlockTimer.Stop();
UnlockAllCharts();
pDoc = nullptr;
}
void SwChartLockController_Helper::LockUnlockAllCharts( bool bLock )
{
if (!pDoc)
return;
uno::Reference< frame::XModel > xRes;
SwOLENode *pONd;
SwStartNode *pStNd;
SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
{
++aIdx;
if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
!pONd->GetChartTableName().isEmpty() /* is chart object? */)
{
uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
{
xRes.set( xIP->getComponent(), uno::UNO_QUERY );
if (xRes.is())
{
if (bLock)
xRes->lockControllers();
else
xRes->unlockControllers();
}
}
}
aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
}
bIsLocked = bLock;
}
IMPL_LINK_NOARG( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, void )
{
UnlockAllCharts();
}
static osl::Mutex & GetChartMutex()
{
static osl::Mutex aMutex;
return aMutex;
}
static void LaunchModifiedEvent(
::comphelper::OInterfaceContainerHelper2 &rICH,
const uno::Reference< uno::XInterface > &rxI )
{
lang::EventObject aEvtObj( rxI );
comphelper::OInterfaceIteratorHelper2 aIt( rICH );
while (aIt.hasMoreElements())
{
uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY );
if (xRef.is())
xRef->modified( aEvtObj );
}
}
/**
* rCellRangeName needs to be of one of the following formats:
* - e.g. "A2:E5" or
* - e.g. "Table1.A2:E5"
*/
bool FillRangeDescriptor(
SwRangeDescriptor &rDesc,
const OUString &rCellRangeName )
{
sal_Int32 nToken = -1 == rCellRangeName.indexOf('.') ? 0 : 1;
OUString aCellRangeNoTableName( rCellRangeName.getToken( nToken, '.' ) );
OUString aTLName( aCellRangeNoTableName.getToken(0, ':') ); // name of top left cell
OUString aBRName( aCellRangeNoTableName.getToken(1, ':') ); // name of bottom right cell
if(aTLName.isEmpty() || aBRName.isEmpty())
return false;
rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
SwXTextTable::GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop );
SwXTextTable::GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
rDesc.Normalize();
OSL_ENSURE( rDesc.nTop != -1 &&
rDesc.nLeft != -1 &&
rDesc.nBottom != -1 &&
rDesc.nRight != -1,
"failed to get range descriptor" );
OSL_ENSURE( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight,
"invalid range descriptor");
return true;
}
static OUString GetCellRangeName( SwFrameFormat &rTableFormat, SwUnoCursor &rTableCursor )
{
OUString aRes;
//!! see also SwXTextTableCursor::getRangeName
SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&rTableCursor);
if (!pUnoTableCursor)
return OUString();
pUnoTableCursor->MakeBoxSels();
const SwStartNode* pStart;
const SwTableBox* pStartBox = nullptr;
const SwTableBox* pEndBox = nullptr;
pStart = pUnoTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
if (pStart)
{
const SwTable* pTable = SwTable::FindTable( &rTableFormat );
pEndBox = pTable->GetTableBox( pStart->GetIndex());
aRes = pEndBox->GetName();
if(pUnoTableCursor->HasMark())
{
pStart = pUnoTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
pStartBox = pTable->GetTableBox( pStart->GetIndex());
}
OSL_ENSURE( pStartBox, "start box not found" );
OSL_ENSURE( pEndBox, "end box not found" );
// need to switch start and end?
if (*pUnoTableCursor->GetPoint() < *pUnoTableCursor->GetMark())
{
const SwTableBox* pTmpBox = pStartBox;
pStartBox = pEndBox;
pEndBox = pTmpBox;
}
if (!pStartBox)
return aRes;
aRes = pStartBox->GetName();
aRes += ":";
if (pEndBox)
aRes += pEndBox->GetName();
else
aRes += pStartBox->GetName();
}
return aRes;
}
static OUString GetRangeRepFromTableAndCells( const OUString &rTableName,
const OUString &rStartCell, const OUString &rEndCell,
bool bForceEndCellName )
{
OSL_ENSURE( !rTableName.isEmpty(), "table name missing" );
OSL_ENSURE( !rStartCell.isEmpty(), "cell name missing" );
OUString aRes( rTableName );
aRes += ".";
aRes += rStartCell;
if (!rEndCell.isEmpty())
{
aRes += ":";
aRes += rEndCell;
}
else if (bForceEndCellName)
{
aRes += ":";
aRes += rStartCell;
}
return aRes;
}
static bool GetTableAndCellsFromRangeRep(
const OUString &rRangeRepresentation,
OUString &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 aRange; // cell range
OUString aStartCell; // name of top left cell
OUString aEndCell; // name of bottom right cell
sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' );
if (nIdx >= 0)
{
aTableName = rRangeRepresentation.copy( 0, nIdx );
aRange = rRangeRepresentation.copy( nIdx + 1 );
sal_Int32 nPos = aRange.indexOf( ':' );
if (nPos >= 0) // a cell-range like "Table1.A2:D4"
{
aStartCell = aRange.copy( 0, nPos );
aEndCell = aRange.copy( nPos + 1 );
// need to switch start and end cell ?
// (does not check for normalization here)
if (bSortStartEndCells && 1 == sw_CompareCellsByColFirst( aStartCell, aEndCell ))
{
OUString aTmp( aStartCell );
aStartCell = aEndCell;
aEndCell = aTmp;
}
}
else // a single cell like in "Table1.B3"
{
aStartCell = aEndCell = aRange;
}
}
bool bSuccess = !aTableName.isEmpty() &&
!aStartCell.isEmpty() && !aEndCell.isEmpty();
if (bSuccess)
{
rTableName = aTableName;
rStartCell = aStartCell;
rEndCell = aEndCell;
}
return bSuccess;
}
static void GetTableByName( const SwDoc &rDoc, const OUString &rTableName,
SwFrameFormat **ppTableFormat, SwTable **ppTable)
{
SwFrameFormat *pTableFormat = nullptr;
// find frame format of table
//! see SwXTextTables::getByName
const size_t nCount = rDoc.GetTableFrameFormatCount(true);
for (size_t i = 0; i < nCount && !pTableFormat; ++i)
{
SwFrameFormat& rTableFormat = rDoc.GetTableFrameFormat(i, true);
if(rTableName == rTableFormat.GetName())
pTableFormat = &rTableFormat;
}
if (ppTableFormat)
*ppTableFormat = pTableFormat;
if (ppTable)
*ppTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
}
static void GetFormatAndCreateCursorFromRangeRep(
const SwDoc *pDoc,
const OUString &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!)
{
OUString 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 );
// #i80314#
// perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
const SwTableBox* pBRBox = pTable->GetTableBox( aEndCell, true );
if(pBRBox)
{
pUnoCursor->SetMark();
pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
pUnoCursor->Move( fnMoveForward, GoInNode );
SwUnoTableCursor& rCursor =
dynamic_cast<SwUnoTableCursor&>(*pUnoCursor.get());
// HACK: remove pending actions for old style tables
UnoActionRemoveContext aRemoveContext(rCursor);
rCursor.MakeBoxSels();
rpUnoCursor = pUnoCursor;
}
}
}
}
static bool GetSubranges( const OUString &rRangeRepresentation,
uno::Sequence< OUString > &rSubRanges, bool bNormalize )
{
bool bRes = true;
const sal_Int32 nLen = comphelper::string::getTokenCount(rRangeRepresentation, ';');
uno::Sequence< OUString > aRanges( nLen );
sal_Int32 nCnt = 0;
if (nLen != 0)
{
OUString *pRanges = aRanges.getArray();
OUString aFirstTable;
sal_Int32 nPos = 0;
for( sal_Int32 i = 0; i < nLen && bRes; ++i )
{
const OUString aRange( rRangeRepresentation.getToken( 0, ';', nPos ) );
if (!aRange.isEmpty())
{
pRanges[nCnt] = aRange;
OUString aTableName, aStartCell, aEndCell;
if (!GetTableAndCellsFromRangeRep( aRange,
aTableName, aStartCell, aEndCell ))
bRes = false;
if (bNormalize)
{
sw_NormalizeRange( aStartCell, aEndCell );
pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
aStartCell, aEndCell, true );
}
// make sure to use only a single table
if (nCnt == 0)
aFirstTable = aTableName;
else
if (aFirstTable != aTableName) bRes = false;
++nCnt;
}
}
}
aRanges.realloc( nCnt );
rSubRanges = aRanges;
return bRes;
}
static void SortSubranges( uno::Sequence< OUString > &rSubRanges, bool bCmpByColumn )
{
sal_Int32 nLen = rSubRanges.getLength();
OUString *pSubRanges = rSubRanges.getArray();
OUString aSmallestTableName;
OUString aSmallestStartCell;
OUString aSmallestEndCell;
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
OUString 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 = 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;
}
}
SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) :
aEvtListeners( GetChartMutex() ),
pDoc( pSwDoc )
{
bDisposed = false;
}
SwChartDataProvider::~SwChartDataProvider()
{
}
uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
const uno::Sequence< beans::PropertyValue >& rArguments, bool bTestOnly )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
uno::Reference< chart2::data::XDataSource > xRes;
if (!pDoc)
throw uno::RuntimeException();
// 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
OUString aChartOleObjectName; //work around wrong writer ranges ( see Issue 58464 )
sal_Int32 nArgs = rArguments.getLength();
OSL_ENSURE( nArgs != 0, "no properties provided" );
if (nArgs == 0)
return xRes;
const beans::PropertyValue *pArg = rArguments.getConstArray();
for (sal_Int32 i = 0; i < nArgs; ++i)
{
if ( pArg[i].Name == "DataRowSource" )
{
chart::ChartDataRowSource eSource;
if (!(pArg[i].Value >>= eSource))
{
sal_Int32 nTmp = 0;
if (!(pArg[i].Value >>= nTmp))
throw lang::IllegalArgumentException();
eSource = static_cast< chart::ChartDataRowSource >( nTmp );
}
bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
}
else if ( pArg[i].Name == "FirstCellAsLabel" )
{
if (!(pArg[i].Value >>= bFirstIsLabel))
throw lang::IllegalArgumentException();
}
else if ( pArg[i].Name == "CellRangeRepresentation" )
{
if (!(pArg[i].Value >>= aRangeRepresentation))
throw lang::IllegalArgumentException();
}
else if ( pArg[i].Name == "SequenceMapping" )
{
if (!(pArg[i].Value >>= aSequenceMapping))
throw lang::IllegalArgumentException();
}
else if ( pArg[i].Name == "ChartOleObjectName" )
{
if (!(pArg[i].Value >>= aChartOleObjectName))
throw lang::IllegalArgumentException();
}
}
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 && pDoc && !aChartOleObjectName.isEmpty() )
{
//try to correct the range here
//work around wrong writer ranges ( see Issue 58464 )
OUString aChartTableName;
const SwNodes& rNodes = pDoc->GetNodes();
for( sal_uLong nN = rNodes.Count(); nN--; )
{
SwNodePtr pNode = rNodes[nN];
if( !pNode )
continue;
const SwOLENode* pOleNode = pNode->GetOLENode();
if( !pOleNode )
continue;
const SwOLEObj& rOObj = pOleNode->GetOLEObj();
if( aChartOleObjectName == rOObj.GetCurrentPersistName() )
{
aChartTableName = pOleNode->GetChartTableName();
break;
}
}
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 xRes; // 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 xRes; // 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 );
const OUString *pSubRanges = aSubRanges.getConstArray();
// 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.getLength() > 0)
GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTableFormat, pUnoCursor );
if (!pTableFormat || !pUnoCursor)
throw lang::IllegalArgumentException();
if(pTableFormat)
{
SwTable* pTable = SwTable::FindTable( pTableFormat );
if(pTable->IsTableComplex())
return xRes; // we can't handle this thus returning an empty references
else
{
// 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 = pTable->GetTabLines().front()->GetTabBoxes().size();
std::vector< std::vector< sal_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
sal_Int32 nSubRanges = aSubRanges.getLength();
for (sal_Int32 i = 0; i < nSubRanges; ++i)
{
OUString aTableName, aStartCell, aEndCell;
bool bOk2 = GetTableAndCellsFromRangeRep(
pSubRanges[i], aTableName, aStartCell, aEndCell );
OSL_ENSURE( bOk2, "failed to get table and start/end cells" );
sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
SwXTextTable::GetCellPosition( aStartCell, nStartCol, nStartRow );
SwXTextTable::GetCellPosition( aEndCell, nEndCol, nEndRow );
OSL_ENSURE( nStartRow <= nEndRow && nStartCol <= nEndCol,
"cell range not normalized");
// test if the ranges span more than the available cells
if( nStartRow < 0 || nEndRow >= nRows ||
nStartCol < 0 || nEndCol >= nCols )
{
throw lang::IllegalArgumentException();
}
for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1)
{
for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2)
aMap[k1][k2] = 'x';
}
}
// find label and data sequences to use
sal_Int32 oi; // outer index (slower changing index)
sal_Int32 ii; // inner index (faster changing index)
sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
std::vector< sal_Int32 > aLabelIdx( oiEnd );
std::vector< sal_Int32 > aDataStartIdx( oiEnd );
std::vector< sal_Int32 > aDataLen( oiEnd );
for (oi = 0; oi < oiEnd; ++oi)
{
aLabelIdx[oi] = -1;
aDataStartIdx[oi] = -1;
aDataLen[oi] = 0;
}
for (oi = 0; oi < oiEnd; ++oi)
{
ii = 0;
while (ii < iiEnd)
{
sal_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;
sal_Char c;
while (ii< iiEnd && 'x' == (c = 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' == (c = 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)
{
sal_Int32 nFirstSeqLen = 0;
sal_Int32 nFirstSeqLabelIdx = -1;
bool bFirstFound = false;
for (oi = 0; oi < oiEnd; ++oi)
{
// row/col used at all?
if (aDataStartIdx[oi] != -1 &&
(!bFirstIsLabel || aLabelIdx[oi] != -1))
{
++nNumLDS;
if (!bFirstFound)
{
nFirstSeqLen = aDataLen[oi];
nFirstSeqLabelIdx = aLabelIdx[oi];
bFirstFound = true;
}
else
{
if (nFirstSeqLen != aDataLen[oi] ||
nFirstSeqLabelIdx != aLabelIdx[oi])
throw lang::IllegalArgumentException();
}
}
}
}
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 xRes; // 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 cell ranges for label and data
SwRangeDescriptor aLabelDesc;
SwRangeDescriptor aDataDesc;
if (bDtaSrcIsColumns) // use columns
{
aLabelDesc.nTop = aLabelIdx[oi];
aLabelDesc.nLeft = oi;
aLabelDesc.nBottom = aLabelDesc.nTop;
aLabelDesc.nRight = oi;
aDataDesc.nTop = aDataStartIdx[oi];
aDataDesc.nLeft = oi;
aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1;
aDataDesc.nRight = oi;
}
else // use rows
{
aLabelDesc.nTop = oi;
aLabelDesc.nLeft = aLabelIdx[oi];
aLabelDesc.nBottom = oi;
aLabelDesc.nRight = aLabelDesc.nLeft;
aDataDesc.nTop = oi;
aDataDesc.nLeft = aDataStartIdx[oi];
aDataDesc.nBottom = oi;
aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1;
}
const OUString aBaseName = pTableFormat->GetName() + ".";
OUString aLabelRange;
if (aLabelIdx[oi] != -1)
{
aLabelRange = aBaseName
+ sw_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop )
+ ":" + sw_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
}
OUString aDataRange;
if (aDataStartIdx[oi] != -1)
{
aDataRange = aBaseName
+ sw_GetCellName( aDataDesc.nLeft, aDataDesc.nTop )
+ ":" + sw_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
}
// get cursors spanning the cell ranges for label and data
std::shared_ptr<SwUnoCursor> pLabelUnoCursor;
std::shared_ptr<SwUnoCursor> pDataUnoCursor;
GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTableFormat, pLabelUnoCursor);
GetFormatAndCreateCursorFromRangeRep( 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)
{
SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence;
pLabeledDtaSeq->setLabel( pLabelSeqs[i] );
pLabeledDtaSeq->setValues( pDataSeqs[i] );
pLDS[i] = pLabeledDtaSeq;
}
// apply 'SequenceMapping' if it was provided
sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength();
if (nSequenceMappingLen)
{
sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
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 i = 0; i < nSequenceMappingLen; ++i)
{
// check that index to be used is valid
// and has not yet been used
sal_Int32 nIdx = pSequenceMapping[i];
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" );
}
xRes = new SwChartDataSource( aLDS );
}
}
return xRes;
}
sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
const uno::Sequence< beans::PropertyValue >& rArguments )
{
SolarMutexGuard aGuard;
bool bPossible = true;
try
{
Impl_createDataSource( rArguments, true );
}
catch (lang::IllegalArgumentException &)
{
bPossible = false;
}
return bPossible;
}
uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
const uno::Sequence< beans::PropertyValue >& rArguments )
{
SolarMutexGuard aGuard;
return Impl_createDataSource( rArguments );
}
/**
* 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(
const OUString &rCellRangeRepresentation )
{
// check that we do not have multiple ranges
if (-1 == rCellRangeRepresentation.indexOf( ';' ))
{
// get current cell and table names
OUString aTableName, 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 );
return GetRangeRepFromTableAndCells( aTableName,
aStartCell, aEndCell, false );
}
return OUString();
}
uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
const uno::Reference< chart2::data::XDataSource >& xDataSource )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
uno::Sequence< beans::PropertyValue > aResult;
if (!xDataSource.is())
return aResult;
const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
if (nNumDS_LDS == 0)
{
OSL_FAIL( "XLabeledDataSequence in data source contains 0 entries" );
return aResult;
}
SwFrameFormat *pTableFormat = nullptr;
SwTable *pTable = nullptr;
OUString aTableName;
sal_Int32 nTableRows = 0;
sal_Int32 nTableCols = 0;
// data used to build 'CellRangeRepresentation' from later on
std::vector< std::vector< sal_Char > > aMap;
uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
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(), uno::UNO_QUERY );
const uno::Reference< chart2::data::XDataSequence > xCurValues( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
// 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)
OUString aLabelTableName, aLabelStartCell, aLabelEndCell;
OUString aValuesTableName, 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_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
const OUString aCell( !aLabelStartCell.isEmpty() ? aLabelStartCell : aValuesStartCell );
OSL_ENSURE( !aCell.isEmpty() , "start cell missing?" );
SwXTextTable::GetCellPosition( aCell, nFirstCol, nFirstRow);
SwXTextTable::GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
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;
else if (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
OSL_ENSURE( nDtaSrcIsColumns == 0 || /* rows */
nDtaSrcIsColumns == 1, /* columns */
"unexpected value for 'nDtaSrcIsColumns'" );
pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
// build data used to determine 'CellRangeRepresentation' later on
GetTableByName( *pDoc, aTableName, &pTableFormat, &pTable );
if (!pTable || pTable->IsTableComplex())
return aResult; // failed -> return empty property sequence
nTableRows = pTable->GetTabLines().size();
nTableCols = pTable->GetTabLines().front()->GetTabBoxes().size();
aMap.resize( nTableRows );
for (sal_Int32 i = 0; i < nTableRows; ++i)
aMap[i].resize( nTableCols );
if (!aLabelStartCell.isEmpty() && !aLabelEndCell.isEmpty())
{
sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow );
if (nStartRow < 0 || nEndRow >= nTableRows ||
nStartCol < 0 || nEndCol >= nTableCols)
{
return aResult; // failed -> return empty property sequence
}
for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
{
for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
{
sal_Char &rChar = aMap[i][k];
if (rChar == '\0') // check for overlapping values and/or labels
rChar = 'L';
else
return aResult; // failed -> return empty property sequence
}
}
}
if (!aValuesStartCell.isEmpty() && !aValuesEndCell.isEmpty())
{
sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow );
if (nStartRow < 0 || nEndRow >= nTableRows ||
nStartCol < 0 || nEndCol >= nTableCols)
{
return aResult; // failed -> return empty property sequence
}
for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
{
for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
{
sal_Char &rChar = aMap[i][k];
if (rChar == '\0') // check for overlapping values and/or labels
rChar = 'x';
else
return aResult; // failed -> return empty property sequence
}
}
}
}
#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 + ".";
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) );
sal_Int32 nSortedRanges = aSortedRanges.getLength();
const OUString *pSortedRanges = aSortedRanges.getConstArray();
OUString aSortedCellRanges;
for (sal_Int32 i = 0; i < nSortedRanges; ++i)
{
if (!aSortedCellRanges.isEmpty())
aSortedCellRanges += ";";
aSortedCellRanges += pSortedRanges[i];
}
// build value for 'SequenceMapping'
uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
std::sort( aSortedMapping.begin(), aSortedMapping.end() );
bool bNeedSequenceMapping = false;
for (sal_Int32 i = 0; i < aSequenceMapping.getLength(); ++i)
{
auto it = std::find( aSortedMapping.begin(), aSortedMapping.end(),
aSequenceMapping[i] );
aSequenceMapping[i] = std::distance(aSortedMapping.begin(), 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;
OSL_ENSURE( !aSortedCellRanges.isEmpty(), "CellRangeRepresentation missing" );
const OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
aResult.realloc(5);
sal_Int32 nProps = 0;
aResult[nProps ].Name = "FirstCellAsLabel";
aResult[nProps++].Value <<= bFirstCellIsLabel;
aResult[nProps ].Name = "CellRangeRepresentation";
aResult[nProps++].Value <<= aSortedCellRanges;
if (!aBrokenCellRangeForExport.isEmpty())
{
aResult[nProps ].Name = "BrokenCellRangeForExport";
aResult[nProps++].Value <<= aBrokenCellRangeForExport;
}
if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
{
chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
aResult[nProps ].Name = "DataRowSource";
aResult[nProps++].Value <<= eDataRowSource;
if (aSequenceMapping.getLength() != 0)
{
aResult[nProps ].Name = "SequenceMapping";
aResult[nProps++].Value <<= aSequenceMapping;
}
}
aResult.realloc( nProps );
return aResult;
}
uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
const OUString& rRangeRepresentation, bool bTestOnly )
{
if (bDisposed)
throw lang::DisposedException();
SwFrameFormat *pTableFormat = nullptr; // pointer to table format
std::shared_ptr<SwUnoCursor> pUnoCursor; // pointer to new created cursor spanning the cell range
GetFormatAndCreateCursorFromRangeRep( 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();
OSL_ENSURE( pTableFormat && pUnoCursor, "table format or cursor missing" );
uno::Reference< chart2::data::XDataSequence > xDataSeq;
if (!bTestOnly)
xDataSeq = new SwChartDataSequence( *this, *pTableFormat, pUnoCursor );
return xDataSeq;
}
sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
const OUString& rRangeRepresentation )
{
SolarMutexGuard aGuard;
bool bPossible = true;
try
{
Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, true );
}
catch (lang::IllegalArgumentException &)
{
bPossible = false;
}
return bPossible;
}
uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
const OUString& rRangeRepresentation )
{
SolarMutexGuard aGuard;
return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
}
uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
{
// note: it is no error to return nothing here
return uno::Reference< sheet::XRangeSelection >();
}
uno::Reference<css::chart2::data::XDataSequence> SAL_CALL
SwChartDataProvider::createDataSequenceByValueArray(
const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/ )
{
return uno::Reference<css::chart2::data::XDataSequence>();
}
void SAL_CALL SwChartDataProvider::dispose( )
{
bool bMustDispose( false );
{
osl::MutexGuard aGuard( GetChartMutex() );
bMustDispose = !bDisposed;
if (!bDisposed)
bDisposed = true;
}
if (bMustDispose)
{
// dispose all data-sequences
Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() );
while (aIt != aDataSequences.end())
{
DisposeAllDataSequences( (*aIt).first );
++aIt;
}
// release all references to data-sequences
aDataSequences.clear();
// require listeners to release references to this object
lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
aEvtListeners.disposeAndClear( aEvtObj );
}
}
void SAL_CALL SwChartDataProvider::addEventListener(
const uno::Reference< lang::XEventListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!bDisposed && rxListener.is())
aEvtListeners.addInterface( rxListener );
}
void SAL_CALL SwChartDataProvider::removeEventListener(
const uno::Reference< lang::XEventListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!bDisposed && rxListener.is())
aEvtListeners.removeInterface( rxListener );
}
OUString SAL_CALL SwChartDataProvider::getImplementationName( )
{
return OUString("SwChartDataProvider");
}
sal_Bool SAL_CALL SwChartDataProvider::supportsService(const OUString& rServiceName )
{
return cppu::supportsService(this, rServiceName);
}
uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( )
{
return { "com.sun.star.chart2.data.DataProvider"};
}
void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > const &rxDataSequence )
{
aDataSequences[ &rTable ].insert( rxDataSequence );
}
void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > const &rxDataSequence )
{
aDataSequences[ &rTable ].erase( rxDataSequence );
}
void SwChartDataProvider::InvalidateTable( const SwTable *pTable )
{
OSL_ENSURE( pTable, "table pointer is NULL" );
if (pTable)
{
if (!bDisposed)
pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() );
while (aIt != rSet.end())
{
uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
if (xRef.is())
{
// mark the sequence as 'dirty' and notify listeners
xRef->setModified( true );
}
++aIt;
}
}
}
void SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
{
OSL_ENSURE( pTable, "table pointer is NULL" );
if (pTable)
{
if (!bDisposed)
pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
// iterate over all data-sequences for that table...
Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate
while (aIt != aEndIt)
{
SwChartDataSequence *pDataSeq = nullptr;
bool bNowEmpty = false;
bool bSeqDisposed = false;
// check if weak reference is still valid...
uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
uno::Reference< chart2::data::XDataSequence > xRef( xTemp, uno::UNO_QUERY );
if (xRef.is())
{
// then delete that table box (check if implementation cursor needs to be adjusted)
pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() );
if (pDataSeq)
{
try
{
bNowEmpty = pDataSeq->DeleteBox( rBox );
}
catch (const lang::DisposedException&)
{
bNowEmpty = true;
bSeqDisposed = true;
}
if (bNowEmpty)
aDelIt = aIt;
}
}
++aIt;
if (bNowEmpty)
{
rSet.erase( aDelIt );
if (pDataSeq && !bSeqDisposed)
pDataSeq->dispose(); // the current way to tell chart that sth. got removed
}
}
}
}
void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
{
OSL_ENSURE( pTable, "table pointer is NULL" );
if (pTable)
{
if (!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 Set_DataSequenceRef_t aSet( aDataSequences[ pTable ] );
Set_DataSequenceRef_t::const_iterator aIt( aSet.begin() );
Set_DataSequenceRef_t::const_iterator aEndIt( aSet.end() );
while (aIt != aEndIt)
{
uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
if (xRef.is())
{
xRef->dispose();
}
++aIt;
}
}
}
/**
* 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;
const size_t nBoxes = rBoxes.size();
if (nBoxes < 1 || nLines < 1)
return;
SwTableBox* pFirstBox = rBoxes[0];
SwTableBox* pLastBox = rBoxes.back();
if (pFirstBox && pLastBox)
{
sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
SwXTextTable::GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow );
SwXTextTable::GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow );
bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/
if (nFirstCol == nLastCol && nFirstRow != nLastRow)
bAddCols = true;
if (nFirstCol == nLastCol || nFirstRow == nLastRow)
{
//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 Set_DataSequenceRef_t &rSet = aDataSequences[ &rTable ];
Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() );
while (aIt != rSet.end())
{
uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
if (xRef.is())
{
const sal_Int32 nLen = xRef->getTextualData().getLength();
if (nLen > 1) // value data-sequence ?
{
uno::Reference< lang::XUnoTunnel > xTunnel( xRef, uno::UNO_QUERY );
if(xTunnel.is())
{
SwChartDataSequence *pDataSeq = reinterpret_cast< SwChartDataSequence * >(
sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething( SwChartDataSequence::getUnoTunnelId() )));
if (pDataSeq)
{
SwRangeDescriptor aDesc;
pDataSeq->FillRangeDesc( aDesc );
chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
eDRSource = chart::ChartDataRowSource_ROWS;
if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
{
// add rows: extend affected columns by newly added row cells
pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
}
else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
{
// add cols: extend affected rows by newly added column cells
pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
}
}
}
}
}
++aIt;
}
}
}
}
// XRangeXMLConversion
OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const OUString& rRangeRepresentation )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
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( pDoc, aRange, &pTableFormat, pCursor );
if (!pTableFormat)
throw lang::IllegalArgumentException();
SwTable* pTable = SwTable::FindTable( pTableFormat );
if (pTable->IsTableComplex())
throw uno::RuntimeException();
// check that there is only one table used in all ranges
if (!pFirstFoundTable)
pFirstFoundTable = pTable;
if (pTable != pFirstFoundTable)
throw lang::IllegalArgumentException();
OUString aTableName;
OUString aStartCell;
OUString aEndCell;
if (!GetTableAndCellsFromRangeRep( aRange, aTableName, aStartCell, aEndCell ))
throw lang::IllegalArgumentException();
sal_Int32 nCol, nRow;
SwXTextTable::GetCellPosition( aStartCell, nCol, nRow );
if (nCol < 0 || nRow < 0)
throw uno::RuntimeException();
//!! following objects/functions are implemented in XMLRangeHelper.?xx
//!! which is a copy of the respective file from chart2 !!
XMLRangeHelper::CellRange aCellRange;
aCellRange.aTableName = aTableName;
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();
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);
return aRes.makeStringAndClear();
}
OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const OUString& rXMLRange )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
if (rXMLRange.isEmpty())
return OUString();
OUStringBuffer aRes;
// 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();
OUString aTmp = aCellRange.aTableName + "." +
sw_GetCellName( aCellRange.aUpperLeft.nColumn,
aCellRange.aUpperLeft.nRow );
// does cell range consist of more than a single cell?
if (!aCellRange.aLowerRight.bIsEmpty)
{
aTmp += ":";
aTmp += sw_GetCellName( aCellRange.aLowerRight.nColumn,
aCellRange.aLowerRight.nRow );
}
if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
aRes.append(";");
aRes.append(aTmp);
}
while (nPos>0);
return aRes.makeStringAndClear();
}
SwChartDataSource::SwChartDataSource(
const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
aLDS( rLDS )
{
}
SwChartDataSource::~SwChartDataSource()
{
}
uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( )
{
SolarMutexGuard aGuard;
return aLDS;
}
OUString SAL_CALL SwChartDataSource::getImplementationName( )
{
return OUString("SwChartDataSource");
}
sal_Bool SAL_CALL SwChartDataSource::supportsService(const OUString& rServiceName )
{
return cppu::supportsService(this, rServiceName);
}
uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( )
{
return { "com.sun.star.chart2.data.DataSource" };
}
SwChartDataSequence::SwChartDataSequence(
SwChartDataProvider &rProvider,
SwFrameFormat &rTableFormat,
const std::shared_ptr<SwUnoCursor>& pTableCursor ) :
SwClient( &rTableFormat ),
m_aEvtListeners( GetChartMutex() ),
m_aModifyListeners( GetChartMutex() ),
m_aRowLabelText( SwResId( STR_CHART2_ROW_LABEL_TEXT ) ),
m_aColLabelText( SwResId( STR_CHART2_COL_LABEL_TEXT ) ),
m_xDataProvider( &rProvider ),
m_pTableCursor( pTableCursor ),
m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
{
m_bDisposed = false;
acquire();
try
{
const SwTable* pTable = SwTable::FindTable( &rTableFormat );
if (pTable)
{
uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
m_xDataProvider->AddDataSequence( *pTable, xRef );
m_xDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
}
else {
OSL_FAIL( "table missing" );
}
}
catch (uno::RuntimeException &)
{
// TODO: shouldn't there be a call to release() here?
throw;
}
catch (uno::Exception &)
{
}
release();
#if OSL_DEBUG_LEVEL > 0
// check if it can properly convert into a SwUnoTableCursor
// which is required for some functions
SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
#endif
}
SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
SwChartDataSequenceBaseClass(rObj),
SwClient( rObj.GetFrameFormat() ),
m_aEvtListeners( GetChartMutex() ),
m_aModifyListeners( GetChartMutex() ),
m_aRole( rObj.m_aRole ),
m_aRowLabelText( SwResId(STR_CHART2_ROW_LABEL_TEXT) ),
m_aColLabelText( SwResId(STR_CHART2_COL_LABEL_TEXT) ),
m_xDataProvider( rObj.m_xDataProvider ),
m_pTableCursor( rObj.m_pTableCursor ),
m_pPropSet( rObj.m_pPropSet )
{
m_bDisposed = false;
acquire();
try
{
const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
if (pTable)
{
uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
m_xDataProvider->AddDataSequence( *pTable, xRef );
m_xDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
}
else {
OSL_FAIL( "table missing" );
}
}
catch (uno::RuntimeException &)
{
// TODO: shouldn't there be a call to release() here?
throw;
}
catch (uno::Exception &)
{
}
release();
#if OSL_DEBUG_LEVEL > 0
// check if it can properly convert into a SwUnoTableCursor
// which is required for some functions
SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
#endif
}
SwChartDataSequence::~SwChartDataSequence()
{
}
namespace
{
class theSwChartDataSequenceUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwChartDataSequenceUnoTunnelId > {};
}
const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
{
return theSwChartDataSequenceUnoTunnelId::get().getSeq();
}
sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
{
if( rId.getLength() == 16
&& 0 == memcmp( getUnoTunnelId().getConstArray(),
rId.getConstArray(), 16 ) )
{
return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
}
return 0;
}
OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
OUString aRes;
SwFrameFormat* pTableFormat = GetFrameFormat();
if (pTableFormat)
{
const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
aRes = pTableFormat->GetName() + "." + aCellRange;
}
return aRes;
}
uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
chart2::data::LabelOrigin eLabelOrigin )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
uno::Sequence< OUString > aLabels;
{
SwRangeDescriptor aDesc;
bool bOk = false;
SwFrameFormat* pTableFormat = GetFrameFormat();
SwTable* pTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
if (!pTableFormat || !pTable || pTable->IsTableComplex())
throw uno::RuntimeException();
const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
bOk = FillRangeDescriptor( aDesc, aCellRange );
OSL_ENSURE( bOk, "failed to get SwRangeDescriptor" );
if (bOk)
{
aDesc.Normalize();
sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
OSL_ENSURE( nColSpan == 1 || nRowSpan == 1,
"unexpected range of selected cells" );
OUString aText; // label text to be returned
bool bReturnEmptyText = false;
bool bUseCol = true;
if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
bUseCol = true;
else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
bUseCol = false;
else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
{
bUseCol = nColSpan < nRowSpan;
bReturnEmptyText = nColSpan == nRowSpan;
}
else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
{
bUseCol = nColSpan > nRowSpan;
bReturnEmptyText = nColSpan == nRowSpan;
}
else {
OSL_FAIL( "unexpected case" );
}
// build label sequence
sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
aLabels.realloc( nSeqLen );
OUString *pLabels = aLabels.getArray();
for (sal_Int32 i = 0; i < nSeqLen; ++i)
{
if (!bReturnEmptyText)
{
aText = bUseCol ? m_aColLabelText : m_aRowLabelText;
sal_Int32 nCol = aDesc.nLeft;
sal_Int32 nRow = aDesc.nTop;
if (bUseCol)
nCol = nCol + i;
else
nRow = nRow + i;
OUString aCellName( sw_GetCellName( nCol, nRow ) );
sal_Int32 nLen = aCellName.getLength();
if (nLen)
{
const sal_Unicode *pBuf = aCellName.getStr();
const sal_Unicode *pEnd = pBuf + nLen;
while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9'))
++pBuf;
// start of number found?
if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
{
OUString aRplc;
OUString aNew;
if (bUseCol)
{
aRplc = "%COLUMNLETTER";
aNew = aCellName.copy(0, pBuf - aCellName.getStr());
}
else
{
aRplc = "%ROWNUMBER";
aNew = OUString(pBuf, (aCellName.getStr() + nLen) - pBuf);
}
aText = aText.replaceFirst( aRplc, aNew );
}
}
}
pLabels[i] = aText;
}
}
}
return aLabels;
}
::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
::sal_Int32 /*nIndex*/ )
{
return 0;
}
std::vector< css::uno::Reference< css::table::XCell > > SwChartDataSequence::GetCells()
{
if (m_bDisposed)
throw lang::DisposedException();
auto pTableFormat(GetFrameFormat());
if(!pTableFormat)
return std::vector< css::uno::Reference< css::table::XCell > >();
auto pTable(SwTable::FindTable(pTableFormat));
if(pTable->IsTableComplex())
return std::vector< css::uno::Reference< css::table::XCell > >();
SwRangeDescriptor aDesc;
if(!FillRangeDescriptor(aDesc, GetCellRangeName(*pTableFormat, *m_pTableCursor)))
return std::vector< css::uno::Reference< css::table::XCell > >();
return SwXCellRange::CreateXCellRange(m_pTableCursor, *pTableFormat, aDesc)->GetCells();
}
uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData()
{
SolarMutexGuard aGuard;
auto vCells(GetCells());
uno::Sequence< OUString > vTextData(vCells.size());
std::transform(vCells.begin(),
vCells.end(),
vTextData.begin(),
[] (decltype(vCells)::value_type& xCell)
{ return static_cast<SwXCell*>(xCell.get())->getString(); });
return vTextData;
}
uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData()
{
SolarMutexGuard aGuard;
auto vCells(GetCells());
uno::Sequence< uno::Any > vAnyData(vCells.size());
std::transform(vCells.begin(),
vCells.end(),
vAnyData.begin(),
[] (decltype(vCells)::value_type& xCell)
{ return static_cast<SwXCell*>(xCell.get())->GetAny(); });
return vAnyData;
}
uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData()
{
SolarMutexGuard aGuard;
auto vCells(GetCells());
uno::Sequence< double > vNumData(vCells.size());
std::transform(vCells.begin(),
vCells.end(),
vNumData.begin(),
[] (decltype(vCells)::value_type& xCell)
{ return static_cast<SwXCell*>(xCell.get())->GetForcedNumericalValue(); });
return vNumData;
}
uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
return new SwChartDataSequence( *this );
}
uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
static uno::Reference< beans::XPropertySetInfo > xRes = m_pPropSet->getPropertySetInfo();
return xRes;
}
void SAL_CALL SwChartDataSequence::setPropertyValue(
const OUString& rPropertyName,
const uno::Any& rValue )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
if (rPropertyName != UNO_NAME_ROLE)
throw beans::UnknownPropertyException();
if ( !(rValue >>= m_aRole) )
throw lang::IllegalArgumentException();
}
uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
const OUString& rPropertyName )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
if (!(rPropertyName == UNO_NAME_ROLE))
throw beans::UnknownPropertyException();
return uno::Any(m_aRole);
}
void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
{
OSL_FAIL( "not implemented" );
}
void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
{
OSL_FAIL( "not implemented" );
}
void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
{
OSL_FAIL( "not implemented" );
}
void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
{
OSL_FAIL( "not implemented" );
}
OUString SAL_CALL SwChartDataSequence::getImplementationName( )
{
return OUString("SwChartDataSequence");
}
sal_Bool SAL_CALL SwChartDataSequence::supportsService(const OUString& rServiceName )
{
return cppu::supportsService(this, rServiceName);
}
uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( )
{
return { "com.sun.star.chart2.data.DataSequence" };
}
void SwChartDataSequence::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
{
ClientModify(this, pOld, pNew );
// table was deleted or cursor was deleted
if(!GetRegisteredIn() || !m_pTableCursor)
{
m_pTableCursor.reset(nullptr);
dispose();
}
else
{
setModified( true );
}
}
sal_Bool SAL_CALL SwChartDataSequence::isModified( )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
return true;
}
void SAL_CALL SwChartDataSequence::setModified(
sal_Bool bModified )
{
SolarMutexGuard aGuard;
if (m_bDisposed)
throw lang::DisposedException();
if (bModified)
LaunchModifiedEvent( m_aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
}
void SAL_CALL SwChartDataSequence::addModifyListener(
const uno::Reference< util::XModifyListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!m_bDisposed && rxListener.is())
m_aModifyListeners.addInterface( rxListener );
}
void SAL_CALL SwChartDataSequence::removeModifyListener(
const uno::Reference< util::XModifyListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!m_bDisposed && rxListener.is())
m_aModifyListeners.removeInterface( rxListener );
}
void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
{
if (m_bDisposed)
throw lang::DisposedException();
if (rSource.Source == static_cast<cppu::OWeakObject*>(m_xDataProvider.get()))
{
m_xDataProvider.clear();
}
}
void SAL_CALL SwChartDataSequence::dispose( )
{
bool bMustDispose( false );
{
osl::MutexGuard aGuard( GetChartMutex() );
bMustDispose = !m_bDisposed;
if (!m_bDisposed)
m_bDisposed = true;
}
if (bMustDispose)
{
m_bDisposed = true;
if (m_xDataProvider.is())
{
const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
if (pTable)
{
uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
m_xDataProvider->RemoveDataSequence( *pTable, xRef );
}
else {
OSL_FAIL( "table missing" );
}
//#i119653# The bug is crashed for an exception thrown by
//SwCharDataSequence::setModified() because
//the SwCharDataSequence object has been disposed.
//Actually, the former design of SwClient will disconnect itself
//from the notification list in its destructor.
//But the SwCharDataSequence won't be destructed but disposed in code
//(the data member SwChartDataSequence::bDisposed will be set to
//TRUE), the relationship between client and modification is not
//released.
//So any notification from modify object will lead to said
//exception threw out. Recorrect the logic of code in
//SwChartDataSequence::Dispose(), release the relationship
//here...
SwModify* pLclRegisteredIn = GetRegisteredInNonConst();
if (pLclRegisteredIn && pLclRegisteredIn->HasWriterListeners())
{
EndListeningAll();
m_pTableCursor.reset(nullptr);
}
}
// require listeners to release references to this object
lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
m_aModifyListeners.disposeAndClear( aEvtObj );
m_aEvtListeners.disposeAndClear( aEvtObj );
}
}
void SAL_CALL SwChartDataSequence::addEventListener(
const uno::Reference< lang::XEventListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!m_bDisposed && rxListener.is())
m_aEvtListeners.addInterface( rxListener );
}
void SAL_CALL SwChartDataSequence::removeEventListener(
const uno::Reference< lang::XEventListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!m_bDisposed && rxListener.is())
m_aEvtListeners.removeInterface( rxListener );
}
bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
{
if (m_bDisposed)
throw lang::DisposedException();
// to be set if the last box of the data-sequence was removed here
bool bNowEmpty = false;
// if the implementation cursor gets affected (i.e. the box where it is located
// in gets removed) we need to move it before that... (otherwise it does not need to change)
const SwStartNode* pPointStartNode = m_pTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
const SwStartNode* pMarkStartNode = m_pTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
if (!m_pTableCursor->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd()))
{
bNowEmpty = true;
}
else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd())
{
sal_Int32 nPointRow = -1, nPointCol = -1;
sal_Int32 nMarkRow = -1, nMarkCol = -1;
const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
OUString aPointCellName( pTable->GetTableBox( pPointStartNode->GetIndex() )->GetName() );
OUString aMarkCellName( pTable->GetTableBox( pMarkStartNode->GetIndex() )->GetName() );
SwXTextTable::GetCellPosition( aPointCellName, nPointCol, nPointRow );
SwXTextTable::GetCellPosition( aMarkCellName, nMarkCol, nMarkRow );
OSL_ENSURE( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
OSL_ENSURE( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
// move vertical or horizontal?
OSL_ENSURE( nPointRow == nMarkRow || nPointCol == nMarkCol,
"row/col indices not matching" );
OSL_ENSURE( nPointRow != nMarkRow || nPointCol != nMarkCol,
"point and mark are identical" );
bool bMoveVertical = (nPointCol == nMarkCol);
bool bMoveHorizontal = (nPointRow == nMarkRow);
// get movement direction
bool bMoveLeft = false; // move left or right?
bool bMoveUp = false; // move up or down?
if (bMoveVertical)
{
if (pPointStartNode == rBox.GetSttNd()) // move point?
bMoveUp = nPointRow > nMarkRow;
else // move mark
bMoveUp = nMarkRow > nPointRow;
}
else if (bMoveHorizontal)
{
if (pPointStartNode == rBox.GetSttNd()) // move point?
bMoveLeft = nPointCol > nMarkCol;
else // move mark
bMoveLeft = nMarkCol > nPointCol;
}
else {
OSL_FAIL( "neither vertical nor horizontal movement" );
}
// get new box (position) to use...
sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
if (bMoveVertical)
nRow += bMoveUp ? -1 : +1;
if (bMoveHorizontal)
nCol += bMoveLeft ? -1 : +1;
const OUString aNewCellName = sw_GetCellName( nCol, nRow );
SwTableBox* pNewBox = const_cast<SwTableBox*>(pTable->GetTableBox( aNewCellName ));
if (pNewBox) // set new position (cell range) to use
{
// This is how you get the first content node of a row:
// First get a SwNodeIndex pointing to the node after SwStartNode of the box...
SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
// This can be a SwContentNode, but might also be a table or section node,
// therefore call GoNext
SwContentNode *pCNd = aIdx.GetNode().GetContentNode();
if (!pCNd)
pCNd = GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx );
// and then one can e.g. create a SwPosition:
SwPosition aNewPos( *pCNd ); // new position to be used with cursor
// if the mark is to be changed, make sure there is one
if (pMarkStartNode == rBox.GetSttNd() && !m_pTableCursor->HasMark())
m_pTableCursor->SetMark();
// set cursor to new position
SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
m_pTableCursor->GetPoint() : m_pTableCursor->GetMark();
if (pPos)
{
pPos->nNode = aNewPos.nNode;
pPos->nContent = aNewPos.nContent;
}
else {
OSL_FAIL( "neither point nor mark available for change" );
}
}
else {
OSL_FAIL( "failed to get position" );
}
}
return bNowEmpty;
}
void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
{
SwFrameFormat* pTableFormat = GetFrameFormat();
if(pTableFormat)
{
SwTable* pTable = SwTable::FindTable( pTableFormat );
if(!pTable->IsTableComplex())
{
FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
}
}
}
/**
* Extends the data-sequence by new cells added at the end of the direction
* the data-sequence points to.
* If the cells are already within the range of the sequence nothing needs
* to be done.
* If the cells are beyond the end of the sequence (are not adjacent to the
* current last cell) nothing can be done. Only if the cells are adjacent to
* the last cell they can be added.
*
* @returns true if the data-sequence was changed.
* @param bExtendCols - specifies if columns or rows are to be extended
* @param nFirstNew - index of first new row/col to be included in data-sequence
* @param nLastNew - index of last new row/col to be included in data-sequence
*/
void SwChartDataSequence::ExtendTo( bool bExtendCol,
sal_Int32 nFirstNew, sal_Int32 nCount )
{
SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
if (!pUnoTableCursor)
return;
const SwStartNode *pStartNd = nullptr;
const SwTableBox *pStartBox = nullptr;
const SwTableBox *pEndBox = nullptr;
const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
OSL_ENSURE( !pTable->IsTableComplex(), "table too complex" );
if (nCount < 1 || nFirstNew < 0 || pTable->IsTableComplex())
return;
// get range descriptor (cell range) for current data-sequence
pStartNd = pUnoTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
pEndBox = pTable->GetTableBox( pStartNd->GetIndex() );
const OUString aEndBox( pEndBox->GetName() );
pStartNd = pUnoTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
pStartBox = pTable->GetTableBox( pStartNd->GetIndex() );
const OUString aStartBox( pStartBox->GetName() );
SwRangeDescriptor aDesc;
// note that cell range here takes the newly added rows/cols already into account
FillRangeDescriptor( aDesc, aStartBox + ":" + aEndBox );
bool bChanged = false;
OUString aNewStartCell;
OUString aNewEndCell;
if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
{
// new column cells adjacent to the bottom of the
// current data-sequence to be added...
OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
bChanged = true;
}
else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
{
// new column cells adjacent to the top of the
// current data-sequence to be added...
OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop - nCount);
aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
bChanged = true;
}
else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
{
// new row cells adjacent to the right of the
// current data-sequence to be added...
OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
aNewEndCell = sw_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
bChanged = true;
}
else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
{
// new row cells adjacent to the left of the
// current data-sequence to be added...
OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
aNewStartCell = sw_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
bChanged = true;
}
if (bChanged)
{
// move table cursor to new start and end of data-sequence
const SwTableBox *pNewStartBox = pTable->GetTableBox( aNewStartCell );
const SwTableBox *pNewEndBox = pTable->GetTableBox( aNewEndCell );
pUnoTableCursor->SetMark();
pUnoTableCursor->GetPoint()->nNode = *pNewEndBox->GetSttNd();
pUnoTableCursor->GetMark()->nNode = *pNewStartBox->GetSttNd();
pUnoTableCursor->Move( fnMoveForward, GoInNode );
pUnoTableCursor->MakeBoxSels();
}
}
SwChartLabeledDataSequence::SwChartLabeledDataSequence() :
aEvtListeners( GetChartMutex() ),
aModifyListeners( GetChartMutex() )
{
bDisposed = false;
}
SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
{
}
uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
return xData;
}
void SwChartLabeledDataSequence::SetDataSequence(
uno::Reference< chart2::data::XDataSequence >& rxDest,
const uno::Reference< chart2::data::XDataSequence >& rxSource)
{
uno::Reference< util::XModifyListener > xML( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY );
uno::Reference< lang::XEventListener > xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY );
// stop listening to old data-sequence
uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
if (xMB.is())
xMB->removeModifyListener( xML );
uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
if (xC.is())
xC->removeEventListener( xEL );
rxDest = rxSource;
// start listening to new data-sequence
xC.set( rxDest, uno::UNO_QUERY );
if (xC.is())
xC->addEventListener( xEL );
xMB.set( rxDest, uno::UNO_QUERY );
if (xMB.is())
xMB->addModifyListener( xML );
}
void SAL_CALL SwChartLabeledDataSequence::setValues(
const uno::Reference< chart2::data::XDataSequence >& rxSequence )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
if (xData != rxSequence)
{
SetDataSequence( xData, rxSequence );
// inform listeners of changes
LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
}
}
uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
return xLabels;
}
void SAL_CALL SwChartLabeledDataSequence::setLabel(
const uno::Reference< chart2::data::XDataSequence >& rxSequence )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
if (xLabels != rxSequence)
{
SetDataSequence( xLabels, rxSequence );
// inform listeners of changes
LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
}
}
uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( )
{
SolarMutexGuard aGuard;
if (bDisposed)
throw lang::DisposedException();
uno::Reference< util::XCloneable > xRes;
uno::Reference< util::XCloneable > xDataCloneable( xData, uno::UNO_QUERY );
uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, uno::UNO_QUERY );
SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence();
if (xDataCloneable.is())
{
uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
pRes->setValues( xDataClone );
}
if (xLabelsCloneable.is())
{
uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
pRes->setLabel( xLabelsClone );
}
xRes = pRes;
return xRes;
}
OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( )
{
return OUString("SwChartLabeledDataSequence");
}
sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
const OUString& rServiceName )
{
return cppu::supportsService(this, rServiceName);
}
uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( )
{
return { "com.sun.star.chart2.data.LabeledDataSequence" };
}
void SAL_CALL SwChartLabeledDataSequence::disposing(
const lang::EventObject& rSource )
{
osl::MutexGuard aGuard( GetChartMutex() );
uno::Reference< uno::XInterface > xRef( rSource.Source );
if (xRef == xData)
xData.clear();
if (xRef == xLabels)
xLabels.clear();
if (!xData.is() && !xLabels.is())
dispose();
}
void SAL_CALL SwChartLabeledDataSequence::modified(
const lang::EventObject& rEvent )
{
if (rEvent.Source == xData || rEvent.Source == xLabels)
{
LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
}
}
void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
const uno::Reference< util::XModifyListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!bDisposed && rxListener.is())
aModifyListeners.addInterface( rxListener );
}
void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
const uno::Reference< util::XModifyListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!bDisposed && rxListener.is())
aModifyListeners.removeInterface( rxListener );
}
void SAL_CALL SwChartLabeledDataSequence::dispose( )
{
bool bMustDispose( false );
{
osl::MutexGuard aGuard( GetChartMutex() );
bMustDispose = !bDisposed;
if (!bDisposed)
bDisposed = true;
}
if (bMustDispose)
{
bDisposed = true;
// require listeners to release references to this object
lang::EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) );
aModifyListeners.disposeAndClear( aEvtObj );
aEvtListeners.disposeAndClear( aEvtObj );
}
}
void SAL_CALL SwChartLabeledDataSequence::addEventListener(
const uno::Reference< lang::XEventListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!bDisposed && rxListener.is())
aEvtListeners.addInterface( rxListener );
}
void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
const uno::Reference< lang::XEventListener >& rxListener )
{
osl::MutexGuard aGuard( GetChartMutex() );
if (!bDisposed && rxListener.is())
aEvtListeners.removeInterface( rxListener );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V547 Expression 'pTableFormat' is always true.
↑ V547 Expression 'aDataStartIdx[oi] != - 1' is always true.