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