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 "sheetdatabuffer.hxx"
23 #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
24 #include <com/sun/star/sheet/XCellRangeData.hpp>
25 #include <com/sun/star/sheet/XFormulaTokens.hpp>
26 #include <com/sun/star/sheet/XMultipleOperation.hpp>
27 #include <com/sun/star/table/XCell.hpp>
28 #include <com/sun/star/text/XText.hpp>
29 #include <com/sun/star/util/DateTime.hpp>
30 #include <com/sun/star/util/NumberFormat.hpp>
31 #include <com/sun/star/util/XMergeable.hpp>
32 #include <com/sun/star/util/XNumberFormatTypes.hpp>
33 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
34 #include <rtl/ustrbuf.hxx>
35 #include <editeng/boxitem.hxx>
36 #include <editeng/editobj.hxx>
37 #include <svl/eitem.hxx>
38 #include <oox/helper/containerhelper.hxx>
39 #include <oox/helper/propertymap.hxx>
40 #include <oox/helper/propertyset.hxx>
41 #include <oox/token/tokens.hxx>
42 #include "addressconverter.hxx"
43 #include "biffinputstream.hxx"
44 #include "formulaparser.hxx"
45 #include "sharedstringsbuffer.hxx"
46 #include "unitconverter.hxx"
47 #include "convuno.hxx"
48 #include "markdata.hxx"
49 #include "rangelst.hxx"
50 #include "document.hxx"
51 #include "scitems.hxx"
52 #include "formulacell.hxx"
53 #include "docpool.hxx"
54 #include "paramisc.hxx"
55 #include "documentimport.hxx"
56 #include "formulabuffer.hxx"
57 #include <numformat.hxx>
62 using namespace ::com::sun::star::lang
;
63 using namespace ::com::sun::star::sheet
;
64 using namespace ::com::sun::star::table
;
65 using namespace ::com::sun::star::text
;
66 using namespace ::com::sun::star::uno
;
67 using namespace ::com::sun::star::util
;
69 CellModel::CellModel() :
70 mnCellType( XML_TOKEN_INVALID
),
72 mbShowPhonetic( false )
76 CellFormulaModel::CellFormulaModel() :
77 mnFormulaType( XML_TOKEN_INVALID
),
82 bool CellFormulaModel::isValidArrayRef( const CellAddress
& rCellAddr
)
85 (maFormulaRef
.Sheet
== rCellAddr
.Sheet
) &&
86 (maFormulaRef
.StartColumn
== rCellAddr
.Column
) &&
87 (maFormulaRef
.StartRow
== rCellAddr
.Row
);
90 bool CellFormulaModel::isValidSharedRef( const CellAddress
& rCellAddr
)
93 (maFormulaRef
.Sheet
== rCellAddr
.Sheet
) &&
94 (maFormulaRef
.StartColumn
<= rCellAddr
.Column
) && (rCellAddr
.Column
<= maFormulaRef
.EndColumn
) &&
95 (maFormulaRef
.StartRow
<= rCellAddr
.Row
) && (rCellAddr
.Row
<= maFormulaRef
.EndRow
);
98 DataTableModel::DataTableModel() :
101 mbRef1Deleted( false ),
102 mbRef2Deleted( false )
106 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper
& rHelper
) :
107 WorksheetHelper( rHelper
),
112 void CellBlockBuffer::setColSpans( sal_Int32 nRow
, const ValueRangeSet
& rColSpans
)
114 OSL_ENSURE( maColSpans
.count( nRow
) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
115 OSL_ENSURE( (mnCurrRow
< nRow
) && (maColSpans
.empty() || (maColSpans
.rbegin()->first
< nRow
)), "CellBlockBuffer::setColSpans - rows are unsorted" );
116 if( (mnCurrRow
< nRow
) && (maColSpans
.count( nRow
) == 0) )
117 maColSpans
[ nRow
] = rColSpans
.getRanges();
120 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper
& rHelper
) :
121 WorksheetHelper( rHelper
),
122 maCellBlocks( rHelper
),
123 mbPendingSharedFmla( false )
127 void SheetDataBuffer::setColSpans( sal_Int32 nRow
, const ValueRangeSet
& rColSpans
)
129 maCellBlocks
.setColSpans( nRow
, rColSpans
);
132 void SheetDataBuffer::setBlankCell( const CellModel
& rModel
)
134 setCellFormat( rModel
);
137 void SheetDataBuffer::setValueCell( const CellModel
& rModel
, double fValue
)
139 putValue( rModel
.maCellAddr
, fValue
);
140 setCellFormat( rModel
);
143 void SheetDataBuffer::setStringCell( const CellModel
& rModel
, const OUString
& rText
)
145 putString( rModel
.maCellAddr
, rText
);
146 setCellFormat( rModel
);
149 void SheetDataBuffer::setStringCell( const CellModel
& rModel
, const RichStringRef
& rxString
)
151 OSL_ENSURE( rxString
.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
152 const oox::xls::Font
* pFirstPortionFont
= getStyles().getFontFromCellXf( rModel
.mnXfId
).get();
154 if( rxString
->extractPlainString( aText
, pFirstPortionFont
) )
156 setStringCell( rModel
, aText
);
160 putRichString( rModel
.maCellAddr
, *rxString
, pFirstPortionFont
);
161 setCellFormat( rModel
);
165 void SheetDataBuffer::setStringCell( const CellModel
& rModel
, sal_Int32 nStringId
)
167 RichStringRef xString
= getSharedStrings().getString( nStringId
);
169 setStringCell( rModel
, xString
);
171 setBlankCell( rModel
);
174 void SheetDataBuffer::setDateTimeCell( const CellModel
& rModel
, const ::com::sun::star::util::DateTime
& rDateTime
)
176 // write serial date/time value into the cell
177 double fSerial
= getUnitConverter().calcSerialFromDateTime( rDateTime
);
178 setValueCell( rModel
, fSerial
);
179 // set appropriate number format
180 using namespace ::com::sun::star::util::NumberFormat
;
181 sal_Int16 nStdFmt
= (fSerial
< 1.0) ? TIME
: (((rDateTime
.Hours
> 0) || (rDateTime
.Minutes
> 0) || (rDateTime
.Seconds
> 0)) ? DATETIME
: DATE
);
182 setStandardNumFmt( rModel
.maCellAddr
, nStdFmt
);
185 void SheetDataBuffer::setBooleanCell( const CellModel
& rModel
, bool bValue
)
187 getFormulaBuffer().setCellFormula(
188 rModel
.maCellAddr
, bValue
? OUString("TRUE()") : OUString("FALSE()"));
190 // #108770# set 'Standard' number format for all Boolean cells
191 setCellFormat( rModel
);
194 void SheetDataBuffer::setErrorCell( const CellModel
& rModel
, const OUString
& rErrorCode
)
196 // Using the formula compiler now we can simply pass on the error string.
197 getFormulaBuffer().setCellFormula( rModel
.maCellAddr
, rErrorCode
);
198 setCellFormat( rModel
);
201 void SheetDataBuffer::setErrorCell( const CellModel
& rModel
, sal_uInt8 nErrorCode
)
203 setErrorCell( rModel
, getUnitConverter().calcErrorString( nErrorCode
));
206 void SheetDataBuffer::setDateCell( const CellModel
& rModel
, const OUString
& rDateString
)
208 ScDocument
& rDoc
= getScDocument();
209 SvNumberFormatter
* pFormatter
= rDoc
.GetFormatTable();
212 sal_uInt32 nFormatIndex
= 0;
213 bool bValid
= pFormatter
->IsNumberFormat( rDateString
, nFormatIndex
, fValue
);
216 setValueCell( rModel
, fValue
);
219 void SheetDataBuffer::createSharedFormula(const CellAddress
& rAddr
, const ApiTokenSequence
& rTokens
)
221 BinAddress
aAddr(rAddr
);
222 maSharedFormulas
[aAddr
] = rTokens
;
223 if( mbPendingSharedFmla
)
224 setCellFormula( maSharedFmlaAddr
, resolveSharedFormula( maSharedBaseAddr
) );
227 void SheetDataBuffer::setFormulaCell( const CellModel
& rModel
, const ApiTokenSequence
& rTokens
)
229 mbPendingSharedFmla
= false;
230 ApiTokenSequence aTokens
;
232 /* Detect special token passed as placeholder for array formulas, shared
233 formulas, and table operations. In BIFF, these formulas are represented
234 by a single tExp resp. tTbl token. If the formula parser finds these
235 tokens, it puts a single OPCODE_BAD token with the base address and
236 formula type into the token sequence. This information will be
237 extracted here, and in case of a shared formula, the shared formula
238 buffer will generate the resulting formula token array. */
239 ApiSpecialTokenInfo aTokenInfo
;
240 if( rTokens
.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo
, rTokens
) )
242 /* The second member of the token info is set to true, if the formula
243 represents a table operation, which will be skipped. In BIFF12 it
244 is not possible to distinguish array and shared formulas
245 (BIFF5/BIFF8 provide this information with a special flag in the
247 if( !aTokenInfo
.Second
)
249 /* Construct the token array representing the shared formula. If
250 the returned sequence is empty, the definition of the shared
251 formula has not been loaded yet, or the cell is part of an
252 array formula. In this case, the cell will be remembered. After
253 reading the formula definition it will be retried to insert the
254 formula via retryPendingSharedFormulaCell(). */
255 BinAddress
aBaseAddr( aTokenInfo
.First
);
256 aTokens
= resolveSharedFormula( aTokenInfo
.First
);
257 if( !aTokens
.hasElements() )
259 maSharedFmlaAddr
= rModel
.maCellAddr
;
260 maSharedBaseAddr
= aTokenInfo
.First
;
261 mbPendingSharedFmla
= true;
267 // simple formula, use the passed token array
271 setCellFormula( rModel
.maCellAddr
, aTokens
);
272 setCellFormat( rModel
);
275 void SheetDataBuffer::createArrayFormula( const CellRangeAddress
& rRange
, const ApiTokenSequence
& rTokens
)
277 /* Array formulas will be inserted later in finalizeImport(). This is
278 needed to not disturb collecting all the cells, which will be put into
279 the sheet in large blocks to increase performance. */
280 maArrayFormulas
.push_back( ArrayFormula( rRange
, rTokens
) );
283 void SheetDataBuffer::createTableOperation( const CellRangeAddress
& rRange
, const DataTableModel
& rModel
)
285 /* Table operations will be inserted later in finalizeImport(). This is
286 needed to not disturb collecting all the cells, which will be put into
287 the sheet in large blocks to increase performance. */
288 maTableOperations
.push_back( TableOperation( rRange
, rModel
) );
291 void SheetDataBuffer::setRowFormat( sal_Int32 nRow
, sal_Int32 nXfId
, bool bCustomFormat
)
293 // set row formatting
296 // try to expand cached row range, if formatting is equal
297 if( (maXfIdRowRange
.maRowRange
.mnLast
< 0) || !maXfIdRowRange
.tryExpand( nRow
, nXfId
) )
300 maXfIdRowRangeList
[ maXfIdRowRange
.mnXfId
].push_back( maXfIdRowRange
.maRowRange
);
301 maXfIdRowRange
.set( nRow
, nXfId
);
304 else if( maXfIdRowRange
.maRowRange
.mnLast
>= 0 )
306 // finish last cached row range
307 maXfIdRowRangeList
[ maXfIdRowRange
.mnXfId
].push_back( maXfIdRowRange
.maRowRange
);
308 maXfIdRowRange
.set( -1, -1 );
312 void SheetDataBuffer::setMergedRange( const CellRangeAddress
& rRange
)
314 maMergedRanges
.push_back( MergedRange( rRange
) );
317 void SheetDataBuffer::setStandardNumFmt( const CellAddress
& rCellAddr
, sal_Int16 nStdNumFmt
)
321 Reference
< XNumberFormatsSupplier
> xNumFmtsSupp( getDocument(), UNO_QUERY_THROW
);
322 Reference
< XNumberFormatTypes
> xNumFmtTypes( xNumFmtsSupp
->getNumberFormats(), UNO_QUERY_THROW
);
323 sal_Int32 nIndex
= xNumFmtTypes
->getStandardFormat( nStdNumFmt
, Locale() );
324 PropertySet
aPropSet( getCell( rCellAddr
) );
325 aPropSet
.setProperty( PROP_NumberFormat
, nIndex
);
332 typedef std::pair
<sal_Int32
, sal_Int32
> FormatKeyPair
;
334 void addIfNotInMyMap( StylesBuffer
& rStyles
, std::map
< FormatKeyPair
, ApiCellRangeList
>& rMap
, sal_Int32 nXfId
, sal_Int32 nFormatId
, const ApiCellRangeList
& rRangeList
)
336 Xf
* pXf1
= rStyles
.getCellXf( nXfId
).get();
339 for ( std::map
< FormatKeyPair
, ApiCellRangeList
>::iterator it
= rMap
.begin(), it_end
= rMap
.end(); it
!= it_end
; ++it
)
341 if ( it
->first
.second
== nFormatId
)
343 Xf
* pXf2
= rStyles
.getCellXf( it
->first
.first
).get();
344 if ( *pXf1
== *pXf2
) // already exists
346 // add ranges from the rangelist to the existing rangelist for the
347 // matching style ( should we check if they overlap ? )
348 for ( ::std::vector
< CellRangeAddress
>::const_iterator iter
= rRangeList
.begin(), iter_end
= rRangeList
.end(); iter
!= iter_end
; ++iter
)
349 it
->second
.push_back( *iter
);
354 rMap
[ FormatKeyPair( nXfId
, nFormatId
) ] = rRangeList
;
358 void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId
, sal_Int32 nFormatId
, const ::com::sun::star::table::CellRangeAddress
& rAddress
, bool bProcessRowRange
)
360 RowRangeStyle aStyleRows
;
361 aStyleRows
.mnNumFmt
.first
= nXfId
;
362 aStyleRows
.mnNumFmt
.second
= nFormatId
;
363 aStyleRows
.mnStartRow
= rAddress
.StartRow
;
364 aStyleRows
.mnEndRow
= rAddress
.EndRow
;
365 for ( sal_Int32 nCol
= rAddress
.StartColumn
; nCol
<= rAddress
.EndColumn
; ++nCol
)
367 if ( !bProcessRowRange
)
368 maStylesPerColumn
[ nCol
].insert( aStyleRows
);
371 RowStyles
& rRowStyles
= maStylesPerColumn
[ nCol
];
372 // If the rowrange style includes rows already
373 // allocated to a style then we need to split
374 // the range style Rows into sections ( to
375 // occupy only rows that have no style definition )
377 // We dont want to set any rowstyle 'rows'
378 // for rows where there is an existing 'style' )
379 std::vector
< RowRangeStyle
> aRangeRowsSplits
;
381 RowStyles::iterator rows_it
= rRowStyles
.begin();
382 RowStyles::iterator rows_end
= rRowStyles
.end();
383 bool bAddRange
= true;
384 for ( ; rows_it
!= rows_end
; ++rows_it
)
386 const RowRangeStyle
& r
= *rows_it
;
387 // if row is completely within existing style, discard it
388 if ( aStyleRows
.mnStartRow
>= r
.mnStartRow
&& aStyleRows
.mnEndRow
<= r
.mnEndRow
)
390 else if ( aStyleRows
.mnStartRow
<= r
.mnStartRow
)
392 // not intersecting at all?, if so finish as none left
393 // to check ( row ranges are in ascending order
394 if ( aStyleRows
.mnEndRow
< r
.mnStartRow
)
396 else if ( aStyleRows
.mnEndRow
<= r
.mnEndRow
)
398 aStyleRows
.mnEndRow
= r
.mnStartRow
- 1;
401 if ( aStyleRows
.mnStartRow
< r
.mnStartRow
)
403 RowRangeStyle aSplit
= aStyleRows
;
404 aSplit
.mnEndRow
= r
.mnStartRow
- 1;
405 aRangeRowsSplits
.push_back( aSplit
);
409 std::vector
< RowRangeStyle
>::iterator splits_it
= aRangeRowsSplits
.begin();
410 std::vector
< RowRangeStyle
>::iterator splits_end
= aRangeRowsSplits
.end();
411 for ( ; splits_it
!= splits_end
; ++splits_it
)
412 rRowStyles
.insert( *splits_it
);
414 rRowStyles
.insert( aStyleRows
);
418 void SheetDataBuffer::finalizeImport()
420 // create all array formulas
421 for( ArrayFormulaList::iterator aIt
= maArrayFormulas
.begin(), aEnd
= maArrayFormulas
.end(); aIt
!= aEnd
; ++aIt
)
422 finalizeArrayFormula( aIt
->first
, aIt
->second
);
424 // create all table operations
425 for( TableOperationList::iterator aIt
= maTableOperations
.begin(), aEnd
= maTableOperations
.end(); aIt
!= aEnd
; ++aIt
)
426 finalizeTableOperation( aIt
->first
, aIt
->second
);
428 // write default formatting of remaining row range
429 maXfIdRowRangeList
[ maXfIdRowRange
.mnXfId
].push_back( maXfIdRowRange
.maRowRange
);
431 std::map
< FormatKeyPair
, ApiCellRangeList
> rangeStyleListMap
;
432 for( XfIdRangeListMap::const_iterator aIt
= maXfIdRangeLists
.begin(), aEnd
= maXfIdRangeLists
.end(); aIt
!= aEnd
; ++aIt
)
434 addIfNotInMyMap( getStyles(), rangeStyleListMap
, aIt
->first
.first
, aIt
->first
.second
, aIt
->second
);
436 // gather all ranges that have the same style and apply them in bulk
437 for ( std::map
< FormatKeyPair
, ApiCellRangeList
>::iterator it
= rangeStyleListMap
.begin(), it_end
= rangeStyleListMap
.end(); it
!= it_end
; ++it
)
439 const ApiCellRangeList
& rRanges( it
->second
);
440 for ( ::std::vector
< CellRangeAddress
>::const_iterator it_range
= rRanges
.begin(), it_rangeend
= rRanges
.end(); it_range
!=it_rangeend
; ++it_range
)
441 addColXfStyle( it
->first
.first
, it
->first
.second
, *it_range
);
444 for ( std::map
< sal_Int32
, std::vector
< ValueRange
> >::iterator it
= maXfIdRowRangeList
.begin(), it_end
= maXfIdRowRangeList
.end(); it
!= it_end
; ++it
)
446 ApiCellRangeList rangeList
;
447 AddressConverter
& rAddrConv
= getAddressConverter();
448 // get all row ranges for id
449 for ( std::vector
< ValueRange
>::iterator rangeIter
= it
->second
.begin(), rangeIter_end
= it
->second
.end(); rangeIter
!= rangeIter_end
; ++rangeIter
)
451 if ( it
->first
== -1 ) // it's a dud skip it
453 CellRangeAddress
aRange( getSheetIndex(), 0, rangeIter
->mnFirst
, rAddrConv
.getMaxApiAddress().Column
, rangeIter
->mnLast
);
455 addColXfStyle( it
->first
, -1, aRange
, true );
459 ScDocumentImport
& rDoc
= getDocImport();
460 StylesBuffer
& rStyles
= getStyles();
461 for ( ColStyles::iterator col
= maStylesPerColumn
.begin(), col_end
= maStylesPerColumn
.end(); col
!= col_end
; ++col
)
463 RowStyles
& rRowStyles
= col
->second
;
465 SCCOL nScCol
= static_cast< SCCOL
>( col
->first
);
466 for ( RowStyles::iterator rRows
= rRowStyles
.begin(), rRows_end
= rRowStyles
.end(); rRows
!= rRows_end
; ++rRows
)
468 Xf
* pXf
= rStyles
.getCellXf( rRows
->mnNumFmt
.first
).get();
471 pXf
->applyPatternToAttrList( aAttrs
, rRows
->mnStartRow
, rRows
->mnEndRow
, rRows
->mnNumFmt
.second
);
473 if (aAttrs
.maAttrs
.empty() || aAttrs
.maAttrs
.back().nRow
!= MAXROW
)
476 aEntry
.nRow
= MAXROW
;
477 aEntry
.pPattern
= rDoc
.getDoc().GetPattern(nScCol
, 0, getSheetIndex());
478 rDoc
.getDoc().GetPool()->Put(*aEntry
.pPattern
);
479 aAttrs
.maAttrs
.push_back(aEntry
);
481 if (!sc::NumFmtUtil::isLatinScript(*aEntry
.pPattern
, rDoc
.getDoc()))
482 aAttrs
.mbLatinNumFmtOnly
= false;
485 ScDocumentImport::Attrs aAttrParam
;
486 aAttrParam
.mnSize
= aAttrs
.maAttrs
.size();
487 aAttrParam
.mpData
= new ScAttrEntry
[aAttrParam
.mnSize
];
488 aAttrParam
.mbLatinNumFmtOnly
= aAttrs
.mbLatinNumFmtOnly
;
489 std::list
<ScAttrEntry
>::const_iterator itr
= aAttrs
.maAttrs
.begin(), itrEnd
= aAttrs
.maAttrs
.end();
490 for (size_t i
= 0; itr
!= itrEnd
; ++itr
, ++i
)
491 aAttrParam
.mpData
[i
] = *itr
;
493 rDoc
.setAttrEntries(getSheetIndex(), nScCol
, aAttrParam
);
496 // merge all cached merged ranges and update right/bottom cell borders
497 for( MergedRangeList::iterator aIt
= maMergedRanges
.begin(), aEnd
= maMergedRanges
.end(); aIt
!= aEnd
; ++aIt
)
498 applyCellMerging( aIt
->maRange
);
499 for( MergedRangeList::iterator aIt
= maCenterFillRanges
.begin(), aEnd
= maCenterFillRanges
.end(); aIt
!= aEnd
; ++aIt
)
500 applyCellMerging( aIt
->maRange
);
503 // private --------------------------------------------------------------------
505 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
511 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow
, sal_Int32 nXfId
)
513 maRowRange
= ValueRange( nRow
);
517 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow
, sal_Int32 nXfId
)
519 if( mnXfId
== nXfId
)
521 if( maRowRange
.mnLast
+ 1 == nRow
)
526 if( maRowRange
.mnFirst
== nRow
+ 1 )
528 --maRowRange
.mnFirst
;
535 SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress
& rRange
) :
537 mnHorAlign( XML_TOKEN_INVALID
)
541 SheetDataBuffer::MergedRange::MergedRange( const CellAddress
& rAddress
, sal_Int32 nHorAlign
) :
542 maRange( rAddress
.Sheet
, rAddress
.Column
, rAddress
.Row
, rAddress
.Column
, rAddress
.Row
),
543 mnHorAlign( nHorAlign
)
547 bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress
& rAddress
, sal_Int32 nHorAlign
)
549 if( (mnHorAlign
== nHorAlign
) && (maRange
.StartRow
== rAddress
.Row
) &&
550 (maRange
.EndRow
== rAddress
.Row
) && (maRange
.EndColumn
+ 1 == rAddress
.Column
) )
558 void SheetDataBuffer::setCellFormula( const CellAddress
& rCellAddr
, const ApiTokenSequence
& rTokens
)
560 if( rTokens
.hasElements() )
562 putFormulaTokens( rCellAddr
, rTokens
);
566 ApiTokenSequence
SheetDataBuffer::resolveSharedFormula( const CellAddress
& rAddr
) const
568 BinAddress
aAddr(rAddr
);
569 ApiTokenSequence aTokens
= ContainerHelper::getMapElement( maSharedFormulas
, aAddr
, ApiTokenSequence() );
573 void SheetDataBuffer::finalizeArrayFormula( const CellRangeAddress
& rRange
, const ApiTokenSequence
& rTokens
) const
575 Reference
< XArrayFormulaTokens
> xTokens( getCellRange( rRange
), UNO_QUERY
);
576 OSL_ENSURE( xTokens
.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
578 xTokens
->setArrayTokens( rTokens
);
581 void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress
& rRange
, const DataTableModel
& rModel
)
583 if (rModel
.mbRef1Deleted
)
586 if (rModel
.maRef1
.isEmpty())
589 if (rRange
.StartColumn
<= 0 || rRange
.StartRow
<= 0)
592 sal_Int16 nSheet
= getSheetIndex();
595 if (!getAddressConverter().convertToCellAddress(aRef1
, rModel
.maRef1
, nSheet
, true))
598 ScDocumentImport
& rDoc
= getDocImport();
602 ScUnoConversion::FillScRange(aScRange
, rRange
);
604 if (rModel
.mb2dTable
)
606 // Two-variable data table.
607 if (rModel
.mbRef2Deleted
)
610 if (rModel
.maRef2
.isEmpty())
614 if (!getAddressConverter().convertToCellAddress(aRef2
, rModel
.maRef2
, nSheet
, true))
617 aParam
.meMode
= ScTabOpParam::Both
;
619 aParam
.aRefFormulaCell
.Set(rRange
.StartColumn
-1, rRange
.StartRow
-1, nSheet
, false, false, false);
620 aParam
.aRefFormulaEnd
= aParam
.aRefFormulaCell
;
622 aScRange
.aStart
.IncRow(-1);
623 aScRange
.aStart
.IncCol(-1);
625 // Ref1 is row input cell and Ref2 is column input cell.
626 aParam
.aRefRowCell
.Set(aRef1
.Column
, aRef1
.Row
, aRef1
.Sheet
, false, false, false);
627 aParam
.aRefColCell
.Set(aRef2
.Column
, aRef2
.Row
, aRef2
.Sheet
, false, false, false);
628 rDoc
.setTableOpCells(aScRange
, aParam
);
633 // One-variable data table.
635 if (rModel
.mbRowTable
)
637 // One-variable row input cell (horizontal).
638 aParam
.meMode
= ScTabOpParam::Row
;
639 aParam
.aRefRowCell
.Set(aRef1
.Column
, aRef1
.Row
, aRef1
.Sheet
, false, false, false);
640 aParam
.aRefFormulaCell
.Set(rRange
.StartColumn
-1, rRange
.StartRow
, nSheet
, false, true, false);
641 aParam
.aRefFormulaEnd
= aParam
.aRefFormulaCell
;
642 aScRange
.aStart
.IncRow(-1);
643 rDoc
.setTableOpCells(aScRange
, aParam
);
647 // One-variable column input cell (vertical).
648 aParam
.meMode
= ScTabOpParam::Column
;
649 aParam
.aRefColCell
.Set(aRef1
.Column
, aRef1
.Row
, aRef1
.Sheet
, false, false, false);
650 aParam
.aRefFormulaCell
.Set(rRange
.StartColumn
, rRange
.StartRow
-1, nSheet
, true, false, false);
651 aParam
.aRefFormulaEnd
= aParam
.aRefFormulaCell
;
652 aScRange
.aStart
.IncCol(-1);
653 rDoc
.setTableOpCells(aScRange
, aParam
);
657 void SheetDataBuffer::setCellFormat( const CellModel
& rModel
, sal_Int32 nNumFmtId
)
659 if( (rModel
.mnXfId
>= 0) || (nNumFmtId
>= 0) )
661 ::std::vector
< CellRangeAddress
>::reverse_iterator aIt
= maXfIdRangeLists
[ XfIdNumFmtKey( rModel
.mnXfId
, nNumFmtId
) ].rbegin();
662 ::std::vector
< CellRangeAddress
>::reverse_iterator aItEnd
= maXfIdRangeLists
[ XfIdNumFmtKey( rModel
.mnXfId
, nNumFmtId
) ].rend();
663 /* The xlsx sheet data contains row wise information.
664 * It is sufficient to check if the row range size is one
667 aIt
->Sheet
== rModel
.maCellAddr
.Sheet
&&
668 aIt
->StartRow
== aIt
->EndRow
&&
669 aIt
->StartRow
== rModel
.maCellAddr
.Row
&&
670 (aIt
->EndColumn
+1) == rModel
.maCellAddr
.Column
)
672 aIt
->EndColumn
++; // Expand Column
676 maXfIdRangeLists
[ XfIdNumFmtKey (rModel
.mnXfId
, nNumFmtId
) ].push_back(
677 CellRangeAddress( rModel
.maCellAddr
.Sheet
, rModel
.maCellAddr
.Column
, rModel
.maCellAddr
.Row
,
678 rModel
.maCellAddr
.Column
, rModel
.maCellAddr
.Row
) );
681 aIt
= maXfIdRangeLists
[ XfIdNumFmtKey( rModel
.mnXfId
, nNumFmtId
) ].rbegin();
682 aItEnd
= maXfIdRangeLists
[ XfIdNumFmtKey( rModel
.mnXfId
, nNumFmtId
) ].rend();
683 ::std::vector
< CellRangeAddress
>::reverse_iterator aItM
= aIt
+1;
684 while( aItM
!= aItEnd
)
686 if( aIt
->Sheet
== aItM
->Sheet
)
688 /* Try to merge this with the previous range */
689 if( aIt
->StartRow
== (aItM
->EndRow
+ 1) &&
690 aIt
->StartColumn
== aItM
->StartColumn
&&
691 aIt
->EndColumn
== aItM
->EndColumn
)
693 aItM
->EndRow
= aIt
->EndRow
;
694 maXfIdRangeLists
[ XfIdNumFmtKey( rModel
.mnXfId
, nNumFmtId
) ].pop_back();
697 else if( aIt
->StartRow
> aItM
->EndRow
+ 1 )
698 break; // Un-necessary to check with any other rows
705 // update merged ranges for 'center across selection' and 'fill'
706 if( const Xf
* pXf
= getStyles().getCellXf( rModel
.mnXfId
).get() )
708 sal_Int32 nHorAlign
= pXf
->getAlignment().getModel().mnHorAlign
;
709 if( (nHorAlign
== XML_centerContinuous
) || (nHorAlign
== XML_fill
) )
711 /* start new merged range, if cell is not empty (#108781#),
712 or try to expand last range with empty cell */
713 if( rModel
.mnCellType
!= XML_TOKEN_INVALID
)
714 maCenterFillRanges
.push_back( MergedRange( rModel
.maCellAddr
, nHorAlign
) );
715 else if( !maCenterFillRanges
.empty() )
716 maCenterFillRanges
.rbegin()->tryExpand( rModel
.maCellAddr
, nHorAlign
);
722 void lcl_SetBorderLine( ScDocument
& rDoc
, ScRange
& rRange
, SCTAB nScTab
, SvxBoxItemLine nLine
)
724 SCCOL nFromScCol
= (nLine
== SvxBoxItemLine::RIGHT
) ? rRange
.aEnd
.Col() : rRange
.aStart
.Col();
725 SCROW nFromScRow
= (nLine
== SvxBoxItemLine::BOTTOM
) ? rRange
.aEnd
.Row() : rRange
.aStart
.Row();
727 const SvxBoxItem
* pFromItem
= static_cast< const SvxBoxItem
* >(
728 rDoc
.GetAttr( nFromScCol
, nFromScRow
, nScTab
, ATTR_BORDER
) );
729 const SvxBoxItem
* pToItem
= static_cast< const SvxBoxItem
* >(
730 rDoc
.GetAttr( rRange
.aStart
.Col(), rRange
.aStart
.Row(), nScTab
, ATTR_BORDER
) );
732 SvxBoxItem
aNewItem( *pToItem
);
733 aNewItem
.SetLine( pFromItem
->GetLine( nLine
), nLine
);
734 rDoc
.ApplyAttr( rRange
.aStart
.Col(), rRange
.aStart
.Row(), nScTab
, aNewItem
);
737 void SheetDataBuffer::applyCellMerging( const CellRangeAddress
& rRange
)
739 bool bMultiCol
= rRange
.StartColumn
< rRange
.EndColumn
;
740 bool bMultiRow
= rRange
.StartRow
< rRange
.EndRow
;
743 ScUnoConversion::FillScRange( aRange
, rRange
);
744 const ScAddress
& rStart
= aRange
.aStart
;
745 const ScAddress
& rEnd
= aRange
.aEnd
;
746 ScDocument
& rDoc
= getScDocument();
747 // set correct right border
749 lcl_SetBorderLine( rDoc
, aRange
, getSheetIndex(), SvxBoxItemLine::RIGHT
);
750 // set correct lower border
752 lcl_SetBorderLine( rDoc
, aRange
, getSheetIndex(), SvxBoxItemLine::BOTTOM
);
754 if( bMultiCol
|| bMultiRow
)
755 rDoc
.DoMerge( getSheetIndex(), rStart
.Col(), rStart
.Row(), rEnd
.Col(), rEnd
.Row() );
756 // #i93609# merged range in a single row: test if manual row height is needed
759 bool bTextWrap
= static_cast< const SfxBoolItem
* >( rDoc
.GetAttr( rStart
.Col(), rStart
.Row(), rStart
.Tab(), ATTR_LINEBREAK
) )->GetValue();
760 if( !bTextWrap
&& (rDoc
.GetCellType( rStart
) == CELLTYPE_EDIT
) )
762 if (const EditTextObject
* pEditObj
= rDoc
.GetEditText(rStart
))
763 bTextWrap
= pEditObj
->GetParagraphCount() > 1;
771 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */