merge the formfield patch from ooo-build
[ooovba.git] / sw / source / core / unocore / unochart.cxx
blob1db4197de0445b59018c18924ddeb70074980541
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: unochart.cxx,v $
10 * $Revision: 1.18.36.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
34 #include <memory>
35 #include <algorithm>
38 #include <com/sun/star/chart/ChartDataRowSource.hpp>
39 #include <com/sun/star/chart2/data/LabelOrigin.hpp>
40 #include <cppuhelper/interfacecontainer.hxx>
41 #include <vos/mutex.hxx>
42 #include <osl/mutex.hxx>
43 #include <vcl/svapp.hxx>
44 #include <svtools/zforlist.hxx> // SvNumberFormatter
45 #include <svtools/chartprettypainter.hxx>
47 #include <tools/link.hxx>
49 #include <XMLRangeHelper.hxx>
50 #include <unochart.hxx>
51 #include <swtable.hxx>
52 #include <unoprnms.hxx>
53 #include <unomap.hxx>
54 #include <unoobj.hxx>
55 #include <unocrsr.hxx>
56 #include <unotbl.hxx>
57 #include <doc.hxx>
58 #include <frmfmt.hxx>
59 #include <docsh.hxx>
60 #include <ndole.hxx>
61 #include <swtable.hxx>
62 #include <swtypes.hxx>
63 #ifndef _UNOCORE_HRC
64 #include <unocore.hrc>
65 #endif
67 #include <docary.hxx>
69 #define SN_DATA_PROVIDER "com.sun.star.chart2.data.DataProvider"
70 #define SN_DATA_SOURCE "com.sun.star.chart2.data.DataSource"
71 #define SN_DATA_SEQUENCE "com.sun.star.chart2.data.DataSequence"
72 #define SN_LABELED_DATA_SEQUENCE "com.sun.star.chart2.data.LabeledDataSequence"
74 #define DIRECTION_DONT_KNOW -1
75 #define DIRECTION_HAS_ERROR -2
76 #define DIRECTION_COLS 0
77 #define DIRECTION_ROWS 1
79 using namespace ::com::sun::star;
80 using ::rtl::OUString;
82 // from unotbl.cxx
83 extern void lcl_GetCellPosition( const String &rCellName, sal_Int32 &rColumn, sal_Int32 &rRow);
84 extern String lcl_GetCellName( sal_Int32 nColumn, sal_Int32 nRow );
85 extern int lcl_CompareCellsByColFirst( const String &rCellName1, const String &rCellName2 );
86 extern int lcl_CompareCellsByRowFirst( const String &rCellName1, const String &rCellName2 );
87 extern int lcl_CompareCellRanges(
88 const String &rRange1StartCell, const String &rRange1EndCell,
89 const String &rRange2StartCell, const String &rRange2EndCell,
90 sal_Bool bCmpColsFirst );
91 extern void lcl_NormalizeRange( String &rCell1, String &rCell2 );
93 //////////////////////////////////////////////////////////////////////
95 //static
96 void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
98 if (!pDoc)
99 return;
101 uno::Reference< frame::XModel > xRes;
103 SwOLENode *pONd;
104 SwStartNode *pStNd;
105 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
106 while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
108 aIdx++;
109 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
110 ChartPrettyPainter::IsChart( pONd->GetOLEObj().GetObject() ) )
112 // Load the object and set modified
114 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
115 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
119 uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
120 xModif->setModified( sal_True );
122 catch ( uno::Exception& )
128 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
132 //////////////////////////////////////////////////////////////////////
134 SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
135 pDoc( pDocument )
137 aUnlockTimer.SetTimeout( 1500 );
138 aUnlockTimer.SetTimeoutHdl( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
142 SwChartLockController_Helper::~SwChartLockController_Helper()
144 if (pDoc) // still connected?
145 Disconnect();
149 void SwChartLockController_Helper::StartOrContinueLocking()
151 if (!bIsLocked)
152 LockAllCharts();
153 aUnlockTimer.Start(); // start or continue time of locking
157 void SwChartLockController_Helper::Disconnect()
159 aUnlockTimer.Stop();
160 UnlockAllCharts();
161 pDoc = 0;
165 void SwChartLockController_Helper::LockUnlockAllCharts( sal_Bool bLock )
167 if (!pDoc)
168 return;
170 const SwFrmFmts& rTblFmts = *pDoc->GetTblFrmFmts();
171 for( USHORT n = 0; n < rTblFmts.Count(); ++n )
173 SwTable* pTmpTbl;
174 const SwTableNode* pTblNd;
175 SwFrmFmt* pFmt = rTblFmts[ n ];
177 if( 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) &&
178 0 != ( pTblNd = pTmpTbl->GetTableNode() ) &&
179 pTblNd->GetNodes().IsDocNodes() )
181 uno::Reference< frame::XModel > xRes;
183 String aName( pTmpTbl->GetFrmFmt()->GetName() );
184 SwOLENode *pONd;
185 SwStartNode *pStNd;
186 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
187 while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
189 aIdx++;
190 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
191 pONd->GetChartTblName().Len() > 0 /* is chart object? */)
193 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
194 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
196 xRes = uno::Reference < frame::XModel >( xIP->getComponent(), uno::UNO_QUERY );
197 if (xRes.is())
199 if (bLock)
200 xRes->lockControllers();
201 else
202 xRes->unlockControllers();
206 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
211 bIsLocked = bLock;
215 IMPL_LINK( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, /*pTimer*/ )
217 UnlockAllCharts();
218 return 0;
222 //////////////////////////////////////////////////////////////////////
224 static osl::Mutex & GetChartMutex()
226 static osl::Mutex aMutex;
227 return aMutex;
231 static void LaunchModifiedEvent(
232 ::cppu::OInterfaceContainerHelper &rICH,
233 const uno::Reference< uno::XInterface > &rxI )
235 lang::EventObject aEvtObj( rxI );
236 cppu::OInterfaceIteratorHelper aIt( rICH );
237 while (aIt.hasMoreElements())
239 uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY );
240 if (xRef.is())
241 xRef->modified( aEvtObj );
245 //////////////////////////////////////////////////////////////////////
247 // rCellRangeName needs to be of one of the following formats:
248 // - e.g. "A2:E5" or
249 // - e.g. "Table1.A2:E5"
250 sal_Bool FillRangeDescriptor(
251 SwRangeDescriptor &rDesc,
252 const String &rCellRangeName )
254 xub_StrLen nToken = STRING_NOTFOUND == rCellRangeName.Search('.') ? 0 : 1;
255 String aCellRangeNoTableName( rCellRangeName.GetToken( nToken, '.' ) );
256 String aTLName( aCellRangeNoTableName.GetToken(0, ':') ); // name of top left cell
257 String aBRName( aCellRangeNoTableName.GetToken(1, ':') ); // name of bottom right cell
258 if(!aTLName.Len() || !aBRName.Len())
259 return sal_False;
261 rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
262 lcl_GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop );
263 lcl_GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
264 rDesc.Normalize();
265 DBG_ASSERT( rDesc.nTop != -1 &&
266 rDesc.nLeft != -1 &&
267 rDesc.nBottom != -1 &&
268 rDesc.nRight != -1,
269 "failed to get range descriptor" );
270 DBG_ASSERT( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight,
271 "invalid range descriptor");
272 return sal_True;
276 static String GetCellRangeName( SwFrmFmt &rTblFmt, SwUnoCrsr &rTblCrsr )
278 String aRes;
280 //!! see also SwXTextTableCursor::getRangeName
282 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(&rTblCrsr);
283 if (!pUnoTblCrsr)
284 return String();
285 pUnoTblCrsr->MakeBoxSels();
287 const SwStartNode* pStart;
288 const SwTableBox* pStartBox = 0;
289 const SwTableBox* pEndBox = 0;
291 pStart = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
292 if (pStart)
294 const SwTable* pTable = SwTable::FindTable( &rTblFmt );
295 pEndBox = pTable->GetTblBox( pStart->GetIndex());
296 aRes = pEndBox->GetName();
298 if(pUnoTblCrsr->HasMark())
300 pStart = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
301 pStartBox = pTable->GetTblBox( pStart->GetIndex());
303 DBG_ASSERT( pStartBox, "start box not found" );
304 DBG_ASSERT( pEndBox, "end box not found" );
305 // need to switch start and end?
306 if (*pUnoTblCrsr->GetPoint() < *pUnoTblCrsr->GetMark())
308 const SwTableBox* pTmpBox = pStartBox;
309 pStartBox = pEndBox;
310 pEndBox = pTmpBox;
313 aRes = pStartBox->GetName();
314 aRes += (sal_Unicode)':';
315 if (pEndBox)
316 aRes += pEndBox->GetName();
317 else
318 aRes += pStartBox->GetName();
321 return aRes;
325 static String GetRangeRepFromTableAndCells( const String &rTableName,
326 const String &rStartCell, const String &rEndCell,
327 sal_Bool bForceEndCellName )
329 DBG_ASSERT( rTableName.Len(), "table name missing" );
330 DBG_ASSERT( rStartCell.Len(), "cell name missing" );
331 String aRes( rTableName );
332 aRes += (sal_Unicode) '.';
333 aRes += rStartCell;
335 if (rEndCell.Len())
337 aRes += (sal_Unicode) ':';
338 aRes += rEndCell;
340 else if (bForceEndCellName)
342 aRes += (sal_Unicode) ':';
343 aRes += rStartCell;
346 return aRes;
350 static sal_Bool GetTableAndCellsFromRangeRep(
351 const OUString &rRangeRepresentation,
352 String &rTblName,
353 String &rStartCell,
354 String &rEndCell,
355 sal_Bool bSortStartEndCells = sal_True )
357 // parse range representation for table name and cell/range names
358 // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
359 String aTblName; // table name
360 OUString aRange; // cell range
361 String aStartCell; // name of top left cell
362 String aEndCell; // name of bottom right cell
363 sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' );
364 if (nIdx >= 0)
366 aTblName = rRangeRepresentation.copy( 0, nIdx );
367 aRange = rRangeRepresentation.copy( nIdx + 1 );
368 sal_Int32 nPos = aRange.indexOf( ':' );
369 if (nPos >= 0) // a cell-range like "Table1.A2:D4"
371 aStartCell = aRange.copy( 0, nPos );
372 aEndCell = aRange.copy( nPos + 1 );
374 // need to switch start and end cell ?
375 // (does not check for normalization here)
376 if (bSortStartEndCells && 1 == lcl_CompareCellsByColFirst( aStartCell, aEndCell ))
378 String aTmp( aStartCell );
379 aStartCell = aEndCell;
380 aEndCell = aTmp;
383 else // a single cell like in "Table1.B3"
385 aStartCell = aEndCell = aRange;
389 sal_Bool bSuccess = aTblName.Len() != 0 &&
390 aStartCell.Len() != 0 && aEndCell.Len() != 0;
391 if (bSuccess)
393 rTblName = aTblName;
394 rStartCell = aStartCell;
395 rEndCell = aEndCell;
397 return bSuccess;
401 static void GetTableByName( const SwDoc &rDoc, const String &rTableName,
402 SwFrmFmt **ppTblFmt, SwTable **ppTable)
404 SwFrmFmt *pTblFmt = NULL;
406 // find frame format of table
407 //! see SwXTextTables::getByName
408 sal_uInt16 nCount = rDoc.GetTblFrmFmtCount(sal_True);
409 for (sal_uInt16 i = 0; i < nCount && !pTblFmt; ++i)
411 SwFrmFmt& rTblFmt = rDoc.GetTblFrmFmt(i, sal_True);
412 if(rTableName == rTblFmt.GetName())
413 pTblFmt = &rTblFmt;
416 if (ppTblFmt)
417 *ppTblFmt = pTblFmt;
419 if (ppTable)
420 *ppTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
424 static void GetFormatAndCreateCursorFromRangeRep(
425 const SwDoc *pDoc,
426 const OUString &rRangeRepresentation, // must be a single range (i.e. so called sub-range)
427 SwFrmFmt **ppTblFmt, // will be set to the table format of the table used in the range representation
428 SwUnoCrsr **ppUnoCrsr ) // will be set to cursor spanning the cell range
429 // (cursor will be created!)
431 String aTblName; // table name
432 String aStartCell; // name of top left cell
433 String aEndCell; // name of bottom right cell
434 sal_Bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
435 aTblName, aStartCell, aEndCell );
437 if (!bNamesFound)
439 if (ppTblFmt)
440 *ppTblFmt = NULL;
441 if (ppUnoCrsr)
442 *ppUnoCrsr = NULL;
444 else
446 SwFrmFmt *pTblFmt = NULL;
448 // is the correct table format already provided?
449 if (*ppTblFmt != NULL && (*ppTblFmt)->GetName() == aTblName)
450 pTblFmt = *ppTblFmt;
451 else if (ppTblFmt)
452 GetTableByName( *pDoc, aTblName, &pTblFmt, NULL );
454 if (ppTblFmt)
455 *ppTblFmt = pTblFmt;
457 if (ppUnoCrsr != NULL)
459 *ppUnoCrsr = NULL; // default result in case of failure
461 SwTable *pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
462 // create new SwUnoCrsr spanning the specified range
463 //! see also SwXTextTable::GetRangeByName
464 // --> OD 2007-08-03 #i80314#
465 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
466 const SwTableBox* pTLBox =
467 pTable ? pTable->GetTblBox( aStartCell, true ) : 0;
468 // <--
469 if(pTLBox)
471 // hier muessen die Actions aufgehoben werden
472 UnoActionRemoveContext aRemoveContext(pTblFmt->GetDoc());
473 const SwStartNode* pSttNd = pTLBox->GetSttNd();
474 SwPosition aPos(*pSttNd);
475 // set cursor to top left box of range
476 SwUnoCrsr* pUnoCrsr = pTblFmt->GetDoc()->CreateUnoCrsr(aPos, sal_True);
477 pUnoCrsr->Move( fnMoveForward, fnGoNode );
478 pUnoCrsr->SetRemainInSection( sal_False );
479 // --> OD 2007-08-03 #i80314#
480 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
481 const SwTableBox* pBRBox = pTable->GetTblBox( aEndCell, true );
482 // <--
483 if(pBRBox)
485 pUnoCrsr->SetMark();
486 pUnoCrsr->GetPoint()->nNode = *pBRBox->GetSttNd();
487 pUnoCrsr->Move( fnMoveForward, fnGoNode );
488 SwUnoTableCrsr* pCrsr =
489 dynamic_cast<SwUnoTableCrsr*>(pUnoCrsr);
490 pCrsr->MakeBoxSels();
492 if (ppUnoCrsr)
493 *ppUnoCrsr = pCrsr;
495 else
497 delete pUnoCrsr;
505 static sal_Bool GetSubranges( const OUString &rRangeRepresentation,
506 uno::Sequence< OUString > &rSubRanges, sal_Bool bNormalize )
508 sal_Bool bRes = sal_True;
509 String aRangesStr( rRangeRepresentation );
510 xub_StrLen nLen = aRangesStr.GetTokenCount( ';' );
511 uno::Sequence< OUString > aRanges( nLen );
513 sal_Int32 nCnt = 0;
514 if (nLen != 0)
516 OUString *pRanges = aRanges.getArray();
517 String aFirstTable;
518 for ( xub_StrLen i = 0; i < nLen && bRes; ++i)
520 String aRange( aRangesStr.GetToken( i, ';' ) );
521 if (aRange.Len())
523 pRanges[nCnt] = aRange;
525 String aTableName, aStartCell, aEndCell;
526 bRes &= GetTableAndCellsFromRangeRep( aRange,
527 aTableName, aStartCell, aEndCell );
529 if (bNormalize)
531 lcl_NormalizeRange( aStartCell, aEndCell );
532 pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
533 aStartCell, aEndCell, sal_True );
536 // make sure to use only a single table
537 if (nCnt == 0)
538 aFirstTable = aTableName;
539 else
540 bRes &= aFirstTable == aTableName;
542 ++nCnt;
546 aRanges.realloc( nCnt );
548 rSubRanges = aRanges;
549 return bRes;
553 static void SortSubranges( uno::Sequence< OUString > &rSubRanges, sal_Bool bCmpByColumn )
555 sal_Int32 nLen = rSubRanges.getLength();
556 OUString *pSubRanges = rSubRanges.getArray();
558 String aSmallestTblName;
559 String aSmallestStartCell;
560 String aSmallestEndCell;
562 for (sal_Int32 i = 0; i < nLen; ++i)
564 sal_Int32 nIdxOfSmallest = i;
565 GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
566 aSmallestTblName, aSmallestStartCell, aSmallestEndCell );
567 if (aSmallestEndCell.Len() == 0)
568 aSmallestEndCell = aSmallestStartCell;
570 for (sal_Int32 k = i+1; k < nLen; ++k)
572 // get cell names for sub range
573 String aTblName;
574 String aStartCell;
575 String aEndCell;
576 GetTableAndCellsFromRangeRep( pSubRanges[k],
577 aTblName, aStartCell, aEndCell );
578 if (aEndCell.Len() == 0)
579 aEndCell = aStartCell;
581 // compare cell ranges ( is the new one smaller? )
582 if (-1 == lcl_CompareCellRanges( aStartCell, aEndCell,
583 aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
585 nIdxOfSmallest = k;
586 aSmallestTblName = aTblName;
587 aSmallestStartCell = aStartCell;
588 aSmallestEndCell = aEndCell;
592 // move smallest element to the start of the not sorted area
593 OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
594 pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
595 pSubRanges[ i ] = aTmp;
599 //////////////////////////////////////////////////////////////////////
601 SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) :
602 aEvtListeners( GetChartMutex() ),
603 pDoc( pSwDoc )
605 bDisposed = sal_False;
609 SwChartDataProvider::~SwChartDataProvider()
613 uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
614 const uno::Sequence< beans::PropertyValue >& rArguments, sal_Bool bTestOnly )
615 throw (lang::IllegalArgumentException, uno::RuntimeException)
617 vos::OGuard aGuard( Application::GetSolarMutex() );
618 if (bDisposed)
619 throw lang::DisposedException();
621 uno::Reference< chart2::data::XDataSource > xRes;
623 if (!pDoc)
624 throw uno::RuntimeException();
626 // get arguments
627 OUString aRangeRepresentation;
628 uno::Sequence< sal_Int32 > aSequenceMapping;
629 sal_Bool bFirstIsLabel = sal_False;
630 sal_Bool bDtaSrcIsColumns = sal_True; // true : DataSource will be sequence of columns
631 // false: DataSource will be sequence of rows
632 OUString aChartOleObjectName;//work around wrong writer ranges ( see Issue 58464 )
633 sal_Int32 nArgs = rArguments.getLength();
634 DBG_ASSERT( nArgs != 0, "no properties provided" );
635 if (nArgs == 0)
636 return xRes;
637 const beans::PropertyValue *pArg = rArguments.getConstArray();
638 for (sal_Int32 i = 0; i < nArgs; ++i)
640 if (pArg[i].Name.equalsAscii( "DataRowSource" ))
642 chart::ChartDataRowSource eSource;
643 if (!(pArg[i].Value >>= eSource))
645 sal_Int32 nTmp = 0;
646 if (!(pArg[i].Value >>= nTmp))
647 throw lang::IllegalArgumentException();
648 eSource = static_cast< chart::ChartDataRowSource >( nTmp );
650 bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
652 else if (pArg[i].Name.equalsAscii( "FirstCellAsLabel" ))
654 if (!(pArg[i].Value >>= bFirstIsLabel))
655 throw lang::IllegalArgumentException();
657 else if (pArg[i].Name.equalsAscii( "CellRangeRepresentation" ))
659 if (!(pArg[i].Value >>= aRangeRepresentation))
660 throw lang::IllegalArgumentException();
662 else if (pArg[i].Name.equalsAscii( "SequenceMapping" ))
664 if (!(pArg[i].Value >>= aSequenceMapping))
665 throw lang::IllegalArgumentException();
667 else if (pArg[i].Name.equalsAscii( "ChartOleObjectName" ))
669 if (!(pArg[i].Value >>= aChartOleObjectName))
670 throw lang::IllegalArgumentException();
674 uno::Sequence< OUString > aSubRanges;
675 // get sub-ranges and check that they all are from the very same table
676 sal_Bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
678 if (!bOk && pDoc && aChartOleObjectName.getLength() )
680 //try to correct the range here
681 //work around wrong writer ranges ( see Issue 58464 )
682 String aChartTableName;
684 const SwNodes& rNodes = pDoc->GetNodes();
685 for( ULONG nN = rNodes.Count(); nN--; )
687 SwNodePtr pNode = rNodes[nN];
688 if( !pNode )
689 continue;
690 const SwOLENode* pOleNode = pNode->GetOLENode();
691 if( !pOleNode )
692 continue;
693 const SwOLEObj& rOObj = pOleNode->GetOLEObj();
694 if( aChartOleObjectName.equals( rOObj.GetCurrentPersistName() ) )
696 aChartTableName = pOleNode->GetChartTblName();
697 break;
701 if( aChartTableName.Len() )
703 //the wrong range is still shifted one row down
704 //thus the first row is missing and an invalid row at the end is added.
705 //Therefore we need to shift the range one row up
706 SwRangeDescriptor aDesc;
707 if (aRangeRepresentation.getLength() == 0)
708 return xRes; // we cant handle this thus returning an empty references
709 aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left
710 FillRangeDescriptor( aDesc, aRangeRepresentation );
711 aDesc.Normalize();
712 if (aDesc.nTop <= 0) // no chance to shift the range one row up?
713 return xRes; // we cant handle this thus returning an empty references
714 aDesc.nTop -= 1;
715 aDesc.nBottom -= 1;
717 String aNewStartCell( lcl_GetCellName( aDesc.nLeft, aDesc.nTop ) );
718 String aNewEndCell( lcl_GetCellName( aDesc.nRight, aDesc.nBottom ) );
719 aRangeRepresentation = GetRangeRepFromTableAndCells(
720 aChartTableName, aNewStartCell, aNewEndCell, sal_True );
721 bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
724 if (!bOk) // different tables used, or incorrect range specifiers
725 throw lang::IllegalArgumentException();
727 SortSubranges( aSubRanges, bDtaSrcIsColumns );
728 const OUString *pSubRanges = aSubRanges.getConstArray();
729 #if OSL_DEBUG_LEVEL > 1
731 sal_Int32 nSR = aSubRanges.getLength();
732 OUString *pSR = aSubRanges.getArray();
733 OUString aRg;
734 for (sal_Int32 i = 0; i < nSR; ++i)
736 aRg = pSR[i];
739 #endif
741 // get table format for that single table from above
742 SwFrmFmt *pTblFmt = 0; // pointer to table format
743 SwUnoCrsr *pUnoCrsr = 0; // here required to check if the cells in the range do actually exist
744 std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr ); // to end lifetime of object pointed to by pUnoCrsr
745 if (aSubRanges.getLength() > 0)
746 GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTblFmt, &pUnoCrsr );
747 if (!pTblFmt || !pUnoCrsr)
748 throw lang::IllegalArgumentException();
750 if(pTblFmt)
752 SwTable* pTable = SwTable::FindTable( pTblFmt );
753 if(pTable->IsTblComplex())
754 return xRes; // we cant handle this thus returning an empty references
755 else
757 // get a character map in the size of the table to mark
758 // all the ranges to use in
759 sal_Int32 nRows = pTable->GetTabLines().Count();
760 sal_Int32 nCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count();
761 std::vector< std::vector< sal_Char > > aMap( nRows );
762 for (sal_Int32 i = 0; i < nRows; ++i)
763 aMap[i].resize( nCols );
765 // iterate over subranges and mark used cells in above map
766 //!! by proceeding this way we automatically get rid of
767 //!! multiple listed or overlapping cell ranges which should
768 //!! just be ignored silently
769 sal_Int32 nSubRanges = aSubRanges.getLength();
770 for (sal_Int32 i = 0; i < nSubRanges; ++i)
772 String aTblName, aStartCell, aEndCell;
773 sal_Bool bOk2 = GetTableAndCellsFromRangeRep(
774 pSubRanges[i], aTblName, aStartCell, aEndCell );
775 (void) bOk2;
776 DBG_ASSERT( bOk2, "failed to get table and start/end cells" );
778 sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
779 lcl_GetCellPosition( aStartCell, nStartCol, nStartRow );
780 lcl_GetCellPosition( aEndCell, nEndCol, nEndRow );
781 DBG_ASSERT( nStartRow <= nEndRow && nStartCol <= nEndCol,
782 "cell range not normalized");
784 // test if the ranges span more than the available cells
785 if( nStartRow < 0 || nEndRow >= nRows ||
786 nStartCol < 0 || nEndCol >= nCols )
788 throw lang::IllegalArgumentException();
790 for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1)
792 for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2)
793 aMap[k1][k2] = 'x';
798 // find label and data sequences to use
800 sal_Int32 oi; // outer index (slower changing index)
801 sal_Int32 ii; // inner index (faster changing index)
802 sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
803 sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
804 std::vector< sal_Int32 > aLabelIdx( oiEnd );
805 std::vector< sal_Int32 > aDataStartIdx( oiEnd );
806 std::vector< sal_Int32 > aDataLen( oiEnd );
807 for (oi = 0; oi < oiEnd; ++oi)
809 aLabelIdx[oi] = -1;
810 aDataStartIdx[oi] = -1;
811 aDataLen[oi] = 0;
814 for (oi = 0; oi < oiEnd; ++oi)
816 ii = 0;
817 while (ii < iiEnd)
819 sal_Char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
821 // label should be used but is not yet found?
822 if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
824 aLabelIdx[oi] = ii;
825 rChar = 'L'; // setting a different char for labels here
826 // makes the test for the data sequence below
827 // easier
830 // find data sequence
831 if (rChar == 'x' && aDataStartIdx[oi] == -1)
833 aDataStartIdx[oi] = ii;
835 // get length of data sequence
836 sal_Int32 nL = 0;
837 sal_Char c;
838 while (ii< iiEnd && 'x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
840 ++nL; ++ii;
842 aDataLen[oi] = nL;
844 // check that there is no other seperate sequence of data
845 // to be found because that is not supported
846 while (ii < iiEnd)
848 if ('x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
849 throw lang::IllegalArgumentException();
850 ++ii;
853 else
854 ++ii;
858 // make some other consistency checks while calculating
859 // the number of XLabeledDataSequence to build:
860 // - labels should always be used or not at all
861 // - the data sequences should have equal non-zero length
862 sal_Int32 nNumLDS = 0;
863 if (oiEnd > 0)
865 sal_Int32 nFirstSeqLen = 0;
866 sal_Int32 nFirstSeqLabelIdx = -1;
867 for (oi = 0; oi < oiEnd; ++oi)
869 sal_Bool bFirstFound = sal_False;
870 // row/col used at all?
871 if (aDataStartIdx[oi] != -1 &&
872 (!bFirstIsLabel || aLabelIdx[oi] != -1))
874 ++nNumLDS;
875 if (!bFirstFound)
877 nFirstSeqLen = aDataLen[oi];
878 nFirstSeqLabelIdx = aLabelIdx[oi];
879 bFirstFound = sal_True;
881 else
883 if (nFirstSeqLen != aDataLen[oi] ||
884 nFirstSeqLabelIdx != aLabelIdx[oi])
885 throw lang::IllegalArgumentException();
890 if (nNumLDS == 0)
891 throw lang::IllegalArgumentException();
893 // now we should have all necessary data to build a proper DataSource
894 // thus if we came this far there should be no further problem
895 if (bTestOnly)
896 return xRes; // have createDataSourcePossible return true
898 // create data source from found label and data sequences
899 uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aLabelSeqs( nNumLDS );
900 uno::Reference< chart2::data::XDataSequence > *pLabelSeqs = aLabelSeqs.getArray();
901 uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aDataSeqs( nNumLDS );
902 uno::Reference< chart2::data::XDataSequence > *pDataSeqs = aDataSeqs.getArray();
903 sal_Int32 nSeqsIdx = 0;
904 for (oi = 0; oi < oiEnd; ++oi)
906 // row/col not used? (see if-statement above where nNumLDS was counted)
907 if (!(aDataStartIdx[oi] != -1 &&
908 (!bFirstIsLabel || aLabelIdx[oi] != -1)))
909 continue;
911 // get cell ranges for label and data
913 SwRangeDescriptor aLabelDesc;
914 SwRangeDescriptor aDataDesc;
915 if (bDtaSrcIsColumns) // use columns
917 aLabelDesc.nTop = aLabelIdx[oi];
918 aLabelDesc.nLeft = oi;
919 aLabelDesc.nBottom = aLabelDesc.nTop;
920 aLabelDesc.nRight = oi;
922 aDataDesc.nTop = aDataStartIdx[oi];
923 aDataDesc.nLeft = oi;
924 aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1;
925 aDataDesc.nRight = oi;
927 else // use rows
929 aLabelDesc.nTop = oi;
930 aLabelDesc.nLeft = aLabelIdx[oi];
931 aLabelDesc.nBottom = oi;
932 aLabelDesc.nRight = aLabelDesc.nLeft;
934 aDataDesc.nTop = oi;
935 aDataDesc.nLeft = aDataStartIdx[oi];
936 aDataDesc.nBottom = oi;
937 aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1;
939 String aBaseName( pTblFmt->GetName() );
940 aBaseName += '.';
942 String aLabelRange;
943 if (aLabelIdx[oi] != -1)
945 aLabelRange += aBaseName;
946 aLabelRange += lcl_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop );
947 aLabelRange += ':';
948 aLabelRange += lcl_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
951 String aDataRange;
952 if (aDataStartIdx[oi] != -1)
954 aDataRange += aBaseName;
955 aDataRange += lcl_GetCellName( aDataDesc.nLeft, aDataDesc.nTop );
956 aDataRange += ':';
957 aDataRange += lcl_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
960 // get cursors spanning the cell ranges for label and data
961 SwUnoCrsr *pLabelUnoCrsr = 0;
962 SwUnoCrsr *pDataUnoCrsr = 0;
963 GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTblFmt, &pLabelUnoCrsr);
964 GetFormatAndCreateCursorFromRangeRep( pDoc, aDataRange, &pTblFmt, &pDataUnoCrsr);
966 // create XDataSequence's from cursors
967 if (pLabelUnoCrsr)
968 pLabelSeqs[ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pLabelUnoCrsr );
969 DBG_ASSERT( pDataUnoCrsr, "pointer to data sequence missing" );
970 if (pDataUnoCrsr)
971 pDataSeqs [ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pDataUnoCrsr );
972 if (pLabelUnoCrsr || pDataUnoCrsr)
973 ++nSeqsIdx;
975 DBG_ASSERT( nSeqsIdx == nNumLDS,
976 "mismatch between sequence size and num,ber of entries" );
978 // build data source from data and label sequences
979 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLDS( nNumLDS );
980 uno::Reference< chart2::data::XLabeledDataSequence > *pLDS = aLDS.getArray();
981 for (sal_Int32 i = 0; i < nNumLDS; ++i)
983 SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence;
984 pLabeledDtaSeq->setLabel( pLabelSeqs[i] );
985 pLabeledDtaSeq->setValues( pDataSeqs[i] );
986 pLDS[i] = pLabeledDtaSeq;
989 // apply 'SequenceMapping' if it was provided
990 sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength();
991 if (nSequenceMappingLen)
993 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
994 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aOld_LDS( aLDS );
995 uno::Reference< chart2::data::XLabeledDataSequence > *pOld_LDS = aOld_LDS.getArray();
997 sal_Int32 nNewCnt = 0;
998 for (sal_Int32 i = 0; i < nSequenceMappingLen; ++i)
1000 // check that index to be used is valid
1001 // and has not yet been used
1002 sal_Int32 nIdx = pSequenceMapping[i];
1003 if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
1005 pLDS[nNewCnt++] = pOld_LDS[nIdx];
1007 // mark index as being used already (avoids duplicate entries)
1008 pOld_LDS[nIdx].clear();
1011 // add not yet used 'old' sequences to new one
1012 for (sal_Int32 i = 0; i < nNumLDS; ++i)
1014 #if OSL_DEBUG_LEVEL > 1
1015 if (!pOld_LDS[i].is())
1016 i = i;
1017 #endif
1018 if (pOld_LDS[i].is())
1019 pLDS[nNewCnt++] = pOld_LDS[i];
1021 DBG_ASSERT( nNewCnt == nNumLDS, "unexpected size of resulting sequence" );
1024 xRes = new SwChartDataSource( aLDS );
1028 return xRes;
1031 sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
1032 const uno::Sequence< beans::PropertyValue >& rArguments )
1033 throw (uno::RuntimeException)
1035 vos::OGuard aGuard( Application::GetSolarMutex() );
1037 sal_Bool bPossible = sal_True;
1040 Impl_createDataSource( rArguments, sal_True );
1042 catch (lang::IllegalArgumentException &)
1044 bPossible = sal_False;
1047 return bPossible;
1050 uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
1051 const uno::Sequence< beans::PropertyValue >& rArguments )
1052 throw (lang::IllegalArgumentException, uno::RuntimeException)
1054 vos::OGuard aGuard( Application::GetSolarMutex() );
1055 return Impl_createDataSource( rArguments );
1058 ////////////////////////////////////////////////////////////
1059 // SwChartDataProvider::GetBrokenCellRangeForExport
1061 // fix for #i79009
1062 // we need to return a property that has the same value as the property
1063 // 'CellRangeRepresentation' but for all rows which are increased by one.
1064 // E.g. Table1:A1:D5 -> Table1:A2:D6
1065 // Since the problem is only for old charts which did not support multiple
1066 // we do not need to provide that property/string if the 'CellRangeRepresentation'
1067 // contains multiple ranges.
1068 OUString SwChartDataProvider::GetBrokenCellRangeForExport(
1069 const OUString &rCellRangeRepresentation )
1071 OUString aRes;
1073 // check that we do not have multiple ranges
1074 if (-1 == rCellRangeRepresentation.indexOf( ';' ))
1076 // get current cell and table names
1077 String aTblName, aStartCell, aEndCell;
1078 GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
1079 aTblName, aStartCell, aEndCell, sal_False );
1080 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1081 lcl_GetCellPosition( aStartCell, nStartCol, nStartRow );
1082 lcl_GetCellPosition( aEndCell, nEndCol, nEndRow );
1084 // get new cell names
1085 ++nStartRow;
1086 ++nEndRow;
1087 aStartCell = lcl_GetCellName( nStartCol, nStartRow );
1088 aEndCell = lcl_GetCellName( nEndCol, nEndRow );
1090 aRes = GetRangeRepFromTableAndCells( aTblName,
1091 aStartCell, aEndCell, sal_False );
1094 return aRes;
1097 uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
1098 const uno::Reference< chart2::data::XDataSource >& xDataSource )
1099 throw (uno::RuntimeException)
1101 vos::OGuard aGuard( Application::GetSolarMutex() );
1102 if (bDisposed)
1103 throw lang::DisposedException();
1105 uno::Sequence< beans::PropertyValue > aResult;
1106 if (!xDataSource.is())
1107 return aResult;
1109 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
1110 const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
1111 sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
1113 if (nNumDS_LDS == 0)
1115 DBG_WARNING( "XLabeledDataSequence in data source contains 0 entries" );
1116 return aResult;
1119 SwFrmFmt *pTableFmt = 0;
1120 SwTable *pTable = 0;
1121 String aTableName;
1122 sal_Int32 nTableRows = 0;
1123 sal_Int32 nTableCols = 0;
1125 // data used to build 'CellRangeRepresentation' from later on
1126 std::vector< std::vector< sal_Char > > aMap;
1128 uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
1129 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
1131 String aCellRanges;
1132 sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither
1133 sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have
1134 // the expected size of 1 (i.e. if FirstCellAsLabel can
1135 // be determined)
1136 // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
1137 // -2: neither/failed
1138 // sal_Int32 nValuesSeqLen = -1; // used to see if all value sequences have the same size
1139 for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1)
1141 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
1142 if( !xLabeledDataSequence.is() )
1144 DBG_ERROR("got NULL for XLabeledDataSequence from Data source");
1145 continue;
1147 const uno::Reference< chart2::data::XDataSequence > xCurLabel( xLabeledDataSequence->getLabel(), uno::UNO_QUERY );
1148 const uno::Reference< chart2::data::XDataSequence > xCurValues( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
1150 // get sequence lengths for label and values.
1151 // (0 length is Ok)
1152 sal_Int32 nCurLabelSeqLen = -1;
1153 sal_Int32 nCurValuesSeqLen = -1;
1154 if (xCurLabel.is())
1155 nCurLabelSeqLen = xCurLabel->getData().getLength();
1156 if (xCurValues.is())
1157 nCurValuesSeqLen = xCurValues->getData().getLength();
1159 // check for consistent use of 'first cell as label'
1160 if (nLabelSeqLen == -1) // set initial value to compare with below further on
1161 nLabelSeqLen = nCurLabelSeqLen;
1162 if (nLabelSeqLen != nCurLabelSeqLen)
1163 nLabelSeqLen = -2; // failed / no consistent use of label cells
1165 // get table and cell names for label and values data sequences
1166 // (start and end cell will be sorted, i.e. start cell <= end cell)
1167 String aLabelTblName, aLabelStartCell, aLabelEndCell;
1168 String aValuesTblName, aValuesStartCell, aValuesEndCell;
1169 String aLabelRange, aValuesRange;
1170 if (xCurLabel.is())
1171 aLabelRange = xCurLabel->getSourceRangeRepresentation();
1172 if (xCurValues.is())
1173 aValuesRange = xCurValues->getSourceRangeRepresentation();
1174 if ((aLabelRange.Len() && !GetTableAndCellsFromRangeRep( aLabelRange,
1175 aLabelTblName, aLabelStartCell, aLabelEndCell )) ||
1176 !GetTableAndCellsFromRangeRep( aValuesRange,
1177 aValuesTblName, aValuesStartCell, aValuesEndCell ))
1179 return aResult; // failed -> return empty property sequence
1182 // make sure all sequences use the same table
1183 if (!aTableName.Len())
1184 aTableName = aValuesTblName; // get initial value to compare with
1185 if (!aTableName.Len() ||
1186 aTableName != aValuesTblName ||
1187 (aLabelTblName.Len() && aTableName != aLabelTblName))
1189 return aResult; // failed -> return empty property sequence
1193 // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
1194 // first and last cell used in both sequences
1196 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1197 String aCell( aLabelStartCell.Len() ? aLabelStartCell : aValuesStartCell );
1198 DBG_ASSERT( aCell.Len() , "start cell missing?" );
1199 lcl_GetCellPosition( aCell, nFirstCol, nFirstRow);
1200 lcl_GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
1202 sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed
1203 if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
1205 DBG_ASSERT( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
1206 "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
1207 nDirection = 0; // default direction for a single cell should be 'columns'
1209 else // more than one cell is availabale (in values and label together!)
1211 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1212 nDirection = 1;
1213 else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
1214 nDirection = 0;
1215 else
1217 DBG_ERROR( "trying to determine 'DataRowSource': unexpected case found" );
1218 nDirection = -2;
1221 // check for consistent direction of data source
1222 if (nDtaSrcIsColumns == -1) // set initial value to compare with below
1223 nDtaSrcIsColumns = nDirection;
1224 if (nDtaSrcIsColumns != nDirection)
1226 nDtaSrcIsColumns = -2; // failed
1230 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1232 // build data to obtain 'SequenceMapping' later on
1234 DBG_ASSERT( nDtaSrcIsColumns == 0 || /* rows */
1235 nDtaSrcIsColumns == 1, /* columns */
1236 "unexpected value for 'nDtaSrcIsColumns'" );
1237 pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
1240 // build data used to determine 'CellRangeRepresentation' later on
1242 GetTableByName( *pDoc, aTableName, &pTableFmt, &pTable );
1243 if (!pTable || pTable->IsTblComplex())
1244 return aResult; // failed -> return empty property sequence
1245 nTableRows = pTable->GetTabLines().Count();
1246 nTableCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count();
1247 aMap.resize( nTableRows );
1248 for (sal_Int32 i = 0; i < nTableRows; ++i)
1249 aMap[i].resize( nTableCols );
1251 if (aLabelStartCell.Len() && aLabelEndCell.Len())
1253 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1254 lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
1255 lcl_GetCellPosition( aLabelEndCell, nEndCol, nEndRow );
1256 if (nStartRow < 0 || nEndRow >= nTableRows ||
1257 nStartCol < 0 || nEndCol >= nTableCols)
1259 return aResult; // failed -> return empty property sequence
1261 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1263 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1265 sal_Char &rChar = aMap[i][k];
1266 if (rChar == '\0') // check for overlapping values and/or labels
1267 rChar = 'L';
1268 else
1269 return aResult; // failed -> return empty property sequence
1273 if (aValuesStartCell.Len() && aValuesEndCell.Len())
1275 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1276 lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
1277 lcl_GetCellPosition( aValuesEndCell, nEndCol, nEndRow );
1278 if (nStartRow < 0 || nEndRow >= nTableRows ||
1279 nStartCol < 0 || nEndCol >= nTableCols)
1281 return aResult; // failed -> return empty property sequence
1283 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1285 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1287 sal_Char &rChar = aMap[i][k];
1288 if (rChar == '\0') // check for overlapping values and/or labels
1289 rChar = 'x';
1290 else
1291 return aResult; // failed -> return empty property sequence
1297 #if OSL_DEBUG_LEVEL > 1
1298 // do some extra sanity checking that the length of the sequences
1299 // matches their range representation
1301 sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
1302 if (xCurLabel.is())
1304 lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
1305 lcl_GetCellPosition( aLabelEndCell, nEndCol, nEndRow);
1306 DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
1307 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
1308 "label sequence length does not match range representation!" );
1310 if (xCurValues.is())
1312 lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
1313 lcl_GetCellPosition( aValuesEndCell, nEndCol, nEndRow);
1314 DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
1315 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
1316 "value sequence length does not match range representation!" );
1319 #endif
1320 } // for
1323 // build value for 'CellRangeRepresentation'
1325 String aCellRangeBase( aTableName );
1326 aCellRangeBase += '.';
1327 String aCurRange;
1328 for (sal_Int32 i = 0; i < nTableRows; ++i)
1330 for (sal_Int32 k = 0; k < nTableCols; ++k)
1332 if (aMap[i][k] != '\0') // top-left cell of a sub-range found
1334 // find rectangular sub-range to use
1335 sal_Int32 nRowIndex1 = i; // row index
1336 sal_Int32 nColIndex1 = k; // column index
1337 sal_Int32 nRowSubLen = 0;
1338 sal_Int32 nColSubLen = 0;
1339 while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
1340 ++nRowSubLen;
1341 // be aware of shifted sequences!
1342 // (according to the checks done prior the length should be ok)
1343 while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
1344 && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
1346 ++nColIndex1;
1347 ++nColSubLen;
1349 String aStartCell( lcl_GetCellName( k, i ) );
1350 String aEndCell( lcl_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
1351 aCurRange = aCellRangeBase;
1352 aCurRange += aStartCell;
1353 aCurRange += ':';
1354 aCurRange += aEndCell;
1355 if (aCellRanges.Len())
1356 aCellRanges += ';';
1357 aCellRanges += aCurRange;
1359 // clear already found sub-range from map
1360 for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2)
1361 for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2)
1362 aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
1366 // to be nice to the user we now sort the cell ranges according to
1367 // rows or columns depending on the direction used in the data source
1368 uno::Sequence< OUString > aSortedRanges;
1369 GetSubranges( aCellRanges, aSortedRanges, sal_False /*sub ranges should already be normalized*/ );
1370 SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
1371 sal_Int32 nSortedRanges = aSortedRanges.getLength();
1372 const OUString *pSortedRanges = aSortedRanges.getConstArray();
1373 OUString aSortedCellRanges;
1374 for (sal_Int32 i = 0; i < nSortedRanges; ++i)
1376 if (aSortedCellRanges.getLength())
1377 aSortedCellRanges += OUString::valueOf( (sal_Unicode) ';');
1378 aSortedCellRanges += pSortedRanges[i];
1382 // build value for 'SequenceMapping'
1384 uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
1385 sal_Int32 *pSortedMapping = aSortedMapping.getArray();
1386 std::sort( pSortedMapping, pSortedMapping + aSortedMapping.getLength() );
1387 DBG_ASSERT( aSortedMapping.getLength() == nNumDS_LDS, "unexpected size of sequence" );
1388 sal_Bool bNeedSequenceMapping = sal_False;
1389 for (sal_Int32 i = 0; i < nNumDS_LDS; ++i)
1391 sal_Int32 *pIt = std::find( pSortedMapping, pSortedMapping + nNumDS_LDS,
1392 pSequenceMapping[i] );
1393 DBG_ASSERT( pIt, "index not found" );
1394 if (!pIt)
1395 return aResult; // failed -> return empty property sequence
1396 pSequenceMapping[i] = pIt - pSortedMapping;
1398 if (i != pSequenceMapping[i])
1399 bNeedSequenceMapping = sal_True;
1402 // check if 'SequenceMapping' is actually not required...
1403 // (don't write unnecessary properties to the XML file)
1404 if (!bNeedSequenceMapping)
1405 aSequenceMapping.realloc(0);
1408 #ifdef TL_NOT_USED // in the end chart2 did not want to have the sequence minimized
1409 // try to shorten the 'SequenceMapping' as much as possible
1410 sal_Int32 k;
1411 for (k = nNumDS_LDS - 1; k >= 0; --k)
1413 if (pSequenceMapping[k] != k)
1414 break;
1416 aSequenceMapping.realloc( k + 1 );
1417 #endif
1421 // build resulting properties
1423 DBG_ASSERT(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
1424 "unexpected value for 'nLabelSeqLen'" );
1425 sal_Bool bFirstCellIsLabel = sal_False; // default value if 'nLabelSeqLen' could not properly determined
1426 if (nLabelSeqLen > 0) // == 0 means no label sequence in use
1427 bFirstCellIsLabel = sal_True;
1429 DBG_ASSERT( aSortedCellRanges.getLength(), "CellRangeRepresentation missing" );
1430 OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
1432 aResult.realloc(5);
1433 sal_Int32 nProps = 0;
1434 aResult[nProps ].Name = C2U("FirstCellAsLabel");
1435 aResult[nProps++].Value <<= bFirstCellIsLabel;
1436 aResult[nProps ].Name = C2U("CellRangeRepresentation");
1437 aResult[nProps++].Value <<= aSortedCellRanges;
1438 if (0 != aBrokenCellRangeForExport.getLength())
1440 aResult[nProps ].Name = C2U("BrokenCellRangeForExport");
1441 aResult[nProps++].Value <<= aBrokenCellRangeForExport;
1443 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1445 chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
1446 chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
1447 aResult[nProps ].Name = C2U("DataRowSource");
1448 aResult[nProps++].Value <<= eDataRowSource;
1450 if (aSequenceMapping.getLength() != 0)
1452 aResult[nProps ].Name = C2U("SequenceMapping");
1453 aResult[nProps++].Value <<= aSequenceMapping;
1456 aResult.realloc( nProps );
1458 return aResult;
1461 uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
1462 const OUString& rRangeRepresentation, sal_Bool bTestOnly )
1463 throw (lang::IllegalArgumentException, uno::RuntimeException)
1465 if (bDisposed)
1466 throw lang::DisposedException();
1468 SwFrmFmt *pTblFmt = 0; // pointer to table format
1469 SwUnoCrsr *pUnoCrsr = 0; // pointer to new created cursor spanning the cell range
1470 GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation,
1471 &pTblFmt, &pUnoCrsr );
1472 if (!pTblFmt || !pUnoCrsr)
1473 throw lang::IllegalArgumentException();
1475 // check that cursors point and mark are in a single row or column.
1476 String aCellRange( GetCellRangeName( *pTblFmt, *pUnoCrsr ) );
1477 SwRangeDescriptor aDesc;
1478 FillRangeDescriptor( aDesc, aCellRange );
1479 if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1480 throw lang::IllegalArgumentException();
1482 DBG_ASSERT( pTblFmt && pUnoCrsr, "table format or cursor missing" );
1483 uno::Reference< chart2::data::XDataSequence > xDataSeq;
1484 if (!bTestOnly)
1485 xDataSeq = new SwChartDataSequence( *this, *pTblFmt, pUnoCrsr );
1487 return xDataSeq;
1490 sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
1491 const OUString& rRangeRepresentation )
1492 throw (uno::RuntimeException)
1494 vos::OGuard aGuard( Application::GetSolarMutex() );
1496 sal_Bool bPossible = sal_True;
1499 Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, sal_True );
1501 catch (lang::IllegalArgumentException &)
1503 bPossible = sal_False;
1506 return bPossible;
1509 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
1510 const OUString& rRangeRepresentation )
1511 throw (lang::IllegalArgumentException, uno::RuntimeException)
1513 vos::OGuard aGuard( Application::GetSolarMutex() );
1514 return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
1518 uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
1519 throw (uno::RuntimeException)
1521 // note: it is no error to return nothing here
1522 return uno::Reference< sheet::XRangeSelection >();
1526 void SAL_CALL SwChartDataProvider::dispose( )
1527 throw (uno::RuntimeException)
1529 sal_Bool bMustDispose( sal_False );
1531 osl::MutexGuard aGuard( GetChartMutex() );
1532 bMustDispose = !bDisposed;
1533 if (!bDisposed)
1534 bDisposed = sal_True;
1536 if (bMustDispose)
1538 // dispose all data-sequences
1539 Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() );
1540 while (aIt != aDataSequences.end())
1542 DisposeAllDataSequences( (*aIt).first );
1543 ++aIt;
1545 // release all references to data-sequences
1546 aDataSequences.clear();
1548 // require listeners to release references to this object
1549 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
1550 aEvtListeners.disposeAndClear( aEvtObj );
1555 void SAL_CALL SwChartDataProvider::addEventListener(
1556 const uno::Reference< lang::XEventListener >& rxListener )
1557 throw (uno::RuntimeException)
1559 osl::MutexGuard aGuard( GetChartMutex() );
1560 if (!bDisposed && rxListener.is())
1561 aEvtListeners.addInterface( rxListener );
1565 void SAL_CALL SwChartDataProvider::removeEventListener(
1566 const uno::Reference< lang::XEventListener >& rxListener )
1567 throw (uno::RuntimeException)
1569 osl::MutexGuard aGuard( GetChartMutex() );
1570 if (!bDisposed && rxListener.is())
1571 aEvtListeners.removeInterface( rxListener );
1576 OUString SAL_CALL SwChartDataProvider::getImplementationName( )
1577 throw (uno::RuntimeException)
1579 return C2U("SwChartDataProvider");
1583 sal_Bool SAL_CALL SwChartDataProvider::supportsService(
1584 const OUString& rServiceName )
1585 throw (uno::RuntimeException)
1587 vos::OGuard aGuard( Application::GetSolarMutex() );
1588 return rServiceName.equalsAscii( SN_DATA_PROVIDER );
1592 uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( )
1593 throw (uno::RuntimeException)
1595 vos::OGuard aGuard( Application::GetSolarMutex() );
1596 uno::Sequence< OUString > aRes(1);
1597 aRes.getArray()[0] = C2U( SN_DATA_PROVIDER );
1598 return aRes;
1602 void SwChartDataProvider::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew)
1604 // actually this function should be superfluous (need to check later)
1605 ClientModify(this, pOld, pNew );
1609 void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
1611 aDataSequences[ &rTable ].insert( rxDataSequence );
1615 void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
1617 aDataSequences[ &rTable ].erase( rxDataSequence );
1621 void SwChartDataProvider::InvalidateTable( const SwTable *pTable )
1623 DBG_ASSERT( pTable, "table pointer is NULL" );
1624 if (pTable)
1626 if (!bDisposed)
1627 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1629 const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
1630 Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
1631 while (aIt != rSet.end())
1633 // uno::Reference< util::XModifiable > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
1634 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1635 uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
1636 if (xRef.is())
1638 // mark the sequence as 'dirty' and notify listeners
1639 xRef->setModified( sal_True );
1641 ++aIt;
1647 sal_Bool SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
1649 sal_Bool bRes = sal_False;
1650 DBG_ASSERT( pTable, "table pointer is NULL" );
1651 if (pTable)
1653 if (!bDisposed)
1654 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1656 Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
1658 // iterate over all data-sequences for that table...
1659 Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
1660 Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
1661 Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate
1662 while (aIt != aEndIt)
1664 SwChartDataSequence *pDataSeq = 0;
1665 sal_Bool bNowEmpty = sal_False;
1667 // check if weak reference is still valid...
1668 // uno::Reference< chart2::data::XDataSequence > xRef( uno::Reference< chart2::data::XDataSequence>(*aIt), uno::UNO_QUERY );
1669 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1670 uno::Reference< chart2::data::XDataSequence > xRef( xTemp, uno::UNO_QUERY );
1671 if (xRef.is())
1673 // then delete that table box (check if implementation cursor needs to be adjusted)
1674 pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() );
1675 if (pDataSeq)
1677 #if OSL_DEBUG_LEVEL > 1
1678 OUString aRangeStr( pDataSeq->getSourceRangeRepresentation() );
1679 #endif
1680 bNowEmpty = pDataSeq->DeleteBox( rBox );
1681 if (bNowEmpty)
1682 aDelIt = aIt;
1685 ++aIt;
1687 if (bNowEmpty)
1689 rSet.erase( aDelIt );
1690 if (pDataSeq)
1691 pDataSeq->dispose(); // the current way to tell chart that sth. got removed
1695 return bRes;
1699 void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
1701 DBG_ASSERT( pTable, "table pointer is NULL" );
1702 if (pTable)
1704 if (!bDisposed)
1705 pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1707 //! make a copy of the STL container!
1708 //! This is necessary since calling 'dispose' will implicitly remove an element
1709 //! of the original container, and thus any iterator in the original container
1710 //! would become invalid.
1711 const Set_DataSequenceRef_t aSet( aDataSequences[ pTable ] );
1713 Set_DataSequenceRef_t::iterator aIt( aSet.begin() );
1714 Set_DataSequenceRef_t::iterator aEndIt( aSet.end() );
1715 while (aIt != aEndIt)
1717 // uno::Reference< lang::XComponent > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
1718 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1719 uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
1720 if (xRef.is())
1722 xRef->dispose();
1724 ++aIt;
1730 ////////////////////////////////////////
1731 // SwChartDataProvider::AddRowCols tries to notify charts of added columns
1732 // or rows and extends the value sequence respectively (if possible).
1733 // If those can be added to the end of existing value data-sequences those
1734 // sequences get mofdified accordingly and will send a modification
1735 // notification (calling 'setModified').
1737 // Since this function is a work-around for non existent Writer core functionality
1738 // (no arbitrary multi-selection in tables that can be used to define a
1739 // data-sequence) this function will be somewhat unreliable.
1740 // For example we will only try to adapt value sequences. For this we assume
1741 // that a sequence of length 1 is a label sequence and those with length >= 2
1742 // we presume to be value sequences. Also new cells can only be added in the
1743 // direction the value sequence is already pointing (rows / cols) and at the
1744 // start or end of the values data-sequence.
1745 // Nothing needs to be done if the new cells are in between the table cursors
1746 // point and mark since data-sequence are considered to consist of all cells
1747 // between those.
1748 // New rows/cols need to be added already to the table before calling
1749 // this function.
1751 void SwChartDataProvider::AddRowCols(
1752 const SwTable &rTable,
1753 const SwSelBoxes& rBoxes,
1754 USHORT nLines, BOOL bBehind )
1756 if (rTable.IsTblComplex())
1757 return;
1759 const USHORT nBoxes = rBoxes.Count();
1760 if (nBoxes < 1 || nLines < 1)
1761 return;
1763 SwTableBox* pFirstBox = *( rBoxes.GetData() + 0 );
1764 SwTableBox* pLastBox = *( rBoxes.GetData() + nBoxes - 1 );
1766 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1767 if (pFirstBox && pLastBox)
1769 lcl_GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow );
1770 lcl_GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow );
1772 bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/
1773 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1774 bAddCols = true;
1775 if (nFirstCol == nLastCol || nFirstRow == nLastRow)
1777 //get range of indices in col/rows for new cells
1778 sal_Int32 nFirstNewCol = nFirstCol;
1779 sal_Int32 nLastNewCol = nLastCol;
1780 sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines;
1781 sal_Int32 nLastNewRow = nFirstNewRow - 1 + nLines;
1782 if (bAddCols)
1784 DBG_ASSERT( nFirstCol == nLastCol, "column indices seem broken" );
1785 nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines;
1786 nLastNewCol = nFirstNewCol - 1 + nLines;
1787 nFirstNewRow = nFirstRow;
1788 nLastNewRow = nLastRow;
1791 // iterate over all data-sequences for the table
1792 const Set_DataSequenceRef_t &rSet = aDataSequences[ &rTable ];
1793 Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
1794 while (aIt != rSet.end())
1796 // uno::Reference< chart2::data::XTextualDataSequence > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
1797 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt); // temporary needed for g++ 3.3.5
1798 uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
1799 if (xRef.is())
1801 const sal_Int32 nLen = xRef->getTextualData().getLength();
1802 if (nLen > 1) // value data-sequence ?
1804 SwChartDataSequence *pDataSeq = 0;
1805 uno::Reference< lang::XUnoTunnel > xTunnel( xRef, uno::UNO_QUERY );
1806 if(xTunnel.is())
1808 pDataSeq = reinterpret_cast< SwChartDataSequence * >(
1809 sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething( SwChartDataSequence::getUnoTunnelId() )));
1811 if (pDataSeq)
1813 SwRangeDescriptor aDesc;
1814 pDataSeq->FillRangeDesc( aDesc );
1816 chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
1817 if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1818 eDRSource = chart::ChartDataRowSource_ROWS;
1820 if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
1822 // add rows: extend affected columns by newly added row cells
1823 pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
1825 else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
1827 // add cols: extend affected rows by newly added column cells
1828 pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
1834 ++aIt;
1842 // XRangeXMLConversion ---------------------------------------------------
1844 rtl::OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const rtl::OUString& rRangeRepresentation )
1845 throw ( uno::RuntimeException, lang::IllegalArgumentException )
1847 vos::OGuard aGuard( Application::GetSolarMutex() );
1848 if (bDisposed)
1849 throw lang::DisposedException();
1851 String aRes;
1852 String aRangeRepresentation( rRangeRepresentation );
1854 // multiple ranges are delimeted by a ';' like in
1855 // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
1856 xub_StrLen nNumRanges = aRangeRepresentation.GetTokenCount( ';' );
1857 SwTable* pFirstFoundTable = 0; // to check that only one table will be used
1858 for (USHORT i = 0; i < nNumRanges; ++i)
1860 String aRange( aRangeRepresentation.GetToken(i, ';') );
1861 SwFrmFmt *pTblFmt = 0; // pointer to table format
1862 // BM: For what should the check be necessary? for #i79009# it is required that NO check is done
1863 // SwUnoCrsr *pUnoCrsr = 0; // here required to check if the cells in the range do actually exist
1864 // std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr ); // to end lifetime of object pointed to by pUnoCrsr
1865 GetFormatAndCreateCursorFromRangeRep( pDoc, aRange, &pTblFmt, NULL );
1866 if (!pTblFmt)
1867 throw lang::IllegalArgumentException();
1868 // if (!pUnoCrsr)
1869 // throw uno::RuntimeException();
1870 SwTable* pTable = SwTable::FindTable( pTblFmt );
1871 if (pTable->IsTblComplex())
1872 throw uno::RuntimeException();
1874 // check that there is only one table used in all ranges
1875 if (!pFirstFoundTable)
1876 pFirstFoundTable = pTable;
1877 if (pTable != pFirstFoundTable)
1878 throw lang::IllegalArgumentException();
1880 String aTblName;
1881 String aStartCell;
1882 String aEndCell;
1883 if (!GetTableAndCellsFromRangeRep( aRange, aTblName, aStartCell, aEndCell ))
1884 throw lang::IllegalArgumentException();
1886 sal_Int32 nCol, nRow;
1887 lcl_GetCellPosition( aStartCell, nCol, nRow );
1888 if (nCol < 0 || nRow < 0)
1889 throw uno::RuntimeException();
1891 //!! following objects/functions are implemented in XMLRangeHelper.?xx
1892 //!! which is a copy of the respective file from chart2 !!
1893 XMLRangeHelper::CellRange aCellRange;
1894 aCellRange.aTableName = aTblName;
1895 aCellRange.aUpperLeft.nColumn = nCol;
1896 aCellRange.aUpperLeft.nRow = nRow;
1897 aCellRange.aUpperLeft.bIsEmpty = false;
1898 if (aStartCell != aEndCell && aEndCell.Len() != 0)
1900 lcl_GetCellPosition( aEndCell, nCol, nRow );
1901 if (nCol < 0 || nRow < 0)
1902 throw uno::RuntimeException();
1904 aCellRange.aLowerRight.nColumn = nCol;
1905 aCellRange.aLowerRight.nRow = nRow;
1906 aCellRange.aLowerRight.bIsEmpty = false;
1908 String aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
1909 if (aRes.Len()) // in case of multiple ranges add delimeter
1910 aRes.AppendAscii( " " );
1911 aRes += aTmp;
1914 return aRes;
1917 rtl::OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const rtl::OUString& rXMLRange )
1918 throw ( uno::RuntimeException, lang::IllegalArgumentException )
1920 vos::OGuard aGuard( Application::GetSolarMutex() );
1921 if (bDisposed)
1922 throw lang::DisposedException();
1924 String aRes;
1925 String aXMLRange( rXMLRange );
1927 // multiple ranges are delimeted by a ' ' like in
1928 // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
1929 xub_StrLen nNumRanges = aXMLRange.GetTokenCount( ' ' );
1930 rtl::OUString aFirstFoundTable; // to check that only one table will be used
1931 for (USHORT i = 0; i < nNumRanges; ++i)
1933 String aRange( aXMLRange.GetToken(i, ' ') );
1935 //!! following objects and function are implemented in XMLRangeHelper.?xx
1936 //!! which is a copy of the respective file from chart2 !!
1937 XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
1939 // check that there is only one table used in all ranges
1940 if (aFirstFoundTable.getLength() == 0)
1941 aFirstFoundTable = aCellRange.aTableName;
1942 if (aCellRange.aTableName != aFirstFoundTable)
1943 throw lang::IllegalArgumentException();
1945 OUString aTmp( aCellRange.aTableName );
1946 aTmp += OUString::valueOf((sal_Unicode) '.');
1947 aTmp += lcl_GetCellName( aCellRange.aUpperLeft.nColumn,
1948 aCellRange.aUpperLeft.nRow );
1949 // does cell range consist of more than a single cell?
1950 if (!aCellRange.aLowerRight.bIsEmpty)
1952 aTmp += OUString::valueOf((sal_Unicode) ':');
1953 aTmp += lcl_GetCellName( aCellRange.aLowerRight.nColumn,
1954 aCellRange.aLowerRight.nRow );
1957 if (aRes.Len()) // in case of multiple ranges add delimeter
1958 aRes.AppendAscii( ";" );
1959 aRes += String(aTmp);
1962 return aRes;
1966 //////////////////////////////////////////////////////////////////////
1968 SwChartDataSource::SwChartDataSource(
1969 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
1970 aLDS( rLDS )
1975 SwChartDataSource::~SwChartDataSource()
1977 // delete pTblCrsr;
1981 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( )
1982 throw (uno::RuntimeException)
1984 vos::OGuard aGuard( Application::GetSolarMutex() );
1985 return aLDS;
1989 OUString SAL_CALL SwChartDataSource::getImplementationName( )
1990 throw (uno::RuntimeException)
1992 vos::OGuard aGuard( Application::GetSolarMutex() );
1993 return C2U("SwChartDataSource");
1997 sal_Bool SAL_CALL SwChartDataSource::supportsService(
1998 const OUString& rServiceName )
1999 throw (uno::RuntimeException)
2001 vos::OGuard aGuard( Application::GetSolarMutex() );
2002 return rServiceName.equalsAscii( SN_DATA_SOURCE );
2006 uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( )
2007 throw (uno::RuntimeException)
2009 vos::OGuard aGuard( Application::GetSolarMutex() );
2010 uno::Sequence< OUString > aRes(1);
2011 aRes.getArray()[0] = C2U( SN_DATA_SOURCE );
2012 return aRes;
2015 //////////////////////////////////////////////////////////////////////
2017 SwChartDataSequence::SwChartDataSequence(
2018 SwChartDataProvider &rProvider,
2019 SwFrmFmt &rTblFmt,
2020 SwUnoCrsr *pTableCursor ) :
2021 SwClient( &rTblFmt ),
2022 aEvtListeners( GetChartMutex() ),
2023 aModifyListeners( GetChartMutex() ),
2024 aRowLabelText( SW_RES( STR_CHART2_ROW_LABEL_TEXT ) ),
2025 aColLabelText( SW_RES( STR_CHART2_COL_LABEL_TEXT ) ),
2026 xDataProvider( &rProvider ),
2027 pDataProvider( &rProvider ),
2028 pTblCrsr( pTableCursor ),
2029 aCursorDepend( this, pTableCursor ),
2030 _pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
2032 bDisposed = sal_False;
2034 acquire();
2037 const SwTable* pTable = SwTable::FindTable( &rTblFmt );
2038 if (pTable)
2040 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2041 pDataProvider->AddDataSequence( *pTable, xRef );
2042 pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
2044 else {
2045 DBG_ERROR( "table missing" );
2048 catch (uno::RuntimeException &)
2050 throw;
2052 catch (uno::Exception &)
2055 release();
2057 #if OSL_DEBUG_LEVEL > 1
2058 OUString aRangeStr( getSourceRangeRepresentation() );
2060 // check if it can properly convert into a SwUnoTableCrsr
2061 // which is required for some functions
2062 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2063 DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
2064 #endif
2068 SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
2069 SwChartDataSequenceBaseClass(),
2070 SwClient( rObj.GetFrmFmt() ),
2071 aEvtListeners( GetChartMutex() ),
2072 aModifyListeners( GetChartMutex() ),
2073 aRole( rObj.aRole ),
2074 aRowLabelText( SW_RES(STR_CHART2_ROW_LABEL_TEXT) ),
2075 aColLabelText( SW_RES(STR_CHART2_COL_LABEL_TEXT) ),
2076 xDataProvider( rObj.pDataProvider ),
2077 pDataProvider( rObj.pDataProvider ),
2078 pTblCrsr( rObj.pTblCrsr->Clone() ),
2079 aCursorDepend( this, pTblCrsr ),
2080 _pPropSet( rObj._pPropSet )
2082 bDisposed = sal_False;
2084 acquire();
2087 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2088 if (pTable)
2090 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2091 pDataProvider->AddDataSequence( *pTable, xRef );
2092 pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
2094 else {
2095 DBG_ERROR( "table missing" );
2098 catch (uno::RuntimeException &)
2100 throw;
2102 catch (uno::Exception &)
2105 release();
2107 #if OSL_DEBUG_LEVEL > 1
2108 OUString aRangeStr( getSourceRangeRepresentation() );
2110 // check if it can properly convert into a SwUnoTableCrsr
2111 // which is required for some functions
2112 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2113 DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
2114 #endif
2118 SwChartDataSequence::~SwChartDataSequence()
2120 // since the data-provider holds only weak references to the data-sequence
2121 // there should be no need here to release them explicitly...
2123 delete pTblCrsr;
2127 const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
2129 static uno::Sequence< sal_Int8 > aSeq = ::CreateUnoTunnelId();
2130 return aSeq;
2134 sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
2135 throw(uno::RuntimeException)
2137 if( rId.getLength() == 16
2138 && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(),
2139 rId.getConstArray(), 16 ) )
2141 return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
2143 return 0;
2147 uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData( )
2148 throw (uno::RuntimeException)
2150 vos::OGuard aGuard( Application::GetSolarMutex() );
2151 if (bDisposed)
2152 throw lang::DisposedException();
2154 uno::Sequence< uno::Any > aRes;
2155 SwFrmFmt* pTblFmt = GetFrmFmt();
2156 if(pTblFmt)
2158 SwTable* pTable = SwTable::FindTable( pTblFmt );
2159 if(!pTable->IsTblComplex())
2161 SwRangeDescriptor aDesc;
2162 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2164 //!! make copy of pTblCrsr (SwUnoCrsr )
2165 // keep original cursor and make copy of it that gets handed
2166 // over to the SwXCellRange object which takes ownership and
2167 // thus will destroy the copy later.
2168 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2169 aRange.GetDataSequence( &aRes, 0, 0 );
2173 return aRes;
2177 OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( )
2178 throw (uno::RuntimeException)
2180 vos::OGuard aGuard( Application::GetSolarMutex() );
2181 if (bDisposed)
2182 throw lang::DisposedException();
2184 String aRes;
2185 SwFrmFmt* pTblFmt = GetFrmFmt();
2186 if (pTblFmt)
2188 aRes = pTblFmt->GetName();
2189 String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2190 DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" );
2191 aRes += (sal_Unicode) '.';
2192 aRes += aCellRange;
2194 return aRes;
2197 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
2198 chart2::data::LabelOrigin eLabelOrigin )
2199 throw (uno::RuntimeException)
2201 vos::OGuard aGuard( Application::GetSolarMutex() );
2202 if (bDisposed)
2203 throw lang::DisposedException();
2205 uno::Sequence< OUString > aLabels;
2208 SwRangeDescriptor aDesc;
2209 sal_Bool bOk sal_False;
2210 SwFrmFmt* pTblFmt = GetFrmFmt();
2211 SwTable* pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
2212 if (!pTblFmt || !pTable || pTable->IsTblComplex())
2213 throw uno::RuntimeException();
2214 else
2216 String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2217 DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" );
2218 bOk = FillRangeDescriptor( aDesc, aCellRange );
2219 DBG_ASSERT( bOk, "falied to get SwRangeDescriptor" );
2221 if (bOk)
2223 aDesc.Normalize();
2224 sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
2225 sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
2226 DBG_ASSERT( nColSpan == 1 || nRowSpan == 1,
2227 "unexpected range of selected cells" );
2229 String aTxt; // label text to be returned
2230 sal_Bool bReturnEmptyTxt = sal_False;
2231 sal_Bool bUseCol = sal_True;
2232 if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
2233 bUseCol = sal_True;
2234 else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
2235 bUseCol = sal_False;
2236 else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
2238 bUseCol = nColSpan < nRowSpan;
2239 bReturnEmptyTxt = nColSpan == nRowSpan;
2241 else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
2243 bUseCol = nColSpan > nRowSpan;
2244 bReturnEmptyTxt = nColSpan == nRowSpan;
2246 else {
2247 DBG_ERROR( "unexpected case" );
2250 // build label sequence
2252 sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
2253 aLabels.realloc( nSeqLen );
2254 OUString *pLabels = aLabels.getArray();
2255 for (sal_Int32 i = 0; i < nSeqLen; ++i)
2257 if (!bReturnEmptyTxt)
2259 aTxt = bUseCol ? aColLabelText : aRowLabelText;
2260 sal_Int32 nCol = aDesc.nLeft;
2261 sal_Int32 nRow = aDesc.nTop;
2262 if (bUseCol)
2263 nCol = nCol + i;
2264 else
2265 nRow = nRow + i;
2266 String aCellName( lcl_GetCellName( nCol, nRow ) );
2268 xub_StrLen nLen = aCellName.Len();
2269 if (nLen)
2271 const sal_Unicode *pBuf = aCellName.GetBuffer();
2272 const sal_Unicode *pEnd = pBuf + nLen;
2273 while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9'))
2274 ++pBuf;
2275 // start of number found?
2276 if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
2278 String aRplc;
2279 String aNew;
2280 if (bUseCol)
2282 aRplc = String::CreateFromAscii( "%COLUMNLETTER" );
2283 aNew = String( aCellName.GetBuffer(), static_cast<xub_StrLen>(pBuf - aCellName.GetBuffer()) );
2285 else
2287 aRplc = String::CreateFromAscii( "%ROWNUMBER" );
2288 aNew = String( pBuf, static_cast<xub_StrLen>((aCellName.GetBuffer() + nLen) - pBuf) );
2290 xub_StrLen nPos = aTxt.Search( aRplc );
2291 if (nPos != STRING_NOTFOUND)
2292 aTxt = aTxt.Replace( nPos, aRplc.Len(), aNew );
2296 pLabels[i] = aTxt;
2301 return aLabels;
2304 ::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
2305 ::sal_Int32 /*nIndex*/ )
2306 throw (lang::IndexOutOfBoundsException,
2307 uno::RuntimeException)
2309 return 0;
2314 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData( )
2315 throw (uno::RuntimeException)
2317 vos::OGuard aGuard( Application::GetSolarMutex() );
2318 if (bDisposed)
2319 throw lang::DisposedException();
2321 uno::Sequence< OUString > aRes;
2322 SwFrmFmt* pTblFmt = GetFrmFmt();
2323 if(pTblFmt)
2325 SwTable* pTable = SwTable::FindTable( pTblFmt );
2326 if(!pTable->IsTblComplex())
2328 SwRangeDescriptor aDesc;
2329 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2331 //!! make copy of pTblCrsr (SwUnoCrsr )
2332 // keep original cursor and make copy of it that gets handed
2333 // over to the SwXCellRange object which takes ownership and
2334 // thus will destroy the copy later.
2335 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2336 aRange.GetDataSequence( 0, &aRes, 0 );
2340 return aRes;
2344 uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData( )
2345 throw (uno::RuntimeException)
2347 vos::OGuard aGuard( Application::GetSolarMutex() );
2348 if (bDisposed)
2349 throw lang::DisposedException();
2351 uno::Sequence< double > aRes;
2352 SwFrmFmt* pTblFmt = GetFrmFmt();
2353 if(pTblFmt)
2355 SwTable* pTable = SwTable::FindTable( pTblFmt );
2356 if(!pTable->IsTblComplex())
2358 SwRangeDescriptor aDesc;
2359 if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2361 //!! make copy of pTblCrsr (SwUnoCrsr )
2362 // keep original cursor and make copy of it that gets handed
2363 // over to the SwXCellRange object which takes ownership and
2364 // thus will destroy the copy later.
2365 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2367 // get numerical values and make an effort to return the
2368 // numerical value for text formatted cells
2369 aRange.GetDataSequence( 0, 0, &aRes, sal_True );
2373 return aRes;
2377 uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( )
2378 throw (uno::RuntimeException)
2380 vos::OGuard aGuard( Application::GetSolarMutex() );
2381 if (bDisposed)
2382 throw lang::DisposedException();
2383 return new SwChartDataSequence( *this );
2387 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( )
2388 throw (uno::RuntimeException)
2390 vos::OGuard aGuard( Application::GetSolarMutex() );
2391 if (bDisposed)
2392 throw lang::DisposedException();
2394 static uno::Reference< beans::XPropertySetInfo > xRes = _pPropSet->getPropertySetInfo();
2395 return xRes;
2399 void SAL_CALL SwChartDataSequence::setPropertyValue(
2400 const OUString& rPropertyName,
2401 const uno::Any& rValue )
2402 throw (beans::UnknownPropertyException, beans::PropertyVetoException, lang::IllegalArgumentException, lang::WrappedTargetException, uno::RuntimeException)
2404 vos::OGuard aGuard( Application::GetSolarMutex() );
2405 if (bDisposed)
2406 throw lang::DisposedException();
2408 if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
2410 if ( !(rValue >>= aRole) )
2411 throw lang::IllegalArgumentException();
2413 else
2414 throw beans::UnknownPropertyException();
2418 uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
2419 const OUString& rPropertyName )
2420 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2422 vos::OGuard aGuard( Application::GetSolarMutex() );
2423 if (bDisposed)
2424 throw lang::DisposedException();
2426 uno::Any aRes;
2427 if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
2428 aRes <<= aRole;
2429 else
2430 throw beans::UnknownPropertyException();
2432 return aRes;
2436 void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
2437 const OUString& /*rPropertyName*/,
2438 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2439 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2441 //vos::OGuard aGuard( Application::GetSolarMutex() );
2442 DBG_ERROR( "not implemented" );
2446 void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
2447 const OUString& /*rPropertyName*/,
2448 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2449 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2451 //vos::OGuard aGuard( Application::GetSolarMutex() );
2452 DBG_ERROR( "not implemented" );
2456 void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
2457 const OUString& /*rPropertyName*/,
2458 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2459 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2461 //vos::OGuard aGuard( Application::GetSolarMutex() );
2462 DBG_ERROR( "not implemented" );
2466 void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
2467 const OUString& /*rPropertyName*/,
2468 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2469 throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2471 //vos::OGuard aGuard( Application::GetSolarMutex() );
2472 DBG_ERROR( "not implemented" );
2476 OUString SAL_CALL SwChartDataSequence::getImplementationName( )
2477 throw (uno::RuntimeException)
2479 return C2U("SwChartDataSequence");
2483 sal_Bool SAL_CALL SwChartDataSequence::supportsService(
2484 const OUString& rServiceName )
2485 throw (uno::RuntimeException)
2487 return rServiceName.equalsAscii( SN_DATA_SEQUENCE );
2491 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( )
2492 throw (uno::RuntimeException)
2494 vos::OGuard aGuard( Application::GetSolarMutex() );
2495 uno::Sequence< OUString > aRes(1);
2496 aRes.getArray()[0] = C2U( SN_DATA_SEQUENCE );
2497 return aRes;
2501 void SwChartDataSequence::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew)
2503 ClientModify(this, pOld, pNew );
2505 // table was deleted or cursor was deleted
2506 if(!GetRegisteredIn() || !aCursorDepend.GetRegisteredIn())
2508 pTblCrsr = 0;
2509 dispose();
2511 else
2513 setModified( sal_True );
2518 sal_Bool SAL_CALL SwChartDataSequence::isModified( )
2519 throw (uno::RuntimeException)
2521 vos::OGuard aGuard( Application::GetSolarMutex() );
2522 if (bDisposed)
2523 throw lang::DisposedException();
2525 return sal_True;
2529 void SAL_CALL SwChartDataSequence::setModified(
2530 ::sal_Bool bModified )
2531 throw (beans::PropertyVetoException, uno::RuntimeException)
2533 vos::OGuard aGuard( Application::GetSolarMutex() );
2534 if (bDisposed)
2535 throw lang::DisposedException();
2537 if (bModified)
2538 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2542 void SAL_CALL SwChartDataSequence::addModifyListener(
2543 const uno::Reference< util::XModifyListener >& rxListener )
2544 throw (uno::RuntimeException)
2546 osl::MutexGuard aGuard( GetChartMutex() );
2547 if (!bDisposed && rxListener.is())
2548 aModifyListeners.addInterface( rxListener );
2552 void SAL_CALL SwChartDataSequence::removeModifyListener(
2553 const uno::Reference< util::XModifyListener >& rxListener )
2554 throw (uno::RuntimeException)
2556 osl::MutexGuard aGuard( GetChartMutex() );
2557 if (!bDisposed && rxListener.is())
2558 aModifyListeners.removeInterface( rxListener );
2562 void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
2563 throw (uno::RuntimeException)
2565 if (bDisposed)
2566 throw lang::DisposedException();
2567 if (rSource.Source == xDataProvider)
2569 pDataProvider = 0;
2570 xDataProvider.clear();
2575 void SAL_CALL SwChartDataSequence::dispose( )
2576 throw (uno::RuntimeException)
2578 sal_Bool bMustDispose( sal_False );
2580 osl::MutexGuard aGuard( GetChartMutex() );
2581 bMustDispose = !bDisposed;
2582 if (!bDisposed)
2583 bDisposed = sal_True;
2585 if (bMustDispose)
2587 bDisposed = sal_True;
2588 if (pDataProvider)
2590 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2591 if (pTable)
2593 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2594 pDataProvider->RemoveDataSequence( *pTable, xRef );
2596 else {
2597 DBG_ERROR( "table missing" );
2601 // require listeners to release references to this object
2602 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
2603 aModifyListeners.disposeAndClear( aEvtObj );
2604 aEvtListeners.disposeAndClear( aEvtObj );
2609 void SAL_CALL SwChartDataSequence::addEventListener(
2610 const uno::Reference< lang::XEventListener >& rxListener )
2611 throw (uno::RuntimeException)
2613 osl::MutexGuard aGuard( GetChartMutex() );
2614 if (!bDisposed && rxListener.is())
2615 aEvtListeners.addInterface( rxListener );
2619 void SAL_CALL SwChartDataSequence::removeEventListener(
2620 const uno::Reference< lang::XEventListener >& rxListener )
2621 throw (uno::RuntimeException)
2623 osl::MutexGuard aGuard( GetChartMutex() );
2624 if (!bDisposed && rxListener.is())
2625 aEvtListeners.removeInterface( rxListener );
2629 sal_Bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
2631 #if OSL_DEBUG_LEVEL > 1
2632 String aBoxName( rBox.GetName() );
2633 #endif
2635 // to be set if the last box of the data-sequence was removed here
2636 sal_Bool bNowEmpty = sal_False;
2638 // if the implementation cursor gets affected (i.e. thew box where it is located
2639 // in gets removed) we need to move it before that... (otherwise it does not need to change)
2641 const SwStartNode* pPointStartNode = pTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2642 const SwStartNode* pMarkStartNode = pTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2644 if (!pTblCrsr->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd()))
2646 bNowEmpty = sal_True;
2648 else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd())
2650 sal_Int32 nPointRow = -1, nPointCol = -1;
2651 sal_Int32 nMarkRow = -1, nMarkCol = -1;
2652 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2653 String aPointCellName( pTable->GetTblBox( pPointStartNode->GetIndex() )->GetName() );
2654 String aMarkCellName( pTable->GetTblBox( pMarkStartNode->GetIndex() )->GetName() );
2656 lcl_GetCellPosition( aPointCellName, nPointCol, nPointRow );
2657 lcl_GetCellPosition( aMarkCellName, nMarkCol, nMarkRow );
2658 DBG_ASSERT( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
2659 DBG_ASSERT( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
2661 // move vertical or horizontal?
2662 DBG_ASSERT( nPointRow == nMarkRow || nPointCol == nMarkCol,
2663 "row/col indices not matching" );
2664 DBG_ASSERT( nPointRow != nMarkRow || nPointCol != nMarkCol,
2665 "point and mark are identical" );
2666 sal_Bool bMoveVertical = (nPointCol == nMarkCol);
2667 sal_Bool bMoveHorizontal = (nPointRow == nMarkRow);
2669 // get movement direction
2670 sal_Bool bMoveLeft = sal_False; // move left or right?
2671 sal_Bool bMoveUp = sal_False; // move up or down?
2672 if (bMoveVertical)
2674 if (pPointStartNode == rBox.GetSttNd()) // move point?
2675 bMoveUp = nPointRow > nMarkRow;
2676 else // move mark
2677 bMoveUp = nMarkRow > nPointRow;
2679 else if (bMoveHorizontal)
2681 if (pPointStartNode == rBox.GetSttNd()) // move point?
2682 bMoveLeft = nPointCol > nMarkCol;
2683 else // move mark
2684 bMoveLeft = nMarkCol > nPointCol;
2686 else {
2687 DBG_ERROR( "neither vertical nor horizontal movement" );
2690 // get new box (position) to use...
2691 sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
2692 sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
2693 if (bMoveVertical)
2694 nRow += bMoveUp ? -1 : +1;
2695 if (bMoveHorizontal)
2696 nCol += bMoveLeft ? -1 : +1;
2697 String aNewCellName = lcl_GetCellName( nCol, nRow );
2698 SwTableBox* pNewBox = (SwTableBox*) pTable->GetTblBox( aNewCellName );
2700 if (pNewBox) // set new position (cell range) to use
2702 // So erhält man den ersten Inhaltsnode in einer gegebenen Zelle:
2703 // Zunächst einen SwNodeIndex auf den Node hinter dem SwStartNode der Box...
2704 SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
2705 // Dies kann ein SwCntntNode sein, kann aber auch ein Tabellen oder Sectionnode sein,
2706 // deshalb das GoNext;
2707 SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode();
2708 if (!pCNd)
2709 pCNd = GetFrmFmt()->GetDoc()->GetNodes().GoNext( &aIdx );
2710 //und damit kann man z.B. eine SwPosition erzeugen:
2711 SwPosition aNewPos( *pCNd ); // new position to beused with cursor
2713 // if the mark is to be changed make sure there is one...
2714 if (pMarkStartNode == rBox.GetSttNd() && !pTblCrsr->HasMark())
2715 pTblCrsr->SetMark();
2717 // set cursor to new position...
2718 SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
2719 pTblCrsr->GetPoint() : pTblCrsr->GetMark();
2720 if (pPos)
2722 pPos->nNode = aNewPos.nNode;
2723 pPos->nContent = aNewPos.nContent;
2725 else {
2726 DBG_ERROR( "neither point nor mark available for change" );
2729 else {
2730 DBG_ERROR( "failed to get position" );
2734 return bNowEmpty;
2738 void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
2740 SwFrmFmt* pTblFmt = GetFrmFmt();
2741 if(pTblFmt)
2743 SwTable* pTable = SwTable::FindTable( pTblFmt );
2744 if(!pTable->IsTblComplex())
2746 FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2752 SwChartDataSequence::ExtendTo
2754 extends the data-sequence by new cells added at the end of the direction
2755 the data-sequence points to.
2756 If the cells are already within the range of the sequence nothing needs
2757 to be done.
2758 If the cells are beyond the end of the sequence (are not adjacent to the
2759 current last cell) nothing can be done. Only if the cells are adjacent to
2760 the last cell they can be added.
2762 @returns true if the data-sequence was changed.
2763 @param bExtendCols
2764 specifies if columns or rows are to be extended
2765 @param nFirstNew
2766 index of first new row/col to be included in data-sequence
2767 @param nLastNew
2768 index of last new row/col to be included in data-sequence
2770 bool SwChartDataSequence::ExtendTo( bool bExtendCol,
2771 sal_Int32 nFirstNew, sal_Int32 nCount )
2773 bool bChanged = false;
2775 SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2776 //pUnoTblCrsr->MakeBoxSels();
2778 const SwStartNode *pStartNd = 0;
2779 const SwTableBox *pStartBox = 0;
2780 const SwTableBox *pEndBox = 0;
2782 const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2783 DBG_ASSERT( !pTable->IsTblComplex(), "table too complex" );
2784 if (nCount < 1 || nFirstNew < 0 || pTable->IsTblComplex())
2785 return false;
2788 // get range descriptor (cell range) for current data-sequence
2790 pStartNd = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2791 pEndBox = pTable->GetTblBox( pStartNd->GetIndex() );
2792 const String aEndBox( pEndBox->GetName() );
2794 pStartNd = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2795 pStartBox = pTable->GetTblBox( pStartNd->GetIndex() );
2796 const String aStartBox( pStartBox->GetName() );
2798 String aCellRange( aStartBox ); // note that cell range here takes the newly added rows/cols already into account
2799 aCellRange.AppendAscii( ":" );
2800 aCellRange += aEndBox;
2801 SwRangeDescriptor aDesc;
2802 FillRangeDescriptor( aDesc, aCellRange );
2804 String aNewStartCell;
2805 String aNewEndCell;
2806 if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
2808 // new column cells adjacent to the bottom of the
2809 // current data-sequence to be added...
2810 DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2811 aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop);
2812 aNewEndCell = lcl_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
2813 bChanged = true;
2815 else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
2817 // new column cells adjacent to the top of the
2818 // current data-sequence to be added...
2819 DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2820 aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop - nCount);
2821 aNewEndCell = lcl_GetCellName(aDesc.nRight, aDesc.nBottom);
2822 bChanged = true;
2824 else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
2826 // new row cells adjacent to the right of the
2827 // current data-sequence to be added...
2828 DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2829 aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop);
2830 aNewEndCell = lcl_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
2831 bChanged = true;
2833 else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
2835 // new row cells adjacent to the left of the
2836 // current data-sequence to be added...
2837 DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2838 aNewStartCell = lcl_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
2839 aNewEndCell = lcl_GetCellName(aDesc.nRight, aDesc.nBottom);
2840 bChanged = true;
2843 if (bChanged)
2845 // move table cursor to new start and end of data-sequence
2846 const SwTableBox *pNewStartBox = pTable->GetTblBox( aNewStartCell );
2847 const SwTableBox *pNewEndBox = pTable->GetTblBox( aNewEndCell );
2848 pUnoTblCrsr->SetMark();
2849 pUnoTblCrsr->GetPoint()->nNode = *pNewEndBox->GetSttNd();
2850 pUnoTblCrsr->GetMark()->nNode = *pNewStartBox->GetSttNd();
2851 pUnoTblCrsr->Move( fnMoveForward, fnGoNode );
2852 pUnoTblCrsr->MakeBoxSels();
2855 return bChanged;
2858 //////////////////////////////////////////////////////////////////////
2860 SwChartLabeledDataSequence::SwChartLabeledDataSequence() :
2861 aEvtListeners( GetChartMutex() ),
2862 aModifyListeners( GetChartMutex() )
2864 bDisposed = sal_False;
2868 SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
2873 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( )
2874 throw (uno::RuntimeException)
2876 vos::OGuard aGuard( Application::GetSolarMutex() );
2877 if (bDisposed)
2878 throw lang::DisposedException();
2879 return xData;
2883 void SwChartLabeledDataSequence::SetDataSequence(
2884 uno::Reference< chart2::data::XDataSequence >& rxDest,
2885 const uno::Reference< chart2::data::XDataSequence >& rxSource)
2887 uno::Reference< util::XModifyListener > xML( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY );
2888 uno::Reference< lang::XEventListener > xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY );
2890 // stop listening to old data-sequence
2891 uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
2892 if (xMB.is())
2893 xMB->removeModifyListener( xML );
2894 uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
2895 if (xC.is())
2896 xC->removeEventListener( xEL );
2898 rxDest = rxSource;
2900 // start listening to new data-sequence
2901 xC = uno::Reference< lang::XComponent >( rxDest, uno::UNO_QUERY );
2902 if (xC.is())
2903 xC->addEventListener( xEL );
2904 xMB = uno::Reference< util::XModifyBroadcaster >( rxDest, uno::UNO_QUERY );
2905 if (xMB.is())
2906 xMB->addModifyListener( xML );
2910 void SAL_CALL SwChartLabeledDataSequence::setValues(
2911 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2912 throw (uno::RuntimeException)
2914 vos::OGuard aGuard( Application::GetSolarMutex() );
2915 if (bDisposed)
2916 throw lang::DisposedException();
2918 if (xData != rxSequence)
2920 SetDataSequence( xData, rxSequence );
2921 // inform listeners of changes
2922 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2927 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( )
2928 throw (uno::RuntimeException)
2930 vos::OGuard aGuard( Application::GetSolarMutex() );
2931 if (bDisposed)
2932 throw lang::DisposedException();
2933 return xLabels;
2937 void SAL_CALL SwChartLabeledDataSequence::setLabel(
2938 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2939 throw (uno::RuntimeException)
2941 vos::OGuard aGuard( Application::GetSolarMutex() );
2942 if (bDisposed)
2943 throw lang::DisposedException();
2945 if (xLabels != rxSequence)
2947 SetDataSequence( xLabels, rxSequence );
2948 // inform listeners of changes
2949 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2954 uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( )
2955 throw (uno::RuntimeException)
2957 vos::OGuard aGuard( Application::GetSolarMutex() );
2958 if (bDisposed)
2959 throw lang::DisposedException();
2961 uno::Reference< util::XCloneable > xRes;
2963 uno::Reference< util::XCloneable > xDataCloneable( xData, uno::UNO_QUERY );
2964 uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, uno::UNO_QUERY );
2965 SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence();
2966 if (xDataCloneable.is())
2968 uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
2969 pRes->setValues( xDataClone );
2972 if (xLabelsCloneable.is())
2974 uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
2975 pRes->setLabel( xLabelsClone );
2977 xRes = pRes;
2978 return xRes;
2982 OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( )
2983 throw (uno::RuntimeException)
2985 return C2U("SwChartLabeledDataSequence");
2989 sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
2990 const OUString& rServiceName )
2991 throw (uno::RuntimeException)
2993 return rServiceName.equalsAscii( SN_LABELED_DATA_SEQUENCE );
2997 uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( )
2998 throw (uno::RuntimeException)
3000 vos::OGuard aGuard( Application::GetSolarMutex() );
3001 uno::Sequence< OUString > aRes(1);
3002 aRes.getArray()[0] = C2U( SN_LABELED_DATA_SEQUENCE );
3003 return aRes;
3007 void SAL_CALL SwChartLabeledDataSequence::disposing(
3008 const lang::EventObject& rSource )
3009 throw (uno::RuntimeException)
3011 osl::MutexGuard aGuard( GetChartMutex() );
3012 uno::Reference< uno::XInterface > xRef( rSource.Source );
3013 if (xRef == xData)
3014 xData.clear();
3015 if (xRef == xLabels)
3016 xLabels.clear();
3017 if (!xData.is() && !xLabels.is())
3018 dispose();
3022 void SAL_CALL SwChartLabeledDataSequence::modified(
3023 const lang::EventObject& rEvent )
3024 throw (uno::RuntimeException)
3026 if (rEvent.Source == xData || rEvent.Source == xLabels)
3028 LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
3033 void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
3034 const uno::Reference< util::XModifyListener >& rxListener )
3035 throw (uno::RuntimeException)
3037 osl::MutexGuard aGuard( GetChartMutex() );
3038 if (!bDisposed && rxListener.is())
3039 aModifyListeners.addInterface( rxListener );
3043 void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
3044 const uno::Reference< util::XModifyListener >& rxListener )
3045 throw (uno::RuntimeException)
3047 osl::MutexGuard aGuard( GetChartMutex() );
3048 if (!bDisposed && rxListener.is())
3049 aModifyListeners.removeInterface( rxListener );
3053 void SAL_CALL SwChartLabeledDataSequence::dispose( )
3054 throw (uno::RuntimeException)
3056 sal_Bool bMustDispose( sal_False );
3058 osl::MutexGuard aGuard( GetChartMutex() );
3059 bMustDispose = !bDisposed;
3060 if (!bDisposed)
3061 bDisposed = sal_True;
3063 if (bMustDispose)
3065 bDisposed = sal_True;
3067 // require listeners to release references to this object
3068 lang::EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) );
3069 aModifyListeners.disposeAndClear( aEvtObj );
3070 aEvtListeners.disposeAndClear( aEvtObj );
3075 void SAL_CALL SwChartLabeledDataSequence::addEventListener(
3076 const uno::Reference< lang::XEventListener >& rxListener )
3077 throw (uno::RuntimeException)
3079 osl::MutexGuard aGuard( GetChartMutex() );
3080 if (!bDisposed && rxListener.is())
3081 aEvtListeners.addInterface( rxListener );
3085 void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
3086 const uno::Reference< lang::XEventListener >& rxListener )
3087 throw (uno::RuntimeException)
3089 osl::MutexGuard aGuard( GetChartMutex() );
3090 if (!bDisposed && rxListener.is())
3091 aEvtListeners.removeInterface( rxListener );
3094 //////////////////////////////////////////////////////////////////////