update credits
[LibreOffice.git] / sw / source / core / unocore / unochart.cxx
blob363c45c4efc11b0864d504ff58611c83ca01d00d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <algorithm>
23 #include <com/sun/star/chart/ChartDataRowSource.hpp>
24 #include <com/sun/star/chart2/data/LabelOrigin.hpp>
25 #include <cppuhelper/interfacecontainer.hxx>
26 #include <osl/mutex.hxx>
27 #include <vcl/svapp.hxx>
28 #include <svl/zforlist.hxx> // SvNumberFormatter
29 #include <svx/charthelper.hxx>
31 #include <XMLRangeHelper.hxx>
32 #include <unochart.hxx>
33 #include <swtable.hxx>
34 #include <unoprnms.hxx>
35 #include <unomap.hxx>
36 #include <unomid.h>
37 #include <unocrsr.hxx>
38 #include <unotbl.hxx>
39 #include <doc.hxx>
40 #include <frmfmt.hxx>
41 #include <docsh.hxx>
42 #include <ndole.hxx>
43 #include <swtypes.hxx>
44 #include <unocore.hrc>
45 #include <docary.hxx>
46 #include <comphelper/servicehelper.hxx>
47 #include <comphelper/string.hxx>
49 #define SN_DATA_PROVIDER "com.sun.star.chart2.data.DataProvider"
50 #define SN_DATA_SOURCE "com.sun.star.chart2.data.DataSource"
51 #define SN_DATA_SEQUENCE "com.sun.star.chart2.data.DataSequence"
52 #define SN_LABELED_DATA_SEQUENCE "com.sun.star.chart2.data.LabeledDataSequence"
54 using namespace ::com::sun::star;
56 // from unotbl.cxx
57 extern void sw_GetCellPosition( const String &rCellName, sal_Int32 &rColumn, sal_Int32 &rRow);
58 extern String sw_GetCellName( sal_Int32 nColumn, sal_Int32 nRow );
59 extern int sw_CompareCellsByColFirst( const String &rCellName1, const String &rCellName2 );
60 extern int sw_CompareCellsByRowFirst( const String &rCellName1, const String &rCellName2 );
61 extern int sw_CompareCellRanges(
62 const String &rRange1StartCell, const String &rRange1EndCell,
63 const String &rRange2StartCell, const String &rRange2EndCell,
64 sal_Bool bCmpColsFirst );
65 extern void sw_NormalizeRange( String &rCell1, String &rCell2 );
67 //static
68 void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
70 if (!pDoc)
71 return;
73 uno::Reference< frame::XModel > xRes;
75 SwOLENode *pONd;
76 SwStartNode *pStNd;
77 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
78 while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
80 ++aIdx;
81 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
82 ChartHelper::IsChart( pONd->GetOLEObj().GetObject() ) )
84 // Load the object and set modified
86 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
87 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
89 try
91 uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
92 xModif->setModified( sal_True );
94 catch ( uno::Exception& )
100 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
104 SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
105 pDoc( pDocument )
106 , bIsLocked( false )
108 aUnlockTimer.SetTimeout( 1500 );
109 aUnlockTimer.SetTimeoutHdl( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
112 SwChartLockController_Helper::~SwChartLockController_Helper()
114 if (pDoc) // still connected?
115 Disconnect();
118 void SwChartLockController_Helper::StartOrContinueLocking()
120 if (!bIsLocked)
121 LockAllCharts();
122 aUnlockTimer.Start(); // start or continue time of locking
125 void SwChartLockController_Helper::Disconnect()
127 aUnlockTimer.Stop();
128 UnlockAllCharts();
129 pDoc = 0;
132 void SwChartLockController_Helper::LockUnlockAllCharts( sal_Bool bLock )
134 if (!pDoc)
135 return;
137 const SwFrmFmts& rTblFmts = *pDoc->GetTblFrmFmts();
138 for( sal_uInt16 n = 0; n < rTblFmts.size(); ++n )
140 SwTable* pTmpTbl;
141 const SwTableNode* pTblNd;
142 SwFrmFmt* pFmt = rTblFmts[ n ];
144 if( 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) &&
145 0 != ( pTblNd = pTmpTbl->GetTableNode() ) &&
146 pTblNd->GetNodes().IsDocNodes() )
148 uno::Reference< frame::XModel > xRes;
149 SwOLENode *pONd;
150 SwStartNode *pStNd;
151 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
152 while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
154 ++aIdx;
155 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
156 pONd->GetChartTblName().Len() > 0 /* is chart object? */)
158 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
159 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
161 xRes = uno::Reference < frame::XModel >( xIP->getComponent(), uno::UNO_QUERY );
162 if (xRes.is())
164 if (bLock)
165 xRes->lockControllers();
166 else
167 xRes->unlockControllers();
171 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
176 bIsLocked = bLock;
179 IMPL_LINK( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, /*pTimer*/ )
181 UnlockAllCharts();
182 return 0;
185 static osl::Mutex & GetChartMutex()
187 static osl::Mutex aMutex;
188 return aMutex;
191 static void LaunchModifiedEvent(
192 ::cppu::OInterfaceContainerHelper &rICH,
193 const uno::Reference< uno::XInterface > &rxI )
195 lang::EventObject aEvtObj( rxI );
196 cppu::OInterfaceIteratorHelper aIt( rICH );
197 while (aIt.hasMoreElements())
199 uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY );
200 if (xRef.is())
201 xRef->modified( aEvtObj );
205 // rCellRangeName needs to be of one of the following formats:
206 // - e.g. "A2:E5" or
207 // - e.g. "Table1.A2:E5"
208 bool FillRangeDescriptor(
209 SwRangeDescriptor &rDesc,
210 const String &rCellRangeName )
212 xub_StrLen nToken = STRING_NOTFOUND == rCellRangeName.Search('.') ? 0 : 1;
213 String aCellRangeNoTableName( rCellRangeName.GetToken( nToken, '.' ) );
214 String aTLName( aCellRangeNoTableName.GetToken(0, ':') ); // name of top left cell
215 String aBRName( aCellRangeNoTableName.GetToken(1, ':') ); // name of bottom right cell
216 if(!aTLName.Len() || !aBRName.Len())
217 return false;
219 rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
220 sw_GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop );
221 sw_GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
222 rDesc.Normalize();
223 OSL_ENSURE( rDesc.nTop != -1 &&
224 rDesc.nLeft != -1 &&
225 rDesc.nBottom != -1 &&
226 rDesc.nRight != -1,
227 "failed to get range descriptor" );
228 OSL_ENSURE( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight,
229 "invalid range descriptor");
230 return true;
233 static String GetCellRangeName( SwFrmFmt &rTblFmt, SwUnoCrsr &rTblCrsr )
235 String aRes;
237 //!! see also SwXTextTableCursor::getRangeName
239 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(&rTblCrsr);
240 if (!pUnoTblCrsr)
241 return String();
242 pUnoTblCrsr->MakeBoxSels();
244 const SwStartNode* pStart;
245 const SwTableBox* pStartBox = 0;
246 const SwTableBox* pEndBox = 0;
248 pStart = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
249 if (pStart)
251 const SwTable* pTable = SwTable::FindTable( &rTblFmt );
252 pEndBox = pTable->GetTblBox( pStart->GetIndex());
253 aRes = pEndBox->GetName();
255 if(pUnoTblCrsr->HasMark())
257 pStart = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
258 pStartBox = pTable->GetTblBox( pStart->GetIndex());
260 OSL_ENSURE( pStartBox, "start box not found" );
261 OSL_ENSURE( pEndBox, "end box not found" );
262 // need to switch start and end?
263 if (*pUnoTblCrsr->GetPoint() < *pUnoTblCrsr->GetMark())
265 const SwTableBox* pTmpBox = pStartBox;
266 pStartBox = pEndBox;
267 pEndBox = pTmpBox;
270 aRes = pStartBox->GetName();
271 aRes += (sal_Unicode)':';
272 if (pEndBox)
273 aRes += pEndBox->GetName();
274 else
275 aRes += pStartBox->GetName();
278 return aRes;
281 static String GetRangeRepFromTableAndCells( const String &rTableName,
282 const String &rStartCell, const String &rEndCell,
283 sal_Bool bForceEndCellName )
285 OSL_ENSURE( rTableName.Len(), "table name missing" );
286 OSL_ENSURE( rStartCell.Len(), "cell name missing" );
287 String aRes( rTableName );
288 aRes += (sal_Unicode) '.';
289 aRes += rStartCell;
291 if (rEndCell.Len())
293 aRes += (sal_Unicode) ':';
294 aRes += rEndCell;
296 else if (bForceEndCellName)
298 aRes += (sal_Unicode) ':';
299 aRes += rStartCell;
302 return aRes;
305 static bool GetTableAndCellsFromRangeRep(
306 const OUString &rRangeRepresentation,
307 String &rTblName,
308 String &rStartCell,
309 String &rEndCell,
310 bool bSortStartEndCells = true )
312 // parse range representation for table name and cell/range names
313 // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
314 String aTblName; // table name
315 OUString aRange; // cell range
316 String aStartCell; // name of top left cell
317 String aEndCell; // name of bottom right cell
318 sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' );
319 if (nIdx >= 0)
321 aTblName = rRangeRepresentation.copy( 0, nIdx );
322 aRange = rRangeRepresentation.copy( nIdx + 1 );
323 sal_Int32 nPos = aRange.indexOf( ':' );
324 if (nPos >= 0) // a cell-range like "Table1.A2:D4"
326 aStartCell = aRange.copy( 0, nPos );
327 aEndCell = aRange.copy( nPos + 1 );
329 // need to switch start and end cell ?
330 // (does not check for normalization here)
331 if (bSortStartEndCells && 1 == sw_CompareCellsByColFirst( aStartCell, aEndCell ))
333 String aTmp( aStartCell );
334 aStartCell = aEndCell;
335 aEndCell = aTmp;
338 else // a single cell like in "Table1.B3"
340 aStartCell = aEndCell = aRange;
344 bool bSuccess = aTblName.Len() != 0 &&
345 aStartCell.Len() != 0 && aEndCell.Len() != 0;
346 if (bSuccess)
348 rTblName = aTblName;
349 rStartCell = aStartCell;
350 rEndCell = aEndCell;
352 return bSuccess;
355 static void GetTableByName( const SwDoc &rDoc, const String &rTableName,
356 SwFrmFmt **ppTblFmt, SwTable **ppTable)
358 SwFrmFmt *pTblFmt = NULL;
360 // find frame format of table
361 //! see SwXTextTables::getByName
362 sal_uInt16 nCount = rDoc.GetTblFrmFmtCount(true);
363 for (sal_uInt16 i = 0; i < nCount && !pTblFmt; ++i)
365 SwFrmFmt& rTblFmt = rDoc.GetTblFrmFmt(i, true);
366 if(rTableName == rTblFmt.GetName())
367 pTblFmt = &rTblFmt;
370 if (ppTblFmt)
371 *ppTblFmt = pTblFmt;
373 if (ppTable)
374 *ppTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
377 static void GetFormatAndCreateCursorFromRangeRep(
378 const SwDoc *pDoc,
379 const OUString &rRangeRepresentation, // must be a single range (i.e. so called sub-range)
380 SwFrmFmt **ppTblFmt, // will be set to the table format of the table used in the range representation
381 SwUnoCrsr **ppUnoCrsr ) // will be set to cursor spanning the cell range
382 // (cursor will be created!)
384 String aTblName; // table name
385 String aStartCell; // name of top left cell
386 String aEndCell; // name of bottom right cell
387 bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
388 aTblName, aStartCell, aEndCell );
390 if (!bNamesFound)
392 if (ppTblFmt)
393 *ppTblFmt = NULL;
394 if (ppUnoCrsr)
395 *ppUnoCrsr = NULL;
397 else
399 SwFrmFmt *pTblFmt = NULL;
401 // is the correct table format already provided?
402 if (*ppTblFmt != NULL && (*ppTblFmt)->GetName() == aTblName)
403 pTblFmt = *ppTblFmt;
404 else if (ppTblFmt)
405 GetTableByName( *pDoc, aTblName, &pTblFmt, NULL );
407 if (ppTblFmt)
408 *ppTblFmt = pTblFmt;
410 if (ppUnoCrsr != NULL)
412 *ppUnoCrsr = NULL; // default result in case of failure
414 SwTable *pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
415 // create new SwUnoCrsr spanning the specified range
416 //! see also SwXTextTable::GetRangeByName
417 // #i80314#
418 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
419 const SwTableBox* pTLBox =
420 pTable ? pTable->GetTblBox( aStartCell, true ) : 0;
421 if(pTLBox)
423 // hier muessen die Actions aufgehoben werden
424 UnoActionRemoveContext aRemoveContext(pTblFmt->GetDoc());
425 const SwStartNode* pSttNd = pTLBox->GetSttNd();
426 SwPosition aPos(*pSttNd);
427 // set cursor to top left box of range
428 SwUnoCrsr* pUnoCrsr = pTblFmt->GetDoc()->CreateUnoCrsr(aPos, true);
429 pUnoCrsr->Move( fnMoveForward, fnGoNode );
430 pUnoCrsr->SetRemainInSection( sal_False );
431 // #i80314#
432 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
433 const SwTableBox* pBRBox = pTable->GetTblBox( aEndCell, true );
434 if(pBRBox)
436 pUnoCrsr->SetMark();
437 pUnoCrsr->GetPoint()->nNode = *pBRBox->GetSttNd();
438 pUnoCrsr->Move( fnMoveForward, fnGoNode );
439 SwUnoTableCrsr* pCrsr =
440 dynamic_cast<SwUnoTableCrsr*>(pUnoCrsr);
441 pCrsr->MakeBoxSels();
443 if (ppUnoCrsr)
444 *ppUnoCrsr = pCrsr;
446 else
448 delete pUnoCrsr;
455 static bool GetSubranges( const OUString &rRangeRepresentation,
456 uno::Sequence< OUString > &rSubRanges, bool bNormalize )
458 bool bRes = true;
459 String aRangesStr( rRangeRepresentation );
460 xub_StrLen nLen = comphelper::string::getTokenCount(aRangesStr, ';');
461 uno::Sequence< OUString > aRanges( nLen );
463 sal_Int32 nCnt = 0;
464 if (nLen != 0)
466 OUString *pRanges = aRanges.getArray();
467 String aFirstTable;
468 for ( xub_StrLen i = 0; i < nLen && bRes; ++i)
470 String aRange( aRangesStr.GetToken( i, ';' ) );
471 if (aRange.Len())
473 pRanges[nCnt] = aRange;
475 String aTableName, aStartCell, aEndCell;
476 if (!GetTableAndCellsFromRangeRep( aRange,
477 aTableName, aStartCell, aEndCell ))
478 bRes = false;
480 if (bNormalize)
482 sw_NormalizeRange( aStartCell, aEndCell );
483 pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
484 aStartCell, aEndCell, sal_True );
487 // make sure to use only a single table
488 if (nCnt == 0)
489 aFirstTable = aTableName;
490 else
491 if (aFirstTable != aTableName) bRes = false;
493 ++nCnt;
497 aRanges.realloc( nCnt );
499 rSubRanges = aRanges;
500 return bRes;
503 static void SortSubranges( uno::Sequence< OUString > &rSubRanges, sal_Bool bCmpByColumn )
505 sal_Int32 nLen = rSubRanges.getLength();
506 OUString *pSubRanges = rSubRanges.getArray();
508 String aSmallestTblName;
509 String aSmallestStartCell;
510 String aSmallestEndCell;
512 for (sal_Int32 i = 0; i < nLen; ++i)
514 sal_Int32 nIdxOfSmallest = i;
515 GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
516 aSmallestTblName, aSmallestStartCell, aSmallestEndCell );
517 if (aSmallestEndCell.Len() == 0)
518 aSmallestEndCell = aSmallestStartCell;
520 for (sal_Int32 k = i+1; k < nLen; ++k)
522 // get cell names for sub range
523 String aTblName;
524 String aStartCell;
525 String aEndCell;
526 GetTableAndCellsFromRangeRep( pSubRanges[k],
527 aTblName, aStartCell, aEndCell );
528 if (aEndCell.Len() == 0)
529 aEndCell = aStartCell;
531 // compare cell ranges ( is the new one smaller? )
532 if (-1 == sw_CompareCellRanges( aStartCell, aEndCell,
533 aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
535 nIdxOfSmallest = k;
536 aSmallestTblName = aTblName;
537 aSmallestStartCell = aStartCell;
538 aSmallestEndCell = aEndCell;
542 // move smallest element to the start of the not sorted area
543 OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
544 pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
545 pSubRanges[ i ] = aTmp;
549 SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) :
550 aEvtListeners( GetChartMutex() ),
551 pDoc( pSwDoc )
553 bDisposed = sal_False;
556 SwChartDataProvider::~SwChartDataProvider()
560 uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
561 const uno::Sequence< beans::PropertyValue >& rArguments, sal_Bool bTestOnly )
562 throw (lang::IllegalArgumentException, uno::RuntimeException)
564 SolarMutexGuard aGuard;
565 if (bDisposed)
566 throw lang::DisposedException();
568 uno::Reference< chart2::data::XDataSource > xRes;
570 if (!pDoc)
571 throw uno::RuntimeException();
573 // get arguments
574 OUString aRangeRepresentation;
575 uno::Sequence< sal_Int32 > aSequenceMapping;
576 sal_Bool bFirstIsLabel = sal_False;
577 sal_Bool bDtaSrcIsColumns = sal_True; // true : DataSource will be sequence of columns
578 // false: DataSource will be sequence of rows
579 OUString aChartOleObjectName;//work around wrong writer ranges ( see Issue 58464 )
580 sal_Int32 nArgs = rArguments.getLength();
581 OSL_ENSURE( nArgs != 0, "no properties provided" );
582 if (nArgs == 0)
583 return xRes;
584 const beans::PropertyValue *pArg = rArguments.getConstArray();
585 for (sal_Int32 i = 0; i < nArgs; ++i)
587 if ( pArg[i].Name == "DataRowSource" )
589 chart::ChartDataRowSource eSource;
590 if (!(pArg[i].Value >>= eSource))
592 sal_Int32 nTmp = 0;
593 if (!(pArg[i].Value >>= nTmp))
594 throw lang::IllegalArgumentException();
595 eSource = static_cast< chart::ChartDataRowSource >( nTmp );
597 bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
599 else if ( pArg[i].Name == "FirstCellAsLabel" )
601 if (!(pArg[i].Value >>= bFirstIsLabel))
602 throw lang::IllegalArgumentException();
604 else if ( pArg[i].Name == "CellRangeRepresentation" )
606 if (!(pArg[i].Value >>= aRangeRepresentation))
607 throw lang::IllegalArgumentException();
609 else if ( pArg[i].Name == "SequenceMapping" )
611 if (!(pArg[i].Value >>= aSequenceMapping))
612 throw lang::IllegalArgumentException();
614 else if ( pArg[i].Name == "ChartOleObjectName" )
616 if (!(pArg[i].Value >>= aChartOleObjectName))
617 throw lang::IllegalArgumentException();
621 uno::Sequence< OUString > aSubRanges;
622 // get sub-ranges and check that they all are from the very same table
623 sal_Bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
625 if (!bOk && pDoc && !aChartOleObjectName.isEmpty() )
627 //try to correct the range here
628 //work around wrong writer ranges ( see Issue 58464 )
629 String aChartTableName;
631 const SwNodes& rNodes = pDoc->GetNodes();
632 for( sal_uLong nN = rNodes.Count(); nN--; )
634 SwNodePtr pNode = rNodes[nN];
635 if( !pNode )
636 continue;
637 const SwOLENode* pOleNode = pNode->GetOLENode();
638 if( !pOleNode )
639 continue;
640 const SwOLEObj& rOObj = pOleNode->GetOLEObj();
641 if( aChartOleObjectName.equals( rOObj.GetCurrentPersistName() ) )
643 aChartTableName = pOleNode->GetChartTblName();
644 break;
648 if( aChartTableName.Len() )
650 //the wrong range is still shifted one row down
651 //thus the first row is missing and an invalid row at the end is added.
652 //Therefore we need to shift the range one row up
653 SwRangeDescriptor aDesc;
654 if (aRangeRepresentation.isEmpty())
655 return xRes; // we cant handle this thus returning an empty references
656 aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left
657 FillRangeDescriptor( aDesc, aRangeRepresentation );
658 aDesc.Normalize();
659 if (aDesc.nTop <= 0) // no chance to shift the range one row up?
660 return xRes; // we cant handle this thus returning an empty references
661 aDesc.nTop -= 1;
662 aDesc.nBottom -= 1;
664 String aNewStartCell( sw_GetCellName( aDesc.nLeft, aDesc.nTop ) );
665 String aNewEndCell( sw_GetCellName( aDesc.nRight, aDesc.nBottom ) );
666 aRangeRepresentation = GetRangeRepFromTableAndCells(
667 aChartTableName, aNewStartCell, aNewEndCell, sal_True );
668 bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
671 if (!bOk) // different tables used, or incorrect range specifiers
672 throw lang::IllegalArgumentException();
674 SortSubranges( aSubRanges, bDtaSrcIsColumns );
675 const OUString *pSubRanges = aSubRanges.getConstArray();
676 #if OSL_DEBUG_LEVEL > 1
678 sal_Int32 nSR = aSubRanges.getLength();
679 OUString *pSR = aSubRanges.getArray();
680 OUString aRg;
681 for (sal_Int32 i = 0; i < nSR; ++i)
683 aRg = pSR[i];
686 #endif
688 // get table format for that single table from above
689 SwFrmFmt *pTblFmt = 0; // pointer to table format
690 SwUnoCrsr *pUnoCrsr = 0; // here required to check if the cells in the range do actually exist
691 std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr ); // to end lifetime of object pointed to by pUnoCrsr
692 if (aSubRanges.getLength() > 0)
693 GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTblFmt, &pUnoCrsr );
694 if (!pTblFmt || !pUnoCrsr)
695 throw lang::IllegalArgumentException();
697 if(pTblFmt)
699 SwTable* pTable = SwTable::FindTable( pTblFmt );
700 if(pTable->IsTblComplex())
701 return xRes; // we cant handle this thus returning an empty references
702 else
704 // get a character map in the size of the table to mark
705 // all the ranges to use in
706 sal_Int32 nRows = pTable->GetTabLines().size();
707 sal_Int32 nCols = pTable->GetTabLines().front()->GetTabBoxes().size();
708 std::vector< std::vector< sal_Char > > aMap( nRows );
709 for (sal_Int32 i = 0; i < nRows; ++i)
710 aMap[i].resize( nCols );
712 // iterate over subranges and mark used cells in above map
713 //!! by proceeding this way we automatically get rid of
714 //!! multiple listed or overlapping cell ranges which should
715 //!! just be ignored silently
716 sal_Int32 nSubRanges = aSubRanges.getLength();
717 for (sal_Int32 i = 0; i < nSubRanges; ++i)
719 String aTblName, aStartCell, aEndCell;
720 bool bOk2 = GetTableAndCellsFromRangeRep(
721 pSubRanges[i], aTblName, aStartCell, aEndCell );
722 (void) bOk2;
723 OSL_ENSURE( bOk2, "failed to get table and start/end cells" );
725 sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
726 sw_GetCellPosition( aStartCell, nStartCol, nStartRow );
727 sw_GetCellPosition( aEndCell, nEndCol, nEndRow );
728 OSL_ENSURE( nStartRow <= nEndRow && nStartCol <= nEndCol,
729 "cell range not normalized");
731 // test if the ranges span more than the available cells
732 if( nStartRow < 0 || nEndRow >= nRows ||
733 nStartCol < 0 || nEndCol >= nCols )
735 throw lang::IllegalArgumentException();
737 for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1)
739 for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2)
740 aMap[k1][k2] = 'x';
745 // find label and data sequences to use
747 sal_Int32 oi; // outer index (slower changing index)
748 sal_Int32 ii; // inner index (faster changing index)
749 sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
750 sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
751 std::vector< sal_Int32 > aLabelIdx( oiEnd );
752 std::vector< sal_Int32 > aDataStartIdx( oiEnd );
753 std::vector< sal_Int32 > aDataLen( oiEnd );
754 for (oi = 0; oi < oiEnd; ++oi)
756 aLabelIdx[oi] = -1;
757 aDataStartIdx[oi] = -1;
758 aDataLen[oi] = 0;
761 for (oi = 0; oi < oiEnd; ++oi)
763 ii = 0;
764 while (ii < iiEnd)
766 sal_Char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
768 // label should be used but is not yet found?
769 if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
771 aLabelIdx[oi] = ii;
772 rChar = 'L'; // setting a different char for labels here
773 // makes the test for the data sequence below
774 // easier
777 // find data sequence
778 if (rChar == 'x' && aDataStartIdx[oi] == -1)
780 aDataStartIdx[oi] = ii;
782 // get length of data sequence
783 sal_Int32 nL = 0;
784 sal_Char c;
785 while (ii< iiEnd && 'x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
787 ++nL; ++ii;
789 aDataLen[oi] = nL;
791 // check that there is no other separate sequence of data
792 // to be found because that is not supported
793 while (ii < iiEnd)
795 if ('x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
796 throw lang::IllegalArgumentException();
797 ++ii;
800 else
801 ++ii;
805 // make some other consistency checks while calculating
806 // the number of XLabeledDataSequence to build:
807 // - labels should always be used or not at all
808 // - the data sequences should have equal non-zero length
809 sal_Int32 nNumLDS = 0;
810 if (oiEnd > 0)
812 sal_Int32 nFirstSeqLen = 0;
813 sal_Int32 nFirstSeqLabelIdx = -1;
814 for (oi = 0; oi < oiEnd; ++oi)
816 bool bFirstFound = false;
817 // row/col used at all?
818 if (aDataStartIdx[oi] != -1 &&
819 (!bFirstIsLabel || aLabelIdx[oi] != -1))
821 ++nNumLDS;
822 if (!bFirstFound)
824 nFirstSeqLen = aDataLen[oi];
825 nFirstSeqLabelIdx = aLabelIdx[oi];
826 bFirstFound = true;
828 else
830 if (nFirstSeqLen != aDataLen[oi] ||
831 nFirstSeqLabelIdx != aLabelIdx[oi])
832 throw lang::IllegalArgumentException();
837 if (nNumLDS == 0)
838 throw lang::IllegalArgumentException();
840 // now we should have all necessary data to build a proper DataSource
841 // thus if we came this far there should be no further problem
842 if (bTestOnly)
843 return xRes; // have createDataSourcePossible return true
845 // create data source from found label and data sequences
846 uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aLabelSeqs( nNumLDS );
847 uno::Reference< chart2::data::XDataSequence > *pLabelSeqs = aLabelSeqs.getArray();
848 uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aDataSeqs( nNumLDS );
849 uno::Reference< chart2::data::XDataSequence > *pDataSeqs = aDataSeqs.getArray();
850 sal_Int32 nSeqsIdx = 0;
851 for (oi = 0; oi < oiEnd; ++oi)
853 // row/col not used? (see if-statement above where nNumLDS was counted)
854 if (!(aDataStartIdx[oi] != -1 &&
855 (!bFirstIsLabel || aLabelIdx[oi] != -1)))
856 continue;
858 // get cell ranges for label and data
860 SwRangeDescriptor aLabelDesc;
861 SwRangeDescriptor aDataDesc;
862 if (bDtaSrcIsColumns) // use columns
864 aLabelDesc.nTop = aLabelIdx[oi];
865 aLabelDesc.nLeft = oi;
866 aLabelDesc.nBottom = aLabelDesc.nTop;
867 aLabelDesc.nRight = oi;
869 aDataDesc.nTop = aDataStartIdx[oi];
870 aDataDesc.nLeft = oi;
871 aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1;
872 aDataDesc.nRight = oi;
874 else // use rows
876 aLabelDesc.nTop = oi;
877 aLabelDesc.nLeft = aLabelIdx[oi];
878 aLabelDesc.nBottom = oi;
879 aLabelDesc.nRight = aLabelDesc.nLeft;
881 aDataDesc.nTop = oi;
882 aDataDesc.nLeft = aDataStartIdx[oi];
883 aDataDesc.nBottom = oi;
884 aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1;
886 String aBaseName( pTblFmt->GetName() );
887 aBaseName += '.';
889 String aLabelRange;
890 if (aLabelIdx[oi] != -1)
892 aLabelRange += aBaseName;
893 aLabelRange += sw_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop );
894 aLabelRange += ':';
895 aLabelRange += sw_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
898 String aDataRange;
899 if (aDataStartIdx[oi] != -1)
901 aDataRange += aBaseName;
902 aDataRange += sw_GetCellName( aDataDesc.nLeft, aDataDesc.nTop );
903 aDataRange += ':';
904 aDataRange += sw_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
907 // get cursors spanning the cell ranges for label and data
908 SwUnoCrsr *pLabelUnoCrsr = 0;
909 SwUnoCrsr *pDataUnoCrsr = 0;
910 GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTblFmt, &pLabelUnoCrsr);
911 GetFormatAndCreateCursorFromRangeRep( pDoc, aDataRange, &pTblFmt, &pDataUnoCrsr);
913 // create XDataSequence's from cursors
914 if (pLabelUnoCrsr)
915 pLabelSeqs[ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pLabelUnoCrsr );
916 OSL_ENSURE( pDataUnoCrsr, "pointer to data sequence missing" );
917 if (pDataUnoCrsr)
918 pDataSeqs [ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pDataUnoCrsr );
919 if (pLabelUnoCrsr || pDataUnoCrsr)
920 ++nSeqsIdx;
922 OSL_ENSURE( nSeqsIdx == nNumLDS,
923 "mismatch between sequence size and num,ber of entries" );
925 // build data source from data and label sequences
926 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLDS( nNumLDS );
927 uno::Reference< chart2::data::XLabeledDataSequence > *pLDS = aLDS.getArray();
928 for (sal_Int32 i = 0; i < nNumLDS; ++i)
930 SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence;
931 pLabeledDtaSeq->setLabel( pLabelSeqs[i] );
932 pLabeledDtaSeq->setValues( pDataSeqs[i] );
933 pLDS[i] = pLabeledDtaSeq;
936 // apply 'SequenceMapping' if it was provided
937 sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength();
938 if (nSequenceMappingLen)
940 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
941 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aOld_LDS( aLDS );
942 uno::Reference< chart2::data::XLabeledDataSequence > *pOld_LDS = aOld_LDS.getArray();
944 sal_Int32 nNewCnt = 0;
945 for (sal_Int32 i = 0; i < nSequenceMappingLen; ++i)
947 // check that index to be used is valid
948 // and has not yet been used
949 sal_Int32 nIdx = pSequenceMapping[i];
950 if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
952 pLDS[nNewCnt++] = pOld_LDS[nIdx];
954 // mark index as being used already (avoids duplicate entries)
955 pOld_LDS[nIdx].clear();
958 // add not yet used 'old' sequences to new one
959 for (sal_Int32 i = 0; i < nNumLDS; ++i)
961 #if OSL_DEBUG_LEVEL > 1
962 if (!pOld_LDS[i].is())
963 i = i;
964 #endif
965 if (pOld_LDS[i].is())
966 pLDS[nNewCnt++] = pOld_LDS[i];
968 OSL_ENSURE( nNewCnt == nNumLDS, "unexpected size of resulting sequence" );
971 xRes = new SwChartDataSource( aLDS );
975 return xRes;
978 sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
979 const uno::Sequence< beans::PropertyValue >& rArguments )
980 throw (uno::RuntimeException)
982 SolarMutexGuard aGuard;
984 sal_Bool bPossible = sal_True;
987 Impl_createDataSource( rArguments, sal_True );
989 catch (lang::IllegalArgumentException &)
991 bPossible = sal_False;
994 return bPossible;
997 uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
998 const uno::Sequence< beans::PropertyValue >& rArguments )
999 throw (lang::IllegalArgumentException, uno::RuntimeException)
1001 SolarMutexGuard aGuard;
1002 return Impl_createDataSource( rArguments );
1005 ////////////////////////////////////////////////////////////
1006 // SwChartDataProvider::GetBrokenCellRangeForExport
1008 // fix for #i79009
1009 // we need to return a property that has the same value as the property
1010 // 'CellRangeRepresentation' but for all rows which are increased by one.
1011 // E.g. Table1:A1:D5 -> Table1:A2:D6
1012 // Since the problem is only for old charts which did not support multiple
1013 // we do not need to provide that property/string if the 'CellRangeRepresentation'
1014 // contains multiple ranges.
1015 OUString SwChartDataProvider::GetBrokenCellRangeForExport(
1016 const OUString &rCellRangeRepresentation )
1018 OUString aRes;
1020 // check that we do not have multiple ranges
1021 if (-1 == rCellRangeRepresentation.indexOf( ';' ))
1023 // get current cell and table names
1024 String aTblName, aStartCell, aEndCell;
1025 GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
1026 aTblName, aStartCell, aEndCell, false );
1027 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1028 sw_GetCellPosition( aStartCell, nStartCol, nStartRow );
1029 sw_GetCellPosition( aEndCell, nEndCol, nEndRow );
1031 // get new cell names
1032 ++nStartRow;
1033 ++nEndRow;
1034 aStartCell = sw_GetCellName( nStartCol, nStartRow );
1035 aEndCell = sw_GetCellName( nEndCol, nEndRow );
1037 aRes = GetRangeRepFromTableAndCells( aTblName,
1038 aStartCell, aEndCell, sal_False );
1041 return aRes;
1044 uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
1045 const uno::Reference< chart2::data::XDataSource >& xDataSource )
1046 throw (uno::RuntimeException)
1048 SolarMutexGuard aGuard;
1049 if (bDisposed)
1050 throw lang::DisposedException();
1052 uno::Sequence< beans::PropertyValue > aResult;
1053 if (!xDataSource.is())
1054 return aResult;
1056 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
1057 const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
1058 sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
1060 if (nNumDS_LDS == 0)
1062 OSL_FAIL( "XLabeledDataSequence in data source contains 0 entries" );
1063 return aResult;
1066 SwFrmFmt *pTableFmt = 0;
1067 SwTable *pTable = 0;
1068 String aTableName;
1069 sal_Int32 nTableRows = 0;
1070 sal_Int32 nTableCols = 0;
1072 // data used to build 'CellRangeRepresentation' from later on
1073 std::vector< std::vector< sal_Char > > aMap;
1075 uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
1076 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
1078 String aCellRanges;
1079 sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither
1080 sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have
1081 // the expected size of 1 (i.e. if FirstCellAsLabel can
1082 // be determined)
1083 // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
1084 // -2: neither/failed
1085 for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1)
1087 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
1088 if( !xLabeledDataSequence.is() )
1090 OSL_FAIL("got NULL for XLabeledDataSequence from Data source");
1091 continue;
1093 const uno::Reference< chart2::data::XDataSequence > xCurLabel( xLabeledDataSequence->getLabel(), uno::UNO_QUERY );
1094 const uno::Reference< chart2::data::XDataSequence > xCurValues( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
1096 // get sequence lengths for label and values.
1097 // (0 length is Ok)
1098 sal_Int32 nCurLabelSeqLen = -1;
1099 sal_Int32 nCurValuesSeqLen = -1;
1100 if (xCurLabel.is())
1101 nCurLabelSeqLen = xCurLabel->getData().getLength();
1102 if (xCurValues.is())
1103 nCurValuesSeqLen = xCurValues->getData().getLength();
1105 // check for consistent use of 'first cell as label'
1106 if (nLabelSeqLen == -1) // set initial value to compare with below further on
1107 nLabelSeqLen = nCurLabelSeqLen;
1108 if (nLabelSeqLen != nCurLabelSeqLen)
1109 nLabelSeqLen = -2; // failed / no consistent use of label cells
1111 // get table and cell names for label and values data sequences
1112 // (start and end cell will be sorted, i.e. start cell <= end cell)
1113 String aLabelTblName, aLabelStartCell, aLabelEndCell;
1114 String aValuesTblName, aValuesStartCell, aValuesEndCell;
1115 String aLabelRange, aValuesRange;
1116 if (xCurLabel.is())
1117 aLabelRange = xCurLabel->getSourceRangeRepresentation();
1118 if (xCurValues.is())
1119 aValuesRange = xCurValues->getSourceRangeRepresentation();
1120 if ((aLabelRange.Len() && !GetTableAndCellsFromRangeRep( aLabelRange,
1121 aLabelTblName, aLabelStartCell, aLabelEndCell )) ||
1122 !GetTableAndCellsFromRangeRep( aValuesRange,
1123 aValuesTblName, aValuesStartCell, aValuesEndCell ))
1125 return aResult; // failed -> return empty property sequence
1128 // make sure all sequences use the same table
1129 if (!aTableName.Len())
1130 aTableName = aValuesTblName; // get initial value to compare with
1131 if (!aTableName.Len() ||
1132 aTableName != aValuesTblName ||
1133 (aLabelTblName.Len() && aTableName != aLabelTblName))
1135 return aResult; // failed -> return empty property sequence
1139 // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
1140 // first and last cell used in both sequences
1142 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1143 String aCell( aLabelStartCell.Len() ? aLabelStartCell : aValuesStartCell );
1144 OSL_ENSURE( aCell.Len() , "start cell missing?" );
1145 sw_GetCellPosition( aCell, nFirstCol, nFirstRow);
1146 sw_GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
1148 sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed
1149 if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
1151 OSL_ENSURE( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
1152 "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
1153 (void)nCurValuesSeqLen;
1154 nDirection = 0; // default direction for a single cell should be 'columns'
1156 else // more than one cell is availabale (in values and label together!)
1158 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1159 nDirection = 1;
1160 else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
1161 nDirection = 0;
1162 else
1164 OSL_FAIL( "trying to determine 'DataRowSource': unexpected case found" );
1165 nDirection = -2;
1168 // check for consistent direction of data source
1169 if (nDtaSrcIsColumns == -1) // set initial value to compare with below
1170 nDtaSrcIsColumns = nDirection;
1171 if (nDtaSrcIsColumns != nDirection)
1173 nDtaSrcIsColumns = -2; // failed
1177 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1179 // build data to obtain 'SequenceMapping' later on
1181 OSL_ENSURE( nDtaSrcIsColumns == 0 || /* rows */
1182 nDtaSrcIsColumns == 1, /* columns */
1183 "unexpected value for 'nDtaSrcIsColumns'" );
1184 pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
1187 // build data used to determine 'CellRangeRepresentation' later on
1189 GetTableByName( *pDoc, aTableName, &pTableFmt, &pTable );
1190 if (!pTable || pTable->IsTblComplex())
1191 return aResult; // failed -> return empty property sequence
1192 nTableRows = pTable->GetTabLines().size();
1193 nTableCols = pTable->GetTabLines().front()->GetTabBoxes().size();
1194 aMap.resize( nTableRows );
1195 for (sal_Int32 i = 0; i < nTableRows; ++i)
1196 aMap[i].resize( nTableCols );
1198 if (aLabelStartCell.Len() && aLabelEndCell.Len())
1200 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1201 sw_GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
1202 sw_GetCellPosition( aLabelEndCell, nEndCol, nEndRow );
1203 if (nStartRow < 0 || nEndRow >= nTableRows ||
1204 nStartCol < 0 || nEndCol >= nTableCols)
1206 return aResult; // failed -> return empty property sequence
1208 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1210 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1212 sal_Char &rChar = aMap[i][k];
1213 if (rChar == '\0') // check for overlapping values and/or labels
1214 rChar = 'L';
1215 else
1216 return aResult; // failed -> return empty property sequence
1220 if (aValuesStartCell.Len() && aValuesEndCell.Len())
1222 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1223 sw_GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
1224 sw_GetCellPosition( aValuesEndCell, nEndCol, nEndRow );
1225 if (nStartRow < 0 || nEndRow >= nTableRows ||
1226 nStartCol < 0 || nEndCol >= nTableCols)
1228 return aResult; // failed -> return empty property sequence
1230 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1232 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1234 sal_Char &rChar = aMap[i][k];
1235 if (rChar == '\0') // check for overlapping values and/or labels
1236 rChar = 'x';
1237 else
1238 return aResult; // failed -> return empty property sequence
1244 #if OSL_DEBUG_LEVEL > 0
1245 // do some extra sanity checking that the length of the sequences
1246 // matches their range representation
1248 sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
1249 if (xCurLabel.is())
1251 sw_GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
1252 sw_GetCellPosition( aLabelEndCell, nEndCol, nEndRow);
1253 OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
1254 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
1255 "label sequence length does not match range representation!" );
1257 if (xCurValues.is())
1259 sw_GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
1260 sw_GetCellPosition( aValuesEndCell, nEndCol, nEndRow);
1261 OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
1262 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
1263 "value sequence length does not match range representation!" );
1266 #endif
1267 } // for
1270 // build value for 'CellRangeRepresentation'
1272 String aCellRangeBase( aTableName );
1273 aCellRangeBase += '.';
1274 String aCurRange;
1275 for (sal_Int32 i = 0; i < nTableRows; ++i)
1277 for (sal_Int32 k = 0; k < nTableCols; ++k)
1279 if (aMap[i][k] != '\0') // top-left cell of a sub-range found
1281 // find rectangular sub-range to use
1282 sal_Int32 nRowIndex1 = i; // row index
1283 sal_Int32 nColIndex1 = k; // column index
1284 sal_Int32 nRowSubLen = 0;
1285 sal_Int32 nColSubLen = 0;
1286 while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
1287 ++nRowSubLen;
1288 // be aware of shifted sequences!
1289 // (according to the checks done prior the length should be ok)
1290 while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
1291 && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
1293 ++nColIndex1;
1294 ++nColSubLen;
1296 String aStartCell( sw_GetCellName( k, i ) );
1297 String aEndCell( sw_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
1298 aCurRange = aCellRangeBase;
1299 aCurRange += aStartCell;
1300 aCurRange += ':';
1301 aCurRange += aEndCell;
1302 if (aCellRanges.Len())
1303 aCellRanges += ';';
1304 aCellRanges += aCurRange;
1306 // clear already found sub-range from map
1307 for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2)
1308 for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2)
1309 aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
1313 // to be nice to the user we now sort the cell ranges according to
1314 // rows or columns depending on the direction used in the data source
1315 uno::Sequence< OUString > aSortedRanges;
1316 GetSubranges( aCellRanges, aSortedRanges, sal_False /*sub ranges should already be normalized*/ );
1317 SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
1318 sal_Int32 nSortedRanges = aSortedRanges.getLength();
1319 const OUString *pSortedRanges = aSortedRanges.getConstArray();
1320 OUString aSortedCellRanges;
1321 for (sal_Int32 i = 0; i < nSortedRanges; ++i)
1323 if (!aSortedCellRanges.isEmpty())
1324 aSortedCellRanges += OUString::valueOf( (sal_Unicode) ';');
1325 aSortedCellRanges += pSortedRanges[i];
1329 // build value for 'SequenceMapping'
1331 uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
1332 sal_Int32 *pSortedMapping = aSortedMapping.getArray();
1333 std::sort( pSortedMapping, pSortedMapping + aSortedMapping.getLength() );
1334 OSL_ENSURE( aSortedMapping.getLength() == nNumDS_LDS, "unexpected size of sequence" );
1335 bool bNeedSequenceMapping = false;
1336 for (sal_Int32 i = 0; i < nNumDS_LDS; ++i)
1338 sal_Int32 *pIt = std::find( pSortedMapping, pSortedMapping + nNumDS_LDS,
1339 pSequenceMapping[i] );
1340 OSL_ENSURE( pIt, "index not found" );
1341 if (!pIt)
1342 return aResult; // failed -> return empty property sequence
1343 pSequenceMapping[i] = pIt - pSortedMapping;
1345 if (i != pSequenceMapping[i])
1346 bNeedSequenceMapping = true;
1349 // check if 'SequenceMapping' is actually not required...
1350 // (don't write unnecessary properties to the XML file)
1351 if (!bNeedSequenceMapping)
1352 aSequenceMapping.realloc(0);
1355 // build resulting properties
1357 OSL_ENSURE(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
1358 "unexpected value for 'nLabelSeqLen'" );
1359 sal_Bool bFirstCellIsLabel = sal_False; // default value if 'nLabelSeqLen' could not properly determined
1360 if (nLabelSeqLen > 0) // == 0 means no label sequence in use
1361 bFirstCellIsLabel = sal_True;
1363 OSL_ENSURE( !aSortedCellRanges.isEmpty(), "CellRangeRepresentation missing" );
1364 OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
1366 aResult.realloc(5);
1367 sal_Int32 nProps = 0;
1368 aResult[nProps ].Name = "FirstCellAsLabel";
1369 aResult[nProps++].Value <<= bFirstCellIsLabel;
1370 aResult[nProps ].Name = "CellRangeRepresentation";
1371 aResult[nProps++].Value <<= aSortedCellRanges;
1372 if (!aBrokenCellRangeForExport.isEmpty())
1374 aResult[nProps ].Name = "BrokenCellRangeForExport";
1375 aResult[nProps++].Value <<= aBrokenCellRangeForExport;
1377 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1379 chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
1380 chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
1381 aResult[nProps ].Name = "DataRowSource";
1382 aResult[nProps++].Value <<= eDataRowSource;
1384 if (aSequenceMapping.getLength() != 0)
1386 aResult[nProps ].Name = "SequenceMapping";
1387 aResult[nProps++].Value <<= aSequenceMapping;
1390 aResult.realloc( nProps );
1392 return aResult;
1395 uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
1396 const OUString& rRangeRepresentation, sal_Bool bTestOnly )
1397 throw (lang::IllegalArgumentException, uno::RuntimeException)
1399 if (bDisposed)
1400 throw lang::DisposedException();
1402 SwFrmFmt *pTblFmt = 0; // pointer to table format
1403 SwUnoCrsr *pUnoCrsr = 0; // pointer to new created cursor spanning the cell range
1404 GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation,
1405 &pTblFmt, &pUnoCrsr );
1406 if (!pTblFmt || !pUnoCrsr)
1407 throw lang::IllegalArgumentException();
1409 // check that cursors point and mark are in a single row or column.
1410 String aCellRange( GetCellRangeName( *pTblFmt, *pUnoCrsr ) );
1411 SwRangeDescriptor aDesc;
1412 FillRangeDescriptor( aDesc, aCellRange );
1413 if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1414 throw lang::IllegalArgumentException();
1416 OSL_ENSURE( pTblFmt && pUnoCrsr, "table format or cursor missing" );
1417 uno::Reference< chart2::data::XDataSequence > xDataSeq;
1418 if (!bTestOnly)
1419 xDataSeq = new SwChartDataSequence( *this, *pTblFmt, pUnoCrsr );
1421 return xDataSeq;
1424 sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
1425 const OUString& rRangeRepresentation )
1426 throw (uno::RuntimeException)
1428 SolarMutexGuard aGuard;
1430 sal_Bool bPossible = sal_True;
1433 Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, sal_True );
1435 catch (lang::IllegalArgumentException &)
1437 bPossible = sal_False;
1440 return bPossible;
1443 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
1444 const OUString& rRangeRepresentation )
1445 throw (lang::IllegalArgumentException, uno::RuntimeException)
1447 SolarMutexGuard aGuard;
1448 return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
1451 uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
1452 throw (uno::RuntimeException)
1454 // note: it is no error to return nothing here
1455 return uno::Reference< sheet::XRangeSelection >();
1458 void SAL_CALL SwChartDataProvider::dispose( )
1459 throw (uno::RuntimeException)
1461 bool bMustDispose( false );
1463 osl::MutexGuard aGuard( GetChartMutex() );
1464 bMustDispose = !bDisposed;
1465 if (!bDisposed)
1466 bDisposed = sal_True;
1468 if (bMustDispose)
1470 // dispose all data-sequences
1471 Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() );
1472 while (aIt != aDataSequences.end())
1474 DisposeAllDataSequences( (*aIt).first );
1475 ++aIt;
1477 // release all references to data-sequences
1478 aDataSequences.clear();
1480 // require listeners to release references to this object
1481 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
1482 aEvtListeners.disposeAndClear( aEvtObj );
1486 void SAL_CALL SwChartDataProvider::addEventListener(
1487 const uno::Reference< lang::XEventListener >& rxListener )
1488 throw (uno::RuntimeException)
1490 osl::MutexGuard aGuard( GetChartMutex() );
1491 if (!bDisposed && rxListener.is())
1492 aEvtListeners.addInterface( rxListener );
1495 void SAL_CALL SwChartDataProvider::removeEventListener(
1496 const uno::Reference< lang::XEventListener >& rxListener )
1497 throw (uno::RuntimeException)
1499 osl::MutexGuard aGuard( GetChartMutex() );
1500 if (!bDisposed && rxListener.is())
1501 aEvtListeners.removeInterface( rxListener );
1504 OUString SAL_CALL SwChartDataProvider::getImplementationName( )
1505 throw (uno::RuntimeException)
1507 return OUString("SwChartDataProvider");
1510 sal_Bool SAL_CALL SwChartDataProvider::supportsService(
1511 const OUString& rServiceName )
1512 throw (uno::RuntimeException)
1514 SolarMutexGuard aGuard;
1515 return rServiceName == SN_DATA_PROVIDER;
1518 uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( )
1519 throw (uno::RuntimeException)
1521 SolarMutexGuard aGuard;
1522 uno::Sequence< OUString > aRes(1);
1523 aRes.getArray()[0] = SN_DATA_PROVIDER;
1524 return aRes;
1527 void SwChartDataProvider::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
1529 // actually this function should be superfluous (need to check later)
1530 ClientModify(this, pOld, pNew );
1533 void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
1535 aDataSequences[ &rTable ].insert( rxDataSequence );
1538 void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
1540 aDataSequences[ &rTable ].erase( rxDataSequence );
1543 void SwChartDataProvider::InvalidateTable( const SwTable *pTable )
1545 OSL_ENSURE( pTable, "table pointer is NULL" );
1546 if (pTable)
1548 if (!bDisposed)
1549 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1551 const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
1552 Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() );
1553 while (aIt != rSet.end())
1555 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1556 uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
1557 if (xRef.is())
1559 // mark the sequence as 'dirty' and notify listeners
1560 xRef->setModified( sal_True );
1562 ++aIt;
1567 sal_Bool SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
1569 sal_Bool bRes = sal_False;
1570 OSL_ENSURE( pTable, "table pointer is NULL" );
1571 if (pTable)
1573 if (!bDisposed)
1574 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1576 Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
1578 // iterate over all data-sequences for that table...
1579 Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
1580 Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
1581 Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate
1582 while (aIt != aEndIt)
1584 SwChartDataSequence *pDataSeq = 0;
1585 sal_Bool bNowEmpty = sal_False;
1586 sal_Bool bSeqDisposed = sal_False;
1588 // check if weak reference is still valid...
1589 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1590 uno::Reference< chart2::data::XDataSequence > xRef( xTemp, uno::UNO_QUERY );
1591 if (xRef.is())
1593 // then delete that table box (check if implementation cursor needs to be adjusted)
1594 pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() );
1595 if (pDataSeq)
1599 bNowEmpty = pDataSeq->DeleteBox( rBox );
1601 catch (const lang::DisposedException&)
1603 bNowEmpty = sal_True;
1604 bSeqDisposed = sal_True;
1607 if (bNowEmpty)
1608 aDelIt = aIt;
1611 ++aIt;
1613 if (bNowEmpty)
1615 rSet.erase( aDelIt );
1616 if (pDataSeq && !bSeqDisposed)
1617 pDataSeq->dispose(); // the current way to tell chart that sth. got removed
1621 return bRes;
1624 void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
1626 OSL_ENSURE( pTable, "table pointer is NULL" );
1627 if (pTable)
1629 if (!bDisposed)
1630 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1632 //! make a copy of the STL container!
1633 //! This is necessary since calling 'dispose' will implicitly remove an element
1634 //! of the original container, and thus any iterator in the original container
1635 //! would become invalid.
1636 const Set_DataSequenceRef_t aSet( aDataSequences[ pTable ] );
1638 Set_DataSequenceRef_t::const_iterator aIt( aSet.begin() );
1639 Set_DataSequenceRef_t::const_iterator aEndIt( aSet.end() );
1640 while (aIt != aEndIt)
1642 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1643 uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
1644 if (xRef.is())
1646 xRef->dispose();
1648 ++aIt;
1653 ////////////////////////////////////////
1654 // SwChartDataProvider::AddRowCols tries to notify charts of added columns
1655 // or rows and extends the value sequence respectively (if possible).
1656 // If those can be added to the end of existing value data-sequences those
1657 // sequences get mofdified accordingly and will send a modification
1658 // notification (calling 'setModified').
1660 // Since this function is a work-around for non existent Writer core functionality
1661 // (no arbitrary multi-selection in tables that can be used to define a
1662 // data-sequence) this function will be somewhat unreliable.
1663 // For example we will only try to adapt value sequences. For this we assume
1664 // that a sequence of length 1 is a label sequence and those with length >= 2
1665 // we presume to be value sequences. Also new cells can only be added in the
1666 // direction the value sequence is already pointing (rows / cols) and at the
1667 // start or end of the values data-sequence.
1668 // Nothing needs to be done if the new cells are in between the table cursors
1669 // point and mark since data-sequence are considered to consist of all cells
1670 // between those.
1671 // New rows/cols need to be added already to the table before calling
1672 // this function.
1674 void SwChartDataProvider::AddRowCols(
1675 const SwTable &rTable,
1676 const SwSelBoxes& rBoxes,
1677 sal_uInt16 nLines, sal_Bool bBehind )
1679 if (rTable.IsTblComplex())
1680 return;
1682 const size_t nBoxes = rBoxes.size();
1683 if (nBoxes < 1 || nLines < 1)
1684 return;
1686 SwTableBox* pFirstBox = rBoxes[0];
1687 SwTableBox* pLastBox = rBoxes.back();
1689 if (pFirstBox && pLastBox)
1691 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1692 sw_GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow );
1693 sw_GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow );
1695 bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/
1696 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1697 bAddCols = true;
1698 if (nFirstCol == nLastCol || nFirstRow == nLastRow)
1700 //get range of indices in col/rows for new cells
1701 sal_Int32 nFirstNewCol = nFirstCol;
1702 sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines;
1703 if (bAddCols)
1705 OSL_ENSURE( nFirstCol == nLastCol, "column indices seem broken" );
1706 nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines;
1707 nFirstNewRow = nFirstRow;
1710 // iterate over all data-sequences for the table
1711 const Set_DataSequenceRef_t &rSet = aDataSequences[ &rTable ];
1712 Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() );
1713 while (aIt != rSet.end())
1715 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1716 uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
1717 if (xRef.is())
1719 const sal_Int32 nLen = xRef->getTextualData().getLength();
1720 if (nLen > 1) // value data-sequence ?
1722 SwChartDataSequence *pDataSeq = 0;
1723 uno::Reference< lang::XUnoTunnel > xTunnel( xRef, uno::UNO_QUERY );
1724 if(xTunnel.is())
1726 pDataSeq = reinterpret_cast< SwChartDataSequence * >(
1727 sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething( SwChartDataSequence::getUnoTunnelId() )));
1729 if (pDataSeq)
1731 SwRangeDescriptor aDesc;
1732 pDataSeq->FillRangeDesc( aDesc );
1734 chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
1735 if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1736 eDRSource = chart::ChartDataRowSource_ROWS;
1738 if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
1740 // add rows: extend affected columns by newly added row cells
1741 pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
1743 else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
1745 // add cols: extend affected rows by newly added column cells
1746 pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
1752 ++aIt;
1759 // XRangeXMLConversion ---------------------------------------------------
1760 OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const OUString& rRangeRepresentation )
1761 throw ( uno::RuntimeException, lang::IllegalArgumentException )
1763 SolarMutexGuard aGuard;
1764 if (bDisposed)
1765 throw lang::DisposedException();
1767 String aRes;
1768 String aRangeRepresentation( rRangeRepresentation );
1770 // multiple ranges are delimeted by a ';' like in
1771 // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
1772 xub_StrLen nNumRanges = comphelper::string::getTokenCount(aRangeRepresentation, ';');
1773 SwTable* pFirstFoundTable = 0; // to check that only one table will be used
1774 for (sal_uInt16 i = 0; i < nNumRanges; ++i)
1776 String aRange( aRangeRepresentation.GetToken(i, ';') );
1777 SwFrmFmt *pTblFmt = 0; // pointer to table format
1778 GetFormatAndCreateCursorFromRangeRep( pDoc, aRange, &pTblFmt, NULL );
1779 if (!pTblFmt)
1780 throw lang::IllegalArgumentException();
1781 SwTable* pTable = SwTable::FindTable( pTblFmt );
1782 if (pTable->IsTblComplex())
1783 throw uno::RuntimeException();
1785 // check that there is only one table used in all ranges
1786 if (!pFirstFoundTable)
1787 pFirstFoundTable = pTable;
1788 if (pTable != pFirstFoundTable)
1789 throw lang::IllegalArgumentException();
1791 String aTblName;
1792 String aStartCell;
1793 String aEndCell;
1794 if (!GetTableAndCellsFromRangeRep( aRange, aTblName, aStartCell, aEndCell ))
1795 throw lang::IllegalArgumentException();
1797 sal_Int32 nCol, nRow;
1798 sw_GetCellPosition( aStartCell, nCol, nRow );
1799 if (nCol < 0 || nRow < 0)
1800 throw uno::RuntimeException();
1802 //!! following objects/functions are implemented in XMLRangeHelper.?xx
1803 //!! which is a copy of the respective file from chart2 !!
1804 XMLRangeHelper::CellRange aCellRange;
1805 aCellRange.aTableName = aTblName;
1806 aCellRange.aUpperLeft.nColumn = nCol;
1807 aCellRange.aUpperLeft.nRow = nRow;
1808 aCellRange.aUpperLeft.bIsEmpty = false;
1809 if (aStartCell != aEndCell && aEndCell.Len() != 0)
1811 sw_GetCellPosition( aEndCell, nCol, nRow );
1812 if (nCol < 0 || nRow < 0)
1813 throw uno::RuntimeException();
1815 aCellRange.aLowerRight.nColumn = nCol;
1816 aCellRange.aLowerRight.nRow = nRow;
1817 aCellRange.aLowerRight.bIsEmpty = false;
1819 String aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
1820 if (aRes.Len()) // in case of multiple ranges add delimeter
1821 aRes.AppendAscii( " " );
1822 aRes += aTmp;
1825 return aRes;
1828 OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const OUString& rXMLRange )
1829 throw ( uno::RuntimeException, lang::IllegalArgumentException )
1831 SolarMutexGuard aGuard;
1832 if (bDisposed)
1833 throw lang::DisposedException();
1835 String aRes;
1836 String aXMLRange( rXMLRange );
1838 // multiple ranges are delimeted by a ' ' like in
1839 // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
1840 xub_StrLen nNumRanges = comphelper::string::getTokenCount(aXMLRange, ' ');
1841 OUString aFirstFoundTable; // to check that only one table will be used
1842 for (sal_uInt16 i = 0; i < nNumRanges; ++i)
1844 String aRange( aXMLRange.GetToken(i, ' ') );
1846 //!! following objects and function are implemented in XMLRangeHelper.?xx
1847 //!! which is a copy of the respective file from chart2 !!
1848 XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
1850 // check that there is only one table used in all ranges
1851 if (aFirstFoundTable.isEmpty())
1852 aFirstFoundTable = aCellRange.aTableName;
1853 if (aCellRange.aTableName != aFirstFoundTable)
1854 throw lang::IllegalArgumentException();
1856 OUString aTmp( aCellRange.aTableName );
1857 aTmp += OUString::valueOf((sal_Unicode) '.');
1858 aTmp += sw_GetCellName( aCellRange.aUpperLeft.nColumn,
1859 aCellRange.aUpperLeft.nRow );
1860 // does cell range consist of more than a single cell?
1861 if (!aCellRange.aLowerRight.bIsEmpty)
1863 aTmp += OUString::valueOf((sal_Unicode) ':');
1864 aTmp += sw_GetCellName( aCellRange.aLowerRight.nColumn,
1865 aCellRange.aLowerRight.nRow );
1868 if (aRes.Len()) // in case of multiple ranges add delimeter
1869 aRes.AppendAscii( ";" );
1870 aRes += String(aTmp);
1873 return aRes;
1876 SwChartDataSource::SwChartDataSource(
1877 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
1878 aLDS( rLDS )
1882 SwChartDataSource::~SwChartDataSource()
1886 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( )
1887 throw (uno::RuntimeException)
1889 SolarMutexGuard aGuard;
1890 return aLDS;
1893 OUString SAL_CALL SwChartDataSource::getImplementationName( )
1894 throw (uno::RuntimeException)
1896 SolarMutexGuard aGuard;
1897 return OUString("SwChartDataSource");
1900 sal_Bool SAL_CALL SwChartDataSource::supportsService(
1901 const OUString& rServiceName )
1902 throw (uno::RuntimeException)
1904 SolarMutexGuard aGuard;
1905 return rServiceName == SN_DATA_SOURCE;
1908 uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( )
1909 throw (uno::RuntimeException)
1911 SolarMutexGuard aGuard;
1912 uno::Sequence< OUString > aRes(1);
1913 aRes.getArray()[0] = SN_DATA_SOURCE;
1914 return aRes;
1917 SwChartDataSequence::SwChartDataSequence(
1918 SwChartDataProvider &rProvider,
1919 SwFrmFmt &rTblFmt,
1920 SwUnoCrsr *pTableCursor ) :
1921 SwClient( &rTblFmt ),
1922 aEvtListeners( GetChartMutex() ),
1923 aModifyListeners( GetChartMutex() ),
1924 aRowLabelText( SW_RES( STR_CHART2_ROW_LABEL_TEXT ) ),
1925 aColLabelText( SW_RES( STR_CHART2_COL_LABEL_TEXT ) ),
1926 xDataProvider( &rProvider ),
1927 pDataProvider( &rProvider ),
1928 pTblCrsr( pTableCursor ),
1929 aCursorDepend( this, pTableCursor ),
1930 _pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
1932 bDisposed = sal_False;
1934 acquire();
1937 const SwTable* pTable = SwTable::FindTable( &rTblFmt );
1938 if (pTable)
1940 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
1941 pDataProvider->AddDataSequence( *pTable, xRef );
1942 pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
1944 else {
1945 OSL_FAIL( "table missing" );
1948 catch (uno::RuntimeException &)
1950 throw;
1952 catch (uno::Exception &)
1955 release();
1957 #if OSL_DEBUG_LEVEL > 0
1958 // check if it can properly convert into a SwUnoTableCrsr
1959 // which is required for some functions
1960 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
1961 OSL_ENSURE(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
1962 (void) pUnoTblCrsr;
1963 #endif
1966 SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
1967 SwChartDataSequenceBaseClass(),
1968 SwClient( rObj.GetFrmFmt() ),
1969 aEvtListeners( GetChartMutex() ),
1970 aModifyListeners( GetChartMutex() ),
1971 aRole( rObj.aRole ),
1972 aRowLabelText( SW_RES(STR_CHART2_ROW_LABEL_TEXT) ),
1973 aColLabelText( SW_RES(STR_CHART2_COL_LABEL_TEXT) ),
1974 xDataProvider( rObj.pDataProvider ),
1975 pDataProvider( rObj.pDataProvider ),
1976 pTblCrsr( rObj.pTblCrsr->Clone() ),
1977 aCursorDepend( this, pTblCrsr ),
1978 _pPropSet( rObj._pPropSet )
1980 bDisposed = sal_False;
1982 acquire();
1985 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
1986 if (pTable)
1988 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
1989 pDataProvider->AddDataSequence( *pTable, xRef );
1990 pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
1992 else {
1993 OSL_FAIL( "table missing" );
1996 catch (uno::RuntimeException &)
1998 throw;
2000 catch (uno::Exception &)
2003 release();
2005 #if OSL_DEBUG_LEVEL > 0
2006 // check if it can properly convert into a SwUnoTableCrsr
2007 // which is required for some functions
2008 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2009 OSL_ENSURE(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
2010 (void) pUnoTblCrsr;
2011 #endif
2014 SwChartDataSequence::~SwChartDataSequence()
2016 // since the data-provider holds only weak references to the data-sequence
2017 // there should be no need here to release them explicitly...
2019 delete pTblCrsr;
2022 namespace
2024 class theSwChartDataSequenceUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwChartDataSequenceUnoTunnelId > {};
2027 const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
2029 return theSwChartDataSequenceUnoTunnelId::get().getSeq();
2032 sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
2033 throw(uno::RuntimeException)
2035 if( rId.getLength() == 16
2036 && 0 == memcmp( getUnoTunnelId().getConstArray(),
2037 rId.getConstArray(), 16 ) )
2039 return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
2041 return 0;
2044 uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData( )
2045 throw (uno::RuntimeException)
2047 SolarMutexGuard aGuard;
2048 if (bDisposed)
2049 throw lang::DisposedException();
2051 uno::Sequence< uno::Any > aRes;
2052 SwFrmFmt* pTblFmt = GetFrmFmt();
2053 if(pTblFmt)
2055 SwTable* pTable = SwTable::FindTable( pTblFmt );
2056 if(!pTable->IsTblComplex())
2058 SwRangeDescriptor aDesc;
2059 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2061 //!! make copy of pTblCrsr (SwUnoCrsr )
2062 // keep original cursor and make copy of it that gets handed
2063 // over to the SwXCellRange object which takes ownership and
2064 // thus will destroy the copy later.
2065 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2066 aRange.GetDataSequence( &aRes, 0, 0 );
2070 return aRes;
2073 OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( )
2074 throw (uno::RuntimeException)
2076 SolarMutexGuard aGuard;
2077 if (bDisposed)
2078 throw lang::DisposedException();
2080 String aRes;
2081 SwFrmFmt* pTblFmt = GetFrmFmt();
2082 if (pTblFmt)
2084 aRes = pTblFmt->GetName();
2085 String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2086 OSL_ENSURE( aCellRange.Len() != 0, "failed to get cell range" );
2087 aRes += (sal_Unicode) '.';
2088 aRes += aCellRange;
2090 return aRes;
2093 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
2094 chart2::data::LabelOrigin eLabelOrigin )
2095 throw (uno::RuntimeException)
2097 SolarMutexGuard aGuard;
2098 if (bDisposed)
2099 throw lang::DisposedException();
2101 uno::Sequence< OUString > aLabels;
2104 SwRangeDescriptor aDesc;
2105 bool bOk = false;
2106 SwFrmFmt* pTblFmt = GetFrmFmt();
2107 SwTable* pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
2108 if (!pTblFmt || !pTable || pTable->IsTblComplex())
2109 throw uno::RuntimeException();
2110 else
2112 String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2113 OSL_ENSURE( aCellRange.Len() != 0, "failed to get cell range" );
2114 bOk = FillRangeDescriptor( aDesc, aCellRange );
2115 OSL_ENSURE( bOk, "falied to get SwRangeDescriptor" );
2117 if (bOk)
2119 aDesc.Normalize();
2120 sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
2121 sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
2122 OSL_ENSURE( nColSpan == 1 || nRowSpan == 1,
2123 "unexpected range of selected cells" );
2125 String aTxt; // label text to be returned
2126 bool bReturnEmptyTxt = false;
2127 bool bUseCol = true;
2128 if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
2129 bUseCol = true;
2130 else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
2131 bUseCol = false;
2132 else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
2134 bUseCol = nColSpan < nRowSpan;
2135 bReturnEmptyTxt = nColSpan == nRowSpan;
2137 else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
2139 bUseCol = nColSpan > nRowSpan;
2140 bReturnEmptyTxt = nColSpan == nRowSpan;
2142 else {
2143 OSL_FAIL( "unexpected case" );
2146 // build label sequence
2148 sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
2149 aLabels.realloc( nSeqLen );
2150 OUString *pLabels = aLabels.getArray();
2151 for (sal_Int32 i = 0; i < nSeqLen; ++i)
2153 if (!bReturnEmptyTxt)
2155 aTxt = bUseCol ? aColLabelText : aRowLabelText;
2156 sal_Int32 nCol = aDesc.nLeft;
2157 sal_Int32 nRow = aDesc.nTop;
2158 if (bUseCol)
2159 nCol = nCol + i;
2160 else
2161 nRow = nRow + i;
2162 String aCellName( sw_GetCellName( nCol, nRow ) );
2164 xub_StrLen nLen = aCellName.Len();
2165 if (nLen)
2167 const sal_Unicode *pBuf = aCellName.GetBuffer();
2168 const sal_Unicode *pEnd = pBuf + nLen;
2169 while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9'))
2170 ++pBuf;
2171 // start of number found?
2172 if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
2174 String aRplc;
2175 String aNew;
2176 if (bUseCol)
2178 aRplc = OUString("%COLUMNLETTER");
2179 aNew = OUString(aCellName.GetBuffer(), pBuf - aCellName.GetBuffer());
2181 else
2183 aRplc = OUString("%ROWNUMBER");
2184 aNew = OUString(pBuf, (aCellName.GetBuffer() + nLen) - pBuf);
2186 xub_StrLen nPos = aTxt.Search( aRplc );
2187 if (nPos != STRING_NOTFOUND)
2188 aTxt = aTxt.Replace( nPos, aRplc.Len(), aNew );
2192 pLabels[i] = aTxt;
2197 return aLabels;
2200 ::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
2201 ::sal_Int32 /*nIndex*/ )
2202 throw (lang::IndexOutOfBoundsException,
2203 uno::RuntimeException)
2205 return 0;
2208 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData( )
2209 throw (uno::RuntimeException)
2211 SolarMutexGuard aGuard;
2212 if (bDisposed)
2213 throw lang::DisposedException();
2215 uno::Sequence< OUString > aRes;
2216 SwFrmFmt* pTblFmt = GetFrmFmt();
2217 if(pTblFmt)
2219 SwTable* pTable = SwTable::FindTable( pTblFmt );
2220 if(!pTable->IsTblComplex())
2222 SwRangeDescriptor aDesc;
2223 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2225 //!! make copy of pTblCrsr (SwUnoCrsr )
2226 // keep original cursor and make copy of it that gets handed
2227 // over to the SwXCellRange object which takes ownership and
2228 // thus will destroy the copy later.
2229 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2230 aRange.GetDataSequence( 0, &aRes, 0 );
2234 return aRes;
2237 uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData( )
2238 throw (uno::RuntimeException)
2240 SolarMutexGuard aGuard;
2241 if (bDisposed)
2242 throw lang::DisposedException();
2244 uno::Sequence< double > aRes;
2245 SwFrmFmt* pTblFmt = GetFrmFmt();
2246 if(pTblFmt)
2248 SwTable* pTable = SwTable::FindTable( pTblFmt );
2249 if(!pTable->IsTblComplex())
2251 SwRangeDescriptor aDesc;
2252 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2254 //!! make copy of pTblCrsr (SwUnoCrsr )
2255 // keep original cursor and make copy of it that gets handed
2256 // over to the SwXCellRange object which takes ownership and
2257 // thus will destroy the copy later.
2258 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2260 // get numerical values and make an effort to return the
2261 // numerical value for text formatted cells
2262 aRange.GetDataSequence( 0, 0, &aRes, sal_True );
2266 return aRes;
2269 uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( )
2270 throw (uno::RuntimeException)
2272 SolarMutexGuard aGuard;
2273 if (bDisposed)
2274 throw lang::DisposedException();
2275 return new SwChartDataSequence( *this );
2278 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( )
2279 throw (uno::RuntimeException)
2281 SolarMutexGuard aGuard;
2282 if (bDisposed)
2283 throw lang::DisposedException();
2285 static uno::Reference< beans::XPropertySetInfo > xRes = _pPropSet->getPropertySetInfo();
2286 return xRes;
2289 void SAL_CALL SwChartDataSequence::setPropertyValue(
2290 const OUString& rPropertyName,
2291 const uno::Any& rValue )
2292 throw (beans::UnknownPropertyException, beans::PropertyVetoException, lang::IllegalArgumentException, lang::WrappedTargetException, uno::RuntimeException)
2294 SolarMutexGuard aGuard;
2295 if (bDisposed)
2296 throw lang::DisposedException();
2298 if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
2300 if ( !(rValue >>= aRole) )
2301 throw lang::IllegalArgumentException();
2303 else
2304 throw beans::UnknownPropertyException();
2307 uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
2308 const OUString& rPropertyName )
2309 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2311 SolarMutexGuard aGuard;
2312 if (bDisposed)
2313 throw lang::DisposedException();
2315 uno::Any aRes;
2316 if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
2317 aRes <<= aRole;
2318 else
2319 throw beans::UnknownPropertyException();
2321 return aRes;
2324 void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
2325 const OUString& /*rPropertyName*/,
2326 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2327 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2329 OSL_FAIL( "not implemented" );
2332 void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
2333 const OUString& /*rPropertyName*/,
2334 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2335 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2337 OSL_FAIL( "not implemented" );
2340 void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
2341 const OUString& /*rPropertyName*/,
2342 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2343 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2345 OSL_FAIL( "not implemented" );
2348 void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
2349 const OUString& /*rPropertyName*/,
2350 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2351 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2353 OSL_FAIL( "not implemented" );
2356 OUString SAL_CALL SwChartDataSequence::getImplementationName( )
2357 throw (uno::RuntimeException)
2359 return OUString("SwChartDataSequence");
2362 sal_Bool SAL_CALL SwChartDataSequence::supportsService(
2363 const OUString& rServiceName )
2364 throw (uno::RuntimeException)
2366 return rServiceName == SN_DATA_SEQUENCE;
2369 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( )
2370 throw (uno::RuntimeException)
2372 SolarMutexGuard aGuard;
2373 uno::Sequence< OUString > aRes(1);
2374 aRes.getArray()[0] = SN_DATA_SEQUENCE;
2375 return aRes;
2378 void SwChartDataSequence::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
2380 ClientModify(this, pOld, pNew );
2382 // table was deleted or cursor was deleted
2383 if(!GetRegisteredIn() || !aCursorDepend.GetRegisteredIn())
2385 pTblCrsr = 0;
2386 dispose();
2388 else
2390 setModified( sal_True );
2394 sal_Bool SAL_CALL SwChartDataSequence::isModified( )
2395 throw (uno::RuntimeException)
2397 SolarMutexGuard aGuard;
2398 if (bDisposed)
2399 throw lang::DisposedException();
2401 return sal_True;
2404 void SAL_CALL SwChartDataSequence::setModified(
2405 ::sal_Bool bModified )
2406 throw (beans::PropertyVetoException, uno::RuntimeException)
2408 SolarMutexGuard aGuard;
2409 if (bDisposed)
2410 throw lang::DisposedException();
2412 if (bModified)
2413 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2416 void SAL_CALL SwChartDataSequence::addModifyListener(
2417 const uno::Reference< util::XModifyListener >& rxListener )
2418 throw (uno::RuntimeException)
2420 osl::MutexGuard aGuard( GetChartMutex() );
2421 if (!bDisposed && rxListener.is())
2422 aModifyListeners.addInterface( rxListener );
2425 void SAL_CALL SwChartDataSequence::removeModifyListener(
2426 const uno::Reference< util::XModifyListener >& rxListener )
2427 throw (uno::RuntimeException)
2429 osl::MutexGuard aGuard( GetChartMutex() );
2430 if (!bDisposed && rxListener.is())
2431 aModifyListeners.removeInterface( rxListener );
2434 void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
2435 throw (uno::RuntimeException)
2437 if (bDisposed)
2438 throw lang::DisposedException();
2439 if (rSource.Source == xDataProvider)
2441 pDataProvider = 0;
2442 xDataProvider.clear();
2446 void SAL_CALL SwChartDataSequence::dispose( )
2447 throw (uno::RuntimeException)
2449 bool bMustDispose( false );
2451 osl::MutexGuard aGuard( GetChartMutex() );
2452 bMustDispose = !bDisposed;
2453 if (!bDisposed)
2454 bDisposed = sal_True;
2456 if (bMustDispose)
2458 bDisposed = sal_True;
2459 if (pDataProvider)
2461 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2462 if (pTable)
2464 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2465 pDataProvider->RemoveDataSequence( *pTable, xRef );
2467 else {
2468 OSL_FAIL( "table missing" );
2471 //#i119653# The bug is crashed for an exception thrown by
2472 //SwCharDataSequence::setModified() because
2473 //the SwCharDataSequence object has been disposed.
2475 //Actually, the former design of SwClient will disconnect itself
2476 //from the notification list in its destructor.
2478 //But the SwCharDataSeqence won't be destructed but disposed in code
2479 //(the data member SwChartDataSequence::bDisposed will be set to
2480 //TRUE), the relationship between client and modification is not
2481 //released.
2483 //So any notification from modify object will lead to said
2484 //exception threw out. Recorrect the logic of code in
2485 //SwChartDataSequence::Dispose(), release the relationship
2486 //here...
2487 SwModify* pLclRegisteredIn = GetRegisteredInNonConst();
2488 if (pLclRegisteredIn && pLclRegisteredIn->GetDepends())
2490 pLclRegisteredIn->Remove(this);
2491 pTblCrsr = NULL;
2495 // require listeners to release references to this object
2496 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
2497 aModifyListeners.disposeAndClear( aEvtObj );
2498 aEvtListeners.disposeAndClear( aEvtObj );
2502 void SAL_CALL SwChartDataSequence::addEventListener(
2503 const uno::Reference< lang::XEventListener >& rxListener )
2504 throw (uno::RuntimeException)
2506 osl::MutexGuard aGuard( GetChartMutex() );
2507 if (!bDisposed && rxListener.is())
2508 aEvtListeners.addInterface( rxListener );
2511 void SAL_CALL SwChartDataSequence::removeEventListener(
2512 const uno::Reference< lang::XEventListener >& rxListener )
2513 throw (uno::RuntimeException)
2515 osl::MutexGuard aGuard( GetChartMutex() );
2516 if (!bDisposed && rxListener.is())
2517 aEvtListeners.removeInterface( rxListener );
2520 sal_Bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
2522 if (bDisposed)
2523 throw lang::DisposedException();
2525 #if OSL_DEBUG_LEVEL > 1
2526 String aBoxName( rBox.GetName() );
2527 #endif
2529 // to be set if the last box of the data-sequence was removed here
2530 sal_Bool bNowEmpty = sal_False;
2532 // if the implementation cursor gets affected (i.e. thew box where it is located
2533 // in gets removed) we need to move it before that... (otherwise it does not need to change)
2535 const SwStartNode* pPointStartNode = pTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2536 const SwStartNode* pMarkStartNode = pTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2538 if (!pTblCrsr->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd()))
2540 bNowEmpty = sal_True;
2542 else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd())
2544 sal_Int32 nPointRow = -1, nPointCol = -1;
2545 sal_Int32 nMarkRow = -1, nMarkCol = -1;
2546 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2547 String aPointCellName( pTable->GetTblBox( pPointStartNode->GetIndex() )->GetName() );
2548 String aMarkCellName( pTable->GetTblBox( pMarkStartNode->GetIndex() )->GetName() );
2550 sw_GetCellPosition( aPointCellName, nPointCol, nPointRow );
2551 sw_GetCellPosition( aMarkCellName, nMarkCol, nMarkRow );
2552 OSL_ENSURE( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
2553 OSL_ENSURE( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
2555 // move vertical or horizontal?
2556 OSL_ENSURE( nPointRow == nMarkRow || nPointCol == nMarkCol,
2557 "row/col indices not matching" );
2558 OSL_ENSURE( nPointRow != nMarkRow || nPointCol != nMarkCol,
2559 "point and mark are identical" );
2560 bool bMoveVertical = (nPointCol == nMarkCol);
2561 bool bMoveHorizontal = (nPointRow == nMarkRow);
2563 // get movement direction
2564 bool bMoveLeft = false; // move left or right?
2565 bool bMoveUp = false; // move up or down?
2566 if (bMoveVertical)
2568 if (pPointStartNode == rBox.GetSttNd()) // move point?
2569 bMoveUp = nPointRow > nMarkRow;
2570 else // move mark
2571 bMoveUp = nMarkRow > nPointRow;
2573 else if (bMoveHorizontal)
2575 if (pPointStartNode == rBox.GetSttNd()) // move point?
2576 bMoveLeft = nPointCol > nMarkCol;
2577 else // move mark
2578 bMoveLeft = nMarkCol > nPointCol;
2580 else {
2581 OSL_FAIL( "neither vertical nor horizontal movement" );
2584 // get new box (position) to use...
2585 sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
2586 sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
2587 if (bMoveVertical)
2588 nRow += bMoveUp ? -1 : +1;
2589 if (bMoveHorizontal)
2590 nCol += bMoveLeft ? -1 : +1;
2591 String aNewCellName = sw_GetCellName( nCol, nRow );
2592 SwTableBox* pNewBox = (SwTableBox*) pTable->GetTblBox( aNewCellName );
2594 if (pNewBox) // set new position (cell range) to use
2596 // So erh lt man den ersten Inhaltsnode in einer gegebenen Zelle:
2597 // Zun chst einen SwNodeIndex auf den Node hinter dem SwStartNode der Box...
2598 SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
2599 // Dies kann ein SwCntntNode sein, kann aber auch ein Tabellen oder Sectionnode sein,
2600 // deshalb das GoNext;
2601 SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode();
2602 if (!pCNd)
2603 pCNd = GetFrmFmt()->GetDoc()->GetNodes().GoNext( &aIdx );
2604 //und damit kann man z.B. eine SwPosition erzeugen:
2605 SwPosition aNewPos( *pCNd ); // new position to beused with cursor
2607 // if the mark is to be changed make sure there is one...
2608 if (pMarkStartNode == rBox.GetSttNd() && !pTblCrsr->HasMark())
2609 pTblCrsr->SetMark();
2611 // set cursor to new position...
2612 SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
2613 pTblCrsr->GetPoint() : pTblCrsr->GetMark();
2614 if (pPos)
2616 pPos->nNode = aNewPos.nNode;
2617 pPos->nContent = aNewPos.nContent;
2619 else {
2620 OSL_FAIL( "neither point nor mark available for change" );
2623 else {
2624 OSL_FAIL( "failed to get position" );
2628 return bNowEmpty;
2631 void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
2633 SwFrmFmt* pTblFmt = GetFrmFmt();
2634 if(pTblFmt)
2636 SwTable* pTable = SwTable::FindTable( pTblFmt );
2637 if(!pTable->IsTblComplex())
2639 FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2645 SwChartDataSequence::ExtendTo
2647 extends the data-sequence by new cells added at the end of the direction
2648 the data-sequence points to.
2649 If the cells are already within the range of the sequence nothing needs
2650 to be done.
2651 If the cells are beyond the end of the sequence (are not adjacent to the
2652 current last cell) nothing can be done. Only if the cells are adjacent to
2653 the last cell they can be added.
2655 @returns true if the data-sequence was changed.
2656 @param bExtendCols
2657 specifies if columns or rows are to be extended
2658 @param nFirstNew
2659 index of first new row/col to be included in data-sequence
2660 @param nLastNew
2661 index of last new row/col to be included in data-sequence
2663 bool SwChartDataSequence::ExtendTo( bool bExtendCol,
2664 sal_Int32 nFirstNew, sal_Int32 nCount )
2666 bool bChanged = false;
2668 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2669 //pUnoTblCrsr->MakeBoxSels();
2671 const SwStartNode *pStartNd = 0;
2672 const SwTableBox *pStartBox = 0;
2673 const SwTableBox *pEndBox = 0;
2675 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2676 OSL_ENSURE( !pTable->IsTblComplex(), "table too complex" );
2677 if (nCount < 1 || nFirstNew < 0 || pTable->IsTblComplex())
2678 return false;
2681 // get range descriptor (cell range) for current data-sequence
2683 pStartNd = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2684 pEndBox = pTable->GetTblBox( pStartNd->GetIndex() );
2685 const String aEndBox( pEndBox->GetName() );
2687 pStartNd = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2688 pStartBox = pTable->GetTblBox( pStartNd->GetIndex() );
2689 const String aStartBox( pStartBox->GetName() );
2691 String aCellRange( aStartBox ); // note that cell range here takes the newly added rows/cols already into account
2692 aCellRange.AppendAscii( ":" );
2693 aCellRange += aEndBox;
2694 SwRangeDescriptor aDesc;
2695 FillRangeDescriptor( aDesc, aCellRange );
2697 String aNewStartCell;
2698 String aNewEndCell;
2699 if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
2701 // new column cells adjacent to the bottom of the
2702 // current data-sequence to be added...
2703 OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2704 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2705 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
2706 bChanged = true;
2708 else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
2710 // new column cells adjacent to the top of the
2711 // current data-sequence to be added...
2712 OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2713 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop - nCount);
2714 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2715 bChanged = true;
2717 else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
2719 // new row cells adjacent to the right of the
2720 // current data-sequence to be added...
2721 OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2722 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2723 aNewEndCell = sw_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
2724 bChanged = true;
2726 else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
2728 // new row cells adjacent to the left of the
2729 // current data-sequence to be added...
2730 OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2731 aNewStartCell = sw_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
2732 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2733 bChanged = true;
2736 if (bChanged)
2738 // move table cursor to new start and end of data-sequence
2739 const SwTableBox *pNewStartBox = pTable->GetTblBox( aNewStartCell );
2740 const SwTableBox *pNewEndBox = pTable->GetTblBox( aNewEndCell );
2741 pUnoTblCrsr->SetMark();
2742 pUnoTblCrsr->GetPoint()->nNode = *pNewEndBox->GetSttNd();
2743 pUnoTblCrsr->GetMark()->nNode = *pNewStartBox->GetSttNd();
2744 pUnoTblCrsr->Move( fnMoveForward, fnGoNode );
2745 pUnoTblCrsr->MakeBoxSels();
2748 return bChanged;
2751 SwChartLabeledDataSequence::SwChartLabeledDataSequence() :
2752 aEvtListeners( GetChartMutex() ),
2753 aModifyListeners( GetChartMutex() )
2755 bDisposed = sal_False;
2758 SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
2762 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( )
2763 throw (uno::RuntimeException)
2765 SolarMutexGuard aGuard;
2766 if (bDisposed)
2767 throw lang::DisposedException();
2768 return xData;
2771 void SwChartLabeledDataSequence::SetDataSequence(
2772 uno::Reference< chart2::data::XDataSequence >& rxDest,
2773 const uno::Reference< chart2::data::XDataSequence >& rxSource)
2775 uno::Reference< util::XModifyListener > xML( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY );
2776 uno::Reference< lang::XEventListener > xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY );
2778 // stop listening to old data-sequence
2779 uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
2780 if (xMB.is())
2781 xMB->removeModifyListener( xML );
2782 uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
2783 if (xC.is())
2784 xC->removeEventListener( xEL );
2786 rxDest = rxSource;
2788 // start listening to new data-sequence
2789 xC = uno::Reference< lang::XComponent >( rxDest, uno::UNO_QUERY );
2790 if (xC.is())
2791 xC->addEventListener( xEL );
2792 xMB = uno::Reference< util::XModifyBroadcaster >( rxDest, uno::UNO_QUERY );
2793 if (xMB.is())
2794 xMB->addModifyListener( xML );
2797 void SAL_CALL SwChartLabeledDataSequence::setValues(
2798 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2799 throw (uno::RuntimeException)
2801 SolarMutexGuard aGuard;
2802 if (bDisposed)
2803 throw lang::DisposedException();
2805 if (xData != rxSequence)
2807 SetDataSequence( xData, rxSequence );
2808 // inform listeners of changes
2809 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2813 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( )
2814 throw (uno::RuntimeException)
2816 SolarMutexGuard aGuard;
2817 if (bDisposed)
2818 throw lang::DisposedException();
2819 return xLabels;
2822 void SAL_CALL SwChartLabeledDataSequence::setLabel(
2823 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2824 throw (uno::RuntimeException)
2826 SolarMutexGuard aGuard;
2827 if (bDisposed)
2828 throw lang::DisposedException();
2830 if (xLabels != rxSequence)
2832 SetDataSequence( xLabels, rxSequence );
2833 // inform listeners of changes
2834 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2838 uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( )
2839 throw (uno::RuntimeException)
2841 SolarMutexGuard aGuard;
2842 if (bDisposed)
2843 throw lang::DisposedException();
2845 uno::Reference< util::XCloneable > xRes;
2847 uno::Reference< util::XCloneable > xDataCloneable( xData, uno::UNO_QUERY );
2848 uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, uno::UNO_QUERY );
2849 SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence();
2850 if (xDataCloneable.is())
2852 uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
2853 pRes->setValues( xDataClone );
2856 if (xLabelsCloneable.is())
2858 uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
2859 pRes->setLabel( xLabelsClone );
2861 xRes = pRes;
2862 return xRes;
2865 OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( )
2866 throw (uno::RuntimeException)
2868 return OUString("SwChartLabeledDataSequence");
2871 sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
2872 const OUString& rServiceName )
2873 throw (uno::RuntimeException)
2875 return rServiceName == SN_LABELED_DATA_SEQUENCE;
2878 uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( )
2879 throw (uno::RuntimeException)
2881 SolarMutexGuard aGuard;
2882 uno::Sequence< OUString > aRes(1);
2883 aRes.getArray()[0] = SN_LABELED_DATA_SEQUENCE;
2884 return aRes;
2887 void SAL_CALL SwChartLabeledDataSequence::disposing(
2888 const lang::EventObject& rSource )
2889 throw (uno::RuntimeException)
2891 osl::MutexGuard aGuard( GetChartMutex() );
2892 uno::Reference< uno::XInterface > xRef( rSource.Source );
2893 if (xRef == xData)
2894 xData.clear();
2895 if (xRef == xLabels)
2896 xLabels.clear();
2897 if (!xData.is() && !xLabels.is())
2898 dispose();
2901 void SAL_CALL SwChartLabeledDataSequence::modified(
2902 const lang::EventObject& rEvent )
2903 throw (uno::RuntimeException)
2905 if (rEvent.Source == xData || rEvent.Source == xLabels)
2907 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2911 void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
2912 const uno::Reference< util::XModifyListener >& rxListener )
2913 throw (uno::RuntimeException)
2915 osl::MutexGuard aGuard( GetChartMutex() );
2916 if (!bDisposed && rxListener.is())
2917 aModifyListeners.addInterface( rxListener );
2920 void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
2921 const uno::Reference< util::XModifyListener >& rxListener )
2922 throw (uno::RuntimeException)
2924 osl::MutexGuard aGuard( GetChartMutex() );
2925 if (!bDisposed && rxListener.is())
2926 aModifyListeners.removeInterface( rxListener );
2929 void SAL_CALL SwChartLabeledDataSequence::dispose( )
2930 throw (uno::RuntimeException)
2932 bool bMustDispose( false );
2934 osl::MutexGuard aGuard( GetChartMutex() );
2935 bMustDispose = !bDisposed;
2936 if (!bDisposed)
2937 bDisposed = sal_True;
2939 if (bMustDispose)
2941 bDisposed = sal_True;
2943 // require listeners to release references to this object
2944 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) );
2945 aModifyListeners.disposeAndClear( aEvtObj );
2946 aEvtListeners.disposeAndClear( aEvtObj );
2950 void SAL_CALL SwChartLabeledDataSequence::addEventListener(
2951 const uno::Reference< lang::XEventListener >& rxListener )
2952 throw (uno::RuntimeException)
2954 osl::MutexGuard aGuard( GetChartMutex() );
2955 if (!bDisposed && rxListener.is())
2956 aEvtListeners.addInterface( rxListener );
2959 void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
2960 const uno::Reference< lang::XEventListener >& rxListener )
2961 throw (uno::RuntimeException)
2963 osl::MutexGuard aGuard( GetChartMutex() );
2964 if (!bDisposed && rxListener.is())
2965 aEvtListeners.removeInterface( rxListener );
2968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */