/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the 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 "rangenam.hxx" #include #include #include #include #include #include #include #include #include #include #include #include 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( 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*)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 { void operator() (FormulaToken* p) const { delete p; } }; } struct TokenTable { SCROW mnRowCount; SCCOL mnColCount; vector 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( mnColCount*mnRowCount ), "too much tokens" ); } sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const { DBG_ASSERT( nCol(nCol*mnRowCount + nRow); DBG_ASSERT( maTokens.size()>= static_cast( mnColCount*mnRowCount ), "too few tokens" ); return nRet; } vector* getColRanges(SCCOL nCol) const; vector* getRowRanges(SCROW nRow) const; vector* getAllRanges() const; }; vector* TokenTable::getColRanges(SCCOL nCol) const { if (nCol >= mnColCount) return NULL; if( mnRowCount<=0 ) return NULL; auto_ptr< vector > pTokens(new vector); 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(p->Clone())); ScRefTokenHelper::join(*pTokens, pCopy); } return pTokens.release(); } vector* TokenTable::getRowRanges(SCROW nRow) const { if (nRow >= mnRowCount) return NULL; if( mnColCount<=0 ) return NULL; auto_ptr< vector > pTokens(new vector); 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(p->Clone())); ScRefTokenHelper::join(*pTokens, p2); } return pTokens.release(); } vector* TokenTable::getAllRanges() const { auto_ptr< vector > pTokens(new vector); sal_uInt32 nStop = mnColCount*mnRowCount; for (sal_uInt32 i = 0; i < nStop; i++) { FormulaToken* p = maTokens[i]; if (!p) continue; ScSharedTokenRef p2(static_cast(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* getLeftUpperCornerRanges() const; vector* getAllColHeaderRanges() const; vector* getAllRowHeaderRanges() const; vector* getColHeaderRanges(SCCOL nChartCol) const; vector* getRowHeaderRanges(SCROW nChartRow) const; vector* getDataColRanges(SCCOL nCol) const; vector* 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(rCols.First()); for (SCCOL nCol = 0; !bFoundValues && nCol < nAllColCount; ++nCol) { if (pCol && nCol>=nHeaderColCount) { ScToken* pToken = static_cast(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(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(pCol->Next()); } if(!bFoundValues && nHeaderColCount>0) nHeaderColCount++; } pCol = static_cast(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(rCols.First()); FormulaToken* pToken = static_cast(pCol->First()); for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol) { if (pCol) { pToken = static_cast(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(pCol->Next()); } } pCol = static_cast(rCols.Next()); } } Chart2PositionMap::~Chart2PositionMap() { maLeftUpperCorner.clear(); maColHeaders.clear(); maRowHeaders.clear(); maData.clear(); } vector* Chart2PositionMap::getLeftUpperCornerRanges() const { return maLeftUpperCorner.getAllRanges(); } vector* Chart2PositionMap::getAllColHeaderRanges() const { return maColHeaders.getAllRanges(); } vector* Chart2PositionMap::getAllRowHeaderRanges() const { return maRowHeaders.getAllRanges(); } vector* Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const { return maColHeaders.getColRanges( nCol); } vector* Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const { return maRowHeaders.getRowRanges( nRow); } vector* Chart2PositionMap::getDataColRanges(SCCOL nCol) const { return maData.getColRanges( nCol); } vector* 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& rRefTokens) : mpRefTokens(new vector(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 > mpRefTokens; auto_ptr 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(); } 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::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(n1); if (n2 > nEndCol) nEndCol = static_cast(n2); if (nTmp > nMaxCols) nMaxCols = static_cast(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(n1); if (n2 > nEndRow) nEndRow = static_cast(n2); if (nTmp > nMaxRows) nMaxRows = static_cast(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(nC*nR); const sal_uInt8 nHole = 0; const sal_uInt8 nOccu = 1; const sal_uInt8 nFree = 2; const sal_uInt8 nGlue = 3; vector aCellStates(nCR); for (vector::const_iterator itr = mpRefTokens->begin(), itrEnd = mpRefTokens->end(); itr != itrEnd; ++itr) { ScRefTokenHelper::getDoubleRefDataFromToken(aData, *itr); SCCOL nCol1 = static_cast(aData.Ref1.nCol) - mnStartCol; SCCOL nCol2 = static_cast(aData.Ref2.nCol) - mnStartCol; SCROW nRow1 = static_cast(aData.Ref1.nRow) - mnStartRow; SCROW nRow2 = static_cast(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(); if (mpPositionMap.get()) return; glueState(); bool bNoGlue = (meGlue == GLUETYPE_NONE); auto_ptr pCols(new Table); auto_ptr pNewAddress; auto_ptr
pNewRowTable(new Table); Table* pCol = NULL; SCROW nNoGlueRow = 0; for (vector::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(nTab) << 16) | (bNoGlue ? 0 : static_cast(nCol1)); for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol) { if (bNoGlue || meGlue == GLUETYPE_ROWS) { pCol = static_cast(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(pCols->Get(nInsCol)); } sal_uInt32 nInsRow = static_cast(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(); pNewRowTable.reset(); bool bFillRowHeader = mbRowHeaders; bool bFillColumnHeader = mbColHeaders; SCSIZE nAllColCount = static_cast(pCols->Count()); SCSIZE nAllRowCount = 0; pCol = static_cast(pCols->First()); if (pCol) { if (mbDummyUpperLeft) pCol->Insert(0, NULL); // Dummy fuer Beschriftung nAllRowCount = static_cast(pCol->Count()); } if( nAllColCount!=0 && nAllRowCount!=0 ) { if (bNoGlue) { Table* pFirstCol = static_cast(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(pCols->Next()); pCol; pCol = static_cast(pCols->Next())) pCol->Insert(nKey, NULL); } } } mpPositionMap.reset( new Chart2PositionMap( static_cast(nAllColCount), static_cast(nAllRowCount), bFillRowHeader, bFillColumnHeader, *pCols, mpDoc)); // Destroy all column instances. for (pCol = static_cast(pCols->First()); pCol; pCol = static_cast(pCols->Next())) delete pCol; } // ============================================================================ /** * Function object to create a range string from a token list. */ class Tokens2RangeString : public unary_function { 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 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 { 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 mpRangeStr; ScDocument* mpDoc; sal_Unicode mcRangeSep; sal_Unicode mcAddrSep; bool mbFirst; }; void lcl_convertTokensToString(OUString& rStr, const vector& 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 aTokens; ScRefTokenHelper::compileRangeRepresentation(aTokens, aRangeRepresentation, m_pDocument, m_pDocument->GetGrammar()); return !aTokens.empty(); } namespace { Reference< chart2::data::XLabeledDataSequence > lcl_createLabeledDataSequenceFromTokens( auto_ptr< vector< ScSharedTokenRef > > pValueTokens, auto_ptr< vector< ScSharedTokenRef > > pLabelTokens, ScDocument* pDoc, const Reference< chart2::data::XDataProvider >& xDP, bool bIncludeHiddenCells ) { Reference< chart2::data::XLabeledDataSequence > xResult; bool bHasValues = pValueTokens.get() && !pValueTokens->empty(); bool bHasLabel = pLabelTokens.get() && !pLabelTokens->empty(); if( bHasValues || bHasLabel ) { try { Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); if ( xContext.is() ) { xResult.set( xContext->getServiceManager()->createInstanceWithContext( ::rtl::OUString::createFromAscii( "com.sun.star.chart2.data.LabeledDataSequence" ), xContext ), uno::UNO_QUERY_THROW ); } if ( bHasValues ) { Reference< chart2::data::XDataSequence > xSeq( new ScChart2DataSequence( pDoc, xDP, pValueTokens.release(), bIncludeHiddenCells ) ); xResult->setValues( xSeq ); } if ( bHasLabel ) { Reference< chart2::data::XDataSequence > xLabelSeq( new ScChart2DataSequence( pDoc, xDP, pLabelTokens.release(), bIncludeHiddenCells ) ); xResult->setLabel( xLabelSeq ); } } catch( const uno::Exception& ) { } } return xResult; } //---------------------------------------------------- /** * 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& 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; sal_uInt16 nFileId = 0; String aExtTabName; bool bExternal = false; vector::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 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 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 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< Reference< chart2::data::XLabeledDataSequence > > aSeqs; // Fill Categories if( bCategories ) { auto_ptr< vector > pValueTokens(NULL); if (bOrientCol) pValueTokens.reset(pChartMap->getAllRowHeaderRanges()); else pValueTokens.reset(pChartMap->getAllColHeaderRanges()); auto_ptr< vector > pLabelTokens(NULL); pLabelTokens.reset(pChartMap->getLeftUpperCornerRanges()); Reference< chart2::data::XLabeledDataSequence > xCategories = lcl_createLabeledDataSequenceFromTokens( pValueTokens, pLabelTokens, m_pDocument, this, m_bIncludeHiddenCells ); //ownership of pointers is transferred! if ( xCategories.is() ) { aSeqs.push_back( xCategories ); } } // Fill Serieses (values and label) sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount(); for (sal_Int32 i = 0; i < nCount; ++i) { auto_ptr< vector > pValueTokens(NULL); auto_ptr< vector > pLabelTokens(NULL); if (bOrientCol) { pValueTokens.reset(pChartMap->getDataColRanges(static_cast(i))); pLabelTokens.reset(pChartMap->getColHeaderRanges(static_cast(i))); } else { pValueTokens.reset(pChartMap->getDataRowRanges(static_cast(i))); pLabelTokens.reset(pChartMap->getRowHeaderRanges(static_cast(i))); } Reference< chart2::data::XLabeledDataSequence > xChartSeries = lcl_createLabeledDataSequenceFromTokens( pValueTokens, pLabelTokens, m_pDocument, this, m_bIncludeHiddenCells ); //ownership of pointers is transferred! if ( xChartSeries.is() ) { aSeqs.push_back( xChartSeries ); } } pDS = new ScChart2DataSource(m_pDocument); ::std::list< Reference< chart2::data::XLabeledDataSequence > >::iterator aItr( aSeqs.begin() ); ::std::list< Reference< chart2::data::XLabeledDataSequence > >::iterator aEndItr( aSeqs.end() ); //reorder labeled sequences according to aSequenceMapping ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aSeqVector; while(aItr != aEndItr) { aSeqVector.push_back(*aItr); ++aItr; } ::std::map< sal_Int32, Reference< chart2::data::XLabeledDataSequence > > 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< Reference< chart2::data::XLabeledDataSequence > >::size_type nOldIndex( static_cast< sal_uInt32 >( aSequenceMapping[nNewIndex] ) ); if( nOldIndex < aSeqVector.size() ) { pDS->AddLabeledSequence( aSeqVector[nOldIndex] ); aSeqVector[nOldIndex] = 0; } } ::std::vector< Reference< chart2::data::XLabeledDataSequence > >::iterator aVectorItr( aSeqVector.begin() ); ::std::vector< Reference< chart2::data::XLabeledDataSequence > >::iterator aVectorEndItr( aSeqVector.end() ); while(aVectorItr != aVectorEndItr) { Reference< chart2::data::XLabeledDataSequence > xSeq( *aVectorItr ); if ( xSeq.is() ) { pDS->AddLabeledSequence( xSeq ); } ++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 { public: InsertTabNumber() : mpTabNumList(new list()) { } 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& rList) { mpTabNumList->swap(rList); } private: shared_ptr< list > mpTabNumList; }; class RangeAnalyzer { public: RangeAnalyzer(); void initRangeAnalyzer( const vector& 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& rTokens ) { mnRowCount=0; mnColumnCount=0; mnStartColumn = -1; mnStartRow = -1; mbAmbiguous=false; if( rTokens.empty() ) { mbEmpty=true; return; } mbEmpty=false; vector::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( mnColumnCount, static_cast(abs(r.Ref2.nCol - r.Ref1.nCol)+1) ); mnRowCount = std::max( mnRowCount, static_cast(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( mnColumnCount, 1); mnRowCount = std::max( 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 aAllCategoriesValuesTokens; vector aAllSeriesLabelTokens; chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS; vector 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 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 aTokens; ScRefTokenHelper::compileRangeRepresentation( aTokens, xLabel->getSourceRangeRepresentation(), m_pDocument, m_pDocument->GetGrammar() ); aLabel.initRangeAnalyzer(aTokens); vector::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 aTokens; ScRefTokenHelper::compileRangeRepresentation( aTokens, xValues->getSourceRangeRepresentation(), m_pDocument, m_pDocument->GetGrammar() ); aValues.initRangeAnalyzer(aTokens); vector::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 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 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 aRefTokens; ScRefTokenHelper::compileRangeRepresentation(aRefTokens, aRangeRepresentation, m_pDocument); if (aRefTokens.empty()) // i120962: If haven't get reference, that means aRangeRepresentation is not a simple address, then try formulas { ScRangeName aLocalRangeName(*(m_pDocument->GetRangeName())); sal_uInt16 nCurPos = 0; sal_Bool bFindName = aLocalRangeName.SearchName(aRangeRepresentation, nCurPos); // Find global name first for (SCTAB Scope = 0; Scope < MAXTABCOUNT && !bFindName; Scope++ ) // Find name in sheet scope bFindName = aLocalRangeName.SearchName(aRangeRepresentation, nCurPos, Scope); if (bFindName) { ScRangeData* pData =(ScRangeData*)(aLocalRangeName.At(nCurPos)); ScTokenArray* pArray = pData->GetCode(); sal_uInt16 nLen = pArray->GetLen(); if (!nLen) ; else if (nLen == 1) // range names { pArray->Reset(); const FormulaToken* p = pArray->GetNextReference(); if (p) aRefTokens.push_back( ScSharedTokenRef(static_cast(p->Clone()))); } else // formulas { String aSymbol; pData->GetSymbol(aSymbol, FormulaGrammar::GRAM_ENGLISH); String aFormulaStr('='); aFormulaStr += aSymbol; ScAddress aAddr; ScFormulaCell* pCell = new ScFormulaCell(m_pDocument, aAddr, aFormulaStr, FormulaGrammar::GRAM_ENGLISH); pCell->Interpret(); if (pCell->GetValidRefToken()) { aRefTokens.push_back( ScSharedTokenRef(static_cast(pCell->GetValidRefToken()->Clone()))); } DELETEZ( pCell ); } } } if (aRefTokens.empty()) return xResult; // ScChart2DataSequence manages the life cycle of pRefTokens. vector* pRefTokens = new vector(); 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 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; // #118840# Only interpret range string when the ScDocument is not just used // temporary (e.g. for transporting a chart over the clipboard). In that case, the local // cell data would be invalid; despite the fact that a 'Sheet1' exists (just because // it's the default) if(!m_pDocument->IsTemporary()) { 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 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); } // 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* 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::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 aHiddenValues; sal_Int32 nDataCount = 0; sal_Int32 nHiddenValueCount = 0; for (vector::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(pCell); sal_uInt16 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::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(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(nC); SCROW nRow = aRange.aStart.Row() + static_cast(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(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& rFileIds = m_pExtRefListener->getAllFileIds(); hash_set::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(); } void ScChart2DataSequence::CopyData(const ScChart2DataSequence& r) { if (!m_pDocument) { DBG_ERROR("document instance is NULL!?"); return; } list 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(*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& rFileIds = r.m_pExtRefListener->getAllFileIds(); hash_set::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 ) ) { sal_uLong nId = static_cast(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 ( sal_uInt16 n=0; nAddUnoListenerCall( *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()); vector::const_iterator itrBeg = m_pTokens->begin(), itrEnd = m_pTokens->end(); for (vector::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(aRanges.Count()), "range list and range index list have different sizes."); auto_ptr 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(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(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& 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::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 aSeq(nCount); double* pArr = aSeq.getArray(); ::std::list::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 aSeq(nCount); rtl::OUString* pArr = aSeq.getArray(); ::std::list::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 { 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 { public: GenerateLabelStrings(sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) : mpLabels(new Sequence(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 getLabels() const { return *mpLabels; } private: GenerateLabelStrings(); // disabled shared_ptr< Sequence > 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(); // 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(); } // 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 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 xSpreadDoc( lcl_GetSpreadSheetDocument( m_pDocument )); if (!xSpreadDoc.is()) return nResult; uno::Reference 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 > pTokensNew; if (m_pTokens.get()) { // Clone tokens. pTokensNew.reset(new vector); pTokensNew->reserve(m_pTokens->size()); vector::const_iterator itr = m_pTokens->begin(), itrEnd = m_pTokens->end(); for (; itr != itrEnd; ++itr) { ScSharedTokenRef p(static_cast((*itr)->Clone())); pTokensNew->push_back(p); } } auto_ptr 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 *pObj = new uno::Reference( 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::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 sal_uInt16 nCount = m_aValueListeners.Count(); for ( sal_uInt16 n=nCount; n--; ) { uno::Reference *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 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& 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& ScChart2DataSequence::getUnoTunnelId() // { // static uno::Sequence * 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 xObj ) // { // ScChart2DataSequence* pRet = NULL; // uno::Reference 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 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& 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& ScChart2EmptyDataSequence::getUnoTunnelId() // { // static uno::Sequence * 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 xObj ) // { // ScChart2DataSequence* pRet = NULL; // uno::Reference xUT( xObj, uno::UNO_QUERY ); // if (xUT.is()) // pRet = (ScChart2EmptyDataSequence*) xUT->getSomething( getUnoTunnelId() ); // return pRet; // } #endif