1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <algorithm>
21 #include <string_view>
22
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 <cppuhelper/supportsservice.hxx>
28 #include <osl/mutex.hxx>
29 #include <vcl/svapp.hxx>
30
31 #include "XMLRangeHelper.hxx"
32 #include <unochart.hxx>
33 #include <swtable.hxx>
34 #include <unoprnms.hxx>
35 #include <unomap.hxx>
36 #include <unocrsr.hxx>
37 #include <unotbl.hxx>
38 #include <doc.hxx>
39 #include <IDocumentChartDataProviderAccess.hxx>
40 #include <frmfmt.hxx>
41 #include <ndole.hxx>
42 #include <swtypes.hxx>
43 #include <strings.hrc>
44 #include <comphelper/servicehelper.hxx>
45 #include <comphelper/string.hxx>
46 #include <svl/itemprop.hxx>
47
48 using namespace ::com::sun::star;
49
DoUpdateAllCharts(SwDoc * pDoc)50 void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
51 {
52 if (!pDoc)
53 return;
54
55 SwOLENode *pONd;
56 SwStartNode *pStNd;
57 SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
58 while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
59 {
60 ++aIdx;
61 if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
62 pONd->GetOLEObj().GetObject().IsChart() )
63 {
64 // Load the object and set modified
65
66 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
67 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
68 {
69 try
70 {
71 uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
72 xModif->setModified( true );
73 }
74 catch ( uno::Exception& )
75 {
76 }
77
78 }
79 }
80 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
81 }
82 }
83
SwChartLockController_Helper(SwDoc * pDocument)84 SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
85 m_pDoc( pDocument )
86 , m_bIsLocked( false )
87 {
88 m_aUnlockTimer.SetTimeout( 1500 );
89 m_aUnlockTimer.SetInvokeHandler( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
90 m_aUnlockTimer.SetDebugName( "sw::SwChartLockController_Helper aUnlockTimer" );
91 }
92
~SwChartLockController_Helper()93 SwChartLockController_Helper::~SwChartLockController_Helper() COVERITY_NOEXCEPT_FALSE
94 {
95 if (m_pDoc) // still connected?
96 Disconnect();
97 }
98
StartOrContinueLocking()99 void SwChartLockController_Helper::StartOrContinueLocking()
100 {
101 if (!m_bIsLocked)
102 LockAllCharts();
103 m_aUnlockTimer.Start(); // start or continue time of locking
104 }
105
Disconnect()106 void SwChartLockController_Helper::Disconnect()
107 {
108 m_aUnlockTimer.Stop();
109 UnlockAllCharts();
110 m_pDoc = nullptr;
111 }
112
LockUnlockAllCharts(bool bLock)113 void SwChartLockController_Helper::LockUnlockAllCharts( bool bLock )
114 {
115 if (!m_pDoc)
116 return;
117
118 uno::Reference< frame::XModel > xRes;
119 SwOLENode *pONd;
120 SwStartNode *pStNd;
121 SwNodeIndex aIdx( *m_pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
122 while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
123 {
124 ++aIdx;
125 if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
126 !pONd->GetChartTableName().isEmpty() /* is chart object? */)
127 {
128 uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
129 if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
130 {
131 xRes.set( xIP->getComponent(), uno::UNO_QUERY );
132 if (xRes.is())
133 {
134 if (bLock)
135 xRes->lockControllers();
136 else
137 xRes->unlockControllers();
138 }
139 }
140 }
141 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
142 }
143
144 m_bIsLocked = bLock;
145 }
146
IMPL_LINK_NOARG(SwChartLockController_Helper,DoUnlockAllCharts,Timer *,void)147 IMPL_LINK_NOARG( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, void )
148 {
149 UnlockAllCharts();
150 }
151
GetChartMutex()152 static osl::Mutex & GetChartMutex()
153 {
154 static osl::Mutex aMutex;
155 return aMutex;
156 }
157
LaunchModifiedEvent(::comphelper::OInterfaceContainerHelper2 & rICH,const uno::Reference<uno::XInterface> & rxI)158 static void LaunchModifiedEvent(
159 ::comphelper::OInterfaceContainerHelper2 &rICH,
160 const uno::Reference< uno::XInterface > &rxI )
161 {
162 lang::EventObject aEvtObj( rxI );
163 comphelper::OInterfaceIteratorHelper2 aIt( rICH );
164 while (aIt.hasMoreElements())
165 {
166 uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY );
167 if (xRef.is())
168 xRef->modified( aEvtObj );
169 }
170 }
171
172 /**
173 * rCellRangeName needs to be of one of the following formats:
174 * - e.g. "A2:E5" or
175 * - e.g. "Table1.A2:E5"
176 */
FillRangeDescriptor(SwRangeDescriptor & rDesc,const OUString & rCellRangeName)177 bool FillRangeDescriptor(
178 SwRangeDescriptor &rDesc,
179 const OUString &rCellRangeName )
180 {
181 sal_Int32 nToken = -1 == rCellRangeName.indexOf('.') ? 0 : 1;
182 OUString aCellRangeNoTableName( rCellRangeName.getToken( nToken, '.' ) );
183 OUString aTLName( aCellRangeNoTableName.getToken(0, ':') ); // name of top left cell
184 OUString aBRName( aCellRangeNoTableName.getToken(1, ':') ); // name of bottom right cell
185 if(aTLName.isEmpty() || aBRName.isEmpty())
186 return false;
187
188 rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
189 SwXTextTable::GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop );
190 SwXTextTable::GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
191 rDesc.Normalize();
192 OSL_ENSURE( rDesc.nTop != -1 &&
193 rDesc.nLeft != -1 &&
194 rDesc.nBottom != -1 &&
195 rDesc.nRight != -1,
196 "failed to get range descriptor" );
197 OSL_ENSURE( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight,
198 "invalid range descriptor");
199 return true;
200 }
201
GetCellRangeName(SwFrameFormat & rTableFormat,SwUnoCursor & rTableCursor)202 static OUString GetCellRangeName( SwFrameFormat &rTableFormat, SwUnoCursor &rTableCursor )
203 {
204 OUString aRes;
205
206 //!! see also SwXTextTableCursor::getRangeName
207
208 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&rTableCursor);
209 if (!pUnoTableCursor)
210 return OUString();
211 pUnoTableCursor->MakeBoxSels();
212
213 const SwStartNode* pStart;
214 const SwTableBox* pStartBox = nullptr;
215 const SwTableBox* pEndBox = nullptr;
216
217 pStart = pUnoTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
218 if (pStart)
219 {
220 const SwTable* pTable = SwTable::FindTable( &rTableFormat );
221 pEndBox = pTable->GetTableBox( pStart->GetIndex());
222 aRes = pEndBox->GetName();
223
224 if(pUnoTableCursor->HasMark())
225 {
226 pStart = pUnoTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
227 pStartBox = pTable->GetTableBox( pStart->GetIndex());
228 }
229 OSL_ENSURE( pStartBox, "start box not found" );
230 OSL_ENSURE( pEndBox, "end box not found" );
231
232 // need to switch start and end?
233 if (*pUnoTableCursor->GetPoint() < *pUnoTableCursor->GetMark())
234 {
235 const SwTableBox* pTmpBox = pStartBox;
236 pStartBox = pEndBox;
237 pEndBox = pTmpBox;
238 }
239
240 if (!pStartBox)
241 return aRes;
242
243 aRes = pStartBox->GetName() + ":";
244 if (pEndBox)
245 aRes += pEndBox->GetName();
246 else
247 aRes += pStartBox->GetName();
248 }
249
250 return aRes;
251 }
252
GetRangeRepFromTableAndCells(std::u16string_view rTableName,std::u16string_view rStartCell,std::u16string_view rEndCell,bool bForceEndCellName)253 static OUString GetRangeRepFromTableAndCells( std::u16string_view rTableName,
254 std::u16string_view rStartCell, std::u16string_view rEndCell,
255 bool bForceEndCellName )
256 {
257 OSL_ENSURE( !rTableName.empty(), "table name missing" );
258 OSL_ENSURE( !rStartCell.empty(), "cell name missing" );
259 OUString aRes = OUString::Concat(rTableName) + "." + rStartCell;
260
261 if (!rEndCell.empty())
262 {
263 aRes += OUString::Concat(":") + rEndCell;
264 }
265 else if (bForceEndCellName)
266 {
267 aRes += OUString::Concat(":") + rStartCell;
268 }
269
270 return aRes;
271 }
272
GetTableAndCellsFromRangeRep(const OUString & rRangeRepresentation,OUString & rTableName,OUString & rStartCell,OUString & rEndCell,bool bSortStartEndCells=true)273 static bool GetTableAndCellsFromRangeRep(
274 const OUString &rRangeRepresentation,
275 OUString &rTableName,
276 OUString &rStartCell,
277 OUString &rEndCell,
278 bool bSortStartEndCells = true )
279 {
280 // parse range representation for table name and cell/range names
281 // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
282 OUString aTableName; // table name
283 OUString aStartCell; // name of top left cell
284 OUString aEndCell; // name of bottom right cell
285 sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' );
286 if (nIdx >= 0)
287 {
288 aTableName = rRangeRepresentation.copy( 0, nIdx );
289 OUString aRange = rRangeRepresentation.copy( nIdx + 1 ); // cell range
290 sal_Int32 nPos = aRange.indexOf( ':' );
291 if (nPos >= 0) // a cell-range like "Table1.A2:D4"
292 {
293 aStartCell = aRange.copy( 0, nPos );
294 aEndCell = aRange.copy( nPos + 1 );
295
296 // need to switch start and end cell ?
297 // (does not check for normalization here)
298 if (bSortStartEndCells && 1 == sw_CompareCellsByColFirst( aStartCell, aEndCell ))
299 {
300 OUString aTmp( aStartCell );
301 aStartCell = aEndCell;
302 aEndCell = aTmp;
303 }
304 }
305 else // a single cell like in "Table1.B3"
306 {
307 aStartCell = aEndCell = aRange;
308 }
309 }
310
311 bool bSuccess = !aTableName.isEmpty() &&
312 !aStartCell.isEmpty() && !aEndCell.isEmpty();
313 if (bSuccess)
314 {
315 rTableName = aTableName;
316 rStartCell = aStartCell;
317 rEndCell = aEndCell;
318 }
319 return bSuccess;
320 }
321
GetTableByName(const SwDoc & rDoc,std::u16string_view rTableName,SwFrameFormat ** ppTableFormat,SwTable ** ppTable)322 static void GetTableByName( const SwDoc &rDoc, std::u16string_view rTableName,
323 SwFrameFormat **ppTableFormat, SwTable **ppTable)
324 {
325 SwFrameFormat *pTableFormat = nullptr;
326
327 // find frame format of table
328 //! see SwXTextTables::getByName
329 const size_t nCount = rDoc.GetTableFrameFormatCount(true);
330 for (size_t i = 0; i < nCount && !pTableFormat; ++i)
331 {
332 SwFrameFormat& rTableFormat = rDoc.GetTableFrameFormat(i, true);
333 if(rTableName == rTableFormat.GetName())
334 pTableFormat = &rTableFormat;
335 }
336
337 if (ppTableFormat)
338 *ppTableFormat = pTableFormat;
339
340 if (ppTable)
341 *ppTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
342 }
343
GetFormatAndCreateCursorFromRangeRep(const SwDoc * pDoc,const OUString & rRangeRepresentation,SwFrameFormat ** ppTableFormat,std::shared_ptr<SwUnoCursor> & rpUnoCursor)344 static void GetFormatAndCreateCursorFromRangeRep(
345 const SwDoc *pDoc,
346 const OUString &rRangeRepresentation, // must be a single range (i.e. so called sub-range)
347 SwFrameFormat **ppTableFormat, // will be set to the table format of the table used in the range representation
348 std::shared_ptr<SwUnoCursor>& rpUnoCursor ) // will be set to cursor spanning the cell range (cursor will be created!)
349 {
350 OUString aTableName; // table name
351 OUString aStartCell; // name of top left cell
352 OUString aEndCell; // name of bottom right cell
353 bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
354 aTableName, aStartCell, aEndCell );
355
356 if (!bNamesFound)
357 {
358 if (ppTableFormat)
359 *ppTableFormat = nullptr;
360 rpUnoCursor.reset();
361 }
362 else
363 {
364 SwFrameFormat *pTableFormat = nullptr;
365
366 // is the correct table format already provided?
367 if (*ppTableFormat != nullptr && (*ppTableFormat)->GetName() == aTableName)
368 pTableFormat = *ppTableFormat;
369 else
370 GetTableByName( *pDoc, aTableName, &pTableFormat, nullptr );
371
372 *ppTableFormat = pTableFormat;
373
374 rpUnoCursor.reset(); // default result in case of failure
375
376 SwTable *pTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
377 // create new SwUnoCursor spanning the specified range
378 //! see also SwXTextTable::GetRangeByName
379 // #i80314#
380 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
381 const SwTableBox* pTLBox =
382 pTable ? pTable->GetTableBox( aStartCell, true ) : nullptr;
383 if(pTLBox)
384 {
385 const SwStartNode* pSttNd = pTLBox->GetSttNd();
386 SwPosition aPos(*pSttNd);
387
388 // set cursor to top left box of range
389 auto pUnoCursor = pTableFormat->GetDoc()->CreateUnoCursor(aPos, true);
390 pUnoCursor->Move( fnMoveForward, GoInNode );
391 pUnoCursor->SetRemainInSection( false );
392
393 // #i80314#
394 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
395 const SwTableBox* pBRBox = pTable->GetTableBox( aEndCell, true );
396 if(pBRBox)
397 {
398 pUnoCursor->SetMark();
399 pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
400 pUnoCursor->Move( fnMoveForward, GoInNode );
401 SwUnoTableCursor& rCursor =
402 dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
403 // HACK: remove pending actions for old style tables
404 UnoActionRemoveContext aRemoveContext(rCursor);
405 rCursor.MakeBoxSels();
406 rpUnoCursor = pUnoCursor;
407 }
408 }
409 }
410 }
411
GetSubranges(const OUString & rRangeRepresentation,uno::Sequence<OUString> & rSubRanges,bool bNormalize)412 static bool GetSubranges( const OUString &rRangeRepresentation,
413 uno::Sequence< OUString > &rSubRanges, bool bNormalize )
414 {
415 bool bRes = true;
416 const sal_Int32 nLen = comphelper::string::getTokenCount(rRangeRepresentation, ';');
417 uno::Sequence< OUString > aRanges( nLen );
418
419 sal_Int32 nCnt = 0;
420 if (nLen != 0)
421 {
422 OUString *pRanges = aRanges.getArray();
423 OUString aFirstTable;
424 sal_Int32 nPos = 0;
425 for( sal_Int32 i = 0; i < nLen && bRes; ++i )
426 {
427 const OUString aRange( rRangeRepresentation.getToken( 0, ';', nPos ) );
428 if (!aRange.isEmpty())
429 {
430 pRanges[nCnt] = aRange;
431
432 OUString aTableName, aStartCell, aEndCell;
433 if (!GetTableAndCellsFromRangeRep( aRange,
434 aTableName, aStartCell, aEndCell ))
435 bRes = false;
436
437 if (bNormalize)
438 {
439 sw_NormalizeRange( aStartCell, aEndCell );
440 pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
441 aStartCell, aEndCell, true );
442 }
443
444 // make sure to use only a single table
445 if (nCnt == 0)
446 aFirstTable = aTableName;
447 else
448 if (aFirstTable != aTableName) bRes = false;
449
450 ++nCnt;
451 }
452 }
453 }
454 aRanges.realloc( nCnt );
455
456 rSubRanges = aRanges;
457 return bRes;
458 }
459
SortSubranges(uno::Sequence<OUString> & rSubRanges,bool bCmpByColumn)460 static void SortSubranges( uno::Sequence< OUString > &rSubRanges, bool bCmpByColumn )
461 {
462 sal_Int32 nLen = rSubRanges.getLength();
463 OUString *pSubRanges = rSubRanges.getArray();
464
465 OUString aSmallestTableName;
466 OUString aSmallestStartCell;
467 OUString aSmallestEndCell;
468
469 for (sal_Int32 i = 0; i < nLen; ++i)
470 {
471 sal_Int32 nIdxOfSmallest = i;
472 GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
473 aSmallestTableName, aSmallestStartCell, aSmallestEndCell );
474 if (aSmallestEndCell.isEmpty())
475 aSmallestEndCell = aSmallestStartCell;
476
477 for (sal_Int32 k = i+1; k < nLen; ++k)
478 {
479 // get cell names for sub range
480 OUString aTableName;
481 OUString aStartCell;
482 OUString aEndCell;
483 GetTableAndCellsFromRangeRep( pSubRanges[k],
484 aTableName, aStartCell, aEndCell );
485 if (aEndCell.isEmpty())
486 aEndCell = aStartCell;
487
488 // compare cell ranges ( is the new one smaller? )
489 if (-1 == sw_CompareCellRanges( aStartCell, aEndCell,
490 aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
491 {
492 nIdxOfSmallest = k;
493 aSmallestTableName = aTableName;
494 aSmallestStartCell = aStartCell;
495 aSmallestEndCell = aEndCell;
496 }
497 }
498
499 // move smallest element to the start of the not sorted area
500 const OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
501 pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
502 pSubRanges[ i ] = aTmp;
503 }
504 }
505
SwChartDataProvider(const SwDoc & rSwDoc)506 SwChartDataProvider::SwChartDataProvider( const SwDoc& rSwDoc ) :
507 m_aEventListeners( GetChartMutex() ),
508 m_pDoc( &rSwDoc )
509 {
510 m_bDisposed = false;
511 }
512
~SwChartDataProvider()513 SwChartDataProvider::~SwChartDataProvider()
514 {
515 }
516
Impl_createDataSource(const uno::Sequence<beans::PropertyValue> & rArguments,bool bTestOnly)517 uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
518 const uno::Sequence< beans::PropertyValue >& rArguments, bool bTestOnly )
519 {
520 SolarMutexGuard aGuard;
521 if (m_bDisposed)
522 throw lang::DisposedException();
523
524 uno::Reference< chart2::data::XDataSource > xRes;
525
526 if (!m_pDoc)
527 throw uno::RuntimeException("Not connected to a document.");
528
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
535
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)
542 {
543 if ( rArg.Name == "DataRowSource" )
544 {
545 chart::ChartDataRowSource eSource;
546 if (!(rArg.Value >>= eSource))
547 {
548 sal_Int32 nTmp = 0;
549 if (!(rArg.Value >>= nTmp))
550 throw lang::IllegalArgumentException();
551 eSource = static_cast< chart::ChartDataRowSource >( nTmp );
552 }
553 bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
554 }
555 else if ( rArg.Name == "FirstCellAsLabel" )
556 {
557 if (!(rArg.Value >>= bFirstIsLabel))
558 throw lang::IllegalArgumentException();
559 }
560 else if ( rArg.Name == "CellRangeRepresentation" )
561 {
562 if (!(rArg.Value >>= aRangeRepresentation))
563 throw lang::IllegalArgumentException();
564 }
565 else if ( rArg.Name == "SequenceMapping" )
566 {
567 if (!(rArg.Value >>= aSequenceMapping))
568 throw lang::IllegalArgumentException();
569 }
570 else if ( rArg.Name == "ChartOleObjectName" )
571 {
572 if (!(rArg.Value >>= aChartOleObjectName))
573 throw lang::IllegalArgumentException();
574 }
575 }
576
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 );
580
581 if (!bOk && m_pDoc && !aChartOleObjectName.isEmpty() )
582 {
583 //try to correct the range here
584 //work around wrong writer ranges ( see Issue 58464 )
585 OUString aChartTableName;
586
587 const SwNodes& rNodes = m_pDoc->GetNodes();
588 for( sal_uLong nN = rNodes.Count(); nN--; )
589 {
590 SwNodePtr 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() )
598 {
599 aChartTableName = pOleNode->GetChartTableName();
600 break;
601 }
602 }
603
604 if( !aChartTableName.isEmpty() )
605 {
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
612
613 aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left
614 FillRangeDescriptor( aDesc, aRangeRepresentation );
615 aDesc.Normalize();
616
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
619
620 aDesc.nTop -= 1;
621 aDesc.nBottom -= 1;
622
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 );
628 }
629 }
630 if (!bOk) // different tables used, or incorrect range specifiers
631 throw lang::IllegalArgumentException();
632
633 SortSubranges( aSubRanges, bDtaSrcIsColumns );
634
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 );
640
641 if (!pTableFormat || !pUnoCursor)
642 throw lang::IllegalArgumentException();
643
644 SwTable* pTable = SwTable::FindTable(pTableFormat);
645 if (pTable->IsTableComplex())
646 return xRes; // we can't handle this thus returning an empty references
647
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 = pTable->GetTabLines().front()->GetTabBoxes().size();
652 std::vector<std::vector<char>> aMap(nRows);
653 for (sal_Int32 i = 0; i < nRows; ++i)
654 aMap[i].resize(nCols);
655
656 // iterate over subranges and mark used cells in above map
657 //!! by proceeding this way we automatically get rid of
658 //!! multiple listed or overlapping cell ranges which should
659 //!! just be ignored silently
660 for (const OUString& rSubRange : std::as_const(aSubRanges))
661 {
662 OUString aTableName, aStartCell, aEndCell;
663 bool bOk2 = GetTableAndCellsFromRangeRep(
664 rSubRange, aTableName, aStartCell, aEndCell );
665 OSL_ENSURE(bOk2, "failed to get table and start/end cells");
666
667 sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
668 SwXTextTable::GetCellPosition(aStartCell, nStartCol, nStartRow);
669 SwXTextTable::GetCellPosition(aEndCell, nEndCol, nEndRow);
670 OSL_ENSURE( nStartRow <= nEndRow && nStartCol <= nEndCol,
671 "cell range not normalized");
672
673 // test if the ranges span more than the available cells
674 if( nStartRow < 0 || nEndRow >= nRows ||
675 nStartCol < 0 || nEndCol >= nCols )
676 {
677 throw lang::IllegalArgumentException();
678 }
679 for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1)
680 {
681 for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2)
682 aMap[k1][k2] = 'x';
683 }
684 }
685
686 // find label and data sequences to use
687
688 sal_Int32 oi; // outer index (slower changing index)
689 sal_Int32 ii; // inner index (faster changing index)
690 sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
691 sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
692 std::vector<sal_Int32> aLabelIdx(oiEnd);
693 std::vector<sal_Int32> aDataStartIdx(oiEnd);
694 std::vector<sal_Int32> aDataLen(oiEnd);
695 for (oi = 0; oi < oiEnd; ++oi)
696 {
697 aLabelIdx[oi] = -1;
698 aDataStartIdx[oi] = -1;
699 aDataLen[oi] = 0;
700 }
701
702 for (oi = 0; oi < oiEnd; ++oi)
703 {
704 ii = 0;
705 while (ii < iiEnd)
706 {
707 char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
708
709 // label should be used but is not yet found?
710 if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
711 {
712 aLabelIdx[oi] = ii;
713 rChar = 'L'; // setting a different char for labels here
714 // makes the test for the data sequence below
715 // easier
716 }
717
718 // find data sequence
719 if (rChar == 'x' && aDataStartIdx[oi] == -1)
720 {
721 aDataStartIdx[oi] = ii;
722
723 // get length of data sequence
724 sal_Int32 nL = 0;
725 while (ii< iiEnd && 'x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
726 {
727 ++nL; ++ii;
728 }
729 aDataLen[oi] = nL;
730
731 // check that there is no other separate sequence of data
732 // to be found because that is not supported
733 while (ii < iiEnd)
734 {
735 if ('x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
736 throw lang::IllegalArgumentException();
737 ++ii;
738 }
739 }
740 else
741 ++ii;
742 }
743 }
744
745 // make some other consistency checks while calculating
746 // the number of XLabeledDataSequence to build:
747 // - labels should always be used or not at all
748 // - the data sequences should have equal non-zero length
749 sal_Int32 nNumLDS = 0;
750 if (oiEnd > 0)
751 {
752 sal_Int32 nFirstSeqLen = 0;
753 sal_Int32 nFirstSeqLabelIdx = -1;
754 bool bFirstFound = false;
755 for (oi = 0; oi < oiEnd; ++oi)
756 {
757 // row/col used at all?
758 if (aDataStartIdx[oi] != -1 &&
759 (!bFirstIsLabel || aLabelIdx[oi] != -1))
760 {
761 ++nNumLDS;
762 if (!bFirstFound)
763 {
764 nFirstSeqLen = aDataLen[oi];
765 nFirstSeqLabelIdx = aLabelIdx[oi];
766 bFirstFound = true;
767 }
768 else
769 {
770 if (nFirstSeqLen != aDataLen[oi] ||
771 nFirstSeqLabelIdx != aLabelIdx[oi])
772 throw lang::IllegalArgumentException();
773 }
774 }
775 }
776 }
777 if (nNumLDS == 0)
778 throw lang::IllegalArgumentException();
779
780 // now we should have all necessary data to build a proper DataSource
781 // thus if we came this far there should be no further problem
782 if (bTestOnly)
783 return xRes; // have createDataSourcePossible return true
784
785 // create data source from found label and data sequences
786 uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aLabelSeqs(nNumLDS);
787 uno::Reference<chart2::data::XDataSequence>* pLabelSeqs = aLabelSeqs.getArray();
788 uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aDataSeqs(nNumLDS);
789 uno::Reference<chart2::data::XDataSequence>* pDataSeqs = aDataSeqs.getArray();
790 sal_Int32 nSeqsIdx = 0;
791 for (oi = 0; oi < oiEnd; ++oi)
792 {
793 // row/col not used? (see if-statement above where nNumLDS was counted)
794 if (!(aDataStartIdx[oi] != -1 &&
795 (!bFirstIsLabel || aLabelIdx[oi] != -1)))
796 continue;
797
798 // get cell ranges for label and data
799
800 SwRangeDescriptor aLabelDesc;
801 SwRangeDescriptor aDataDesc;
802 if (bDtaSrcIsColumns) // use columns
803 {
804 aLabelDesc.nTop = aLabelIdx[oi];
805 aLabelDesc.nLeft = oi;
806 aLabelDesc.nBottom = aLabelDesc.nTop;
807 aLabelDesc.nRight = oi;
808
809 aDataDesc.nTop = aDataStartIdx[oi];
810 aDataDesc.nLeft = oi;
811 aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1;
812 aDataDesc.nRight = oi;
813 }
814 else // use rows
815 {
816 aLabelDesc.nTop = oi;
817 aLabelDesc.nLeft = aLabelIdx[oi];
818 aLabelDesc.nBottom = oi;
819 aLabelDesc.nRight = aLabelDesc.nLeft;
820
821 aDataDesc.nTop = oi;
822 aDataDesc.nLeft = aDataStartIdx[oi];
823 aDataDesc.nBottom = oi;
824 aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1;
825 }
826 const OUString aBaseName = pTableFormat->GetName() + ".";
827
828 OUString aLabelRange;
829 if (aLabelIdx[oi] != -1)
830 {
831 aLabelRange = aBaseName
832 + sw_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop )
833 + ":" + sw_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
834 }
835
836 OUString aDataRange = aBaseName
837 + sw_GetCellName( aDataDesc.nLeft, aDataDesc.nTop )
838 + ":" + sw_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
839
840 // get cursors spanning the cell ranges for label and data
841 std::shared_ptr<SwUnoCursor> pLabelUnoCursor;
842 std::shared_ptr<SwUnoCursor> pDataUnoCursor;
843 GetFormatAndCreateCursorFromRangeRep(m_pDoc, aLabelRange, &pTableFormat, pLabelUnoCursor);
844 GetFormatAndCreateCursorFromRangeRep(m_pDoc, aDataRange, &pTableFormat, pDataUnoCursor);
845
846 // create XDataSequence's from cursors
847 if (pLabelUnoCursor)
848 pLabelSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pLabelUnoCursor);
849 OSL_ENSURE(pDataUnoCursor, "pointer to data sequence missing");
850 if (pDataUnoCursor)
851 pDataSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pDataUnoCursor);
852 if (pLabelUnoCursor || pDataUnoCursor)
853 ++nSeqsIdx;
854 }
855 OSL_ENSURE(nSeqsIdx == nNumLDS, "mismatch between sequence size and num,ber of entries");
856
857 // build data source from data and label sequences
858 uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aLDS(nNumLDS);
859 uno::Reference<chart2::data::XLabeledDataSequence>* pLDS = aLDS.getArray();
860 for (sal_Int32 i = 0; i < nNumLDS; ++i)
861 {
862 rtl::Reference<SwChartLabeledDataSequence> pLabeledDtaSeq = new SwChartLabeledDataSequence;
863 pLabeledDtaSeq->setLabel(pLabelSeqs[i]);
864 pLabeledDtaSeq->setValues(pDataSeqs[i]);
865 pLDS[i] = pLabeledDtaSeq;
866 }
867
868 // apply 'SequenceMapping' if it was provided
869 if (aSequenceMapping.hasElements())
870 {
871 uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aOld_LDS(aLDS);
872 uno::Reference<chart2::data::XLabeledDataSequence>* pOld_LDS = aOld_LDS.getArray();
873
874 sal_Int32 nNewCnt = 0;
875 for (sal_Int32 nIdx : aSequenceMapping)
876 {
877 // check that index to be used is valid
878 // and has not yet been used
879 if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
880 {
881 pLDS[nNewCnt++] = pOld_LDS[nIdx];
882
883 // mark index as being used already (avoids duplicate entries)
884 pOld_LDS[nIdx].clear();
885 }
886 }
887 // add not yet used 'old' sequences to new one
888 for (sal_Int32 i = 0; i < nNumLDS; ++i)
889 {
890 if (pOld_LDS[i].is())
891 pLDS[nNewCnt++] = pOld_LDS[i];
892 }
893 OSL_ENSURE(nNewCnt == nNumLDS, "unexpected size of resulting sequence");
894 }
895
896 xRes = new SwChartDataSource(aLDS);
897 return xRes;
898 }
899
createDataSourcePossible(const uno::Sequence<beans::PropertyValue> & rArguments)900 sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
901 const uno::Sequence< beans::PropertyValue >& rArguments )
902 {
903 SolarMutexGuard aGuard;
904
905 bool bPossible = true;
906 try
907 {
908 Impl_createDataSource( rArguments, true );
909 }
910 catch (lang::IllegalArgumentException &)
911 {
912 bPossible = false;
913 }
914
915 return bPossible;
916 }
917
createDataSource(const uno::Sequence<beans::PropertyValue> & rArguments)918 uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
919 const uno::Sequence< beans::PropertyValue >& rArguments )
920 {
921 SolarMutexGuard aGuard;
922 return Impl_createDataSource( rArguments );
923 }
924
925 /**
926 * Fix for #i79009
927 * we need to return a property that has the same value as the property
928 * 'CellRangeRepresentation' but for all rows which are increased by one.
929 * E.g. Table1.A1:D5 -> Table1,A2:D6
930 * Since the problem is only for old charts which did not support multiple
931 * we do not need to provide that property/string if the 'CellRangeRepresentation'
932 * contains multiple ranges.
933 */
GetBrokenCellRangeForExport(const OUString & rCellRangeRepresentation)934 OUString SwChartDataProvider::GetBrokenCellRangeForExport(
935 const OUString &rCellRangeRepresentation )
936 {
937 // check that we do not have multiple ranges
938 if (-1 == rCellRangeRepresentation.indexOf( ';' ))
939 {
940 // get current cell and table names
941 OUString aTableName, aStartCell, aEndCell;
942 GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
943 aTableName, aStartCell, aEndCell, false );
944 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
945 SwXTextTable::GetCellPosition( aStartCell, nStartCol, nStartRow );
946 SwXTextTable::GetCellPosition( aEndCell, nEndCol, nEndRow );
947
948 // get new cell names
949 ++nStartRow;
950 ++nEndRow;
951 aStartCell = sw_GetCellName( nStartCol, nStartRow );
952 aEndCell = sw_GetCellName( nEndCol, nEndRow );
953
954 return GetRangeRepFromTableAndCells( aTableName,
955 aStartCell, aEndCell, false );
956 }
957
958 return OUString();
959 }
960
detectArguments(const uno::Reference<chart2::data::XDataSource> & xDataSource)961 uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
962 const uno::Reference< chart2::data::XDataSource >& xDataSource )
963 {
964 SolarMutexGuard aGuard;
965 if (m_bDisposed)
966 throw lang::DisposedException();
967
968 uno::Sequence< beans::PropertyValue > aResult;
969 if (!xDataSource.is())
970 return aResult;
971
972 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
973 const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
974 sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
975
976 if (nNumDS_LDS == 0)
977 {
978 OSL_FAIL( "XLabeledDataSequence in data source contains 0 entries" );
979 return aResult;
980 }
981
982 SwFrameFormat *pTableFormat = nullptr;
983 SwTable *pTable = nullptr;
984 OUString aTableName;
985 sal_Int32 nTableRows = 0;
986 sal_Int32 nTableCols = 0;
987
988 // data used to build 'CellRangeRepresentation' from later on
989 std::vector< std::vector< char > > aMap;
990
991 uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
992 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
993
994 OUString aCellRanges;
995 sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither
996 sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have
997 // the expected size of 1 (i.e. if FirstCellAsLabel can
998 // be determined)
999 // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
1000 // -2: neither/failed
1001 for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1)
1002 {
1003 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
1004 if( !xLabeledDataSequence.is() )
1005 {
1006 OSL_FAIL("got NULL for XLabeledDataSequence from Data source");
1007 continue;
1008 }
1009 const uno::Reference< chart2::data::XDataSequence > xCurLabel = xLabeledDataSequence->getLabel();
1010 const uno::Reference< chart2::data::XDataSequence > xCurValues = xLabeledDataSequence->getValues();
1011
1012 // get sequence lengths for label and values.
1013 // (0 length is Ok)
1014 sal_Int32 nCurLabelSeqLen = -1;
1015 sal_Int32 nCurValuesSeqLen = -1;
1016 if (xCurLabel.is())
1017 nCurLabelSeqLen = xCurLabel->getData().getLength();
1018 if (xCurValues.is())
1019 nCurValuesSeqLen = xCurValues->getData().getLength();
1020
1021 // check for consistent use of 'first cell as label'
1022 if (nLabelSeqLen == -1) // set initial value to compare with below further on
1023 nLabelSeqLen = nCurLabelSeqLen;
1024 if (nLabelSeqLen != nCurLabelSeqLen)
1025 nLabelSeqLen = -2; // failed / no consistent use of label cells
1026
1027 // get table and cell names for label and values data sequences
1028 // (start and end cell will be sorted, i.e. start cell <= end cell)
1029 OUString aLabelTableName, aLabelStartCell, aLabelEndCell;
1030 OUString aValuesTableName, aValuesStartCell, aValuesEndCell;
1031 OUString aLabelRange, aValuesRange;
1032 if (xCurLabel.is())
1033 aLabelRange = xCurLabel->getSourceRangeRepresentation();
1034 if (xCurValues.is())
1035 aValuesRange = xCurValues->getSourceRangeRepresentation();
1036 if ((!aLabelRange.isEmpty() && !GetTableAndCellsFromRangeRep( aLabelRange,
1037 aLabelTableName, aLabelStartCell, aLabelEndCell )) ||
1038 !GetTableAndCellsFromRangeRep( aValuesRange,
1039 aValuesTableName, aValuesStartCell, aValuesEndCell ))
1040 {
1041 return aResult; // failed -> return empty property sequence
1042 }
1043
1044 // make sure all sequences use the same table
1045 if (aTableName.isEmpty())
1046 aTableName = aValuesTableName; // get initial value to compare with
1047 if (aTableName.isEmpty() ||
1048 aTableName != aValuesTableName ||
1049 (!aLabelTableName.isEmpty() && aTableName != aLabelTableName))
1050 {
1051 return aResult; // failed -> return empty property sequence
1052 }
1053
1054 // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
1055 // first and last cell used in both sequences
1056
1057 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1058 const OUString aCell( !aLabelStartCell.isEmpty() ? aLabelStartCell : aValuesStartCell );
1059 OSL_ENSURE( !aCell.isEmpty() , "start cell missing?" );
1060 SwXTextTable::GetCellPosition( aCell, nFirstCol, nFirstRow);
1061 SwXTextTable::GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
1062
1063 sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed
1064 if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
1065 {
1066 OSL_ENSURE( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
1067 "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
1068 nDirection = 0; // default direction for a single cell should be 'columns'
1069 }
1070 else // more than one cell is available (in values and label together!)
1071 {
1072 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1073 nDirection = 1;
1074 else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
1075 nDirection = 0;
1076 else
1077 {
1078 OSL_FAIL( "trying to determine 'DataRowSource': unexpected case found" );
1079 nDirection = -2;
1080 }
1081 }
1082 // check for consistent direction of data source
1083 if (nDtaSrcIsColumns == -1) // set initial value to compare with below
1084 nDtaSrcIsColumns = nDirection;
1085 if (nDtaSrcIsColumns != nDirection)
1086 {
1087 nDtaSrcIsColumns = -2; // failed
1088 }
1089
1090 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1091 {
1092 // build data to obtain 'SequenceMapping' later on
1093
1094 OSL_ENSURE( nDtaSrcIsColumns == 0 || /* rows */
1095 nDtaSrcIsColumns == 1, /* columns */
1096 "unexpected value for 'nDtaSrcIsColumns'" );
1097 pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
1098
1099 // build data used to determine 'CellRangeRepresentation' later on
1100
1101 GetTableByName( *m_pDoc, aTableName, &pTableFormat, &pTable );
1102 if (!pTable || pTable->IsTableComplex())
1103 return aResult; // failed -> return empty property sequence
1104 nTableRows = pTable->GetTabLines().size();
1105 nTableCols = pTable->GetTabLines().front()->GetTabBoxes().size();
1106 aMap.resize( nTableRows );
1107 for (sal_Int32 i = 0; i < nTableRows; ++i)
1108 aMap[i].resize( nTableCols );
1109
1110 if (!aLabelStartCell.isEmpty() && !aLabelEndCell.isEmpty())
1111 {
1112 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1113 SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
1114 SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow );
1115 if (nStartRow < 0 || nEndRow >= nTableRows ||
1116 nStartCol < 0 || nEndCol >= nTableCols)
1117 {
1118 return aResult; // failed -> return empty property sequence
1119 }
1120 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1121 {
1122 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1123 {
1124 char &rChar = aMap[i][k];
1125 if (rChar == '\0') // check for overlapping values and/or labels
1126 rChar = 'L';
1127 else
1128 return aResult; // failed -> return empty property sequence
1129 }
1130 }
1131 }
1132 if (!aValuesStartCell.isEmpty() && !aValuesEndCell.isEmpty())
1133 {
1134 sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1135 SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
1136 SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow );
1137 if (nStartRow < 0 || nEndRow >= nTableRows ||
1138 nStartCol < 0 || nEndCol >= nTableCols)
1139 {
1140 return aResult; // failed -> return empty property sequence
1141 }
1142 for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
1143 {
1144 for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
1145 {
1146 char &rChar = aMap[i][k];
1147 if (rChar == '\0') // check for overlapping values and/or labels
1148 rChar = 'x';
1149 else
1150 return aResult; // failed -> return empty property sequence
1151 }
1152 }
1153 }
1154 }
1155
1156 #if OSL_DEBUG_LEVEL > 0
1157 // do some extra sanity checking that the length of the sequences
1158 // matches their range representation
1159 {
1160 sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
1161 if (xCurLabel.is())
1162 {
1163 SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
1164 SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow);
1165 OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
1166 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
1167 "label sequence length does not match range representation!" );
1168 }
1169 if (xCurValues.is())
1170 {
1171 SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
1172 SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow);
1173 OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
1174 (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
1175 "value sequence length does not match range representation!" );
1176 }
1177 }
1178 #endif
1179 } // for
1180
1181 // build value for 'CellRangeRepresentation'
1182
1183 const OUString aCellRangeBase = aTableName + ".";
1184 OUString aCurRange;
1185 for (sal_Int32 i = 0; i < nTableRows; ++i)
1186 {
1187 for (sal_Int32 k = 0; k < nTableCols; ++k)
1188 {
1189 if (aMap[i][k] != '\0') // top-left cell of a sub-range found
1190 {
1191 // find rectangular sub-range to use
1192 sal_Int32 nRowIndex1 = i; // row index
1193 sal_Int32 nColIndex1 = k; // column index
1194 sal_Int32 nRowSubLen = 0;
1195 sal_Int32 nColSubLen = 0;
1196 while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
1197 ++nRowSubLen;
1198 // be aware of shifted sequences!
1199 // (according to the checks done prior the length should be ok)
1200 while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
1201 && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
1202 {
1203 ++nColIndex1;
1204 ++nColSubLen;
1205 }
1206 OUString aStartCell( sw_GetCellName( k, i ) );
1207 OUString aEndCell( sw_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
1208 aCurRange = aCellRangeBase + aStartCell + ":" + aEndCell;
1209 if (!aCellRanges.isEmpty())
1210 aCellRanges += ";";
1211 aCellRanges += aCurRange;
1212
1213 // clear already found sub-range from map
1214 for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2)
1215 for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2)
1216 aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
1217 }
1218 }
1219 }
1220 // to be nice to the user we now sort the cell ranges according to
1221 // rows or columns depending on the direction used in the data source
1222 uno::Sequence< OUString > aSortedRanges;
1223 GetSubranges( aCellRanges, aSortedRanges, false /*sub ranges should already be normalized*/ );
1224 SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
1225 OUString aSortedCellRanges;
1226 for (const OUString& rSortedRange : std::as_const(aSortedRanges))
1227 {
1228 if (!aSortedCellRanges.isEmpty())
1229 aSortedCellRanges += ";";
1230 aSortedCellRanges += rSortedRange;
1231 }
1232
1233 // build value for 'SequenceMapping'
1234
1235 uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
1236 std::sort( aSortedMapping.begin(), aSortedMapping.end() );
1237 bool bNeedSequenceMapping = false;
1238 for (sal_Int32 i = 0; i < aSequenceMapping.getLength(); ++i)
1239 {
1240 auto it = std::find( aSortedMapping.begin(), aSortedMapping.end(),
1241 aSequenceMapping[i] );
1242 aSequenceMapping[i] = std::distance(aSortedMapping.begin(), it);
1243
1244 if (i != aSequenceMapping[i])
1245 bNeedSequenceMapping = true;
1246 }
1247
1248 // check if 'SequenceMapping' is actually not required...
1249 // (don't write unnecessary properties to the XML file)
1250 if (!bNeedSequenceMapping)
1251 aSequenceMapping.realloc(0);
1252
1253 // build resulting properties
1254
1255 OSL_ENSURE(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
1256 "unexpected value for 'nLabelSeqLen'" );
1257 bool bFirstCellIsLabel = false; // default value if 'nLabelSeqLen' could not properly determined
1258 if (nLabelSeqLen > 0) // == 0 means no label sequence in use
1259 bFirstCellIsLabel = true;
1260
1261 OSL_ENSURE( !aSortedCellRanges.isEmpty(), "CellRangeRepresentation missing" );
1262 const OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
1263
1264 aResult.realloc(5);
1265 sal_Int32 nProps = 0;
1266 aResult[nProps ].Name = "FirstCellAsLabel";
1267 aResult[nProps++].Value <<= bFirstCellIsLabel;
1268 aResult[nProps ].Name = "CellRangeRepresentation";
1269 aResult[nProps++].Value <<= aSortedCellRanges;
1270 if (!aBrokenCellRangeForExport.isEmpty())
1271 {
1272 aResult[nProps ].Name = "BrokenCellRangeForExport";
1273 aResult[nProps++].Value <<= aBrokenCellRangeForExport;
1274 }
1275 if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1276 {
1277 chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
1278 chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
1279 aResult[nProps ].Name = "DataRowSource";
1280 aResult[nProps++].Value <<= eDataRowSource;
1281
1282 if (aSequenceMapping.hasElements())
1283 {
1284 aResult[nProps ].Name = "SequenceMapping";
1285 aResult[nProps++].Value <<= aSequenceMapping;
1286 }
1287 }
1288 aResult.realloc( nProps );
1289
1290 return aResult;
1291 }
1292
Impl_createDataSequenceByRangeRepresentation(const OUString & rRangeRepresentation,bool bTestOnly)1293 uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
1294 const OUString& rRangeRepresentation, bool bTestOnly )
1295 {
1296 if (m_bDisposed)
1297 throw lang::DisposedException();
1298
1299 SwFrameFormat *pTableFormat = nullptr; // pointer to table format
1300 std::shared_ptr<SwUnoCursor> pUnoCursor; // pointer to new created cursor spanning the cell range
1301 GetFormatAndCreateCursorFromRangeRep( m_pDoc, rRangeRepresentation,
1302 &pTableFormat, pUnoCursor );
1303 if (!pTableFormat || !pUnoCursor)
1304 throw lang::IllegalArgumentException();
1305
1306 // check that cursors point and mark are in a single row or column.
1307 OUString aCellRange( GetCellRangeName( *pTableFormat, *pUnoCursor ) );
1308 SwRangeDescriptor aDesc;
1309 FillRangeDescriptor( aDesc, aCellRange );
1310 if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1311 throw lang::IllegalArgumentException();
1312
1313 OSL_ENSURE( pTableFormat && pUnoCursor, "table format or cursor missing" );
1314 uno::Reference< chart2::data::XDataSequence > xDataSeq;
1315 if (!bTestOnly)
1316 xDataSeq = new SwChartDataSequence( *this, *pTableFormat, pUnoCursor );
1317
1318 return xDataSeq;
1319 }
1320
createDataSequenceByRangeRepresentationPossible(const OUString & rRangeRepresentation)1321 sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
1322 const OUString& rRangeRepresentation )
1323 {
1324 SolarMutexGuard aGuard;
1325
1326 bool bPossible = true;
1327 try
1328 {
1329 Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, true );
1330 }
1331 catch (lang::IllegalArgumentException &)
1332 {
1333 bPossible = false;
1334 }
1335
1336 return bPossible;
1337 }
1338
createDataSequenceByRangeRepresentation(const OUString & rRangeRepresentation)1339 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
1340 const OUString& rRangeRepresentation )
1341 {
1342 SolarMutexGuard aGuard;
1343 return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
1344 }
1345
getRangeSelection()1346 uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
1347 {
1348 // note: it is no error to return nothing here
1349 return uno::Reference< sheet::XRangeSelection >();
1350 }
1351
1352 uno::Reference<css::chart2::data::XDataSequence> SAL_CALL
createDataSequenceByValueArray(const OUString &,const OUString &)1353 SwChartDataProvider::createDataSequenceByValueArray(
1354 const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/ )
1355 {
1356 return uno::Reference<css::chart2::data::XDataSequence>();
1357 }
1358
dispose()1359 void SAL_CALL SwChartDataProvider::dispose( )
1360 {
1361 bool bMustDispose( false );
1362 {
1363 osl::MutexGuard aGuard( GetChartMutex() );
1364 bMustDispose = !m_bDisposed;
1365 if (!m_bDisposed)
1366 m_bDisposed = true;
1367 }
1368 if (!bMustDispose)
1369 return;
1370
1371 // dispose all data-sequences
1372 for (const auto& rEntry : m_aDataSequences)
1373 {
1374 DisposeAllDataSequences( rEntry.first );
1375 }
1376 // release all references to data-sequences
1377 m_aDataSequences.clear();
1378
1379 // require listeners to release references to this object
1380 lang::EventObject aEvtObj( static_cast< chart2::data::XDataProvider * >(this) );
1381 m_aEventListeners.disposeAndClear( aEvtObj );
1382 }
1383
addEventListener(const uno::Reference<lang::XEventListener> & rxListener)1384 void SAL_CALL SwChartDataProvider::addEventListener(
1385 const uno::Reference< lang::XEventListener >& rxListener )
1386 {
1387 osl::MutexGuard aGuard( GetChartMutex() );
1388 if (!m_bDisposed && rxListener.is())
1389 m_aEventListeners.addInterface( rxListener );
1390 }
1391
removeEventListener(const uno::Reference<lang::XEventListener> & rxListener)1392 void SAL_CALL SwChartDataProvider::removeEventListener(
1393 const uno::Reference< lang::XEventListener >& rxListener )
1394 {
1395 osl::MutexGuard aGuard( GetChartMutex() );
1396 if (!m_bDisposed && rxListener.is())
1397 m_aEventListeners.removeInterface( rxListener );
1398 }
1399
getImplementationName()1400 OUString SAL_CALL SwChartDataProvider::getImplementationName( )
1401 {
1402 return "SwChartDataProvider";
1403 }
1404
supportsService(const OUString & rServiceName)1405 sal_Bool SAL_CALL SwChartDataProvider::supportsService(const OUString& rServiceName )
1406 {
1407 return cppu::supportsService(this, rServiceName);
1408 }
1409
getSupportedServiceNames()1410 uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( )
1411 {
1412 return { "com.sun.star.chart2.data.DataProvider"};
1413 }
1414
AddDataSequence(const SwTable & rTable,uno::Reference<chart2::data::XDataSequence> const & rxDataSequence)1415 void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > const &rxDataSequence )
1416 {
1417 m_aDataSequences[ &rTable ].insert( rxDataSequence );
1418 }
1419
RemoveDataSequence(const SwTable & rTable,uno::Reference<chart2::data::XDataSequence> const & rxDataSequence)1420 void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > const &rxDataSequence )
1421 {
1422 m_aDataSequences[ &rTable ].erase( rxDataSequence );
1423 }
1424
InvalidateTable(const SwTable * pTable,bool bImmediate)1425 void SwChartDataProvider::InvalidateTable( const SwTable *pTable, bool bImmediate )
1426 {
1427 OSL_ENSURE( pTable, "table pointer is NULL" );
1428 if (!pTable)
1429 return;
1430
1431 if (!m_bDisposed)
1432 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
1433
1434 const Set_DataSequenceRef_t &rSet = m_aDataSequences[ pTable ];
1435 for (const auto& rItem : rSet)
1436 {
1437 uno::Reference< chart2::data::XDataSequence > xTemp(rItem); // temporary needed for g++ 3.3.5
1438 uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
1439 if (xRef.is())
1440 {
1441 // mark the sequence as 'dirty' and notify listeners
1442 xRef->setModified( true );
1443 }
1444 }
1445
1446 // tdf#122995 added Immediate-mode to allow non-timer-delayed Chart invalidation
1447 if (bImmediate && !m_bDisposed)
1448 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().Disconnect();
1449 }
1450
DeleteBox(const SwTable * pTable,const SwTableBox & rBox)1451 void SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
1452 {
1453 OSL_ENSURE( pTable, "table pointer is NULL" );
1454 if (!pTable)
1455 return;
1456
1457 if (!m_bDisposed)
1458 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
1459
1460 Set_DataSequenceRef_t &rSet = m_aDataSequences[ pTable ];
1461
1462 // iterate over all data-sequences for that table...
1463 Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
1464 Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
1465 Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate
1466 while (aIt != aEndIt)
1467 {
1468 SwChartDataSequence *pDataSeq = nullptr;
1469 bool bNowEmpty = false;
1470 bool bSeqDisposed = false;
1471
1472 // check if weak reference is still valid...
1473 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);
1474 if (xTemp.is())
1475 {
1476 // then delete that table box (check if implementation cursor needs to be adjusted)
1477 pDataSeq = static_cast< SwChartDataSequence * >( xTemp.get() );
1478 if (pDataSeq)
1479 {
1480 try
1481 {
1482 bNowEmpty = pDataSeq->DeleteBox( rBox );
1483 }
1484 catch (const lang::DisposedException&)
1485 {
1486 bNowEmpty = true;
1487 bSeqDisposed = true;
1488 }
1489
1490 if (bNowEmpty)
1491 aDelIt = aIt;
1492 }
1493 }
1494 ++aIt;
1495
1496 if (bNowEmpty)
1497 {
1498 rSet.erase( aDelIt );
1499 if (pDataSeq && !bSeqDisposed)
1500 pDataSeq->dispose(); // the current way to tell chart that sth. got removed
1501 }
1502 }
1503 }
1504
DisposeAllDataSequences(const SwTable * pTable)1505 void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
1506 {
1507 OSL_ENSURE( pTable, "table pointer is NULL" );
1508 if (!pTable)
1509 return;
1510
1511 if (!m_bDisposed)
1512 pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
1513
1514 //! make a copy of the STL container!
1515 //! This is necessary since calling 'dispose' will implicitly remove an element
1516 //! of the original container, and thus any iterator in the original container
1517 //! would become invalid.
1518 const Set_DataSequenceRef_t aSet( m_aDataSequences[ pTable ] );
1519
1520 for (const auto& rItem : aSet)
1521 {
1522 uno::Reference< chart2::data::XDataSequence > xTemp(rItem); // temporary needed for g++ 3.3.5
1523 uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
1524 if (xRef.is())
1525 {
1526 xRef->dispose();
1527 }
1528 }
1529 }
1530
1531 /**
1532 * SwChartDataProvider::AddRowCols tries to notify charts of added columns
1533 * or rows and extends the value sequence respectively (if possible).
1534 * If those can be added to the end of existing value data-sequences those
1535 * sequences get modified accordingly and will send a modification
1536 * notification (calling 'setModified
1537 *
1538 * Since this function is a work-around for non existent Writer core functionality
1539 * (no arbitrary multi-selection in tables that can be used to define a
1540 * data-sequence) this function will be somewhat unreliable.
1541 * For example we will only try to adapt value sequences. For this we assume
1542 * that a sequence of length 1 is a label sequence and those with length >= 2
1543 * we presume to be value sequences. Also new cells can only be added in the
1544 * direction the value sequence is already pointing (rows / cols) and at the
1545 * start or end of the values data-sequence.
1546 * Nothing needs to be done if the new cells are in between the table cursors
1547 * point and mark since data-sequence are considered to consist of all cells
1548 * between those.
1549 * New rows/cols need to be added already to the table before calling
1550 * this function.
1551 */
AddRowCols(const SwTable & rTable,const SwSelBoxes & rBoxes,sal_uInt16 nLines,bool bBehind)1552 void SwChartDataProvider::AddRowCols(
1553 const SwTable &rTable,
1554 const SwSelBoxes& rBoxes,
1555 sal_uInt16 nLines, bool bBehind )
1556 {
1557 if (rTable.IsTableComplex())
1558 return;
1559
1560 const size_t nBoxes = rBoxes.size();
1561 if (nBoxes < 1 || nLines < 1)
1562 return;
1563
1564 SwTableBox* pFirstBox = rBoxes[0];
1565 SwTableBox* pLastBox = rBoxes.back();
1566
1567 if (!(pFirstBox && pLastBox))
1568 return;
1569
1570 sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1571 SwXTextTable::GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow );
1572 SwXTextTable::GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow );
1573
1574 bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/
1575 if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1576 bAddCols = true;
1577 if (nFirstCol != nLastCol && nFirstRow != nLastRow)
1578 return;
1579
1580 //get range of indices in col/rows for new cells
1581 sal_Int32 nFirstNewCol = nFirstCol;
1582 sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines;
1583 if (bAddCols)
1584 {
1585 OSL_ENSURE( nFirstCol == nLastCol, "column indices seem broken" );
1586 nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines;
1587 nFirstNewRow = nFirstRow;
1588 }
1589
1590 // iterate over all data-sequences for the table
1591 const Set_DataSequenceRef_t &rSet = m_aDataSequences[ &rTable ];
1592 for (const auto& rItem : rSet)
1593 {
1594 uno::Reference< chart2::data::XDataSequence > xTemp(rItem); // temporary needed for g++ 3.3.5
1595 uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
1596 if (xRef.is())
1597 {
1598 const sal_Int32 nLen = xRef->getTextualData().getLength();
1599 if (nLen > 1) // value data-sequence ?
1600 {
1601 auto pDataSeq = comphelper::getUnoTunnelImplementation<SwChartDataSequence>(xRef);
1602 if (pDataSeq)
1603 {
1604 SwRangeDescriptor aDesc;
1605 pDataSeq->FillRangeDesc( aDesc );
1606
1607 chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
1608 if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1609 eDRSource = chart::ChartDataRowSource_ROWS;
1610
1611 if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
1612 {
1613 // add rows: extend affected columns by newly added row cells
1614 pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
1615 }
1616 else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
1617 {
1618 // add cols: extend affected rows by newly added column cells
1619 pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
1620 }
1621 }
1622 }
1623 }
1624 }
1625 }
1626
1627 // XRangeXMLConversion
convertRangeToXML(const OUString & rRangeRepresentation)1628 OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const OUString& rRangeRepresentation )
1629 {
1630 SolarMutexGuard aGuard;
1631 if (m_bDisposed)
1632 throw lang::DisposedException();
1633
1634 if (rRangeRepresentation.isEmpty())
1635 return OUString();
1636
1637 OUStringBuffer aRes;
1638
1639 // multiple ranges are delimited by a ';' like in
1640 // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
1641 SwTable* pFirstFoundTable = nullptr; // to check that only one table will be used
1642 sal_Int32 nPos = 0;
1643 do {
1644 const OUString aRange( rRangeRepresentation.getToken(0, ';', nPos) );
1645 SwFrameFormat *pTableFormat = nullptr; // pointer to table format
1646 std::shared_ptr<SwUnoCursor> pCursor;
1647 GetFormatAndCreateCursorFromRangeRep( m_pDoc, aRange, &pTableFormat, pCursor );
1648 if (!pTableFormat)
1649 throw lang::IllegalArgumentException();
1650 SwTable* pTable = SwTable::FindTable( pTableFormat );
1651 if (pTable->IsTableComplex())
1652 throw uno::RuntimeException("Table too complex.");
1653
1654 // check that there is only one table used in all ranges
1655 if (!pFirstFoundTable)
1656 pFirstFoundTable = pTable;
1657 if (pTable != pFirstFoundTable)
1658 throw lang::IllegalArgumentException();
1659
1660 OUString aTableName;
1661 OUString aStartCell;
1662 OUString aEndCell;
1663 if (!GetTableAndCellsFromRangeRep( aRange, aTableName, aStartCell, aEndCell ))
1664 throw lang::IllegalArgumentException();
1665
1666 sal_Int32 nCol, nRow;
1667 SwXTextTable::GetCellPosition( aStartCell, nCol, nRow );
1668 if (nCol < 0 || nRow < 0)
1669 throw uno::RuntimeException("Cell not found.");
1670
1671 //!! following objects/functions are implemented in XMLRangeHelper.?xx
1672 //!! which is a copy of the respective file from chart2 !!
1673 XMLRangeHelper::CellRange aCellRange;
1674 aCellRange.aTableName = aTableName;
1675 aCellRange.aUpperLeft.nColumn = nCol;
1676 aCellRange.aUpperLeft.nRow = nRow;
1677 aCellRange.aUpperLeft.bIsEmpty = false;
1678 if (aStartCell != aEndCell && !aEndCell.isEmpty())
1679 {
1680 SwXTextTable::GetCellPosition( aEndCell, nCol, nRow );
1681 if (nCol < 0 || nRow < 0)
1682 throw uno::RuntimeException("Cell not found.");
1683
1684 aCellRange.aLowerRight.nColumn = nCol;
1685 aCellRange.aLowerRight.nRow = nRow;
1686 aCellRange.aLowerRight.bIsEmpty = false;
1687 }
1688 OUString aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
1689 if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
1690 aRes.append(" ");
1691 aRes.append(aTmp);
1692 }
1693 while (nPos>0);
1694
1695 return aRes.makeStringAndClear();
1696 }
1697
convertRangeFromXML(const OUString & rXMLRange)1698 OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const OUString& rXMLRange )
1699 {
1700 SolarMutexGuard aGuard;
1701 if (m_bDisposed)
1702 throw lang::DisposedException();
1703
1704 if (rXMLRange.isEmpty())
1705 return OUString();
1706
1707 OUStringBuffer aRes;
1708
1709 // multiple ranges are delimited by a ' ' like in
1710 // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
1711 OUString aFirstFoundTable; // to check that only one table will be used
1712 sal_Int32 nPos = 0;
1713 do
1714 {
1715 OUString aRange( rXMLRange.getToken(0, ' ', nPos) );
1716
1717 //!! following objects and function are implemented in XMLRangeHelper.?xx
1718 //!! which is a copy of the respective file from chart2 !!
1719 XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
1720
1721 // check that there is only one table used in all ranges
1722 if (aFirstFoundTable.isEmpty())
1723 aFirstFoundTable = aCellRange.aTableName;
1724 if (aCellRange.aTableName != aFirstFoundTable)
1725 throw lang::IllegalArgumentException();
1726
1727 OUString aTmp = aCellRange.aTableName + "." +
1728 sw_GetCellName( aCellRange.aUpperLeft.nColumn,
1729 aCellRange.aUpperLeft.nRow );
1730 // does cell range consist of more than a single cell?
1731 if (!aCellRange.aLowerRight.bIsEmpty)
1732 {
1733 aTmp += ":" + sw_GetCellName( aCellRange.aLowerRight.nColumn,
1734 aCellRange.aLowerRight.nRow );
1735 }
1736
1737 if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
1738 aRes.append(";");
1739 aRes.append(aTmp);
1740 }
1741 while (nPos>0);
1742
1743 return aRes.makeStringAndClear();
1744 }
1745
SwChartDataSource(const uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> & rLDS)1746 SwChartDataSource::SwChartDataSource(
1747 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
1748 m_aLDS( rLDS )
1749 {
1750 }
1751
~SwChartDataSource()1752 SwChartDataSource::~SwChartDataSource()
1753 {
1754 }
1755
getDataSequences()1756 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( )
1757 {
1758 SolarMutexGuard aGuard;
1759 return m_aLDS;
1760 }
1761
getImplementationName()1762 OUString SAL_CALL SwChartDataSource::getImplementationName( )
1763 {
1764 return "SwChartDataSource";
1765 }
1766
supportsService(const OUString & rServiceName)1767 sal_Bool SAL_CALL SwChartDataSource::supportsService(const OUString& rServiceName )
1768 {
1769 return cppu::supportsService(this, rServiceName);
1770 }
1771
getSupportedServiceNames()1772 uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( )
1773 {
1774 return { "com.sun.star.chart2.data.DataSource" };
1775 }
1776
SwChartDataSequence(SwChartDataProvider & rProvider,SwFrameFormat & rTableFormat,const std::shared_ptr<SwUnoCursor> & pTableCursor)1777 SwChartDataSequence::SwChartDataSequence(
1778 SwChartDataProvider& rProvider,
1779 SwFrameFormat& rTableFormat,
1780 const std::shared_ptr<SwUnoCursor>& pTableCursor ) :
1781 m_pFormat(&rTableFormat),
1782 m_aEvtListeners( GetChartMutex() ),
1783 m_aModifyListeners( GetChartMutex() ),
1784 m_aRowLabelText( SwResId( STR_CHART2_ROW_LABEL_TEXT ) ),
1785 m_aColLabelText( SwResId( STR_CHART2_COL_LABEL_TEXT ) ),
1786 m_xDataProvider( &rProvider ),
1787 m_pTableCursor( pTableCursor ),
1788 m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
1789 {
1790 StartListening(rTableFormat.GetNotifier());
1791 m_bDisposed = false;
1792
1793 acquire();
1794 try
1795 {
1796 const SwTable* pTable = SwTable::FindTable( &rTableFormat );
1797 if (pTable)
1798 {
1799 uno::Reference< chart2::data::XDataSequence > xRef( static_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
1800 m_xDataProvider->AddDataSequence( *pTable, xRef );
1801 m_xDataProvider->addEventListener( static_cast< lang::XEventListener * >(this) );
1802 }
1803 else {
1804 OSL_FAIL( "table missing" );
1805 }
1806 }
1807 catch (uno::RuntimeException &)
1808 {
1809 // TODO: shouldn't there be a call to release() here?
1810 throw;
1811 }
1812 catch (uno::Exception &)
1813 {
1814 }
1815 release();
1816
1817 #if OSL_DEBUG_LEVEL > 0
1818 // check if it can properly convert into a SwUnoTableCursor
1819 // which is required for some functions
1820 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
1821 OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
1822 #endif
1823 }
1824
SwChartDataSequence(const SwChartDataSequence & rObj)1825 SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
1826 SwChartDataSequenceBaseClass(rObj),
1827 SvtListener(),
1828 m_pFormat( rObj.m_pFormat ),
1829 m_aEvtListeners( GetChartMutex() ),
1830 m_aModifyListeners( GetChartMutex() ),
1831 m_aRole( rObj.m_aRole ),
1832 m_aRowLabelText( SwResId(STR_CHART2_ROW_LABEL_TEXT) ),
1833 m_aColLabelText( SwResId(STR_CHART2_COL_LABEL_TEXT) ),
1834 m_xDataProvider( rObj.m_xDataProvider ),
1835 m_pTableCursor( rObj.m_pTableCursor ),
1836 m_pPropSet( rObj.m_pPropSet )
1837 {
1838 if(m_pFormat)
1839 StartListening(m_pFormat->GetNotifier());
1840 m_bDisposed = false;
1841
1842 acquire();
1843 try
1844 {
1845 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
1846 if (pTable)
1847 {
1848 uno::Reference< chart2::data::XDataSequence > xRef( static_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
1849 m_xDataProvider->AddDataSequence( *pTable, xRef );
1850 m_xDataProvider->addEventListener( static_cast< lang::XEventListener * >(this) );
1851 }
1852 else {
1853 OSL_FAIL( "table missing" );
1854 }
1855 }
1856 catch (uno::RuntimeException &)
1857 {
1858 // TODO: shouldn't there be a call to release() here?
1859 throw;
1860 }
1861 catch (uno::Exception &)
1862 {
1863 }
1864 release();
1865
1866 #if OSL_DEBUG_LEVEL > 0
1867 // check if it can properly convert into a SwUnoTableCursor
1868 // which is required for some functions
1869 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
1870 OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
1871 #endif
1872 }
1873
~SwChartDataSequence()1874 SwChartDataSequence::~SwChartDataSequence()
1875 {
1876 }
1877
getUnoTunnelId()1878 const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
1879 {
1880 static const UnoTunnelIdInit theSwChartDataSequenceUnoTunnelId;
1881 return theSwChartDataSequenceUnoTunnelId.getSeq();
1882 }
1883
getSomething(const uno::Sequence<sal_Int8> & rId)1884 sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
1885 {
1886 if( isUnoTunnelId<SwChartDataSequence>(rId) )
1887 {
1888 return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
1889 }
1890 return 0;
1891 }
1892
1893
getSourceRangeRepresentation()1894 OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( )
1895 {
1896 SolarMutexGuard aGuard;
1897 if (m_bDisposed)
1898 throw lang::DisposedException();
1899
1900 OUString aRes;
1901 SwFrameFormat* pTableFormat = GetFrameFormat();
1902 if (pTableFormat)
1903 {
1904 const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
1905 OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
1906 aRes = pTableFormat->GetName() + "." + aCellRange;
1907 }
1908 return aRes;
1909 }
1910
generateLabel(chart2::data::LabelOrigin eLabelOrigin)1911 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
1912 chart2::data::LabelOrigin eLabelOrigin )
1913 {
1914 SolarMutexGuard aGuard;
1915 if (m_bDisposed)
1916 throw lang::DisposedException();
1917
1918 uno::Sequence< OUString > aLabels;
1919
1920 {
1921 SwRangeDescriptor aDesc;
1922 bool bOk = false;
1923 SwFrameFormat* pTableFormat = GetFrameFormat();
1924 if (!pTableFormat)
1925 throw uno::RuntimeException("No table format found.");
1926 SwTable* pTable = SwTable::FindTable( pTableFormat );
1927 if (!pTable)
1928 throw uno::RuntimeException("No table found.");
1929 if (pTable->IsTableComplex())
1930 throw uno::RuntimeException("Table too complex.");
1931
1932 const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
1933 OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
1934 bOk = FillRangeDescriptor( aDesc, aCellRange );
1935 OSL_ENSURE( bOk, "failed to get SwRangeDescriptor" );
1936
1937 if (bOk)
1938 {
1939 aDesc.Normalize();
1940 sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
1941 sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
1942 OSL_ENSURE( nColSpan == 1 || nRowSpan == 1,
1943 "unexpected range of selected cells" );
1944
1945 OUString aText; // label text to be returned
1946 bool bReturnEmptyText = false;
1947 bool bUseCol = true;
1948 if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
1949 bUseCol = true;
1950 else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
1951 bUseCol = false;
1952 else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
1953 {
1954 bUseCol = nColSpan < nRowSpan;
1955 bReturnEmptyText = nColSpan == nRowSpan;
1956 }
1957 else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
1958 {
1959 bUseCol = nColSpan > nRowSpan;
1960 bReturnEmptyText = nColSpan == nRowSpan;
1961 }
1962 else {
1963 OSL_FAIL( "unexpected case" );
1964 }
1965
1966 // build label sequence
1967
1968 sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
1969 aLabels.realloc( nSeqLen );
1970 OUString *pLabels = aLabels.getArray();
1971 for (sal_Int32 i = 0; i < nSeqLen; ++i)
1972 {
1973 if (!bReturnEmptyText)
1974 {
1975 aText = bUseCol ? m_aColLabelText : m_aRowLabelText;
1976 sal_Int32 nCol = aDesc.nLeft;
1977 sal_Int32 nRow = aDesc.nTop;
1978 if (bUseCol)
1979 nCol = nCol + i;
1980 else
1981 nRow = nRow + i;
1982 OUString aCellName( sw_GetCellName( nCol, nRow ) );
1983
1984 sal_Int32 nLen = aCellName.getLength();
1985 if (nLen)
1986 {
1987 const sal_Unicode *pBuf = aCellName.getStr();
1988 const sal_Unicode *pEnd = pBuf + nLen;
1989 while (pBuf < pEnd && ('0' > *pBuf || *pBuf > '9'))
1990 ++pBuf;
1991 // start of number found?
1992 if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
1993 {
1994 OUString aRplc;
1995 OUString aNew;
1996 if (bUseCol)
1997 {
1998 aRplc = "%COLUMNLETTER";
1999 aNew = aCellName.copy(0, pBuf - aCellName.getStr());
2000 }
2001 else
2002 {
2003 aRplc = "%ROWNUMBER";
2004 aNew = OUString(pBuf, (aCellName.getStr() + nLen) - pBuf);
2005 }
2006 aText = aText.replaceFirst( aRplc, aNew );
2007 }
2008 }
2009 }
2010 pLabels[i] = aText;
2011 }
2012 }
2013 }
2014
2015 return aLabels;
2016 }
2017
getNumberFormatKeyByIndex(::sal_Int32)2018 ::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
2019 ::sal_Int32 /*nIndex*/ )
2020 {
2021 return 0;
2022 }
2023
GetCells()2024 std::vector< css::uno::Reference< css::table::XCell > > SwChartDataSequence::GetCells()
2025 {
2026 if (m_bDisposed)
2027 throw lang::DisposedException();
2028 auto pTableFormat(GetFrameFormat());
2029 if(!pTableFormat)
2030 return std::vector< css::uno::Reference< css::table::XCell > >();
2031 auto pTable(SwTable::FindTable(pTableFormat));
2032 if(pTable->IsTableComplex())
2033 return std::vector< css::uno::Reference< css::table::XCell > >();
2034 SwRangeDescriptor aDesc;
2035 if(!FillRangeDescriptor(aDesc, GetCellRangeName(*pTableFormat, *m_pTableCursor)))
2036 return std::vector< css::uno::Reference< css::table::XCell > >();
2037 return SwXCellRange::CreateXCellRange(m_pTableCursor, *pTableFormat, aDesc)->GetCells();
2038 }
2039
getTextualData()2040 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData()
2041 {
2042 SolarMutexGuard aGuard;
2043 auto vCells(GetCells());
2044 uno::Sequence< OUString > vTextData(vCells.size());
2045 std::transform(vCells.begin(),
2046 vCells.end(),
2047 vTextData.begin(),
2048 [] (decltype(vCells)::value_type& xCell)
2049 { return static_cast<SwXCell*>(xCell.get())->getString(); });
2050 return vTextData;
2051 }
2052
getData()2053 uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData()
2054 {
2055 SolarMutexGuard aGuard;
2056 auto vCells(GetCells());
2057 uno::Sequence< uno::Any > vAnyData(vCells.size());
2058 std::transform(vCells.begin(),
2059 vCells.end(),
2060 vAnyData.begin(),
2061 [] (decltype(vCells)::value_type& xCell)
2062 { return static_cast<SwXCell*>(xCell.get())->GetAny(); });
2063 return vAnyData;
2064 }
2065
getNumericalData()2066 uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData()
2067 {
2068 SolarMutexGuard aGuard;
2069 auto vCells(GetCells());
2070 uno::Sequence< double > vNumData(vCells.size());
2071 std::transform(vCells.begin(),
2072 vCells.end(),
2073 vNumData.begin(),
2074 [] (decltype(vCells)::value_type& xCell)
2075 { return static_cast<SwXCell*>(xCell.get())->GetForcedNumericalValue(); });
2076 return vNumData;
2077 }
2078
createClone()2079 uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( )
2080 {
2081 SolarMutexGuard aGuard;
2082 if (m_bDisposed)
2083 throw lang::DisposedException();
2084 return new SwChartDataSequence( *this );
2085 }
2086
getPropertySetInfo()2087 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( )
2088 {
2089 SolarMutexGuard aGuard;
2090 if (m_bDisposed)
2091 throw lang::DisposedException();
2092
2093 static uno::Reference< beans::XPropertySetInfo > xRes = m_pPropSet->getPropertySetInfo();
2094 return xRes;
2095 }
2096
setPropertyValue(const OUString & rPropertyName,const uno::Any & rValue)2097 void SAL_CALL SwChartDataSequence::setPropertyValue(
2098 const OUString& rPropertyName,
2099 const uno::Any& rValue )
2100 {
2101 SolarMutexGuard aGuard;
2102 if (m_bDisposed)
2103 throw lang::DisposedException();
2104
2105 if (rPropertyName != UNO_NAME_ROLE)
2106 throw beans::UnknownPropertyException(rPropertyName);
2107
2108 if ( !(rValue >>= m_aRole) )
2109 throw lang::IllegalArgumentException();
2110 }
2111
getPropertyValue(const OUString & rPropertyName)2112 uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
2113 const OUString& rPropertyName )
2114 {
2115 SolarMutexGuard aGuard;
2116 if (m_bDisposed)
2117 throw lang::DisposedException();
2118
2119 if (!(rPropertyName == UNO_NAME_ROLE))
2120 throw beans::UnknownPropertyException(rPropertyName);
2121
2122 return uno::Any(m_aRole);
2123 }
2124
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)2125 void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
2126 const OUString& /*rPropertyName*/,
2127 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2128 {
2129 OSL_FAIL( "not implemented" );
2130 }
2131
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)2132 void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
2133 const OUString& /*rPropertyName*/,
2134 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2135 {
2136 OSL_FAIL( "not implemented" );
2137 }
2138
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)2139 void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
2140 const OUString& /*rPropertyName*/,
2141 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2142 {
2143 OSL_FAIL( "not implemented" );
2144 }
2145
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)2146 void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
2147 const OUString& /*rPropertyName*/,
2148 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2149 {
2150 OSL_FAIL( "not implemented" );
2151 }
2152
getImplementationName()2153 OUString SAL_CALL SwChartDataSequence::getImplementationName( )
2154 {
2155 return "SwChartDataSequence";
2156 }
2157
supportsService(const OUString & rServiceName)2158 sal_Bool SAL_CALL SwChartDataSequence::supportsService(const OUString& rServiceName )
2159 {
2160 return cppu::supportsService(this, rServiceName);
2161 }
2162
getSupportedServiceNames()2163 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( )
2164 {
2165 return { "com.sun.star.chart2.data.DataSequence" };
2166 }
2167
Notify(const SfxHint & rHint)2168 void SwChartDataSequence::Notify( const SfxHint& rHint)
2169 {
2170 if(rHint.GetId() == SfxHintId::Dying)
2171 m_pFormat = nullptr;
2172 if(!m_pFormat || !m_pTableCursor)
2173 {
2174 m_pFormat = nullptr;
2175 m_pTableCursor.reset(nullptr);
2176 dispose();
2177 }
2178 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
2179 {
2180 setModified( true );
2181 }
2182 }
2183
isModified()2184 sal_Bool SAL_CALL SwChartDataSequence::isModified( )
2185 {
2186 SolarMutexGuard aGuard;
2187 if (m_bDisposed)
2188 throw lang::DisposedException();
2189
2190 return true;
2191 }
2192
setModified(sal_Bool bModified)2193 void SAL_CALL SwChartDataSequence::setModified(
2194 sal_Bool bModified )
2195 {
2196 SolarMutexGuard aGuard;
2197 if (m_bDisposed)
2198 throw lang::DisposedException();
2199
2200 if (bModified)
2201 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2202 }
2203
addModifyListener(const uno::Reference<util::XModifyListener> & rxListener)2204 void SAL_CALL SwChartDataSequence::addModifyListener(
2205 const uno::Reference< util::XModifyListener >& rxListener )
2206 {
2207 osl::MutexGuard aGuard( GetChartMutex() );
2208 if (!m_bDisposed && rxListener.is())
2209 m_aModifyListeners.addInterface( rxListener );
2210 }
2211
removeModifyListener(const uno::Reference<util::XModifyListener> & rxListener)2212 void SAL_CALL SwChartDataSequence::removeModifyListener(
2213 const uno::Reference< util::XModifyListener >& rxListener )
2214 {
2215 osl::MutexGuard aGuard( GetChartMutex() );
2216 if (!m_bDisposed && rxListener.is())
2217 m_aModifyListeners.removeInterface( rxListener );
2218 }
2219
disposing(const lang::EventObject & rSource)2220 void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
2221 {
2222 if (m_bDisposed)
2223 throw lang::DisposedException();
2224 if (rSource.Source == static_cast<cppu::OWeakObject*>(m_xDataProvider.get()))
2225 {
2226 m_xDataProvider.clear();
2227 }
2228 }
2229
dispose()2230 void SAL_CALL SwChartDataSequence::dispose( )
2231 {
2232 bool bMustDispose( false );
2233 {
2234 osl::MutexGuard aGuard( GetChartMutex() );
2235 bMustDispose = !m_bDisposed;
2236 if (!m_bDisposed)
2237 m_bDisposed = true;
2238 }
2239 if (!bMustDispose)
2240 return;
2241
2242 m_bDisposed = true;
2243 if (m_xDataProvider.is())
2244 {
2245 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
2246 if (pTable)
2247 {
2248 uno::Reference< chart2::data::XDataSequence > xRef( static_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2249 m_xDataProvider->RemoveDataSequence( *pTable, xRef );
2250 }
2251 else {
2252 OSL_FAIL( "table missing" );
2253 }
2254
2255 //#i119653# The bug is crashed for an exception thrown by
2256 //SwCharDataSequence::setModified() because
2257 //the SwCharDataSequence object has been disposed.
2258
2259 //Actually, the former design of SwClient will disconnect itself
2260 //from the notification list in its destructor.
2261
2262 //But the SwCharDataSequence won't be destructed but disposed in code
2263 //(the data member SwChartDataSequence::bDisposed will be set to
2264 //TRUE), the relationship between client and modification is not
2265 //released.
2266
2267 //So any notification from modify object will lead to said
2268 //exception threw out. Recorrect the logic of code in
2269 //SwChartDataSequence::Dispose(), release the relationship
2270 //here...
2271 if (m_pFormat && m_pFormat->HasWriterListeners())
2272 {
2273 EndListeningAll();
2274 m_pFormat = nullptr;
2275 m_pTableCursor.reset(nullptr);
2276 }
2277 }
2278
2279 // require listeners to release references to this object
2280 lang::EventObject aEvtObj( static_cast< chart2::data::XDataSequence * >(this) );
2281 m_aModifyListeners.disposeAndClear( aEvtObj );
2282 m_aEvtListeners.disposeAndClear( aEvtObj );
2283 }
2284
addEventListener(const uno::Reference<lang::XEventListener> & rxListener)2285 void SAL_CALL SwChartDataSequence::addEventListener(
2286 const uno::Reference< lang::XEventListener >& rxListener )
2287 {
2288 osl::MutexGuard aGuard( GetChartMutex() );
2289 if (!m_bDisposed && rxListener.is())
2290 m_aEvtListeners.addInterface( rxListener );
2291 }
2292
removeEventListener(const uno::Reference<lang::XEventListener> & rxListener)2293 void SAL_CALL SwChartDataSequence::removeEventListener(
2294 const uno::Reference< lang::XEventListener >& rxListener )
2295 {
2296 osl::MutexGuard aGuard( GetChartMutex() );
2297 if (!m_bDisposed && rxListener.is())
2298 m_aEvtListeners.removeInterface( rxListener );
2299 }
2300
DeleteBox(const SwTableBox & rBox)2301 bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
2302 {
2303 if (m_bDisposed)
2304 throw lang::DisposedException();
2305
2306 // to be set if the last box of the data-sequence was removed here
2307 bool bNowEmpty = false;
2308
2309 // if the implementation cursor gets affected (i.e. the box where it is located
2310 // in gets removed) we need to move it before that... (otherwise it does not need to change)
2311
2312 const SwStartNode* pPointStartNode = m_pTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2313 const SwStartNode* pMarkStartNode = m_pTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2314
2315 if (!m_pTableCursor->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd()))
2316 {
2317 bNowEmpty = true;
2318 }
2319 else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd())
2320 {
2321 sal_Int32 nPointRow = -1, nPointCol = -1;
2322 sal_Int32 nMarkRow = -1, nMarkCol = -1;
2323 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
2324 OUString aPointCellName( pTable->GetTableBox( pPointStartNode->GetIndex() )->GetName() );
2325 OUString aMarkCellName( pTable->GetTableBox( pMarkStartNode->GetIndex() )->GetName() );
2326
2327 SwXTextTable::GetCellPosition( aPointCellName, nPointCol, nPointRow );
2328 SwXTextTable::GetCellPosition( aMarkCellName, nMarkCol, nMarkRow );
2329 OSL_ENSURE( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
2330 OSL_ENSURE( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
2331
2332 // move vertical or horizontal?
2333 OSL_ENSURE( nPointRow == nMarkRow || nPointCol == nMarkCol,
2334 "row/col indices not matching" );
2335 OSL_ENSURE( nPointRow != nMarkRow || nPointCol != nMarkCol,
2336 "point and mark are identical" );
2337 bool bMoveVertical = (nPointCol == nMarkCol);
2338 bool bMoveHorizontal = (nPointRow == nMarkRow);
2339
2340 // get movement direction
2341 bool bMoveLeft = false; // move left or right?
2342 bool bMoveUp = false; // move up or down?
2343 if (bMoveVertical)
2344 {
2345 if (pPointStartNode == rBox.GetSttNd()) // move point?
2346 bMoveUp = nPointRow > nMarkRow;
2347 else // move mark
2348 bMoveUp = nMarkRow > nPointRow;
2349 }
2350 else if (bMoveHorizontal)
2351 {
2352 if (pPointStartNode == rBox.GetSttNd()) // move point?
2353 bMoveLeft = nPointCol > nMarkCol;
2354 else // move mark
2355 bMoveLeft = nMarkCol > nPointCol;
2356 }
2357 else {
2358 OSL_FAIL( "neither vertical nor horizontal movement" );
2359 }
2360
2361 // get new box (position) to use...
2362 sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
2363 sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
2364 if (bMoveVertical)
2365 nRow += bMoveUp ? -1 : +1;
2366 if (bMoveHorizontal)
2367 nCol += bMoveLeft ? -1 : +1;
2368 const OUString aNewCellName = sw_GetCellName( nCol, nRow );
2369 SwTableBox* pNewBox = const_cast<SwTableBox*>(pTable->GetTableBox( aNewCellName ));
2370
2371 if (pNewBox) // set new position (cell range) to use
2372 {
2373 // This is how you get the first content node of a row:
2374 // First get a SwNodeIndex pointing to the node after SwStartNode of the box...
2375 SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
2376 // This can be a SwContentNode, but might also be a table or section node,
2377 // therefore call GoNext
2378 SwContentNode *pCNd = aIdx.GetNode().GetContentNode();
2379 if (!pCNd)
2380 pCNd = GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx );
2381 // and then one can e.g. create a SwPosition:
2382 SwPosition aNewPos( *pCNd ); // new position to be used with cursor
2383
2384 // if the mark is to be changed, make sure there is one
2385 if (pMarkStartNode == rBox.GetSttNd() && !m_pTableCursor->HasMark())
2386 m_pTableCursor->SetMark();
2387
2388 // set cursor to new position
2389 SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
2390 m_pTableCursor->GetPoint() : m_pTableCursor->GetMark();
2391 if (pPos)
2392 {
2393 pPos->nNode = aNewPos.nNode;
2394 pPos->nContent = aNewPos.nContent;
2395 }
2396 else {
2397 OSL_FAIL( "neither point nor mark available for change" );
2398 }
2399 }
2400 else {
2401 OSL_FAIL( "failed to get position" );
2402 }
2403 }
2404
2405 return bNowEmpty;
2406 }
2407
FillRangeDesc(SwRangeDescriptor & rRangeDesc) const2408 void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
2409 {
2410 SwFrameFormat* pTableFormat = GetFrameFormat();
2411 if(pTableFormat)
2412 {
2413 SwTable* pTable = SwTable::FindTable( pTableFormat );
2414 if(!pTable->IsTableComplex())
2415 {
2416 FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
2417 }
2418 }
2419 }
2420
2421 /**
2422 * Extends the data-sequence by new cells added at the end of the direction
2423 * the data-sequence points to.
2424 * If the cells are already within the range of the sequence nothing needs
2425 * to be done.
2426 * If the cells are beyond the end of the sequence (are not adjacent to the
2427 * current last cell) nothing can be done. Only if the cells are adjacent to
2428 * the last cell they can be added.
2429 *
2430 * @returns true if the data-sequence was changed.
2431 * @param bExtendCols - specifies if columns or rows are to be extended
2432 * @param nFirstNew - index of first new row/col to be included in data-sequence
2433 * @param nLastNew - index of last new row/col to be included in data-sequence
2434 */
ExtendTo(bool bExtendCol,sal_Int32 nFirstNew,sal_Int32 nCount)2435 void SwChartDataSequence::ExtendTo( bool bExtendCol,
2436 sal_Int32 nFirstNew, sal_Int32 nCount )
2437 {
2438 SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
2439 if (!pUnoTableCursor)
2440 return;
2441
2442 const SwStartNode *pStartNd = nullptr;
2443 const SwTableBox *pStartBox = nullptr;
2444 const SwTableBox *pEndBox = nullptr;
2445
2446 const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
2447 OSL_ENSURE( !pTable->IsTableComplex(), "table too complex" );
2448 if (nCount < 1 || nFirstNew < 0 || pTable->IsTableComplex())
2449 return;
2450
2451 // get range descriptor (cell range) for current data-sequence
2452
2453 pStartNd = pUnoTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2454 pEndBox = pTable->GetTableBox( pStartNd->GetIndex() );
2455 const OUString aEndBox( pEndBox->GetName() );
2456
2457 pStartNd = pUnoTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2458 pStartBox = pTable->GetTableBox( pStartNd->GetIndex() );
2459 const OUString aStartBox( pStartBox->GetName() );
2460
2461 SwRangeDescriptor aDesc;
2462 // note that cell range here takes the newly added rows/cols already into account
2463 FillRangeDescriptor( aDesc, aStartBox + ":" + aEndBox );
2464
2465 bool bChanged = false;
2466 OUString aNewStartCell;
2467 OUString aNewEndCell;
2468 if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
2469 {
2470 // new column cells adjacent to the bottom of the
2471 // current data-sequence to be added...
2472 OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2473 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2474 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
2475 bChanged = true;
2476 }
2477 else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
2478 {
2479 // new column cells adjacent to the top of the
2480 // current data-sequence to be added...
2481 OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2482 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop - nCount);
2483 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2484 bChanged = true;
2485 }
2486 else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
2487 {
2488 // new row cells adjacent to the right of the
2489 // current data-sequence to be added...
2490 OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2491 aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
2492 aNewEndCell = sw_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
2493 bChanged = true;
2494 }
2495 else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
2496 {
2497 // new row cells adjacent to the left of the
2498 // current data-sequence to be added...
2499 OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2500 aNewStartCell = sw_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
2501 aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
2502 bChanged = true;
2503 }
2504
2505 if (bChanged)
2506 {
2507 // move table cursor to new start and end of data-sequence
2508 const SwTableBox *pNewStartBox = pTable->GetTableBox( aNewStartCell );
2509 const SwTableBox *pNewEndBox = pTable->GetTableBox( aNewEndCell );
2510 pUnoTableCursor->SetMark();
2511 pUnoTableCursor->GetPoint()->nNode = *pNewEndBox->GetSttNd();
2512 pUnoTableCursor->GetMark()->nNode = *pNewStartBox->GetSttNd();
2513 pUnoTableCursor->Move( fnMoveForward, GoInNode );
2514 pUnoTableCursor->MakeBoxSels();
2515 }
2516 }
2517
SwChartLabeledDataSequence()2518 SwChartLabeledDataSequence::SwChartLabeledDataSequence() :
2519 m_aEventListeners( GetChartMutex() ),
2520 m_aModifyListeners( GetChartMutex() )
2521 {
2522 m_bDisposed = false;
2523 }
2524
~SwChartLabeledDataSequence()2525 SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
2526 {
2527 }
2528
getValues()2529 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( )
2530 {
2531 SolarMutexGuard aGuard;
2532 if (m_bDisposed)
2533 throw lang::DisposedException();
2534 return m_xData;
2535 }
2536
SetDataSequence(uno::Reference<chart2::data::XDataSequence> & rxDest,const uno::Reference<chart2::data::XDataSequence> & rxSource)2537 void SwChartLabeledDataSequence::SetDataSequence(
2538 uno::Reference< chart2::data::XDataSequence >& rxDest,
2539 const uno::Reference< chart2::data::XDataSequence >& rxSource)
2540 {
2541 uno::Reference< util::XModifyListener > xML( static_cast< util::XModifyListener* >(this), uno::UNO_QUERY );
2542 uno::Reference< lang::XEventListener > xEL( static_cast< lang::XEventListener* >(this), uno::UNO_QUERY );
2543
2544 // stop listening to old data-sequence
2545 uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
2546 if (xMB.is())
2547 xMB->removeModifyListener( xML );
2548 uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
2549 if (xC.is())
2550 xC->removeEventListener( xEL );
2551
2552 rxDest = rxSource;
2553
2554 // start listening to new data-sequence
2555 xC.set( rxDest, uno::UNO_QUERY );
2556 if (xC.is())
2557 xC->addEventListener( xEL );
2558 xMB.set( rxDest, uno::UNO_QUERY );
2559 if (xMB.is())
2560 xMB->addModifyListener( xML );
2561 }
2562
setValues(const uno::Reference<chart2::data::XDataSequence> & rxSequence)2563 void SAL_CALL SwChartLabeledDataSequence::setValues(
2564 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2565 {
2566 SolarMutexGuard aGuard;
2567 if (m_bDisposed)
2568 throw lang::DisposedException();
2569
2570 if (m_xData != rxSequence)
2571 {
2572 SetDataSequence( m_xData, rxSequence );
2573 // inform listeners of changes
2574 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2575 }
2576 }
2577
getLabel()2578 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( )
2579 {
2580 SolarMutexGuard aGuard;
2581 if (m_bDisposed)
2582 throw lang::DisposedException();
2583 return m_xLabels;
2584 }
2585
setLabel(const uno::Reference<chart2::data::XDataSequence> & rxSequence)2586 void SAL_CALL SwChartLabeledDataSequence::setLabel(
2587 const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2588 {
2589 SolarMutexGuard aGuard;
2590 if (m_bDisposed)
2591 throw lang::DisposedException();
2592
2593 if (m_xLabels != rxSequence)
2594 {
2595 SetDataSequence( m_xLabels, rxSequence );
2596 // inform listeners of changes
2597 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2598 }
2599 }
2600
createClone()2601 uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( )
2602 {
2603 SolarMutexGuard aGuard;
2604 if (m_bDisposed)
2605 throw lang::DisposedException();
2606
2607 uno::Reference< util::XCloneable > xDataCloneable( m_xData, uno::UNO_QUERY );
2608 uno::Reference< util::XCloneable > xLabelsCloneable( m_xLabels, uno::UNO_QUERY );
2609 rtl::Reference<SwChartLabeledDataSequence > pRes = new SwChartLabeledDataSequence();
2610 if (xDataCloneable.is())
2611 {
2612 uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
2613 pRes->setValues( xDataClone );
2614 }
2615
2616 if (xLabelsCloneable.is())
2617 {
2618 uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
2619 pRes->setLabel( xLabelsClone );
2620 }
2621 return pRes;
2622 }
2623
getImplementationName()2624 OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( )
2625 {
2626 return "SwChartLabeledDataSequence";
2627 }
2628
supportsService(const OUString & rServiceName)2629 sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
2630 const OUString& rServiceName )
2631 {
2632 return cppu::supportsService(this, rServiceName);
2633 }
2634
getSupportedServiceNames()2635 uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( )
2636 {
2637 return { "com.sun.star.chart2.data.LabeledDataSequence" };
2638 }
2639
disposing(const lang::EventObject & rSource)2640 void SAL_CALL SwChartLabeledDataSequence::disposing(
2641 const lang::EventObject& rSource )
2642 {
2643 osl::MutexGuard aGuard( GetChartMutex() );
2644 uno::Reference< uno::XInterface > xRef( rSource.Source );
2645 if (xRef == m_xData)
2646 m_xData.clear();
2647 if (xRef == m_xLabels)
2648 m_xLabels.clear();
2649 if (!m_xData.is() && !m_xLabels.is())
2650 dispose();
2651 }
2652
modified(const lang::EventObject & rEvent)2653 void SAL_CALL SwChartLabeledDataSequence::modified(
2654 const lang::EventObject& rEvent )
2655 {
2656 if (rEvent.Source == m_xData || rEvent.Source == m_xLabels)
2657 {
2658 LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
2659 }
2660 }
2661
addModifyListener(const uno::Reference<util::XModifyListener> & rxListener)2662 void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
2663 const uno::Reference< util::XModifyListener >& rxListener )
2664 {
2665 osl::MutexGuard aGuard( GetChartMutex() );
2666 if (!m_bDisposed && rxListener.is())
2667 m_aModifyListeners.addInterface( rxListener );
2668 }
2669
removeModifyListener(const uno::Reference<util::XModifyListener> & rxListener)2670 void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
2671 const uno::Reference< util::XModifyListener >& rxListener )
2672 {
2673 osl::MutexGuard aGuard( GetChartMutex() );
2674 if (!m_bDisposed && rxListener.is())
2675 m_aModifyListeners.removeInterface( rxListener );
2676 }
2677
dispose()2678 void SAL_CALL SwChartLabeledDataSequence::dispose( )
2679 {
2680 bool bMustDispose( false );
2681 {
2682 osl::MutexGuard aGuard( GetChartMutex() );
2683 bMustDispose = !m_bDisposed;
2684 if (!m_bDisposed)
2685 m_bDisposed = true;
2686 }
2687 if (bMustDispose)
2688 {
2689 m_bDisposed = true;
2690
2691 // require listeners to release references to this object
2692 lang::EventObject aEvtObj( static_cast< chart2::data::XLabeledDataSequence * >(this) );
2693 m_aModifyListeners.disposeAndClear( aEvtObj );
2694 m_aEventListeners.disposeAndClear( aEvtObj );
2695 }
2696 }
2697
addEventListener(const uno::Reference<lang::XEventListener> & rxListener)2698 void SAL_CALL SwChartLabeledDataSequence::addEventListener(
2699 const uno::Reference< lang::XEventListener >& rxListener )
2700 {
2701 osl::MutexGuard aGuard( GetChartMutex() );
2702 if (!m_bDisposed && rxListener.is())
2703 m_aEventListeners.addInterface( rxListener );
2704 }
2705
removeEventListener(const uno::Reference<lang::XEventListener> & rxListener)2706 void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
2707 const uno::Reference< lang::XEventListener >& rxListener )
2708 {
2709 osl::MutexGuard aGuard( GetChartMutex() );
2710 if (!m_bDisposed && rxListener.is())
2711 m_aEventListeners.removeInterface( rxListener );
2712 }
2713
2714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2715