diff options
Diffstat (limited to 'sc/source/ui/unoobj/chart2uno.cxx')
-rw-r--r-- | sc/source/ui/unoobj/chart2uno.cxx | 3914 |
1 files changed, 3914 insertions, 0 deletions
diff --git a/sc/source/ui/unoobj/chart2uno.cxx b/sc/source/ui/unoobj/chart2uno.cxx new file mode 100644 index 000000000000..8a8081e412d3 --- /dev/null +++ b/sc/source/ui/unoobj/chart2uno.cxx @@ -0,0 +1,3914 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +#include "chart2uno.hxx" +#include "miscuno.hxx" +#include "document.hxx" +#include "unoguard.hxx" +#include "cell.hxx" +#include "chartpos.hxx" +#include "unonames.hxx" +#include "globstr.hrc" +#include "convuno.hxx" +#include "rangeutl.hxx" +#include "hints.hxx" +#include "unoreflist.hxx" +#include "compiler.hxx" +#include "reftokenhelper.hxx" +#include "chartlis.hxx" + +#include <sfx2/objsh.hxx> +#include <tools/table.hxx> + +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/text/XText.hpp> +#include <comphelper/extract.hxx> + +#include <vector> +#include <list> +#include <rtl/math.hxx> + +SC_SIMPLE_SERVICE_INFO( ScChart2DataProvider, "ScChart2DataProvider", + "com.sun.star.chart2.data.DataProvider") +SC_SIMPLE_SERVICE_INFO( ScChart2DataSource, "ScChart2DataSource", + "com.sun.star.chart2.data.DataSource") +SC_SIMPLE_SERVICE_INFO( ScChart2LabeledDataSequence, "ScChart2LabeledDataSequence", + "com.sun.star.chart2.data.LabeledDataSequence") +SC_SIMPLE_SERVICE_INFO( ScChart2DataSequence, "ScChart2DataSequence", + "com.sun.star.chart2.data.DataSequence") +#if USE_CHART2_EMPTYDATASEQUENCE +SC_SIMPLE_SERVICE_INFO( ScChart2EmptyDataSequence, "ScChart2EmptyDataSequence", + "com.sun.star.chart2.data.DataSequence") +#endif + +using namespace ::com::sun::star; +using namespace ::formula; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::std::auto_ptr; +using ::std::vector; +using ::std::list; +using ::std::distance; +using ::std::unary_function; +using ::std::hash_set; +using ::boost::shared_ptr; + +namespace +{ +const SfxItemPropertyMapEntry* lcl_GetDataProviderPropertyMap() +{ + static SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] = + { + {MAP_CHAR_LEN(SC_UNONAME_INCLUDEHIDDENCELLS), 0, &getBooleanCppuType(), 0, 0 }, + {0,0,0,0,0,0} + }; + return aDataProviderPropertyMap_Impl; +} + +const SfxItemPropertyMapEntry* lcl_GetDataSequencePropertyMap() +{ + static SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] = + { + {MAP_CHAR_LEN(SC_UNONAME_HIDDENVALUES), 0, &getCppuType((uno::Sequence<sal_Int32>*)0 ), 0, 0 }, + {MAP_CHAR_LEN(SC_UNONAME_ROLE), 0, &getCppuType((::com::sun::star::chart2::data::DataSequenceRole*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNONAME_INCLUDEHIDDENCELLS), 0, &getBooleanCppuType(), 0, 0 }, + {0,0,0,0,0,0} + }; + return aDataSequencePropertyMap_Impl; +} + +template< typename T > +::com::sun::star::uno::Sequence< T > lcl_VectorToSequence( + const ::std::vector< T > & rCont ) +{ + ::com::sun::star::uno::Sequence< T > aResult( rCont.size()); + ::std::copy( rCont.begin(), rCont.end(), aResult.getArray()); + return aResult; +} + +struct lcl_appendTableNumber : public ::std::unary_function< SCTAB, void > +{ + lcl_appendTableNumber( ::rtl::OUStringBuffer & rBuffer ) : + m_rBuffer( rBuffer ) + {} + void operator() ( SCTAB nTab ) + { + // there is no append with SCTAB or sal_Int16 + m_rBuffer.append( static_cast< sal_Int32 >( nTab )); + m_rBuffer.append( sal_Unicode( ' ' )); + } +private: + ::rtl::OUStringBuffer & m_rBuffer; +}; + +::rtl::OUString lcl_createTableNumberList( const ::std::list< SCTAB > & rTableList ) +{ + ::rtl::OUStringBuffer aBuffer; + ::std::for_each( rTableList.begin(), rTableList.end(), lcl_appendTableNumber( aBuffer )); + // remove last trailing ' ' + if( aBuffer.getLength() > 0 ) + aBuffer.setLength( aBuffer.getLength() - 1 ); + return aBuffer.makeStringAndClear(); +} + +uno::Reference< frame::XModel > lcl_GetXModel( ScDocument * pDoc ) +{ + uno::Reference< frame::XModel > xModel; + SfxObjectShell * pObjSh( pDoc ? pDoc->GetDocumentShell() : 0 ); + if( pObjSh ) + xModel.set( pObjSh->GetModel()); + return xModel; +} + +uno::Reference< sheet::XSpreadsheetDocument > lcl_GetSpreadSheetDocument( ScDocument * pDoc ) +{ + return uno::Reference< sheet::XSpreadsheetDocument >( lcl_GetXModel( pDoc ), uno::UNO_QUERY ); +} + +// ============================================================================ + +namespace { + +struct DeleteInstance : public unary_function<FormulaToken*, void> +{ + void operator() (FormulaToken* p) const + { + delete p; + } +}; + +} + +struct TokenTable +{ + SCROW mnRowCount; + SCCOL mnColCount; + vector<FormulaToken*> maTokens; + + void init( SCCOL nColCount, SCROW nRowCount ) + { + mnColCount = nColCount; + mnRowCount = nRowCount; + maTokens.reserve(mnColCount*mnRowCount); + } + void clear() + { + for_each(maTokens.begin(), maTokens.end(), DeleteInstance()); + } + + void push_back( FormulaToken* pToken ) + { + maTokens.push_back( pToken ); + DBG_ASSERT( maTokens.size()<= static_cast<sal_uInt32>( mnColCount*mnRowCount ), "too much tokens" ); + } + + sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const + { + DBG_ASSERT( nCol<mnColCount, "wrong column index" ); + DBG_ASSERT( nRow<mnRowCount, "wrong row index" ); + sal_uInt32 nRet = static_cast<sal_uInt32>(nCol*mnRowCount + nRow); + DBG_ASSERT( maTokens.size()>= static_cast<sal_uInt32>( mnColCount*mnRowCount ), "too few tokens" ); + return nRet; + } + + vector<ScSharedTokenRef>* getColRanges(SCCOL nCol) const; + vector<ScSharedTokenRef>* getRowRanges(SCROW nRow) const; + vector<ScSharedTokenRef>* getAllRanges() const; +}; + +vector<ScSharedTokenRef>* TokenTable::getColRanges(SCCOL nCol) const +{ + if (nCol >= mnColCount) + return NULL; + if( mnRowCount<=0 ) + return NULL; + + auto_ptr< vector<ScSharedTokenRef> > pTokens(new vector<ScSharedTokenRef>); + sal_uInt32 nLast = getIndex(nCol, mnRowCount-1); + for (sal_uInt32 i = getIndex(nCol, 0); i <= nLast; ++i) + { + FormulaToken* p = maTokens[i]; + if (!p) + continue; + + ScSharedTokenRef pCopy(static_cast<ScToken*>(p->Clone())); + ScRefTokenHelper::join(*pTokens, pCopy); + } + return pTokens.release(); +} + +vector<ScSharedTokenRef>* TokenTable::getRowRanges(SCROW nRow) const +{ + if (nRow >= mnRowCount) + return NULL; + if( mnColCount<=0 ) + return NULL; + + auto_ptr< vector<ScSharedTokenRef> > pTokens(new vector<ScSharedTokenRef>); + sal_uInt32 nLast = getIndex(mnColCount-1, nRow); + for (sal_uInt32 i = getIndex(0, nRow); i <= nLast; i += mnRowCount) + { + FormulaToken* p = maTokens[i]; + if (!p) + continue; + + ScSharedTokenRef p2(static_cast<ScToken*>(p->Clone())); + ScRefTokenHelper::join(*pTokens, p2); + } + return pTokens.release(); +} + +vector<ScSharedTokenRef>* TokenTable::getAllRanges() const +{ + auto_ptr< vector<ScSharedTokenRef> > pTokens(new vector<ScSharedTokenRef>); + sal_uInt32 nStop = mnColCount*mnRowCount; + for (sal_uInt32 i = 0; i < nStop; i++) + { + FormulaToken* p = maTokens[i]; + if (!p) + continue; + + ScSharedTokenRef p2(static_cast<ScToken*>(p->Clone())); + ScRefTokenHelper::join(*pTokens, p2); + } + return pTokens.release(); +} + +// ============================================================================ + +class Chart2PositionMap +{ +public: + Chart2PositionMap(SCCOL nColCount, SCROW nRowCount, + bool bFillRowHeader, bool bFillColumnHeader, Table& rCols, + ScDocument* pDoc ); + ~Chart2PositionMap(); + + SCCOL getDataColCount() const { return mnDataColCount; } + SCROW getDataRowCount() const { return mnDataRowCount; } + + vector<ScSharedTokenRef>* getLeftUpperCornerRanges() const; + vector<ScSharedTokenRef>* getAllColHeaderRanges() const; + vector<ScSharedTokenRef>* getAllRowHeaderRanges() const; + + vector<ScSharedTokenRef>* getColHeaderRanges(SCCOL nChartCol) const; + vector<ScSharedTokenRef>* getRowHeaderRanges(SCROW nChartRow) const; + + vector<ScSharedTokenRef>* getDataColRanges(SCCOL nCol) const; + vector<ScSharedTokenRef>* getDataRowRanges(SCROW nRow) const; + +private: + SCCOL mnDataColCount; + SCROW mnDataRowCount; + + TokenTable maLeftUpperCorner; //nHeaderColCount*nHeaderRowCount + TokenTable maColHeaders; //mnDataColCount*nHeaderRowCount + TokenTable maRowHeaders; //nHeaderColCount*mnDataRowCount + TokenTable maData;//mnDataColCount*mnDataRowCount +}; + +Chart2PositionMap::Chart2PositionMap(SCCOL nAllColCount, SCROW nAllRowCount, + bool bFillRowHeader, bool bFillColumnHeader, Table& rCols, ScDocument* pDoc) +{ + // if bFillRowHeader is true, at least the first column serves as a row header. + // If more than one column is pure text all the first pure text columns are used as header. + // Likewise, if bFillColumnHeader is true, at least the first row serves as a column header. + // If more than one row is pure text all the first pure text rows are used as header. + + SCROW nHeaderRowCount = (bFillColumnHeader && nAllColCount && nAllRowCount) ? 1 : 0; + SCCOL nHeaderColCount = (bFillRowHeader && nAllColCount && nAllRowCount) ? 1 : 0; + + if( nHeaderColCount || nHeaderRowCount ) + { + const SCCOL nInitialHeaderColCount = nHeaderColCount; + //check whether there is more than one text column or row that should be added to the headers + SCROW nSmallestValueRowIndex = nAllRowCount; + bool bFoundValues = false; + bool bFoundAnything = false; + Table* pCol = static_cast<Table*>(rCols.First()); + for (SCCOL nCol = 0; !bFoundValues && nCol < nAllColCount; ++nCol) + { + if (pCol && nCol>=nHeaderColCount) + { + ScToken* pToken = static_cast<ScToken*>(pCol->First()); + for (SCROW nRow = 0; !bFoundValues && nRow < nSmallestValueRowIndex; ++nRow) + { + if (pToken && nRow>=nHeaderRowCount) + { + ScRange aRange; + bool bExternal = false; + StackVar eType = pToken->GetType(); + if( eType==svExternal || eType==svExternalSingleRef || eType==svExternalDoubleRef || eType==svExternalName ) + bExternal = true;//lllll todo correct? + ScSharedTokenRef pSharedToken(static_cast<ScToken*>(pToken->Clone())); + ScRefTokenHelper::getRangeFromToken(aRange, pSharedToken, bExternal ); + SCCOL nCol1=0, nCol2=0; + SCROW nRow1=0, nRow2=0; + SCTAB nTab1=0, nTab2=0; + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if (pDoc && pDoc->HasValueData( nCol1, nRow1, nTab1 )) + { + bFoundValues = bFoundAnything = true; + nSmallestValueRowIndex = std::min( nSmallestValueRowIndex, nRow ); + } + if( !bFoundAnything ) + { + if (pDoc && pDoc->HasData( nCol1, nRow1, nTab1 ) ) + bFoundAnything = true; + } + } + pToken = static_cast<ScToken*>(pCol->Next()); + } + if(!bFoundValues && nHeaderColCount>0) + nHeaderColCount++; + } + pCol = static_cast<Table*>(rCols.Next()); + } + if( bFoundAnything ) + { + if(nHeaderRowCount>0) + { + if( bFoundValues ) + nHeaderRowCount = nSmallestValueRowIndex; + else if( nAllRowCount>1 ) + nHeaderRowCount = nAllRowCount-1; + } + } + else //if the cells are completely empty, just use single header rows and columns + nHeaderColCount = nInitialHeaderColCount; + } + + mnDataColCount = nAllColCount - nHeaderColCount; + mnDataRowCount = nAllRowCount - nHeaderRowCount; + + maLeftUpperCorner.init(nHeaderColCount,nHeaderRowCount); + maColHeaders.init(mnDataColCount,nHeaderRowCount); + maRowHeaders.init(nHeaderColCount,mnDataRowCount); + maData.init(mnDataColCount,mnDataRowCount); + + Table* pCol = static_cast<Table*>(rCols.First()); + FormulaToken* pToken = static_cast<FormulaToken*>(pCol->First()); + for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol) + { + if (pCol) + { + pToken = static_cast<FormulaToken*>(pCol->First()); + for (SCROW nRow = 0; nRow < nAllRowCount; ++nRow) + { + if( nCol < nHeaderColCount ) + { + if( nRow < nHeaderRowCount ) + maLeftUpperCorner.push_back(pToken); + else + maRowHeaders.push_back(pToken); + } + else if( nRow < nHeaderRowCount ) + maColHeaders.push_back(pToken); + else + maData.push_back(pToken); + + pToken = static_cast<FormulaToken*>(pCol->Next()); + } + } + pCol = static_cast<Table*>(rCols.Next()); + } +} + +Chart2PositionMap::~Chart2PositionMap() +{ + maLeftUpperCorner.clear(); + maColHeaders.clear(); + maRowHeaders.clear(); + maData.clear(); +} + +vector<ScSharedTokenRef>* Chart2PositionMap::getLeftUpperCornerRanges() const +{ + return maLeftUpperCorner.getAllRanges(); +} +vector<ScSharedTokenRef>* Chart2PositionMap::getAllColHeaderRanges() const +{ + return maColHeaders.getAllRanges(); +} +vector<ScSharedTokenRef>* Chart2PositionMap::getAllRowHeaderRanges() const +{ + return maRowHeaders.getAllRanges(); +} +vector<ScSharedTokenRef>* Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const +{ + return maColHeaders.getColRanges( nCol); +} +vector<ScSharedTokenRef>* Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const +{ + return maRowHeaders.getRowRanges( nRow); +} + +vector<ScSharedTokenRef>* Chart2PositionMap::getDataColRanges(SCCOL nCol) const +{ + return maData.getColRanges( nCol); +} + +vector<ScSharedTokenRef>* Chart2PositionMap::getDataRowRanges(SCROW nRow) const +{ + return maData.getRowRanges( nRow); +} + +// ---------------------------------------------------------------------------- + +/** + * Designed to be a drop-in replacement for ScChartPositioner, in order to + * handle external references. + */ +class Chart2Positioner +{ + enum GlueType + { + GLUETYPE_NA, + GLUETYPE_NONE, + GLUETYPE_COLS, + GLUETYPE_ROWS, + GLUETYPE_BOTH + }; + +public: + Chart2Positioner(ScDocument* pDoc, const vector<ScSharedTokenRef>& rRefTokens) : + mpRefTokens(new vector<ScSharedTokenRef>(rRefTokens)), + mpPositionMap(NULL), + meGlue(GLUETYPE_NA), + mpDoc(pDoc), + mbColHeaders(false), + mbRowHeaders(false), + mbDummyUpperLeft(false) + { + } + + ~Chart2Positioner() + { + } + + void setHeaders(bool bColHeaders, bool bRowHeaders) + { + mbColHeaders = bColHeaders; + mbRowHeaders = bRowHeaders; + } + + bool hasColHeaders() const { return mbColHeaders; } + bool hasRowHeaders() const { return mbRowHeaders; } + + Chart2PositionMap* getPositionMap() + { + createPositionMap(); + return mpPositionMap.get(); + } + +private: + Chart2Positioner(); // disabled + + void invalidateGlue(); + void glueState(); + void createPositionMap(); + +private: + shared_ptr< vector<ScSharedTokenRef> > mpRefTokens; + auto_ptr<Chart2PositionMap> mpPositionMap; + GlueType meGlue; + SCCOL mnStartCol; + SCROW mnStartRow; + ScDocument* mpDoc; + bool mbColHeaders:1; + bool mbRowHeaders:1; + bool mbDummyUpperLeft:1; +}; + +void Chart2Positioner::invalidateGlue() +{ + meGlue = GLUETYPE_NA; + mpPositionMap.reset(NULL); +} + +void Chart2Positioner::glueState() +{ + if (meGlue != GLUETYPE_NA) + return; + + mbDummyUpperLeft = false; + if (mpRefTokens->size() <= 1) + { + const ScSharedTokenRef& p = mpRefTokens->front(); + ScComplexRefData aData; + if (ScRefTokenHelper::getDoubleRefDataFromToken(aData, p)) + { + if (aData.Ref1.nTab == aData.Ref2.nTab) + meGlue = GLUETYPE_NONE; + else + meGlue = GLUETYPE_COLS; + mnStartCol = aData.Ref1.nCol; + mnStartRow = aData.Ref1.nRow; + } + else + { + invalidateGlue(); + mnStartCol = 0; + mnStartRow = 0; + } + return; + } + + ScComplexRefData aData; + ScRefTokenHelper::getDoubleRefDataFromToken(aData, mpRefTokens->front()); + mnStartCol = aData.Ref1.nCol; + mnStartRow = aData.Ref1.nRow; + + SCCOL nMaxCols = 0, nEndCol = 0; + SCROW nMaxRows = 0, nEndRow = 0; + for (vector<ScSharedTokenRef>::const_iterator itr = mpRefTokens->begin(), itrEnd = mpRefTokens->end() + ; itr != itrEnd; ++itr) + { + ScRefTokenHelper::getDoubleRefDataFromToken(aData, *itr); + SCCOLROW n1 = aData.Ref1.nCol; + SCCOLROW n2 = aData.Ref2.nCol; + if (n1 > MAXCOL) + n1 = MAXCOL; + if (n2 > MAXCOL) + n2 = MAXCOL; + SCCOLROW nTmp = n2 - n1 + 1; + if (n1 < mnStartCol) + mnStartCol = static_cast<SCCOL>(n1); + if (n2 > nEndCol) + nEndCol = static_cast<SCCOL>(n2); + if (nTmp > nMaxCols) + nMaxCols = static_cast<SCCOL>(nTmp); + + n1 = aData.Ref1.nRow; + n2 = aData.Ref2.nRow; + if (n1 > MAXROW) + n1 = MAXROW; + if (n2 > MAXROW) + n2 = MAXROW; + nTmp = n2 - n1 + 1; + + if (n1 < mnStartRow) + mnStartRow = static_cast<SCROW>(n1); + if (n2 > nEndRow) + nEndRow = static_cast<SCROW>(n2); + if (nTmp > nMaxRows) + nMaxRows = static_cast<SCROW>(nTmp); + } + + // total column size ? + SCCOL nC = nEndCol - mnStartCol + 1; + if (nC == 1) + { + meGlue = GLUETYPE_ROWS; + return; + } + // total row size ? + SCROW nR = nEndRow - mnStartRow + 1; + if (nR == 1) + { + meGlue = GLUETYPE_COLS; + return; + } + // #i103540# prevent invalid vector size + if ((nC <= 0) || (nR <= 0)) + { + invalidateGlue(); + mnStartCol = 0; + mnStartRow = 0; + return; + } + sal_uInt32 nCR = static_cast<sal_uInt32>(nC*nR); + + const sal_uInt8 nHole = 0; + const sal_uInt8 nOccu = 1; + const sal_uInt8 nFree = 2; + const sal_uInt8 nGlue = 3; + + vector<sal_uInt8> aCellStates(nCR); + for (vector<ScSharedTokenRef>::const_iterator itr = mpRefTokens->begin(), itrEnd = mpRefTokens->end(); + itr != itrEnd; ++itr) + { + ScRefTokenHelper::getDoubleRefDataFromToken(aData, *itr); + SCCOL nCol1 = static_cast<SCCOL>(aData.Ref1.nCol) - mnStartCol; + SCCOL nCol2 = static_cast<SCCOL>(aData.Ref2.nCol) - mnStartCol; + SCROW nRow1 = static_cast<SCROW>(aData.Ref1.nRow) - mnStartRow; + SCROW nRow2 = static_cast<SCROW>(aData.Ref2.nRow) - mnStartRow; + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + { + size_t i = nCol*nR + nRow; + aCellStates[i] = nOccu; + } + } + bool bGlue = true; + + size_t i = 0; + bool bGlueCols = false; + for (SCCOL nCol = 0; bGlue && nCol < nC; ++nCol) + { + for (SCROW nRow = 0; bGlue && nRow < nR; ++nRow) + { + i = nCol*nR + nRow; + if (aCellStates[i] == nOccu) + { + if (nRow > 0 && nRow > 0) + bGlue = false; + else + nRow = nR; + } + else + aCellStates[i] = nFree; + } + i = (nCol+1)*nR - 1; // index for the last cell in the column. + if (bGlue && (aCellStates[i] == nFree)) + { + aCellStates[i] = nGlue; + bGlueCols = true; + } + } + + bool bGlueRows = false; + for (SCROW nRow = 0; bGlue && nRow < nR; ++nRow) + { + i = nRow; + for (SCCOL nCol = 0; bGlue && nCol < nC; ++nCol, i += nR) + { + if (aCellStates[i] == nOccu) + { + if (nCol > 0 && nRow > 0) + bGlue = false; + else + nCol = nC; + } + else + aCellStates[i] = nFree; + } + i = (nC-1)*nR + nRow; // index for the row position in the last column. + if (bGlue && aCellStates[i] == nFree) + { + aCellStates[i] = nGlue; + bGlueRows = true; + } + } + + i = 1; + for (sal_uInt32 n = 1; bGlue && n < nCR; ++n, ++i) + if (aCellStates[i] == nHole) + bGlue = false; + + if (bGlue) + { + if (bGlueCols && bGlueRows) + meGlue = GLUETYPE_BOTH; + else if (bGlueRows) + meGlue = GLUETYPE_ROWS; + else + meGlue = GLUETYPE_COLS; + if (aCellStates.front() != nOccu) + mbDummyUpperLeft = true; + } + else + meGlue = GLUETYPE_NONE; +} + +void Chart2Positioner::createPositionMap() +{ + if (meGlue == GLUETYPE_NA && mpPositionMap.get()) + mpPositionMap.reset(NULL); + + if (mpPositionMap.get()) + return; + + glueState(); + + bool bNoGlue = (meGlue == GLUETYPE_NONE); + auto_ptr<Table> pCols(new Table); + auto_ptr<FormulaToken> pNewAddress; + auto_ptr<Table> pNewRowTable(new Table); + Table* pCol = NULL; + SCROW nNoGlueRow = 0; + for (vector<ScSharedTokenRef>::const_iterator itr = mpRefTokens->begin(), itrEnd = mpRefTokens->end(); + itr != itrEnd; ++itr) + { + const ScSharedTokenRef& pToken = *itr; + + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; + String aTabName = bExternal ? pToken->GetString() : String(); + + ScComplexRefData aData; + ScRefTokenHelper::getDoubleRefDataFromToken(aData, *itr); + const ScSingleRefData& s = aData.Ref1; + const ScSingleRefData& e = aData.Ref2; + SCCOL nCol1 = s.nCol, nCol2 = e.nCol; + SCROW nRow1 = s.nRow, nRow2 = e.nRow; + SCTAB nTab1 = s.nTab, nTab2 = e.nTab; + + for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab) + { + // What's this for ??? + sal_uInt32 nInsCol = (static_cast<sal_uInt32>(nTab) << 16) | + (bNoGlue ? 0 : static_cast<sal_uInt32>(nCol1)); + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol) + { + if (bNoGlue || meGlue == GLUETYPE_ROWS) + { + pCol = static_cast<Table*>(pCols->Get(nInsCol)); + if (!pCol) + { + pCol = pNewRowTable.get(); + pCols->Insert(nInsCol, pNewRowTable.release()); + pNewRowTable.reset(new Table); + } + } + else + { + if (pCols->Insert(nInsCol, pNewRowTable.get())) + { + pCol = pNewRowTable.release(); + pNewRowTable.reset(new Table); + } + else + pCol = static_cast<Table*>(pCols->Get(nInsCol)); + } + + sal_uInt32 nInsRow = static_cast<sal_uInt32>(bNoGlue ? nNoGlueRow : nRow1); + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow, ++nInsRow) + { + ScSingleRefData aCellData; + aCellData.InitFlags(); + aCellData.SetFlag3D(true); + aCellData.SetColRel(false); + aCellData.SetRowRel(false); + aCellData.SetTabRel(false); + aCellData.nCol = nCol; + aCellData.nRow = nRow; + aCellData.nTab = nTab; + + if (bExternal) + pNewAddress.reset(new ScExternalSingleRefToken(nFileId, aTabName, aCellData)); + else + pNewAddress.reset(new ScSingleRefToken(aCellData)); + + if (pCol->Insert(nInsRow, pNewAddress.get())) + pNewAddress.release(); // To prevent the instance from being destroyed. + } + } + } + nNoGlueRow += nRow2 - nRow1 + 1; + } + pNewAddress.reset(NULL); + pNewRowTable.reset(NULL); + + bool bFillRowHeader = mbRowHeaders; + bool bFillColumnHeader = mbColHeaders; + + SCSIZE nAllColCount = static_cast<SCSIZE>(pCols->Count()); + SCSIZE nAllRowCount = 0; + pCol = static_cast<Table*>(pCols->First()); + if (pCol) + { + if (mbDummyUpperLeft) + pCol->Insert(0, NULL); // Dummy fuer Beschriftung + nAllRowCount = static_cast<SCSIZE>(pCol->Count()); + } + + if( nAllColCount!=0 && nAllRowCount!=0 ) + { + if (bNoGlue) + { + Table* pFirstCol = static_cast<Table*>(pCols->First()); + sal_uInt32 nCount = pFirstCol->Count(); + pFirstCol->First(); + for (sal_uInt32 n = 0; n < nCount; ++n, pFirstCol->Next()) + { + sal_uInt32 nKey = pFirstCol->GetCurKey(); + pCols->First(); + for (pCol = static_cast<Table*>(pCols->Next()); pCol; pCol = static_cast<Table*>(pCols->Next())) + pCol->Insert(nKey, NULL); + } + } + } + mpPositionMap.reset( + new Chart2PositionMap( + static_cast<SCCOL>(nAllColCount), static_cast<SCROW>(nAllRowCount), + bFillRowHeader, bFillColumnHeader, *pCols, mpDoc)); + + // Destroy all column instances. + for (pCol = static_cast<Table*>(pCols->First()); pCol; pCol = static_cast<Table*>(pCols->Next())) + delete pCol; +} + +// ============================================================================ + +/** + * Function object to create a range string from a token list. + */ +class Tokens2RangeString : public unary_function<ScSharedTokenRef, void> +{ +public: + Tokens2RangeString(ScDocument* pDoc, FormulaGrammar::Grammar eGram, sal_Unicode cRangeSep) : + mpRangeStr(new OUStringBuffer), + mpDoc(pDoc), + meGrammar(eGram), + mcRangeSep(cRangeSep), + mbFirst(true) + { + } + + Tokens2RangeString(const Tokens2RangeString& r) : + mpRangeStr(r.mpRangeStr), + mpDoc(r.mpDoc), + meGrammar(r.meGrammar), + mcRangeSep(r.mcRangeSep), + mbFirst(r.mbFirst) + { + } + + void operator() (const ScSharedTokenRef& rToken) + { + ScCompiler aCompiler(mpDoc, ScAddress(0,0,0)); + aCompiler.SetGrammar(meGrammar); + String aStr; + aCompiler.CreateStringFromToken(aStr, rToken.get()); + if (mbFirst) + mbFirst = false; + else + mpRangeStr->append(mcRangeSep); + mpRangeStr->append(aStr); + } + + void getString(OUString& rStr) + { + rStr = mpRangeStr->makeStringAndClear(); + } + +private: + Tokens2RangeString(); // disabled + +private: + shared_ptr<OUStringBuffer> mpRangeStr; + ScDocument* mpDoc; + FormulaGrammar::Grammar meGrammar; + sal_Unicode mcRangeSep; + bool mbFirst; +}; + +/** + * Function object to convert a list of tokens into a string form suitable + * for ODF export. In ODF, a range is expressed as + * + * (start cell address):(end cell address) + * + * and each address doesn't include any '$' symbols. + */ +class Tokens2RangeStringXML : public unary_function<ScSharedTokenRef, void> +{ +public: + Tokens2RangeStringXML(ScDocument* pDoc) : + mpRangeStr(new OUStringBuffer), + mpDoc(pDoc), + mcRangeSep(' '), + mcAddrSep(':'), + mbFirst(true) + { + } + + Tokens2RangeStringXML(const Tokens2RangeStringXML& r) : + mpRangeStr(r.mpRangeStr), + mpDoc(r.mpDoc), + mcRangeSep(r.mcRangeSep), + mcAddrSep(r.mcAddrSep), + mbFirst(r.mbFirst) + { + } + + void operator() (const ScSharedTokenRef& rToken) + { + if (mbFirst) + mbFirst = false; + else + mpRangeStr->append(mcRangeSep); + + ScSharedTokenRef aStart, aEnd; + splitRangeToken(rToken, aStart, aEnd); + ScCompiler aCompiler(mpDoc, ScAddress(0,0,0)); + aCompiler.SetGrammar(FormulaGrammar::GRAM_ENGLISH); + { + String aStr; + aCompiler.CreateStringFromToken(aStr, aStart.get()); + mpRangeStr->append(aStr); + } + mpRangeStr->append(mcAddrSep); + { + String aStr; + aCompiler.CreateStringFromToken(aStr, aEnd.get()); + mpRangeStr->append(aStr); + } + } + + void getString(OUString& rStr) + { + rStr = mpRangeStr->makeStringAndClear(); + } + +private: + Tokens2RangeStringXML(); // disabled + + void splitRangeToken(const ScSharedTokenRef& pToken, ScSharedTokenRef& rStart, ScSharedTokenRef& rEnd) const + { + ScComplexRefData aData; + ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken); + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0; + String aTabName = bExternal ? pToken->GetString() : String(); + + // In saving to XML, we don't prepend address with '$'. + setRelative(aData.Ref1); + setRelative(aData.Ref2); + + // In XML, the end range must explicitly specify sheet name. + aData.Ref2.SetFlag3D(true); + + if (bExternal) + rStart.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref1)); + else + rStart.reset(new ScSingleRefToken(aData.Ref1)); + + if (bExternal) + rEnd.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref2)); + else + rEnd.reset(new ScSingleRefToken(aData.Ref2)); + } + + void setRelative(ScSingleRefData& rData) const + { + rData.SetColRel(true); + rData.SetRowRel(true); + rData.SetTabRel(true); + } + +private: + shared_ptr<OUStringBuffer> mpRangeStr; + ScDocument* mpDoc; + sal_Unicode mcRangeSep; + sal_Unicode mcAddrSep; + bool mbFirst; +}; + +void lcl_convertTokensToString(OUString& rStr, const vector<ScSharedTokenRef>& rTokens, ScDocument* pDoc) +{ + const sal_Unicode cRangeSep = ScCompiler::GetNativeSymbol(ocSep).GetChar(0); + FormulaGrammar::Grammar eGrammar = pDoc->GetGrammar(); + Tokens2RangeString func(pDoc, eGrammar, cRangeSep); + func = for_each(rTokens.begin(), rTokens.end(), func); + func.getString(rStr); +} + +} // anonymous namespace + +// DataProvider ============================================================== + +ScChart2DataProvider::ScChart2DataProvider( ScDocument* pDoc ) + : m_pDocument( pDoc) + , m_aPropSet(lcl_GetDataProviderPropertyMap()) + , m_bIncludeHiddenCells( sal_True) +{ + if ( m_pDocument ) + m_pDocument->AddUnoObject( *this); +} + +ScChart2DataProvider::~ScChart2DataProvider() +{ + if ( m_pDocument ) + m_pDocument->RemoveUnoObject( *this); +} + + +void ScChart2DataProvider::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.ISA( SfxSimpleHint ) && + ((const SfxSimpleHint&)rHint).GetId() == SFX_HINT_DYING ) + { + m_pDocument = NULL; + } +} + +::sal_Bool SAL_CALL ScChart2DataProvider::createDataSourcePossible( const uno::Sequence< beans::PropertyValue >& aArguments ) + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + if( ! m_pDocument ) + return false; + + rtl::OUString aRangeRepresentation; + for(sal_Int32 i = 0; i < aArguments.getLength(); ++i) + { + rtl::OUString sName(aArguments[i].Name); + if (aArguments[i].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("CellRangeRepresentation"))) + { + aArguments[i].Value >>= aRangeRepresentation; + } + } + + vector<ScSharedTokenRef> aTokens; + ScRefTokenHelper::compileRangeRepresentation(aTokens, aRangeRepresentation, m_pDocument, m_pDocument->GetGrammar()); + return !aTokens.empty(); +} + +namespace +{ + +ScChart2LabeledDataSequence* lcl_createScChart2DataSequenceFromTokens( auto_ptr< vector<ScSharedTokenRef> > pValueTokens, auto_ptr< vector<ScSharedTokenRef> > pLabelTokens, + ScDocument* pDoc, const uno::Reference < chart2::data::XDataProvider >& xDP, bool bIncludeHiddenCells ) +{ + ScChart2LabeledDataSequence* pRet = 0; + bool bHasValues = pValueTokens.get() && !pValueTokens->empty(); + bool bHasLabel = pLabelTokens.get() && !pLabelTokens->empty(); + if( bHasValues || bHasLabel ) + { + pRet = new ScChart2LabeledDataSequence(pDoc); + if(bHasValues) + { + uno::Reference < chart2::data::XDataSequence > xSeq(new ScChart2DataSequence(pDoc, xDP, pValueTokens.release(), bIncludeHiddenCells)); + pRet->setValues(xSeq); + } + if(bHasLabel) + { + uno::Reference < chart2::data::XDataSequence > xLabelSeq(new ScChart2DataSequence(pDoc, xDP, pLabelTokens.release(), bIncludeHiddenCells)); + pRet->setLabel(xLabelSeq); + } + } + return pRet; +} + +//---------------------------------------------------- +/** + * Check the current list of reference tokens, and add the upper left + * corner of the minimum range that encloses all ranges if certain + * conditions are met. + * + * @param rRefTokens list of reference tokens + * + * @return true if the corner was added, false otherwise. + */ +bool lcl_addUpperLeftCornerIfMissing(vector<ScSharedTokenRef>& rRefTokens, + SCROW nCornerRowCount=1, SCCOL nCornerColumnCount=1) +{ + using ::std::max; + using ::std::min; + + if (rRefTokens.empty()) + return false; + + SCCOL nMinCol = MAXCOLCOUNT; + SCROW nMinRow = MAXROWCOUNT; + SCCOL nMaxCol = 0; + SCROW nMaxRow = 0; + SCTAB nTab = 0; + + USHORT nFileId = 0; + String aExtTabName; + bool bExternal = false; + + vector<ScSharedTokenRef>::const_iterator itr = rRefTokens.begin(), itrEnd = rRefTokens.end(); + + // Get the first ref token. + ScSharedTokenRef pToken = *itr; + switch (pToken->GetType()) + { + case svSingleRef: + { + const ScSingleRefData& rData = pToken->GetSingleRef(); + nMinCol = rData.nCol; + nMinRow = rData.nRow; + nMaxCol = rData.nCol; + nMaxRow = rData.nRow; + nTab = rData.nTab; + } + break; + case svDoubleRef: + { + const ScComplexRefData& rData = pToken->GetDoubleRef(); + nMinCol = min(rData.Ref1.nCol, rData.Ref2.nCol); + nMinRow = min(rData.Ref1.nRow, rData.Ref2.nRow); + nMaxCol = max(rData.Ref1.nCol, rData.Ref2.nCol); + nMaxRow = max(rData.Ref1.nRow, rData.Ref2.nRow); + nTab = rData.Ref1.nTab; + } + break; + case svExternalSingleRef: + { + const ScSingleRefData& rData = pToken->GetSingleRef(); + nMinCol = rData.nCol; + nMinRow = rData.nRow; + nMaxCol = rData.nCol; + nMaxRow = rData.nRow; + nTab = rData.nTab; + nFileId = pToken->GetIndex(); + aExtTabName = pToken->GetString(); + bExternal = true; + } + break; + case svExternalDoubleRef: + { + const ScComplexRefData& rData = pToken->GetDoubleRef(); + nMinCol = min(rData.Ref1.nCol, rData.Ref2.nCol); + nMinRow = min(rData.Ref1.nRow, rData.Ref2.nRow); + nMaxCol = max(rData.Ref1.nCol, rData.Ref2.nCol); + nMaxRow = max(rData.Ref1.nRow, rData.Ref2.nRow); + nTab = rData.Ref1.nTab; + nFileId = pToken->GetIndex(); + aExtTabName = pToken->GetString(); + bExternal = true; + } + break; + default: + ; + } + + // Determine the minimum range enclosing all data ranges. Also make sure + // that they are all on the same table. + + for (++itr; itr != itrEnd; ++itr) + { + pToken = *itr; + switch (pToken->GetType()) + { + case svSingleRef: + { + const ScSingleRefData& rData = pToken->GetSingleRef(); + + nMinCol = min(nMinCol, rData.nCol); + nMinRow = min(nMinRow, rData.nRow); + nMaxCol = max(nMaxCol, rData.nCol); + nMaxRow = max(nMaxRow, rData.nRow); + if (nTab != rData.nTab || bExternal) + return false; + } + break; + case svDoubleRef: + { + const ScComplexRefData& rData = pToken->GetDoubleRef(); + + nMinCol = min(nMinCol, rData.Ref1.nCol); + nMinCol = min(nMinCol, rData.Ref2.nCol); + nMinRow = min(nMinRow, rData.Ref1.nRow); + nMinRow = min(nMinRow, rData.Ref2.nRow); + + nMaxCol = max(nMaxCol, rData.Ref1.nCol); + nMaxCol = max(nMaxCol, rData.Ref2.nCol); + nMaxRow = max(nMaxRow, rData.Ref1.nRow); + nMaxRow = max(nMaxRow, rData.Ref2.nRow); + + if (nTab != rData.Ref1.nTab || bExternal) + return false; + } + break; + case svExternalSingleRef: + { + if (!bExternal) + return false; + + if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString()) + return false; + + const ScSingleRefData& rData = pToken->GetSingleRef(); + + nMinCol = min(nMinCol, rData.nCol); + nMinRow = min(nMinRow, rData.nRow); + nMaxCol = max(nMaxCol, rData.nCol); + nMaxRow = max(nMaxRow, rData.nRow); + } + break; + case svExternalDoubleRef: + { + if (!bExternal) + return false; + + if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString()) + return false; + + const ScComplexRefData& rData = pToken->GetDoubleRef(); + + nMinCol = min(nMinCol, rData.Ref1.nCol); + nMinCol = min(nMinCol, rData.Ref2.nCol); + nMinRow = min(nMinRow, rData.Ref1.nRow); + nMinRow = min(nMinRow, rData.Ref2.nRow); + + nMaxCol = max(nMaxCol, rData.Ref1.nCol); + nMaxCol = max(nMaxCol, rData.Ref2.nCol); + nMaxRow = max(nMaxRow, rData.Ref1.nRow); + nMaxRow = max(nMaxRow, rData.Ref2.nRow); + } + break; + default: + ; + } + } + + if (nMinRow >= nMaxRow || nMinCol >= nMaxCol || + nMinRow >= MAXROWCOUNT || nMinCol >= MAXCOLCOUNT || + nMaxRow >= MAXROWCOUNT || nMaxCol >= MAXCOLCOUNT) + { + // Invalid range. Bail out. + return false; + } + + // Check if the following conditions are met: + // + // 1) The upper-left corner cell is not included. + // 2) The three adjacent cells of that corner cell are included. + + bool bRight = false, bBottom = false, bDiagonal = false; + for (itr = rRefTokens.begin(); itr != itrEnd; ++itr) + { + pToken = *itr; + switch (pToken->GetType()) + { + case svSingleRef: + case svExternalSingleRef: + { + const ScSingleRefData& rData = pToken->GetSingleRef(); + if (rData.nCol == nMinCol && rData.nRow == nMinRow) + // The corner cell is contained. + return false; + + if (rData.nCol == nMinCol+nCornerColumnCount && rData.nRow == nMinRow) + bRight = true; + + if (rData.nCol == nMinCol && rData.nRow == nMinRow+nCornerRowCount) + bBottom = true; + + if (rData.nCol == nMinCol+nCornerColumnCount && rData.nRow == nMinRow+nCornerRowCount) + bDiagonal = true; + } + break; + case svDoubleRef: + case svExternalDoubleRef: + { + const ScComplexRefData& rData = pToken->GetDoubleRef(); + const ScSingleRefData& r1 = rData.Ref1; + const ScSingleRefData& r2 = rData.Ref2; + if (r1.nCol <= nMinCol && nMinCol <= r2.nCol && + r1.nRow <= nMinRow && nMinRow <= r2.nRow) + // The corner cell is contained. + return false; + + if (r1.nCol <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.nCol && + r1.nRow <= nMinRow && nMinRow <= r2.nRow) + bRight = true; + + if (r1.nCol <= nMinCol && nMinCol <= r2.nCol && + r1.nRow <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.nRow) + bBottom = true; + + if (r1.nCol <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.nCol && + r1.nRow <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.nRow) + bDiagonal = true; + } + break; + default: + ; + } + } + + if (!bRight || !bBottom || !bDiagonal) + // Not all the adjacent cells are included. Bail out. + return false; + +#if 0 // Do we really need to do this ??? + if (rRefTokens.size() == 2) + { + // Make a simple rectangular range if possible. + ScRange aRightPart(ScAddress(nMinCol+1, nMinRow, nTab), ScAddress(nMaxCol, nMaxRow, nTab)); + ScRange aBottomPart(ScAddress(nMinCol, nMinRow+1, nTab), ScAddress(nMaxCol, nMaxRow, nTab)); + vector<ScRange> aRanges; + aRanges.reserve(2); + aRanges.push_back(aRightPart); + aRanges.push_back(aBottomPart); + if (lcl_isRangeContained(rRefTokens, aRanges)) + { + // Consolidate them into a single rectangle. + ScComplexRefData aData; + aData.InitFlags(); + aData.Ref1.SetFlag3D(true); + aData.Ref1.SetColRel(false); + aData.Ref1.SetRowRel(false); + aData.Ref1.SetTabRel(false); + aData.Ref2.SetColRel(false); + aData.Ref2.SetRowRel(false); + aData.Ref2.SetTabRel(false); + aData.Ref1.nCol = nMinCol; + aData.Ref1.nRow = nMinRow; + aData.Ref1.nTab = nTab; + aData.Ref2.nCol = nMaxCol; + aData.Ref2.nRow = nMaxRow; + aData.Ref2.nTab = nTab; + vector<ScSharedTokenRef> aNewTokens; + aNewTokens.reserve(1); + if (bExternal) + { + ScSharedTokenRef p( + new ScExternalDoubleRefToken(nFileId, aExtTabName, aData)); + aNewTokens.push_back(p); + } + else + { + ScSharedTokenRef p(new ScDoubleRefToken(aData)); + aNewTokens.push_back(p); + } + rRefTokens.swap(aNewTokens); + return true; + } + } +#endif + + ScSingleRefData aData; + aData.InitFlags(); + aData.SetFlag3D(true); + aData.SetColRel(false); + aData.SetRowRel(false); + aData.SetTabRel(false); + aData.nCol = nMinCol; + aData.nRow = nMinRow; + aData.nTab = nTab; + + if( nCornerRowCount==1 && nCornerColumnCount==1 ) + { + if (bExternal) + { + ScSharedTokenRef pCorner( + new ScExternalSingleRefToken(nFileId, aExtTabName, aData)); + ScRefTokenHelper::join(rRefTokens, pCorner); + } + else + { + ScSharedTokenRef pCorner(new ScSingleRefToken(aData)); + ScRefTokenHelper::join(rRefTokens, pCorner); + } + } + else + { + ScSingleRefData aDataEnd(aData); + aDataEnd.nCol += (nCornerColumnCount-1); + aDataEnd.nRow += (nCornerRowCount-1); + ScComplexRefData r; + r.Ref1=aData; + r.Ref2=aDataEnd; + if (bExternal) + { + ScSharedTokenRef pCorner( + new ScExternalDoubleRefToken(nFileId, aExtTabName, r)); + ScRefTokenHelper::join(rRefTokens, pCorner); + } + else + { + ScSharedTokenRef pCorner(new ScDoubleRefToken(r)); + ScRefTokenHelper::join(rRefTokens, pCorner); + } + } + + return true; +} + +} + +uno::Reference< chart2::data::XDataSource> SAL_CALL +ScChart2DataProvider::createDataSource( + const uno::Sequence< beans::PropertyValue >& aArguments ) + throw( lang::IllegalArgumentException, uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( ! m_pDocument ) + throw uno::RuntimeException(); + + uno::Reference< chart2::data::XDataSource> xResult; + bool bLabel = true; + bool bCategories = false; + bool bOrientCol = true; + ::rtl::OUString aRangeRepresentation; + uno::Sequence< sal_Int32 > aSequenceMapping; + for(sal_Int32 i = 0; i < aArguments.getLength(); ++i) + { + rtl::OUString sName(aArguments[i].Name); + if (aArguments[i].Name == rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("DataRowSource"))) + { + chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS; + if( ! (aArguments[i].Value >>= eSource)) + { + sal_Int32 nSource(0); + if( aArguments[i].Value >>= nSource ) + eSource = (static_cast< chart::ChartDataRowSource >( nSource )); + } + bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS); + } + else if (aArguments[i].Name == rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("FirstCellAsLabel"))) + { + bLabel = ::cppu::any2bool(aArguments[i].Value); + } + else if (aArguments[i].Name == rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("HasCategories"))) + { + bCategories = ::cppu::any2bool(aArguments[i].Value); + } + else if (aArguments[i].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("CellRangeRepresentation"))) + { + aArguments[i].Value >>= aRangeRepresentation; + } + else if (aArguments[i].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("SequenceMapping"))) + { + aArguments[i].Value >>= aSequenceMapping; + } + } + + vector<ScSharedTokenRef> aRefTokens; + ScRefTokenHelper::compileRangeRepresentation(aRefTokens, aRangeRepresentation, m_pDocument, m_pDocument->GetGrammar()); + if (aRefTokens.empty()) + // Invalid range representation. Bail out. + throw lang::IllegalArgumentException(); + + if (bLabel) + lcl_addUpperLeftCornerIfMissing(aRefTokens); //#i90669# + + bool bColHeaders = (bOrientCol ? bLabel : bCategories ); + bool bRowHeaders = (bOrientCol ? bCategories : bLabel ); + + Chart2Positioner aChPositioner(m_pDocument, aRefTokens); + aChPositioner.setHeaders(bColHeaders, bRowHeaders); + + const Chart2PositionMap* pChartMap = aChPositioner.getPositionMap(); + if (!pChartMap) + // No chart position map instance. Bail out. + return xResult; + + ScChart2DataSource* pDS = NULL; + std::list < ScChart2LabeledDataSequence* > aSeqs; + + // Fill Categories + if( bCategories ) + { + auto_ptr< vector<ScSharedTokenRef> > pValueTokens(NULL); + if (bOrientCol) + pValueTokens.reset(pChartMap->getAllRowHeaderRanges()); + else + pValueTokens.reset(pChartMap->getAllColHeaderRanges()); + + auto_ptr< vector<ScSharedTokenRef> > pLabelTokens(NULL); + pLabelTokens.reset(pChartMap->getLeftUpperCornerRanges()); + + ScChart2LabeledDataSequence* pCategories = lcl_createScChart2DataSequenceFromTokens( pValueTokens, pLabelTokens, m_pDocument, this, m_bIncludeHiddenCells );//ownership of pointers is transfered! + if( pCategories ) + aSeqs.push_back(pCategories); + } + + // Fill Serieses (values and label) + sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + auto_ptr< vector<ScSharedTokenRef> > pValueTokens(NULL); + auto_ptr< vector<ScSharedTokenRef> > pLabelTokens(NULL); + if (bOrientCol) + { + pValueTokens.reset(pChartMap->getDataColRanges(static_cast<SCCOL>(i))); + pLabelTokens.reset(pChartMap->getColHeaderRanges(static_cast<SCCOL>(i))); + } + else + { + pValueTokens.reset(pChartMap->getDataRowRanges(static_cast<SCROW>(i))); + pLabelTokens.reset(pChartMap->getRowHeaderRanges(static_cast<SCROW>(i))); + } + ScChart2LabeledDataSequence* pChartSeries = lcl_createScChart2DataSequenceFromTokens( pValueTokens, pLabelTokens, m_pDocument, this, m_bIncludeHiddenCells ); //ownership of pointers is transfered! + if( pChartSeries ) + aSeqs.push_back(pChartSeries); + } + + pDS = new ScChart2DataSource(m_pDocument); + std::list < ScChart2LabeledDataSequence* >::iterator aItr(aSeqs.begin()); + std::list < ScChart2LabeledDataSequence* >::iterator aEndItr(aSeqs.end()); + + //reorder labeled sequences according to aSequenceMapping + std::vector< ScChart2LabeledDataSequence* > aSeqVector; + while(aItr != aEndItr) + { + aSeqVector.push_back(*aItr); + ++aItr; + } + + std::map< sal_Int32, ScChart2LabeledDataSequence* > aSequenceMap; + for( sal_Int32 nNewIndex = 0; nNewIndex < aSequenceMapping.getLength(); nNewIndex++ ) + { + // note: assuming that the values in the sequence mapping are always non-negative + std::vector< ScChart2LabeledDataSequence* >::size_type nOldIndex( static_cast< sal_uInt32 >( aSequenceMapping[nNewIndex] )); + if( nOldIndex < aSeqVector.size() ) + { + pDS->AddLabeledSequence( aSeqVector[nOldIndex] ); + aSeqVector[nOldIndex] = 0; + } + + } + + std::vector< ScChart2LabeledDataSequence* >::iterator aVectorItr(aSeqVector.begin()); + std::vector< ScChart2LabeledDataSequence* >::iterator aVectorEndItr(aSeqVector.end()); + while(aVectorItr != aVectorEndItr) + { + if(*aVectorItr) + pDS->AddLabeledSequence(*aVectorItr); + ++aVectorItr; + } + + xResult.set( pDS ); + return xResult; +} + +namespace +{ + +/** + * Function object to create a list of table numbers from a token list. + */ +class InsertTabNumber : public unary_function<ScSharedTokenRef, void> +{ +public: + InsertTabNumber() : + mpTabNumList(new list<SCTAB>()) + { + } + + InsertTabNumber(const InsertTabNumber& r) : + mpTabNumList(r.mpTabNumList) + { + } + + void operator() (const ScSharedTokenRef& pToken) const + { + if (!ScRefTokenHelper::isRef(pToken)) + return; + + const ScSingleRefData& r = pToken->GetSingleRef(); + mpTabNumList->push_back(r.nTab); + } + + void getList(list<SCTAB>& rList) + { + mpTabNumList->swap(rList); + } +private: + shared_ptr< list<SCTAB> > mpTabNumList; +}; + +class RangeAnalyzer +{ +public: + RangeAnalyzer(); + void initRangeAnalyzer( const vector<ScSharedTokenRef>& rTokens ); + void analyzeRange( sal_Int32& rnDataInRows, sal_Int32& rnDataInCols, + bool& rbRowSourceAmbiguous ) const; + bool inSameSingleRow( RangeAnalyzer& rOther ); + bool inSameSingleColumn( RangeAnalyzer& rOther ); + SCROW getRowCount() { return mnRowCount; } + SCCOL getColumnCount() { return mnColumnCount; } + +private: + bool mbEmpty; + bool mbAmbiguous; + SCROW mnRowCount; + SCCOL mnColumnCount; + + SCCOL mnStartColumn; + SCROW mnStartRow; +}; + +RangeAnalyzer::RangeAnalyzer() + : mbEmpty(true) + , mbAmbiguous(false) + , mnRowCount(0) + , mnColumnCount(0) + , mnStartColumn(-1) + , mnStartRow(-1) +{ +} + +void RangeAnalyzer::initRangeAnalyzer( const vector<ScSharedTokenRef>& rTokens ) +{ + mnRowCount=0; + mnColumnCount=0; + mnStartColumn = -1; + mnStartRow = -1; + mbAmbiguous=false; + if( rTokens.empty() ) + { + mbEmpty=true; + return; + } + mbEmpty=false; + + vector<ScSharedTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end(); + for (; itr != itrEnd ; ++itr) + { + ScSharedTokenRef aRefToken = *itr; + StackVar eVar = aRefToken->GetType(); + if (eVar == svDoubleRef || eVar == svExternalDoubleRef) + { + const ScComplexRefData& r = aRefToken->GetDoubleRef(); + if (r.Ref1.nTab == r.Ref2.nTab) + { + mnColumnCount = std::max<SCCOL>( mnColumnCount, static_cast<SCCOL>(abs(r.Ref2.nCol - r.Ref1.nCol)+1) ); + mnRowCount = std::max<SCROW>( mnRowCount, static_cast<SCROW>(abs(r.Ref2.nRow - r.Ref1.nRow)+1) ); + if( mnStartColumn == -1 ) + { + mnStartColumn = r.Ref1.nCol; + mnStartRow = r.Ref1.nRow; + } + else + { + if( mnStartColumn != r.Ref1.nCol && mnStartRow != r.Ref1.nRow ) + mbAmbiguous=true; + } + } + else + mbAmbiguous=true; + } + else if (eVar == svSingleRef || eVar == svExternalSingleRef) + { + const ScSingleRefData& r = aRefToken->GetSingleRef(); + mnColumnCount = std::max<SCCOL>( mnColumnCount, 1); + mnRowCount = std::max<SCROW>( mnRowCount, 1); + if( mnStartColumn == -1 ) + { + mnStartColumn = r.nCol; + mnStartRow = r.nRow; + } + else + { + if( mnStartColumn != r.nCol && mnStartRow != r.nRow ) + mbAmbiguous=true; + } + } + else + mbAmbiguous=true; + } +} + +void RangeAnalyzer::analyzeRange( sal_Int32& rnDataInRows, + sal_Int32& rnDataInCols, + bool& rbRowSourceAmbiguous ) const +{ + if(!mbEmpty && !mbAmbiguous) + { + if( mnRowCount==1 && mnColumnCount>1 ) + ++rnDataInRows; + else if( mnColumnCount==1 && mnRowCount>1 ) + ++rnDataInCols; + else if( mnRowCount>1 && mnColumnCount>1 ) + rbRowSourceAmbiguous = true; + } + else if( !mbEmpty ) + rbRowSourceAmbiguous = true; +} + +bool RangeAnalyzer::inSameSingleRow( RangeAnalyzer& rOther ) +{ + if( mnStartRow==rOther.mnStartRow && + mnRowCount==1 && rOther.mnRowCount==1 ) + return true; + return false; +} + +bool RangeAnalyzer::inSameSingleColumn( RangeAnalyzer& rOther ) +{ + if( mnStartColumn==rOther.mnStartColumn && + mnColumnCount==1 && rOther.mnColumnCount==1 ) + return true; + return false; +} + +} //end anonymous namespace + +uno::Sequence< beans::PropertyValue > SAL_CALL ScChart2DataProvider::detectArguments( + const uno::Reference< chart2::data::XDataSource >& xDataSource ) + throw (uno::RuntimeException) +{ + ::std::vector< beans::PropertyValue > aResult; + bool bRowSourceDetected = false; + bool bFirstCellAsLabel = false; + bool bHasCategories = false; + ::rtl::OUString sRangeRep; + + bool bHasCategoriesLabels = false; + vector<ScSharedTokenRef> aAllCategoriesValuesTokens; + vector<ScSharedTokenRef> aAllSeriesLabelTokens; + + chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS; + + vector<ScSharedTokenRef> aAllTokens; + + // parse given data source and collect infos + { + ScUnoGuard aGuard; + DBG_ASSERT( m_pDocument, "No Document -> no detectArguments" ); + if(!m_pDocument ||!xDataSource.is()) + return lcl_VectorToSequence( aResult ); + + sal_Int32 nDataInRows = 0; + sal_Int32 nDataInCols = 0; + bool bRowSourceAmbiguous = false; + + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences()); + const sal_Int32 nCount( aSequences.getLength()); + RangeAnalyzer aPrevLabel,aPrevValues; + for( sal_Int32 nIdx=0; nIdx<nCount; ++nIdx ) + { + Reference< chart2::data::XLabeledDataSequence > xLS(aSequences[nIdx]); + if( xLS.is() ) + { + bool bThisIsCategories = false; + if(!bHasCategories) + { + Reference< beans::XPropertySet > xSeqProp( xLS->getValues(), uno::UNO_QUERY ); + ::rtl::OUString aRole; + if( xSeqProp.is() && (xSeqProp->getPropertyValue(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Role"))) >>= aRole) && + aRole.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("categories")) ) + bThisIsCategories = bHasCategories = true; + } + + RangeAnalyzer aLabel,aValues; + // label + Reference< chart2::data::XDataSequence > xLabel( xLS->getLabel()); + if( xLabel.is()) + { + bFirstCellAsLabel = true; + vector<ScSharedTokenRef> aTokens; + ScRefTokenHelper::compileRangeRepresentation( aTokens, xLabel->getSourceRangeRepresentation(), m_pDocument, m_pDocument->GetGrammar() ); + aLabel.initRangeAnalyzer(aTokens); + vector<ScSharedTokenRef>::const_iterator itr = aTokens.begin(), itrEnd = aTokens.end(); + for (; itr != itrEnd; ++itr) + { + ScRefTokenHelper::join(aAllTokens, *itr); + if(!bThisIsCategories) + ScRefTokenHelper::join(aAllSeriesLabelTokens, *itr); + } + if(bThisIsCategories) + bHasCategoriesLabels=true; + } + // values + Reference< chart2::data::XDataSequence > xValues( xLS->getValues()); + if( xValues.is()) + { + vector<ScSharedTokenRef> aTokens; + ScRefTokenHelper::compileRangeRepresentation( aTokens, xValues->getSourceRangeRepresentation(), m_pDocument, m_pDocument->GetGrammar() ); + aValues.initRangeAnalyzer(aTokens); + vector<ScSharedTokenRef>::const_iterator itr = aTokens.begin(), itrEnd = aTokens.end(); + for (; itr != itrEnd; ++itr) + { + ScRefTokenHelper::join(aAllTokens, *itr); + if(bThisIsCategories) + ScRefTokenHelper::join(aAllCategoriesValuesTokens, *itr); + } + } + //detect row source + if(!bThisIsCategories || nCount==1) //categories might span multiple rows *and* columns, so they should be used for detection only if nothing else is available + { + if (!bRowSourceAmbiguous) + { + aValues.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous); + aLabel.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous); + if (nDataInRows > 1 && nDataInCols > 1) + bRowSourceAmbiguous = true; + else if( !bRowSourceAmbiguous && !nDataInRows && !nDataInCols ) + { + if( aValues.inSameSingleColumn( aLabel ) ) + nDataInCols++; + else if( aValues.inSameSingleRow( aLabel ) ) + nDataInRows++; + else + { + //#i86188# also detect a single column split into rows correctly + if( aValues.inSameSingleColumn( aPrevValues ) ) + nDataInRows++; + else if( aValues.inSameSingleRow( aPrevValues ) ) + nDataInCols++; + else if( aLabel.inSameSingleColumn( aPrevLabel ) ) + nDataInRows++; + else if( aLabel.inSameSingleRow( aPrevLabel ) ) + nDataInCols++; + } + } + } + } + aPrevValues=aValues; + aPrevLabel=aLabel; + } + } + + if (!bRowSourceAmbiguous) + { + bRowSourceDetected = true; + eRowSource = ( nDataInRows > 0 + ? chart::ChartDataRowSource_ROWS + : chart::ChartDataRowSource_COLUMNS ); + } + else + { + // set DataRowSource to the better of the two ambiguities + eRowSource = ( nDataInRows > nDataInCols + ? chart::ChartDataRowSource_ROWS + : chart::ChartDataRowSource_COLUMNS ); + } + + } + + // TableNumberList + { + list<SCTAB> aTableNumList; + InsertTabNumber func; + func = for_each(aAllTokens.begin(), aAllTokens.end(), func); + func.getList(aTableNumList); + aResult.push_back( + beans::PropertyValue( ::rtl::OUString::createFromAscii("TableNumberList"), -1, + uno::makeAny( lcl_createTableNumberList( aTableNumList ) ), + beans::PropertyState_DIRECT_VALUE )); + } + + // DataRowSource (calculated before) + if( bRowSourceDetected ) + { + aResult.push_back( + beans::PropertyValue( ::rtl::OUString::createFromAscii("DataRowSource"), -1, + uno::makeAny( eRowSource ), beans::PropertyState_DIRECT_VALUE )); + } + + // HasCategories + if( bRowSourceDetected ) + { + aResult.push_back( + beans::PropertyValue( ::rtl::OUString::createFromAscii("HasCategories"), -1, + uno::makeAny( bHasCategories ), beans::PropertyState_DIRECT_VALUE )); + } + + // FirstCellAsLabel + if( bRowSourceDetected ) + { + aResult.push_back( + beans::PropertyValue( ::rtl::OUString::createFromAscii("FirstCellAsLabel"), -1, + uno::makeAny( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE )); + } + + // Add the left upper corner to the range if it is missing. + if (bRowSourceDetected && bFirstCellAsLabel && bHasCategories && !bHasCategoriesLabels ) + { + RangeAnalyzer aTop,aLeft; + if( eRowSource==chart::ChartDataRowSource_COLUMNS ) + { + aTop.initRangeAnalyzer(aAllSeriesLabelTokens); + aLeft.initRangeAnalyzer(aAllCategoriesValuesTokens); + } + else + { + aTop.initRangeAnalyzer(aAllCategoriesValuesTokens); + aLeft.initRangeAnalyzer(aAllSeriesLabelTokens); + } + lcl_addUpperLeftCornerIfMissing(aAllTokens, aTop.getRowCount(), aLeft.getColumnCount());//e.g. #i91212# + } + + // Get range string. + lcl_convertTokensToString(sRangeRep, aAllTokens, m_pDocument); + + // add cell range property + aResult.push_back( + beans::PropertyValue( ::rtl::OUString::createFromAscii("CellRangeRepresentation"), -1, + uno::makeAny( sRangeRep ), beans::PropertyState_DIRECT_VALUE )); + + //Sequence Mapping + bool bSequencesReordered = true;//todo detect this above or detect this sequence mapping cheaper ... + if( bSequencesReordered && bRowSourceDetected ) + { + bool bDifferentIndexes = false; + + std::vector< sal_Int32 > aSequenceMappingVector; + + uno::Reference< chart2::data::XDataSource > xCompareDataSource; + try + { + xCompareDataSource.set( this->createDataSource( lcl_VectorToSequence( aResult ) ) ); + } + catch( const lang::IllegalArgumentException & ) + { + // creation of data source to compare didn't work, so we cannot + // create a sequence mapping + } + + if( xDataSource.is() && xCompareDataSource.is() ) + { + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > aOldSequences( + xCompareDataSource->getDataSequences() ); + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aNewSequences( + xDataSource->getDataSequences()); + + rtl::OUString aOldLabel; + rtl::OUString aNewLabel; + rtl::OUString aOldValues; + rtl::OUString aNewValues; + rtl::OUString aEmpty; + + for( sal_Int32 nNewIndex = 0; nNewIndex < aNewSequences.getLength(); nNewIndex++ ) + { + uno::Reference< chart2::data::XLabeledDataSequence> xNew( aNewSequences[nNewIndex] ); + for( sal_Int32 nOldIndex = 0; nOldIndex < aOldSequences.getLength(); nOldIndex++ ) + { + uno::Reference< chart2::data::XLabeledDataSequence> xOld( aOldSequences[nOldIndex] ); + + if( xOld.is() && xNew.is() ) + { + aOldLabel = aNewLabel = aOldValues = aNewValues = aEmpty; + if( xOld.is() && xOld->getLabel().is() ) + aOldLabel = xOld->getLabel()->getSourceRangeRepresentation(); + if( xNew.is() && xNew->getLabel().is() ) + aNewLabel = xNew->getLabel()->getSourceRangeRepresentation(); + if( xOld.is() && xOld->getValues().is() ) + aOldValues = xOld->getValues()->getSourceRangeRepresentation(); + if( xNew.is() && xNew->getValues().is() ) + aNewValues = xNew->getValues()->getSourceRangeRepresentation(); + + if( aOldLabel.equals(aNewLabel) + && ( aOldValues.equals(aNewValues) ) ) + { + if( nOldIndex!=nNewIndex ) + bDifferentIndexes = true; + aSequenceMappingVector.push_back(nOldIndex); + break; + } + } + } + } + } + + if( bDifferentIndexes && aSequenceMappingVector.size() ) + { + aResult.push_back( + beans::PropertyValue( ::rtl::OUString::createFromAscii("SequenceMapping"), -1, + uno::makeAny( lcl_VectorToSequence(aSequenceMappingVector) ) + , beans::PropertyState_DIRECT_VALUE )); + } + } + + return lcl_VectorToSequence( aResult ); +} + +::sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByRangeRepresentationPossible( const ::rtl::OUString& aRangeRepresentation ) + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + if( ! m_pDocument ) + return false; + + vector<ScSharedTokenRef> aTokens; + ScRefTokenHelper::compileRangeRepresentation(aTokens, aRangeRepresentation, m_pDocument, m_pDocument->GetGrammar()); + return !aTokens.empty(); +} + +uno::Reference< chart2::data::XDataSequence > SAL_CALL + ScChart2DataProvider::createDataSequenceByRangeRepresentation( + const ::rtl::OUString& aRangeRepresentation ) + throw (lang::IllegalArgumentException, + uno::RuntimeException) +{ + ScUnoGuard aGuard; + uno::Reference< chart2::data::XDataSequence > xResult; + + DBG_ASSERT( m_pDocument, "No Document -> no createDataSequenceByRangeRepresentation" ); + if(!m_pDocument || (aRangeRepresentation.getLength() == 0)) + return xResult; + + // Note: the range representation must be in Calc A1 format. The import + // filters use this method to pass data ranges, and they have no idea what + // the current formula syntax is. In the future we should add another + // method to allow the client code to directly pass tokens representing + // ranges. + + vector<ScSharedTokenRef> aRefTokens; + ScRefTokenHelper::compileRangeRepresentation(aRefTokens, aRangeRepresentation, m_pDocument); + if (aRefTokens.empty()) + return xResult; + + // ScChart2DataSequence manages the life cycle of pRefTokens. + vector<ScSharedTokenRef>* pRefTokens = new vector<ScSharedTokenRef>(); + pRefTokens->swap(aRefTokens); + xResult.set(new ScChart2DataSequence(m_pDocument, this, pRefTokens, m_bIncludeHiddenCells)); + + return xResult; +} + +uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::getRangeSelection() + throw (uno::RuntimeException) +{ + uno::Reference< sheet::XRangeSelection > xResult; + + uno::Reference< frame::XModel > xModel( lcl_GetXModel( m_pDocument )); + if( xModel.is()) + xResult.set( xModel->getCurrentController(), uno::UNO_QUERY ); + + return xResult; +} + +/*uno::Reference< util::XNumberFormatsSupplier > SAL_CALL ScChart2DataProvider::getNumberFormatsSupplier() + throw (uno::RuntimeException) +{ + return uno::Reference< util::XNumberFormatsSupplier >( lcl_GetXModel( m_pDocument ), uno::UNO_QUERY ); +}*/ + +// XRangeXMLConversion --------------------------------------------------- + +rtl::OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const rtl::OUString& sRangeRepresentation ) + throw ( uno::RuntimeException, lang::IllegalArgumentException ) +{ + OUString aRet; + if (!m_pDocument) + return aRet; + + if (!sRangeRepresentation.getLength()) + // Empty data range is allowed. + return aRet; + + vector<ScSharedTokenRef> aRefTokens; + ScRefTokenHelper::compileRangeRepresentation(aRefTokens, sRangeRepresentation, m_pDocument, m_pDocument->GetGrammar()); + if (aRefTokens.empty()) + throw lang::IllegalArgumentException(); + + Tokens2RangeStringXML converter(m_pDocument); + converter = for_each(aRefTokens.begin(), aRefTokens.end(), converter); + converter.getString(aRet); + + return aRet; +} + +rtl::OUString SAL_CALL ScChart2DataProvider::convertRangeFromXML( const rtl::OUString& sXMLRange ) + throw ( uno::RuntimeException, lang::IllegalArgumentException ) +{ + const sal_Unicode cSep = ' '; + const sal_Unicode cQuote = '\''; + + if (!m_pDocument) + { + // #i74062# When loading flat XML, this is called before the referenced sheets are in the document, + // so the conversion has to take place directly with the strings, without looking up the sheets. + + rtl::OUStringBuffer sRet; + sal_Int32 nOffset = 0; + while( nOffset >= 0 ) + { + rtl::OUString sToken; + ScRangeStringConverter::GetTokenByOffset( sToken, sXMLRange, nOffset, cSep, cQuote ); + if( nOffset >= 0 ) + { + // convert one address (remove dots) + + String aUIString(sToken); + + sal_Int32 nIndex = ScRangeStringConverter::IndexOf( sToken, ':', 0, cQuote ); + if ( nIndex >= 0 && nIndex < aUIString.Len() - 1 && + aUIString.GetChar((xub_StrLen)nIndex + 1) == (sal_Unicode) '.' ) + aUIString.Erase( (xub_StrLen)nIndex + 1, 1 ); + + if ( aUIString.GetChar(0) == (sal_Unicode) '.' ) + aUIString.Erase( 0, 1 ); + + if( sRet.getLength() ) + sRet.append( (sal_Unicode) ';' ); + sRet.append( aUIString ); + } + } + + return sRet.makeStringAndClear(); + } + + OUString aRet; + ScRangeStringConverter::GetStringFromXMLRangeString(aRet, sXMLRange, m_pDocument); + return aRet; +} + +// DataProvider XPropertySet ------------------------------------------------- + +uno::Reference< beans::XPropertySetInfo> SAL_CALL +ScChart2DataProvider::getPropertySetInfo() throw( uno::RuntimeException) +{ + ScUnoGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() ); + return aRef; +} + + +void SAL_CALL ScChart2DataProvider::setPropertyValue( + const ::rtl::OUString& rPropertyName, const uno::Any& rValue) + throw( beans::UnknownPropertyException, + beans::PropertyVetoException, + lang::IllegalArgumentException, + lang::WrappedTargetException, uno::RuntimeException) +{ + if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS))) + { + if ( !(rValue >>= m_bIncludeHiddenCells)) + throw lang::IllegalArgumentException(); + } + else + throw beans::UnknownPropertyException(); +} + + +uno::Any SAL_CALL ScChart2DataProvider::getPropertyValue( + const ::rtl::OUString& rPropertyName) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + uno::Any aRet; + if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS))) + aRet <<= m_bIncludeHiddenCells; + else + throw beans::UnknownPropertyException(); + return aRet; +} + + +void SAL_CALL ScChart2DataProvider::addPropertyChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2DataProvider::removePropertyChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2DataProvider::addVetoableChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2DataProvider::removeVetoableChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ ) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + OSL_ENSURE( false, "Not yet implemented" ); +} + +// DataSource ================================================================ + +ScChart2DataSource::ScChart2DataSource( ScDocument* pDoc) + : m_pDocument( pDoc) +{ + if ( m_pDocument ) + m_pDocument->AddUnoObject( *this); +} + + +ScChart2DataSource::~ScChart2DataSource() +{ + if ( m_pDocument ) + m_pDocument->RemoveUnoObject( *this); +} + + +void ScChart2DataSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.ISA( SfxSimpleHint ) && + ((const SfxSimpleHint&)rHint).GetId() == SFX_HINT_DYING ) + { + m_pDocument = NULL; + } +} + + +uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > SAL_CALL +ScChart2DataSource::getDataSequences() throw ( uno::RuntimeException) +{ + ScUnoGuard aGuard; + + LabeledList::const_iterator aItr(m_aLabeledSequences.begin()); + LabeledList::const_iterator aEndItr(m_aLabeledSequences.end()); + + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aRet(m_aLabeledSequences.size()); + + sal_Int32 i = 0; + while (aItr != aEndItr) + { + aRet[i] = *aItr; + ++i; + ++aItr; + } + + return aRet; + +/* typedef ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > tVec; + tVec aVec; + bool bSeries = false; + // split into columns - FIXME: different if GlueState() is used + for ( ScRangePtr p = m_xRanges->First(); p; p = m_xRanges->Next()) + { + for ( SCCOL nCol = p->aStart.Col(); nCol <= p->aEnd.Col(); ++nCol) + { + uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( + new ScChart2LabeledDataSequence( m_pDocument)); + if( xLabeledSeq.is()) + { + aVec.push_back( xLabeledSeq ); + if( bSeries ) + { + ScRangeListRef aColRanges = new ScRangeList; + // one single sheet selected assumed for now + aColRanges->Append( ScRange( nCol, p->aStart.Row(), + p->aStart.Tab(), nCol, p->aStart.Row(), + p->aStart.Tab())); + // TEST: add range two times, once as label, once as data + // TODO: create pure Numerical and Text sequences if possible + uno::Reference< chart2::data::XDataSequence > xLabel( + new ScChart2DataSequence( m_pDocument, aColRanges)); + + // set role + uno::Reference< beans::XPropertySet > xProp( xLabel, uno::UNO_QUERY ); + if( xProp.is()) + xProp->setPropertyValue( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Role" )), + ::uno::makeAny( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "label" )))); + + xLabeledSeq->setLabel( xLabel ); + } + + ScRangeListRef aColRanges = new ScRangeList; + + // one single sheet selected assumed for now + aColRanges->Append( ScRange( nCol, p->aStart.Row() + 1, + p->aStart.Tab(), nCol, p->aEnd.Row(), + p->aStart.Tab())); + uno::Reference< chart2::data::XDataSequence > xData( + new ScChart2DataSequence( m_pDocument, aColRanges)); + + // set role + uno::Reference< beans::XPropertySet > xProp( xData, uno::UNO_QUERY ); + if( xProp.is()) + xProp->setPropertyValue( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Role" )), + ::uno::makeAny( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "values" )))); + + xLabeledSeq->setValues( xData ); + + bSeries = true; + } + } + } + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > aSequences( + aVec.size()); + uno::Reference< chart2::data::XLabeledDataSequence> * pArr = aSequences.getArray(); + sal_Int32 j = 0; + for ( tVec::const_iterator iSeq = aVec.begin(); iSeq != aVec.end(); + ++iSeq, ++j) + { + pArr[j] = *iSeq; + } + return aSequences;*/ +} + +void ScChart2DataSource::AddLabeledSequence(const uno::Reference < chart2::data::XLabeledDataSequence >& xNew) +{ + m_aLabeledSequences.push_back(xNew); +} + +// LabeledDataSequence ======================================================= + +ScChart2LabeledDataSequence::ScChart2LabeledDataSequence( + ScDocument* pDoc ) : + m_pDocument( pDoc ) +{ + if ( m_pDocument ) + m_pDocument->AddUnoObject( *this); +} + +ScChart2LabeledDataSequence::~ScChart2LabeledDataSequence() +{ + if ( m_pDocument ) + m_pDocument->RemoveUnoObject( *this); +} + +// SfxListener ----------------------------------------------------------- + +void ScChart2LabeledDataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.ISA( SfxSimpleHint ) && + ((const SfxSimpleHint&)rHint).GetId() == SFX_HINT_DYING ) + { + ScUnoGuard aGuard; + m_pDocument = NULL; + } +} + +// XLabeledDataSequence -------------------------------------------------- + +uno::Reference< chart2::data::XDataSequence > SAL_CALL ScChart2LabeledDataSequence::getValues() + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + return m_aData; +} + +void SAL_CALL ScChart2LabeledDataSequence::setValues( + const uno::Reference< chart2::data::XDataSequence >& xSequence ) + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + m_aData = xSequence; +} + +uno::Reference< chart2::data::XDataSequence > SAL_CALL ScChart2LabeledDataSequence::getLabel() + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + return m_aLabel; +} + +void SAL_CALL ScChart2LabeledDataSequence::setLabel( + const uno::Reference< chart2::data::XDataSequence >& xSequence ) + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + m_aLabel = xSequence; +} + +// XCloneable ================================================================ + +uno::Reference< util::XCloneable > SAL_CALL ScChart2LabeledDataSequence::createClone() + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + uno::Reference< util::XCloneable > xToClone(m_aData, uno::UNO_QUERY); + if (xToClone.is()) + { + ScChart2LabeledDataSequence* pRet = new ScChart2LabeledDataSequence(m_pDocument); + uno::Reference< chart2::data::XDataSequence > xSequence(xToClone->createClone(), uno::UNO_QUERY); + pRet->setValues(xSequence); + xToClone.set(m_aLabel, uno::UNO_QUERY); + if(xToClone.is()) + { + xSequence.set(xToClone->createClone(), uno::UNO_QUERY); + pRet->setLabel(xSequence); + } + return pRet; + } + return NULL; +} + +// XModifyBroadcaster ======================================================== + +void SAL_CALL ScChart2LabeledDataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener ) + throw (uno::RuntimeException) +{ + // quick'n dirty: just add the listener to each DataSequence + + uno::Reference<util::XModifyBroadcaster> xDataBroadcaster( m_aData, uno::UNO_QUERY ); + if ( xDataBroadcaster.is() ) + xDataBroadcaster->addModifyListener( aListener ); + uno::Reference<util::XModifyBroadcaster> xLabelBroadcaster( m_aLabel, uno::UNO_QUERY ); + if ( xLabelBroadcaster.is() ) + xLabelBroadcaster->addModifyListener( aListener ); +} + +void SAL_CALL ScChart2LabeledDataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener ) + throw (uno::RuntimeException) +{ + uno::Reference<util::XModifyBroadcaster> xDataBroadcaster( m_aData, uno::UNO_QUERY ); + if ( xDataBroadcaster.is() ) + xDataBroadcaster->removeModifyListener( aListener ); + uno::Reference<util::XModifyBroadcaster> xLabelBroadcaster( m_aLabel, uno::UNO_QUERY ); + if ( xLabelBroadcaster.is() ) + xLabelBroadcaster->removeModifyListener( aListener ); +} + +// DataSequence ============================================================== + +ScChart2DataSequence::Item::Item() : + mfValue(0.0), mbIsValue(false) +{ + ::rtl::math::setNan(&mfValue); +} + +ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence& rParent) : + mrParent(rParent) +{ +} + +ScChart2DataSequence::HiddenRangeListener::~HiddenRangeListener() +{ +} + +void ScChart2DataSequence::HiddenRangeListener::notify() +{ + mrParent.setDataChangedHint(true); +} + +ScChart2DataSequence::ScChart2DataSequence( ScDocument* pDoc, + const uno::Reference < chart2::data::XDataProvider >& xDP, + vector<ScSharedTokenRef>* pTokens, + bool bIncludeHiddenCells ) + : m_bIncludeHiddenCells( bIncludeHiddenCells) + , m_nObjectId( 0 ) + , m_pDocument( pDoc) + , m_pTokens(pTokens) + , m_pRangeIndices(NULL) + , m_pExtRefListener(NULL) + , m_xDataProvider( xDP) + , m_aPropSet(lcl_GetDataSequencePropertyMap()) + , m_pHiddenListener(NULL) + , m_pValueListener( NULL ) + , m_bGotDataChangedHint(false) + , m_bExtDataRebuildQueued(false) +{ + DBG_ASSERT(pTokens, "reference token list is null"); + + if ( m_pDocument ) + { + m_pDocument->AddUnoObject( *this); + m_nObjectId = m_pDocument->GetNewUnoId(); + } + // FIXME: real implementation of identifier and it's mapping to ranges. + // Reuse ScChartListener? + + // BM: don't use names of named ranges but the UI range strings +// String aStr; +// rRangeList->Format( aStr, SCR_ABS_3D, m_pDocument ); +// m_aIdentifier = ::rtl::OUString( aStr ); + +// m_aIdentifier = ::rtl::OUString::createFromAscii( "ID_"); +// static sal_Int32 nID = 0; +// m_aIdentifier += ::rtl::OUString::valueOf( ++nID); +} + +ScChart2DataSequence::~ScChart2DataSequence() +{ + if ( m_pDocument ) + { + m_pDocument->RemoveUnoObject( *this); + if (m_pHiddenListener.get()) + { + ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection(); + if (pCLC) + pCLC->EndListeningHiddenRange(m_pHiddenListener.get()); + } + StopListeningToAllExternalRefs(); + } + + delete m_pValueListener; +} + +void ScChart2DataSequence::RefChanged() +{ + if( m_pValueListener && m_aValueListeners.Count() != 0 ) + { + m_pValueListener->EndListeningAll(); + + if( m_pDocument ) + { + ScChartListenerCollection* pCLC = NULL; + if (m_pHiddenListener.get()) + { + pCLC = m_pDocument->GetChartListenerCollection(); + if (pCLC) + pCLC->EndListeningHiddenRange(m_pHiddenListener.get()); + } + + vector<ScSharedTokenRef>::const_iterator itr = m_pTokens->begin(), itrEnd = m_pTokens->end(); + for (; itr != itrEnd; ++itr) + { + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(aRange, *itr)) + continue; + + m_pDocument->StartListeningArea(aRange, m_pValueListener); + if (pCLC) + pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get()); + } + } + } +} + +void ScChart2DataSequence::BuildDataCache() +{ + m_bExtDataRebuildQueued = false; + + if (!m_aDataArray.empty()) + return; + + if (!m_pTokens.get()) + { + DBG_ERROR("m_pTokens == NULL! Something is wrong."); + return; + } + + StopListeningToAllExternalRefs(); + + ::std::list<sal_Int32> aHiddenValues; + sal_Int32 nDataCount = 0; + sal_Int32 nHiddenValueCount = 0; + + for (vector<ScSharedTokenRef>::const_iterator itr = m_pTokens->begin(), itrEnd = m_pTokens->end(); + itr != itrEnd; ++itr) + { + if (ScRefTokenHelper::isExternalRef(*itr)) + { + nDataCount += FillCacheFromExternalRef(*itr); + } + else + { + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(aRange, *itr)) + continue; + + SCCOL nLastCol = -1; + SCROW nLastRow = -1; + for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab) + { + for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) + { + for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) + { + bool bColHidden = m_pDocument->ColHidden(nCol, nTab, nLastCol); + bool bRowHidden = m_pDocument->RowHidden(nRow, nTab, nLastRow); + + if (bColHidden || bRowHidden) + { + // hidden cell + ++nHiddenValueCount; + aHiddenValues.push_back(nDataCount-1); + + if( !m_bIncludeHiddenCells ) + continue; + } + + m_aDataArray.push_back(Item()); + Item& rItem = m_aDataArray.back(); + ++nDataCount; + + ScAddress aAdr(nCol, nRow, nTab); + ScBaseCell* pCell = m_pDocument->GetCell(aAdr); + if (!pCell) + continue; + + if (pCell->HasStringData()) + rItem.maString = pCell->GetStringData(); + else + { + String aStr; + m_pDocument->GetString(nCol, nRow, nTab, aStr); + rItem.maString = aStr; + } + + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + rItem.mfValue = static_cast< ScValueCell*>(pCell)->GetValue(); + rItem.mbIsValue = true; + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); + USHORT nErr = pFCell->GetErrCode(); + if (nErr) + break; + + if (pFCell->HasValueData()) + { + rItem.mfValue = pFCell->GetValue(); + rItem.mbIsValue = true; + } + } + break; +#if DBG_UTIL + case CELLTYPE_DESTROYED: +#endif + case CELLTYPE_EDIT: + case CELLTYPE_NONE: + case CELLTYPE_NOTE: + case CELLTYPE_STRING: + case CELLTYPE_SYMBOLS: + default: + ; // do nothing + } + } + } + } + } + } + + // convert the hidden cell list to sequence. + m_aHiddenValues.realloc(nHiddenValueCount); + sal_Int32* pArr = m_aHiddenValues.getArray(); + ::std::list<sal_Int32>::const_iterator itr = aHiddenValues.begin(), itrEnd = aHiddenValues.end(); + for (;itr != itrEnd; ++itr, ++pArr) + *pArr = *itr; + + // Clear the data series cache when the array is re-built. + m_aMixedDataCache.realloc(0); +} + +void ScChart2DataSequence::RebuildDataCache() +{ + if (!m_bExtDataRebuildQueued) + { + m_aDataArray.clear(); + m_pDocument->BroadcastUno(ScHint(SC_HINT_DATACHANGED, ScAddress(), NULL)); + m_bExtDataRebuildQueued = true; + m_bGotDataChangedHint = true; + } +} + +sal_Int32 ScChart2DataSequence::FillCacheFromExternalRef(const ScSharedTokenRef& pToken) +{ + ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager(); + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(aRange, pToken, true)) + return 0; + + sal_uInt16 nFileId = pToken->GetIndex(); + const String& rTabName = pToken->GetString(); + ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, rTabName, aRange, NULL); + if (!pArray) + // no external data exists for this range. + return 0; + + // Start listening for this external document. + ExternalRefListener* pExtRefListener = GetExtRefListener(); + pRefMgr->addLinkListener(nFileId, pExtRefListener); + pExtRefListener->addFileId(nFileId); + + ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, rTabName, false, NULL); + sal_Int32 nDataCount = 0; + for (FormulaToken* p = pArray->First(); p; p = pArray->Next()) + { + // Cached external range is always represented as a single + // matrix token, although that might change in the future when + // we introduce a new token type to store multi-table range + // data. + + if (p->GetType() != svMatrix) + { + DBG_ERROR("Cached array is not a matrix token."); + continue; + } + + const ScMatrix* pMat = static_cast<ScToken*>(p)->GetMatrix(); + SCSIZE nCSize, nRSize; + pMat->GetDimensions(nCSize, nRSize); + for (SCSIZE nC = 0; nC < nCSize; ++nC) + { + for (SCSIZE nR = 0; nR < nRSize; ++nR) + { + if (pMat->IsValue(nC, nR) || pMat->IsBoolean(nC, nR)) + { + m_aDataArray.push_back(Item()); + Item& rItem = m_aDataArray.back(); + ++nDataCount; + + rItem.mbIsValue = true; + rItem.mfValue = pMat->GetDouble(nC, nR); + + SvNumberFormatter* pFormatter = m_pDocument->GetFormatTable(); + if (pFormatter) + { + String aStr; + const double fVal = rItem.mfValue; + Color* pColor = NULL; + sal_uInt32 nFmt = 0; + if (pTable) + { + // Get the correct format index from the cache. + SCCOL nCol = aRange.aStart.Col() + static_cast<SCCOL>(nC); + SCROW nRow = aRange.aStart.Row() + static_cast<SCROW>(nR); + pTable->getCell(nCol, nRow, &nFmt); + } + pFormatter->GetOutputString(fVal, nFmt, aStr, &pColor); + rItem.maString = aStr; + } + } + else if (pMat->IsString(nC, nR)) + { + m_aDataArray.push_back(Item()); + Item& rItem = m_aDataArray.back(); + ++nDataCount; + + rItem.mbIsValue = false; + rItem.maString = pMat->GetString(nC, nR); + } + } + } + } + return nDataCount; +} + +void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList& rRanges) +{ + if (!m_pRangeIndices.get()) + return; + + sal_uInt32 nCount = rRanges.Count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + ScSharedTokenRef pToken; + ScRange* pRange = static_cast<ScRange*>(rRanges.GetObject(i)); + DBG_ASSERT(pRange, "range object is NULL."); + + ScRefTokenHelper::getTokenFromRange(pToken, *pRange); + sal_uInt32 nOrigPos = (*m_pRangeIndices)[i]; + (*m_pTokens)[nOrigPos] = pToken; + } + + RefChanged(); + + // any change of the range address is broadcast to value (modify) listeners + if ( m_aValueListeners.Count() ) + m_bGotDataChangedHint = true; +} + +ScChart2DataSequence::ExternalRefListener* ScChart2DataSequence::GetExtRefListener() +{ + if (!m_pExtRefListener.get()) + m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument)); + + return m_pExtRefListener.get(); +} + +void ScChart2DataSequence::StopListeningToAllExternalRefs() +{ + if (!m_pExtRefListener.get()) + return; + + const hash_set<sal_uInt16>& rFileIds = m_pExtRefListener->getAllFileIds(); + hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end(); + ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager(); + for (; itr != itrEnd; ++itr) + pRefMgr->removeLinkListener(*itr, m_pExtRefListener.get()); + + m_pExtRefListener.reset(NULL); +} + +void ScChart2DataSequence::CopyData(const ScChart2DataSequence& r) +{ + if (!m_pDocument) + { + DBG_ERROR("document instance is NULL!?"); + return; + } + + list<Item> aDataArray(r.m_aDataArray); + m_aDataArray.swap(aDataArray); + + m_aHiddenValues = r.m_aHiddenValues; + m_aRole = r.m_aRole; + + if (r.m_pRangeIndices.get()) + m_pRangeIndices.reset(new vector<sal_uInt32>(*r.m_pRangeIndices)); + + if (r.m_pExtRefListener.get()) + { + // Re-register all external files that the old instance was + // listening to. + + ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager(); + m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument)); + const hash_set<sal_uInt16>& rFileIds = r.m_pExtRefListener->getAllFileIds(); + hash_set<sal_uInt16>::const_iterator itr = rFileIds.begin(), itrEnd = rFileIds.end(); + for (; itr != itrEnd; ++itr) + { + pRefMgr->addLinkListener(*itr, m_pExtRefListener.get()); + m_pExtRefListener->addFileId(*itr); + } + } +} + +void ScChart2DataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.ISA( SfxSimpleHint ) ) + { + ULONG nId = static_cast<const SfxSimpleHint&>(rHint).GetId(); + if ( nId ==SFX_HINT_DYING ) + { + m_pDocument = NULL; + } + else if ( nId == SFX_HINT_DATACHANGED ) + { + // delayed broadcast as in ScCellRangesBase + + if ( m_bGotDataChangedHint && m_pDocument ) + { + m_aDataArray.clear(); + lang::EventObject aEvent; + aEvent.Source.set((cppu::OWeakObject*)this); + + if( m_pDocument ) + { + for ( USHORT n=0; n<m_aValueListeners.Count(); n++ ) + m_pDocument->AddUnoListenerCall( *m_aValueListeners[n], aEvent ); + } + + m_bGotDataChangedHint = false; + } + } + else if ( nId == SC_HINT_CALCALL ) + { + // broadcast from DoHardRecalc - set m_bGotDataChangedHint + // (SFX_HINT_DATACHANGED follows separately) + + if ( m_aValueListeners.Count() ) + m_bGotDataChangedHint = true; + } + } + else if ( rHint.ISA( ScUpdateRefHint ) ) + { + // Create a range list from the token list, have the range list + // updated, and bring the change back to the token list. + + ScRangeList aRanges; + m_pRangeIndices.reset(new vector<sal_uInt32>()); + vector<ScSharedTokenRef>::const_iterator itrBeg = m_pTokens->begin(), itrEnd = m_pTokens->end(); + for (vector<ScSharedTokenRef>::const_iterator itr = itrBeg ;itr != itrEnd; ++itr) + { + if (!ScRefTokenHelper::isExternalRef(*itr)) + { + ScRange aRange; + ScRefTokenHelper::getRangeFromToken(aRange, *itr); + aRanges.Append(aRange); + sal_uInt32 nPos = distance(itrBeg, itr); + m_pRangeIndices->push_back(nPos); + } + } + + DBG_ASSERT(m_pRangeIndices->size() == static_cast<size_t>(aRanges.Count()), + "range list and range index list have different sizes."); + + auto_ptr<ScRangeList> pUndoRanges; + if ( m_pDocument->HasUnoRefUndo() ) + pUndoRanges.reset(new ScRangeList(aRanges)); + + const ScUpdateRefHint& rRef = (const ScUpdateRefHint&)rHint; + bool bChanged = aRanges.UpdateReference( + rRef.GetMode(), m_pDocument, rRef.GetRange(), rRef.GetDx(), rRef.GetDy(), rRef.GetDz()); + + if (bChanged) + { + DBG_ASSERT(m_pRangeIndices->size() == static_cast<size_t>(aRanges.Count()), + "range list and range index list have different sizes after the reference update."); + + // Bring the change back from the range list to the token list. + UpdateTokensFromRanges(aRanges); + + if (pUndoRanges.get()) + m_pDocument->AddUnoRefChange(m_nObjectId, *pUndoRanges); + } + } + else if ( rHint.ISA( ScUnoRefUndoHint ) ) + { + const ScUnoRefUndoHint& rUndoHint = static_cast<const ScUnoRefUndoHint&>(rHint); + + do + { + if (rUndoHint.GetObjectId() != m_nObjectId) + break; + + // The hint object provides the old ranges. Restore the old state + // from these ranges. + + if (!m_pRangeIndices.get() || m_pRangeIndices->empty()) + { + DBG_ERROR(" faulty range indices"); + break; + } + + const ScRangeList& rRanges = rUndoHint.GetRanges(); + + sal_uInt32 nCount = rRanges.Count(); + if (nCount != m_pRangeIndices->size()) + { + DBG_ERROR("range count and range index count differ."); + break; + } + + UpdateTokensFromRanges(rRanges); + } + while (false); + } +} + + +IMPL_LINK( ScChart2DataSequence, ValueListenerHdl, SfxHint*, pHint ) +{ + if ( m_pDocument && pHint && pHint->ISA( SfxSimpleHint ) && + ((const SfxSimpleHint*)pHint)->GetId() & (SC_HINT_DATACHANGED | SC_HINT_DYING) ) + { + // This may be called several times for a single change, if several formulas + // in the range are notified. So only a flag is set that is checked when + // SFX_HINT_DATACHANGED is received. + + setDataChangedHint(true); + } + return 0; +} + +// ---------------------------------------------------------------------------- + +ScChart2DataSequence::ExternalRefListener::ExternalRefListener( + ScChart2DataSequence& rParent, ScDocument* pDoc) : + ScExternalRefManager::LinkListener(), + mrParent(rParent), + mpDoc(pDoc) +{ +} + +ScChart2DataSequence::ExternalRefListener::~ExternalRefListener() +{ + if (!mpDoc || mpDoc->IsInDtorClear()) + // The document is being destroyed. Do nothing. + return; + + // Make sure to remove all pointers to this object. + mpDoc->GetExternalRefManager()->removeLinkListener(this); +} + +void ScChart2DataSequence::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) +{ + switch (eType) + { + case ScExternalRefManager::LINK_MODIFIED: + { + if (maFileIds.count(nFileId)) + // We are listening to this external document. + mrParent.RebuildDataCache(); + } + break; + case ScExternalRefManager::LINK_BROKEN: + removeFileId(nFileId); + break; + } +} + +void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId) +{ + maFileIds.insert(nFileId); +} + +void ScChart2DataSequence::ExternalRefListener::removeFileId(sal_uInt16 nFileId) +{ + maFileIds.erase(nFileId); +} + +const hash_set<sal_uInt16>& ScChart2DataSequence::ExternalRefListener::getAllFileIds() +{ + return maFileIds; +} + +// ---------------------------------------------------------------------------- + +uno::Sequence< uno::Any> SAL_CALL ScChart2DataSequence::getData() + throw ( uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + BuildDataCache(); + + if (!m_aMixedDataCache.getLength()) + { + // Build a cache for the 1st time... + + sal_Int32 nCount = m_aDataArray.size(); + m_aMixedDataCache.realloc(nCount); + uno::Any* pArr = m_aMixedDataCache.getArray(); + ::std::list<Item>::const_iterator itr = m_aDataArray.begin(), itrEnd = m_aDataArray.end(); + for (; itr != itrEnd; ++itr, ++pArr) + { + if (itr->mbIsValue) + *pArr <<= itr->mfValue; + else + *pArr <<= itr->maString; + } + } + return m_aMixedDataCache; +} + +// XNumericalDataSequence -------------------------------------------------- + +uno::Sequence< double > SAL_CALL ScChart2DataSequence::getNumericalData() + throw ( uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + BuildDataCache(); + + double fNAN; + ::rtl::math::setNan(&fNAN); + + sal_Int32 nCount = m_aDataArray.size(); + uno::Sequence<double> aSeq(nCount); + double* pArr = aSeq.getArray(); + ::std::list<Item>::const_iterator itr = m_aDataArray.begin(), itrEnd = m_aDataArray.end(); + for (; itr != itrEnd; ++itr, ++pArr) + *pArr = itr->mbIsValue ? itr->mfValue : fNAN; + + return aSeq; +} + +// XTextualDataSequence -------------------------------------------------- + +uno::Sequence< rtl::OUString > SAL_CALL ScChart2DataSequence::getTextualData( ) throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + BuildDataCache(); + + sal_Int32 nCount = m_aDataArray.size(); + uno::Sequence<rtl::OUString> aSeq(nCount); + rtl::OUString* pArr = aSeq.getArray(); + ::std::list<Item>::const_iterator itr = m_aDataArray.begin(), itrEnd = m_aDataArray.end(); + for (; itr != itrEnd; ++itr, ++pArr) + *pArr = itr->maString; + + return aSeq; +} + +::rtl::OUString SAL_CALL ScChart2DataSequence::getSourceRangeRepresentation() + throw ( uno::RuntimeException) +{ + ScUnoGuard aGuard; + OUString aStr; + DBG_ASSERT( m_pDocument, "No Document -> no SourceRangeRepresentation" ); + if (m_pDocument && m_pTokens.get()) + lcl_convertTokensToString(aStr, *m_pTokens, m_pDocument); + + return aStr; +} + +namespace { + +/** + * This function object is used to accumulatively count the numbers of + * columns and rows in all reference tokens. + */ +class AccumulateRangeSize : public unary_function<ScSharedTokenRef, void> +{ +public: + AccumulateRangeSize() : + mnCols(0), mnRows(0) {} + + AccumulateRangeSize(const AccumulateRangeSize& r) : + mnCols(r.mnCols), mnRows(r.mnRows) {} + + void operator() (const ScSharedTokenRef& pToken) + { + ScRange r; + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + ScRefTokenHelper::getRangeFromToken(r, pToken, bExternal); + r.Justify(); + mnCols += r.aEnd.Col() - r.aStart.Col() + 1; + mnRows += r.aEnd.Row() - r.aStart.Row() + 1; + } + + SCCOL getCols() const { return mnCols; } + SCROW getRows() const { return mnRows; } +private: + SCCOL mnCols; + SCROW mnRows; +}; + +/** + * This function object is used to generate label strings from a list of + * reference tokens. + */ +class GenerateLabelStrings : public unary_function<ScSharedTokenRef, void> +{ +public: + GenerateLabelStrings(sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) : + mpLabels(new Sequence<OUString>(nSize)), + meOrigin(eOrigin), + mnCount(0), + mbColumn(bColumn) {} + + GenerateLabelStrings(const GenerateLabelStrings& r) : + mpLabels(r.mpLabels), + meOrigin(r.meOrigin), + mnCount(r.mnCount), + mbColumn(r.mbColumn) {} + + void operator() (const ScSharedTokenRef& pToken) + { + bool bExternal = ScRefTokenHelper::isExternalRef(pToken); + ScRange aRange; + ScRefTokenHelper::getRangeFromToken(aRange, pToken, bExternal); + OUString* pArr = mpLabels->getArray(); + if (mbColumn) + { + for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol) + { + if ( meOrigin != chart2::data::LabelOrigin_LONG_SIDE) + { + String aString = ScGlobal::GetRscString(STR_COLUMN); + aString += ' '; + ScAddress aPos( nCol, 0, 0 ); + String aColStr; + aPos.Format( aColStr, SCA_VALID_COL, NULL ); + aString += aColStr; + pArr[mnCount] = aString; + } + else //only indices for categories + pArr[mnCount] = String::CreateFromInt32( mnCount+1 ); + ++mnCount; + } + } + else + { + for (sal_Int32 nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow) + { + if (meOrigin != chart2::data::LabelOrigin_LONG_SIDE) + { + String aString = ScGlobal::GetRscString(STR_ROW); + aString += ' '; + aString += String::CreateFromInt32( nRow+1 ); + pArr[mnCount] = aString; + } + else //only indices for categories + pArr[mnCount] = String::CreateFromInt32( mnCount+1 ); + ++mnCount; + } + } + } + + Sequence<OUString> getLabels() const { return *mpLabels; } + +private: + GenerateLabelStrings(); // disabled + + shared_ptr< Sequence<OUString> > mpLabels; + chart2::data::LabelOrigin meOrigin; + sal_Int32 mnCount; + bool mbColumn; +}; + +} + +uno::Sequence< ::rtl::OUString > SAL_CALL ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin) + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + if (!m_pTokens.get()) + return Sequence<OUString>(); + + // Determine the total size of all ranges. + AccumulateRangeSize func; + func = for_each(m_pTokens->begin(), m_pTokens->end(), func); + SCCOL nCols = func.getCols(); + SCROW nRows = func.getRows(); + + // Detemine whether this is column-major or row-major. + bool bColumn = true; + if ((eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) || + (eOrigin == chart2::data::LabelOrigin_LONG_SIDE)) + { + if (nRows > nCols) + { + if (eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) + bColumn = true; + else + bColumn = false; + } + else if (nCols > nRows) + { + if (eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) + bColumn = false; + else + bColumn = true; + } + else + return Sequence<OUString>(); + } + + // Generate label strings based on the info so far. + sal_Int32 nCount = bColumn ? nCols : nRows; + GenerateLabelStrings genLabels(nCount, eOrigin, bColumn); + genLabels = for_each(m_pTokens->begin(), m_pTokens->end(), genLabels); + Sequence<OUString> aSeq = genLabels.getLabels(); + + return aSeq; +} + +::sal_Int32 SAL_CALL ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex ) + throw (lang::IndexOutOfBoundsException, + uno::RuntimeException) +{ + // index -1 means a heuristic value for the entire sequence + bool bGetSeriesFormat = (nIndex == -1); + sal_Int32 nResult = 0; + + ScUnoGuard aGuard; + if ( !m_pDocument || !m_pTokens.get()) + return nResult; + + sal_Int32 nCount = 0; + bool bFound = false; + ScRangePtr p; + + uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( lcl_GetSpreadSheetDocument( m_pDocument )); + if (!xSpreadDoc.is()) + return nResult; + + uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY ); + if (!xIndex.is()) + return nResult; + + ScRangeList aRanges; + ScRefTokenHelper::getRangeListFromTokens(aRanges, *m_pTokens); + uno::Reference< table::XCellRange > xSheet; + for ( p = aRanges.First(); p && !bFound; p = aRanges.Next()) + { + // TODO: use DocIter? + table::CellAddress aStart, aEnd; + ScUnoConversion::FillApiAddress( aStart, p->aStart ); + ScUnoConversion::FillApiAddress( aEnd, p->aEnd ); + for ( sal_Int16 nSheet = aStart.Sheet; nSheet <= aEnd.Sheet && !bFound; ++nSheet) + { + xSheet.set(xIndex->getByIndex(nSheet), uno::UNO_QUERY); + for ( sal_Int32 nCol = aStart.Column; nCol <= aEnd.Column && !bFound; ++nCol) + { + for ( sal_Int32 nRow = aStart.Row; nRow <= aEnd.Row && !bFound; ++nRow) + { + if( bGetSeriesFormat ) + { + // TODO: use nicer heuristic + // return format of first non-empty cell + uno::Reference< text::XText > xText( + xSheet->getCellByPosition(nCol, nRow), uno::UNO_QUERY); + if (xText.is() && xText->getString().getLength()) + { + uno::Reference< beans::XPropertySet > xProp(xText, uno::UNO_QUERY); + if( xProp.is()) + xProp->getPropertyValue( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("NumberFormat"))) >>= nResult; + bFound = true; + break; + } + } + else if( nCount == nIndex ) + { + uno::Reference< beans::XPropertySet > xProp( + xSheet->getCellByPosition(nCol, nRow), uno::UNO_QUERY); + if( xProp.is()) + xProp->getPropertyValue( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("NumberFormat"))) >>= nResult; + bFound = true; + break; + } + ++nCount; + } + } + } + } + + return nResult; +} + +// XCloneable ================================================================ + +uno::Reference< util::XCloneable > SAL_CALL ScChart2DataSequence::createClone() + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + + auto_ptr< vector<ScSharedTokenRef> > pTokensNew; + if (m_pTokens.get()) + { + // Clone tokens. + pTokensNew.reset(new vector<ScSharedTokenRef>); + pTokensNew->reserve(m_pTokens->size()); + vector<ScSharedTokenRef>::const_iterator itr = m_pTokens->begin(), itrEnd = m_pTokens->end(); + for (; itr != itrEnd; ++itr) + { + ScSharedTokenRef p(static_cast<ScToken*>((*itr)->Clone())); + pTokensNew->push_back(p); + } + } + + auto_ptr<ScChart2DataSequence> p(new ScChart2DataSequence(m_pDocument, m_xDataProvider, pTokensNew.release(), m_bIncludeHiddenCells)); + p->CopyData(*this); + Reference< util::XCloneable > xClone(p.release()); + + return xClone; +} + +// XModifyBroadcaster ======================================================== + +void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener ) + throw (uno::RuntimeException) +{ + // like ScCellRangesBase::addModifyListener + ScUnoGuard aGuard; + if (!m_pTokens.get() || m_pTokens->empty()) + return; + + ScRangeList aRanges; + ScRefTokenHelper::getRangeListFromTokens(aRanges, *m_pTokens); + uno::Reference<util::XModifyListener> *pObj = + new uno::Reference<util::XModifyListener>( aListener ); + m_aValueListeners.Insert( pObj, m_aValueListeners.Count() ); + + if ( m_aValueListeners.Count() == 1 ) + { + if (!m_pValueListener) + m_pValueListener = new ScLinkListener( LINK( this, ScChart2DataSequence, ValueListenerHdl ) ); + + if (!m_pHiddenListener.get()) + m_pHiddenListener.reset(new HiddenRangeListener(*this)); + + if( m_pDocument ) + { + ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection(); + vector<ScSharedTokenRef>::const_iterator itr = m_pTokens->begin(), itrEnd = m_pTokens->end(); + for (; itr != itrEnd; ++itr) + { + ScRange aRange; + if (!ScRefTokenHelper::getRangeFromToken(aRange, *itr)) + continue; + + m_pDocument->StartListeningArea( aRange, m_pValueListener ); + if (pCLC) + pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get()); + } + } + + acquire(); // don't lose this object (one ref for all listeners) + } +} + +void SAL_CALL ScChart2DataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener ) + throw (uno::RuntimeException) +{ + // like ScCellRangesBase::removeModifyListener + + ScUnoGuard aGuard; + if (!m_pTokens.get() || m_pTokens->empty()) + return; + + acquire(); // in case the listeners have the last ref - released below + + USHORT nCount = m_aValueListeners.Count(); + for ( USHORT n=nCount; n--; ) + { + uno::Reference<util::XModifyListener> *pObj = m_aValueListeners[n]; + if ( *pObj == aListener ) + { + m_aValueListeners.DeleteAndDestroy( n ); + + if ( m_aValueListeners.Count() == 0 ) + { + if (m_pValueListener) + m_pValueListener->EndListeningAll(); + + if (m_pHiddenListener.get() && m_pDocument) + { + ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection(); + if (pCLC) + pCLC->EndListeningHiddenRange(m_pHiddenListener.get()); + } + + release(); // release the ref for the listeners + } + + break; + } + } + + release(); // might delete this object +} + +// DataSequence XPropertySet ------------------------------------------------- + +uno::Reference< beans::XPropertySetInfo> SAL_CALL +ScChart2DataSequence::getPropertySetInfo() throw( uno::RuntimeException) +{ + ScUnoGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() ); + return aRef; +} + + +void SAL_CALL ScChart2DataSequence::setPropertyValue( + const ::rtl::OUString& rPropertyName, const uno::Any& rValue) + throw( beans::UnknownPropertyException, + beans::PropertyVetoException, + lang::IllegalArgumentException, + lang::WrappedTargetException, uno::RuntimeException) +{ + if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_ROLE))) + { + if ( !(rValue >>= m_aRole)) + throw lang::IllegalArgumentException(); + } + else if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS))) + { + sal_Bool bOldValue = m_bIncludeHiddenCells; + if ( !(rValue >>= m_bIncludeHiddenCells)) + throw lang::IllegalArgumentException(); + if( bOldValue != m_bIncludeHiddenCells ) + m_aDataArray.clear();//data array is dirty now + } + else + throw beans::UnknownPropertyException(); + // TODO: support optional properties +} + + +uno::Any SAL_CALL ScChart2DataSequence::getPropertyValue( + const ::rtl::OUString& rPropertyName) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + uno::Any aRet; + if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_ROLE))) + aRet <<= m_aRole; + else if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS))) + aRet <<= m_bIncludeHiddenCells; + else if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(SC_UNONAME_HIDDENVALUES))) + { + // This property is read-only thus cannot be set externally via + // setPropertyValue(...). + BuildDataCache(); + aRet <<= m_aHiddenValues; + } + else + throw beans::UnknownPropertyException(); + // TODO: support optional properties + return aRet; +} + + +void SAL_CALL ScChart2DataSequence::addPropertyChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2DataSequence::removePropertyChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2DataSequence::addVetoableChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2DataSequence::removeVetoableChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + +void ScChart2DataSequence::setDataChangedHint(bool b) +{ + m_bGotDataChangedHint = b; +} + +// XUnoTunnel + +// sal_Int64 SAL_CALL ScChart2DataSequence::getSomething( +// const uno::Sequence<sal_Int8 >& rId ) throw(uno::RuntimeException) +// { +// if ( rId.getLength() == 16 && +// 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), +// rId.getConstArray(), 16 ) ) +// { +// return (sal_Int64)this; +// } +// return 0; +// } + +// // static +// const uno::Sequence<sal_Int8>& ScChart2DataSequence::getUnoTunnelId() +// { +// static uno::Sequence<sal_Int8> * pSeq = 0; +// if( !pSeq ) +// { +// osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() ); +// if( !pSeq ) +// { +// static uno::Sequence< sal_Int8 > aSeq( 16 ); +// rtl_createUuid( (sal_uInt8*)aSeq.getArray(), 0, sal_True ); +// pSeq = &aSeq; +// } +// } +// return *pSeq; +// } + +// // static +// ScChart2DataSequence* ScChart2DataSequence::getImplementation( const uno::Reference<uno::XInterface> xObj ) +// { +// ScChart2DataSequence* pRet = NULL; +// uno::Reference<lang::XUnoTunnel> xUT( xObj, uno::UNO_QUERY ); +// if (xUT.is()) +// pRet = (ScChart2DataSequence*) xUT->getSomething( getUnoTunnelId() ); +// return pRet; +// } + +#if USE_CHART2_EMPTYDATASEQUENCE +// DataSequence ============================================================== + +ScChart2EmptyDataSequence::ScChart2EmptyDataSequence( ScDocument* pDoc, + const uno::Reference < chart2::data::XDataProvider >& xDP, + const ScRangeListRef& rRangeList, + sal_Bool bColumn) + : m_bIncludeHiddenCells( sal_True) + , m_xRanges( rRangeList) + , m_pDocument( pDoc) + , m_xDataProvider( xDP) + , m_aPropSet(lcl_GetDataSequencePropertyMap()) + , m_bColumn(bColumn) +{ + if ( m_pDocument ) + m_pDocument->AddUnoObject( *this); + // FIXME: real implementation of identifier and it's mapping to ranges. + // Reuse ScChartListener? + + // BM: don't use names of named ranges but the UI range strings +// String aStr; +// rRangeList->Format( aStr, SCR_ABS_3D, m_pDocument ); +// m_aIdentifier = ::rtl::OUString( aStr ); + +// m_aIdentifier = ::rtl::OUString::createFromAscii( "ID_"); +// static sal_Int32 nID = 0; +// m_aIdentifier += ::rtl::OUString::valueOf( ++nID); +} + + +ScChart2EmptyDataSequence::~ScChart2EmptyDataSequence() +{ + if ( m_pDocument ) + m_pDocument->RemoveUnoObject( *this); +} + + +void ScChart2EmptyDataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if ( rHint.ISA( SfxSimpleHint ) && + ((const SfxSimpleHint&)rHint).GetId() == SFX_HINT_DYING ) + { + m_pDocument = NULL; + } +} + + +uno::Sequence< uno::Any> SAL_CALL ScChart2EmptyDataSequence::getData() + throw ( uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + return uno::Sequence< uno::Any>(); +} + +// XTextualDataSequence -------------------------------------------------- + +uno::Sequence< rtl::OUString > SAL_CALL ScChart2EmptyDataSequence::getTextualData( ) throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + if ( !m_pDocument) + throw uno::RuntimeException(); + + sal_Int32 nCount = 0; + ScRangePtr p; + + DBG_ASSERT(m_xRanges->Count() == 1, "not handled count of ranges"); + + for ( p = m_xRanges->First(); p; p = m_xRanges->Next()) + { + p->Justify(); + // TODO: handle overlaping ranges? + nCount += m_bColumn ? p->aEnd.Col() - p->aStart.Col() + 1 : p->aEnd.Row() - p->aStart.Row() + 1; + } + uno::Sequence< rtl::OUString > aSeq( nCount); + rtl::OUString* pArr = aSeq.getArray(); + nCount = 0; + for ( p = m_xRanges->First(); p; p = m_xRanges->Next()) + { + if (m_bColumn) + { + for (SCCOL nCol = p->aStart.Col(); nCol <= p->aEnd.Col(); ++nCol) + { + String aString = ScGlobal::GetRscString(STR_COLUMN); + aString += ' '; + ScAddress aPos( nCol, 0, 0 ); + String aColStr; + aPos.Format( aColStr, SCA_VALID_COL, NULL ); + aString += aColStr; + pArr[nCount] = aString; + ++nCount; + } + } + else + { + for (sal_Int32 nRow = p->aStart.Row(); nRow <= p->aEnd.Row(); ++nRow) + { + String aString = ScGlobal::GetRscString(STR_ROW); + aString += ' '; + aString += String::CreateFromInt32( nRow+1 ); + pArr[nCount] = aString; + ++nCount; + } + } + } + return aSeq; +} + +::rtl::OUString SAL_CALL ScChart2EmptyDataSequence::getSourceRangeRepresentation() + throw ( uno::RuntimeException) +{ + ScUnoGuard aGuard; + String aStr; + DBG_ASSERT( m_pDocument, "No Document -> no SourceRangeRepresentation" ); + if( m_pDocument ) + m_xRanges->Format( aStr, SCR_ABS_3D, m_pDocument, m_pDocument->GetAddressConvention() ); + return aStr; +} + +uno::Sequence< ::rtl::OUString > SAL_CALL ScChart2EmptyDataSequence::generateLabel(chart2::data::LabelOrigin /*nOrigin*/) + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + uno::Sequence< ::rtl::OUString > aRet; + return aRet; +} + +::sal_Int32 SAL_CALL ScChart2EmptyDataSequence::getNumberFormatKeyByIndex( ::sal_Int32 /*nIndex*/ ) + throw (lang::IndexOutOfBoundsException, + uno::RuntimeException) +{ + sal_Int32 nResult = 0; + + ScUnoGuard aGuard; + if ( !m_pDocument) + return nResult; + + return nResult; +} + +// XCloneable ================================================================ + +uno::Reference< util::XCloneable > SAL_CALL ScChart2EmptyDataSequence::createClone() + throw (uno::RuntimeException) +{ + ScUnoGuard aGuard; + if (m_xDataProvider.is()) + { + // copy properties + uno::Reference < util::XCloneable > xClone(new ScChart2EmptyDataSequence(m_pDocument, m_xDataProvider, new ScRangeList(*m_xRanges), m_bColumn)); + uno::Reference< beans::XPropertySet > xProp( xClone, uno::UNO_QUERY ); + if( xProp.is()) + { + xProp->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNONAME_ROLE )), + uno::makeAny( m_aRole )); + xProp->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS )), + uno::makeAny( m_bIncludeHiddenCells )); + } + return xClone; + } + return uno::Reference< util::XCloneable >(); +} + +// XModifyBroadcaster ======================================================== + +void SAL_CALL ScChart2EmptyDataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& /*aListener*/ ) + throw (uno::RuntimeException) +{ + // TODO: Implement +} + +void SAL_CALL ScChart2EmptyDataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& /*aListener*/ ) + throw (uno::RuntimeException) +{ + // TODO: Implement +} + +// DataSequence XPropertySet ------------------------------------------------- + +uno::Reference< beans::XPropertySetInfo> SAL_CALL +ScChart2EmptyDataSequence::getPropertySetInfo() throw( uno::RuntimeException) +{ + ScUnoGuard aGuard; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() ); + return aRef; +} + + +void SAL_CALL ScChart2EmptyDataSequence::setPropertyValue( + const ::rtl::OUString& rPropertyName, const uno::Any& rValue) + throw( beans::UnknownPropertyException, + beans::PropertyVetoException, + lang::IllegalArgumentException, + lang::WrappedTargetException, uno::RuntimeException) +{ + if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_ROLE))) + { + if ( !(rValue >>= m_aRole)) + throw lang::IllegalArgumentException(); + } + else if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS))) + { + if ( !(rValue >>= m_bIncludeHiddenCells)) + throw lang::IllegalArgumentException(); + } + else + throw beans::UnknownPropertyException(); + // TODO: support optional properties +} + + +uno::Any SAL_CALL ScChart2EmptyDataSequence::getPropertyValue( + const ::rtl::OUString& rPropertyName) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + uno::Any aRet; + if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_ROLE))) + aRet <<= m_aRole; + else if ( rPropertyName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( SC_UNONAME_INCLUDEHIDDENCELLS))) + aRet <<= m_bIncludeHiddenCells; + else + throw beans::UnknownPropertyException(); + // TODO: support optional properties + return aRet; +} + + +void SAL_CALL ScChart2EmptyDataSequence::addPropertyChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2EmptyDataSequence::removePropertyChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2EmptyDataSequence::addVetoableChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + + +void SAL_CALL ScChart2EmptyDataSequence::removeVetoableChangeListener( + const ::rtl::OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ ) + throw( beans::UnknownPropertyException, + lang::WrappedTargetException, uno::RuntimeException) +{ + // FIXME: real implementation +// throw uno::RuntimeException(); + OSL_ENSURE( false, "Not yet implemented" ); +} + +// XUnoTunnel + +// sal_Int64 SAL_CALL ScChart2EmptyDataSequence::getSomething( +// const uno::Sequence<sal_Int8 >& rId ) throw(uno::RuntimeException) +// { +// if ( rId.getLength() == 16 && +// 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), +// rId.getConstArray(), 16 ) ) +// { +// return (sal_Int64)this; +// } +// return 0; +// } + +// // static +// const uno::Sequence<sal_Int8>& ScChart2EmptyDataSequence::getUnoTunnelId() +// { +// static uno::Sequence<sal_Int8> * pSeq = 0; +// if( !pSeq ) +// { +// osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() ); +// if( !pSeq ) +// { +// static uno::Sequence< sal_Int8 > aSeq( 16 ); +// rtl_createUuid( (sal_uInt8*)aSeq.getArray(), 0, sal_True ); +// pSeq = &aSeq; +// } +// } +// return *pSeq; +// } + +// // static +// ScChart2DataSequence* ScChart2EmptyDataSequence::getImplementation( const uno::Reference<uno::XInterface> xObj ) +// { +// ScChart2DataSequence* pRet = NULL; +// uno::Reference<lang::XUnoTunnel> xUT( xObj, uno::UNO_QUERY ); +// if (xUT.is()) +// pRet = (ScChart2EmptyDataSequence*) xUT->getSomething( getUnoTunnelId() ); +// return pRet; +// } +#endif |