1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "chartpos.hxx"
21 #include "document.hxx"
22 #include "rechead.hxx"
26 bool lcl_hasValueDataButNoDates( ScDocument
* pDocument
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
)
29 if (pDocument
->HasValueData( nCol
, nRow
, nTab
))
31 //treat dates like text #i25706#
32 sal_uInt32 nNumberFormat
= pDocument
->GetNumberFormat( ScAddress( nCol
, nRow
, nTab
) );
33 short nType
= pDocument
->GetFormatTable()->GetType(nNumberFormat
);
34 bool bIsDate
= (nType
& NUMBERFORMAT_DATE
);
41 ScChartPositioner::ScChartPositioner( ScDocument
* pDoc
, SCTAB nTab
,
42 SCCOL nStartColP
, SCROW nStartRowP
, SCCOL nEndColP
, SCROW nEndRowP
) :
45 eGlue( SC_CHARTGLUE_NA
),
50 bDummyUpperLeft( false )
52 SetRangeList( ScRange( nStartColP
, nStartRowP
, nTab
, nEndColP
, nEndRowP
, nTab
) );
56 ScChartPositioner::ScChartPositioner( ScDocument
* pDoc
, const ScRangeListRef
& rRangeList
) :
57 aRangeListRef( rRangeList
),
60 eGlue( SC_CHARTGLUE_NA
),
65 bDummyUpperLeft( false )
67 if ( aRangeListRef
.Is() )
71 ScChartPositioner::ScChartPositioner( const ScChartPositioner
& rPositioner
) :
72 aRangeListRef( rPositioner
.aRangeListRef
),
73 pDocument(rPositioner
.pDocument
),
75 eGlue(rPositioner
.eGlue
),
76 nStartCol(rPositioner
.nStartCol
),
77 nStartRow(rPositioner
.nStartRow
),
78 bColHeaders(rPositioner
.bColHeaders
),
79 bRowHeaders(rPositioner
.bRowHeaders
),
80 bDummyUpperLeft( rPositioner
.bDummyUpperLeft
)
84 ScChartPositioner::~ScChartPositioner()
89 sal_Bool
ScChartPositioner::operator==(const ScChartPositioner
& rCmp
) const
91 return bColHeaders
== rCmp
.bColHeaders
92 && bRowHeaders
== rCmp
.bRowHeaders
93 && *aRangeListRef
== *rCmp
.aRangeListRef
;
96 void ScChartPositioner::SetRangeList( const ScRange
& rRange
)
98 aRangeListRef
= new ScRangeList
;
99 aRangeListRef
->Append( rRange
);
103 void ScChartPositioner::GlueState()
105 if ( eGlue
!= SC_CHARTGLUE_NA
)
107 bDummyUpperLeft
= false;
109 if ( aRangeListRef
->size() <= 1 )
111 if ( !aRangeListRef
->empty() )
113 pR
= aRangeListRef
->front();
114 if ( pR
->aStart
.Tab() == pR
->aEnd
.Tab() )
115 eGlue
= SC_CHARTGLUE_NONE
;
117 eGlue
= SC_CHARTGLUE_COLS
; // several tables column by column
118 nStartCol
= pR
->aStart
.Col();
119 nStartRow
= pR
->aStart
.Row();
130 pR
= aRangeListRef
->front();
131 nStartCol
= pR
->aStart
.Col();
132 nStartRow
= pR
->aStart
.Row();
133 SCCOL nMaxCols
, nEndCol
;
134 SCROW nMaxRows
, nEndRow
;
135 nMaxCols
= nEndCol
= 0;
136 nMaxRows
= nEndRow
= 0;
138 // <= so 1 extra pass after last item
139 for ( size_t i
= 1, nRanges
= aRangeListRef
->size(); i
<= nRanges
; ++i
)
140 { // detect spanning/surrounding area etc.
141 SCCOLROW nTmp
, n1
, n2
;
142 if ( (n1
= pR
->aStart
.Col()) < nStartCol
) nStartCol
= static_cast<SCCOL
>(n1
);
143 if ( (n2
= pR
->aEnd
.Col() ) > nEndCol
) nEndCol
= static_cast<SCCOL
>(n2
);
144 if ( (nTmp
= n2
- n1
+ 1 ) > nMaxCols
) nMaxCols
= static_cast<SCCOL
>(nTmp
);
145 if ( (n1
= pR
->aStart
.Row()) < nStartRow
) nStartRow
= static_cast<SCROW
>(n1
);
146 if ( (n2
= pR
->aEnd
.Row() ) > nEndRow
) nEndRow
= static_cast<SCROW
>(n2
);
147 if ( (nTmp
= n2
- n1
+ 1 ) > nMaxRows
) nMaxRows
= static_cast<SCROW
>(nTmp
);
149 // in last pass; i = nRanges so don't use at()
151 pR
= (*aRangeListRef
)[i
];
153 SCCOL nC
= nEndCol
- nStartCol
+ 1;
156 eGlue
= SC_CHARTGLUE_ROWS
;
159 SCROW nR
= nEndRow
- nStartRow
+ 1;
162 eGlue
= SC_CHARTGLUE_COLS
;
165 sal_uLong nCR
= (sal_uLong
)nC
* nR
;
169 First do it simple without bit masking. A maximum of 8MB could be allocated
170 this way (256 Cols x 32000 Rows). That could be reduced to 2MB by
171 using 2 Bits per entry, but it is faster this way.
172 Another optimization would be to store only used rows/columns in the array, but
173 would mean another iteration of the RangeList indirect access to the array. */
175 const sal_uInt8 nHole
= 0;
176 const sal_uInt8 nOccu
= 1;
177 const sal_uInt8 nFree
= 2;
178 const sal_uInt8 nGlue
= 3;
180 sal_uInt8
* pA
= new sal_uInt8
[ nCR
];
181 memset( pA
, 0, nCR
* sizeof(sal_uInt8
) );
183 SCCOL nCol
, nCol1
, nCol2
;
184 SCROW nRow
, nRow1
, nRow2
;
185 for ( size_t i
= 0, nRanges
= aRangeListRef
->size(); i
< nRanges
; ++i
)
186 { // mark selections as used in 2D
187 pR
= (*aRangeListRef
)[i
];
188 nCol1
= pR
->aStart
.Col() - nStartCol
;
189 nCol2
= pR
->aEnd
.Col() - nStartCol
;
190 nRow1
= pR
->aStart
.Row() - nStartRow
;
191 nRow2
= pR
->aEnd
.Row() - nStartRow
;
192 for ( nCol
= nCol1
; nCol
<= nCol2
; nCol
++ )
194 p
= pA
+ (sal_uLong
)nCol
* nR
+ nRow1
;
195 for ( nRow
= nRow1
; nRow
<= nRow2
; nRow
++, p
++ )
199 sal_Bool bGlue
= sal_True
;
201 sal_Bool bGlueCols
= false;
202 for ( nCol
= 0; bGlue
&& nCol
< nC
; nCol
++ )
203 { // iterate columns and try to mark as unused
204 p
= pA
+ (sal_uLong
)nCol
* nR
;
205 for ( nRow
= 0; bGlue
&& nRow
< nR
; nRow
++, p
++ )
208 { // If there's one right in the middle, we can't combine.
209 // If it were at the edge, we could combine, if in this Column
210 // in every set line, one is set.
211 if ( nRow
> 0 && nCol
> 0 )
212 bGlue
= false; // nCol==0 can be DummyUpperLeft
219 if ( bGlue
&& *(p
= (pA
+ ((((sal_uLong
)nCol
+1) * nR
) - 1))) == nFree
)
220 { // mark column as totally unused
222 bGlueCols
= sal_True
; // one unused column at least
226 sal_Bool bGlueRows
= false;
227 for ( nRow
= 0; bGlue
&& nRow
< nR
; nRow
++ )
228 { // iterate rows and try to mark as unused
230 for ( nCol
= 0; bGlue
&& nCol
< nC
; nCol
++, p
+=nR
)
234 if ( nCol
> 0 && nRow
> 0 )
235 bGlue
= false; // nRow==0 can be DummyUpperLeft
242 if ( bGlue
&& *(p
= (pA
+ ((((sal_uLong
)nC
-1) * nR
) + nRow
))) == nFree
)
243 { // mark row as totally unused
245 bGlueRows
= sal_True
; // one unused row at least
249 // If n=1: The upper left corner could be automagically pulled in for labeling
251 for ( sal_uLong n
= 1; bGlue
&& n
< nCR
; n
++, p
++ )
252 { // An untouched field means we could neither reach it through rows nor columns,
253 // thus we can't combine anything
259 if ( bGlueCols
&& bGlueRows
)
260 eGlue
= SC_CHARTGLUE_BOTH
;
261 else if ( bGlueRows
)
262 eGlue
= SC_CHARTGLUE_ROWS
;
264 eGlue
= SC_CHARTGLUE_COLS
;
266 bDummyUpperLeft
= sal_True
;
270 eGlue
= SC_CHARTGLUE_NONE
;
276 void ScChartPositioner::CheckColRowHeaders()
278 SCCOL nCol1
, nCol2
, iCol
;
279 SCROW nRow1
, nRow2
, iRow
;
282 sal_Bool bColStrings
= sal_True
;
283 sal_Bool bRowStrings
= sal_True
;
285 if ( aRangeListRef
->size() == 1 )
287 aRangeListRef
->front()->GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
288 if ( nCol1
> nCol2
|| nRow1
> nRow2
)
289 bColStrings
= bRowStrings
= false;
292 for (iCol
=nCol1
; iCol
<=nCol2
&& bColStrings
; iCol
++)
294 if (lcl_hasValueDataButNoDates( pDocument
, iCol
, nRow1
, nTab1
))
297 for (iRow
=nRow1
; iRow
<=nRow2
&& bRowStrings
; iRow
++)
299 if (lcl_hasValueDataButNoDates( pDocument
, nCol1
, iRow
, nTab1
))
306 sal_Bool bVert
= (eGlue
== SC_CHARTGLUE_NONE
|| eGlue
== SC_CHARTGLUE_ROWS
);
307 for ( size_t i
= 0, nRanges
= aRangeListRef
->size();
308 (i
< nRanges
) && (bColStrings
|| bRowStrings
);
312 ScRange
* pR
= (*aRangeListRef
)[i
];
313 pR
->GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
314 sal_Bool bTopRow
= (nRow1
== nStartRow
);
315 if ( bRowStrings
&& (bVert
|| nCol1
== nStartCol
) )
316 { // NONE or ROWS: RowStrings in every selection possible
317 // COLS or BOTH: only from first column
318 if ( nCol1
<= nCol2
)
319 for (iRow
=nRow1
; iRow
<=nRow2
&& bRowStrings
; iRow
++)
321 if (lcl_hasValueDataButNoDates( pDocument
, nCol1
, iRow
, nTab1
))
325 if ( bColStrings
&& bTopRow
)
326 { // ColStrings only from first row
327 if ( nRow1
<= nRow2
)
328 for (iCol
=nCol1
; iCol
<=nCol2
&& bColStrings
; iCol
++)
330 if (lcl_hasValueDataButNoDates( pDocument
, iCol
, nRow1
, nTab1
))
336 bColHeaders
= bColStrings
;
337 bRowHeaders
= bRowStrings
;
340 const ScChartPositionMap
* ScChartPositioner::GetPositionMap()
347 void ScChartPositioner::CreatePositionMap()
349 if ( eGlue
== SC_CHARTGLUE_NA
&& pPositionMap
)
358 SCSIZE nColAdd
= bRowHeaders
? 1 : 0;
359 SCSIZE nRowAdd
= bColHeaders
? 1 : 0;
361 SCCOL nCol
, nCol1
, nCol2
;
362 SCROW nRow
, nRow1
, nRow2
;
363 SCTAB nTab
, nTab1
, nTab2
;
366 // real size (without hidden rows/columns)
369 SCSIZE nColCount
= 0;
370 SCSIZE nRowCount
= 0;
374 const sal_Bool bNoGlue
= (eGlue
== SC_CHARTGLUE_NONE
);
375 ColumnMap
* pCols
= new ColumnMap
;
376 SCROW nNoGlueRow
= 0;
377 for ( size_t i
= 0, nRanges
= aRangeListRef
->size(); i
< nRanges
; ++i
)
379 ScRange
* pR
= (*aRangeListRef
)[i
];
380 pR
->GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
381 for ( nTab
= nTab1
; nTab
<= nTab2
; nTab
++ )
383 // nTab in ColKey to allow to have the same col/row in another tabe
384 sal_uLong nInsCol
= (static_cast<sal_uLong
>(nTab
) << 16) | (bNoGlue
? 0 :
385 static_cast<sal_uLong
>(nCol1
));
386 for ( nCol
= nCol1
; nCol
<= nCol2
; ++nCol
, ++nInsCol
)
389 ColumnMap::const_iterator it
= pCols
->find( nInsCol
);
390 if ( it
== pCols
->end() )
393 pCols
->insert( ColumnMap::value_type( nInsCol
, pCol
) );
398 // in other table a new ColKey already was created,
399 // the rows must be equal to be filled with Dummy
400 sal_uLong nInsRow
= (bNoGlue
? nNoGlueRow
: nRow1
);
401 for ( nRow
= nRow1
; nRow
<= nRow2
; nRow
++, nInsRow
++ )
403 if ( pCol
->find( nInsRow
) == pCol
->end() )
405 pCol
->insert( RowMap::value_type( nInsRow
, new ScAddress( nCol
, nRow
, nTab
) ) );
410 // For NoGlue: associated tables will be rendered as ColGlue
411 nNoGlueRow
+= nRow2
- nRow1
+ 1;
415 nColCount
= static_cast< SCSIZE
>( pCols
->size());
416 if ( !pCols
->empty() )
418 RowMap
* pCol
= pCols
->begin()->second
;
419 if ( bDummyUpperLeft
)
420 (*pCol
)[ 0 ] = NULL
; // Dummy for labeling
421 nRowCount
= static_cast< SCSIZE
>( pCol
->size());
426 nColCount
-= nColAdd
;
428 nRowCount
-= nRowAdd
;
430 if ( nColCount
==0 || nRowCount
==0 )
431 { // create an entry without data
433 if ( !pCols
->empty() )
434 pCol
= pCols
->begin()->second
;
438 (*pCols
)[ 0 ] = pCol
;
441 if ( !pCol
->empty() )
442 { // cannot be if nColCount==0 || nRowCount==0
443 ScAddress
* pPos
= pCol
->begin()->second
;
446 sal_uLong nCurrentKey
= pCol
->begin()->first
;
448 (*pCol
)[ nCurrentKey
] = NULL
;
460 { // fill gaps with Dummies, first column is master
461 RowMap
* pFirstCol
= pCols
->begin()->second
;
462 sal_uLong nCount
= pFirstCol
->size();
463 RowMap::const_iterator it1
= pFirstCol
->begin();
464 for ( sal_uLong n
= 0; n
< nCount
; n
++, ++it1
)
466 sal_uLong nKey
= it1
->first
;
467 for (ColumnMap::const_iterator it2
= ++pCols
->begin(); it2
!= pCols
->end(); ++it2
)
468 it2
->second
->insert( RowMap::value_type( nKey
, (ScAddress
*)NULL
)); // no data
473 pPositionMap
= new ScChartPositionMap( static_cast<SCCOL
>(nColCount
), static_cast<SCROW
>(nRowCount
),
474 static_cast<SCCOL
>(nColAdd
), static_cast<SCROW
>(nRowAdd
), *pCols
);
477 for (ColumnMap::const_iterator it
= pCols
->begin(); it
!= pCols
->end(); ++it
)
478 { // Only delete tables, not the ScAddress*!
485 ScChartPositionMap::ScChartPositionMap( SCCOL nChartCols
, SCROW nChartRows
,
486 SCCOL nColAdd
, SCROW nRowAdd
, ColumnMap
& rCols
) :
487 ppData( new ScAddress
* [ nChartCols
* nChartRows
] ),
488 ppColHeader( new ScAddress
* [ nChartCols
] ),
489 ppRowHeader( new ScAddress
* [ nChartRows
] ),
490 nCount( (sal_uLong
) nChartCols
* nChartRows
),
491 nColCount( nChartCols
),
492 nRowCount( nChartRows
)
494 OSL_ENSURE( nColCount
&& nRowCount
, "ScChartPositionMap without dimension" );
496 ColumnMap::const_iterator pColIter
= rCols
.begin();
497 RowMap
* pCol1
= pColIter
->second
;
498 RowMap::const_iterator pPos1Iter
;
501 pPos1Iter
= pCol1
->begin();
507 for ( ; nRow
< nRowCount
&& pPos1Iter
!= pCol1
->end(); nRow
++ )
509 ppRowHeader
[ nRow
] = pPos1Iter
->second
;
512 for ( ; nRow
< nRowCount
; nRow
++ )
513 ppRowHeader
[ nRow
] = NULL
;
518 for ( ; nRow
< nRowCount
&& pPos1Iter
!= pCol1
->end(); nRow
++ )
520 ppRowHeader
[ nRow
] = pPos1Iter
->second
?
521 new ScAddress( *pPos1Iter
->second
) : NULL
;
524 for ( ; nRow
< nRowCount
; nRow
++ )
525 ppRowHeader
[ nRow
] = NULL
;
532 // data column by column and column-header
533 sal_uLong nIndex
= 0;
534 for ( SCCOL nCol
= 0; nCol
< nColCount
; nCol
++ )
536 if ( pColIter
!= rCols
.end() )
538 RowMap
* pCol2
= pColIter
->second
;
539 RowMap::const_iterator pPosIter
= pCol2
->begin();
540 if ( pPosIter
!= pCol2
->end() )
544 ppColHeader
[ nCol
] = pPosIter
->second
; // independent
548 ppColHeader
[ nCol
] = pPosIter
->second
?
549 new ScAddress( *pPosIter
->second
) : NULL
;
553 for ( ; nRow
< nRowCount
&& pPosIter
!= pCol2
->end(); nRow
++, nIndex
++ )
555 ppData
[ nIndex
] = pPosIter
->second
;
558 for ( ; nRow
< nRowCount
; nRow
++, nIndex
++ )
559 ppData
[ nIndex
] = NULL
;
565 ppColHeader
[ nCol
] = NULL
;
566 for ( SCROW nRow
= 0; nRow
< nRowCount
; nRow
++, nIndex
++ )
568 ppData
[ nIndex
] = NULL
;
575 ScChartPositionMap::~ScChartPositionMap()
577 for ( sal_uLong nIndex
=0; nIndex
< nCount
; nIndex
++ )
579 delete ppData
[nIndex
];
584 for ( j
=0; j
< nColCount
; j
++ )
586 delete ppColHeader
[j
];
588 delete [] ppColHeader
;
590 for ( i
=0; i
< nRowCount
; i
++ )
592 delete ppRowHeader
[i
];
594 delete [] ppRowHeader
;
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */