xref: /aoo4110/main/sw/source/core/unocore/unochart.cxx (revision b1cdbd2c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 #include <memory>
28 #include <algorithm>
29 
30 #include <com/sun/star/chart/ChartDataRowSource.hpp>
31 #include <com/sun/star/chart2/data/LabelOrigin.hpp>
32 #include <cppuhelper/interfacecontainer.hxx>
33 #include <vos/mutex.hxx>
34 #include <osl/mutex.hxx>
35 #include <vcl/svapp.hxx>
36 #include <svl/zforlist.hxx>     // SvNumberFormatter
37 #include <svx/charthelper.hxx>
38 
39 #include <tools/link.hxx>
40 
41 #include <XMLRangeHelper.hxx>
42 #include <unochart.hxx>
43 #include <swtable.hxx>
44 #include <unoprnms.hxx>
45 #include <unomap.hxx>
46 #include <unomid.h>
47 #include <unocrsr.hxx>
48 #include <unotbl.hxx>
49 #include <doc.hxx>
50 #include <frmfmt.hxx>
51 #include <docsh.hxx>
52 #include <ndole.hxx>
53 #include <swtable.hxx>
54 #include <swtypes.hxx>
55 #ifndef _UNOCORE_HRC
56 #include <unocore.hrc>
57 #endif
58 
59 #include <docary.hxx>
60 
61 #define SN_DATA_PROVIDER            "com.sun.star.chart2.data.DataProvider"
62 #define SN_DATA_SOURCE              "com.sun.star.chart2.data.DataSource"
63 #define SN_DATA_SEQUENCE            "com.sun.star.chart2.data.DataSequence"
64 #define SN_LABELED_DATA_SEQUENCE    "com.sun.star.chart2.data.LabeledDataSequence"
65 
66 #define DIRECTION_DONT_KNOW     -1
67 #define DIRECTION_HAS_ERROR     -2
68 #define DIRECTION_COLS           0
69 #define DIRECTION_ROWS           1
70 
71 using namespace ::com::sun::star;
72 using ::rtl::OUString;
73 
74 // from unotbl.cxx
75 extern void lcl_GetCellPosition( const String &rCellName, sal_Int32 &rColumn, sal_Int32 &rRow);
76 extern String lcl_GetCellName( sal_Int32 nColumn, sal_Int32 nRow );
77 extern int lcl_CompareCellsByColFirst( const String &rCellName1, const String &rCellName2 );
78 extern int lcl_CompareCellsByRowFirst( const String &rCellName1, const String &rCellName2 );
79 extern int lcl_CompareCellRanges(
80         const String &rRange1StartCell, const String &rRange1EndCell,
81         const String &rRange2StartCell, const String &rRange2EndCell,
82         sal_Bool bCmpColsFirst );
83 extern void lcl_NormalizeRange( String &rCell1, String &rCell2 );
84 
85 //////////////////////////////////////////////////////////////////////
86 
87 //static
DoUpdateAllCharts(SwDoc * pDoc)88 void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
89 {
90     if (!pDoc)
91         return;
92 
93     uno::Reference< frame::XModel > xRes;
94 
95     SwOLENode *pONd;
96     SwStartNode *pStNd;
97     SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
98     while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
99     {
100         aIdx++;
101         if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
102             ChartHelper::IsChart( pONd->GetOLEObj().GetObject() ) )
103         {
104             // Load the object and set modified
105 
106             uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
107             if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
108             {
109                 try
110                 {
111                     uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
112                     xModif->setModified( sal_True );
113                 }
114                 catch ( uno::Exception& )
115                 {
116                 }
117 
118             }
119         }
120         aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
121     }
122 }
123 
124 //////////////////////////////////////////////////////////////////////
125 
SwChartLockController_Helper(SwDoc * pDocument)126 SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
127     pDoc( pDocument )
128 {
129     aUnlockTimer.SetTimeout( 1500 );
130     aUnlockTimer.SetTimeoutHdl( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
131 }
132 
133 
~SwChartLockController_Helper()134 SwChartLockController_Helper::~SwChartLockController_Helper()
135 {
136     if (pDoc)   // still connected?
137         Disconnect();
138 }
139 
140 
StartOrContinueLocking()141 void SwChartLockController_Helper::StartOrContinueLocking()
142 {
143     if (!bIsLocked)
144         LockAllCharts();
145     aUnlockTimer.Start();   // start or continue time of locking
146 }
147 
148 
Disconnect()149 void SwChartLockController_Helper::Disconnect()
150 {
151     aUnlockTimer.Stop();
152     UnlockAllCharts();
153     pDoc = 0;
154 }
155 
156 
LockUnlockAllCharts(sal_Bool bLock)157 void SwChartLockController_Helper::LockUnlockAllCharts( sal_Bool bLock )
158 {
159     if (!pDoc)
160         return;
161 
162     const SwFrmFmts& rTblFmts = *pDoc->GetTblFrmFmts();
163     for( sal_uInt16 n = 0; n < rTblFmts.Count(); ++n )
164     {
165         SwTable* pTmpTbl;
166         const SwTableNode* pTblNd;
167         SwFrmFmt* pFmt = rTblFmts[ n ];
168 
169         if( 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) &&
170             0 != ( pTblNd = pTmpTbl->GetTableNode() ) &&
171             pTblNd->GetNodes().IsDocNodes() )
172         {
173             uno::Reference< frame::XModel > xRes;
174 
175             String aName( pTmpTbl->GetFrmFmt()->GetName() );
176             SwOLENode *pONd;
177             SwStartNode *pStNd;
178             SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
179             while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
180             {
181                 aIdx++;
182                 if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
183                     pONd->GetChartTblName().Len() > 0 /* is chart object? */)
184                 {
185                     uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
186                     if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
187                     {
188                         xRes = uno::Reference < frame::XModel >( xIP->getComponent(), uno::UNO_QUERY );
189                         if (xRes.is())
190                         {
191                             if (bLock)
192                                 xRes->lockControllers();
193                             else
194                                 xRes->unlockControllers();
195                         }
196                     }
197                 }
198                 aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
199             }
200         }
201     }
202 
203     bIsLocked = bLock;
204 }
205 
206 
207 IMPL_LINK( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, /*pTimer*/ )
208 {
209     UnlockAllCharts();
210     return 0;
211 }
212 
213 
214 //////////////////////////////////////////////////////////////////////
215 
GetChartMutex()216 static osl::Mutex &    GetChartMutex()
217 {
218     static osl::Mutex   aMutex;
219     return aMutex;
220 }
221 
222 
LaunchModifiedEvent(::cppu::OInterfaceContainerHelper & rICH,const uno::Reference<uno::XInterface> & rxI)223 static void LaunchModifiedEvent(
224 		::cppu::OInterfaceContainerHelper &rICH,
225 		const uno::Reference< uno::XInterface > &rxI )
226 {
227     lang::EventObject aEvtObj( rxI );
228     cppu::OInterfaceIteratorHelper aIt( rICH );
229     while (aIt.hasMoreElements())
230     {
231         uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY );
232         if (xRef.is())
233             xRef->modified( aEvtObj );
234     }
235 }
236 
237 //////////////////////////////////////////////////////////////////////
238 
239 // rCellRangeName needs to be of one of the following formats:
240 // - e.g. "A2:E5" or
241 // - e.g. "Table1.A2:E5"
FillRangeDescriptor(SwRangeDescriptor & rDesc,const String & rCellRangeName)242 sal_Bool FillRangeDescriptor(
243         SwRangeDescriptor &rDesc,
244         const String &rCellRangeName )
245 {
246 	xub_StrLen nToken = STRING_NOTFOUND == rCellRangeName.Search('.') ? 0 : 1;
247 	String aCellRangeNoTableName( rCellRangeName.GetToken( nToken, '.' ) );
248     String aTLName( aCellRangeNoTableName.GetToken(0, ':') );  // name of top left cell
249     String aBRName( aCellRangeNoTableName.GetToken(1, ':') );  // name of bottom right cell
250     if(!aTLName.Len() || !aBRName.Len())
251         return sal_False;
252 
253     rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
254     lcl_GetCellPosition( aTLName, rDesc.nLeft,  rDesc.nTop );
255     lcl_GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
256     rDesc.Normalize();
257     DBG_ASSERT( rDesc.nTop    != -1 &&
258                 rDesc.nLeft   != -1 &&
259                 rDesc.nBottom != -1 &&
260                 rDesc.nRight  != -1,
261             "failed to get range descriptor" );
262     DBG_ASSERT( rDesc.nTop <= rDesc.nBottom  &&  rDesc.nLeft <= rDesc.nRight,
263             "invalid range descriptor");
264     return sal_True;
265 }
266 
267 
GetCellRangeName(SwFrmFmt & rTblFmt,SwUnoCrsr & rTblCrsr)268 static String GetCellRangeName( SwFrmFmt &rTblFmt, SwUnoCrsr &rTblCrsr )
269 {
270     String aRes;
271 
272     //!! see also SwXTextTableCursor::getRangeName
273 
274     SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(&rTblCrsr);
275     if (!pUnoTblCrsr)
276         return String();
277     pUnoTblCrsr->MakeBoxSels();
278 
279     const SwStartNode*  pStart;
280     const SwTableBox*   pStartBox   = 0;
281     const SwTableBox*   pEndBox     = 0;
282 
283     pStart = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
284     if (pStart)
285     {
286         const SwTable* pTable = SwTable::FindTable( &rTblFmt );
287         pEndBox = pTable->GetTblBox( pStart->GetIndex());
288         aRes = pEndBox->GetName();
289 
290         if(pUnoTblCrsr->HasMark())
291         {
292             pStart = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
293             pStartBox = pTable->GetTblBox( pStart->GetIndex());
294         }
295         DBG_ASSERT( pStartBox, "start box not found" );
296         DBG_ASSERT( pEndBox, "end box not found" );
297         // need to switch start and end?
298         if (*pUnoTblCrsr->GetPoint() < *pUnoTblCrsr->GetMark())
299         {
300             const SwTableBox* pTmpBox = pStartBox;
301             pStartBox = pEndBox;
302             pEndBox = pTmpBox;
303         }
304 
305         aRes = pStartBox->GetName();
306         aRes += (sal_Unicode)':';
307         if (pEndBox)
308             aRes += pEndBox->GetName();
309         else
310             aRes += pStartBox->GetName();
311     }
312 
313     return aRes;
314 }
315 
316 
GetRangeRepFromTableAndCells(const String & rTableName,const String & rStartCell,const String & rEndCell,sal_Bool bForceEndCellName)317 static String GetRangeRepFromTableAndCells( const String &rTableName,
318         const String &rStartCell, const String &rEndCell,
319         sal_Bool bForceEndCellName )
320 {
321     DBG_ASSERT( rTableName.Len(), "table name missing" );
322     DBG_ASSERT( rStartCell.Len(), "cell name missing" );
323     String aRes( rTableName );
324     aRes += (sal_Unicode) '.';
325     aRes += rStartCell;
326 
327     if (rEndCell.Len())
328     {
329         aRes += (sal_Unicode) ':';
330         aRes += rEndCell;
331     }
332     else if (bForceEndCellName)
333     {
334         aRes += (sal_Unicode) ':';
335         aRes += rStartCell;
336     }
337 
338     return aRes;
339 }
340 
341 
GetTableAndCellsFromRangeRep(const OUString & rRangeRepresentation,String & rTblName,String & rStartCell,String & rEndCell,sal_Bool bSortStartEndCells=sal_True)342 static sal_Bool GetTableAndCellsFromRangeRep(
343         const OUString &rRangeRepresentation,
344         String &rTblName,
345         String &rStartCell,
346         String &rEndCell,
347         sal_Bool bSortStartEndCells = sal_True )
348 {
349     // parse range representation for table name and cell/range names
350     // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
351     String aTblName;    // table name
352     OUString aRange;    // cell range
353     String aStartCell;  // name of top left cell
354     String aEndCell;    // name of bottom right cell
355     sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' );
356     if (nIdx >= 0)
357     {
358         aTblName = rRangeRepresentation.copy( 0, nIdx );
359         aRange = rRangeRepresentation.copy( nIdx + 1 );
360 		sal_Int32 nPos = aRange.indexOf( ':' );
361         if (nPos >= 0) // a cell-range like "Table1.A2:D4"
362         {
363             aStartCell = aRange.copy( 0, nPos );
364             aEndCell   = aRange.copy( nPos + 1 );
365 
366             // need to switch start and end cell ?
367             // (does not check for normalization here)
368             if (bSortStartEndCells && 1 == lcl_CompareCellsByColFirst( aStartCell, aEndCell ))
369             {
370                 String aTmp( aStartCell );
371                 aStartCell  = aEndCell;
372                 aEndCell    = aTmp;
373             }
374         }
375 		else	// a single cell like in "Table1.B3"
376 		{
377 			aStartCell = aEndCell = aRange;
378 		}
379     }
380 
381     sal_Bool bSuccess = aTblName.Len() != 0 &&
382                         aStartCell.Len() != 0 && aEndCell.Len() != 0;
383     if (bSuccess)
384     {
385         rTblName    = aTblName;
386         rStartCell  = aStartCell;
387         rEndCell    = aEndCell;
388     }
389     return bSuccess;
390 }
391 
392 
GetTableByName(const SwDoc & rDoc,const String & rTableName,SwFrmFmt ** ppTblFmt,SwTable ** ppTable)393 static void GetTableByName( const SwDoc &rDoc, const String &rTableName,
394         SwFrmFmt **ppTblFmt, SwTable **ppTable)
395 {
396     SwFrmFmt *pTblFmt = NULL;
397 
398     // find frame format of table
399     //! see SwXTextTables::getByName
400     sal_uInt16 nCount = rDoc.GetTblFrmFmtCount(sal_True);
401     for (sal_uInt16 i = 0; i < nCount && !pTblFmt; ++i)
402     {
403         SwFrmFmt& rTblFmt = rDoc.GetTblFrmFmt(i, sal_True);
404         if(rTableName == rTblFmt.GetName())
405             pTblFmt = &rTblFmt;
406     }
407 
408     if (ppTblFmt)
409         *ppTblFmt = pTblFmt;
410 
411     if (ppTable)
412         *ppTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
413 }
414 
415 
GetFormatAndCreateCursorFromRangeRep(const SwDoc * pDoc,const OUString & rRangeRepresentation,SwFrmFmt ** ppTblFmt,SwUnoCrsr ** ppUnoCrsr)416 static void GetFormatAndCreateCursorFromRangeRep(
417         const SwDoc    *pDoc,
418         const OUString &rRangeRepresentation,   // must be a single range (i.e. so called sub-range)
419         SwFrmFmt    **ppTblFmt,     // will be set to the table format of the table used in the range representation
420         SwUnoCrsr   **ppUnoCrsr )   // will be set to cursor spanning the cell range
421                                     // (cursor will be created!)
422 {
423     String aTblName;    // table name
424     String aStartCell;  // name of top left cell
425     String aEndCell;    // name of bottom right cell
426     sal_Bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
427                                   aTblName, aStartCell, aEndCell );
428 
429     if (!bNamesFound)
430     {
431 		if (ppTblFmt)
432 			*ppTblFmt   = NULL;
433 		if (ppUnoCrsr)
434 			*ppUnoCrsr  = NULL;
435     }
436     else
437     {
438         SwFrmFmt *pTblFmt = NULL;
439 
440         // is the correct table format already provided?
441         if (*ppTblFmt != NULL  &&  (*ppTblFmt)->GetName() == aTblName)
442             pTblFmt = *ppTblFmt;
443         else if (ppTblFmt)
444             GetTableByName( *pDoc, aTblName, &pTblFmt, NULL );
445 
446 		if (ppTblFmt)
447 			*ppTblFmt = pTblFmt;
448 
449         if (ppUnoCrsr != NULL)
450         {
451             *ppUnoCrsr = NULL;  // default result in case of failure
452 
453             SwTable *pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
454             // create new SwUnoCrsr spanning the specified range
455             //! see also SwXTextTable::GetRangeByName
456             // --> OD 2007-08-03 #i80314#
457             // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
458             const SwTableBox* pTLBox =
459                             pTable ? pTable->GetTblBox( aStartCell, true ) : 0;
460             // <--
461             if(pTLBox)
462             {
463                 // hier muessen die Actions aufgehoben werden
464                 UnoActionRemoveContext aRemoveContext(pTblFmt->GetDoc());
465                 const SwStartNode* pSttNd = pTLBox->GetSttNd();
466                 SwPosition aPos(*pSttNd);
467                 // set cursor to top left box of range
468                 SwUnoCrsr* pUnoCrsr = pTblFmt->GetDoc()->CreateUnoCrsr(aPos, sal_True);
469                 pUnoCrsr->Move( fnMoveForward, fnGoNode );
470                 pUnoCrsr->SetRemainInSection( sal_False );
471                 // --> OD 2007-08-03 #i80314#
472                 // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
473                 const SwTableBox* pBRBox = pTable->GetTblBox( aEndCell, true );
474                 // <--
475                 if(pBRBox)
476                 {
477                     pUnoCrsr->SetMark();
478                     pUnoCrsr->GetPoint()->nNode = *pBRBox->GetSttNd();
479                     pUnoCrsr->Move( fnMoveForward, fnGoNode );
480                     SwUnoTableCrsr* pCrsr =
481                         dynamic_cast<SwUnoTableCrsr*>(pUnoCrsr);
482                     pCrsr->MakeBoxSels();
483 
484                     if (ppUnoCrsr)
485                         *ppUnoCrsr = pCrsr;
486                 }
487                 else
488                 {
489                     delete pUnoCrsr;
490                 }
491             }
492         }
493     }
494 }
495 
496 
GetSubranges(const OUString & rRangeRepresentation,uno::Sequence<OUString> & rSubRanges,sal_Bool bNormalize)497 static sal_Bool GetSubranges( const OUString &rRangeRepresentation,
498         uno::Sequence< OUString > &rSubRanges, sal_Bool bNormalize )
499 {
500     sal_Bool bRes = sal_True;
501     String aRangesStr( rRangeRepresentation );
502     xub_StrLen nLen = aRangesStr.GetTokenCount( ';' );
503     uno::Sequence< OUString > aRanges( nLen );
504 
505     sal_Int32 nCnt = 0;
506     if (nLen != 0)
507     {
508         OUString *pRanges = aRanges.getArray();
509         String aFirstTable;
510         for ( xub_StrLen i = 0;  i < nLen && bRes;  ++i)
511         {
512             String aRange( aRangesStr.GetToken( i, ';' ) );
513             if (aRange.Len())
514             {
515                 pRanges[nCnt] = aRange;
516 
517                 String aTableName, aStartCell, aEndCell;
518                 bRes &= GetTableAndCellsFromRangeRep( aRange,
519                                 aTableName, aStartCell, aEndCell );
520 
521                 if (bNormalize)
522                 {
523                     lcl_NormalizeRange( aStartCell, aEndCell );
524                     pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
525                                     aStartCell, aEndCell, sal_True );
526                 }
527 
528                 // make sure to use only a single table
529                 if (nCnt == 0)
530                     aFirstTable = aTableName;
531                 else
532                     bRes &= aFirstTable == aTableName;
533 
534                 ++nCnt;
535             }
536         }
537     }
538     aRanges.realloc( nCnt );
539 
540     rSubRanges = aRanges;
541     return bRes;
542 }
543 
544 
SortSubranges(uno::Sequence<OUString> & rSubRanges,sal_Bool bCmpByColumn)545 static void SortSubranges( uno::Sequence< OUString > &rSubRanges, sal_Bool bCmpByColumn )
546 {
547     sal_Int32 nLen = rSubRanges.getLength();
548     OUString *pSubRanges = rSubRanges.getArray();
549 
550     String aSmallestTblName;
551     String aSmallestStartCell;
552     String aSmallestEndCell;
553 
554     for (sal_Int32 i = 0;  i < nLen;  ++i)
555     {
556 		sal_Int32 nIdxOfSmallest = i;
557 		GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
558 				aSmallestTblName, aSmallestStartCell, aSmallestEndCell );
559 		if (aSmallestEndCell.Len() == 0)
560 			aSmallestEndCell = aSmallestStartCell;
561 
562         for (sal_Int32 k = i+1;  k < nLen;  ++k)
563         {
564             // get cell names for sub range
565             String aTblName;
566             String aStartCell;
567             String aEndCell;
568             GetTableAndCellsFromRangeRep( pSubRanges[k],
569                     aTblName, aStartCell, aEndCell );
570             if (aEndCell.Len() == 0)
571                 aEndCell = aStartCell;
572 
573             // compare cell ranges ( is the new one smaller? )
574             if (-1 == lcl_CompareCellRanges( aStartCell, aEndCell,
575                                 aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
576             {
577                 nIdxOfSmallest = k;
578                 aSmallestTblName    = aTblName;
579                 aSmallestStartCell  = aStartCell;
580                 aSmallestEndCell    = aEndCell;
581             }
582         }
583 
584         // move smallest element to the start of the not sorted area
585         OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
586         pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
587         pSubRanges[ i ] = aTmp;
588     }
589 }
590 
591 //////////////////////////////////////////////////////////////////////
592 
SwChartDataProvider(const SwDoc * pSwDoc)593 SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) :
594     aEvtListeners( GetChartMutex() ),
595     pDoc( pSwDoc )
596 {
597     bDisposed = sal_False;
598 }
599 
600 
~SwChartDataProvider()601 SwChartDataProvider::~SwChartDataProvider()
602 {
603 }
604 
Impl_createDataSource(const uno::Sequence<beans::PropertyValue> & rArguments,sal_Bool bTestOnly)605 uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
606         const uno::Sequence< beans::PropertyValue >& rArguments, sal_Bool bTestOnly )
607     throw (lang::IllegalArgumentException, uno::RuntimeException)
608 {
609     vos::OGuard aGuard( Application::GetSolarMutex() );
610     if (bDisposed)
611         throw lang::DisposedException();
612 
613     uno::Reference< chart2::data::XDataSource > xRes;
614 
615     if (!pDoc)
616         throw uno::RuntimeException();
617 
618     // get arguments
619     OUString aRangeRepresentation;
620     uno::Sequence< sal_Int32 > aSequenceMapping;
621     sal_Bool bFirstIsLabel      = sal_False;
622     sal_Bool bDtaSrcIsColumns   = sal_True; // true : DataSource will be sequence of columns
623                                             // false: DataSource will be sequence of rows
624     OUString aChartOleObjectName;//work around wrong writer ranges ( see Issue 58464 )
625     sal_Int32 nArgs = rArguments.getLength();
626     DBG_ASSERT( nArgs != 0, "no properties provided" );
627     if (nArgs == 0)
628         return xRes;
629     const beans::PropertyValue *pArg = rArguments.getConstArray();
630     for (sal_Int32 i = 0;  i < nArgs;  ++i)
631     {
632         if (pArg[i].Name.equalsAscii( "DataRowSource" ))
633         {
634             chart::ChartDataRowSource eSource;
635             if (!(pArg[i].Value >>= eSource))
636             {
637                 sal_Int32 nTmp = 0;
638                 if (!(pArg[i].Value >>= nTmp))
639                     throw lang::IllegalArgumentException();
640                 eSource = static_cast< chart::ChartDataRowSource >( nTmp );
641             }
642             bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
643         }
644         else if (pArg[i].Name.equalsAscii( "FirstCellAsLabel" ))
645         {
646             if (!(pArg[i].Value >>= bFirstIsLabel))
647                 throw lang::IllegalArgumentException();
648         }
649         else if (pArg[i].Name.equalsAscii( "CellRangeRepresentation" ))
650         {
651             if (!(pArg[i].Value >>= aRangeRepresentation))
652                 throw lang::IllegalArgumentException();
653         }
654         else if (pArg[i].Name.equalsAscii( "SequenceMapping" ))
655         {
656             if (!(pArg[i].Value >>= aSequenceMapping))
657                 throw lang::IllegalArgumentException();
658         }
659         else if (pArg[i].Name.equalsAscii( "ChartOleObjectName" ))
660         {
661             if (!(pArg[i].Value >>= aChartOleObjectName))
662                 throw lang::IllegalArgumentException();
663         }
664 	}
665 
666 	uno::Sequence< OUString > aSubRanges;
667     // get sub-ranges and check that they all are from the very same table
668     sal_Bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
669 
670     if (!bOk && pDoc && aChartOleObjectName.getLength() )
671     {
672         //try to correct the range here
673         //work around wrong writer ranges ( see Issue 58464 )
674         String aChartTableName;
675 
676         const SwNodes& rNodes = pDoc->GetNodes();
677         for( sal_uLong nN = rNodes.Count(); nN--; )
678         {
679             SwNodePtr pNode = rNodes[nN];
680             if( !pNode )
681                 continue;
682             const SwOLENode* pOleNode = pNode->GetOLENode();
683             if( !pOleNode )
684                 continue;
685             const SwOLEObj& rOObj = pOleNode->GetOLEObj();
686             if( aChartOleObjectName.equals( rOObj.GetCurrentPersistName() ) )
687             {
688                 aChartTableName = pOleNode->GetChartTblName();
689                 break;
690             }
691         }
692 
693         if( aChartTableName.Len() )
694         {
695             //the wrong range is still shifted one row down
696             //thus the first row is missing and an invalid row at the end is added.
697             //Therefore we need to shift the range one row up
698             SwRangeDescriptor aDesc;
699             if (aRangeRepresentation.getLength() == 0)
700                 return xRes;        // we cant handle this thus returning an empty references
701             aRangeRepresentation = aRangeRepresentation.copy( 1 );    // get rid of '.' to have only the cell range left
702             FillRangeDescriptor( aDesc, aRangeRepresentation );
703             aDesc.Normalize();
704             if (aDesc.nTop <= 0)    // no chance to shift the range one row up?
705                 return xRes;        // we cant handle this thus returning an empty references
706             aDesc.nTop      -= 1;
707             aDesc.nBottom   -= 1;
708 
709             String aNewStartCell( lcl_GetCellName( aDesc.nLeft, aDesc.nTop ) );
710             String aNewEndCell( lcl_GetCellName( aDesc.nRight, aDesc.nBottom ) );
711             aRangeRepresentation = GetRangeRepFromTableAndCells(
712                         aChartTableName, aNewStartCell, aNewEndCell, sal_True );
713             bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
714         }
715     }
716     if (!bOk)    // different tables used, or incorrect range specifiers
717         throw lang::IllegalArgumentException();
718 
719     SortSubranges( aSubRanges, bDtaSrcIsColumns );
720     const OUString *pSubRanges = aSubRanges.getConstArray();
721 #if OSL_DEBUG_LEVEL > 1
722     {
723         sal_Int32 nSR = aSubRanges.getLength();
724         OUString *pSR = aSubRanges.getArray();
725         OUString aRg;
726         for (sal_Int32 i = 0;  i < nSR;  ++i)
727         {
728             aRg = pSR[i];
729         }
730     }
731 #endif
732 
733     // get table format for that single table from above
734     SwFrmFmt    *pTblFmt  = 0;      // pointer to table format
735     SwUnoCrsr   *pUnoCrsr = 0;      // here required to check if the cells in the range do actually exist
736     std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr );  // to end lifetime of object pointed to by pUnoCrsr
737     if (aSubRanges.getLength() > 0)
738         GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTblFmt, &pUnoCrsr );
739     if (!pTblFmt || !pUnoCrsr)
740         throw lang::IllegalArgumentException();
741 
742     if(pTblFmt)
743     {
744         SwTable* pTable = SwTable::FindTable( pTblFmt );
745         if(pTable->IsTblComplex())
746             return xRes;    // we cant handle this thus returning an empty references
747         else
748         {
749             // get a character map in the size of the table to mark
750             // all the ranges to use in
751             sal_Int32 nRows = pTable->GetTabLines().Count();
752             sal_Int32 nCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count();
753             std::vector< std::vector< sal_Char > > aMap( nRows );
754             for (sal_Int32 i = 0;  i < nRows;  ++i)
755                 aMap[i].resize( nCols );
756 
757             // iterate over subranges and mark used cells in above map
758             //!! by proceeding this way we automatically get rid of
759             //!! multiple listed or overlapping cell ranges which should
760             //!! just be ignored silently
761             sal_Int32 nSubRanges = aSubRanges.getLength();
762             for (sal_Int32 i = 0;  i < nSubRanges;  ++i)
763             {
764                 String aTblName, aStartCell, aEndCell;
765                 sal_Bool bOk2 = GetTableAndCellsFromRangeRep(
766                                     pSubRanges[i], aTblName, aStartCell, aEndCell );
767                 (void) bOk2;
768                 DBG_ASSERT( bOk2, "failed to get table and start/end cells" );
769 
770                 sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
771                 lcl_GetCellPosition( aStartCell, nStartCol, nStartRow );
772                 lcl_GetCellPosition( aEndCell,   nEndCol,   nEndRow );
773                 DBG_ASSERT( nStartRow <= nEndRow && nStartCol <= nEndCol,
774                         "cell range not normalized");
775 
776                 // test if the ranges span more than the available cells
777                 if( nStartRow < 0 || nEndRow >= nRows ||
778                     nStartCol < 0 || nEndCol >= nCols )
779                 {
780                     throw lang::IllegalArgumentException();
781                 }
782                 for (sal_Int32 k1 = nStartRow;  k1 <= nEndRow;  ++k1)
783                 {
784                     for (sal_Int32 k2 = nStartCol;  k2 <= nEndCol;  ++k2)
785                         aMap[k1][k2] = 'x';
786                 }
787             }
788 
789             //
790             // find label and data sequences to use
791             //
792             sal_Int32 oi;  // outer index (slower changing index)
793             sal_Int32 ii;  // inner index (faster changing index)
794             sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
795             sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
796             std::vector< sal_Int32 > aLabelIdx( oiEnd );
797             std::vector< sal_Int32 > aDataStartIdx( oiEnd );
798             std::vector< sal_Int32 > aDataLen( oiEnd );
799             for (oi = 0;  oi < oiEnd;  ++oi)
800             {
801                 aLabelIdx[oi]       = -1;
802                 aDataStartIdx[oi]   = -1;
803                 aDataLen[oi]        = 0;
804             }
805             //
806             for (oi = 0;  oi < oiEnd;  ++oi)
807             {
808                 ii = 0;
809                 while (ii < iiEnd)
810                 {
811                     sal_Char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
812 
813                     // label should be used but is not yet found?
814                     if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
815                     {
816                         aLabelIdx[oi] = ii;
817                         rChar = 'L';    // setting a different char for labels here
818                                         // makes the test for the data sequence below
819                                         // easier
820                     }
821 
822                     // find data sequence
823                     if (rChar == 'x' && aDataStartIdx[oi] == -1)
824                     {
825                         aDataStartIdx[oi] = ii;
826 
827                         // get length of data sequence
828                         sal_Int32 nL = 0;
829                         sal_Char c;
830                         while (ii< iiEnd && 'x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
831                         {
832                             ++nL;   ++ii;
833                         }
834                         aDataLen[oi] = nL;
835 
836                         // check that there is no other seperate sequence of data
837                         // to be found because that is not supported
838                         while (ii < iiEnd)
839                         {
840                             if ('x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
841                                 throw lang::IllegalArgumentException();
842                             ++ii;
843                         }
844                     }
845                     else
846                         ++ii;
847                 }
848             }
849 
850             // make some other consistency checks while calculating
851             // the number of XLabeledDataSequence to build:
852             // - labels should always be used or not at all
853             // - the data sequences should have equal non-zero length
854             sal_Int32 nNumLDS = 0;
855             if (oiEnd > 0)
856             {
857                 sal_Int32 nFirstSeqLen = 0;
858                 sal_Int32 nFirstSeqLabelIdx = -1;
859                 for (oi = 0;  oi < oiEnd;  ++oi)
860                 {
861                     sal_Bool bFirstFound = sal_False;
862                     // row/col used at all?
863                     if (aDataStartIdx[oi] != -1 &&
864                         (!bFirstIsLabel || aLabelIdx[oi] != -1))
865                     {
866                         ++nNumLDS;
867                         if (!bFirstFound)
868                         {
869                             nFirstSeqLen        = aDataLen[oi];
870                             nFirstSeqLabelIdx   = aLabelIdx[oi];
871                             bFirstFound = sal_True;
872                         }
873                         else
874                         {
875                             if (nFirstSeqLen != aDataLen[oi] ||
876                                 nFirstSeqLabelIdx != aLabelIdx[oi])
877                                 throw lang::IllegalArgumentException();
878                         }
879                     }
880                 }
881             }
882             if (nNumLDS == 0)
883                 throw lang::IllegalArgumentException();
884 
885             // now we should have all necessary data to build a proper DataSource
886             // thus if we came this far there should be no further problem
887             if (bTestOnly)
888                 return xRes;    // have createDataSourcePossible return true
889 
890             // create data source from found label and data sequences
891             uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aLabelSeqs( nNumLDS );
892             uno::Reference< chart2::data::XDataSequence > *pLabelSeqs = aLabelSeqs.getArray();
893             uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aDataSeqs( nNumLDS );
894             uno::Reference< chart2::data::XDataSequence > *pDataSeqs = aDataSeqs.getArray();
895             sal_Int32 nSeqsIdx = 0;
896             for (oi = 0;  oi < oiEnd;  ++oi)
897             {
898                 // row/col not used? (see if-statement above where nNumLDS was counted)
899                 if (!(aDataStartIdx[oi] != -1 &&
900                         (!bFirstIsLabel || aLabelIdx[oi] != -1)))
901                     continue;
902 
903                 // get cell ranges for label and data
904                 //
905                 SwRangeDescriptor aLabelDesc;
906                 SwRangeDescriptor aDataDesc;
907                 if (bDtaSrcIsColumns)   // use columns
908                 {
909                     aLabelDesc.nTop     = aLabelIdx[oi];
910                     aLabelDesc.nLeft    = oi;
911                     aLabelDesc.nBottom  = aLabelDesc.nTop;
912                     aLabelDesc.nRight   = oi;
913 
914                     aDataDesc.nTop      = aDataStartIdx[oi];
915                     aDataDesc.nLeft     = oi;
916                     aDataDesc.nBottom   = aDataDesc.nTop + aDataLen[oi] - 1;
917                     aDataDesc.nRight    = oi;
918                 }
919                 else    // use rows
920                 {
921                     aLabelDesc.nTop     = oi;
922                     aLabelDesc.nLeft    = aLabelIdx[oi];
923                     aLabelDesc.nBottom  = oi;
924                     aLabelDesc.nRight   = aLabelDesc.nLeft;
925 
926                     aDataDesc.nTop      = oi;
927                     aDataDesc.nLeft     = aDataStartIdx[oi];
928                     aDataDesc.nBottom   = oi;
929                     aDataDesc.nRight    = aDataDesc.nLeft + aDataLen[oi] - 1;
930                 }
931                 String aBaseName( pTblFmt->GetName() );
932                 aBaseName += '.';
933                 //
934                 String aLabelRange;
935                 if (aLabelIdx[oi] != -1)
936                 {
937                     aLabelRange += aBaseName;
938                     aLabelRange += lcl_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop );
939                     aLabelRange += ':';
940                     aLabelRange += lcl_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
941                 }
942                 //
943                 String aDataRange;
944                 if (aDataStartIdx[oi] != -1)
945                 {
946                     aDataRange += aBaseName;
947                     aDataRange += lcl_GetCellName( aDataDesc.nLeft, aDataDesc.nTop );
948                     aDataRange += ':';
949                     aDataRange += lcl_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
950                 }
951 
952                 // get cursors spanning the cell ranges for label and data
953                 SwUnoCrsr   *pLabelUnoCrsr  = 0;
954                 SwUnoCrsr   *pDataUnoCrsr   = 0;
955                 GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTblFmt, &pLabelUnoCrsr);
956                 GetFormatAndCreateCursorFromRangeRep( pDoc, aDataRange,  &pTblFmt, &pDataUnoCrsr);
957 
958                 // create XDataSequence's from cursors
959 				if (pLabelUnoCrsr)
960                     pLabelSeqs[ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pLabelUnoCrsr );
961                 DBG_ASSERT( pDataUnoCrsr, "pointer to data sequence missing" );
962 				if (pDataUnoCrsr)
963                     pDataSeqs [ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pDataUnoCrsr );
964                 if (pLabelUnoCrsr || pDataUnoCrsr)
965                     ++nSeqsIdx;
966             }
967             DBG_ASSERT( nSeqsIdx == nNumLDS,
968                     "mismatch between sequence size and num,ber of entries" );
969 
970             // build data source from data and label sequences
971             uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLDS( nNumLDS );
972             uno::Reference< chart2::data::XLabeledDataSequence > *pLDS = aLDS.getArray();
973             for (sal_Int32 i = 0;  i < nNumLDS;  ++i)
974             {
975                 SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence;
976                 pLabeledDtaSeq->setLabel( pLabelSeqs[i] );
977                 pLabeledDtaSeq->setValues( pDataSeqs[i] );
978                 pLDS[i] = pLabeledDtaSeq;
979             }
980 
981             // apply 'SequenceMapping' if it was provided
982             sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength();
983             if (nSequenceMappingLen)
984             {
985                 sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
986                 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aOld_LDS( aLDS );
987                 uno::Reference< chart2::data::XLabeledDataSequence > *pOld_LDS = aOld_LDS.getArray();
988 
989                 sal_Int32 nNewCnt = 0;
990                 for (sal_Int32 i = 0;  i < nSequenceMappingLen;  ++i)
991                 {
992                     // check that index to be used is valid
993                     // and has not yet been used
994                     sal_Int32 nIdx = pSequenceMapping[i];
995                     if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
996                     {
997                         pLDS[nNewCnt++] = pOld_LDS[nIdx];
998 
999                         // mark index as being used already (avoids duplicate entries)
1000                         pOld_LDS[nIdx].clear();
1001                     }
1002                 }
1003                 // add not yet used 'old' sequences to new one
1004                 for (sal_Int32 i = 0;  i < nNumLDS;  ++i)
1005                 {
1006 #if OSL_DEBUG_LEVEL > 1
1007                         if (!pOld_LDS[i].is())
1008                             i = i;
1009 #endif
1010                     if (pOld_LDS[i].is())
1011                         pLDS[nNewCnt++] = pOld_LDS[i];
1012                 }
1013                 DBG_ASSERT( nNewCnt == nNumLDS, "unexpected size of resulting sequence" );
1014             }
1015 
1016             xRes = new SwChartDataSource( aLDS );
1017         }
1018     }
1019 
1020     return xRes;
1021 }
1022 
createDataSourcePossible(const uno::Sequence<beans::PropertyValue> & rArguments)1023 sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
1024         const uno::Sequence< beans::PropertyValue >& rArguments )
1025     throw (uno::RuntimeException)
1026 {
1027     vos::OGuard aGuard( Application::GetSolarMutex() );
1028 
1029     sal_Bool bPossible = sal_True;
1030     try
1031     {
1032         Impl_createDataSource( rArguments, sal_True );
1033     }
1034     catch (lang::IllegalArgumentException &)
1035     {
1036         bPossible = sal_False;
1037     }
1038 
1039     return bPossible;
1040 }
1041 
createDataSource(const uno::Sequence<beans::PropertyValue> & rArguments)1042 uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
1043         const uno::Sequence< beans::PropertyValue >& rArguments )
1044     throw (lang::IllegalArgumentException, uno::RuntimeException)
1045 {
1046     vos::OGuard aGuard( Application::GetSolarMutex() );
1047     return Impl_createDataSource( rArguments );
1048 }
1049 
1050 ////////////////////////////////////////////////////////////
1051 // SwChartDataProvider::GetBrokenCellRangeForExport
1052 //
1053 // fix for #i79009
1054 // we need to return a property that has the same value as the property
1055 // 'CellRangeRepresentation' but for all rows which are increased by one.
1056 // E.g. Table1:A1:D5 -> Table1:A2:D6
1057 // Since the problem is only for old charts which did not support multiple
1058 // we do not need to provide that property/string if the 'CellRangeRepresentation'
1059 // contains multiple ranges.
GetBrokenCellRangeForExport(const OUString & rCellRangeRepresentation)1060 OUString SwChartDataProvider::GetBrokenCellRangeForExport(
1061     const OUString &rCellRangeRepresentation )
1062 {
1063     OUString aRes;
1064 
1065     // check that we do not have multiple ranges
1066     if (-1 == rCellRangeRepresentation.indexOf( ';' ))
1067     {
1068         // get current cell and table names
1069         String aTblName, aStartCell, aEndCell;
1070         GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
1071             aTblName, aStartCell, aEndCell, sal_False );
1072         sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1073         lcl_GetCellPosition( aStartCell, nStartCol, nStartRow );
1074         lcl_GetCellPosition( aEndCell, nEndCol, nEndRow );
1075 
1076         // get new cell names
1077         ++nStartRow;
1078         ++nEndRow;
1079         aStartCell = lcl_GetCellName( nStartCol, nStartRow );
1080         aEndCell   = lcl_GetCellName( nEndCol, nEndRow );
1081 
1082         aRes = GetRangeRepFromTableAndCells( aTblName,
1083                 aStartCell, aEndCell, sal_False );
1084     }
1085 
1086     return aRes;
1087 }
1088 
detectArguments(const uno::Reference<chart2::data::XDataSource> & xDataSource)1089 uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
1090         const uno::Reference< chart2::data::XDataSource >& xDataSource )
1091     throw (uno::RuntimeException)
1092 {
1093     vos::OGuard aGuard( Application::GetSolarMutex() );
1094     if (bDisposed)
1095         throw lang::DisposedException();
1096 
1097     uno::Sequence< beans::PropertyValue > aResult;
1098     if (!xDataSource.is())
1099         return aResult;
1100 
1101     const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
1102     const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
1103     sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
1104 
1105     if (nNumDS_LDS == 0)
1106 	{
1107 	    DBG_WARNING( "XLabeledDataSequence in data source contains 0 entries" );
1108         return aResult;
1109 	}
1110 
1111     SwFrmFmt *pTableFmt = 0;
1112     SwTable  *pTable    = 0;
1113     String    aTableName;
1114     sal_Int32 nTableRows = 0;
1115     sal_Int32 nTableCols = 0;
1116 
1117     // data used to build 'CellRangeRepresentation' from later on
1118     std::vector< std::vector< sal_Char > > aMap;
1119 
1120     uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
1121     sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
1122 
1123     String aCellRanges;
1124     sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true  -2: neither
1125     sal_Int32 nLabelSeqLen  = -1;   // used to see if labels are always used or not and have
1126                                     // the expected size of 1 (i.e. if FirstCellAsLabel can
1127                                     // be determined)
1128                                     // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
1129 									// -2: neither/failed
1130 //     sal_Int32 nValuesSeqLen = -1;   // used to see if all value sequences have the same size
1131     for (sal_Int32 nDS1 = 0;  nDS1 < nNumDS_LDS;  ++nDS1)
1132     {
1133         uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
1134         if( !xLabeledDataSequence.is() )
1135         {
1136             DBG_ERROR("got NULL for XLabeledDataSequence from Data source");
1137             continue;
1138         }
1139         const uno::Reference< chart2::data::XDataSequence > xCurLabel( xLabeledDataSequence->getLabel(), uno::UNO_QUERY );
1140         const uno::Reference< chart2::data::XDataSequence > xCurValues( xLabeledDataSequence->getValues(), uno::UNO_QUERY );
1141 
1142         // get sequence lengths for label and values.
1143 		// (0 length is Ok)
1144         sal_Int32 nCurLabelSeqLen   = -1;
1145         sal_Int32 nCurValuesSeqLen  = -1;
1146         if (xCurLabel.is())
1147             nCurLabelSeqLen = xCurLabel->getData().getLength();
1148         if (xCurValues.is())
1149             nCurValuesSeqLen = xCurValues->getData().getLength();
1150 
1151 		// check for consistent use of 'first cell as label'
1152 		if (nLabelSeqLen == -1)		// set initial value to compare with below further on
1153 			nLabelSeqLen = nCurLabelSeqLen;
1154 		if (nLabelSeqLen != nCurLabelSeqLen)
1155 			nLabelSeqLen = -2;	// failed / no consistent use of label cells
1156 
1157         // get table and cell names for label and values data sequences
1158         // (start and end cell will be sorted, i.e. start cell <= end cell)
1159         String aLabelTblName, aLabelStartCell, aLabelEndCell;
1160         String aValuesTblName, aValuesStartCell, aValuesEndCell;
1161         String aLabelRange, aValuesRange;
1162 		if (xCurLabel.is())
1163 			aLabelRange = xCurLabel->getSourceRangeRepresentation();
1164 		if (xCurValues.is())
1165 			aValuesRange = xCurValues->getSourceRangeRepresentation();
1166         if ((aLabelRange.Len() && !GetTableAndCellsFromRangeRep( aLabelRange,
1167                 aLabelTblName, aLabelStartCell, aLabelEndCell ))  ||
1168             !GetTableAndCellsFromRangeRep( aValuesRange,
1169                 aValuesTblName, aValuesStartCell, aValuesEndCell ))
1170         {
1171             return aResult; // failed -> return empty property sequence
1172         }
1173 
1174         // make sure all sequences use the same table
1175         if (!aTableName.Len())
1176             aTableName = aValuesTblName;  // get initial value to compare with
1177         if (!aTableName.Len() ||
1178              aTableName != aValuesTblName ||
1179             (aLabelTblName.Len() && aTableName != aLabelTblName))
1180         {
1181             return aResult; // failed -> return empty property sequence
1182         }
1183 
1184 
1185         // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
1186         // first and last cell used in both sequences
1187 		//
1188         sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1189         String aCell( aLabelStartCell.Len() ? aLabelStartCell : aValuesStartCell );
1190         DBG_ASSERT( aCell.Len() , "start cell missing?" );
1191         lcl_GetCellPosition( aCell, nFirstCol, nFirstRow);
1192         lcl_GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
1193         //
1194         sal_Int16 nDirection = -1;  // -1: not yet set,  0: columns,  1: rows, -2: failed
1195         if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
1196         {
1197             DBG_ASSERT( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
1198                     "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
1199             nDirection = 0;     // default direction for a single cell should be 'columns'
1200         }
1201         else    // more than one cell is availabale (in values and label together!)
1202         {
1203             if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1204                 nDirection = 1;
1205             else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
1206                 nDirection = 0;
1207             else
1208             {
1209                 DBG_ERROR( "trying to determine 'DataRowSource': unexpected case found" );
1210                 nDirection = -2;
1211             }
1212         }
1213         // check for consistent direction of data source
1214         if (nDtaSrcIsColumns == -1)     // set initial value to compare with below
1215             nDtaSrcIsColumns = nDirection;
1216         if (nDtaSrcIsColumns != nDirection)
1217         {
1218             nDtaSrcIsColumns = -2;	// failed
1219         }
1220 
1221 
1222 		if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1223 		{
1224 			// build data to obtain 'SequenceMapping' later on
1225 			//
1226 			DBG_ASSERT( nDtaSrcIsColumns == 0  ||   /* rows */
1227 						nDtaSrcIsColumns == 1,      /* columns */
1228 					"unexpected value for 'nDtaSrcIsColumns'" );
1229 			pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
1230 
1231 
1232 			// build data used to determine 'CellRangeRepresentation' later on
1233 			//
1234 			GetTableByName( *pDoc, aTableName, &pTableFmt, &pTable );
1235 			if (!pTable || pTable->IsTblComplex())
1236 				return aResult; // failed -> return empty property sequence
1237 			nTableRows = pTable->GetTabLines().Count();
1238 			nTableCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count();
1239 			aMap.resize( nTableRows );
1240             for (sal_Int32 i = 0;  i < nTableRows;  ++i)
1241 				aMap[i].resize( nTableCols );
1242 			//
1243 			if (aLabelStartCell.Len() && aLabelEndCell.Len())
1244 			{
1245 				sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1246 				lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
1247 				lcl_GetCellPosition( aLabelEndCell,   nEndCol,   nEndRow );
1248 				if (nStartRow < 0 || nEndRow >= nTableRows ||
1249 					nStartCol < 0 || nEndCol >= nTableCols)
1250 				{
1251 					return aResult; // failed -> return empty property sequence
1252 				}
1253                 for (sal_Int32 i = nStartRow;  i <= nEndRow;  ++i)
1254                 {
1255                     for (sal_Int32 k = nStartCol;  k <= nEndCol;  ++k)
1256                     {
1257                         sal_Char &rChar = aMap[i][k];
1258                         if (rChar == '\0')   // check for overlapping values and/or labels
1259                             rChar = 'L';
1260                         else
1261                             return aResult; // failed -> return empty property sequence
1262                     }
1263                 }
1264 			}
1265 			if (aValuesStartCell.Len() && aValuesEndCell.Len())
1266 			{
1267 				sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
1268 				lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
1269 				lcl_GetCellPosition( aValuesEndCell,   nEndCol,   nEndRow );
1270 				if (nStartRow < 0 || nEndRow >= nTableRows ||
1271 					nStartCol < 0 || nEndCol >= nTableCols)
1272 				{
1273 					return aResult; // failed -> return empty property sequence
1274 				}
1275                 for (sal_Int32 i = nStartRow;  i <= nEndRow;  ++i)
1276                 {
1277                     for (sal_Int32 k = nStartCol;  k <= nEndCol;  ++k)
1278                     {
1279                         sal_Char &rChar = aMap[i][k];
1280                         if (rChar == '\0')   // check for overlapping values and/or labels
1281                             rChar = 'x';
1282                         else
1283                             return aResult; // failed -> return empty property sequence
1284                     }
1285                 }
1286 			}
1287 		}
1288 
1289 #if OSL_DEBUG_LEVEL > 1
1290         // do some extra sanity checking that the length of the sequences
1291         // matches their range representation
1292         {
1293             sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
1294             if (xCurLabel.is())
1295             {
1296                 lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
1297                 lcl_GetCellPosition( aLabelEndCell,   nEndCol,   nEndRow);
1298                 DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
1299                             (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
1300                         "label sequence length does not match range representation!" );
1301             }
1302             if (xCurValues.is())
1303             {
1304                 lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
1305                 lcl_GetCellPosition( aValuesEndCell,   nEndCol,   nEndRow);
1306                 DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
1307                             (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
1308                         "value sequence length does not match range representation!" );
1309             }
1310         }
1311 #endif
1312     } // for
1313 
1314 
1315     // build value for 'CellRangeRepresentation'
1316     //
1317     String aCellRangeBase( aTableName );
1318     aCellRangeBase += '.';
1319     String aCurRange;
1320     for (sal_Int32 i = 0;  i < nTableRows;  ++i)
1321 	{
1322         for (sal_Int32 k = 0;  k < nTableCols;  ++k)
1323         {
1324             if (aMap[i][k] != '\0')  // top-left cell of a sub-range found
1325             {
1326                 // find rectangular sub-range to use
1327                 sal_Int32 nRowIndex1 = i;   // row index
1328                 sal_Int32 nColIndex1 = k;   // column index
1329                 sal_Int32 nRowSubLen = 0;
1330                 sal_Int32 nColSubLen = 0;
1331                 while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
1332                     ++nRowSubLen;
1333                 // be aware of shifted sequences!
1334                 // (according to the checks done prior the length should be ok)
1335                 while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
1336                                        && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
1337                 {
1338                     ++nColIndex1;
1339                     ++nColSubLen;
1340                 }
1341                 String aStartCell( lcl_GetCellName( k, i ) );
1342                 String aEndCell( lcl_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
1343                 aCurRange = aCellRangeBase;
1344                 aCurRange += aStartCell;
1345                 aCurRange += ':';
1346                 aCurRange += aEndCell;
1347                 if (aCellRanges.Len())
1348                     aCellRanges += ';';
1349                 aCellRanges += aCurRange;
1350 
1351                 // clear already found sub-range from map
1352                 for (sal_Int32 nRowIndex2 = 0;  nRowIndex2 < nRowSubLen;  ++nRowIndex2)
1353                     for (sal_Int32 nColumnIndex2 = 0;  nColumnIndex2 < nColSubLen;  ++nColumnIndex2)
1354                         aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
1355             }
1356         }
1357     }
1358     // to be nice to the user we now sort the cell ranges according to
1359     // rows or columns depending on the direction used in the data source
1360     uno::Sequence< OUString > aSortedRanges;
1361     GetSubranges( aCellRanges, aSortedRanges, sal_False /*sub ranges should already be normalized*/ );
1362     SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
1363     sal_Int32 nSortedRanges = aSortedRanges.getLength();
1364     const OUString *pSortedRanges = aSortedRanges.getConstArray();
1365     OUString aSortedCellRanges;
1366     for (sal_Int32 i = 0;  i < nSortedRanges;  ++i)
1367     {
1368         if (aSortedCellRanges.getLength())
1369             aSortedCellRanges += OUString::valueOf( (sal_Unicode) ';');
1370         aSortedCellRanges += pSortedRanges[i];
1371     }
1372 
1373 
1374     // build value for 'SequenceMapping'
1375     //
1376     uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
1377     sal_Int32 *pSortedMapping = aSortedMapping.getArray();
1378     std::sort( pSortedMapping, pSortedMapping + aSortedMapping.getLength() );
1379     DBG_ASSERT( aSortedMapping.getLength() == nNumDS_LDS, "unexpected size of sequence" );
1380 	sal_Bool bNeedSequenceMapping = sal_False;
1381     for (sal_Int32 i = 0;  i < nNumDS_LDS;  ++i)
1382     {
1383         sal_Int32 *pIt = std::find( pSortedMapping, pSortedMapping + nNumDS_LDS,
1384                                     pSequenceMapping[i] );
1385         DBG_ASSERT( pIt, "index not found" );
1386         if (!pIt)
1387             return aResult; // failed -> return empty property sequence
1388         pSequenceMapping[i] = pIt - pSortedMapping;
1389 
1390 		if (i != pSequenceMapping[i])
1391 			bNeedSequenceMapping = sal_True;
1392     }
1393 
1394 	// check if 'SequenceMapping' is actually not required...
1395 	// (don't write unnecessary properties to the XML file)
1396 	if (!bNeedSequenceMapping)
1397 		aSequenceMapping.realloc(0);
1398 
1399 
1400 #ifdef TL_NOT_USED  // in the end chart2 did not want to have the sequence minimized
1401     // try to shorten the 'SequenceMapping' as much as possible
1402     sal_Int32 k;
1403     for (k = nNumDS_LDS - 1;  k >= 0;  --k)
1404     {
1405         if (pSequenceMapping[k] != k)
1406             break;
1407     }
1408     aSequenceMapping.realloc( k + 1 );
1409 #endif
1410 
1411 
1412     //
1413     // build resulting properties
1414     //
1415     DBG_ASSERT(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
1416             "unexpected value for 'nLabelSeqLen'" );
1417     sal_Bool bFirstCellIsLabel = sal_False;     // default value if 'nLabelSeqLen' could not properly determined
1418     if (nLabelSeqLen > 0) // == 0 means no label sequence in use
1419         bFirstCellIsLabel = sal_True;
1420 	//
1421     DBG_ASSERT( aSortedCellRanges.getLength(), "CellRangeRepresentation missing" );
1422     OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
1423 	//
1424     aResult.realloc(5);
1425     sal_Int32 nProps = 0;
1426     aResult[nProps  ].Name = C2U("FirstCellAsLabel");
1427     aResult[nProps++].Value <<= bFirstCellIsLabel;
1428     aResult[nProps  ].Name = C2U("CellRangeRepresentation");
1429     aResult[nProps++].Value <<= aSortedCellRanges;
1430     if (0 != aBrokenCellRangeForExport.getLength())
1431     {
1432         aResult[nProps  ].Name = C2U("BrokenCellRangeForExport");
1433         aResult[nProps++].Value <<= aBrokenCellRangeForExport;
1434     }
1435 	if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
1436 	{
1437 		chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
1438 					chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
1439 		aResult[nProps  ].Name = C2U("DataRowSource");
1440 		aResult[nProps++].Value <<= eDataRowSource;
1441 
1442 		if (aSequenceMapping.getLength() != 0)
1443 		{
1444 			aResult[nProps  ].Name = C2U("SequenceMapping");
1445 			aResult[nProps++].Value <<= aSequenceMapping;
1446 		}
1447 	}
1448 	aResult.realloc( nProps );
1449 
1450     return aResult;
1451 }
1452 
Impl_createDataSequenceByRangeRepresentation(const OUString & rRangeRepresentation,sal_Bool bTestOnly)1453 uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
1454         const OUString& rRangeRepresentation, sal_Bool bTestOnly )
1455     throw (lang::IllegalArgumentException, uno::RuntimeException)
1456 {
1457     if (bDisposed)
1458         throw lang::DisposedException();
1459 
1460     SwFrmFmt    *pTblFmt    = 0;    // pointer to table format
1461     SwUnoCrsr   *pUnoCrsr   = 0;    // pointer to new created cursor spanning the cell range
1462     GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation,
1463                                           &pTblFmt, &pUnoCrsr );
1464     if (!pTblFmt || !pUnoCrsr)
1465         throw lang::IllegalArgumentException();
1466 
1467     // check that cursors point and mark are in a single row or column.
1468     String aCellRange( GetCellRangeName( *pTblFmt, *pUnoCrsr ) );
1469     SwRangeDescriptor aDesc;
1470     FillRangeDescriptor( aDesc, aCellRange );
1471     if (aDesc.nTop != aDesc.nBottom  &&  aDesc.nLeft != aDesc.nRight)
1472         throw lang::IllegalArgumentException();
1473 
1474     DBG_ASSERT( pTblFmt && pUnoCrsr, "table format or cursor missing" );
1475     uno::Reference< chart2::data::XDataSequence > xDataSeq;
1476     if (!bTestOnly)
1477         xDataSeq = new SwChartDataSequence( *this, *pTblFmt, pUnoCrsr );
1478 
1479     return xDataSeq;
1480 }
1481 
createDataSequenceByRangeRepresentationPossible(const OUString & rRangeRepresentation)1482 sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
1483         const OUString& rRangeRepresentation )
1484     throw (uno::RuntimeException)
1485 {
1486     vos::OGuard aGuard( Application::GetSolarMutex() );
1487 
1488     sal_Bool bPossible = sal_True;
1489     try
1490     {
1491         Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, sal_True );
1492     }
1493     catch (lang::IllegalArgumentException &)
1494     {
1495         bPossible = sal_False;
1496     }
1497 
1498     return bPossible;
1499 }
1500 
createDataSequenceByRangeRepresentation(const OUString & rRangeRepresentation)1501 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
1502         const OUString& rRangeRepresentation )
1503     throw (lang::IllegalArgumentException, uno::RuntimeException)
1504 {
1505     vos::OGuard aGuard( Application::GetSolarMutex() );
1506     return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
1507 }
1508 
1509 
getRangeSelection()1510 uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection(  )
1511     throw (uno::RuntimeException)
1512 {
1513     // note: it is no error to return nothing here
1514     return uno::Reference< sheet::XRangeSelection >();
1515 }
1516 
1517 
dispose()1518 void SAL_CALL SwChartDataProvider::dispose(  )
1519     throw (uno::RuntimeException)
1520 {
1521     sal_Bool bMustDispose( sal_False );
1522 	{
1523 		osl::MutexGuard  aGuard( GetChartMutex() );
1524         bMustDispose = !bDisposed;
1525 		if (!bDisposed)
1526 			bDisposed = sal_True;
1527 	}
1528     if (bMustDispose)
1529     {
1530         // dispose all data-sequences
1531         Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() );
1532         while (aIt != aDataSequences.end())
1533 		{
1534             DisposeAllDataSequences( (*aIt).first );
1535 			++aIt;
1536 		}
1537 		// release all references to data-sequences
1538 		aDataSequences.clear();
1539 
1540 		// require listeners to release references to this object
1541         lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
1542         aEvtListeners.disposeAndClear( aEvtObj );
1543     }
1544 }
1545 
1546 
addEventListener(const uno::Reference<lang::XEventListener> & rxListener)1547 void SAL_CALL SwChartDataProvider::addEventListener(
1548         const uno::Reference< lang::XEventListener >& rxListener )
1549     throw (uno::RuntimeException)
1550 {
1551     osl::MutexGuard  aGuard( GetChartMutex() );
1552     if (!bDisposed && rxListener.is())
1553         aEvtListeners.addInterface( rxListener );
1554 }
1555 
1556 
removeEventListener(const uno::Reference<lang::XEventListener> & rxListener)1557 void SAL_CALL SwChartDataProvider::removeEventListener(
1558         const uno::Reference< lang::XEventListener >& rxListener )
1559     throw (uno::RuntimeException)
1560 {
1561     osl::MutexGuard  aGuard( GetChartMutex() );
1562     if (!bDisposed && rxListener.is())
1563         aEvtListeners.removeInterface( rxListener );
1564 }
1565 
1566 
1567 
getImplementationName()1568 OUString SAL_CALL SwChartDataProvider::getImplementationName(  )
1569     throw (uno::RuntimeException)
1570 {
1571     return C2U("SwChartDataProvider");
1572 }
1573 
1574 
supportsService(const OUString & rServiceName)1575 sal_Bool SAL_CALL SwChartDataProvider::supportsService(
1576         const OUString& rServiceName )
1577     throw (uno::RuntimeException)
1578 {
1579     vos::OGuard aGuard( Application::GetSolarMutex() );
1580     return rServiceName.equalsAscii( SN_DATA_PROVIDER );
1581 }
1582 
1583 
getSupportedServiceNames()1584 uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames(  )
1585     throw (uno::RuntimeException)
1586 {
1587     vos::OGuard aGuard( Application::GetSolarMutex() );
1588     uno::Sequence< OUString > aRes(1);
1589     aRes.getArray()[0] = C2U( SN_DATA_PROVIDER );
1590     return aRes;
1591 }
1592 
1593 
Modify(const SfxPoolItem * pOld,const SfxPoolItem * pNew)1594 void SwChartDataProvider::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
1595 {
1596     // actually this function should be superfluous (need to check later)
1597     ClientModify(this, pOld, pNew );
1598 }
1599 
1600 
AddDataSequence(const SwTable & rTable,uno::Reference<chart2::data::XDataSequence> & rxDataSequence)1601 void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
1602 {
1603     aDataSequences[ &rTable ].insert( rxDataSequence );
1604 }
1605 
1606 
RemoveDataSequence(const SwTable & rTable,uno::Reference<chart2::data::XDataSequence> & rxDataSequence)1607 void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
1608 {
1609     aDataSequences[ &rTable ].erase( rxDataSequence );
1610 }
1611 
1612 
InvalidateTable(const SwTable * pTable)1613 void SwChartDataProvider::InvalidateTable( const SwTable *pTable )
1614 {
1615     DBG_ASSERT( pTable, "table pointer is NULL" );
1616     if (pTable)
1617     {
1618 		if (!bDisposed)
1619 	       pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1620 
1621 		const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
1622         Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() );
1623         while (aIt != rSet.end())
1624         {
1625 //            uno::Reference< util::XModifiable > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
1626             uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
1627             uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
1628             if (xRef.is())
1629             {
1630                 // mark the sequence as 'dirty' and notify listeners
1631                 xRef->setModified( sal_True );
1632             }
1633             ++aIt;
1634         }
1635     }
1636 }
1637 
1638 
DeleteBox(const SwTable * pTable,const SwTableBox & rBox)1639 sal_Bool SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
1640 {
1641     sal_Bool bRes = sal_False;
1642     DBG_ASSERT( pTable, "table pointer is NULL" );
1643     if (pTable)
1644     {
1645 		if (!bDisposed)
1646 	        pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1647 
1648         Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
1649 
1650         // iterate over all data-sequences for that table...
1651         Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
1652         Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
1653         Set_DataSequenceRef_t::iterator aDelIt;     // iterator used for deletion when appropriate
1654         while (aIt != aEndIt)
1655         {
1656 			SwChartDataSequence *pDataSeq = 0;
1657             sal_Bool bNowEmpty = sal_False;
1658             sal_Bool bSeqDisposed = sal_False;
1659 
1660             // check if weak reference is still valid...
1661 //            uno::Reference< chart2::data::XDataSequence > xRef( uno::Reference< chart2::data::XDataSequence>(*aIt), uno::UNO_QUERY );
1662             uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
1663             uno::Reference< chart2::data::XDataSequence > xRef( xTemp, uno::UNO_QUERY );
1664             if (xRef.is())
1665             {
1666                 // then delete that table box (check if implementation cursor needs to be adjusted)
1667                 pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() );
1668                 if (pDataSeq)
1669                 {
1670                     try
1671                     {
1672 #if OSL_DEBUG_LEVEL > 1
1673                     OUString aRangeStr( pDataSeq->getSourceRangeRepresentation() );
1674 #endif
1675                     bNowEmpty = pDataSeq->DeleteBox( rBox );
1676                     }
1677                     catch (lang::DisposedException&)
1678                     {
1679                         bNowEmpty = sal_True;
1680                         bSeqDisposed = sal_True;
1681                     }
1682 
1683                     if (bNowEmpty)
1684                         aDelIt = aIt;
1685                 }
1686             }
1687             ++aIt;
1688 
1689             if (bNowEmpty)
1690 			{
1691                 rSet.erase( aDelIt );
1692                 if (pDataSeq && !bSeqDisposed)
1693                     pDataSeq->dispose();    // the current way to tell chart that sth. got removed
1694 			}
1695         }
1696     }
1697     return bRes;
1698 }
1699 
1700 
DisposeAllDataSequences(const SwTable * pTable)1701 void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
1702 {
1703     DBG_ASSERT( pTable, "table pointer is NULL" );
1704     if (pTable)
1705     {
1706 		if (!bDisposed)
1707 			pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();
1708 
1709         //! make a copy of the STL container!
1710         //! This is necessary since calling 'dispose' will implicitly remove an element
1711         //! of the original container, and thus any iterator in the original container
1712         //! would become invalid.
1713         const Set_DataSequenceRef_t aSet( aDataSequences[ pTable ] );
1714 
1715         Set_DataSequenceRef_t::const_iterator aIt( aSet.begin() );
1716         Set_DataSequenceRef_t::const_iterator aEndIt( aSet.end() );
1717         while (aIt != aEndIt)
1718         {
1719 //            uno::Reference< lang::XComponent > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
1720             uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
1721             uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
1722             if (xRef.is())
1723             {
1724                 xRef->dispose();
1725             }
1726             ++aIt;
1727         }
1728     }
1729 }
1730 
1731 
1732 ////////////////////////////////////////
1733 // SwChartDataProvider::AddRowCols tries to notify charts of added columns
1734 // or rows and extends the value sequence respectively (if possible).
1735 // If those can be added to the end of existing value data-sequences those
1736 // sequences get mofdified accordingly and will send a modification
1737 // notification (calling 'setModified').
1738 //
1739 // Since this function is a work-around for non existent Writer core functionality
1740 // (no arbitrary multi-selection in tables that can be used to define a
1741 // data-sequence) this function will be somewhat unreliable.
1742 // For example we will only try to adapt value sequences. For this we assume
1743 // that a sequence of length 1 is a label sequence and those with length >= 2
1744 // we presume to be value sequences. Also new cells can only be added in the
1745 // direction the value sequence is already pointing (rows / cols) and at the
1746 // start or end of the values data-sequence.
1747 // Nothing needs to be done if the new cells are in between the table cursors
1748 // point and mark since data-sequence are considered to consist of all cells
1749 // between those.
1750 // New rows/cols need to be added already to the table before calling
1751 // this function.
1752 //
AddRowCols(const SwTable & rTable,const SwSelBoxes & rBoxes,sal_uInt16 nLines,sal_Bool bBehind)1753 void SwChartDataProvider::AddRowCols(
1754         const SwTable &rTable,
1755         const SwSelBoxes& rBoxes,
1756         sal_uInt16 nLines, sal_Bool bBehind )
1757 {
1758 	if (rTable.IsTblComplex())
1759 		return;
1760 
1761 	const sal_uInt16 nBoxes		= rBoxes.Count();
1762     if (nBoxes < 1 || nLines < 1)
1763         return;
1764 
1765 	SwTableBox* pFirstBox	= *( rBoxes.GetData() + 0 );
1766 	SwTableBox* pLastBox	= *( rBoxes.GetData() + nBoxes - 1 );
1767 
1768     sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
1769 	if (pFirstBox && pLastBox)
1770 	{
1771         lcl_GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow  );
1772         lcl_GetCellPosition( pLastBox->GetName(),  nLastCol,  nLastRow );
1773 
1774         bool bAddCols = false;  // default; also to be used if nBoxes == 1 :-/
1775         if (nFirstCol == nLastCol && nFirstRow != nLastRow)
1776             bAddCols = true;
1777         if (nFirstCol == nLastCol || nFirstRow == nLastRow)
1778 		{
1779 			//get range of indices in col/rows for new cells
1780             sal_Int32 nFirstNewCol = nFirstCol;
1781             sal_Int32 nLastNewCol  = nLastCol;
1782             sal_Int32 nFirstNewRow = bBehind ?  nFirstRow + 1 : nFirstRow - nLines;
1783             sal_Int32 nLastNewRow  = nFirstNewRow - 1 + nLines;
1784             if (bAddCols)
1785             {
1786                 DBG_ASSERT( nFirstCol == nLastCol, "column indices seem broken" );
1787                 nFirstNewCol = bBehind ?  nFirstCol + 1 : nFirstCol - nLines;
1788                 nLastNewCol  = nFirstNewCol - 1 + nLines;
1789                 nFirstNewRow = nFirstRow;
1790                 nLastNewRow  = nLastRow;
1791             }
1792 
1793 			// iterate over all data-sequences for the table
1794 			const Set_DataSequenceRef_t &rSet = aDataSequences[ &rTable ];
1795 			Set_DataSequenceRef_t::const_iterator aIt( rSet.begin() );
1796 			while (aIt != rSet.end())
1797 			{
1798 //               uno::Reference< chart2::data::XTextualDataSequence > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
1799                 uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
1800                 uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
1801                 if (xRef.is())
1802 				{
1803 					const sal_Int32 nLen = xRef->getTextualData().getLength();
1804 					if (nLen > 1) // value data-sequence ?
1805 					{
1806 						SwChartDataSequence *pDataSeq = 0;
1807 						uno::Reference< lang::XUnoTunnel > xTunnel( xRef, uno::UNO_QUERY );
1808 						if(xTunnel.is())
1809 						{
1810 							pDataSeq = reinterpret_cast< SwChartDataSequence * >(
1811 									sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething( SwChartDataSequence::getUnoTunnelId() )));
1812 
1813 							if (pDataSeq)
1814 							{
1815 								SwRangeDescriptor aDesc;
1816 								pDataSeq->FillRangeDesc( aDesc );
1817 
1818 								chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
1819 								if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
1820 									eDRSource = chart::ChartDataRowSource_ROWS;
1821 
1822 								if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
1823 								{
1824 									// add rows: extend affected columns by newly added row cells
1825                                     pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
1826 								}
1827 								else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
1828 								{
1829 									// add cols: extend affected rows by newly added column cells
1830                                     pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
1831 								}
1832 							}
1833 						}
1834 					}
1835 				}
1836 				++aIt;
1837 			}
1838 
1839 		}
1840 	}
1841 }
1842 
1843 
1844 // XRangeXMLConversion ---------------------------------------------------
1845 
convertRangeToXML(const rtl::OUString & rRangeRepresentation)1846 rtl::OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const rtl::OUString& rRangeRepresentation )
1847     throw ( uno::RuntimeException, lang::IllegalArgumentException )
1848 {
1849     vos::OGuard aGuard( Application::GetSolarMutex() );
1850     if (bDisposed)
1851         throw lang::DisposedException();
1852 
1853     String aRes;
1854     String aRangeRepresentation( rRangeRepresentation );
1855 
1856     // multiple ranges are delimeted by a ';' like in
1857     // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
1858     xub_StrLen nNumRanges = aRangeRepresentation.GetTokenCount( ';' );
1859     SwTable* pFirstFoundTable = 0;  // to check that only one table will be used
1860     for (sal_uInt16 i = 0;  i < nNumRanges;  ++i)
1861     {
1862         String aRange( aRangeRepresentation.GetToken(i, ';') );
1863         SwFrmFmt    *pTblFmt  = 0;      // pointer to table format
1864         // BM: For what should the check be necessary? for #i79009# it is required that NO check is done
1865 //         SwUnoCrsr   *pUnoCrsr = 0;      // here required to check if the cells in the range do actually exist
1866 //         std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr );  // to end lifetime of object pointed to by pUnoCrsr
1867         GetFormatAndCreateCursorFromRangeRep( pDoc, aRange, &pTblFmt, NULL );
1868         if (!pTblFmt)
1869             throw lang::IllegalArgumentException();
1870 //    if (!pUnoCrsr)
1871 //        throw uno::RuntimeException();
1872         SwTable* pTable = SwTable::FindTable( pTblFmt );
1873         if  (pTable->IsTblComplex())
1874             throw uno::RuntimeException();
1875 
1876         // check that there is only one table used in all ranges
1877         if (!pFirstFoundTable)
1878             pFirstFoundTable = pTable;
1879         if (pTable != pFirstFoundTable)
1880             throw lang::IllegalArgumentException();
1881 
1882         String aTblName;
1883         String aStartCell;
1884         String aEndCell;
1885         if (!GetTableAndCellsFromRangeRep( aRange, aTblName, aStartCell, aEndCell ))
1886             throw lang::IllegalArgumentException();
1887 
1888         sal_Int32 nCol, nRow;
1889         lcl_GetCellPosition( aStartCell, nCol, nRow );
1890         if (nCol < 0 || nRow < 0)
1891             throw uno::RuntimeException();
1892 
1893         //!! following objects/functions are implemented in XMLRangeHelper.?xx
1894         //!! which is a copy of the respective file from chart2 !!
1895         XMLRangeHelper::CellRange aCellRange;
1896         aCellRange.aTableName = aTblName;
1897         aCellRange.aUpperLeft.nColumn   = nCol;
1898         aCellRange.aUpperLeft.nRow      = nRow;
1899         aCellRange.aUpperLeft.bIsEmpty  = false;
1900         if (aStartCell != aEndCell && aEndCell.Len() != 0)
1901         {
1902             lcl_GetCellPosition( aEndCell, nCol, nRow );
1903             if (nCol < 0 || nRow < 0)
1904                 throw uno::RuntimeException();
1905 
1906             aCellRange.aLowerRight.nColumn   = nCol;
1907             aCellRange.aLowerRight.nRow      = nRow;
1908             aCellRange.aLowerRight.bIsEmpty  = false;
1909         }
1910         String aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
1911         if (aRes.Len()) // in case of multiple ranges add delimeter
1912             aRes.AppendAscii( " " );
1913         aRes += aTmp;
1914     }
1915 
1916     return aRes;
1917 }
1918 
convertRangeFromXML(const rtl::OUString & rXMLRange)1919 rtl::OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const rtl::OUString& rXMLRange )
1920     throw ( uno::RuntimeException, lang::IllegalArgumentException )
1921 {
1922     vos::OGuard aGuard( Application::GetSolarMutex() );
1923     if (bDisposed)
1924         throw lang::DisposedException();
1925 
1926     String aRes;
1927     String aXMLRange( rXMLRange );
1928 
1929     // multiple ranges are delimeted by a ' ' like in
1930     // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
1931     xub_StrLen nNumRanges = aXMLRange.GetTokenCount( ' ' );
1932     rtl::OUString aFirstFoundTable; // to check that only one table will be used
1933     for (sal_uInt16 i = 0;  i < nNumRanges;  ++i)
1934     {
1935         String aRange( aXMLRange.GetToken(i, ' ') );
1936 
1937         //!! following objects and function are implemented in XMLRangeHelper.?xx
1938         //!! which is a copy of the respective file from chart2 !!
1939         XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
1940 
1941         // check that there is only one table used in all ranges
1942         if (aFirstFoundTable.getLength() == 0)
1943             aFirstFoundTable = aCellRange.aTableName;
1944         if (aCellRange.aTableName != aFirstFoundTable)
1945             throw lang::IllegalArgumentException();
1946 
1947         OUString aTmp( aCellRange.aTableName );
1948         aTmp += OUString::valueOf((sal_Unicode) '.');
1949         aTmp += lcl_GetCellName( aCellRange.aUpperLeft.nColumn,
1950                                  aCellRange.aUpperLeft.nRow );
1951         // does cell range consist of more than a single cell?
1952         if (!aCellRange.aLowerRight.bIsEmpty)
1953         {
1954             aTmp += OUString::valueOf((sal_Unicode) ':');
1955             aTmp += lcl_GetCellName( aCellRange.aLowerRight.nColumn,
1956                                      aCellRange.aLowerRight.nRow );
1957         }
1958 
1959         if (aRes.Len()) // in case of multiple ranges add delimeter
1960             aRes.AppendAscii( ";" );
1961         aRes += String(aTmp);
1962     }
1963 
1964     return aRes;
1965 }
1966 
1967 
1968 //////////////////////////////////////////////////////////////////////
1969 
SwChartDataSource(const uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> & rLDS)1970 SwChartDataSource::SwChartDataSource(
1971         const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
1972     aLDS( rLDS )
1973 {
1974 }
1975 
1976 
~SwChartDataSource()1977 SwChartDataSource::~SwChartDataSource()
1978 {
1979 //    delete pTblCrsr;
1980 }
1981 
1982 
getDataSequences()1983 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences(  )
1984     throw (uno::RuntimeException)
1985 {
1986     vos::OGuard aGuard( Application::GetSolarMutex() );
1987     return aLDS;
1988 }
1989 
1990 
getImplementationName()1991 OUString SAL_CALL SwChartDataSource::getImplementationName(  )
1992     throw (uno::RuntimeException)
1993 {
1994     vos::OGuard aGuard( Application::GetSolarMutex() );
1995     return C2U("SwChartDataSource");
1996 }
1997 
1998 
supportsService(const OUString & rServiceName)1999 sal_Bool SAL_CALL SwChartDataSource::supportsService(
2000         const OUString& rServiceName )
2001     throw (uno::RuntimeException)
2002 {
2003     vos::OGuard aGuard( Application::GetSolarMutex() );
2004     return rServiceName.equalsAscii( SN_DATA_SOURCE );
2005 }
2006 
2007 
getSupportedServiceNames()2008 uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames(  )
2009     throw (uno::RuntimeException)
2010 {
2011     vos::OGuard aGuard( Application::GetSolarMutex() );
2012     uno::Sequence< OUString > aRes(1);
2013     aRes.getArray()[0] = C2U( SN_DATA_SOURCE );
2014     return aRes;
2015 }
2016 
2017 //////////////////////////////////////////////////////////////////////
2018 
SwChartDataSequence(SwChartDataProvider & rProvider,SwFrmFmt & rTblFmt,SwUnoCrsr * pTableCursor)2019 SwChartDataSequence::SwChartDataSequence(
2020         SwChartDataProvider &rProvider,
2021         SwFrmFmt   &rTblFmt,
2022         SwUnoCrsr  *pTableCursor ) :
2023     SwClient( &rTblFmt ),
2024     aEvtListeners( GetChartMutex() ),
2025     aModifyListeners( GetChartMutex() ),
2026     aRowLabelText( SW_RES( STR_CHART2_ROW_LABEL_TEXT ) ),
2027     aColLabelText( SW_RES( STR_CHART2_COL_LABEL_TEXT ) ),
2028     xDataProvider( &rProvider ),
2029     pDataProvider( &rProvider ),
2030     pTblCrsr( pTableCursor ),
2031     aCursorDepend( this, pTableCursor ),
2032     _pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
2033 {
2034     bDisposed = sal_False;
2035 
2036     acquire();
2037     try
2038     {
2039         const SwTable* pTable = SwTable::FindTable( &rTblFmt );
2040         if (pTable)
2041         {
2042             uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2043             pDataProvider->AddDataSequence( *pTable, xRef );
2044             pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
2045         }
2046         else {
2047             DBG_ERROR( "table missing" );
2048         }
2049     }
2050     catch (uno::RuntimeException &)
2051     {
2052         throw;
2053     }
2054     catch (uno::Exception &)
2055     {
2056     }
2057     release();
2058 
2059 #if OSL_DEBUG_LEVEL > 1
2060     OUString aRangeStr( getSourceRangeRepresentation() );
2061 
2062 	// check if it can properly convert into a SwUnoTableCrsr
2063 	// which is required for some functions
2064     SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2065     DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
2066     (void) pUnoTblCrsr;
2067 #endif
2068 }
2069 
2070 
SwChartDataSequence(const SwChartDataSequence & rObj)2071 SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
2072     SwChartDataSequenceBaseClass(),
2073     SwClient( rObj.GetFrmFmt() ),
2074     aEvtListeners( GetChartMutex() ),
2075     aModifyListeners( GetChartMutex() ),
2076     aRole( rObj.aRole ),
2077     aRowLabelText( SW_RES(STR_CHART2_ROW_LABEL_TEXT) ),
2078     aColLabelText( SW_RES(STR_CHART2_COL_LABEL_TEXT) ),
2079     xDataProvider( rObj.pDataProvider ),
2080     pDataProvider( rObj.pDataProvider ),
2081     pTblCrsr( rObj.pTblCrsr->Clone() ),
2082     aCursorDepend( this, pTblCrsr ),
2083     _pPropSet( rObj._pPropSet )
2084 {
2085     bDisposed = sal_False;
2086 
2087     acquire();
2088     try
2089     {
2090         const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2091         if (pTable)
2092         {
2093             uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2094             pDataProvider->AddDataSequence( *pTable, xRef );
2095             pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
2096         }
2097         else {
2098             DBG_ERROR( "table missing" );
2099         }
2100     }
2101     catch (uno::RuntimeException &)
2102     {
2103         throw;
2104     }
2105     catch (uno::Exception &)
2106     {
2107     }
2108     release();
2109 
2110 #if OSL_DEBUG_LEVEL > 1
2111     OUString aRangeStr( getSourceRangeRepresentation() );
2112 
2113     // check if it can properly convert into a SwUnoTableCrsr
2114 	// which is required for some functions
2115     SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2116     DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
2117     (void) pUnoTblCrsr;
2118 #endif
2119 }
2120 
2121 
~SwChartDataSequence()2122 SwChartDataSequence::~SwChartDataSequence()
2123 {
2124     // since the data-provider holds only weak references to the data-sequence
2125     // there should be no need here to release them explicitly...
2126 
2127     delete pTblCrsr;
2128 }
2129 
2130 
getUnoTunnelId()2131 const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
2132 {
2133     static uno::Sequence< sal_Int8 > aSeq = ::CreateUnoTunnelId();
2134     return aSeq;
2135 }
2136 
2137 
getSomething(const uno::Sequence<sal_Int8> & rId)2138 sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
2139     throw(uno::RuntimeException)
2140 {
2141     if( rId.getLength() == 16
2142         && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(),
2143                                         rId.getConstArray(), 16 ) )
2144     {
2145         return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
2146     }
2147     return 0;
2148 }
2149 
2150 
getData()2151 uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData(  )
2152     throw (uno::RuntimeException)
2153 {
2154     vos::OGuard aGuard( Application::GetSolarMutex() );
2155     if (bDisposed)
2156         throw lang::DisposedException();
2157 
2158     uno::Sequence< uno::Any > aRes;
2159     SwFrmFmt* pTblFmt = GetFrmFmt();
2160     if(pTblFmt)
2161     {
2162         SwTable* pTable = SwTable::FindTable( pTblFmt );
2163         if(!pTable->IsTblComplex())
2164         {
2165             SwRangeDescriptor aDesc;
2166             if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2167             {
2168 				//!! make copy of pTblCrsr (SwUnoCrsr )
2169 				// keep original cursor and make copy of it that gets handed
2170 				// over to the SwXCellRange object which takes ownership and
2171 				// thus will destroy the copy later.
2172                 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2173                 aRange.GetDataSequence( &aRes, 0, 0 );
2174             }
2175         }
2176     }
2177     return aRes;
2178 }
2179 
2180 
getSourceRangeRepresentation()2181 OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation(  )
2182     throw (uno::RuntimeException)
2183 {
2184     vos::OGuard aGuard( Application::GetSolarMutex() );
2185     if (bDisposed)
2186         throw lang::DisposedException();
2187 
2188     String aRes;
2189     SwFrmFmt* pTblFmt = GetFrmFmt();
2190     if (pTblFmt)
2191     {
2192         aRes = pTblFmt->GetName();
2193         String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2194         DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" );
2195         aRes += (sal_Unicode) '.';
2196         aRes += aCellRange;
2197     }
2198     return aRes;
2199 }
2200 
generateLabel(chart2::data::LabelOrigin eLabelOrigin)2201 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
2202         chart2::data::LabelOrigin eLabelOrigin )
2203     throw (uno::RuntimeException)
2204 {
2205     vos::OGuard aGuard( Application::GetSolarMutex() );
2206     if (bDisposed)
2207         throw lang::DisposedException();
2208 
2209     uno::Sequence< OUString > aLabels;
2210 
2211     {
2212         SwRangeDescriptor aDesc;
2213         sal_Bool bOk sal_False;
2214         SwFrmFmt* pTblFmt = GetFrmFmt();
2215         SwTable* pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
2216         if (!pTblFmt || !pTable || pTable->IsTblComplex())
2217             throw uno::RuntimeException();
2218         else
2219         {
2220             String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2221             DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" );
2222             bOk = FillRangeDescriptor( aDesc, aCellRange );
2223             DBG_ASSERT( bOk, "falied to get SwRangeDescriptor" );
2224         }
2225         if (bOk)
2226         {
2227             aDesc.Normalize();
2228             sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
2229             sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
2230             DBG_ASSERT( nColSpan == 1 || nRowSpan == 1,
2231                     "unexpected range of selected cells" );
2232 
2233             String aTxt;    // label text to be returned
2234             sal_Bool bReturnEmptyTxt = sal_False;
2235             sal_Bool bUseCol = sal_True;
2236             if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
2237                 bUseCol = sal_True;
2238             else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
2239                 bUseCol = sal_False;
2240             else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
2241             {
2242                 bUseCol = nColSpan < nRowSpan;
2243                 bReturnEmptyTxt = nColSpan == nRowSpan;
2244             }
2245             else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
2246             {
2247                 bUseCol = nColSpan > nRowSpan;
2248                 bReturnEmptyTxt = nColSpan == nRowSpan;
2249             }
2250             else {
2251                 DBG_ERROR( "unexpected case" );
2252             }
2253 
2254             // build label sequence
2255             //
2256             sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
2257             aLabels.realloc( nSeqLen );
2258             OUString *pLabels = aLabels.getArray();
2259             for (sal_Int32 i = 0;  i < nSeqLen;  ++i)
2260             {
2261                 if (!bReturnEmptyTxt)
2262                 {
2263                     aTxt = bUseCol ? aColLabelText : aRowLabelText;
2264                     sal_Int32 nCol = aDesc.nLeft;
2265                     sal_Int32 nRow = aDesc.nTop;
2266                     if (bUseCol)
2267                         nCol = nCol + i;
2268                     else
2269                         nRow = nRow + i;
2270                     String aCellName( lcl_GetCellName( nCol, nRow ) );
2271 
2272                     xub_StrLen nLen = aCellName.Len();
2273                     if (nLen)
2274                     {
2275                         const sal_Unicode *pBuf = aCellName.GetBuffer();
2276                         const sal_Unicode *pEnd = pBuf + nLen;
2277                         while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9'))
2278                             ++pBuf;
2279                         // start of number found?
2280                         if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
2281                         {
2282                             String aRplc;
2283                             String aNew;
2284                             if (bUseCol)
2285                             {
2286 								aRplc = String::CreateFromAscii( "%COLUMNLETTER" );
2287                                 aNew = String( aCellName.GetBuffer(), static_cast<xub_StrLen>(pBuf - aCellName.GetBuffer()) );
2288                             }
2289                             else
2290                             {
2291                                 aRplc = String::CreateFromAscii( "%ROWNUMBER" );
2292                                 aNew = String( pBuf, static_cast<xub_StrLen>((aCellName.GetBuffer() + nLen) - pBuf) );
2293                             }
2294                             xub_StrLen nPos = aTxt.Search( aRplc );
2295                             if (nPos != STRING_NOTFOUND)
2296                                 aTxt = aTxt.Replace( nPos, aRplc.Len(), aNew );
2297                         }
2298                     }
2299                 }
2300                 pLabels[i] = aTxt;
2301             }
2302         }
2303     }
2304 
2305     return aLabels;
2306 }
2307 
getNumberFormatKeyByIndex(::sal_Int32)2308 ::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
2309     ::sal_Int32 /*nIndex*/ )
2310     throw (lang::IndexOutOfBoundsException,
2311            uno::RuntimeException)
2312 {
2313     return 0;
2314 }
2315 
2316 
2317 
getTextualData()2318 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData(  )
2319     throw (uno::RuntimeException)
2320 {
2321     vos::OGuard aGuard( Application::GetSolarMutex() );
2322     if (bDisposed)
2323         throw lang::DisposedException();
2324 
2325     uno::Sequence< OUString > aRes;
2326     SwFrmFmt* pTblFmt = GetFrmFmt();
2327     if(pTblFmt)
2328     {
2329         SwTable* pTable = SwTable::FindTable( pTblFmt );
2330         if(!pTable->IsTblComplex())
2331         {
2332             SwRangeDescriptor aDesc;
2333             if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2334             {
2335 				//!! make copy of pTblCrsr (SwUnoCrsr )
2336 				// keep original cursor and make copy of it that gets handed
2337 				// over to the SwXCellRange object which takes ownership and
2338 				// thus will destroy the copy later.
2339                 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2340                 aRange.GetDataSequence( 0, &aRes, 0 );
2341             }
2342         }
2343     }
2344     return aRes;
2345 }
2346 
2347 
getNumericalData()2348 uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData(  )
2349     throw (uno::RuntimeException)
2350 {
2351     vos::OGuard aGuard( Application::GetSolarMutex() );
2352     if (bDisposed)
2353         throw lang::DisposedException();
2354 
2355     uno::Sequence< double > aRes;
2356     SwFrmFmt* pTblFmt = GetFrmFmt();
2357     if(pTblFmt)
2358     {
2359         SwTable* pTable = SwTable::FindTable( pTblFmt );
2360         if(!pTable->IsTblComplex())
2361         {
2362             SwRangeDescriptor aDesc;
2363             if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
2364             {
2365                 //!! make copy of pTblCrsr (SwUnoCrsr )
2366                 // keep original cursor and make copy of it that gets handed
2367                 // over to the SwXCellRange object which takes ownership and
2368                 // thus will destroy the copy later.
2369                 SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
2370 
2371                 // get numerical values and make an effort to return the
2372                 // numerical value for text formatted cells
2373                 aRange.GetDataSequence( 0, 0, &aRes, sal_True );
2374             }
2375         }
2376     }
2377     return aRes;
2378 }
2379 
2380 
createClone()2381 uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone(  )
2382     throw (uno::RuntimeException)
2383 {
2384     vos::OGuard aGuard( Application::GetSolarMutex() );
2385     if (bDisposed)
2386         throw lang::DisposedException();
2387     return new SwChartDataSequence( *this );
2388 }
2389 
2390 
getPropertySetInfo()2391 uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo(  )
2392     throw (uno::RuntimeException)
2393 {
2394     vos::OGuard aGuard( Application::GetSolarMutex() );
2395     if (bDisposed)
2396         throw lang::DisposedException();
2397 
2398     static uno::Reference< beans::XPropertySetInfo > xRes = _pPropSet->getPropertySetInfo();
2399     return xRes;
2400 }
2401 
2402 
setPropertyValue(const OUString & rPropertyName,const uno::Any & rValue)2403 void SAL_CALL SwChartDataSequence::setPropertyValue(
2404         const OUString& rPropertyName,
2405         const uno::Any& rValue )
2406     throw (beans::UnknownPropertyException, beans::PropertyVetoException, lang::IllegalArgumentException, lang::WrappedTargetException, uno::RuntimeException)
2407 {
2408     vos::OGuard aGuard( Application::GetSolarMutex() );
2409     if (bDisposed)
2410         throw lang::DisposedException();
2411 
2412     if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
2413     {
2414         if ( !(rValue >>= aRole) )
2415             throw lang::IllegalArgumentException();
2416     }
2417     else
2418         throw beans::UnknownPropertyException();
2419 }
2420 
2421 
getPropertyValue(const OUString & rPropertyName)2422 uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
2423         const OUString& rPropertyName )
2424     throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2425 {
2426     vos::OGuard aGuard( Application::GetSolarMutex() );
2427     if (bDisposed)
2428         throw lang::DisposedException();
2429 
2430     uno::Any aRes;
2431     if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
2432         aRes <<= aRole;
2433     else
2434         throw beans::UnknownPropertyException();
2435 
2436     return aRes;
2437 }
2438 
2439 
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)2440 void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
2441         const OUString& /*rPropertyName*/,
2442         const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2443     throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2444 {
2445     //vos::OGuard aGuard( Application::GetSolarMutex() );
2446     DBG_ERROR( "not implemented" );
2447 }
2448 
2449 
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)2450 void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
2451         const OUString& /*rPropertyName*/,
2452         const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
2453     throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2454 {
2455     //vos::OGuard aGuard( Application::GetSolarMutex() );
2456     DBG_ERROR( "not implemented" );
2457 }
2458 
2459 
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)2460 void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
2461         const OUString& /*rPropertyName*/,
2462         const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2463     throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2464 {
2465     //vos::OGuard aGuard( Application::GetSolarMutex() );
2466     DBG_ERROR( "not implemented" );
2467 }
2468 
2469 
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)2470 void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
2471         const OUString& /*rPropertyName*/,
2472         const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
2473     throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
2474 {
2475     //vos::OGuard aGuard( Application::GetSolarMutex() );
2476     DBG_ERROR( "not implemented" );
2477 }
2478 
2479 
getImplementationName()2480 OUString SAL_CALL SwChartDataSequence::getImplementationName(  )
2481     throw (uno::RuntimeException)
2482 {
2483     return C2U("SwChartDataSequence");
2484 }
2485 
2486 
supportsService(const OUString & rServiceName)2487 sal_Bool SAL_CALL SwChartDataSequence::supportsService(
2488         const OUString& rServiceName )
2489     throw (uno::RuntimeException)
2490 {
2491     return rServiceName.equalsAscii( SN_DATA_SEQUENCE );
2492 }
2493 
2494 
getSupportedServiceNames()2495 uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames(  )
2496     throw (uno::RuntimeException)
2497 {
2498     vos::OGuard aGuard( Application::GetSolarMutex() );
2499     uno::Sequence< OUString > aRes(1);
2500     aRes.getArray()[0] = C2U( SN_DATA_SEQUENCE );
2501     return aRes;
2502 }
2503 
2504 
Modify(const SfxPoolItem * pOld,const SfxPoolItem * pNew)2505 void SwChartDataSequence::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
2506 {
2507     ClientModify(this, pOld, pNew );
2508 
2509     // table was deleted or cursor was deleted
2510     if(!GetRegisteredIn() || !aCursorDepend.GetRegisteredIn())
2511 	{
2512         pTblCrsr = 0;
2513         dispose();
2514 	}
2515 	else
2516 	{
2517         setModified( sal_True );
2518 	}
2519 }
2520 
2521 
isModified()2522 sal_Bool SAL_CALL SwChartDataSequence::isModified(  )
2523     throw (uno::RuntimeException)
2524 {
2525     vos::OGuard aGuard( Application::GetSolarMutex() );
2526     if (bDisposed)
2527         throw lang::DisposedException();
2528 
2529     return sal_True;
2530 }
2531 
2532 
setModified(::sal_Bool bModified)2533 void SAL_CALL SwChartDataSequence::setModified(
2534         ::sal_Bool bModified )
2535     throw (beans::PropertyVetoException, uno::RuntimeException)
2536 {
2537     vos::OGuard aGuard( Application::GetSolarMutex() );
2538     if (bDisposed)
2539         throw lang::DisposedException();
2540 
2541     if (bModified)
2542 		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2543 }
2544 
2545 
addModifyListener(const uno::Reference<util::XModifyListener> & rxListener)2546 void SAL_CALL SwChartDataSequence::addModifyListener(
2547         const uno::Reference< util::XModifyListener >& rxListener )
2548     throw (uno::RuntimeException)
2549 {
2550     osl::MutexGuard  aGuard( GetChartMutex() );
2551     if (!bDisposed && rxListener.is())
2552         aModifyListeners.addInterface( rxListener );
2553 }
2554 
2555 
removeModifyListener(const uno::Reference<util::XModifyListener> & rxListener)2556 void SAL_CALL SwChartDataSequence::removeModifyListener(
2557         const uno::Reference< util::XModifyListener >& rxListener )
2558     throw (uno::RuntimeException)
2559 {
2560     osl::MutexGuard  aGuard( GetChartMutex() );
2561     if (!bDisposed && rxListener.is())
2562         aModifyListeners.removeInterface( rxListener );
2563 }
2564 
2565 
disposing(const lang::EventObject & rSource)2566 void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
2567     throw (uno::RuntimeException)
2568 {
2569     if (bDisposed)
2570         throw lang::DisposedException();
2571     if (rSource.Source == xDataProvider)
2572     {
2573         pDataProvider = 0;
2574         xDataProvider.clear();
2575     }
2576 }
2577 
2578 
dispose()2579 void SAL_CALL SwChartDataSequence::dispose(  )
2580     throw (uno::RuntimeException)
2581 {
2582     sal_Bool bMustDispose( sal_False );
2583 	{
2584 		osl::MutexGuard  aGuard( GetChartMutex() );
2585         bMustDispose = !bDisposed;
2586 		if (!bDisposed)
2587 			bDisposed = sal_True;
2588 	}
2589     if (bMustDispose)
2590     {
2591         bDisposed = sal_True;
2592         if (pDataProvider)
2593         {
2594             const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2595             if (pTable)
2596             {
2597                 uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
2598                 pDataProvider->RemoveDataSequence( *pTable, xRef );
2599             }
2600             else {
2601                 DBG_ERROR( "table missing" );
2602             }
2603 
2604 		//Comment: The bug is crashed for an exception threw out in SwCharDataSequence::setModified(), just because
2605 		//the SwCharDataSequence object has been disposed. Actually, the former design of SwClient will disband
2606 		//itself from the notification list in its destruction. But the SwCharDataSeqence wont be destructed but disposed
2607 		//in code (the data member SwChartDataSequence::bDisposed will be set to TRUE), the relationship between client
2608 		//and modification are not released. So any notification from modify object will lead said exception threw out.
2609 		//Recorrect the logic of code in SwChartDataSequence::Dispose(), release the relationship inside...
2610 		SwModify* pRegisteredIn = GetRegisteredInNonConst();
2611 		if (pRegisteredIn && pRegisteredIn->GetDepends())
2612 		{
2613 			pRegisteredIn->Remove(this);
2614 			pTblCrsr = NULL;
2615 		}
2616 
2617         }
2618 
2619         // require listeners to release references to this object
2620         lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
2621         aModifyListeners.disposeAndClear( aEvtObj );
2622         aEvtListeners.disposeAndClear( aEvtObj );
2623     }
2624 }
2625 
2626 
addEventListener(const uno::Reference<lang::XEventListener> & rxListener)2627 void SAL_CALL SwChartDataSequence::addEventListener(
2628         const uno::Reference< lang::XEventListener >& rxListener )
2629     throw (uno::RuntimeException)
2630 {
2631     osl::MutexGuard  aGuard( GetChartMutex() );
2632     if (!bDisposed && rxListener.is())
2633         aEvtListeners.addInterface( rxListener );
2634 }
2635 
2636 
removeEventListener(const uno::Reference<lang::XEventListener> & rxListener)2637 void SAL_CALL SwChartDataSequence::removeEventListener(
2638         const uno::Reference< lang::XEventListener >& rxListener )
2639     throw (uno::RuntimeException)
2640 {
2641     osl::MutexGuard  aGuard( GetChartMutex() );
2642     if (!bDisposed && rxListener.is())
2643         aEvtListeners.removeInterface( rxListener );
2644 }
2645 
2646 
DeleteBox(const SwTableBox & rBox)2647 sal_Bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
2648 {
2649 	if (bDisposed)
2650 		throw lang::DisposedException();
2651 
2652 #if OSL_DEBUG_LEVEL > 1
2653 	String aBoxName( rBox.GetName() );
2654 #endif
2655 
2656     // to be set if the last box of the data-sequence was removed here
2657     sal_Bool bNowEmpty = sal_False;
2658 
2659     // if the implementation cursor gets affected (i.e. thew box where it is located
2660     // in gets removed) we need to move it before that... (otherwise it does not need to change)
2661     //
2662     const SwStartNode* pPointStartNode = pTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2663     const SwStartNode* pMarkStartNode  = pTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2664     //
2665     if (!pTblCrsr->HasMark() || (pPointStartNode == rBox.GetSttNd()  &&  pMarkStartNode == rBox.GetSttNd()))
2666     {
2667         bNowEmpty = sal_True;
2668     }
2669     else if (pPointStartNode == rBox.GetSttNd()  ||  pMarkStartNode == rBox.GetSttNd())
2670     {
2671         sal_Int32 nPointRow = -1, nPointCol = -1;
2672         sal_Int32 nMarkRow  = -1, nMarkCol  = -1;
2673         const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2674         String aPointCellName( pTable->GetTblBox( pPointStartNode->GetIndex() )->GetName() );
2675 		String aMarkCellName( pTable->GetTblBox( pMarkStartNode->GetIndex() )->GetName() );
2676 
2677         lcl_GetCellPosition( aPointCellName, nPointCol, nPointRow );
2678         lcl_GetCellPosition( aMarkCellName,  nMarkCol,  nMarkRow );
2679         DBG_ASSERT( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
2680         DBG_ASSERT( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
2681 
2682         // move vertical or horizontal?
2683         DBG_ASSERT( nPointRow == nMarkRow || nPointCol == nMarkCol,
2684                 "row/col indices not matching" );
2685         DBG_ASSERT( nPointRow != nMarkRow || nPointCol != nMarkCol,
2686                 "point and mark are identical" );
2687         sal_Bool bMoveVertical      = (nPointCol == nMarkCol);
2688         sal_Bool bMoveHorizontal    = (nPointRow == nMarkRow);
2689 
2690         // get movement direction
2691         sal_Bool bMoveLeft  = sal_False;    // move left or right?
2692         sal_Bool bMoveUp    = sal_False;    // move up or down?
2693         if (bMoveVertical)
2694         {
2695             if (pPointStartNode == rBox.GetSttNd()) // move point?
2696                 bMoveUp = nPointRow > nMarkRow;
2697             else    // move mark
2698                 bMoveUp = nMarkRow > nPointRow;
2699         }
2700         else if (bMoveHorizontal)
2701         {
2702             if (pPointStartNode == rBox.GetSttNd()) // move point?
2703                 bMoveLeft = nPointCol > nMarkCol;
2704             else    // move mark
2705                 bMoveLeft = nMarkCol > nPointCol;
2706         }
2707         else {
2708             DBG_ERROR( "neither vertical nor horizontal movement" );
2709         }
2710 
2711         // get new box (position) to use...
2712         sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
2713         sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
2714 		if (bMoveVertical)
2715 			nRow += bMoveUp ? -1 : +1;
2716 		if (bMoveHorizontal)
2717 			nCol += bMoveLeft ? -1 : +1;
2718         String aNewCellName = lcl_GetCellName( nCol, nRow );
2719         SwTableBox* pNewBox = (SwTableBox*) pTable->GetTblBox( aNewCellName );
2720 
2721         if (pNewBox)    // set new position (cell range) to use
2722         {
2723             // So erh�lt man den ersten Inhaltsnode in einer gegebenen Zelle:
2724             // Zun�chst einen SwNodeIndex auf den Node hinter dem SwStartNode der Box...
2725             SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
2726             // Dies kann ein SwCntntNode sein, kann aber auch ein Tabellen oder Sectionnode sein,
2727             // deshalb das GoNext;
2728             SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode();
2729             if (!pCNd)
2730                 pCNd = GetFrmFmt()->GetDoc()->GetNodes().GoNext( &aIdx );
2731             //und damit kann man z.B. eine SwPosition erzeugen:
2732             SwPosition aNewPos( *pCNd );   // new position to beused with cursor
2733 
2734             // if the mark is to be changed make sure there is one...
2735             if (pMarkStartNode == rBox.GetSttNd() && !pTblCrsr->HasMark())
2736                 pTblCrsr->SetMark();
2737 
2738             // set cursor to new position...
2739             SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
2740                         pTblCrsr->GetPoint() : pTblCrsr->GetMark();
2741             if (pPos)
2742             {
2743                 pPos->nNode     = aNewPos.nNode;
2744                 pPos->nContent  = aNewPos.nContent;
2745             }
2746             else {
2747                 DBG_ERROR( "neither point nor mark available for change" );
2748             }
2749         }
2750         else {
2751             DBG_ERROR( "failed to get position" );
2752         }
2753     }
2754 
2755     return bNowEmpty;
2756 }
2757 
2758 
FillRangeDesc(SwRangeDescriptor & rRangeDesc) const2759 void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
2760 {
2761     SwFrmFmt* pTblFmt = GetFrmFmt();
2762     if(pTblFmt)
2763     {
2764         SwTable* pTable = SwTable::FindTable( pTblFmt );
2765         if(!pTable->IsTblComplex())
2766         {
2767             FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) );
2768         }
2769     }
2770 }
2771 
2772 /**
2773 SwChartDataSequence::ExtendTo
2774 
2775 extends the data-sequence by new cells added at the end of the direction
2776 the data-sequence points to.
2777 If the cells are already within the range of the sequence nothing needs
2778 to be done.
2779 If the cells are beyond the end of the sequence (are not adjacent to the
2780 current last cell) nothing can be done. Only if the cells are adjacent to
2781 the last cell they can be added.
2782 
2783 @returns     true if the data-sequence was changed.
2784 @param       bExtendCols
2785              specifies if columns or rows are to be extended
2786 @param       nFirstNew
2787              index of first new row/col to be included in data-sequence
2788 @param       nLastNew
2789              index of last new row/col to be included in data-sequence
2790 */
ExtendTo(bool bExtendCol,sal_Int32 nFirstNew,sal_Int32 nCount)2791 bool SwChartDataSequence::ExtendTo( bool bExtendCol,
2792         sal_Int32 nFirstNew, sal_Int32 nCount )
2793 {
2794     bool bChanged = false;
2795 
2796     SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
2797     //pUnoTblCrsr->MakeBoxSels();
2798 
2799     const SwStartNode *pStartNd  = 0;
2800     const SwTableBox  *pStartBox = 0;
2801     const SwTableBox  *pEndBox   = 0;
2802 
2803     const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
2804 	DBG_ASSERT( !pTable->IsTblComplex(), "table too complex" );
2805 	if (nCount < 1 || nFirstNew < 0 || pTable->IsTblComplex())
2806 		return false;
2807 
2808 	//
2809 	// get range descriptor (cell range) for current data-sequence
2810 	//
2811     pStartNd = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2812     pEndBox = pTable->GetTblBox( pStartNd->GetIndex() );
2813     const String aEndBox( pEndBox->GetName() );
2814 	//
2815     pStartNd = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
2816     pStartBox = pTable->GetTblBox( pStartNd->GetIndex() );
2817     const String aStartBox( pStartBox->GetName() );
2818 	//
2819     String aCellRange( aStartBox );     // note that cell range here takes the newly added rows/cols already into account
2820     aCellRange.AppendAscii( ":" );
2821     aCellRange += aEndBox;
2822     SwRangeDescriptor aDesc;
2823     FillRangeDescriptor( aDesc, aCellRange );
2824 
2825     String aNewStartCell;
2826     String aNewEndCell;
2827     if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
2828     {
2829         // new column cells adjacent to the bottom of the
2830 		// current data-sequence to be added...
2831         DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2832         aNewStartCell = lcl_GetCellName(aDesc.nLeft,  aDesc.nTop);
2833         aNewEndCell   = lcl_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
2834 		bChanged = true;
2835     }
2836     else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
2837     {
2838         // new column cells adjacent to the top of the
2839 		// current data-sequence to be added...
2840         DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
2841         aNewStartCell = lcl_GetCellName(aDesc.nLeft,  aDesc.nTop - nCount);
2842         aNewEndCell   = lcl_GetCellName(aDesc.nRight, aDesc.nBottom);
2843 		bChanged = true;
2844     }
2845     else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
2846     {
2847         // new row cells adjacent to the right of the
2848 		// current data-sequence to be added...
2849         DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2850         aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop);
2851         aNewEndCell   = lcl_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
2852 		bChanged = true;
2853     }
2854     else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
2855     {
2856         // new row cells adjacent to the left of the
2857 		// current data-sequence to be added...
2858         DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
2859         aNewStartCell = lcl_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
2860         aNewEndCell   = lcl_GetCellName(aDesc.nRight, aDesc.nBottom);
2861 		bChanged = true;
2862     }
2863 
2864 	if (bChanged)
2865 	{
2866 		// move table cursor to new start and end of data-sequence
2867         const SwTableBox *pNewStartBox = pTable->GetTblBox( aNewStartCell );
2868         const SwTableBox *pNewEndBox   = pTable->GetTblBox( aNewEndCell );
2869         pUnoTblCrsr->SetMark();
2870         pUnoTblCrsr->GetPoint()->nNode = *pNewEndBox->GetSttNd();
2871         pUnoTblCrsr->GetMark()->nNode  = *pNewStartBox->GetSttNd();
2872         pUnoTblCrsr->Move( fnMoveForward, fnGoNode );
2873         pUnoTblCrsr->MakeBoxSels();
2874 	}
2875 
2876     return bChanged;
2877 }
2878 
2879 //////////////////////////////////////////////////////////////////////
2880 
SwChartLabeledDataSequence()2881 SwChartLabeledDataSequence::SwChartLabeledDataSequence() :
2882     aEvtListeners( GetChartMutex() ),
2883     aModifyListeners( GetChartMutex() )
2884 {
2885     bDisposed = sal_False;
2886 }
2887 
2888 
~SwChartLabeledDataSequence()2889 SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
2890 {
2891 }
2892 
2893 
getValues()2894 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues(  )
2895     throw (uno::RuntimeException)
2896 {
2897     vos::OGuard aGuard( Application::GetSolarMutex() );
2898     if (bDisposed)
2899         throw lang::DisposedException();
2900     return xData;
2901 }
2902 
2903 
SetDataSequence(uno::Reference<chart2::data::XDataSequence> & rxDest,const uno::Reference<chart2::data::XDataSequence> & rxSource)2904 void SwChartLabeledDataSequence::SetDataSequence(
2905 		uno::Reference< chart2::data::XDataSequence >& rxDest,
2906 		const uno::Reference< chart2::data::XDataSequence >& rxSource)
2907 {
2908     uno::Reference< util::XModifyListener >  xML( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY );
2909     uno::Reference< lang::XEventListener >   xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY );
2910 
2911     // stop listening to old data-sequence
2912     uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
2913     if (xMB.is())
2914         xMB->removeModifyListener( xML );
2915     uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
2916     if (xC.is())
2917         xC->removeEventListener( xEL );
2918 
2919     rxDest = rxSource;
2920 
2921     // start listening to new data-sequence
2922     xC = uno::Reference< lang::XComponent >( rxDest, uno::UNO_QUERY );
2923     if (xC.is())
2924         xC->addEventListener( xEL );
2925     xMB = uno::Reference< util::XModifyBroadcaster >( rxDest, uno::UNO_QUERY );
2926     if (xMB.is())
2927         xMB->addModifyListener( xML );
2928 }
2929 
2930 
setValues(const uno::Reference<chart2::data::XDataSequence> & rxSequence)2931 void SAL_CALL SwChartLabeledDataSequence::setValues(
2932         const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2933     throw (uno::RuntimeException)
2934 {
2935     vos::OGuard aGuard( Application::GetSolarMutex() );
2936     if (bDisposed)
2937         throw lang::DisposedException();
2938 
2939     if (xData != rxSequence)
2940     {
2941 		SetDataSequence( xData, rxSequence );
2942         // inform listeners of changes
2943 		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2944     }
2945 }
2946 
2947 
getLabel()2948 uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel(  )
2949     throw (uno::RuntimeException)
2950 {
2951     vos::OGuard aGuard( Application::GetSolarMutex() );
2952     if (bDisposed)
2953         throw lang::DisposedException();
2954     return xLabels;
2955 }
2956 
2957 
setLabel(const uno::Reference<chart2::data::XDataSequence> & rxSequence)2958 void SAL_CALL SwChartLabeledDataSequence::setLabel(
2959         const uno::Reference< chart2::data::XDataSequence >& rxSequence )
2960     throw (uno::RuntimeException)
2961 {
2962     vos::OGuard aGuard( Application::GetSolarMutex() );
2963     if (bDisposed)
2964         throw lang::DisposedException();
2965 
2966     if (xLabels != rxSequence)
2967     {
2968 		SetDataSequence( xLabels, rxSequence );
2969         // inform listeners of changes
2970 		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
2971     }
2972 }
2973 
2974 
createClone()2975 uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone(  )
2976     throw (uno::RuntimeException)
2977 {
2978     vos::OGuard aGuard( Application::GetSolarMutex() );
2979     if (bDisposed)
2980         throw lang::DisposedException();
2981 
2982     uno::Reference< util::XCloneable > xRes;
2983 
2984     uno::Reference< util::XCloneable > xDataCloneable( xData, uno::UNO_QUERY );
2985     uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, uno::UNO_QUERY );
2986     SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence();
2987     if (xDataCloneable.is())
2988     {
2989         uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
2990         pRes->setValues( xDataClone );
2991     }
2992 
2993     if (xLabelsCloneable.is())
2994     {
2995         uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
2996         pRes->setLabel( xLabelsClone );
2997     }
2998     xRes = pRes;
2999     return xRes;
3000 }
3001 
3002 
getImplementationName()3003 OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName(  )
3004     throw (uno::RuntimeException)
3005 {
3006     return C2U("SwChartLabeledDataSequence");
3007 }
3008 
3009 
supportsService(const OUString & rServiceName)3010 sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
3011         const OUString& rServiceName )
3012     throw (uno::RuntimeException)
3013 {
3014     return rServiceName.equalsAscii( SN_LABELED_DATA_SEQUENCE );
3015 }
3016 
3017 
getSupportedServiceNames()3018 uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames(  )
3019     throw (uno::RuntimeException)
3020 {
3021     vos::OGuard aGuard( Application::GetSolarMutex() );
3022     uno::Sequence< OUString > aRes(1);
3023     aRes.getArray()[0] = C2U( SN_LABELED_DATA_SEQUENCE );
3024     return aRes;
3025 }
3026 
3027 
disposing(const lang::EventObject & rSource)3028 void SAL_CALL SwChartLabeledDataSequence::disposing(
3029         const lang::EventObject& rSource )
3030     throw (uno::RuntimeException)
3031 {
3032     osl::MutexGuard  aGuard( GetChartMutex() );
3033     uno::Reference< uno::XInterface > xRef( rSource.Source );
3034     if (xRef == xData)
3035         xData.clear();
3036     if (xRef == xLabels)
3037         xLabels.clear();
3038     if (!xData.is() && !xLabels.is())
3039         dispose();
3040 }
3041 
3042 
modified(const lang::EventObject & rEvent)3043 void SAL_CALL SwChartLabeledDataSequence::modified(
3044         const lang::EventObject& rEvent )
3045     throw (uno::RuntimeException)
3046 {
3047     if (rEvent.Source == xData || rEvent.Source == xLabels)
3048     {
3049 		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
3050     }
3051 }
3052 
3053 
addModifyListener(const uno::Reference<util::XModifyListener> & rxListener)3054 void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
3055         const uno::Reference< util::XModifyListener >& rxListener )
3056     throw (uno::RuntimeException)
3057 {
3058     osl::MutexGuard  aGuard( GetChartMutex() );
3059     if (!bDisposed && rxListener.is())
3060         aModifyListeners.addInterface( rxListener );
3061 }
3062 
3063 
removeModifyListener(const uno::Reference<util::XModifyListener> & rxListener)3064 void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
3065         const uno::Reference< util::XModifyListener >& rxListener )
3066     throw (uno::RuntimeException)
3067 {
3068     osl::MutexGuard  aGuard( GetChartMutex() );
3069     if (!bDisposed && rxListener.is())
3070         aModifyListeners.removeInterface( rxListener );
3071 }
3072 
3073 
dispose()3074 void SAL_CALL SwChartLabeledDataSequence::dispose(  )
3075     throw (uno::RuntimeException)
3076 {
3077     sal_Bool bMustDispose( sal_False );
3078 	{
3079 		osl::MutexGuard  aGuard( GetChartMutex() );
3080         bMustDispose = !bDisposed;
3081 		if (!bDisposed)
3082 			bDisposed = sal_True;
3083 	}
3084     if (bMustDispose)
3085     {
3086         bDisposed = sal_True;
3087 
3088         // require listeners to release references to this object
3089         lang::EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) );
3090         aModifyListeners.disposeAndClear( aEvtObj );
3091         aEvtListeners.disposeAndClear( aEvtObj );
3092     }
3093 }
3094 
3095 
addEventListener(const uno::Reference<lang::XEventListener> & rxListener)3096 void SAL_CALL SwChartLabeledDataSequence::addEventListener(
3097         const uno::Reference< lang::XEventListener >& rxListener )
3098     throw (uno::RuntimeException)
3099 {
3100     osl::MutexGuard  aGuard( GetChartMutex() );
3101     if (!bDisposed && rxListener.is())
3102         aEvtListeners.addInterface( rxListener );
3103 }
3104 
3105 
removeEventListener(const uno::Reference<lang::XEventListener> & rxListener)3106 void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
3107         const uno::Reference< lang::XEventListener >& rxListener )
3108     throw (uno::RuntimeException)
3109 {
3110     osl::MutexGuard  aGuard( GetChartMutex() );
3111     if (!bDisposed && rxListener.is())
3112         aEvtListeners.removeInterface( rxListener );
3113 }
3114 
3115 //////////////////////////////////////////////////////////////////////
3116