vcl: flatten control/calendar.cxx functions
[LibreOffice.git] / sw / source / core / unocore / unochart.cxx
blobdbfaa99c9671cafd107c51ce27ab757c0d16c15f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
21 #include <string_view>
23 #include <com/sun/star/chart/ChartDataRowSource.hpp>
24 #include <com/sun/star/chart2/data/LabelOrigin.hpp>
25 #include <com/sun/star/embed/XEmbeddedObject.hpp>
26 #include <com/sun/star/frame/XModel.hpp>
27 #include <comphelper/diagnose_ex.hxx>
28 #include <cppuhelper/supportsservice.hxx>
29 #include <o3tl/deleter.hxx>
30 #include <o3tl/string_view.hxx>
31 #include <mutex>
32 #include <vcl/svapp.hxx>
34 #include "XMLRangeHelper.hxx"
35 #include <unochart.hxx>
36 #include <swtable.hxx>
37 #include <unoprnms.hxx>
38 #include <unomap.hxx>
39 #include <unocrsr.hxx>
40 #include <unotbl.hxx>
41 #include <doc.hxx>
42 #include <IDocumentChartDataProviderAccess.hxx>
43 #include <frmfmt.hxx>
44 #include <ndole.hxx>
45 #include <swtypes.hxx>
46 #include <strings.hrc>
47 #include <comphelper/servicehelper.hxx>
48 #include <comphelper/string.hxx>
49 #include <svl/itemprop.hxx>
51 using namespace ::com::sun::star;
53 void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
55 if (!pDoc)
56 return;
58 SwOLENode *pONd;
59 SwStartNode *pStNd;
60 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
61 while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
63 ++aIdx;
64 if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
65 pONd->GetOLEObj().GetObject().IsChart() )
67 // Load the object and set modified
69 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
70 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
72 try
74 uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY );
75 if (xModif)
76 xModif->setModified( true );
78 catch ( uno::Exception& )
84 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
88 SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
89 m_pDoc( pDocument )
90 , m_aUnlockTimer( "sw::SwChartLockController_Helper aUnlockTimer" )
91 , m_bIsLocked( false )
93 m_aUnlockTimer.SetTimeout( 1500 );
94 m_aUnlockTimer.SetInvokeHandler( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
97 SwChartLockController_Helper::~SwChartLockController_Helper()
99 if (m_pDoc) // still connected?
100 suppress_fun_call_w_exception(Disconnect());
103 void SwChartLockController_Helper::StartOrContinueLocking()
105 if (!m_bIsLocked)
106 LockAllCharts();
107 m_aUnlockTimer.Start(); // start or continue time of locking
110 void SwChartLockController_Helper::Disconnect()
112 m_aUnlockTimer.Stop();
113 UnlockAllCharts();
114 m_pDoc = nullptr;
117 void SwChartLockController_Helper::LockUnlockAllCharts( bool bLock )
119 if (!m_pDoc)
120 return;
122 uno::Reference< frame::XModel > xRes;
123 SwOLENode *pONd;
124 SwStartNode *pStNd;
125 SwNodeIndex aIdx( *m_pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
126 while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
128 ++aIdx;
129 if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
130 !pONd->GetChartTableName().isEmpty() /* is chart object? */)
132 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
133 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
135 xRes.set( xIP->getComponent(), uno::UNO_QUERY );
136 if (xRes.is())
138 if (bLock)
139 xRes->lockControllers();
140 else
141 xRes->unlockControllers();
145 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
148 m_bIsLocked = bLock;
151 IMPL_LINK_NOARG( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, void )
153 UnlockAllCharts();
156 static std::mutex & GetChartMutex()
158 static std::mutex aMutex;
159 return aMutex;
162 static void LaunchModifiedEvent(
163 ::comphelper::OInterfaceContainerHelper4<util::XModifyListener> &rICH,
164 const uno::Reference< uno::XInterface > &rxI )
166 lang::EventObject aEvtObj( rxI );
167 std::unique_lock aGuard(GetChartMutex());
168 rICH.notifyEach( aGuard, &util::XModifyListener::modified, aEvtObj );
172 * rCellRangeName needs to be of one of the following formats:
173 * - e.g. "A2:E5" or
174 * - e.g. "Table1.A2:E5"
176 bool FillRangeDescriptor(
177 SwRangeDescriptor &rDesc,
178 std::u16string_view rCellRangeName )
180 sal_Int32 nToken = std::u16string_view::npos == rCellRangeName.find('.') ? 0 : 1;
181 std::u16string_view aCellRangeNoTableName( o3tl::getToken(rCellRangeName, nToken, '.' ) );
182 OUString aTLName( o3tl::getToken(aCellRangeNoTableName, 0, ':') ); // name of top left cell
183 OUString aBRName( o3tl::getToken(aCellRangeNoTableName, 1, ':') ); // name of bottom right cell
184 if(aTLName.isEmpty() || aBRName.isEmpty())
185 return false;
187 rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
188 SwXTextTable::GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop );
189 SwXTextTable::GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
190 rDesc.Normalize();
191 OSL_ENSURE( rDesc.nTop != -1 &&
192 rDesc.nLeft != -1 &&
193 rDesc.nBottom != -1 &&
194 rDesc.nRight != -1,
195 "failed to get range descriptor" );
196 OSL_ENSURE( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight,
197 "invalid range descriptor");
198 return true;
201 static OUString GetCellRangeName( const SwFrameFormat &rTableFormat, SwUnoCursor &rTableCursor )
203 OUString aRes;
205 //!! see also SwXTextTableCursor::getRangeName
207 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&rTableCursor);
208 if (!pUnoTableCursor)
209 return OUString();
211 // tdf#132714 empty outdated selection cache to avoid crashing in ActualizeSelection()
212 size_t nCount = pUnoTableCursor->GetSelectedBoxesCount();
213 while (nCount--)
214 pUnoTableCursor->DeleteBox(nCount);
216 pUnoTableCursor->MakeBoxSels();
218 const SwStartNode* pStart;
219 const SwTableBox* pStartBox = nullptr;
220 const SwTableBox* pEndBox = nullptr;
222 pStart = pUnoTableCursor->GetPoint()->GetNode().FindTableBoxStartNode();
223 if (pStart)
225 const SwTable* pTable = SwTable::FindTable( &rTableFormat );
226 pEndBox = pTable->GetTableBox( pStart->GetIndex());
227 aRes = pEndBox->GetName();
229 if(pUnoTableCursor->HasMark())
231 pStart = pUnoTableCursor->GetMark()->GetNode().FindTableBoxStartNode();
232 pStartBox = pTable->GetTableBox( pStart->GetIndex());
234 OSL_ENSURE( pStartBox, "start box not found" );
235 OSL_ENSURE( pEndBox, "end box not found" );
237 // need to switch start and end?
238 if (*pUnoTableCursor->GetPoint() < *pUnoTableCursor->GetMark())
240 const SwTableBox* pTmpBox = pStartBox;
241 pStartBox = pEndBox;
242 pEndBox = pTmpBox;
245 if (!pStartBox)
246 return aRes;
248 aRes = pStartBox->GetName() + ":";
249 if (pEndBox)
250 aRes += pEndBox->GetName();
251 else
252 aRes += pStartBox->GetName();
255 return aRes;
258 static OUString GetRangeRepFromTableAndCells( std::u16string_view rTableName,
259 std::u16string_view rStartCell, std::u16string_view rEndCell,
260 bool bForceEndCellName )
262 OSL_ENSURE( !rTableName.empty(), "table name missing" );
263 OSL_ENSURE( !rStartCell.empty(), "cell name missing" );
264 OUString aRes = OUString::Concat(rTableName) + "." + rStartCell;
266 if (!rEndCell.empty())
268 aRes += OUString::Concat(":") + rEndCell;
270 else if (bForceEndCellName)
272 aRes += OUString::Concat(":") + rStartCell;
275 return aRes;
278 static bool GetTableAndCellsFromRangeRep(
279 std::u16string_view rRangeRepresentation,
280 OUString &rTableName,
281 OUString &rStartCell,
282 OUString &rEndCell,
283 bool bSortStartEndCells = true )
285 // parse range representation for table name and cell/range names
286 // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
287 OUString aTableName; // table name
288 OUString aStartCell; // name of top left cell
289 OUString aEndCell; // name of bottom right cell
290 size_t nIdx = rRangeRepresentation.find( '.' );
291 if (nIdx != std::u16string_view::npos)
293 aTableName = rRangeRepresentation.substr( 0, nIdx );
294 std::u16string_view aRange = rRangeRepresentation.substr( nIdx + 1 ); // cell range
295 size_t nPos = aRange.find( ':' );
296 if (nPos != std::u16string_view::npos) // a cell-range like "Table1.A2:D4"
298 aStartCell = aRange.substr( 0, nPos );
299 aEndCell = aRange.substr( nPos + 1 );
301 // need to switch start and end cell ?
302 // (does not check for normalization here)
303 if (bSortStartEndCells && 1 == sw_CompareCellsByColFirst( aStartCell, aEndCell ))
304 std::swap(aStartCell, aEndCell);
306 else // a single cell like in "Table1.B3"
308 aStartCell = aEndCell = aRange;
312 bool bSuccess = !aTableName.isEmpty() &&
313 !aStartCell.isEmpty() && !aEndCell.isEmpty();
314 if (bSuccess)
316 rTableName = aTableName;
317 rStartCell = aStartCell;
318 rEndCell = aEndCell;
320 return bSuccess;
323 static void GetTableByName( const SwDoc &rDoc, std::u16string_view rTableName,
324 SwFrameFormat **ppTableFormat, SwTable **ppTable)
326 SwFrameFormat *pTableFormat = nullptr;
328 // find frame format of table
329 //! see SwXTextTables::getByName
330 const size_t nCount = rDoc.GetTableFrameFormatCount(true);
331 for (size_t i = 0; i < nCount && !pTableFormat; ++i)
333 SwFrameFormat& rTableFormat = rDoc.GetTableFrameFormat(i, true);
334 if(rTableName == rTableFormat.GetName())
335 pTableFormat = &rTableFormat;
338 if (ppTableFormat)
339 *ppTableFormat = pTableFormat;
341 if (ppTable)
342 *ppTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
345 static void GetFormatAndCreateCursorFromRangeRep(
346 const SwDoc *pDoc,
347 std::u16string_view rRangeRepresentation, // must be a single range (i.e. so called sub-range)
348 SwFrameFormat **ppTableFormat, // will be set to the table format of the table used in the range representation
349 std::shared_ptr<SwUnoCursor>& rpUnoCursor ) // will be set to cursor spanning the cell range (cursor will be created!)
351 OUString aTableName; // table name
352 OUString aStartCell; // name of top left cell
353 OUString aEndCell; // name of bottom right cell
354 bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
355 aTableName, aStartCell, aEndCell );
357 if (!bNamesFound)
359 if (ppTableFormat)
360 *ppTableFormat = nullptr;
361 rpUnoCursor.reset();
363 else
365 SwFrameFormat *pTableFormat = nullptr;
367 // is the correct table format already provided?
368 if (*ppTableFormat != nullptr && (*ppTableFormat)->GetName() == aTableName)
369 pTableFormat = *ppTableFormat;
370 else
371 GetTableByName( *pDoc, aTableName, &pTableFormat, nullptr );
373 *ppTableFormat = pTableFormat;
375 rpUnoCursor.reset(); // default result in case of failure
377 SwTable *pTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
378 // create new SwUnoCursor spanning the specified range
379 //! see also SwXTextTable::GetRangeByName
380 // #i80314#
381 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
382 const SwTableBox* pTLBox =
383 pTable ? pTable->GetTableBox( aStartCell, true ) : nullptr;
384 if(pTLBox)
386 const SwStartNode* pSttNd = pTLBox->GetSttNd();
387 SwPosition aPos(*pSttNd);
389 // set cursor to top left box of range
390 auto pUnoCursor = pTableFormat->GetDoc()->CreateUnoCursor(aPos, true);
391 pUnoCursor->Move( fnMoveForward, GoInNode );
392 pUnoCursor->SetRemainInSection( false );
394 // #i80314#
395 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
396 const SwTableBox* pBRBox = pTable->GetTableBox( aEndCell, true );
397 if(pBRBox)
399 pUnoCursor->SetMark();
400 pUnoCursor->GetPoint()->Assign( *pBRBox->GetSttNd() );
401 pUnoCursor->Move( fnMoveForward, GoInNode );
402 SwUnoTableCursor& rCursor =
403 dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
404 // HACK: remove pending actions for old style tables
405 UnoActionRemoveContext aRemoveContext(rCursor);
406 rCursor.MakeBoxSels();
407 rpUnoCursor = std::move(pUnoCursor);
413 static bool GetSubranges( std::u16string_view rRangeRepresentation,
414 uno::Sequence< OUString > &rSubRanges, bool bNormalize )
416 bool bRes = true;
417 const sal_Int32 nLen = comphelper::string::getTokenCount(rRangeRepresentation, ';');
418 uno::Sequence< OUString > aRanges( nLen );
420 sal_Int32 nCnt = 0;
421 if (nLen != 0)
423 OUString *pRanges = aRanges.getArray();
424 OUString aFirstTable;
425 sal_Int32 nPos = 0;
426 for( sal_Int32 i = 0; i < nLen && bRes; ++i )
428 const OUString aRange( o3tl::getToken(rRangeRepresentation, 0, ';', nPos ) );
429 if (!aRange.isEmpty())
431 pRanges[nCnt] = aRange;
433 OUString aTableName, aStartCell, aEndCell;
434 if (!GetTableAndCellsFromRangeRep( aRange,
435 aTableName, aStartCell, aEndCell ))
436 bRes = false;
438 if (bNormalize)
440 sw_NormalizeRange( aStartCell, aEndCell );
441 pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
442 aStartCell, aEndCell, true );
445 // make sure to use only a single table
446 if (nCnt == 0)
447 aFirstTable = aTableName;
448 else
449 if (aFirstTable != aTableName) bRes = false;
451 ++nCnt;
455 aRanges.realloc( nCnt );
457 rSubRanges = std::move(aRanges);
458 return bRes;
461 static void SortSubranges( uno::Sequence< OUString > &rSubRanges, bool bCmpByColumn )
463 sal_Int32 nLen = rSubRanges.getLength();
464 OUString *pSubRanges = rSubRanges.getArray();
466 OUString aSmallestTableName;
467 OUString aSmallestStartCell;
468 OUString aSmallestEndCell;
470 for (sal_Int32 i = 0; i < nLen; ++i)
472 sal_Int32 nIdxOfSmallest = i;
473 GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
474 aSmallestTableName, aSmallestStartCell, aSmallestEndCell );
475 if (aSmallestEndCell.isEmpty())
476 aSmallestEndCell = aSmallestStartCell;
478 for (sal_Int32 k = i+1; k < nLen; ++k)
480 // get cell names for sub range
481 OUString aTableName;
482 OUString aStartCell;
483 OUString aEndCell;
484 GetTableAndCellsFromRangeRep( pSubRanges[k],
485 aTableName, aStartCell, aEndCell );
486 if (aEndCell.isEmpty())
487 aEndCell = aStartCell;
489 // compare cell ranges ( is the new one smaller? )
490 if (-1 == sw_CompareCellRanges( aStartCell, aEndCell,
491 aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
493 nIdxOfSmallest = k;
494 aSmallestTableName = aTableName;
495 aSmallestStartCell = aStartCell;
496 aSmallestEndCell = aEndCell;
500 // move smallest element to the start of the not sorted area
501 const OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
502 pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
503 pSubRanges[ i ] = aTmp;
507 SwChartDataProvider::SwChartDataProvider( const SwDoc& rSwDoc ) :
508 m_pDoc( &rSwDoc )
510 m_bDisposed = false;
513 SwChartDataProvider::~SwChartDataProvider()
517 uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
518 const uno::Sequence< beans::PropertyValue >& rArguments, bool bTestOnly )
520 SolarMutexGuard aGuard;
521 if (m_bDisposed)
522 throw lang::DisposedException();
524 uno::Reference< chart2::data::XDataSource > xRes;
526 if (!m_pDoc)
527 throw uno::RuntimeException(u"Not connected to a document."_ustr);
529 // get arguments
530 OUString aRangeRepresentation;
531 uno::Sequence< sal_Int32 > aSequenceMapping;
532 bool bFirstIsLabel = false;
533 bool bDtaSrcIsColumns = true; // true : DataSource will be sequence of columns
534 // false: DataSource will be sequence of rows
536 OUString aChartOleObjectName; //work around wrong writer ranges ( see Issue 58464 )
537 sal_Int32 nArgs = rArguments.getLength();
538 OSL_ENSURE( nArgs != 0, "no properties provided" );
539 if (nArgs == 0)
540 return xRes;
541 for (const beans::PropertyValue& rArg : rArguments)
543 if ( rArg.Name == "DataRowSource" )
545 chart::ChartDataRowSource eSource;
546 if (!(rArg.Value >>= eSource))
548 sal_Int32 nTmp = 0;
549 if (!(rArg.Value >>= nTmp))
550 throw lang::IllegalArgumentException();
551 eSource = static_cast< chart::ChartDataRowSource >( nTmp );
553 bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
555 else if ( rArg.Name == "FirstCellAsLabel" )
557 if (!(rArg.Value >>= bFirstIsLabel))
558 throw lang::IllegalArgumentException();
560 else if ( rArg.Name == "CellRangeRepresentation" )
562 if (!(rArg.Value >>= aRangeRepresentation))
563 throw lang::IllegalArgumentException();
565 else if ( rArg.Name == "SequenceMapping" )
567 if (!(rArg.Value >>= aSequenceMapping))
568 throw lang::IllegalArgumentException();
570 else if ( rArg.Name == "ChartOleObjectName" )
572 if (!(rArg.Value >>= aChartOleObjectName))
573 throw lang::IllegalArgumentException();
577 uno::Sequence< OUString > aSubRanges;
578 // get sub-ranges and check that they all are from the very same table
579 bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, true );
581 if (!bOk && m_pDoc && !aChartOleObjectName.isEmpty() )
583 //try to correct the range here
584 //work around wrong writer ranges ( see Issue 58464 )
585 OUString aChartTableName;
587 const SwNodes& rNodes = m_pDoc->GetNodes();
588 for( SwNodeOffset nN = rNodes.Count(); nN--; )
590 SwNode* pNode = rNodes[nN];
591 if( !pNode )
592 continue;
593 const SwOLENode* pOleNode = pNode->GetOLENode();
594 if( !pOleNode )
595 continue;
596 const SwOLEObj& rOObj = pOleNode->GetOLEObj();
597 if( aChartOleObjectName == rOObj.GetCurrentPersistName() )
599 aChartTableName = pOleNode->GetChartTableName();
600 break;
604 if( !aChartTableName.isEmpty() )
606 //the wrong range is still shifted one row down
607 //thus the first row is missing and an invalid row at the end is added.
608 //Therefore we need to shift the range one row up
609 SwRangeDescriptor aDesc;
610 if (aRangeRepresentation.isEmpty())
611 return xRes; // we can't handle this thus returning an empty references
613 aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left
614 FillRangeDescriptor( aDesc, aRangeRepresentation );
615 aDesc.Normalize();
617 if (aDesc.nTop <= 0) // no chance to shift the range one row up?
618 return xRes; // we can't handle this thus returning an empty references
620 aDesc.nTop -= 1;
621 aDesc.nBottom -= 1;
623 OUString aNewStartCell( sw_GetCellName( aDesc.nLeft, aDesc.nTop ) );
624 OUString aNewEndCell( sw_GetCellName( aDesc.nRight, aDesc.nBottom ) );
625 aRangeRepresentation = GetRangeRepFromTableAndCells(
626 aChartTableName, aNewStartCell, aNewEndCell, true );
627 bOk = GetSubranges( aRangeRepresentation, aSubRanges, true );
630 if (!bOk) // different tables used, or incorrect range specifiers
631 throw lang::IllegalArgumentException();
633 SortSubranges( aSubRanges, bDtaSrcIsColumns );
635 // get table format for that single table from above
636 SwFrameFormat *pTableFormat = nullptr; // pointer to table format
637 std::shared_ptr<SwUnoCursor> pUnoCursor; // here required to check if the cells in the range do actually exist
638 if (aSubRanges.hasElements())
639 GetFormatAndCreateCursorFromRangeRep( m_pDoc, aSubRanges[0], &pTableFormat, pUnoCursor );
641 if (!pTableFormat || !pUnoCursor)
642 throw lang::IllegalArgumentException();
644 SwTable* pTable = SwTable::FindTable(pTableFormat);
645 if (pTable->IsTableComplex())
646 return xRes; // we can't handle this thus returning an empty references
648 // get a character map in the size of the table to mark
649 // all the ranges to use in
650 sal_Int32 nRows = pTable->GetTabLines().size();
651 sal_Int32 nCols = 0;
652 // As per tdf#149718 one should know that some cells can be merged together.
653 // Therefore, the number of columns (boxes in each row) are not necessarily
654 // equal. Here, we calculate the maximum number of columns in all rows.
655 for (sal_Int32 i = 0; i < nRows; ++i)
656 nCols = std::max(nCols, static_cast<sal_Int32>(pTable->GetTabLines()[i]->GetTabBoxes().size()));
658 std::vector<std::vector<char>> aMap(nRows);
659 for (sal_Int32 i = 0; i < nRows; ++i)
660 aMap[i].resize(nCols);
662 // iterate over subranges and mark used cells in above map
663 //!! by proceeding this way we automatically get rid of
664 //!! multiple listed or overlapping cell ranges which should
665 //!! just be ignored silently
666 for (const OUString& rSubRange : aSubRanges)
668 OUString aTableName, aStartCell, aEndCell;
669 bool bOk2 = GetTableAndCellsFromRangeRep(
670 rSubRange, aTableName, aStartCell, aEndCell );
671 OSL_ENSURE(bOk2, "failed to get table and start/end cells");
673 sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
674 SwXTextTable::GetCellPosition(aStartCell, nStartCol, nStartRow);
675 SwXTextTable::GetCellPosition(aEndCell, nEndCol, nEndRow);
676 OSL_ENSURE( nStartRow <= nEndRow && nStartCol <= nEndCol,
677 "cell range not normalized");
679 // test if the ranges span more than the available cells
680 if( nStartRow < 0 || nEndRow >= nRows ||
681 nStartCol < 0 || nEndCol >= nCols )
683 throw lang::IllegalArgumentException();
685 for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1)
687 for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2)
688 aMap[k1][k2] = 'x';
692 // find label and data sequences to use
694 sal_Int32 oi; // outer index (slower changing index)
695 sal_Int32 ii; // inner index (faster changing index)
696 sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
697 sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
698 std::vector<sal_Int32> aLabelIdx(oiEnd);
699 std::vector<sal_Int32> aDataStartIdx(oiEnd);
700 std::vector<sal_Int32> aDataLen(oiEnd);
701 for (oi = 0; oi < oiEnd; ++oi)
703 aLabelIdx[oi] = -1;
704 aDataStartIdx[oi] = -1;
705 aDataLen[oi] = 0;
708 for (oi = 0; oi < oiEnd; ++oi)
710 ii = 0;
711 while (ii < iiEnd)
713 char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
715 // label should be used but is not yet found?
716 if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
718 aLabelIdx[oi] = ii;
719 rChar = 'L'; // setting a different char for labels here
720 // makes the test for the data sequence below
721 // easier
724 // find data sequence
725 if (rChar == 'x' && aDataStartIdx[oi] == -1)
727 aDataStartIdx[oi] = ii;
729 // get length of data sequence
730 sal_Int32 nL = 0;
731 while (ii< iiEnd && 'x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
733 ++nL; ++ii;
735 aDataLen[oi] = nL;
737 // check that there is no other separate sequence of data
738 // to be found because that is not supported
739 while (ii < iiEnd)
741 if ('x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
742 throw lang::IllegalArgumentException();
743 ++ii;
746 else
747 ++ii;
751 // make some other consistency checks while calculating
752 // the number of XLabeledDataSequence to build:
753 // - labels should always be used or not at all
754 // - the data sequences should have equal non-zero length
755 sal_Int32 nNumLDS = 0;
756 if (oiEnd > 0)
758 for (oi = 0; oi < oiEnd; ++oi)
760 // row/col used at all?
761 if (aDataStartIdx[oi] != -1 &&
762 (!bFirstIsLabel || aLabelIdx[oi] != -1))
764 ++nNumLDS;
768 if (nNumLDS == 0)
769 throw lang::IllegalArgumentException();
771 // now we should have all necessary data to build a proper DataSource
772 // thus if we came this far there should be no further problem
773 if (bTestOnly)
774 return xRes; // have createDataSourcePossible return true
776 // create data source from found label and data sequences
777 uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aLabelSeqs(nNumLDS);
778 uno::Reference<chart2::data::XDataSequence>* pLabelSeqs = aLabelSeqs.getArray();
779 uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aDataSeqs(nNumLDS);
780 uno::Reference<chart2::data::XDataSequence>* pDataSeqs = aDataSeqs.getArray();
781 sal_Int32 nSeqsIdx = 0;
782 for (oi = 0; oi < oiEnd; ++oi)
784 // row/col not used? (see if-statement above where nNumLDS was counted)
785 if (!(aDataStartIdx[oi] != -1 &&
786 (!bFirstIsLabel || aLabelIdx[oi] != -1)))
787 continue;
789 // get cell ranges for label and data
791 SwRangeDescriptor aLabelDesc;
792 SwRangeDescriptor aDataDesc;
793 if (bDtaSrcIsColumns) // use columns
795 aLabelDesc.nTop = aLabelIdx[oi];
796 aLabelDesc.nLeft = oi;
797 aLabelDesc.nBottom = aLabelDesc.nTop;
798 aLabelDesc.nRight = oi;
800 aDataDesc.nTop = aDataStartIdx[oi];
801 aDataDesc.nLeft = oi;
802 aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1;
803 aDataDesc.nRight = oi;
805 else // use rows
807 aLabelDesc.nTop = oi;
808 aLabelDesc.nLeft = aLabelIdx[oi];
809 aLabelDesc.nBottom = oi;
810 aLabelDesc.nRight = aLabelDesc.nLeft;
812 aDataDesc.nTop = oi;
813 aDataDesc.nLeft = aDataStartIdx[oi];
814 aDataDesc.nBottom = oi;
815 aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1;
817 const OUString aBaseName = pTableFormat->GetName() + ".";
819 OUString aLabelRange;
820 if (aLabelIdx[oi] != -1)
822 aLabelRange = aBaseName
823 + sw_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop )
824 + ":" + sw_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
827 OUString aDataRange = aBaseName
828 + sw_GetCellName( aDataDesc.nLeft, aDataDesc.nTop )
829 + ":" + sw_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
831 // get cursors spanning the cell ranges for label and data
832 std::shared_ptr<SwUnoCursor> pLabelUnoCursor;
833 std::shared_ptr<SwUnoCursor> pDataUnoCursor;
834 GetFormatAndCreateCursorFromRangeRep(m_pDoc, aLabelRange, &pTableFormat, pLabelUnoCursor);
835 GetFormatAndCreateCursorFromRangeRep(m_pDoc, aDataRange, &pTableFormat, pDataUnoCursor);
837 // create XDataSequence's from cursors
838 if (pLabelUnoCursor)
839 pLabelSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pLabelUnoCursor);
840 OSL_ENSURE(pDataUnoCursor, "pointer to data sequence missing");
841 if (pDataUnoCursor)
842 pDataSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pDataUnoCursor);
843 if (pLabelUnoCursor || pDataUnoCursor)
844 ++nSeqsIdx;
846 OSL_ENSURE(nSeqsIdx == nNumLDS, "mismatch between sequence size and num,ber of entries");
848 // build data source from data and label sequences
849 uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aLDS(nNumLDS);
850 uno::Reference<chart2::data::XLabeledDataSequence>* pLDS = aLDS.getArray();
851 for (sal_Int32 i = 0; i < nNumLDS; ++i)
853 rtl::Reference<SwChartLabeledDataSequence> pLabeledDtaSeq = new SwChartLabeledDataSequence;
854 pLabeledDtaSeq->setLabel(pLabelSeqs[i]);
855 pLabeledDtaSeq->setValues(pDataSeqs[i]);
856 pLDS[i] = pLabeledDtaSeq;
859 // apply 'SequenceMapping' if it was provided
860 if (aSequenceMapping.hasElements())
862 uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aOld_LDS(aLDS);
863 uno::Reference<chart2::data::XLabeledDataSequence>* pOld_LDS = aOld_LDS.getArray();
865 sal_Int32 nNewCnt = 0;
866 for (sal_Int32 nIdx : aSequenceMapping)
868 // check that index to be used is valid
869 // and has not yet been used
870 if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
872 pLDS[nNewCnt++] = pOld_LDS[nIdx];
874 // mark index as being used already (avoids duplicate entries)
875 pOld_LDS[nIdx].clear();
878 // add not yet used 'old' sequences to new one
879 for (sal_Int32 i = 0; i < nNumLDS; ++i)
881 if (pOld_LDS[i].is())
882 pLDS[nNewCnt++] = pOld_LDS[i];
884 OSL_ENSURE(nNewCnt == nNumLDS, "unexpected size of resulting sequence");
887 xRes = new SwChartDataSource(aLDS);
888 return xRes;
891 sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
892 const uno::Sequence< beans::PropertyValue >& rArguments )
894 SolarMutexGuard aGuard;
896 bool bPossible = true;
899 Impl_createDataSource( rArguments, true );
901 catch (lang::IllegalArgumentException &)
903 bPossible = false;
906 return bPossible;
909 uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
910 const uno::Sequence< beans::PropertyValue >& rArguments )
912 SolarMutexGuard aGuard;
913 return Impl_createDataSource( rArguments );
917 * Fix for #i79009
918 * we need to return a property that has the same value as the property
919 * 'CellRangeRepresentation' but for all rows which are increased by one.
920 * E.g. Table1.A1:D5 -> Table1,A2:D6
921 * Since the problem is only for old charts which did not support multiple
922 * we do not need to provide that property/string if the 'CellRangeRepresentation'
923 * contains multiple ranges.
925 OUString SwChartDataProvider::GetBrokenCellRangeForExport(
926 std::u16string_view rCellRangeRepresentation )
928 // check that we do not have multiple ranges
929 if (std::u16string_view::npos == rCellRangeRepresentation.find( ';' ))
931 // get current cell and table names
932 OUString aTableName, aStartCell, aEndCell;
933 GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
934 aTableName, aStartCell, aEndCell, false );
935 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
936 SwXTextTable::GetCellPosition( aStartCell, nStartCol, nStartRow );
937 SwXTextTable::GetCellPosition( aEndCell, nEndCol, nEndRow );
939 // get new cell names
940 ++nStartRow;
941 ++nEndRow;
942 aStartCell = sw_GetCellName( nStartCol, nStartRow );
943 aEndCell = sw_GetCellName( nEndCol, nEndRow );
945 return GetRangeRepFromTableAndCells( aTableName,
946 aStartCell, aEndCell, false );
949 return OUString();
952 uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
953 const uno::Reference< chart2::data::XDataSource >& xDataSource )
955 SolarMutexGuard aGuard;
956 if (m_bDisposed)
957 throw lang::DisposedException();
959 uno::Sequence< beans::PropertyValue > aResult;
960 if (!xDataSource.is())
961 return aResult;
963 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
964 const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
965 sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
967 if (nNumDS_LDS == 0)
969 OSL_FAIL( "XLabeledDataSequence in data source contains 0 entries" );
970 return aResult;
973 SwFrameFormat *pTableFormat = nullptr;
974 SwTable *pTable = nullptr;
975 OUString aTableName;
976 sal_Int32 nTableRows = 0;
977 sal_Int32 nTableCols = 0;
979 // data used to build 'CellRangeRepresentation' from later on
980 std::vector< std::vector< char > > aMap;
982 uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
983 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
985 OUString aCellRanges;
986 sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither
987 sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have
988 // the expected size of 1 (i.e. if FirstCellAsLabel can
989 // be determined)
990 // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
991 // -2: neither/failed
992 for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1)
994 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
995 if( !xLabeledDataSequence.is() )
997 OSL_FAIL("got NULL for XLabeledDataSequence from Data source");
998 continue;
1000 const uno::Reference< chart2::data::XDataSequence > xCurLabel = xLabeledDataSequence->getLabel();
1001 const uno::Reference< chart2::data::XDataSequence > xCurValues = xLabeledDataSequence->getValues();
1003 // get sequence lengths for label and values.
1004 // (0 length is Ok)
1005 sal_Int32 nCurLabelSeqLen = -1;
1006 sal_Int32 nCurValuesSeqLen = -1;
1007 if (xCurLabel.is())
1008 nCurLabelSeqLen = xCurLabel->getData().getLength();
1009 if (xCurValues.is())
1010 nCurValuesSeqLen = xCurValues->getData().getLength();
1012 // check for consistent use of 'first cell as label'
1013 if (nLabelSeqLen == -1) // set initial value to compare with below further on
1014 nLabelSeqLen = nCurLabelSeqLen;
1015 if (nLabelSeqLen != nCurLabelSeqLen)
1016 nLabelSeqLen = -2; // failed / no consistent use of label cells
1018 // get table and cell names for label and values data sequences
1019 // (start and end cell will be sorted, i.e. start cell <= end cell)
1020 OUString aLabelTableName, aLabelStartCell, aLabelEndCell;
1021 OUString aValuesTableName, aValuesStartCell, aValuesEndCell;
1022 OUString aLabelRange, aValuesRange;
1023 if (xCurLabel.is())
1024 aLabelRange = xCurLabel->getSourceRangeRepresentation();
1025 if (xCurValues.is())
1026 aValuesRange = xCurValues->getSourceRangeRepresentation();
1027 if ((!aLabelRange.isEmpty() && !GetTableAndCellsFromRangeRep( aLabelRange,
1028 aLabelTableName, aLabelStartCell, aLabelEndCell )) ||
1029 !GetTableAndCellsFromRangeRep( aValuesRange,
1030 aValuesTableName, aValuesStartCell, aValuesEndCell ))
1032 return aResult; // failed -> return empty property sequence
1035 // make sure all sequences use the same table
1036 if (aTableName.isEmpty())
1037 aTableName = aValuesTableName; // get initial value to compare with
1038 if (aTableName.isEmpty() ||
1039 aTableName != aValuesTableName ||
1040 (!aLabelTableName.isEmpty() && aTableName != aLabelTableName))
1042 return aResult; // failed -> return empty property sequence
1045 // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
1046 // first and last cell used in both sequences
1048 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1049 const OUString aCell( !aLabelStartCell.isEmpty() ? aLabelStartCell : aValuesStartCell );
1050 OSL_ENSURE( !aCell.isEmpty() , "start cell missing?" );
1051 SwXTextTable::GetCellPosition( aCell, nFirstCol, nFirstRow);
1052 SwXTextTable::GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
1054 sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed
1055 if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
1057 OSL_ENSURE( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
1058 "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
1059 nDirection = 0; // default direction for a single cell should be 'columns'
1061 else // more than one cell is available (in values and label together!)
1063 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1064 nDirection = 1;
1065 else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
1066 nDirection = 0;
1067 else
1069 OSL_FAIL( "trying to determine 'DataRowSource': unexpected case found" );
1070 nDirection = -2;
1073 // check for consistent direction of data source
1074 if (nDtaSrcIsColumns == -1) // set initial value to compare with below
1075 nDtaSrcIsColumns = nDirection;
1076 if (nDtaSrcIsColumns != nDirection)
1078 nDtaSrcIsColumns = -2; // failed
1081 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1083 // build data to obtain 'SequenceMapping' later on
1085 OSL_ENSURE( nDtaSrcIsColumns == 0 || /* rows */
1086 nDtaSrcIsColumns == 1, /* columns */
1087 "unexpected value for 'nDtaSrcIsColumns'" );
1088 pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
1090 // build data used to determine 'CellRangeRepresentation' later on
1092 GetTableByName( *m_pDoc, aTableName, &pTableFormat, &pTable );
1093 if (!pTable || pTable->IsTableComplex())
1094 return aResult; // failed -> return empty property sequence
1095 nTableRows = pTable->GetTabLines().size();
1096 nTableCols = pTable->GetTabLines().front()->GetTabBoxes().size();
1097 aMap.resize( nTableRows );
1098 for (sal_Int32 i = 0; i < nTableRows; ++i)
1099 aMap[i].resize( nTableCols );
1101 if (!aLabelStartCell.isEmpty() && !aLabelEndCell.isEmpty())
1103 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1104 SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
1105 SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow );
1106 if (nStartRow < 0 || nEndRow >= nTableRows ||
1107 nStartCol < 0 || nEndCol >= nTableCols)
1109 return aResult; // failed -> return empty property sequence
1111 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1113 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1115 char &rChar = aMap[i][k];
1116 if (rChar == '\0') // check for overlapping values and/or labels
1117 rChar = 'L';
1118 else
1119 return aResult; // failed -> return empty property sequence
1123 if (!aValuesStartCell.isEmpty() && !aValuesEndCell.isEmpty())
1125 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1126 SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
1127 SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow );
1128 if (nStartRow < 0 || nEndRow >= nTableRows ||
1129 nStartCol < 0 || nEndCol >= nTableCols)
1131 return aResult; // failed -> return empty property sequence
1133 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1135 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1137 char &rChar = aMap[i][k];
1138 if (rChar == '\0') // check for overlapping values and/or labels
1139 rChar = 'x';
1140 else
1141 return aResult; // failed -> return empty property sequence
1147 #if OSL_DEBUG_LEVEL > 0
1148 // do some extra sanity checking that the length of the sequences
1149 // matches their range representation
1151 sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
1152 if (xCurLabel.is())
1154 SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
1155 SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow);
1156 OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
1157 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
1158 "label sequence length does not match range representation!" );
1160 if (xCurValues.is())
1162 SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
1163 SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow);
1164 OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
1165 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
1166 "value sequence length does not match range representation!" );
1169 #endif
1170 } // for
1172 // build value for 'CellRangeRepresentation'
1174 const OUString aCellRangeBase = aTableName + ".";
1175 OUString aCurRange;
1176 for (sal_Int32 i = 0; i < nTableRows; ++i)
1178 for (sal_Int32 k = 0; k < nTableCols; ++k)
1180 if (aMap[i][k] != '\0') // top-left cell of a sub-range found
1182 // find rectangular sub-range to use
1183 sal_Int32 nRowIndex1 = i; // row index
1184 sal_Int32 nColIndex1 = k; // column index
1185 sal_Int32 nRowSubLen = 0;
1186 sal_Int32 nColSubLen = 0;
1187 while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
1188 ++nRowSubLen;
1189 // be aware of shifted sequences!
1190 // (according to the checks done prior the length should be ok)
1191 while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
1192 && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
1194 ++nColIndex1;
1195 ++nColSubLen;
1197 OUString aStartCell( sw_GetCellName( k, i ) );
1198 OUString aEndCell( sw_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
1199 aCurRange = aCellRangeBase + aStartCell + ":" + aEndCell;
1200 if (!aCellRanges.isEmpty())
1201 aCellRanges += ";";
1202 aCellRanges += aCurRange;
1204 // clear already found sub-range from map
1205 for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2)
1206 for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2)
1207 aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
1211 // to be nice to the user we now sort the cell ranges according to
1212 // rows or columns depending on the direction used in the data source
1213 uno::Sequence< OUString > aSortedRanges;
1214 GetSubranges( aCellRanges, aSortedRanges, false /*sub ranges should already be normalized*/ );
1215 SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
1216 OUString aSortedCellRanges;
1217 for (const OUString& rSortedRange : aSortedRanges)
1219 if (!aSortedCellRanges.isEmpty())
1220 aSortedCellRanges += ";";
1221 aSortedCellRanges += rSortedRange;
1224 // build value for 'SequenceMapping'
1226 uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
1227 auto [begin, end] = asNonConstRange(aSortedMapping);
1228 std::sort(begin, end);
1229 bool bNeedSequenceMapping = false;
1230 for (sal_Int32 i = 0; i < aSequenceMapping.getLength(); ++i)
1232 auto it = std::find( std::cbegin(aSortedMapping), std::cend(aSortedMapping),
1233 aSequenceMapping[i] );
1234 pSequenceMapping[i] = std::distance(std::cbegin(aSortedMapping), it);
1236 if (i != aSequenceMapping[i])
1237 bNeedSequenceMapping = true;
1240 // check if 'SequenceMapping' is actually not required...
1241 // (don't write unnecessary properties to the XML file)
1242 if (!bNeedSequenceMapping)
1243 aSequenceMapping.realloc(0);
1245 // build resulting properties
1247 OSL_ENSURE(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
1248 "unexpected value for 'nLabelSeqLen'" );
1249 bool bFirstCellIsLabel = false; // default value if 'nLabelSeqLen' could not properly determined
1250 if (nLabelSeqLen > 0) // == 0 means no label sequence in use
1251 bFirstCellIsLabel = true;
1253 OSL_ENSURE( !aSortedCellRanges.isEmpty(), "CellRangeRepresentation missing" );
1254 const OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
1256 aResult.realloc(5);
1257 auto pResult = aResult.getArray();
1258 sal_Int32 nProps = 0;
1259 pResult[nProps ].Name = "FirstCellAsLabel";
1260 pResult[nProps++].Value <<= bFirstCellIsLabel;
1261 pResult[nProps ].Name = "CellRangeRepresentation";
1262 pResult[nProps++].Value <<= aSortedCellRanges;
1263 if (!aBrokenCellRangeForExport.isEmpty())
1265 pResult[nProps ].Name = "BrokenCellRangeForExport";
1266 pResult[nProps++].Value <<= aBrokenCellRangeForExport;
1268 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1270 chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
1271 chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
1272 pResult[nProps ].Name = "DataRowSource";
1273 pResult[nProps++].Value <<= eDataRowSource;
1275 if (aSequenceMapping.hasElements())
1277 pResult[nProps ].Name = "SequenceMapping";
1278 pResult[nProps++].Value <<= aSequenceMapping;
1281 aResult.realloc( nProps );
1283 return aResult;
1286 uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
1287 std::u16string_view rRangeRepresentation, bool bTestOnly )
1289 if (m_bDisposed)
1290 throw lang::DisposedException();
1292 SwFrameFormat *pTableFormat = nullptr; // pointer to table format
1293 std::shared_ptr<SwUnoCursor> pUnoCursor; // pointer to new created cursor spanning the cell range
1294 GetFormatAndCreateCursorFromRangeRep( m_pDoc, rRangeRepresentation,
1295 &pTableFormat, pUnoCursor );
1296 if (!pTableFormat || !pUnoCursor)
1297 throw lang::IllegalArgumentException();
1299 // check that cursors point and mark are in a single row or column.
1300 OUString aCellRange( GetCellRangeName( *pTableFormat, *pUnoCursor ) );
1301 SwRangeDescriptor aDesc;
1302 FillRangeDescriptor( aDesc, aCellRange );
1303 if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1304 throw lang::IllegalArgumentException();
1306 uno::Reference< chart2::data::XDataSequence > xDataSeq;
1307 if (!bTestOnly)
1308 xDataSeq = new SwChartDataSequence( *this, *pTableFormat, pUnoCursor );
1310 return xDataSeq;
1313 sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
1314 const OUString& rRangeRepresentation )
1316 SolarMutexGuard aGuard;
1318 bool bPossible = true;
1321 Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, true );
1323 catch (lang::IllegalArgumentException &)
1325 bPossible = false;
1328 return bPossible;
1331 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
1332 const OUString& rRangeRepresentation )
1334 SolarMutexGuard aGuard;
1335 return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
1338 uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
1340 // note: it is no error to return nothing here
1341 return uno::Reference< sheet::XRangeSelection >();
1344 uno::Reference<css::chart2::data::XDataSequence> SAL_CALL
1345 SwChartDataProvider::createDataSequenceByValueArray(
1346 const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/,
1347 const OUString& /*aRoleQualifier*/ )
1349 return uno::Reference<css::chart2::data::XDataSequence>();
1352 void SAL_CALL SwChartDataProvider::dispose( )
1354 bool bMustDispose( false );
1356 std::unique_lock aGuard( GetChartMutex() );
1357 bMustDispose = !m_bDisposed;
1358 if (!m_bDisposed)
1359 m_bDisposed = true;
1361 if (!bMustDispose)
1362 return;
1364 // dispose all data-sequences
1365 for (const auto& rEntry : m_aDataSequences)
1367 DisposeAllDataSequences( rEntry.first );
1369 // release all references to data-sequences
1370 m_aDataSequences.clear();
1372 // require listeners to release references to this object
1373 lang::EventObject aEvtObj( static_cast< chart2::data::XDataProvider * >(this) );
1374 std::unique_lock aGuard( GetChartMutex() );
1375 m_aEventListeners.disposeAndClear( aGuard, aEvtObj );
1378 void SAL_CALL SwChartDataProvider::addEventListener(
1379 const uno::Reference< lang::XEventListener >& rxListener )
1381 std::unique_lock aGuard( GetChartMutex() );
1382 if (!m_bDisposed && rxListener.is())
1383 m_aEventListeners.addInterface( aGuard, rxListener );
1386 void SAL_CALL SwChartDataProvider::removeEventListener(
1387 const uno::Reference< lang::XEventListener >& rxListener )
1389 std::unique_lock aGuard( GetChartMutex() );
1390 if (!m_bDisposed && rxListener.is())
1391 m_aEventListeners.removeInterface( aGuard, rxListener );
1394 OUString SAL_CALL SwChartDataProvider::getImplementationName( )
1396 return u"SwChartDataProvider"_ustr;
1399 sal_Bool SAL_CALL SwChartDataProvider::supportsService(const OUString& rServiceName )
1401 return cppu::supportsService(this, rServiceName);
1404 uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( )
1406 return { u"com.sun.star.chart2.data.DataProvider"_ustr};
1409 void SwChartDataProvider::AddDataSequence( const SwTable &rTable, rtl::Reference< SwChartDataSequence > const &rxDataSequence )
1411 Vec_DataSequenceRef_t& rVec = m_aDataSequences[ &rTable ];
1412 assert(std::find_if(rVec.begin(), rVec.end(),
1413 [&rxDataSequence](const unotools::WeakReference < SwChartDataSequence >& i)
1415 return i.get() == rxDataSequence;
1416 }) == rVec.end() && "duplicate insert");
1417 rVec.push_back( rxDataSequence );
1420 void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, rtl::Reference< SwChartDataSequence > const &rxDataSequence )
1422 Vec_DataSequenceRef_t& rVec = m_aDataSequences[ &rTable ];
1423 std::erase_if(rVec,
1424 [&rxDataSequence](const unotools::WeakReference < SwChartDataSequence >& i)
1426 return i.get() == rxDataSequence;
1430 void SwChartDataProvider::InvalidateTable( const SwTable *pTable, bool bImmediate )
1432 OSL_ENSURE( pTable, "table pointer is NULL" );
1433 if (!pTable)
1434 return;
1436 if (!m_bDisposed)
1437 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
1439 const Vec_DataSequenceRef_t &rVec = m_aDataSequences[ pTable ];
1440 for (const unotools::WeakReference<SwChartDataSequence>& rItem : rVec)
1442 rtl::Reference< SwChartDataSequence > xRef(rItem);
1443 if (xRef.is())
1445 // mark the sequence as 'dirty' and notify listeners
1446 xRef->setModified( true );
1450 // tdf#122995 added Immediate-mode to allow non-timer-delayed Chart invalidation
1451 if (bImmediate && !m_bDisposed)
1452 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().Disconnect();
1455 void SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
1457 OSL_ENSURE( pTable, "table pointer is NULL" );
1458 if (!pTable)
1459 return;
1461 if (!m_bDisposed)
1462 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
1464 Vec_DataSequenceRef_t &rVec = m_aDataSequences[ pTable ];
1466 // iterate over all data-sequences for that table...
1467 auto aIt( rVec.begin() );
1468 while (aIt != rVec.end())
1470 bool bNowEmpty = false;
1471 bool bSeqDisposed = false;
1473 // check if weak reference is still valid...
1474 rtl::Reference< SwChartDataSequence > pDataSeq(*aIt);
1475 if (pDataSeq.is())
1477 // then delete that table box (check if implementation cursor needs to be adjusted)
1480 bNowEmpty = pDataSeq->DeleteBox( rBox );
1482 catch (const lang::DisposedException&)
1484 bNowEmpty = true;
1485 bSeqDisposed = true;
1489 if (bNowEmpty)
1491 aIt = rVec.erase( aIt );
1492 if (pDataSeq && !bSeqDisposed)
1493 pDataSeq->dispose(); // the current way to tell chart that sth. got removed
1495 else
1496 ++aIt;
1500 void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
1502 OSL_ENSURE( pTable, "table pointer is NULL" );
1503 if (!pTable)
1504 return;
1506 if (!m_bDisposed)
1507 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
1509 //! make a copy of the STL container!
1510 //! This is necessary since calling 'dispose' will implicitly remove an element
1511 //! of the original container, and thus any iterator in the original container
1512 //! would become invalid.
1513 const Vec_DataSequenceRef_t aVec( m_aDataSequences[ pTable ] );
1515 for (const unotools::WeakReference<SwChartDataSequence>& rItem : aVec)
1517 rtl::Reference< SwChartDataSequence > xRef(rItem);
1518 if (xRef.is())
1520 xRef->dispose();
1526 * SwChartDataProvider::AddRowCols tries to notify charts of added columns
1527 * or rows and extends the value sequence respectively (if possible).
1528 * If those can be added to the end of existing value data-sequences those
1529 * sequences get modified accordingly and will send a modification
1530 * notification (calling 'setModified
1532 * Since this function is a work-around for non existent Writer core functionality
1533 * (no arbitrary multi-selection in tables that can be used to define a
1534 * data-sequence) this function will be somewhat unreliable.
1535 * For example we will only try to adapt value sequences. For this we assume
1536 * that a sequence of length 1 is a label sequence and those with length >= 2
1537 * we presume to be value sequences. Also new cells can only be added in the
1538 * direction the value sequence is already pointing (rows / cols) and at the
1539 * start or end of the values data-sequence.
1540 * Nothing needs to be done if the new cells are in between the table cursors
1541 * point and mark since data-sequence are considered to consist of all cells
1542 * between those.
1543 * New rows/cols need to be added already to the table before calling
1544 * this function.
1546 void SwChartDataProvider::AddRowCols(
1547 const SwTable &rTable,
1548 const SwSelBoxes& rBoxes,
1549 sal_uInt16 nLines, bool bBehind )
1551 if (rTable.IsTableComplex())
1552 return;
1554 const size_t nBoxes = rBoxes.size();
1555 if (nBoxes < 1 || nLines < 1)
1556 return;
1558 SwTableBox* pFirstBox = rBoxes[0];
1559 SwTableBox* pLastBox = rBoxes.back();
1561 if (!(pFirstBox && pLastBox))
1562 return;
1564 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1565 SwXTextTable::GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow );
1566 SwXTextTable::GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow );
1568 bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/
1569 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1570 bAddCols = true;
1571 if (nFirstCol != nLastCol && nFirstRow != nLastRow)
1572 return;
1574 //get range of indices in col/rows for new cells
1575 sal_Int32 nFirstNewCol = nFirstCol;
1576 sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines;
1577 if (bAddCols)
1579 OSL_ENSURE( nFirstCol == nLastCol, "column indices seem broken" );
1580 nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines;
1581 nFirstNewRow = nFirstRow;
1584 // iterate over all data-sequences for the table
1585 const Vec_DataSequenceRef_t &rVec = m_aDataSequences[ &rTable ];
1586 for (const unotools::WeakReference<SwChartDataSequence>& rItem : rVec)
1588 rtl::Reference< SwChartDataSequence > pDataSeq(rItem);
1589 if (pDataSeq.is())
1591 const sal_Int32 nLen = pDataSeq->getTextualData().getLength();
1592 if (nLen > 1) // value data-sequence ?
1594 SwRangeDescriptor aDesc;
1595 pDataSeq->FillRangeDesc( aDesc );
1597 chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
1598 if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1599 eDRSource = chart::ChartDataRowSource_ROWS;
1601 if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
1603 // add rows: extend affected columns by newly added row cells
1604 pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
1606 else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
1608 // add cols: extend affected rows by newly added column cells
1609 pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
1616 // XRangeXMLConversion
1617 OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const OUString& rRangeRepresentation )
1619 SolarMutexGuard aGuard;
1620 if (m_bDisposed)
1621 throw lang::DisposedException();
1623 if (rRangeRepresentation.isEmpty())
1624 return OUString();
1626 OUStringBuffer aRes;
1628 // multiple ranges are delimited by a ';' like in
1629 // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
1630 SwTable* pFirstFoundTable = nullptr; // to check that only one table will be used
1631 sal_Int32 nPos = 0;
1632 do {
1633 const OUString aRange( rRangeRepresentation.getToken(0, ';', nPos) );
1634 SwFrameFormat *pTableFormat = nullptr; // pointer to table format
1635 std::shared_ptr<SwUnoCursor> pCursor;
1636 GetFormatAndCreateCursorFromRangeRep( m_pDoc, aRange, &pTableFormat, pCursor );
1637 if (!pTableFormat)
1638 throw lang::IllegalArgumentException();
1639 SwTable* pTable = SwTable::FindTable( pTableFormat );
1640 if (pTable->IsTableComplex())
1641 throw uno::RuntimeException(u"Table too complex."_ustr);
1643 // check that there is only one table used in all ranges
1644 if (!pFirstFoundTable)
1645 pFirstFoundTable = pTable;
1646 if (pTable != pFirstFoundTable)
1647 throw lang::IllegalArgumentException();
1649 OUString aTableName;
1650 OUString aStartCell;
1651 OUString aEndCell;
1652 if (!GetTableAndCellsFromRangeRep( aRange, aTableName, aStartCell, aEndCell ))
1653 throw lang::IllegalArgumentException();
1655 sal_Int32 nCol, nRow;
1656 SwXTextTable::GetCellPosition( aStartCell, nCol, nRow );
1657 if (nCol < 0 || nRow < 0)
1658 throw uno::RuntimeException(u"Cell not found."_ustr);
1660 //!! following objects/functions are implemented in XMLRangeHelper.?xx
1661 //!! which is a copy of the respective file from chart2 !!
1662 XMLRangeHelper::CellRange aCellRange;
1663 aCellRange.aTableName = aTableName;
1664 aCellRange.aUpperLeft.nColumn = nCol;
1665 aCellRange.aUpperLeft.nRow = nRow;
1666 aCellRange.aUpperLeft.bIsEmpty = false;
1667 if (aStartCell != aEndCell && !aEndCell.isEmpty())
1669 SwXTextTable::GetCellPosition( aEndCell, nCol, nRow );
1670 if (nCol < 0 || nRow < 0)
1671 throw uno::RuntimeException(u"Cell not found."_ustr);
1673 aCellRange.aLowerRight.nColumn = nCol;
1674 aCellRange.aLowerRight.nRow = nRow;
1675 aCellRange.aLowerRight.bIsEmpty = false;
1677 OUString aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
1678 if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
1679 aRes.append(" ");
1680 aRes.append(aTmp);
1682 while (nPos>0);
1684 return aRes.makeStringAndClear();
1687 OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const OUString& rXMLRange )
1689 SolarMutexGuard aGuard;
1690 if (m_bDisposed)
1691 throw lang::DisposedException();
1693 if (rXMLRange.isEmpty())
1694 return OUString();
1696 OUStringBuffer aRes;
1698 // multiple ranges are delimited by a ' ' like in
1699 // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
1700 OUString aFirstFoundTable; // to check that only one table will be used
1701 sal_Int32 nPos = 0;
1704 OUString aRange( rXMLRange.getToken(0, ' ', nPos) );
1706 //!! following objects and function are implemented in XMLRangeHelper.?xx
1707 //!! which is a copy of the respective file from chart2 !!
1708 XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
1710 // check that there is only one table used in all ranges
1711 if (aFirstFoundTable.isEmpty())
1712 aFirstFoundTable = aCellRange.aTableName;
1713 if (aCellRange.aTableName != aFirstFoundTable)
1714 throw lang::IllegalArgumentException();
1716 OUString aTmp = aCellRange.aTableName + "." +
1717 sw_GetCellName( aCellRange.aUpperLeft.nColumn,
1718 aCellRange.aUpperLeft.nRow );
1719 // does cell range consist of more than a single cell?
1720 if (!aCellRange.aLowerRight.bIsEmpty)
1722 aTmp += ":" + sw_GetCellName( aCellRange.aLowerRight.nColumn,
1723 aCellRange.aLowerRight.nRow );
1726 if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
1727 aRes.append(";");
1728 aRes.append(aTmp);
1730 while (nPos>0);
1732 return aRes.makeStringAndClear();
1735 SwChartDataSource::SwChartDataSource(
1736 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
1737 m_aLDS( rLDS )
1741 SwChartDataSource::~SwChartDataSource()
1745 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( )
1747 SolarMutexGuard aGuard;
1748 return m_aLDS;
1751 OUString SAL_CALL SwChartDataSource::getImplementationName( )
1753 return u"SwChartDataSource"_ustr;
1756 sal_Bool SAL_CALL SwChartDataSource::supportsService(const OUString& rServiceName )
1758 return cppu::supportsService(this, rServiceName);
1761 uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( )
1763 return { u"com.sun.star.chart2.data.DataSource"_ustr };
1766 SwChartDataSequence::SwChartDataSequence(
1767 SwChartDataProvider& rProvider,
1768 SwFrameFormat& rTableFormat,
1769 const std::shared_ptr<SwUnoCursor>& pTableCursor ) :
1770 m_pFormat(&rTableFormat),
1771 m_aRowLabelText( SwResId( STR_CHART2_ROW_LABEL_TEXT ) ),
1772 m_aColLabelText( SwResId( STR_CHART2_COL_LABEL_TEXT ) ),
1773 m_xDataProvider( &rProvider ),
1774 m_pTableCursor( pTableCursor ),
1775 m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
1777 StartListening(rTableFormat.GetNotifier());
1778 m_bDisposed = false;
1780 acquire();
1783 const SwTable* pTable = SwTable::FindTable( &rTableFormat );
1784 if (pTable)
1786 m_xDataProvider->AddDataSequence( *pTable, this );
1787 m_xDataProvider->addEventListener( static_cast< lang::XEventListener * >(this) );
1789 else {
1790 OSL_FAIL( "table missing" );
1793 catch (uno::RuntimeException &)
1795 // TODO: shouldn't there be a call to release() here?
1796 throw;
1798 catch (uno::Exception &)
1801 release();
1803 #if OSL_DEBUG_LEVEL > 0
1804 // check if it can properly convert into a SwUnoTableCursor
1805 // which is required for some functions
1806 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
1807 OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
1808 #endif
1811 SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
1812 SwChartDataSequenceBaseClass(rObj),
1813 SvtListener(),
1814 m_pFormat( rObj.m_pFormat ),
1815 m_aRole( rObj.m_aRole ),
1816 m_aRowLabelText( SwResId(STR_CHART2_ROW_LABEL_TEXT) ),
1817 m_aColLabelText( SwResId(STR_CHART2_COL_LABEL_TEXT) ),
1818 m_xDataProvider( rObj.m_xDataProvider ),
1819 m_pTableCursor( rObj.m_pTableCursor ),
1820 m_pPropSet( rObj.m_pPropSet )
1822 if(m_pFormat)
1823 StartListening(m_pFormat->GetNotifier());
1824 m_bDisposed = false;
1826 acquire();
1829 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
1830 if (pTable)
1832 m_xDataProvider->AddDataSequence( *pTable, this );
1833 m_xDataProvider->addEventListener( static_cast< lang::XEventListener * >(this) );
1835 else {
1836 OSL_FAIL( "table missing" );
1839 catch (uno::RuntimeException &)
1841 // TODO: shouldn't there be a call to release() here?
1842 throw;
1844 catch (uno::Exception &)
1847 release();
1849 #if OSL_DEBUG_LEVEL > 0
1850 // check if it can properly convert into a SwUnoTableCursor
1851 // which is required for some functions
1852 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
1853 OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
1854 #endif
1857 SwChartDataSequence::~SwChartDataSequence()
1861 OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( )
1863 SolarMutexGuard aGuard;
1864 if (m_bDisposed)
1865 throw lang::DisposedException();
1867 OUString aRes;
1868 SwFrameFormat* pTableFormat = GetFrameFormat();
1869 if (pTableFormat)
1871 const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
1872 OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
1873 aRes = pTableFormat->GetName() + "." + aCellRange;
1875 return aRes;
1878 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
1879 chart2::data::LabelOrigin eLabelOrigin )
1881 SolarMutexGuard aGuard;
1882 if (m_bDisposed)
1883 throw lang::DisposedException();
1885 uno::Sequence< OUString > aLabels;
1888 SwRangeDescriptor aDesc;
1889 bool bOk = false;
1890 SwFrameFormat* pTableFormat = GetFrameFormat();
1891 if (!pTableFormat)
1892 throw uno::RuntimeException(u"No table format found."_ustr);
1893 SwTable* pTable = SwTable::FindTable( pTableFormat );
1894 if (!pTable)
1895 throw uno::RuntimeException(u"No table found."_ustr);
1896 if (pTable->IsTableComplex())
1897 throw uno::RuntimeException(u"Table too complex."_ustr);
1899 const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
1900 OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
1901 bOk = FillRangeDescriptor( aDesc, aCellRange );
1902 OSL_ENSURE( bOk, "failed to get SwRangeDescriptor" );
1904 if (bOk)
1906 aDesc.Normalize();
1907 sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
1908 sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
1909 OSL_ENSURE( nColSpan == 1 || nRowSpan == 1,
1910 "unexpected range of selected cells" );
1912 OUString aText; // label text to be returned
1913 bool bReturnEmptyText = false;
1914 bool bUseCol = true;
1915 if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
1916 bUseCol = true;
1917 else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
1918 bUseCol = false;
1919 else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
1921 bUseCol = nColSpan < nRowSpan;
1922 bReturnEmptyText = nColSpan == nRowSpan;
1924 else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
1926 bUseCol = nColSpan > nRowSpan;
1927 bReturnEmptyText = nColSpan == nRowSpan;
1929 else {
1930 OSL_FAIL( "unexpected case" );
1933 // build label sequence
1935 sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
1936 aLabels.realloc( nSeqLen );
1937 OUString *pLabels = aLabels.getArray();
1938 for (sal_Int32 i = 0; i < nSeqLen; ++i)
1940 if (!bReturnEmptyText)
1942 aText = bUseCol ? m_aColLabelText : m_aRowLabelText;
1943 sal_Int32 nCol = aDesc.nLeft;
1944 sal_Int32 nRow = aDesc.nTop;
1945 if (bUseCol)
1946 nCol = nCol + i;
1947 else
1948 nRow = nRow + i;
1949 OUString aCellName( sw_GetCellName( nCol, nRow ) );
1951 sal_Int32 nLen = aCellName.getLength();
1952 if (nLen)
1954 const sal_Unicode *pBuf = aCellName.getStr();
1955 const sal_Unicode *pEnd = pBuf + nLen;
1956 while (pBuf < pEnd && ('0' > *pBuf || *pBuf > '9'))
1957 ++pBuf;
1958 // start of number found?
1959 if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
1961 OUString aRplc;
1962 std::u16string_view aNew;
1963 if (bUseCol)
1965 aRplc = "%COLUMNLETTER";
1966 aNew = aCellName.subView(0, pBuf - aCellName.getStr());
1968 else
1970 aRplc = "%ROWNUMBER";
1971 aNew = std::u16string_view(pBuf, (aCellName.getStr() + nLen) - pBuf);
1973 aText = aText.replaceFirst( aRplc, aNew );
1977 pLabels[i] = aText;
1982 return aLabels;
1985 ::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
1986 ::sal_Int32 /*nIndex*/ )
1988 return 0;
1991 std::vector< css::uno::Reference< css::table::XCell > > SwChartDataSequence::GetCells()
1993 if (m_bDisposed)
1994 throw lang::DisposedException();
1995 auto pTableFormat(GetFrameFormat());
1996 if(!pTableFormat)
1997 return std::vector< css::uno::Reference< css::table::XCell > >();
1998 auto pTable(SwTable::FindTable(pTableFormat));
1999 if(pTable->IsTableComplex())
2000 return std::vector< css::uno::Reference< css::table::XCell > >();
2001 SwRangeDescriptor aDesc;
2002 if(!FillRangeDescriptor(aDesc, GetCellRangeName(*pTableFormat, *m_pTableCursor)))
2003 return std::vector< css::uno::Reference< css::table::XCell > >();
2004 return SwXCellRange::CreateXCellRange(m_pTableCursor, *pTableFormat, aDesc)->GetCells();
2007 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData()
2009 SolarMutexGuard aGuard;
2010 auto vCells(GetCells());
2011 uno::Sequence< OUString > vTextData(vCells.size());
2012 std::transform(vCells.begin(),
2013 vCells.end(),
2014 vTextData.getArray(),
2015 [] (decltype(vCells)::value_type& xCell)
2016 { return static_cast<SwXCell*>(xCell.get())->getString(); });
2017 return vTextData;
2020 uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData()
2022 SolarMutexGuard aGuard;
2025 auto vCells(GetCells());
2026 uno::Sequence< uno::Any > vAnyData(vCells.size());
2027 std::transform(vCells.begin(),
2028 vCells.end(),
2029 vAnyData.getArray(),
2030 [] (decltype(vCells)::value_type& xCell)
2031 { return static_cast<SwXCell*>(xCell.get())->GetAny(); });
2032 return vAnyData;
2034 catch (const lang::DisposedException&)
2036 TOOLS_WARN_EXCEPTION( "sw", "unexpected exception caught" );
2038 return uno::Sequence< uno::Any >{};
2041 uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData()
2043 SolarMutexGuard aGuard;
2044 auto vCells(GetCells());
2045 uno::Sequence< double > vNumData(vCells.size());
2046 std::transform(vCells.begin(),
2047 vCells.end(),
2048 vNumData.getArray(),
2049 [] (decltype(vCells)::value_type& xCell)
2050 { return static_cast<SwXCell*>(xCell.get())->GetForcedNumericalValue(); });
2051 return vNumData;
2054 uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( )
2056 SolarMutexGuard aGuard;
2057 if (m_bDisposed)
2058 throw lang::DisposedException();
2059 return new SwChartDataSequence( *this );
2062 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( )
2064 SolarMutexGuard aGuard;
2065 if (m_bDisposed)
2066 throw lang::DisposedException();
2068 static uno::Reference< beans::XPropertySetInfo > xRes = m_pPropSet->getPropertySetInfo();
2069 return xRes;
2072 void SAL_CALL SwChartDataSequence::setPropertyValue(
2073 const OUString& rPropertyName,
2074 const uno::Any& rValue )
2076 SolarMutexGuard aGuard;
2077 if (m_bDisposed)
2078 throw lang::DisposedException();
2080 if (rPropertyName != UNO_NAME_ROLE)
2081 throw beans::UnknownPropertyException(rPropertyName);
2083 if ( !(rValue >>= m_aRole) )
2084 throw lang::IllegalArgumentException();
2087 uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
2088 const OUString& rPropertyName )
2090 SolarMutexGuard aGuard;
2091 if (m_bDisposed)
2092 throw lang::DisposedException();
2094 if (!(rPropertyName == UNO_NAME_ROLE))
2095 throw beans::UnknownPropertyException(rPropertyName);
2097 return uno::Any(m_aRole);
2100 void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
2101 const OUString& /*rPropertyName*/,
2102 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2104 OSL_FAIL( "not implemented" );
2107 void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
2108 const OUString& /*rPropertyName*/,
2109 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2111 OSL_FAIL( "not implemented" );
2114 void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
2115 const OUString& /*rPropertyName*/,
2116 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2118 OSL_FAIL( "not implemented" );
2121 void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
2122 const OUString& /*rPropertyName*/,
2123 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2125 OSL_FAIL( "not implemented" );
2128 OUString SAL_CALL SwChartDataSequence::getImplementationName( )
2130 return u"SwChartDataSequence"_ustr;
2133 sal_Bool SAL_CALL SwChartDataSequence::supportsService(const OUString& rServiceName )
2135 return cppu::supportsService(this, rServiceName);
2138 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( )
2140 return { u"com.sun.star.chart2.data.DataSequence"_ustr };
2143 void SwChartDataSequence::Notify( const SfxHint& rHint)
2145 if(rHint.GetId() == SfxHintId::Dying)
2146 m_pFormat = nullptr;
2147 if(!m_pFormat || !m_pTableCursor)
2149 m_pFormat = nullptr;
2150 m_pTableCursor.reset(nullptr);
2151 dispose();
2153 else if (rHint.GetId() == SfxHintId::SwLegacyModify
2154 || rHint.GetId() == SfxHintId::SwFormatChange
2155 || rHint.GetId() == SfxHintId::SwAttrSetChange
2156 || rHint.GetId() == SfxHintId::SwObjectDying
2157 || rHint.GetId() == SfxHintId::SwUpdateAttr)
2159 setModified( true );
2163 sal_Bool SAL_CALL SwChartDataSequence::isModified( )
2165 SolarMutexGuard aGuard;
2166 if (m_bDisposed)
2167 throw lang::DisposedException();
2169 return true;
2172 void SAL_CALL SwChartDataSequence::setModified(
2173 sal_Bool bModified )
2175 SolarMutexGuard aGuard;
2176 if (m_bDisposed)
2177 throw lang::DisposedException();
2179 if (bModified)
2180 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2183 void SAL_CALL SwChartDataSequence::addModifyListener(
2184 const uno::Reference< util::XModifyListener >& rxListener )
2186 std::unique_lock aGuard( GetChartMutex() );
2187 if (!m_bDisposed && rxListener.is())
2188 m_aModifyListeners.addInterface( aGuard, rxListener );
2191 void SAL_CALL SwChartDataSequence::removeModifyListener(
2192 const uno::Reference< util::XModifyListener >& rxListener )
2194 std::unique_lock aGuard( GetChartMutex() );
2195 if (!m_bDisposed && rxListener.is())
2196 m_aModifyListeners.removeInterface( aGuard, rxListener );
2199 void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
2201 if (m_bDisposed)
2202 throw lang::DisposedException();
2203 if (rSource.Source == cppu::getXWeak(m_xDataProvider.get()))
2205 m_xDataProvider.clear();
2209 void SAL_CALL SwChartDataSequence::dispose( )
2212 std::unique_lock aGuard( GetChartMutex() );
2213 if (m_bDisposed)
2214 return;
2215 m_bDisposed = true;
2218 if (m_xDataProvider.is())
2220 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
2221 if (pTable)
2223 m_xDataProvider->RemoveDataSequence( *pTable, this );
2225 else {
2226 OSL_FAIL( "table missing" );
2229 //#i119653# The bug is crashed for an exception thrown by
2230 //SwCharDataSequence::setModified() because
2231 //the SwCharDataSequence object has been disposed.
2233 //Actually, the former design of SwClient will disconnect itself
2234 //from the notification list in its destructor.
2236 //But the SwCharDataSequence won't be destructed but disposed in code
2237 //(the data member SwChartDataSequence::bDisposed will be set to
2238 //TRUE), the relationship between client and modification is not
2239 //released.
2241 //So any notification from modify object will lead to said
2242 //exception threw out. Recorrect the logic of code in
2243 //SwChartDataSequence::Dispose(), release the relationship
2244 //here...
2245 if (m_pFormat && m_pFormat->HasWriterListeners())
2247 EndListeningAll();
2248 m_pFormat = nullptr;
2249 m_pTableCursor.reset(nullptr);
2253 // require listeners to release references to this object
2254 lang::EventObject aEvtObj( static_cast< chart2::data::XDataSequence * >(this) );
2255 std::unique_lock aGuard( GetChartMutex() );
2256 m_aModifyListeners.disposeAndClear( aGuard, aEvtObj );
2257 m_aEvtListeners.disposeAndClear( aGuard, aEvtObj );
2260 void SAL_CALL SwChartDataSequence::addEventListener(
2261 const uno::Reference< lang::XEventListener >& rxListener )
2263 std::unique_lock aGuard( GetChartMutex() );
2264 if (!m_bDisposed && rxListener.is())
2265 m_aEvtListeners.addInterface( aGuard, rxListener );
2268 void SAL_CALL SwChartDataSequence::removeEventListener(
2269 const uno::Reference< lang::XEventListener >& rxListener )
2271 std::unique_lock aGuard( GetChartMutex() );
2272 if (!m_bDisposed && rxListener.is())
2273 m_aEvtListeners.removeInterface( aGuard, rxListener );
2276 bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
2278 if (m_bDisposed)
2279 throw lang::DisposedException();
2281 // to be set if the last box of the data-sequence was removed here
2282 bool bNowEmpty = false;
2284 // if the implementation cursor gets affected (i.e. the box where it is located
2285 // in gets removed) we need to move it before that... (otherwise it does not need to change)
2287 const SwStartNode* pPointStartNode = m_pTableCursor->GetPoint()->GetNode().FindTableBoxStartNode();
2288 const SwStartNode* pMarkStartNode = m_pTableCursor->GetMark()->GetNode().FindTableBoxStartNode();
2290 if (!m_pTableCursor->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd()))
2292 bNowEmpty = true;
2294 else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd())
2296 sal_Int32 nPointRow = -1, nPointCol = -1;
2297 sal_Int32 nMarkRow = -1, nMarkCol = -1;
2298 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
2299 OUString aPointCellName( pTable->GetTableBox( pPointStartNode->GetIndex() )->GetName() );
2300 OUString aMarkCellName( pTable->GetTableBox( pMarkStartNode->GetIndex() )->GetName() );
2302 SwXTextTable::GetCellPosition( aPointCellName, nPointCol, nPointRow );
2303 SwXTextTable::GetCellPosition( aMarkCellName, nMarkCol, nMarkRow );
2304 OSL_ENSURE( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
2305 OSL_ENSURE( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
2307 // move vertical or horizontal?
2308 OSL_ENSURE( nPointRow == nMarkRow || nPointCol == nMarkCol,
2309 "row/col indices not matching" );
2310 OSL_ENSURE( nPointRow != nMarkRow || nPointCol != nMarkCol,
2311 "point and mark are identical" );
2312 bool bMoveVertical = (nPointCol == nMarkCol);
2313 bool bMoveHorizontal = (nPointRow == nMarkRow);
2315 // get movement direction
2316 bool bMoveLeft = false; // move left or right?
2317 bool bMoveUp = false; // move up or down?
2318 if (bMoveVertical)
2320 if (pPointStartNode == rBox.GetSttNd()) // move point?
2321 bMoveUp = nPointRow > nMarkRow;
2322 else // move mark
2323 bMoveUp = nMarkRow > nPointRow;
2325 else if (bMoveHorizontal)
2327 if (pPointStartNode == rBox.GetSttNd()) // move point?
2328 bMoveLeft = nPointCol > nMarkCol;
2329 else // move mark
2330 bMoveLeft = nMarkCol > nPointCol;
2332 else {
2333 OSL_FAIL( "neither vertical nor horizontal movement" );
2336 // get new box (position) to use...
2337 sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
2338 sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
2339 if (bMoveVertical)
2340 nRow += bMoveUp ? -1 : +1;
2341 if (bMoveHorizontal)
2342 nCol += bMoveLeft ? -1 : +1;
2343 const OUString aNewCellName = sw_GetCellName( nCol, nRow );
2344 SwTableBox* pNewBox = const_cast<SwTableBox*>(pTable->GetTableBox( aNewCellName ));
2346 if (pNewBox) // set new position (cell range) to use
2348 // This is how you get the first content node of a row:
2349 // First get a SwNodeIndex pointing to the node after SwStartNode of the box...
2350 SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
2351 // This can be a SwContentNode, but might also be a table or section node,
2352 // therefore call GoNext
2353 SwContentNode *pCNd = aIdx.GetNode().GetContentNode();
2354 if (!pCNd)
2355 pCNd = SwNodes::GoNext(&aIdx);
2356 // and then one can e.g. create a SwPosition:
2357 SwPosition aNewPos( *pCNd ); // new position to be used with cursor
2359 // if the mark is to be changed, make sure there is one
2360 if (pMarkStartNode == rBox.GetSttNd() && !m_pTableCursor->HasMark())
2361 m_pTableCursor->SetMark();
2363 // set cursor to new position
2364 SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
2365 m_pTableCursor->GetPoint() : m_pTableCursor->GetMark();
2366 if (pPos)
2368 *pPos = std::move(aNewPos);
2370 else {
2371 OSL_FAIL( "neither point nor mark available for change" );
2374 else {
2375 OSL_FAIL( "failed to get position" );
2379 return bNowEmpty;
2382 void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
2384 SwFrameFormat* pTableFormat = GetFrameFormat();
2385 if(pTableFormat)
2387 SwTable* pTable = SwTable::FindTable( pTableFormat );
2388 if(!pTable->IsTableComplex())
2390 FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
2396 * Extends the data-sequence by new cells added at the end of the direction
2397 * the data-sequence points to.
2398 * If the cells are already within the range of the sequence nothing needs
2399 * to be done.
2400 * If the cells are beyond the end of the sequence (are not adjacent to the
2401 * current last cell) nothing can be done. Only if the cells are adjacent to
2402 * the last cell they can be added.
2404 * @returns true if the data-sequence was changed.
2405 * @param bExtendCols - specifies if columns or rows are to be extended
2406 * @param nFirstNew - index of first new row/col to be included in data-sequence
2407 * @param nLastNew - index of last new row/col to be included in data-sequence
2409 void SwChartDataSequence::ExtendTo( bool bExtendCol,
2410 sal_Int32 nFirstNew, sal_Int32 nCount )
2412 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
2413 if (!pUnoTableCursor)
2414 return;
2416 const SwStartNode *pStartNd = nullptr;
2417 const SwTableBox *pStartBox = nullptr;
2418 const SwTableBox *pEndBox = nullptr;
2420 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
2421 OSL_ENSURE( !pTable->IsTableComplex(), "table too complex" );
2422 if (nCount < 1 || nFirstNew < 0 || pTable->IsTableComplex())
2423 return;
2425 // get range descriptor (cell range) for current data-sequence
2427 pStartNd = pUnoTableCursor->GetPoint()->GetNode().FindTableBoxStartNode();
2428 pEndBox = pTable->GetTableBox( pStartNd->GetIndex() );
2429 const OUString aEndBox( pEndBox->GetName() );
2431 pStartNd = pUnoTableCursor->GetMark()->GetNode().FindTableBoxStartNode();
2432 pStartBox = pTable->GetTableBox( pStartNd->GetIndex() );
2433 const OUString aStartBox( pStartBox->GetName() );
2435 SwRangeDescriptor aDesc;
2436 // note that cell range here takes the newly added rows/cols already into account
2437 OUString sDescrip = aStartBox + ":" + aEndBox;
2438 FillRangeDescriptor( aDesc, sDescrip );
2440 bool bChanged = false;
2441 OUString aNewStartCell;
2442 OUString aNewEndCell;
2443 if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
2445 // new column cells adjacent to the bottom of the
2446 // current data-sequence to be added...
2447 OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2448 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2449 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
2450 bChanged = true;
2452 else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
2454 // new column cells adjacent to the top of the
2455 // current data-sequence to be added...
2456 OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2457 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop - nCount);
2458 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2459 bChanged = true;
2461 else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
2463 // new row cells adjacent to the right of the
2464 // current data-sequence to be added...
2465 OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2466 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2467 aNewEndCell = sw_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
2468 bChanged = true;
2470 else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
2472 // new row cells adjacent to the left of the
2473 // current data-sequence to be added...
2474 OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2475 aNewStartCell = sw_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
2476 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2477 bChanged = true;
2480 if (bChanged)
2482 // move table cursor to new start and end of data-sequence
2483 const SwTableBox *pNewStartBox = pTable->GetTableBox( aNewStartCell );
2484 const SwTableBox *pNewEndBox = pTable->GetTableBox( aNewEndCell );
2485 pUnoTableCursor->SetMark();
2486 pUnoTableCursor->GetPoint()->Assign( *pNewEndBox->GetSttNd() );
2487 pUnoTableCursor->GetMark()->Assign( *pNewStartBox->GetSttNd() );
2488 pUnoTableCursor->Move( fnMoveForward, GoInNode );
2489 pUnoTableCursor->MakeBoxSels();
2493 SwChartLabeledDataSequence::SwChartLabeledDataSequence()
2495 m_bDisposed = false;
2498 SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
2502 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( )
2504 SolarMutexGuard aGuard;
2505 if (m_bDisposed)
2506 throw lang::DisposedException();
2507 return m_xData;
2510 void SwChartLabeledDataSequence::SetDataSequence(
2511 uno::Reference< chart2::data::XDataSequence >& rxDest,
2512 const uno::Reference< chart2::data::XDataSequence >& rxSource)
2514 uno::Reference< util::XModifyListener > xML(this);
2515 uno::Reference< lang::XEventListener > xEL(this);
2517 // stop listening to old data-sequence
2518 uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
2519 if (xMB.is())
2520 xMB->removeModifyListener( xML );
2521 uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
2522 if (xC.is())
2523 xC->removeEventListener( xEL );
2525 rxDest = rxSource;
2527 // start listening to new data-sequence
2528 xC.set( rxDest, uno::UNO_QUERY );
2529 if (xC.is())
2530 xC->addEventListener( xEL );
2531 xMB.set( rxDest, uno::UNO_QUERY );
2532 if (xMB.is())
2533 xMB->addModifyListener( xML );
2536 void SAL_CALL SwChartLabeledDataSequence::setValues(
2537 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2539 SolarMutexGuard aGuard;
2540 if (m_bDisposed)
2541 throw lang::DisposedException();
2543 if (m_xData != rxSequence)
2545 SetDataSequence( m_xData, rxSequence );
2546 // inform listeners of changes
2547 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2551 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( )
2553 SolarMutexGuard aGuard;
2554 if (m_bDisposed)
2555 throw lang::DisposedException();
2556 return m_xLabels;
2559 void SAL_CALL SwChartLabeledDataSequence::setLabel(
2560 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2562 SolarMutexGuard aGuard;
2563 if (m_bDisposed)
2564 throw lang::DisposedException();
2566 if (m_xLabels != rxSequence)
2568 SetDataSequence( m_xLabels, rxSequence );
2569 // inform listeners of changes
2570 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2574 uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( )
2576 SolarMutexGuard aGuard;
2577 if (m_bDisposed)
2578 throw lang::DisposedException();
2580 uno::Reference< util::XCloneable > xDataCloneable( m_xData, uno::UNO_QUERY );
2581 uno::Reference< util::XCloneable > xLabelsCloneable( m_xLabels, uno::UNO_QUERY );
2582 rtl::Reference<SwChartLabeledDataSequence > pRes = new SwChartLabeledDataSequence();
2583 if (xDataCloneable.is())
2585 uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
2586 pRes->setValues( xDataClone );
2589 if (xLabelsCloneable.is())
2591 uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
2592 pRes->setLabel( xLabelsClone );
2594 return pRes;
2597 OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( )
2599 return u"SwChartLabeledDataSequence"_ustr;
2602 sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
2603 const OUString& rServiceName )
2605 return cppu::supportsService(this, rServiceName);
2608 uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( )
2610 return { u"com.sun.star.chart2.data.LabeledDataSequence"_ustr };
2613 void SAL_CALL SwChartLabeledDataSequence::disposing(
2614 const lang::EventObject& rSource )
2616 std::unique_lock aGuard( GetChartMutex() );
2617 uno::Reference< uno::XInterface > xRef( rSource.Source );
2618 if (xRef == m_xData)
2619 m_xData.clear();
2620 if (xRef == m_xLabels)
2621 m_xLabels.clear();
2622 if (!m_xData.is() && !m_xLabels.is())
2624 aGuard.unlock();
2625 dispose();
2629 void SAL_CALL SwChartLabeledDataSequence::modified(
2630 const lang::EventObject& rEvent )
2632 if (rEvent.Source == m_xData || rEvent.Source == m_xLabels)
2634 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2638 void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
2639 const uno::Reference< util::XModifyListener >& rxListener )
2641 std::unique_lock aGuard( GetChartMutex() );
2642 if (!m_bDisposed && rxListener.is())
2643 m_aModifyListeners.addInterface( aGuard, rxListener );
2646 void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
2647 const uno::Reference< util::XModifyListener >& rxListener )
2649 std::unique_lock aGuard( GetChartMutex() );
2650 if (!m_bDisposed && rxListener.is())
2651 m_aModifyListeners.removeInterface( aGuard, rxListener );
2654 void SAL_CALL SwChartLabeledDataSequence::dispose( )
2656 std::unique_lock aGuard( GetChartMutex() );
2657 if (m_bDisposed)
2658 return;
2659 m_bDisposed = true;
2661 // require listeners to release references to this object
2662 lang::EventObject aEvtObj( static_cast< chart2::data::XLabeledDataSequence * >(this) );
2663 m_aModifyListeners.disposeAndClear( aGuard, aEvtObj );
2664 m_aEventListeners.disposeAndClear( aGuard, aEvtObj );
2667 void SAL_CALL SwChartLabeledDataSequence::addEventListener(
2668 const uno::Reference< lang::XEventListener >& rxListener )
2670 std::unique_lock aGuard( GetChartMutex() );
2671 if (!m_bDisposed && rxListener.is())
2672 m_aEventListeners.addInterface( aGuard, rxListener );
2675 void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
2676 const uno::Reference< lang::XEventListener >& rxListener )
2678 std::unique_lock aGuard( GetChartMutex() );
2679 if (!m_bDisposed && rxListener.is())
2680 m_aEventListeners.removeInterface( aGuard, rxListener );
2683 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */