From 9cddf9da7fb256418e1bc3b4719abb55e3b0604c Mon Sep 17 00:00:00 2001 From: Vladimir Glazounov Date: Tue, 22 May 2007 15:33:44 +0000 Subject: INTEGRATION: CWS chart2mst3 (1.1.2); FILE ADDED 2007/05/15 06:07:20 bm 1.1.2.51: #i77020# fixed crash due to strange uint <-> int conversions 2007/04/27 14:16:16 bm 1.1.2.50: RESYNC: warnings removed as preparation for warning==error 2007/04/20 11:58:07 tl 1.1.2.49: #i75825# checking for FirstCellAsLabel in detectArguments 2007/03/27 08:55:28 bm 1.1.2.48: #i75058# createDataSource: check if the source range passed here is too large for the table 2007/03/02 12:54:41 bm 1.1.2.47: #i75058# throw IllegalArgumentException instead of RuntimeException when createDataSequenceByRangeRepresentation fails 2007/02/16 15:15:16 tl 1.1.2.46: #i74133# possible crash fixed 2007/02/16 10:14:38 tl 1.1.2.45: #i74133# possible crash fixed 2007/02/12 17:20:31 bm 1.1.2.44: calling an interface method that throws a DisposedException in the DTOR is no good idea 2007/02/09 21:56:35 iha 1.1.2.43: resync m195 -> m202 - missing pch 2007/02/02 16:15:37 tl 1.1.2.42: #i74133# introduce DisposeExceptions and relaxed consistency checks in detectArguments 2007/02/02 15:34:12 tl 1.1.2.41: #i74133# introduce DisposeExceptions and relaxed consistency checks in detectArguments 2007/02/02 11:24:03 tl 1.1.2.40: #i55007# automatic chart update 2007/01/29 14:06:56 tl 1.1.2.39: #i55007#, #i73791# automatic update for charts, and update charts for undo-actions 2006/12/13 12:31:03 tl 1.1.2.38: #i71244# update charts in writer 2006/12/13 11:07:28 tl 1.1.2.37: #i71244# update charts in writer 2006/12/12 16:43:14 tl 1.1.2.36: #i71244# update charts in writer 2006/12/11 15:23:39 tl 1.1.2.35: #i71244# update charts in writer 2006/12/07 11:47:06 tl 1.1.2.34: #i71244# update chart in writer 2006/12/07 11:20:47 tl 1.1.2.33: #i71244# update chart in writer 2006/12/06 09:27:23 bm 1.1.2.32: syntax error in preprocessor directive 2006/12/05 16:32:07 iha 1.1.2.31: correct cloning of SwChartLabeledDataSequence 2006/12/05 13:35:07 iha 1.1.2.30: #i72002# #i71677# crashes with xy charts (fix from tl) 2006/11/01 17:40:10 iha 1.1.2.29: changed resource STR_CHART2_COL_LABEL_TEXT according to changed spec 2006/05/08 08:47:47 bm 1.1.2.28: XDataSequence: chart data listener removed. Instead XModifyBroadcaster must be implemented in the future 2006/01/13 12:39:58 tl 1.1.2.27: #i30052# added string resources 2005/12/06 12:46:25 tl 1.1.2.26: #58464# generateLabel fixed as told 2005/12/06 11:32:04 tl 1.1.2.25: #58464# createDataSource improved to handle bad exported SO8 charts (shifted cell ranges) 2005/12/06 09:28:21 tl 1.1.2.24: #i30052# createDataSource for rows fixed; DBG only implementation of 'generateLabel' 2005/11/30 10:59:14 bm 1.1.2.23: ^M character in code (breaking Solaris build) 2005/11/29 18:15:47 iha 1.1.2.22: #i58464# work around wrong writer ranges in file 2005/11/24 10:51:56 tl 1.1.2.21: #i30052# implementing chart2 API 2005/11/23 07:24:25 tl 1.1.2.20: #i30052# chart2 API 2005/11/22 14:19:23 tl 1.1.2.19: #i30052# chart2 API 2005/11/15 10:22:40 tl 1.1.2.18: #i30052# chart2 support 2005/11/15 09:10:55 tl 1.1.2.17: #i30052# chart2 support 2005/10/11 10:07:04 bm 1.1.2.16: license header change 2005/09/09 15:05:50 bm 1.1.2.15: +XRangeXMLConversion interface stub 2005/09/09 12:37:50 bm 1.1.2.14: return a non-empty string as CellRepresentation to allow newly created files to be identified as ones that get their data from writer 2005/09/08 12:36:53 iha 1.1.2.13: added method generateLabel 2005/09/08 11:49:25 iha 1.1.2.12: set number formatter independent from dataprovider thus also a chart with own data has the numberformatter of the parent 2005/09/01 13:50:48 bm 1.1.2.11: set table cursor to 0 if table or cursor has been deleted, lock solar mutex in DTOR 2005/08/05 13:59:17 bm 1.1.2.10: -XDataSequence::generateLabel 2005/08/05 09:19:18 bm 1.1.2.9: return a valid number formatter 2005/07/26 14:04:35 bm 1.1.2.8: chart2 UNO API changes 2005/05/12 14:27:41 bm 1.1.2.7: API changes in XDataProvider 2005/05/09 09:55:48 bm 1.1.2.6: moved chart2 API to data namespace 2005/05/03 16:35:55 bm 1.1.2.5: removed identifiers from XSequences. XSequences are no longer stored in the XDataProvider 2004/06/29 11:07:50 bm 1.1.2.4: XDataProvider: getRangeRepresentationByRangeIdentifiers -> mergeRangeRepresentations 2004/06/14 07:07:29 bm 1.1.2.3: tools string's length method is Len() not getLength() 2004/06/10 16:58:43 bm 1.1.2.2: createClone(): duplicate variable names (error with solaris compiler) 2004/06/10 11:54:55 tl 1.1.2.1: #i30052# chart2 support --- sw/source/core/unocore/unochart.cxx | 2703 +++++++++++++++++++++++++++++++++++ 1 file changed, 2703 insertions(+) create mode 100644 sw/source/core/unocore/unochart.cxx diff --git a/sw/source/core/unocore/unochart.cxx b/sw/source/core/unocore/unochart.cxx new file mode 100644 index 000000000000..4434878455b5 --- /dev/null +++ b/sw/source/core/unocore/unochart.cxx @@ -0,0 +1,2703 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: unochart.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: vg $ $Date: 2007-05-22 16:33:44 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +#include +#include + + +#ifndef _COM_SUN_STAR_CHART_DATAROWSOURCE_HPP_ +#include +#endif +#ifndef _COM_SUN_STAR_CHART_DATAROWSOURCE_HPP_ +#include +#endif + +#ifndef _CPPUHELPER_INTERFACECONTAINER_HXX_ +#include +#endif + +#ifndef _VOS_MUTEX_HXX_ //autogen +#include +#endif +#ifndef _OSL_MUTEX_HXX_ +#include +#endif +#ifndef _SV_SVAPP_HXX //autogen +#include +#endif + +#ifndef _ZFORLIST_HXX +#include // SvNumberFormatter +#endif + +#include + +#include + +#ifndef _UNOCHART_HXX +#include +#endif +#ifndef _SWTABLE_HXX +#include +#endif +#ifndef _UNOPRNMS_HXX +#include +#endif +#ifndef _UNOMAP_HXX +#include +#endif +#ifndef _UNOOBJ_HXX +#include +#endif +#ifndef _UNOCRSR_HXX +#include +#endif +#ifndef _UNOTBL_HXX +#include +#endif +#ifndef _DOC_HXX +#include +#endif +#ifndef _FRMFMT_HXX +#include +#endif +#ifndef _SWDOCSH_HXX +#include +#endif +#ifndef _NDOLE_HXX +#include +#endif +#ifndef _SWTABLE_HXX +#include +#endif + +#ifndef _SWTYPES_HXX +#include +#endif +#ifndef _UNOCORE_HRC +#include +#endif + +#include + +#define SN_DATA_PROVIDER "com.sun.star.chart2.data.DataProvider" +#define SN_DATA_SOURCE "com.sun.star.chart2.data.DataSource" +#define SN_DATA_SEQUENCE "com.sun.star.chart2.data.DataSequence" +#define SN_LABELED_DATA_SEQUENCE "com.sun.star.chart2.data.LabeledDataSequence" + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::chart2::data; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::util; +using namespace ::rtl; + +// from unotbl.cxx +extern void lcl_GetRowCol(const String& rCellName, sal_uInt16& rRow, sal_uInt16& rCol); +extern void lcl_GetCellPosition( const String &rCellName, sal_Int16 &rColumn, sal_Int16 &rRow); +extern String lcl_GetCellName(sal_Int16 nColumn, sal_Int16 nRow); +extern int lcl_CompareCellsByColFirst( const String &rCellName1, const String &rCellName2 ); +extern int lcl_CompareCellsByRowFirst( const String &rCellName1, const String &rCellName2 ); +extern int lcl_CompareCellRanges( + const String &rRange1StartCell, const String &rRange1EndCell, + const String &rRange2StartCell, const String &rRange2EndCell, + sal_Bool bCmpColsFirst ); +extern void lcl_NormalizeRange( String &rCell1, String &rCell2 ); + +////////////////////////////////////////////////////////////////////// + +SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) : + pDoc( pDocument ) +{ + aUnlockTimer.SetTimeout( 1500 ); + aUnlockTimer.SetTimeoutHdl( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts )); +} + + +SwChartLockController_Helper::~SwChartLockController_Helper() +{ + if (pDoc) // still connected? + Disconnect(); +} + + +void SwChartLockController_Helper::StartOrContinueLocking() +{ + if (!bIsLocked) + LockAllCharts(); + aUnlockTimer.Start(); // start or continue time of locking +} + + +void SwChartLockController_Helper::Disconnect() +{ + aUnlockTimer.Stop(); + UnlockAllCharts(); + pDoc = 0; +} + + +void SwChartLockController_Helper::LockUnlockAllCharts( sal_Bool bLock ) +{ + if (!pDoc) + return; + + const SwFrmFmts& rTblFmts = *pDoc->GetTblFrmFmts(); + for( USHORT n = 0; n < rTblFmts.Count(); ++n ) + { + SwTable* pTmpTbl; + const SwTableNode* pTblNd; + SwFrmFmt* pFmt = rTblFmts[ n ]; + + if( 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) && + 0 != ( pTblNd = pTmpTbl->GetTableNode() ) && + pTblNd->GetNodes().IsDocNodes() ) + { + uno::Reference< frame::XModel > xRes; + + String aName( pTmpTbl->GetFrmFmt()->GetName() ); + SwOLENode *pONd; + SwStartNode *pStNd; + SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 ); + while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) ) + { + aIdx++; + if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) && + pONd->GetChartTblName().Len() > 0 /* is chart object? */) + { + uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef(); + if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) ) + { + xRes = uno::Reference < frame::XModel >( xIP->getComponent(), uno::UNO_QUERY ); + if (xRes.is()) + { + if (bLock) + xRes->lockControllers(); + else + xRes->unlockControllers(); + } + } + } + aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 ); + } + } + } + + bIsLocked = bLock; +} + + +IMPL_LINK( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, /*pTimer*/ ) +{ + UnlockAllCharts(); + return 0; +} + + +////////////////////////////////////////////////////////////////////// + +static osl::Mutex & GetChartMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} + + +static void LaunchModifiedEvent( + ::cppu::OInterfaceContainerHelper &rICH, + const Reference< XInterface > &rxI ) +{ + EventObject aEvtObj( rxI ); + cppu::OInterfaceIteratorHelper aIt( rICH ); + while (aIt.hasMoreElements()) + { + Reference< XModifyListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->modified( aEvtObj ); + } +} + +////////////////////////////////////////////////////////////////////// + +static sal_Bool FillRangeDescriptor( + SwRangeDescriptor &rDesc, + const String &rCellRangeName ) +{ + String aTLName( rCellRangeName.GetToken(0, ':') ); // name of top left cell + String aBRName( rCellRangeName.GetToken(1, ':') ); // name of bottom right cell + if(!aTLName.Len() || !aBRName.Len()) + return sal_False; + + rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = static_cast< sal_uInt16 >( -1 ); + lcl_GetRowCol( aTLName, rDesc.nTop, rDesc.nLeft ); + lcl_GetRowCol( aBRName, rDesc.nBottom, rDesc.nRight ); + rDesc.Normalize(); + DBG_ASSERT( rDesc.nTop != static_cast< sal_uInt16 >( -1 ) && + rDesc.nLeft != static_cast< sal_uInt16 >( -1 ) && + rDesc.nBottom != static_cast< sal_uInt16 >( -1 ) && + rDesc.nRight != static_cast< sal_uInt16 >( -1 ), + "failed to get range descriptor" ); + DBG_ASSERT( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight, + "invalid range descriptor"); + return sal_True; +} + + +static String GetCellRangeName( SwFrmFmt &rTblFmt, SwUnoCrsr &rTblCrsr ) +{ + String aRes; + + //!! see also SwXTextTableCursor::getRangeName + + SwUnoTableCrsr* pUnoTblCrsr = rTblCrsr; +#if OSL_DEBUG_LEVEL > 1 + if (!pUnoTblCrsr) + return String(); +#endif + pUnoTblCrsr->MakeBoxSels(); + + const SwStartNode* pStart; + const SwTableBox* pStartBox = 0; + const SwTableBox* pEndBox = 0; + + pStart = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode(); + const SwTable* pTable = SwTable::FindTable( &rTblFmt ); + pEndBox = pTable->GetTblBox( pStart->GetIndex()); + aRes = pEndBox->GetName(); + + if(pUnoTblCrsr->HasMark()) + { + pStart = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode(); + pStartBox = pTable->GetTblBox( pStart->GetIndex()); + + } + DBG_ASSERT( pStartBox, "start box not found" ); + DBG_ASSERT( pEndBox, "end box not found" ); + if (pStart) + { + // need to switch start and end? + if (*pUnoTblCrsr->GetPoint() < *pUnoTblCrsr->GetMark()) + { + const SwTableBox* pTmpBox = pStartBox; + pStartBox = pEndBox; + pEndBox = pTmpBox; + } + + aRes = pStartBox->GetName(); + aRes += (sal_Unicode)':'; + if (pEndBox) + aRes += pEndBox->GetName(); + else + aRes += pStartBox->GetName(); + } + + return aRes; +} + + +static String GetRangeRepFromTableAndCells( const String &rTableName, + const String &rStartCell, const String &rEndCell, + sal_Bool bForceEndCellName ) +{ + DBG_ASSERT( rTableName.Len(), "table name missing" ); + DBG_ASSERT( rStartCell.Len(), "cell name missing" ); + String aRes( rTableName ); + aRes += (sal_Unicode) '.'; + aRes += rStartCell; + + if (rEndCell.Len()) + { + aRes += (sal_Unicode) ':'; + aRes += rEndCell; + } + else if (bForceEndCellName) + { + aRes += (sal_Unicode) ':'; + aRes += rStartCell; + } + + return aRes; +} + + +static sal_Bool GetTableAndCellsFromRangeRep( + const OUString &rRangeRepresentation, + String &rTblName, + String &rStartCell, + String &rEndCell, + sal_Bool bSortStartEndCells = sal_True ) +{ + // parse range representation for table name and cell/range names + // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2" + String aTblName; // table name + OUString aRange; // cell range + String aStartCell; // name of top left cell + String aEndCell; // name of bottom right cell + sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' ); + if (nIdx >= 0) + { + aTblName = rRangeRepresentation.copy( 0, nIdx ); + aRange = rRangeRepresentation.copy( nIdx + 1 ); + sal_Int32 nPos = aRange.indexOf( ':' ); + if (nPos >= 0) // a cell-range like "Table1.A2:D4" + { + aStartCell = aRange.copy( 0, nPos ); + aEndCell = aRange.copy( nPos + 1 ); + + // need to switch start and end cell ? + // (does not check for normalization here) + if (bSortStartEndCells && 1 == lcl_CompareCellsByColFirst( aStartCell, aEndCell )) + { + String aTmp( aStartCell ); + aStartCell = aEndCell; + aEndCell = aTmp; + } + } + else // a single cell like in "Table1.B3" + { + aStartCell = aEndCell = aRange; + } + } + + sal_Bool bSuccess = aTblName.Len() != 0 && + aStartCell.Len() != 0 && aEndCell.Len() != 0; + if (bSuccess) + { + rTblName = aTblName; + rStartCell = aStartCell; + rEndCell = aEndCell; + } + return bSuccess; +} + + +static void GetTableByName( const SwDoc &rDoc, const String &rTableName, + SwFrmFmt **ppTblFmt, SwTable **ppTable) +{ + SwFrmFmt *pTblFmt = NULL; + + // find frame format of table + //! see SwXTextTables::getByName + sal_uInt16 nCount = rDoc.GetTblFrmFmtCount(sal_True); + for (sal_uInt16 i = 0; i < nCount && !pTblFmt; ++i) + { + SwFrmFmt& rTblFmt = rDoc.GetTblFrmFmt(i, sal_True); + if(rTableName == rTblFmt.GetName()) + pTblFmt = &rTblFmt; + } + + if (ppTblFmt) + *ppTblFmt = pTblFmt; + + if (ppTable) + *ppTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0; +} + + +static void GetFormatAndCreateCursorFromRangeRep( + const SwDoc *pDoc, + const OUString &rRangeRepresentation, // must be a single range (i.e. so called sub-range) + SwFrmFmt **ppTblFmt, // will be set to the table format of the table used in the range representation + SwUnoCrsr **ppUnoCrsr ) // will be set to cursor spanning the cell range + // (cursor will be created!) +{ + String aTblName; // table name + String aStartCell; // name of top left cell + String aEndCell; // name of bottom right cell + sal_Bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation, + aTblName, aStartCell, aEndCell ); + + if (!bNamesFound) + { + if (ppTblFmt) + *ppTblFmt = NULL; + if (ppUnoCrsr) + *ppUnoCrsr = NULL; + } + else + { + SwFrmFmt *pTblFmt = NULL; + + // is the correct table format already provided? + if (*ppTblFmt != NULL && (*ppTblFmt)->GetName() == aTblName) + pTblFmt = *ppTblFmt; + else if (ppTblFmt) + GetTableByName( *pDoc, aTblName, &pTblFmt, NULL ); + + if (ppTblFmt) + *ppTblFmt = pTblFmt; + + if (ppUnoCrsr != NULL) + { + *ppUnoCrsr = NULL; // default result in case of failure + + SwTable *pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0; + // create new SwUnoCrsr spanning the specified range + //! see also SwXTextTable::GetRangeByName + const SwTableBox* pTLBox = pTable ? pTable->GetTblBox( aStartCell ) : 0; + if(pTLBox) + { + // hier muessen die Actions aufgehoben werden + UnoActionRemoveContext aRemoveContext(pTblFmt->GetDoc()); + const SwStartNode* pSttNd = pTLBox->GetSttNd(); + SwPosition aPos(*pSttNd); + // set cursor to top left box of range + SwUnoCrsr* pUnoCrsr = pTblFmt->GetDoc()->CreateUnoCrsr(aPos, sal_True); + pUnoCrsr->Move( fnMoveForward, fnGoNode ); + pUnoCrsr->SetRemainInSection( sal_False ); + const SwTableBox* pBRBox = pTable->GetTblBox( aEndCell ); + if(pBRBox) + { + pUnoCrsr->SetMark(); + pUnoCrsr->GetPoint()->nNode = *pBRBox->GetSttNd(); + pUnoCrsr->Move( fnMoveForward, fnGoNode ); + SwUnoTableCrsr* pCrsr = *pUnoCrsr; + pCrsr->MakeBoxSels(); + + if (ppUnoCrsr) + *ppUnoCrsr = pCrsr; + } + else + { + delete pUnoCrsr; + } + } + } + } +} + + +static sal_Bool GetSubranges( const OUString &rRangeRepresentation, + Sequence< OUString > &rSubRanges, sal_Bool bNormalize ) +{ + sal_Bool bRes = sal_True; + String aRangesStr( rRangeRepresentation ); + xub_StrLen nLen = aRangesStr.GetTokenCount( ';' ); + Sequence< OUString > aRanges( nLen ); + + sal_Int32 nCnt = 0; + if (nLen != 0) + { + OUString *pRanges = aRanges.getArray(); + String aFirstTable; + for ( xub_StrLen i = 0; i < nLen && bRes; ++i) + { + String aRange( aRangesStr.GetToken( i, ';' ) ); + if (aRange.Len()) + { + pRanges[nCnt] = aRange; + + String aTableName, aStartCell, aEndCell; + bRes &= GetTableAndCellsFromRangeRep( aRange, + aTableName, aStartCell, aEndCell ); + + if (bNormalize) + { + lcl_NormalizeRange( aStartCell, aEndCell ); + pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName, + aStartCell, aEndCell, sal_True ); + } + + // make sure to use only a single table + if (nCnt == 0) + aFirstTable = aTableName; + else + bRes &= aFirstTable == aTableName; + + ++nCnt; + } + } + } + aRanges.realloc( nCnt ); + + rSubRanges = aRanges; + return bRes; +} + + +static void SortSubranges( Sequence< OUString > &rSubRanges, sal_Bool bCmpByColumn ) +{ + sal_Int32 nLen = rSubRanges.getLength(); + OUString *pSubRanges = rSubRanges.getArray(); + + String aSmallestTblName; + String aSmallestStartCell; + String aSmallestEndCell; + + for (sal_Int32 i = 0; i < nLen; ++i) + { + sal_Int32 nIdxOfSmallest = i; + GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest], + aSmallestTblName, aSmallestStartCell, aSmallestEndCell ); + if (aSmallestEndCell.Len() == 0) + aSmallestEndCell = aSmallestStartCell; + + for (sal_Int32 k = i+1; k < nLen; ++k) + { + // get cell names for sub range + String aTblName; + String aStartCell; + String aEndCell; + GetTableAndCellsFromRangeRep( pSubRanges[k], + aTblName, aStartCell, aEndCell ); + if (aEndCell.Len() == 0) + aEndCell = aStartCell; + + // compare cell ranges ( is the new one smaller? ) + if (-1 == lcl_CompareCellRanges( aStartCell, aEndCell, + aSmallestStartCell, aSmallestEndCell, bCmpByColumn )) + { + nIdxOfSmallest = k; + aSmallestTblName = aTblName; + aSmallestStartCell = aStartCell; + aSmallestEndCell = aEndCell; + } + } + + // move smallest element to the start of the not sorted area + OUString aTmp( pSubRanges[ nIdxOfSmallest ] ); + pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ]; + pSubRanges[ i ] = aTmp; + } +} + +////////////////////////////////////////////////////////////////////// + +SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) : + aEvtListeners( GetChartMutex() ), + pDoc( pSwDoc ) +{ + bDisposed = sal_False; +} + + +SwChartDataProvider::~SwChartDataProvider() +{ +} + + +Reference< XDataSource > SAL_CALL SwChartDataProvider::createDataSource( + const Sequence< PropertyValue >& rArguments ) + throw (lang::IllegalArgumentException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Reference< XDataSource > xRes; + + if (!pDoc) + throw RuntimeException(); + + // get arguments + OUString aRangeRepresentation; + Sequence< sal_Int32 > aSequenceMapping; + sal_Bool bFirstIsLabel = sal_False; + sal_Bool bDtaSrcIsColumns = sal_True; // true : DataSource will be sequence of columns + // false: DataSource will be sequence of rows + OUString aChartOleObjectName;//work around wrong writer ranges ( see Issue 58464 ) + sal_Int32 nArgs = rArguments.getLength(); + DBG_ASSERT( nArgs != 0, "no properties provided" ); + if (nArgs == 0) + return xRes; + const PropertyValue *pArg = rArguments.getConstArray(); + for (sal_Int32 i = 0; i < nArgs; ++i) + { + if (pArg[i].Name.equalsAscii( "DataRowSource" )) + { + chart::ChartDataRowSource eSource; + if (!(pArg[i].Value >>= eSource)) + { + sal_Int32 nTmp; + if (!(pArg[i].Value >>= nTmp)) + throw IllegalArgumentException(); + eSource = static_cast< chart::ChartDataRowSource >( nTmp ); + } + bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS; + } + else if (pArg[i].Name.equalsAscii( "FirstCellAsLabel" )) + { + if (!(pArg[i].Value >>= bFirstIsLabel)) + throw lang::IllegalArgumentException(); + } + else if (pArg[i].Name.equalsAscii( "CellRangeRepresentation" )) + { + if (!(pArg[i].Value >>= aRangeRepresentation)) + throw lang::IllegalArgumentException(); + } + else if (pArg[i].Name.equalsAscii( "SequenceMapping" )) + { + if (!(pArg[i].Value >>= aSequenceMapping)) + throw lang::IllegalArgumentException(); + } + else if (pArg[i].Name.equalsAscii( "ChartOleObjectName" )) + { + if (!(pArg[i].Value >>= aChartOleObjectName)) + throw lang::IllegalArgumentException(); + } + } + +#if OSL_DEBUG_LEVEL > 1 + { + static bool bTest = sal_False; + if (bTest) + { + OUString aTmp( OUString::createFromAscii("BarChart_sw5") ); + aTmp += aRangeRepresentation; + aRangeRepresentation = aTmp; + //createDataSequenceByRangeRepresentation( aRangeRepresentation ); + } + } +#endif + + Sequence< OUString > aSubRanges; + // get sub-ranges and check that they all are from the very same table + sal_Bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True ); + + if (!bOk && pDoc && aChartOleObjectName.getLength() ) + { + //try to correct the range here + //work around wrong writer ranges ( see Issue 58464 ) + String aChartTableName; + + const SwNodes& rNodes = pDoc->GetNodes(); + for( ULONG nN = rNodes.Count(); nN--; ) + { + SwNodePtr pNode = rNodes[nN]; + if( !pNode ) + continue; + const SwOLENode* pOleNode = pNode->GetOLENode(); + if( !pOleNode ) + continue; + const SwOLEObj& rOObj = pOleNode->GetOLEObj(); + if( aChartOleObjectName.equals( rOObj.GetCurrentPersistName() ) ) + { + aChartTableName = pOleNode->GetChartTblName(); + break; + } + } + + if( aChartTableName.Len() ) + { + //the wrong range is still shifted one row down + //thus the first row is missing and an invalid row at the end is added. + //Therefore we need to shift the range one row up + SwRangeDescriptor aDesc; + if (aRangeRepresentation.getLength() == 0) + return xRes; // we cant handle this thus returning an empty references + aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left + FillRangeDescriptor( aDesc, aRangeRepresentation ); + aDesc.Normalize(); + if (aDesc.nTop <= 0) // no chance to shift the range one row up? + return xRes; // we cant handle this thus returning an empty references + aDesc.nTop -= 1; + aDesc.nBottom -= 1; + + String aNewStartCell( lcl_GetCellName( aDesc.nLeft, aDesc.nTop ) ); + String aNewEndCell( lcl_GetCellName( aDesc.nRight, aDesc.nBottom ) ); + aRangeRepresentation = GetRangeRepFromTableAndCells( + aChartTableName, aNewStartCell, aNewEndCell, sal_True ); + bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True ); + } + } + if (!bOk) // different tables used, or incorrect range specifiers + throw lang::IllegalArgumentException(); + + SortSubranges( aSubRanges, bDtaSrcIsColumns ); + const OUString *pSubRanges = aSubRanges.getConstArray(); +#if OSL_DEBUG_LEVEL > 1 + { + sal_Int32 nSR = aSubRanges.getLength(); + OUString *pSR = aSubRanges.getArray(); + OUString aRg; + for (sal_Int32 i = 0; i < nSR; ++i) + { + aRg = pSR[i]; + } + } +#endif + + // get table format for that single table from above + SwFrmFmt *pTblFmt = 0; // pointer to table format + if (aSubRanges.getLength() > 0) + GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTblFmt, NULL ); + if (!pTblFmt) + throw lang::IllegalArgumentException(); + + if(pTblFmt) + { + SwTable* pTable = SwTable::FindTable( pTblFmt ); + if(pTable->IsTblComplex()) + return xRes; // we cant handle this thus returning an empty references + else + { + // get a character map in the size of the table to mark + // all the ranges to use in + sal_Int16 nRows = pTable->GetTabLines().Count(); + sal_Int16 nCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count(); + std::vector< std::vector< sal_Char > > aMap( nRows ); + for (sal_Int16 i = 0; i < nRows; ++i) + aMap[i].resize( nCols ); + + // iterate over subranges and mark used cells in above map + //!! by proceeding this way we automatically get rid of + //!! multiple listed or overlapping cell ranges which should + //!! just be ignored silently + sal_Int32 nSubRanges = aSubRanges.getLength(); + for (sal_Int32 i = 0; i < nSubRanges; ++i) + { + String aTblName, aStartCell, aEndCell; + sal_Bool bOk = GetTableAndCellsFromRangeRep( + pSubRanges[i], aTblName, aStartCell, aEndCell ); + DBG_ASSERT( bOk, "failed to get table and start/end cells" ); + + sal_Int16 nStartRow, nStartCol, nEndRow, nEndCol; + { + sal_uInt16 nTmpStartRow, nTmpStartCol, nTmpEndRow, nTmpEndCol; + lcl_GetRowCol( aStartCell, nTmpStartRow, nTmpStartCol ); + lcl_GetRowCol( aEndCell, nTmpEndRow, nTmpEndCol ); + nStartRow = static_cast< sal_Int16 >( nTmpStartRow ); + nEndRow = static_cast< sal_Int16 >( nTmpEndRow ); + nStartCol = static_cast< sal_Int16 >( nTmpStartCol ); + nEndCol = static_cast< sal_Int16 >( nTmpEndCol ); + } + DBG_ASSERT( nStartRow <= nEndRow && nStartCol <= nEndCol, + "cell range not normalized"); + // test if the ranges span more than the available cells + if( nStartRow < 0 || nEndRow >= nRows || + nStartCol < 0 || nEndCol >= nCols ) + { + throw lang::IllegalArgumentException(); + } + for (sal_Int16 k1 = nStartRow; k1 <= nEndRow; ++k1) + { + for (sal_Int16 k2 = nStartCol; k2 <= nEndCol; ++k2) + aMap[k1][k2] = 'x'; + } + } + + // + // find label and data sequences to use + // + sal_Int16 oi; // outer index (slower changing index) + sal_Int16 ii; // inner index (faster changing index) + sal_Int16 oiEnd = bDtaSrcIsColumns ? nCols : nRows; + sal_Int16 iiEnd = bDtaSrcIsColumns ? nRows : nCols; + std::vector< sal_Int16 > aLabelIdx( oiEnd ); + std::vector< sal_Int16 > aDataStartIdx( oiEnd ); + std::vector< sal_Int16 > aDataLen( oiEnd ); + for (oi = 0; oi < oiEnd; ++oi) + { + aLabelIdx[oi] = -1; + aDataStartIdx[oi] = -1; + aDataLen[oi] = 0; + } + // + for (oi = 0; oi < oiEnd; ++oi) + { + ii = 0; + while (ii < iiEnd) + { + sal_Char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]; + + // label should be used but is not yet found? + if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1) + { + aLabelIdx[oi] = ii; + rChar = 'L'; // setting a different char for labels here + // makes the test for the data sequence below + // easier + } + + // find data sequence + if (rChar == 'x' && aDataStartIdx[oi] == -1) + { + aDataStartIdx[oi] = ii; + + // get length of data sequence + sal_Int16 nL = 0; + sal_Char c; + while (ii< iiEnd && 'x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii])) + { + ++nL; ++ii; + } + aDataLen[oi] = nL; + + // check that there is no other seperate sequence of data + // to be found because that is not supported + while (ii < iiEnd) + { + if ('x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii])) + throw lang::IllegalArgumentException(); + ++ii; + } + } + else + ++ii; + } + } + + // make some other consistency checks while calculating + // the number of XLabeledDataSequence to build: + // - labels should always be used or not at all + // - the data sequences should have equal non-zero length + sal_Int32 nNumLDS = 0; + if (oiEnd > 0) + { + sal_Int16 nFirstSeqLen = 0; + sal_Int16 nFirstSeqLabelIdx = -1; + for (oi = 0; oi < oiEnd; ++oi) + { + sal_Bool bFirstFound = sal_False; + // row/col used at all? + if (aDataStartIdx[oi] != -1 && + (!bFirstIsLabel || aLabelIdx[oi] != -1)) + { + ++nNumLDS; + if (!bFirstFound) + { + nFirstSeqLen = aDataLen[oi]; + nFirstSeqLabelIdx = aLabelIdx[oi]; + bFirstFound = sal_True; + } + else + { + if (nFirstSeqLen != aDataLen[oi] || + nFirstSeqLabelIdx != aLabelIdx[oi]) + throw lang::IllegalArgumentException(); + } + } + } + } + if (nNumLDS == 0) + throw uno::RuntimeException(); + + // create data source from found label and data sequences + Sequence< Reference< XDataSequence > > aLabelSeqs( nNumLDS ); + Reference< XDataSequence > *pLabelSeqs = aLabelSeqs.getArray(); + Sequence< Reference< XDataSequence > > aDataSeqs( nNumLDS ); + Reference< XDataSequence > *pDataSeqs = aDataSeqs.getArray(); + sal_Int32 nSeqsIdx = 0; + for (oi = 0; oi < oiEnd; ++oi) + { + // row/col not used? (see if-statement above where nNumLDS was counted) + if (!(aDataStartIdx[oi] != -1 && + (!bFirstIsLabel || aLabelIdx[oi] != -1))) + continue; + + // get cell ranges for label and data + // + SwRangeDescriptor aLabelDesc; + SwRangeDescriptor aDataDesc; + if (bDtaSrcIsColumns) // use columns + { + aLabelDesc.nTop = aLabelIdx[oi]; + aLabelDesc.nLeft = oi; + aLabelDesc.nBottom = aLabelDesc.nTop; + aLabelDesc.nRight = oi; + + aDataDesc.nTop = aDataStartIdx[oi]; + aDataDesc.nLeft = oi; + aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1; + aDataDesc.nRight = oi; + } + else // use rows + { + aLabelDesc.nTop = oi; + aLabelDesc.nLeft = aLabelIdx[oi]; + aLabelDesc.nBottom = oi; + aLabelDesc.nRight = aLabelDesc.nLeft; + + aDataDesc.nTop = oi; + aDataDesc.nLeft = aDataStartIdx[oi]; + aDataDesc.nBottom = oi; + aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1; + } + String aBaseName( pTblFmt->GetName() ); + aBaseName += '.'; + // + String aLabelRange; + if (aLabelIdx[oi] != -1) + { + aLabelRange += aBaseName; + aLabelRange += lcl_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop ); + aLabelRange += ':'; + aLabelRange += lcl_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom ); + } + // + String aDataRange; + if (aDataStartIdx[oi] != -1) + { + aDataRange += aBaseName; + aDataRange += lcl_GetCellName( aDataDesc.nLeft, aDataDesc.nTop ); + aDataRange += ':'; + aDataRange += lcl_GetCellName( aDataDesc.nRight, aDataDesc.nBottom ); + } + + // get cursors spanning the cell ranges for label and data + SwUnoCrsr *pLabelUnoCrsr = 0; + SwUnoCrsr *pDataUnoCrsr = 0; + GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTblFmt, &pLabelUnoCrsr); + GetFormatAndCreateCursorFromRangeRep( pDoc, aDataRange, &pTblFmt, &pDataUnoCrsr); + + // create XDataSequence's from cursors + if (pLabelUnoCrsr) + pLabelSeqs[ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pLabelUnoCrsr ); + DBG_ASSERT( pDataUnoCrsr, "pointer to data sequence missing" ); + if (pDataUnoCrsr) + pDataSeqs [ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pDataUnoCrsr ); + if (pLabelUnoCrsr || pDataUnoCrsr) + ++nSeqsIdx; + } + DBG_ASSERT( nSeqsIdx == nNumLDS, + "mismatch between sequence size and num,ber of entries" ); + + // build data source from data and label sequences + Sequence< Reference< XLabeledDataSequence > > aLDS( nNumLDS ); + Reference< XLabeledDataSequence > *pLDS = aLDS.getArray(); + for (sal_Int32 i = 0; i < nNumLDS; ++i) + { + SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence; + pLabeledDtaSeq->setLabel( pLabelSeqs[i] ); + pLabeledDtaSeq->setValues( pDataSeqs[i] ); + pLDS[i] = pLabeledDtaSeq; + } + + // apply 'SequenceMapping' if it was provided + sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength(); + if (nSequenceMappingLen) + { + sal_Int32 *pSequenceMapping = aSequenceMapping.getArray(); + Sequence< Reference< XLabeledDataSequence > > aOld_LDS( aLDS ); + Reference< XLabeledDataSequence > *pOld_LDS = aOld_LDS.getArray(); + + sal_Int32 nNewCnt = 0; + for (sal_Int32 i = 0; i < nSequenceMappingLen; ++i) + { + // check that index to be used is valid + // and has not yet been used + sal_Int32 nIdx = pSequenceMapping[i]; + if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is()) + { + pLDS[nNewCnt++] = pOld_LDS[nIdx]; + + // mark index as being used already (avoids duplicate entries) + pOld_LDS[nIdx] = Reference< XLabeledDataSequence >(); + } + } + // add not yet used 'old' sequences to new one + for (sal_Int32 i = 0; i < nNumLDS; ++i) + { +#if OSL_DEBUG_LEVEL > 1 + if (!pOld_LDS[i].is()) + i = i; +#endif + if (pOld_LDS[i].is()) + pLDS[nNewCnt++] = pOld_LDS[i]; + } + DBG_ASSERT( nNewCnt == nNumLDS, "unexpected size of resulting sequence" ); + } + + xRes = new SwChartDataSource( aLDS ); + } + } + + return xRes; +} + + +Sequence< PropertyValue > SAL_CALL SwChartDataProvider::detectArguments( + const Reference< XDataSource >& xDataSource ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Sequence< PropertyValue > aResult; + if (!xDataSource.is()) + return aResult; + + const Sequence< Reference< XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() ); + const Reference< XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray(); + sal_Int32 nNumDS_LDS = aDS_LDS.getLength(); + + DBG_ASSERT( nNumDS_LDS != 0, "data source contains no XLabeledDataSequence" ); + if (nNumDS_LDS == 0) + return aResult; + + SwFrmFmt *pTableFmt = 0; + SwTable *pTable = 0; + String aTableName; + sal_Int16 nTableRows = 0; + sal_Int16 nTableCols = 0; + + // data used to build 'CellRangeRepresentation' from later on + std::vector< std::vector< sal_Char > > aMap; + + Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS ); + sal_Int32 *pSequenceMapping = aSequenceMapping.getArray(); + + String aCellRanges; + sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither + sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have + // the expected size of 1 (i.e. if FirstCellAsLabel can + // be determined) + // -1: don't know yet, 0: not used, 1: always a single labe cell, ... + // -2: neither/failed +// sal_Int32 nValuesSeqLen = -1; // used to see if all value sequences have the same size + for (sal_Int32 i = 0; i < nNumDS_LDS; ++i) + { + Reference< XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[i] ); + if( !xLabeledDataSequence.is() ) + { + DBG_ERROR("got NULL for XLabeledDataSequence from Data source"); + continue; + } + const Reference< XDataSequence > xCurLabel( xLabeledDataSequence->getLabel(), UNO_QUERY ); + const Reference< XDataSequence > xCurValues( xLabeledDataSequence->getValues(), UNO_QUERY ); + + // get sequence lengths for label and values. + // (0 length is Ok) + sal_Int32 nCurLabelSeqLen = -1; + sal_Int32 nCurValuesSeqLen = -1; + if (xCurLabel.is()) + nCurLabelSeqLen = xCurLabel->getData().getLength(); + if (xCurValues.is()) + nCurValuesSeqLen = xCurValues->getData().getLength(); + + // check for consistent use of 'first cell as label' + if (nLabelSeqLen == -1) // set initial value to compare with below further on + nLabelSeqLen = nCurLabelSeqLen; + if (nLabelSeqLen != nCurLabelSeqLen) + nLabelSeqLen = -2; // failed / no consistent use of label cells + + // get table and cell names for label and values data sequences + // (start and end cell will be sorted, i.e. start cell <= end cell) + String aLabelTblName, aLabelStartCell, aLabelEndCell; + String aValuesTblName, aValuesStartCell, aValuesEndCell; + String aLabelRange, aValuesRange; + if (xCurLabel.is()) + aLabelRange = xCurLabel->getSourceRangeRepresentation(); + if (xCurValues.is()) + aValuesRange = xCurValues->getSourceRangeRepresentation(); + if ((aLabelRange.Len() && !GetTableAndCellsFromRangeRep( aLabelRange, + aLabelTblName, aLabelStartCell, aLabelEndCell )) || + !GetTableAndCellsFromRangeRep( aValuesRange, + aValuesTblName, aValuesStartCell, aValuesEndCell )) + { + return aResult; // failed -> return empty property sequence + } + + // make sure all sequences use the same table + if (!aTableName.Len()) + aTableName = aValuesTblName; // get initial value to compare with + if (!aTableName.Len() || + aTableName != aValuesTblName || + (aLabelTblName.Len() && aTableName != aLabelTblName)) + { + return aResult; // failed -> return empty property sequence + } + + + // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting + // first and last cell used in both sequences + // + sal_Int16 nFirstCol, nFirstRow, nLastCol, nLastRow; + String aCell( aLabelStartCell.Len() ? aLabelStartCell : aValuesStartCell ); + DBG_ASSERT( aCell.Len() , "start cell missing?" ); + lcl_GetCellPosition( aCell, nFirstCol, nFirstRow); + lcl_GetCellPosition( aValuesEndCell, nLastCol, nLastRow); + // + sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed + if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell... + { + DBG_ASSERT( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1, + "trying to determine 'DataRowSource': something's fishy... should have been a single cell"); + nDirection = 0; // default direction for a single cell should be 'columns' + } + else // more than one cell is availabale (in values and label together!) + { + if (nFirstCol == nLastCol && nFirstRow != nLastRow) + nDirection = 1; + else if (nFirstCol != nLastCol && nFirstRow == nLastRow) + nDirection = 0; + else + { + DBG_ERROR( "trying to determine 'DataRowSource': unexpected case found" ); + nDirection = -2; + } + } + // check for consistent direction of data source + if (nDtaSrcIsColumns == -1) // set initial value to compare with below + nDtaSrcIsColumns = nDirection; + if (nDtaSrcIsColumns != nDirection) + { + nDtaSrcIsColumns = -2; // failed + } + + + if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1) + { + // build data to obtain 'SequenceMapping' later on + // + DBG_ASSERT( nDtaSrcIsColumns == 0 || /* rows */ + nDtaSrcIsColumns == 1, /* columns */ + "unexpected value for 'nDtaSrcIsColumns'" ); + pSequenceMapping[i] = nDtaSrcIsColumns ? nFirstCol : nFirstRow; + + + // build data used to determine 'CellRangeRepresentation' later on + // + GetTableByName( *pDoc, aTableName, &pTableFmt, &pTable ); + if (!pTable || pTable->IsTblComplex()) + return aResult; // failed -> return empty property sequence + nTableRows = pTable->GetTabLines().Count(); + nTableCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count(); + aMap.resize( nTableRows ); + for (sal_Int16 i = 0; i < nTableRows; ++i) + aMap[i].resize( nTableCols ); + // + if (aLabelStartCell.Len() && aLabelEndCell.Len()) + { + sal_Int16 nStartCol, nStartRow, nEndCol, nEndRow; + lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow ); + lcl_GetCellPosition( aLabelEndCell, nEndCol, nEndRow ); + if (nStartRow < 0 || nEndRow >= nTableRows || + nStartCol < 0 || nEndCol >= nTableCols) + { + return aResult; // failed -> return empty property sequence + } + for (sal_Int16 i = nStartRow; i <= nEndRow; ++i) + { + for (sal_Int16 k = nStartCol; k <= nEndCol; ++k) + { + sal_Char &rChar = aMap[i][k]; + if (rChar == '\0') // check for overlapping values and/or labels + rChar = 'L'; + else + return aResult; // failed -> return empty property sequence + } + } + } + if (aValuesStartCell.Len() && aValuesEndCell.Len()) + { + sal_Int16 nStartCol, nStartRow, nEndCol, nEndRow; + lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow ); + lcl_GetCellPosition( aValuesEndCell, nEndCol, nEndRow ); + if (nStartRow < 0 || nEndRow >= nTableRows || + nStartCol < 0 || nEndCol >= nTableCols) + { + return aResult; // failed -> return empty property sequence + } + for (sal_Int16 i = nStartRow; i <= nEndRow; ++i) + { + for (sal_Int16 k = nStartCol; k <= nEndCol; ++k) + { + sal_Char &rChar = aMap[i][k]; + if (rChar == '\0') // check for overlapping values and/or labels + rChar = 'x'; + else + return aResult; // failed -> return empty property sequence + } + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + // do some extra sanity checking that the length of the sequences + // matches their range representation + { + sal_Int16 nStartRow, nStartCol, nEndRow, nEndCol; + if (xCurLabel.is()) + { + lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow); + lcl_GetCellPosition( aLabelEndCell, nEndCol, nEndRow); + DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) || + (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()), + "label sequence length does not match range representation!" ); + } + if (xCurValues.is()) + { + lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow); + lcl_GetCellPosition( aValuesEndCell, nEndCol, nEndRow); + DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) || + (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()), + "value sequence length does not match range representation!" ); + } + } +#endif + } // for + + + // build value for 'CellRangeRepresentation' + // + String aCellRangeBase( aTableName ); + aCellRangeBase += '.'; + String aCurRange; + for (sal_Int16 i = 0; i < nTableRows; ++i) + { + for (sal_Int16 k = 0; k < nTableCols; ++k) + { + if (aMap[i][k] != '\0') // top-left cell of a sub-range found + { + // find rectangular sub-range to use + sal_Int16 ri = i; // row index + sal_Int16 ci = k; // column index + sal_Int16 nRowSubLen = 0; + sal_Int16 nColSubLen = 0; + while (ri < nTableRows && aMap[ri++][k] != '\0') + ++nRowSubLen; + // be aware of shifted sequences! + // (according to the checks done prior the length should be ok) + while (ci < nTableCols && aMap[i][ci] != '\0' + && aMap[i + nRowSubLen-1][ci] != '\0') + { + ++ci; + ++nColSubLen; + } + String aStartCell( lcl_GetCellName( k, i ) ); + String aEndCell( lcl_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) ); + aCurRange = aCellRangeBase; + aCurRange += aStartCell; + aCurRange += ':'; + aCurRange += aEndCell; + if (aCellRanges.Len()) + aCellRanges += ';'; + aCellRanges += aCurRange; + + // clear already found sub-range from map + for (sal_Int16 ri = 0; ri < nRowSubLen; ++ri) + for (sal_Int16 ci = 0; ci < nColSubLen; ++ci) + aMap[i + ri][k + ci] = '\0'; + } + } + } + // to be nice to the user we now sort the cell ranges according to + // rows or columns depending on the direction used in the data source + Sequence< OUString > aSortedRanges; + GetSubranges( aCellRanges, aSortedRanges, sal_False /*sub ranges should already be normalized*/ ); + SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) ); + sal_Int32 nSortedRanges = aSortedRanges.getLength(); + const OUString *pSortedRanges = aSortedRanges.getConstArray(); + OUString aSortedCellRanges; + for (sal_Int32 i = 0; i < nSortedRanges; ++i) + { + if (aSortedCellRanges.getLength()) + aSortedCellRanges += OUString::valueOf( (sal_Unicode) ';'); + aSortedCellRanges += pSortedRanges[i]; + } + + + // build value for 'SequenceMapping' + // + Sequence< sal_Int32 > aSortedMapping( aSequenceMapping ); + sal_Int32 *pSortedMapping = aSortedMapping.getArray(); + std::sort( pSortedMapping, pSortedMapping + aSortedMapping.getLength() ); + DBG_ASSERT( aSortedMapping.getLength() == nNumDS_LDS, "unexpected size of sequence" ); + for (sal_Int32 i = 0; i < nNumDS_LDS; ++i) + { + sal_Int32 *pIt = std::find( pSortedMapping, pSortedMapping + nNumDS_LDS, + pSequenceMapping[i] ); + DBG_ASSERT( pIt, "index not found" ); + if (!pIt) + return aResult; // failed -> return empty property sequence + pSequenceMapping[i] = pIt - pSortedMapping; + } +#ifdef TL_NOT_USED // in the end chart2 did not want to have the sequence minimized + // try to shorten the 'SequenceMapping' as much as possible + sal_Int32 k; + for (k = nNumDS_LDS - 1; k >= 0; --k) + { + if (pSequenceMapping[k] != k) + break; + } + aSequenceMapping.realloc( k + 1 ); +#endif + + + // + // build resulting properties + // + DBG_ASSERT(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/, + "unexpected value for 'nLabelSeqLen'" ); + sal_Bool bFirstCellIsLabel = sal_False; // default value if 'nLabelSeqLen' could not properly determined + if (nLabelSeqLen > 0) // == 0 means no label sequence in use + bFirstCellIsLabel = sal_True; + // + DBG_ASSERT( aSortedCellRanges.getLength(), "CellRangeRepresentation missing" ); + // + aResult.realloc(4); + sal_Int32 nProps = 0; + aResult[nProps ].Name = C2U("FirstCellAsLabel"); + aResult[nProps++].Value <<= bFirstCellIsLabel; + aResult[nProps ].Name = C2U("CellRangeRepresentation"); + aResult[nProps++].Value <<= aSortedCellRanges; + if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1) + { + chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ? + chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS; + aResult[nProps ].Name = C2U("DataRowSource"); + aResult[nProps++].Value <<= eDataRowSource; + + if (aSequenceMapping.getLength() != 0) + { + aResult[nProps ].Name = C2U("SequenceMapping"); + aResult[nProps++].Value <<= aSequenceMapping; + } + } + aResult.realloc( nProps ); + + return aResult; +} + +Reference< XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation( + const OUString& rRangeRepresentation ) + throw (lang::IllegalArgumentException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + SwFrmFmt *pTblFmt = 0; // pointer to table format + SwUnoCrsr *pUnoCrsr = 0; // pointer to new created cursor spanning the cell range + GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation, + &pTblFmt, &pUnoCrsr ); + if (!pTblFmt || !pUnoCrsr) + throw lang::IllegalArgumentException(); + + DBG_ASSERT( pTblFmt && pUnoCrsr, "table format or cursor missing" ); + return new SwChartDataSequence( *this, *pTblFmt, pUnoCrsr ); +} + + +Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( ) + throw (RuntimeException) +{ + // note: it is no error to return nothing here + return Reference< sheet::XRangeSelection >(); +} + + +void SAL_CALL SwChartDataProvider::dispose( ) + throw (RuntimeException) +{ + sal_Bool bMustDispose( sal_False ); + { + osl::MutexGuard aGuard( GetChartMutex() ); + bMustDispose = !bDisposed; + if (!bDisposed) + bDisposed = sal_True; + } + if (bMustDispose) + { + // dispose all data-sequences + Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() ); + while (aIt != aDataSequences.end()) + { + DisposeAllDataSequences( (*aIt).first ); + ++aIt; + } + // release all references to data-sequences + aDataSequences.clear(); + + // require listeners to release references to this object + EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) ); + aEvtListeners.disposeAndClear( aEvtObj ); + } +} + + +void SAL_CALL SwChartDataProvider::addEventListener( + const Reference< lang::XEventListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aEvtListeners.addInterface( rxListener ); +} + + +void SAL_CALL SwChartDataProvider::removeEventListener( + const Reference< lang::XEventListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aEvtListeners.removeInterface( rxListener ); +} + + + +OUString SAL_CALL SwChartDataProvider::getImplementationName( ) + throw (RuntimeException) +{ + return C2U("SwChartDataProvider"); +} + + +sal_Bool SAL_CALL SwChartDataProvider::supportsService( + const OUString& rServiceName ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return rServiceName.equalsAscii( SN_DATA_PROVIDER ); +} + + +Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + Sequence< OUString > aRes(1); + aRes.getArray()[0] = C2U( SN_DATA_PROVIDER ); + return aRes; +} + + +void SwChartDataProvider::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew) +{ + // actually this function should be superfluous (need to check later) + ClientModify(this, pOld, pNew ); +} + + +void SwChartDataProvider::AddDataSequence( const SwTable &rTable, Reference< chart2::data::XDataSequence > &rxDataSequence ) +{ + aDataSequences[ &rTable ].insert( rxDataSequence ); +} + + +void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, Reference< chart2::data::XDataSequence > &rxDataSequence ) +{ + aDataSequences[ &rTable ].erase( rxDataSequence ); +} + + +void SwChartDataProvider::InvalidateTable( const SwTable *pTable ) +{ + DBG_ASSERT( pTable, "table pointer is NULL" ); + if (pTable) + { + if (!bDisposed) + pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking(); + + const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ]; + Set_DataSequenceRef_t::iterator aIt( rSet.begin() ); + while (aIt != rSet.end()) + { + Reference< util::XModifiable > xRef( Reference< chart2::data::XDataSequence >(*aIt), UNO_QUERY ); + if (xRef.is()) + { + // mark the sequence as 'dirty' and notify listeners + xRef->setModified( sal_True ); + } + ++aIt; + } + } +} + + +sal_Bool SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox ) +{ + sal_Bool bRes = sal_False; + DBG_ASSERT( pTable, "table pointer is NULL" ); + if (pTable) + { + if (!bDisposed) + pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking(); + + Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ]; + + // iterate over all data-sequences for that table... + Set_DataSequenceRef_t::iterator aIt( rSet.begin() ); + Set_DataSequenceRef_t::iterator aEndIt( rSet.end() ); + Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate + while (aIt != aEndIt) + { + SwChartDataSequence *pDataSeq = 0; + sal_Bool bNowEmpty = sal_False; + + // check if weak reference is still valid... + Reference< chart2::data::XDataSequence > xRef( Reference< chart2::data::XDataSequence>(*aIt), UNO_QUERY ); + if (xRef.is()) + { + // then delete that table box (check if implementation cursor needs to be adjusted) + pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() ); + if (pDataSeq) + { +#if OSL_DEBUG_LEVEL > 1 + OUString aRangeStr( pDataSeq->getSourceRangeRepresentation() ); +#endif + bNowEmpty = pDataSeq->DeleteBox( rBox ); + if (bNowEmpty) + aDelIt = aIt; + } + } + ++aIt; + + if (bNowEmpty) + { + rSet.erase( aDelIt ); + if (pDataSeq) + pDataSeq->dispose(); // the current way to tell chart that sth. got removed + } + } + } + return bRes; +} + + +void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable ) +{ + DBG_ASSERT( pTable, "table pointer is NULL" ); + if (pTable) + { + if (!bDisposed) + pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking(); + + const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ]; + Set_DataSequenceRef_t::iterator aIt( rSet.begin() ); + Set_DataSequenceRef_t::iterator aEndIt( rSet.end() ); + while (aIt != aEndIt) + { + Reference< lang::XComponent > xRef( Reference< chart2::data::XDataSequence >(*aIt), UNO_QUERY ); + if (xRef.is()) + { + xRef->dispose(); + } + ++aIt; + } + } +} + + +// XRangeXMLConversion --------------------------------------------------- + +rtl::OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const rtl::OUString& rRangeRepresentation ) + throw ( RuntimeException, IllegalArgumentException ) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + SwFrmFmt *pTblFmt = 0; // pointer to table format + //SwUnoCrsr *pUnoCrsr = 0; // pointer to new created cursor spanning the cell range + GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation, + &pTblFmt, /*&pUnoCrsr*/ NULL ); + if (!pTblFmt) + throw IllegalArgumentException(); +// if (!pUnoCrsr) +// throw RuntimeException(); + SwTable* pTable = SwTable::FindTable( pTblFmt ); + if (pTable->IsTblComplex()) + throw RuntimeException(); + + String aTblName; + String aStartCell; + String aEndCell; + if (!GetTableAndCellsFromRangeRep( rRangeRepresentation, aTblName, aStartCell, aEndCell )) + throw IllegalArgumentException(); + + sal_Int16 nCol, nRow; + lcl_GetCellPosition( aStartCell, nCol, nRow ); + if (nCol < 0 || nRow < 0) + throw RuntimeException(); + + //!! following objects/functions are implemented in XMLRangeHelper.?xx + //!! which is a copy of the respective file from chart2 !! + XMLRangeHelper::CellRange aCellRange; + aCellRange.aTableName = aTblName; + aCellRange.aUpperLeft.nColumn = nCol; + aCellRange.aUpperLeft.nRow = nRow; + aCellRange.aUpperLeft.bIsEmpty = false; + if (aStartCell != aEndCell && aEndCell.Len() != 0) + { + lcl_GetCellPosition( aEndCell, nCol, nRow ); + if (nCol < 0 || nRow < 0) + throw RuntimeException(); + + aCellRange.aLowerRight.nColumn = nCol; + aCellRange.aLowerRight.nRow = nRow; + aCellRange.aLowerRight.bIsEmpty = false; + } + String aRes( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) ); + + return aRes; +} + +rtl::OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const rtl::OUString& rXMLRange ) + throw ( RuntimeException, IllegalArgumentException ) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + //!! following objects and function are implemented in XMLRangeHelper.?xx + //!! which is a copy of the respective file from chart2 !! + XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( rXMLRange )); + + OUString aRes( aCellRange.aTableName ); + aRes += OUString::valueOf((sal_Unicode) '.'); + aRes += lcl_GetCellName( static_cast< sal_Int16 >( aCellRange.aUpperLeft.nColumn ), + static_cast< sal_Int16 >( aCellRange.aUpperLeft.nRow ) ); + // does cell range consist of more than a single cell? + if (!aCellRange.aLowerRight.bIsEmpty) + { + aRes += OUString::valueOf((sal_Unicode) ':'); + aRes += lcl_GetCellName( static_cast< sal_Int16 >( aCellRange.aLowerRight.nColumn ), + static_cast< sal_Int16 >( aCellRange.aLowerRight.nRow )); + } + + return aRes; +} + + +////////////////////////////////////////////////////////////////////// + +SwChartDataSource::SwChartDataSource( + const Sequence< Reference< XLabeledDataSequence > > &rLDS ) : + aLDS( rLDS ) +{ +} + + +SwChartDataSource::~SwChartDataSource() +{ +// delete pTblCrsr; +} + + +Sequence< Reference< XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return aLDS; +} + + +OUString SAL_CALL SwChartDataSource::getImplementationName( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return C2U("SwChartDataSource"); +} + + +sal_Bool SAL_CALL SwChartDataSource::supportsService( + const OUString& rServiceName ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return rServiceName.equalsAscii( SN_DATA_SOURCE ); +} + + +Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + Sequence< OUString > aRes(1); + aRes.getArray()[0] = C2U( SN_DATA_SOURCE ); + return aRes; +} + +////////////////////////////////////////////////////////////////////// + +SwChartDataSequence::SwChartDataSequence( + SwChartDataProvider &rProvider, + SwFrmFmt &rTblFmt, + SwUnoCrsr *pTableCursor ) : + SwClient( &rTblFmt ), + aEvtListeners( GetChartMutex() ), + aModifyListeners( GetChartMutex() ), + xDataProvider( &rProvider ), + pDataProvider( &rProvider ), + pTblCrsr( pTableCursor ), + aCursorDepend( this, pTableCursor ), + pMap( aSwMapProvider.GetPropertyMap( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) ), + aRowLabelText( SW_RES( STR_CHART2_ROW_LABEL_TEXT ) ), + aColLabelText( SW_RES( STR_CHART2_COL_LABEL_TEXT ) ) +{ + bDisposed = sal_False; + + acquire(); + try + { + const SwTable* pTable = SwTable::FindTable( &rTblFmt ); + if (pTable) + { + Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), UNO_QUERY ); + pDataProvider->AddDataSequence( *pTable, xRef ); + pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) ); + } + else + DBG_ERROR( "table missing" ); + } + catch (uno::RuntimeException &) + { + throw; + } + catch (uno::Exception &) + { + } + release(); + +#if OSL_DEBUG_LEVEL > 1 + OUString aRangeStr( getSourceRangeRepresentation() ); + + // check if it can properly convert into a SwUnoTableCrsr + // which is required for some functions + SwUnoTableCrsr* pUnoTblCrsr = *pTblCrsr; + if (!pUnoTblCrsr) + pUnoTblCrsr = *pTblCrsr; +#endif +} + + +SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) : + SwClient( rObj.GetFrmFmt() ), + aEvtListeners( GetChartMutex() ), + aModifyListeners( GetChartMutex() ), + aRole( rObj.aRole ), + xDataProvider( rObj.pDataProvider ), + pDataProvider( rObj.pDataProvider ), + pTblCrsr( rObj.pTblCrsr->Clone() ), + aCursorDepend( this, pTblCrsr ), + pMap( rObj.pMap ), + aRowLabelText( SW_RES(STR_CHART2_ROW_LABEL_TEXT) ), + aColLabelText( SW_RES(STR_CHART2_COL_LABEL_TEXT) ) +{ + bDisposed = sal_False; + + acquire(); + try + { + const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); + if (pTable) + { + Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), UNO_QUERY ); + pDataProvider->AddDataSequence( *pTable, xRef ); + pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) ); + } + else + DBG_ERROR( "table missing" ); + } + catch (uno::RuntimeException &) + { + throw; + } + catch (uno::Exception &) + { + } + release(); + +#if OSL_DEBUG_LEVEL > 1 + OUString aRangeStr( getSourceRangeRepresentation() ); + + // check if it can properly convert into a SwUnoTableCrsr + // which is required for some functions + SwUnoTableCrsr* pUnoTblCrsr = *pTblCrsr; + if (!pUnoTblCrsr) + pUnoTblCrsr = *pTblCrsr; +#endif +} + + +SwChartDataSequence::~SwChartDataSequence() +{ + // since the data-provider holds only weak references to the data-sequence + // there should be no need here to release them explicitly... + + delete pTblCrsr; +} + + +const Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId() +{ + static uno::Sequence< sal_Int8 > aSeq = ::CreateUnoTunnelId(); + return aSeq; +} + + +sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const Sequence< sal_Int8 > &rId ) + throw(RuntimeException) +{ + if( rId.getLength() == 16 + && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(), + rId.getConstArray(), 16 ) ) + { + return (sal_Int64)this; + } + return 0; +} + + +Sequence< Any > SAL_CALL SwChartDataSequence::getData( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Sequence< Any > aRes; + SwFrmFmt* pTblFmt = GetFrmFmt(); + if(pTblFmt) + { + SwTable* pTable = SwTable::FindTable( pTblFmt ); + if(!pTable->IsTblComplex()) + { + SwRangeDescriptor aDesc; + if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) )) + { + //!! make copy of pTblCrsr (SwUnoCrsr ) + // keep original cursor and make copy of it that gets handed + // over to the SwXCellRange object which takes ownership and + // thus will destroy the copy later. + SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc ); + aRange.GetDataSequence( &aRes, 0, 0 ); + } + } + } + return aRes; +} + + +OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + String aRes; + SwFrmFmt* pTblFmt = GetFrmFmt(); + if (pTblFmt) + { + aRes = pTblFmt->GetName(); + String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) ); + DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" ); + aRes += (sal_Unicode) '.'; + aRes += aCellRange; + } + return aRes; +} + +Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel( + LabelOrigin eLabelOrigin ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Sequence< OUString > aLabels; + + { + SwRangeDescriptor aDesc; + sal_Bool bOk sal_False; + SwFrmFmt* pTblFmt = GetFrmFmt(); + SwTable* pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0; + if (!pTblFmt || !pTable || pTable->IsTblComplex()) + throw uno::RuntimeException(); + else + { + String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) ); + DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" ); + bOk = FillRangeDescriptor( aDesc, aCellRange ); + DBG_ASSERT( bOk, "falied to get SwRangeDescriptor" ); + } + if (bOk) + { + aDesc.Normalize(); + sal_Int16 nColSpan = aDesc.nRight - aDesc.nLeft + 1; + sal_Int16 nRowSpan = aDesc.nBottom - aDesc.nTop + 1; + DBG_ASSERT( nColSpan == 1 || nRowSpan == 1, + "unexpected range of selected cells" ); + + String aTxt; // label text to be returned + sal_Bool bReturnEmptyTxt = sal_False; + sal_Bool bUseCol = sal_True; + if (eLabelOrigin == LabelOrigin_COLUMN) + bUseCol = sal_True; + else if (eLabelOrigin == LabelOrigin_ROW) + bUseCol = sal_False; + else if (eLabelOrigin == LabelOrigin_SHORT_SIDE) + { + bUseCol = nColSpan < nRowSpan; + bReturnEmptyTxt = nColSpan == nRowSpan; + } + else if (eLabelOrigin == LabelOrigin_LONG_SIDE) + { + bUseCol = nColSpan > nRowSpan; + bReturnEmptyTxt = nColSpan == nRowSpan; + } + else + DBG_ERROR( "unexpected case" ); + + // build label sequence + // + sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan; + aLabels.realloc( nSeqLen ); + OUString *pLabels = aLabels.getArray(); + for (sal_Int32 i = 0; i < nSeqLen; ++i) + { + if (!bReturnEmptyTxt) + { + aTxt = bUseCol ? aColLabelText : aRowLabelText; + sal_Int16 nCol = aDesc.nLeft; + sal_Int16 nRow = aDesc.nTop; + if (bUseCol) + nCol = nCol + static_cast(i); + else + nRow = nRow + static_cast(i); + String aCellName( lcl_GetCellName( nCol, nRow ) ); + + xub_StrLen nLen = aCellName.Len(); + if (nLen) + { + const sal_Unicode *pBuf = aCellName.GetBuffer(); + const sal_Unicode *pEnd = pBuf + nLen; + while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9')) + ++pBuf; + // start of number found? + if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9')) + { + String aRplc; + String aNew; + if (bUseCol) + { + aRplc = String::CreateFromAscii( "%COLUMNLETTER" ); + aNew = String( aCellName.GetBuffer(), static_cast(pBuf - aCellName.GetBuffer()) ); + } + else + { + aRplc = String::CreateFromAscii( "%ROWNUMBER" ); + aNew = String( pBuf, static_cast((aCellName.GetBuffer() + nLen) - pBuf) ); + } + xub_StrLen nPos = aTxt.Search( aRplc ); + if (nPos != STRING_NOTFOUND) + aTxt = aTxt.Replace( nPos, aRplc.Len(), aNew ); + } + } + } + pLabels[i] = aTxt; + } + } + } + + return aLabels; +} + +::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex( + ::sal_Int32 /*nIndex*/ ) + throw (lang::IndexOutOfBoundsException, + uno::RuntimeException) +{ + return 0; +} + + + +Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Sequence< OUString > aRes; + SwFrmFmt* pTblFmt = GetFrmFmt(); + if(pTblFmt) + { + SwTable* pTable = SwTable::FindTable( pTblFmt ); + if(!pTable->IsTblComplex()) + { + SwRangeDescriptor aDesc; + if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) )) + { + //!! make copy of pTblCrsr (SwUnoCrsr ) + // keep original cursor and make copy of it that gets handed + // over to the SwXCellRange object which takes ownership and + // thus will destroy the copy later. + SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc ); + aRange.GetDataSequence( 0, &aRes, 0 ); + } + } + } + return aRes; +} + + +Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Sequence< double > aRes; + SwFrmFmt* pTblFmt = GetFrmFmt(); + if(pTblFmt) + { + SwTable* pTable = SwTable::FindTable( pTblFmt ); + if(!pTable->IsTblComplex()) + { + SwRangeDescriptor aDesc; + if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) )) + { + //!! make copy of pTblCrsr (SwUnoCrsr ) + // keep original cursor and make copy of it that gets handed + // over to the SwXCellRange object which takes ownership and + // thus will destroy the copy later. + SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc ); + + // get numerical values and make an effort to return the + // numerical value for text formatted cells + aRange.GetDataSequence( 0, 0, &aRes, sal_True ); + } + } + } + return aRes; +} + + +Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + return new SwChartDataSequence( *this ); +} + + +Reference< XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + static Reference< XPropertySetInfo > xRes = new SfxItemPropertySetInfo( pMap ); + return xRes; +} + + +void SAL_CALL SwChartDataSequence::setPropertyValue( + const OUString& rPropertyName, + const Any& rValue ) + throw (UnknownPropertyException, PropertyVetoException, lang::IllegalArgumentException, lang::WrappedTargetException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE ))) + { + if ( !(rValue >>= aRole) ) + throw lang::IllegalArgumentException(); + } + else + throw beans::UnknownPropertyException(); +} + + +Any SAL_CALL SwChartDataSequence::getPropertyValue( + const OUString& rPropertyName ) + throw (UnknownPropertyException, lang::WrappedTargetException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + uno::Any aRes; + if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE ))) + aRes <<= aRole; + else + throw beans::UnknownPropertyException(); + + return aRes; +} + + +void SAL_CALL SwChartDataSequence::addPropertyChangeListener( + const OUString& /*rPropertyName*/, + const Reference< XPropertyChangeListener >& /*xListener*/ ) + throw (UnknownPropertyException, lang::WrappedTargetException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + DBG_ERROR( "not implemented" ); +} + + +void SAL_CALL SwChartDataSequence::removePropertyChangeListener( + const OUString& /*rPropertyName*/, + const Reference< XPropertyChangeListener >& /*xListener*/ ) + throw (UnknownPropertyException, lang::WrappedTargetException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + DBG_ERROR( "not implemented" ); +} + + +void SAL_CALL SwChartDataSequence::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const Reference< XVetoableChangeListener >& /*xListener*/ ) + throw (UnknownPropertyException, lang::WrappedTargetException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + DBG_ERROR( "not implemented" ); +} + + +void SAL_CALL SwChartDataSequence::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const Reference< XVetoableChangeListener >& /*xListener*/ ) + throw (UnknownPropertyException, lang::WrappedTargetException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + DBG_ERROR( "not implemented" ); +} + + +OUString SAL_CALL SwChartDataSequence::getImplementationName( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return C2U("SwChartDataSequence"); +} + + +sal_Bool SAL_CALL SwChartDataSequence::supportsService( + const OUString& rServiceName ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return rServiceName.equalsAscii( SN_DATA_SEQUENCE ); +} + + +Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + Sequence< OUString > aRes(1); + aRes.getArray()[0] = C2U( SN_DATA_SEQUENCE ); + return aRes; +} + + +void SwChartDataSequence::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew) +{ + ClientModify(this, pOld, pNew ); + + // table was deleted or cursor was deleted + if(!GetRegisteredIn() || !aCursorDepend.GetRegisteredIn()) + { + pTblCrsr = 0; + dispose(); + } + else + { + setModified( sal_True ); + } +} + + +sal_Bool SAL_CALL SwChartDataSequence::isModified( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + return sal_True; +} + + +void SAL_CALL SwChartDataSequence::setModified( + ::sal_Bool bModified ) + throw (beans::PropertyVetoException, RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + if (bModified) + LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); +} + + +void SAL_CALL SwChartDataSequence::addModifyListener( + const Reference< util::XModifyListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aModifyListeners.addInterface( rxListener ); +} + + +void SAL_CALL SwChartDataSequence::removeModifyListener( + const Reference< util::XModifyListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aModifyListeners.removeInterface( rxListener ); +} + + +void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource ) + throw (RuntimeException) +{ + if (bDisposed) + throw lang::DisposedException(); + if (rSource.Source == xDataProvider) + { + pDataProvider = 0; + xDataProvider.clear(); + } +} + + +void SAL_CALL SwChartDataSequence::dispose( ) + throw (RuntimeException) +{ + sal_Bool bMustDispose( sal_False ); + { + osl::MutexGuard aGuard( GetChartMutex() ); + bMustDispose = !bDisposed; + if (!bDisposed) + bDisposed = sal_True; + } + if (bMustDispose) + { + bDisposed = sal_True; + if (pDataProvider) + { + const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); + if (pTable) + { + Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), UNO_QUERY ); + pDataProvider->RemoveDataSequence( *pTable, xRef ); + } + else + DBG_ERROR( "table missing" ); + } + + // require listeners to release references to this object + EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) ); + aModifyListeners.disposeAndClear( aEvtObj ); + aEvtListeners.disposeAndClear( aEvtObj ); + } +} + + +void SAL_CALL SwChartDataSequence::addEventListener( + const Reference< lang::XEventListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aEvtListeners.addInterface( rxListener ); +} + + +void SAL_CALL SwChartDataSequence::removeEventListener( + const Reference< lang::XEventListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aEvtListeners.removeInterface( rxListener ); +} + + +sal_Bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox ) +{ +#if OSL_DEBUG_LEVEL > 1 + String aBoxName( rBox.GetName() ); +#endif + + // to be set if the last box of the data-sequence was removed here + sal_Bool bNowEmpty = sal_False; + + // if the implementation cursor gets affected (i.e. thew box where it is located + // in gets removed) we need to move it before that... (otherwise it does not need to change) + // + const SwStartNode* pPointStartNode = pTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode(); + const SwStartNode* pMarkStartNode = pTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode(); + // + if (!pTblCrsr->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd())) + { + bNowEmpty = sal_True; + } + else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd()) + { + //void lcl_GetRowCol(const String& rCellName, sal_uInt16& rRow, sal_uInt16& rCol); + sal_Int16 nPointRow, nPointCol; + sal_Int16 nMarkRow, nMarkCol; + const SwTable* pTable = SwTable::FindTable( GetFrmFmt() ); + String aPointCellName( pTable->GetTblBox( pPointStartNode->GetIndex() )->GetName() ); + String aMarkCellName( pTable->GetTblBox( pMarkStartNode->GetIndex() )->GetName() ); + + lcl_GetCellPosition( aPointCellName, nPointCol, nPointRow ); + lcl_GetCellPosition( aMarkCellName, nMarkCol, nMarkRow ); + DBG_ASSERT( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" ); + DBG_ASSERT( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" ); + + // move vertical or horizontal? + DBG_ASSERT( nPointRow == nMarkRow || nPointCol == nMarkCol, + "row/col indices not matching" ); + DBG_ASSERT( nPointRow != nMarkRow || nPointCol != nMarkCol, + "point and mark are identical" ); + sal_Bool bMoveVertical = (nPointCol == nMarkCol); + sal_Bool bMoveHorizontal = (nPointRow == nMarkRow); + + // get movement direction + sal_Bool bMoveLeft = sal_False; // move left or right? + sal_Bool bMoveUp = sal_False; // move up or down? + if (bMoveVertical) + { + if (pPointStartNode == rBox.GetSttNd()) // move point? + bMoveUp = nPointRow > nMarkRow; + else // move mark + bMoveUp = nMarkRow > nPointRow; + } + else if (bMoveHorizontal) + { + if (pPointStartNode == rBox.GetSttNd()) // move point? + bMoveLeft = nPointCol > nMarkCol; + else // move mark + bMoveLeft = nMarkCol > nPointCol; + } + else + DBG_ERROR( "neither vertical nor horizontal movement" ); + + // get new box (position) to use... + sal_Int16 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow; + sal_Int16 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol; + if (bMoveVertical) + nRow += bMoveUp ? -1 : +1; + if (bMoveHorizontal) + nCol += bMoveLeft ? -1 : +1; + String aNewCellName = lcl_GetCellName( nCol, nRow ); + SwTableBox* pNewBox = (SwTableBox*) pTable->GetTblBox( aNewCellName ); + + if (pNewBox) // set new position (cell range) to use + { + // So erhält man den ersten Inhaltsnode in einer gegebenen Zelle: + // Zunächst einen SwNodeIndex auf den Node hinter dem SwStartNode der Box... + SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 ); + // Dies kann ein SwCntntNode sein, kann aber auch ein Tabellen oder Sectionnode sein, + // deshalb das GoNext; + SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode(); + if (!pCNd) + pCNd = GetFrmFmt()->GetDoc()->GetNodes().GoNext( &aIdx ); + //und damit kann man z.B. eine SwPosition erzeugen: + SwPosition aNewPos( *pCNd ); // new position to beused with cursor + + // if the mark is to be changed make sure there is one... + if (pMarkStartNode == rBox.GetSttNd() && !pTblCrsr->HasMark()) + pTblCrsr->SetMark(); + + // set cursor to new position... + SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ? + pTblCrsr->GetPoint() : pTblCrsr->GetMark(); + if (pPos) + { + pPos->nNode = aNewPos.nNode; + pPos->nContent = aNewPos.nContent; + } + else + DBG_ERROR( "neither point nor mark available for change" ); + } + else + DBG_ERROR( "failed to get position" ); + } + + return bNowEmpty; +} + + +////////////////////////////////////////////////////////////////////// + +SwChartLabeledDataSequence::SwChartLabeledDataSequence() : + aEvtListeners( GetChartMutex() ), + aModifyListeners( GetChartMutex() ) +{ + bDisposed = sal_False; +} + + +SwChartLabeledDataSequence::~SwChartLabeledDataSequence() +{ +} + + +Reference< XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + return xData; +} + + +void SwChartLabeledDataSequence::SetDataSequence( + Reference< XDataSequence >& rxDest, + const Reference< XDataSequence >& rxSource) +{ + Reference< util::XModifyListener > xML( dynamic_cast< util::XModifyListener* >(this), UNO_QUERY ); + Reference< lang::XEventListener > xEL( dynamic_cast< lang::XEventListener* >(this), UNO_QUERY ); + + // stop listening to old data-sequence + Reference< util::XModifyBroadcaster > xMB( rxDest, UNO_QUERY ); + if (xMB.is()) + xMB->removeModifyListener( xML ); + Reference< lang::XComponent > xC( rxDest, UNO_QUERY ); + if (xC.is()) + xC->removeEventListener( xEL ); + + rxDest = rxSource; + + // start listening to new data-sequence + xC = Reference< lang::XComponent >( rxDest, UNO_QUERY ); + if (xC.is()) + xC->addEventListener( xEL ); + xMB = Reference< util::XModifyBroadcaster >( rxDest, UNO_QUERY ); + if (xMB.is()) + xMB->addModifyListener( xML ); +} + + +void SAL_CALL SwChartLabeledDataSequence::setValues( + const Reference< XDataSequence >& rxSequence ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + if (xData != rxSequence) + { + SetDataSequence( xData, rxSequence ); + // inform listeners of changes + LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); + } +} + + +Reference< XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + return xLabels; +} + + +void SAL_CALL SwChartLabeledDataSequence::setLabel( + const Reference< XDataSequence >& rxSequence ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + if (xLabels != rxSequence) + { + SetDataSequence( xLabels, rxSequence ); + // inform listeners of changes + LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); + } +} + + +Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + if (bDisposed) + throw lang::DisposedException(); + + Reference< util::XCloneable > xRes; + + uno::Reference< util::XCloneable > xDataCloneable( xData, UNO_QUERY ); + uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, UNO_QUERY ); + SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence(); + if (xDataCloneable.is()) + { + Reference< XDataSequence > xDataClone( xDataCloneable->createClone(), UNO_QUERY ); + pRes->setValues( xDataClone ); + } + + if (xLabelsCloneable.is()) + { + Reference< XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), UNO_QUERY ); + pRes->setLabel( xLabelsClone ); + } + xRes = pRes; + return xRes; +} + + +OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return C2U("SwChartLabeledDataSequence"); +} + + +sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService( + const OUString& rServiceName ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + return rServiceName.equalsAscii( SN_LABELED_DATA_SEQUENCE ); +} + + +Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( ) + throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + Sequence< OUString > aRes(1); + aRes.getArray()[0] = C2U( SN_LABELED_DATA_SEQUENCE ); + return aRes; +} + + +void SAL_CALL SwChartLabeledDataSequence::disposing( + const lang::EventObject& rSource ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + Reference< XInterface > xRef( rSource.Source ); + if (xRef == xData) + xData = Reference< chart2::data::XDataSequence >(); + if (xRef == xLabels) + xLabels = Reference< chart2::data::XDataSequence >(); + if (!xData.is() && !xLabels.is()) + dispose(); +} + + +void SAL_CALL SwChartLabeledDataSequence::modified( + const lang::EventObject& rEvent ) + throw (RuntimeException) +{ + if (rEvent.Source == xData || rEvent.Source == xLabels) + { + LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); + } +} + + +void SAL_CALL SwChartLabeledDataSequence::addModifyListener( + const Reference< util::XModifyListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aModifyListeners.addInterface( rxListener ); +} + + +void SAL_CALL SwChartLabeledDataSequence::removeModifyListener( + const Reference< util::XModifyListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aModifyListeners.removeInterface( rxListener ); +} + + +void SAL_CALL SwChartLabeledDataSequence::dispose( ) + throw (RuntimeException) +{ + sal_Bool bMustDispose( sal_False ); + { + osl::MutexGuard aGuard( GetChartMutex() ); + bMustDispose = !bDisposed; + if (!bDisposed) + bDisposed = sal_True; + } + if (bMustDispose) + { + bDisposed = sal_True; + + // require listeners to release references to this object + EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) ); + aModifyListeners.disposeAndClear( aEvtObj ); + aEvtListeners.disposeAndClear( aEvtObj ); + } +} + + +void SAL_CALL SwChartLabeledDataSequence::addEventListener( + const Reference< lang::XEventListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aEvtListeners.addInterface( rxListener ); +} + + +void SAL_CALL SwChartLabeledDataSequence::removeEventListener( + const Reference< lang::XEventListener >& rxListener ) + throw (RuntimeException) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!bDisposed && rxListener.is()) + aEvtListeners.removeInterface( rxListener ); +} + +////////////////////////////////////////////////////////////////////// + + -- cgit v1.2.3