Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "sheetdatabuffer.hxx"
21 :
22 : #include <algorithm>
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 "cell.hxx"
53 :
54 : namespace oox {
55 : namespace xls {
56 :
57 : // ============================================================================
58 :
59 : using namespace ::com::sun::star::lang;
60 : using namespace ::com::sun::star::sheet;
61 : using namespace ::com::sun::star::table;
62 : using namespace ::com::sun::star::text;
63 : using namespace ::com::sun::star::uno;
64 : using namespace ::com::sun::star::util;
65 :
66 : using ::rtl::OUString;
67 : using ::rtl::OUStringBuffer;
68 :
69 : // ============================================================================
70 :
71 50 : CellModel::CellModel() :
72 : mnCellType( XML_TOKEN_INVALID ),
73 : mnXfId( -1 ),
74 50 : mbShowPhonetic( false )
75 : {
76 50 : }
77 :
78 : // ----------------------------------------------------------------------------
79 :
80 50 : CellFormulaModel::CellFormulaModel() :
81 : mnFormulaType( XML_TOKEN_INVALID ),
82 50 : mnSharedId( -1 )
83 : {
84 50 : }
85 :
86 0 : bool CellFormulaModel::isValidArrayRef( const CellAddress& rCellAddr )
87 : {
88 : return
89 : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
90 : (maFormulaRef.StartColumn == rCellAddr.Column) &&
91 0 : (maFormulaRef.StartRow == rCellAddr.Row);
92 : }
93 :
94 2 : bool CellFormulaModel::isValidSharedRef( const CellAddress& rCellAddr )
95 : {
96 : return
97 : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
98 : (maFormulaRef.StartColumn <= rCellAddr.Column) && (rCellAddr.Column <= maFormulaRef.EndColumn) &&
99 2 : (maFormulaRef.StartRow <= rCellAddr.Row) && (rCellAddr.Row <= maFormulaRef.EndRow);
100 : }
101 :
102 : // ----------------------------------------------------------------------------
103 :
104 50 : DataTableModel::DataTableModel() :
105 : mb2dTable( false ),
106 : mbRowTable( false ),
107 : mbRef1Deleted( false ),
108 50 : mbRef2Deleted( false )
109 : {
110 50 : }
111 :
112 : // ============================================================================
113 :
114 50 : CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
115 : WorksheetHelper( rHelper ),
116 50 : mnCurrRow( -1 )
117 : {
118 50 : }
119 :
120 306 : void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
121 : {
122 : OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
123 : OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
124 306 : if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
125 306 : maColSpans[ nRow ] = rColSpans.getRanges();
126 306 : }
127 :
128 50 : void CellBlockBuffer::finalizeImport()
129 : {
130 50 : }
131 :
132 : // ============================================================================
133 :
134 50 : SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
135 : WorksheetHelper( rHelper ),
136 : maCellBlocks( rHelper ),
137 50 : mbPendingSharedFmla( false )
138 : {
139 50 : }
140 :
141 306 : void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
142 : {
143 306 : maCellBlocks.setColSpans( nRow, rColSpans );
144 306 : }
145 :
146 0 : void SheetDataBuffer::setBlankCell( const CellModel& rModel )
147 : {
148 0 : setCellFormat( rModel );
149 0 : }
150 :
151 1158 : void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
152 : {
153 1158 : putValue( rModel.maCellAddr, fValue );
154 1158 : setCellFormat( rModel );
155 1158 : }
156 :
157 152 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
158 : {
159 152 : putString( rModel.maCellAddr, rText );
160 152 : setCellFormat( rModel );
161 152 : }
162 :
163 156 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
164 : {
165 : OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
166 156 : const Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
167 156 : OUString aText;
168 156 : if( rxString->extractPlainString( aText, pFirstPortionFont ) )
169 : {
170 152 : setStringCell( rModel, aText );
171 : }
172 : else
173 : {
174 4 : putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
175 4 : setCellFormat( rModel );
176 156 : }
177 156 : }
178 :
179 156 : void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
180 : {
181 156 : RichStringRef xString = getSharedStrings().getString( nStringId );
182 156 : if( xString.get() )
183 156 : setStringCell( rModel, xString );
184 : else
185 0 : setBlankCell( rModel );
186 156 : }
187 :
188 0 : void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const ::com::sun::star::util::DateTime& rDateTime )
189 : {
190 : // write serial date/time value into the cell
191 0 : double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
192 0 : setValueCell( rModel, fSerial );
193 : // set appropriate number format
194 : using namespace ::com::sun::star::util::NumberFormat;
195 0 : sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
196 0 : setStandardNumFmt( rModel.maCellAddr, nStdFmt );
197 0 : }
198 :
199 0 : void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
200 : {
201 0 : setCellFormula( rModel.maCellAddr, getFormulaParser().convertBoolToFormula( bValue ) );
202 : // #108770# set 'Standard' number format for all Boolean cells
203 0 : setCellFormat( rModel, 0 );
204 0 : }
205 :
206 0 : void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
207 : {
208 0 : setErrorCell( rModel, getUnitConverter().calcBiffErrorCode( rErrorCode ) );
209 0 : }
210 :
211 0 : void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
212 : {
213 0 : setCellFormula( rModel.maCellAddr, getFormulaParser().convertErrorToFormula( nErrorCode ) );
214 0 : setCellFormat( rModel );
215 0 : }
216 :
217 0 : void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
218 : {
219 0 : mbPendingSharedFmla = false;
220 0 : ApiTokenSequence aTokens;
221 :
222 : /* Detect special token passed as placeholder for array formulas, shared
223 : formulas, and table operations. In BIFF, these formulas are represented
224 : by a single tExp resp. tTbl token. If the formula parser finds these
225 : tokens, it puts a single OPCODE_BAD token with the base address and
226 : formula type into the token sequence. This information will be
227 : extracted here, and in case of a shared formula, the shared formula
228 : buffer will generate the resulting formula token array. */
229 0 : ApiSpecialTokenInfo aTokenInfo;
230 0 : if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
231 : {
232 : /* The second member of the token info is set to true, if the formula
233 : represents a table operation, which will be skipped. In BIFF12 it
234 : is not possible to distinguish array and shared formulas
235 : (BIFF5/BIFF8 provide this information with a special flag in the
236 : FORMULA record). */
237 0 : if( !aTokenInfo.Second )
238 : {
239 : /* Construct the token array representing the shared formula. If
240 : the returned sequence is empty, the definition of the shared
241 : formula has not been loaded yet, or the cell is part of an
242 : array formula. In this case, the cell will be remembered. After
243 : reading the formula definition it will be retried to insert the
244 : formula via retryPendingSharedFormulaCell(). */
245 0 : BinAddress aBaseAddr( aTokenInfo.First );
246 0 : aTokens = resolveSharedFormula( aBaseAddr );
247 0 : if( !aTokens.hasElements() )
248 : {
249 0 : maSharedFmlaAddr = rModel.maCellAddr;
250 0 : maSharedBaseAddr = aBaseAddr;
251 0 : mbPendingSharedFmla = true;
252 : }
253 : }
254 : }
255 : else
256 : {
257 : // simple formula, use the passed token array
258 0 : aTokens = rTokens;
259 : }
260 :
261 0 : setCellFormula( rModel.maCellAddr, aTokens );
262 0 : setCellFormat( rModel );
263 0 : }
264 :
265 0 : void SheetDataBuffer::createArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens )
266 : {
267 : /* Array formulas will be inserted later in finalizeImport(). This is
268 : needed to not disturb collecting all the cells, which will be put into
269 : the sheet in large blocks to increase performance. */
270 0 : maArrayFormulas.push_back( ArrayFormula( rRange, rTokens ) );
271 0 : }
272 :
273 0 : void SheetDataBuffer::createTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
274 : {
275 : /* Table operations will be inserted later in finalizeImport(). This is
276 : needed to not disturb collecting all the cells, which will be put into
277 : the sheet in large blocks to increase performance. */
278 0 : maTableOperations.push_back( TableOperation( rRange, rModel ) );
279 0 : }
280 :
281 0 : void SheetDataBuffer::createSharedFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
282 : {
283 0 : createSharedFormula( BinAddress( rCellAddr ), rTokens );
284 0 : }
285 :
286 306 : void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
287 : {
288 : // set row formatting
289 306 : if( bCustomFormat )
290 : {
291 : // try to expand cached row range, if formatting is equal
292 0 : if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
293 : {
294 :
295 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
296 0 : maXfIdRowRange.set( nRow, nXfId );
297 : }
298 : }
299 306 : else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
300 : {
301 : // finish last cached row range
302 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
303 0 : maXfIdRowRange.set( -1, -1 );
304 : }
305 306 : }
306 :
307 2 : void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
308 : {
309 2 : maMergedRanges.push_back( MergedRange( rRange ) );
310 2 : }
311 :
312 0 : void SheetDataBuffer::setStandardNumFmt( const CellAddress& rCellAddr, sal_Int16 nStdNumFmt )
313 : {
314 : try
315 : {
316 0 : Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
317 0 : Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
318 0 : sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdNumFmt, Locale() );
319 0 : PropertySet aPropSet( getCell( rCellAddr ) );
320 0 : aPropSet.setProperty( PROP_NumberFormat, nIndex );
321 : }
322 0 : catch( Exception& )
323 : {
324 : }
325 0 : }
326 :
327 76 : void addIfNotInMyMap( StylesBuffer& rStyles, std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ApiCellRangeList& rRangeList )
328 : {
329 76 : Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
330 76 : if ( pXf1 )
331 : {
332 316 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rMap.begin(), it_end = rMap.end(); it != it_end; ++it )
333 : {
334 240 : if ( it->first.second == nFormatId )
335 : {
336 240 : Xf* pXf2 = rStyles.getCellXf( it->first.first ).get();
337 240 : if ( *pXf1 == *pXf2 ) // already exists
338 : {
339 : // add ranges from the rangelist to the existing rangelist for the
340 : // matching style ( should we check if they overlap ? )
341 0 : for ( ApiCellRangeList::const_iterator iter = rRangeList.begin(), iter_end = rRangeList.end(); iter != iter_end; ++iter )
342 0 : it->second.push_back( *iter );
343 76 : return;
344 : }
345 : }
346 : }
347 76 : rMap[ std::pair<sal_Int32, sal_Int32>( nXfId, nFormatId ) ] = rRangeList;
348 : }
349 : }
350 :
351 50 : void SheetDataBuffer::finalizeImport()
352 : {
353 : // insert all cells of all open cell blocks
354 50 : maCellBlocks.finalizeImport();
355 :
356 : // create all array formulas
357 50 : for( ArrayFormulaList::iterator aIt = maArrayFormulas.begin(), aEnd = maArrayFormulas.end(); aIt != aEnd; ++aIt )
358 0 : finalizeArrayFormula( aIt->first, aIt->second );
359 :
360 : // create all table operations
361 50 : for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
362 0 : finalizeTableOperation( aIt->first, aIt->second );
363 :
364 : // write default formatting of remaining row range
365 50 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
366 100 : for ( std::map< sal_Int32, std::vector< ValueRange > >::iterator it = maXfIdRowRangeList.begin(), it_end = maXfIdRowRangeList.end(); it != it_end; ++it )
367 : {
368 50 : ApiCellRangeList rangeList;
369 50 : AddressConverter& rAddrConv = getAddressConverter();
370 : // get all row ranges for id
371 100 : for ( std::vector< ValueRange >::iterator rangeIter = it->second.begin(), rangeIter_end = it->second.end(); rangeIter != rangeIter_end; ++rangeIter )
372 : {
373 50 : CellRangeAddress aRange( getSheetIndex(), 0, rangeIter->mnFirst, rAddrConv.getMaxApiAddress().Column, rangeIter->mnLast );
374 50 : rangeList.push_back( aRange );
375 : }
376 50 : ScRangeList aList;
377 100 : for ( ApiCellRangeList::const_iterator itRange = rangeList.begin(), itRange_end = rangeList.end(); itRange!=itRange_end; ++itRange )
378 : {
379 50 : ScRange* pRange = new ScRange();
380 50 : ScUnoConversion::FillScRange( *pRange, *itRange );
381 50 : aList.push_back( pRange );
382 : }
383 50 : ScMarkData aMark;
384 50 : aMark.MarkFromRangeList( aList, false );
385 :
386 50 : getStyles().writeCellXfToMarkData( aMark, it->first, -1 );
387 50 : }
388 50 : std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList > rangeStyleListMap;
389 : // gather all ranges that have the same style and apply them in bulk
390 126 : for( XfIdRangeListMap::const_iterator aIt = maXfIdRangeLists.begin(), aEnd = maXfIdRangeLists.end(); aIt != aEnd; ++aIt )
391 76 : addIfNotInMyMap( getStyles(), rangeStyleListMap, aIt->first.first, aIt->first.second, aIt->second );
392 126 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rangeStyleListMap.begin(), it_end = rangeStyleListMap.end(); it != it_end; ++it )
393 76 : writeXfIdRangeListProperties( it->first.first, it->first.second, it->second );
394 : // merge all cached merged ranges and update right/bottom cell borders
395 52 : for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
396 2 : applyCellMerging( aIt->maRange );
397 50 : for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
398 50 : applyCellMerging( aIt->maRange );
399 50 : }
400 :
401 : // private --------------------------------------------------------------------
402 :
403 50 : SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
404 : maRowRange( -1 ),
405 50 : mnXfId( -1 )
406 : {
407 50 : }
408 :
409 0 : void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
410 : {
411 0 : maRowRange = ValueRange( nRow );
412 0 : mnXfId = nXfId;
413 0 : }
414 :
415 0 : bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
416 : {
417 0 : if( mnXfId == nXfId )
418 : {
419 0 : if( maRowRange.mnLast + 1 == nRow )
420 : {
421 0 : ++maRowRange.mnLast;
422 0 : return true;
423 : }
424 0 : if( maRowRange.mnFirst == nRow + 1 )
425 : {
426 0 : --maRowRange.mnFirst;
427 0 : return true;
428 : }
429 : }
430 0 : return false;
431 : }
432 :
433 :
434 2 : SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
435 : maRange( rRange ),
436 2 : mnHorAlign( XML_TOKEN_INVALID )
437 : {
438 2 : }
439 :
440 0 : SheetDataBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
441 : maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
442 0 : mnHorAlign( nHorAlign )
443 : {
444 0 : }
445 :
446 0 : bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
447 : {
448 0 : if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
449 : (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
450 : {
451 0 : ++maRange.EndColumn;
452 0 : return true;
453 : }
454 0 : return false;
455 : }
456 :
457 : // ----------------------------------------------------------------------------
458 :
459 0 : void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
460 : {
461 0 : if( rTokens.hasElements() )
462 : {
463 0 : putFormulaTokens( rCellAddr, rTokens );
464 : }
465 0 : }
466 :
467 0 : void SheetDataBuffer::createSharedFormula( const BinAddress& rMapKey, const ApiTokenSequence& rTokens )
468 : {
469 : // create the defined name that will represent the shared formula
470 0 : OUString aName = OUStringBuffer().appendAscii( RTL_CONSTASCII_STRINGPARAM( "__shared_" ) ).
471 0 : append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) ).
472 0 : append( sal_Unicode( '_' ) ).append( rMapKey.mnRow ).
473 0 : append( sal_Unicode( '_' ) ).append( rMapKey.mnCol ).makeStringAndClear();
474 0 : ScRangeData* pScRangeData = createNamedRangeObject( aName, rTokens, 0 );
475 0 : pScRangeData->SetType(RT_SHARED);
476 :
477 : // get and store the token index of the defined name
478 : OSL_ENSURE( maSharedFormulas.count( rMapKey ) == 0, "SheetDataBuffer::createSharedFormula - shared formula exists already" );
479 0 : sal_Int32 nTokenIndex = static_cast< sal_Int32 >( pScRangeData->GetIndex() );
480 0 : if( nTokenIndex >= 0 ) try
481 : {
482 : // store the token index in the map
483 0 : maSharedFormulas[ rMapKey ] = nTokenIndex;
484 : // retry to insert a pending shared formula cell
485 0 : if( mbPendingSharedFmla )
486 0 : setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
487 : }
488 0 : catch( Exception& )
489 : {
490 : }
491 0 : mbPendingSharedFmla = false;
492 0 : }
493 :
494 0 : ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const BinAddress& rMapKey ) const
495 : {
496 0 : sal_Int32 nTokenIndex = ContainerHelper::getMapElement( maSharedFormulas, rMapKey, -1 );
497 0 : return (nTokenIndex >= 0) ? getFormulaParser().convertNameToFormula( nTokenIndex ) : ApiTokenSequence();
498 : }
499 :
500 0 : void SheetDataBuffer::finalizeArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens ) const
501 : {
502 0 : Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
503 : OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
504 0 : if( xTokens.is() )
505 0 : xTokens->setArrayTokens( rTokens );
506 0 : }
507 :
508 0 : void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel ) const
509 : {
510 0 : sal_Int16 nSheet = getSheetIndex();
511 0 : bool bOk = false;
512 0 : if( !rModel.mbRef1Deleted && !rModel.maRef1.isEmpty() && (rRange.StartColumn > 0) && (rRange.StartRow > 0) )
513 : {
514 0 : CellRangeAddress aOpRange = rRange;
515 0 : CellAddress aRef1;
516 0 : if( getAddressConverter().convertToCellAddress( aRef1, rModel.maRef1, nSheet, true ) ) try
517 : {
518 0 : if( rModel.mb2dTable )
519 : {
520 0 : CellAddress aRef2;
521 0 : if( !rModel.mbRef2Deleted && getAddressConverter().convertToCellAddress( aRef2, rModel.maRef2, nSheet, true ) )
522 : {
523 : // API call expects input values inside operation range
524 0 : --aOpRange.StartColumn;
525 0 : --aOpRange.StartRow;
526 : // formula range is top-left cell of operation range
527 0 : CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn, aOpRange.StartRow, aOpRange.StartColumn, aOpRange.StartRow );
528 : // set multiple operation
529 0 : Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
530 0 : xMultOp->setTableOperation( aFormulaRange, TableOperationMode_BOTH, aRef2, aRef1 );
531 0 : bOk = true;
532 : }
533 : }
534 0 : else if( rModel.mbRowTable )
535 : {
536 : // formula range is column to the left of operation range
537 0 : CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn - 1, aOpRange.StartRow, aOpRange.StartColumn - 1, aOpRange.EndRow );
538 : // API call expects input values (top row) inside operation range
539 0 : --aOpRange.StartRow;
540 : // set multiple operation
541 0 : Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
542 0 : xMultOp->setTableOperation( aFormulaRange, TableOperationMode_ROW, aRef1, aRef1 );
543 0 : bOk = true;
544 : }
545 : else
546 : {
547 : // formula range is row above operation range
548 0 : CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn, aOpRange.StartRow - 1, aOpRange.EndColumn, aOpRange.StartRow - 1 );
549 : // API call expects input values (left column) inside operation range
550 0 : --aOpRange.StartColumn;
551 : // set multiple operation
552 0 : Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
553 0 : xMultOp->setTableOperation( aFormulaRange, TableOperationMode_COLUMN, aRef1, aRef1 );
554 0 : bOk = true;
555 : }
556 : }
557 0 : catch( Exception& )
558 : {
559 : }
560 : }
561 :
562 : // on error: fill cell range with #REF! error codes
563 0 : if( !bOk ) try
564 : {
565 0 : Reference< XCellRangeData > xCellRangeData( getCellRange( rRange ), UNO_QUERY_THROW );
566 0 : size_t nWidth = static_cast< size_t >( rRange.EndColumn - rRange.StartColumn + 1 );
567 0 : size_t nHeight = static_cast< size_t >( rRange.EndRow - rRange.StartRow + 1 );
568 0 : Matrix< Any > aErrorCells( nWidth, nHeight, Any( getFormulaParser().convertErrorToFormula( BIFF_ERR_REF ) ) );
569 0 : xCellRangeData->setDataArray( ContainerHelper::matrixToSequenceSequence( aErrorCells ) );
570 : }
571 0 : catch( Exception& )
572 : {
573 : }
574 0 : }
575 :
576 1382 : void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
577 : {
578 1382 : if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
579 : {
580 656 : ApiCellRangeList::reverse_iterator aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
581 656 : ApiCellRangeList::reverse_iterator aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
582 : /* The xlsx sheet data contains row wise information.
583 : * It is sufficient to check if the row range size is one
584 : */
585 2824 : if( aIt != aItEnd &&
586 580 : aIt->Sheet == rModel.maCellAddr.Sheet &&
587 1160 : aIt->StartRow == aIt->EndRow &&
588 244 : aIt->StartRow == rModel.maCellAddr.Row &&
589 184 : (aIt->EndColumn+1) == rModel.maCellAddr.Column )
590 : {
591 144 : aIt->EndColumn++; // Expand Column
592 : }
593 : else
594 : {
595 1024 : maXfIdRangeLists[ XfIdNumFmtKey (rModel.mnXfId, nNumFmtId ) ].push_back(
596 : CellRangeAddress( rModel.maCellAddr.Sheet, rModel.maCellAddr.Column, rModel.maCellAddr.Row,
597 1536 : rModel.maCellAddr.Column, rModel.maCellAddr.Row ) );
598 : }
599 :
600 656 : aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
601 656 : aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
602 656 : ApiCellRangeList::reverse_iterator aItM = aIt+1;
603 2674 : while( aItM != aItEnd )
604 : {
605 1784 : if( aIt->Sheet == aItM->Sheet )
606 : {
607 : /* Try to merge this with the previous range */
608 5880 : if( aIt->StartRow == (aItM->EndRow + 1) &&
609 3196 : aIt->StartColumn == aItM->StartColumn &&
610 900 : aIt->EndColumn == aItM->EndColumn)
611 : {
612 366 : aItM->EndRow = aIt->EndRow;
613 366 : maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].pop_back();
614 366 : break;
615 : }
616 1418 : else if( aIt->StartRow > aItM->EndRow + 1 )
617 56 : break; // Un-necessary to check with any other rows
618 : }
619 : else
620 0 : break;
621 1362 : ++aItM;
622 : }
623 :
624 : // update merged ranges for 'center across selection' and 'fill'
625 656 : if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
626 : {
627 656 : sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
628 656 : if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
629 : {
630 : /* start new merged range, if cell is not empty (#108781#),
631 : or try to expand last range with empty cell */
632 0 : if( rModel.mnCellType != XML_TOKEN_INVALID )
633 0 : maCenterFillRanges.push_back( MergedRange( rModel.maCellAddr, nHorAlign ) );
634 0 : else if( !maCenterFillRanges.empty() )
635 0 : maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
636 : }
637 : }
638 : }
639 1382 : }
640 :
641 76 : void SheetDataBuffer::writeXfIdRangeListProperties( sal_Int32 nXfId, sal_Int32 nNumFmtId, const ApiCellRangeList& rRanges ) const
642 : {
643 76 : StylesBuffer& rStyles = getStyles();
644 76 : ScRangeList aList;
645 222 : for ( ApiCellRangeList::const_iterator it = rRanges.begin(), it_end = rRanges.end(); it!=it_end; ++it )
646 : {
647 146 : ScRange* pRange = new ScRange();
648 146 : ScUnoConversion::FillScRange( *pRange, *it );
649 146 : aList.push_back( pRange );
650 : }
651 76 : ScMarkData aMark;
652 76 : aMark.MarkFromRangeList( aList, false );
653 76 : rStyles.writeCellXfToMarkData( aMark, nXfId, nNumFmtId );
654 76 : }
655 :
656 4 : void lcl_SetBorderLine( ScDocument& rDoc, ScRange& rRange, SCTAB nScTab, sal_uInt16 nLine )
657 : {
658 4 : SCCOL nFromScCol = (nLine == BOX_LINE_RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
659 4 : SCROW nFromScRow = (nLine == BOX_LINE_BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
660 :
661 : const SvxBoxItem* pFromItem = static_cast< const SvxBoxItem* >(
662 4 : rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ) );
663 : const SvxBoxItem* pToItem = static_cast< const SvxBoxItem* >(
664 4 : rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ) );
665 :
666 4 : SvxBoxItem aNewItem( *pToItem );
667 4 : aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
668 4 : rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
669 4 : }
670 :
671 2 : void SheetDataBuffer::applyCellMerging( const CellRangeAddress& rRange )
672 : {
673 2 : bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
674 2 : bool bMultiRow = rRange.StartRow < rRange.EndRow;
675 :
676 2 : ScRange aRange;
677 2 : ScUnoConversion::FillScRange( aRange, rRange );
678 2 : const ScAddress& rStart = aRange.aStart;
679 2 : const ScAddress& rEnd = aRange.aEnd;
680 2 : ScDocument& rDoc = getScDocument();
681 : // set correct right border
682 2 : if( bMultiCol )
683 2 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_RIGHT );
684 : // set correct lower border
685 2 : if( bMultiRow )
686 2 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_BOTTOM );
687 : // do merge
688 2 : if( bMultiCol || bMultiRow )
689 2 : rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
690 : // #i93609# merged range in a single row: test if manual row height is needed
691 2 : if( !bMultiRow )
692 : {
693 0 : bool bTextWrap = static_cast< const SfxBoolItem* >( rDoc.GetAttr( rStart.Col(), rStart.Row(), rStart.Tab(), ATTR_LINEBREAK ) )->GetValue();
694 0 : if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
695 : {
696 0 : if( const EditTextObject* pEditObj = static_cast< const ScEditCell* >( rDoc.GetCell( rStart ) )->GetData() )
697 0 : bTextWrap = pEditObj->GetParagraphCount() > 1;
698 : }
699 : }
700 2 : }
701 :
702 : } // namespace xls
703 24 : } // namespace oox
704 :
705 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|