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