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