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