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 "formulacell.hxx"
53 : #include "docpool.hxx"
54 : #include "paramisc.hxx"
55 : #include "documentimport.hxx"
56 : #include "formulabuffer.hxx"
57 :
58 : namespace oox {
59 : namespace xls {
60 :
61 : using namespace ::com::sun::star::lang;
62 : using namespace ::com::sun::star::sheet;
63 : using namespace ::com::sun::star::table;
64 : using namespace ::com::sun::star::text;
65 : using namespace ::com::sun::star::uno;
66 : using namespace ::com::sun::star::util;
67 :
68 :
69 85 : CellModel::CellModel() :
70 : mnCellType( XML_TOKEN_INVALID ),
71 : mnXfId( -1 ),
72 85 : mbShowPhonetic( false )
73 : {
74 85 : }
75 :
76 85 : CellFormulaModel::CellFormulaModel() :
77 : mnFormulaType( XML_TOKEN_INVALID ),
78 85 : mnSharedId( -1 )
79 : {
80 85 : }
81 :
82 0 : bool CellFormulaModel::isValidArrayRef( const CellAddress& rCellAddr )
83 : {
84 : return
85 0 : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
86 0 : (maFormulaRef.StartColumn == rCellAddr.Column) &&
87 0 : (maFormulaRef.StartRow == rCellAddr.Row);
88 : }
89 :
90 6 : bool CellFormulaModel::isValidSharedRef( const CellAddress& rCellAddr )
91 : {
92 : return
93 12 : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
94 18 : (maFormulaRef.StartColumn <= rCellAddr.Column) && (rCellAddr.Column <= maFormulaRef.EndColumn) &&
95 18 : (maFormulaRef.StartRow <= rCellAddr.Row) && (rCellAddr.Row <= maFormulaRef.EndRow);
96 : }
97 :
98 85 : DataTableModel::DataTableModel() :
99 : mb2dTable( false ),
100 : mbRowTable( false ),
101 : mbRef1Deleted( false ),
102 85 : mbRef2Deleted( false )
103 : {
104 85 : }
105 :
106 85 : CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
107 : WorksheetHelper( rHelper ),
108 85 : mnCurrRow( -1 )
109 : {
110 85 : }
111 :
112 540 : void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
113 : {
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 540 : if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
117 540 : maColSpans[ nRow ] = rColSpans.getRanges();
118 540 : }
119 :
120 85 : void CellBlockBuffer::finalizeImport()
121 : {
122 85 : }
123 :
124 85 : SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
125 : WorksheetHelper( rHelper ),
126 : maCellBlocks( rHelper ),
127 85 : mbPendingSharedFmla( false )
128 : {
129 85 : }
130 :
131 540 : void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
132 : {
133 540 : maCellBlocks.setColSpans( nRow, rColSpans );
134 540 : }
135 :
136 274 : void SheetDataBuffer::setBlankCell( const CellModel& rModel )
137 : {
138 274 : setCellFormat( rModel );
139 274 : }
140 :
141 1154 : void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
142 : {
143 1154 : putValue( rModel.maCellAddr, fValue );
144 1154 : setCellFormat( rModel );
145 1154 : }
146 :
147 274 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
148 : {
149 274 : putString( rModel.maCellAddr, rText );
150 274 : setCellFormat( rModel );
151 274 : }
152 :
153 275 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
154 : {
155 : OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
156 275 : const Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
157 275 : OUString aText;
158 275 : if( rxString->extractPlainString( aText, pFirstPortionFont ) )
159 : {
160 274 : setStringCell( rModel, aText );
161 : }
162 : else
163 : {
164 1 : putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
165 1 : setCellFormat( rModel );
166 275 : }
167 275 : }
168 :
169 275 : void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
170 : {
171 275 : RichStringRef xString = getSharedStrings().getString( nStringId );
172 275 : if( xString.get() )
173 275 : setStringCell( rModel, xString );
174 : else
175 0 : setBlankCell( rModel );
176 275 : }
177 :
178 0 : void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const ::com::sun::star::util::DateTime& rDateTime )
179 : {
180 : // write serial date/time value into the cell
181 0 : double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
182 0 : setValueCell( rModel, fSerial );
183 : // set appropriate number format
184 : using namespace ::com::sun::star::util::NumberFormat;
185 0 : sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
186 0 : setStandardNumFmt( rModel.maCellAddr, nStdFmt );
187 0 : }
188 :
189 0 : void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
190 : {
191 0 : getFormulaBuffer().setCellFormula(
192 0 : rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
193 :
194 : // #108770# set 'Standard' number format for all Boolean cells
195 0 : setCellFormat( rModel, 0 );
196 0 : }
197 :
198 0 : void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
199 : {
200 0 : setErrorCell( rModel, getUnitConverter().calcBiffErrorCode( rErrorCode ) );
201 0 : }
202 :
203 0 : void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
204 : {
205 0 : OUStringBuffer aBuf;
206 0 : aBuf.append('{');
207 0 : aBuf.append(BiffHelper::calcDoubleFromError(nErrorCode));
208 0 : aBuf.append('}');
209 :
210 0 : getFormulaBuffer().setCellFormula(rModel.maCellAddr, aBuf.makeStringAndClear());
211 0 : setCellFormat( rModel );
212 0 : }
213 :
214 0 : void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
215 : {
216 0 : ScDocument& rDoc = getScDocument();
217 0 : SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
218 :
219 0 : double fValue = 0.0;
220 0 : sal_uInt32 nFormatIndex = 0;
221 0 : bool bValid = pFormatter->IsNumberFormat( rDateString, nFormatIndex, fValue );
222 :
223 0 : if(bValid)
224 0 : setValueCell( rModel, fValue );
225 0 : }
226 :
227 0 : void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
228 : {
229 0 : mbPendingSharedFmla = false;
230 0 : ApiTokenSequence aTokens;
231 :
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 0 : ApiSpecialTokenInfo aTokenInfo;
240 0 : if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
241 : {
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
246 : FORMULA record). */
247 0 : if( !aTokenInfo.Second )
248 : {
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 0 : BinAddress aBaseAddr( aTokenInfo.First );
256 0 : aTokens = resolveSharedFormula( aBaseAddr );
257 0 : if( !aTokens.hasElements() )
258 : {
259 0 : maSharedFmlaAddr = rModel.maCellAddr;
260 0 : maSharedBaseAddr = aBaseAddr;
261 0 : mbPendingSharedFmla = true;
262 : }
263 : }
264 : }
265 : else
266 : {
267 : // simple formula, use the passed token array
268 0 : aTokens = rTokens;
269 : }
270 :
271 0 : setCellFormula( rModel.maCellAddr, aTokens );
272 0 : setCellFormat( rModel );
273 0 : }
274 :
275 0 : void SheetDataBuffer::createArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens )
276 : {
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 0 : maArrayFormulas.push_back( ArrayFormula( rRange, rTokens ) );
281 0 : }
282 :
283 3 : void SheetDataBuffer::createTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
284 : {
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 3 : maTableOperations.push_back( TableOperation( rRange, rModel ) );
289 3 : }
290 :
291 540 : void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
292 : {
293 : // set row formatting
294 540 : if( bCustomFormat )
295 : {
296 : // try to expand cached row range, if formatting is equal
297 0 : if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
298 : {
299 :
300 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
301 0 : maXfIdRowRange.set( nRow, nXfId );
302 : }
303 : }
304 540 : else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
305 : {
306 : // finish last cached row range
307 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
308 0 : maXfIdRowRange.set( -1, -1 );
309 : }
310 540 : }
311 :
312 2 : void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
313 : {
314 2 : maMergedRanges.push_back( MergedRange( rRange ) );
315 2 : }
316 :
317 0 : void SheetDataBuffer::setStandardNumFmt( const CellAddress& rCellAddr, sal_Int16 nStdNumFmt )
318 : {
319 : try
320 : {
321 0 : Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
322 0 : Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
323 0 : sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdNumFmt, Locale() );
324 0 : PropertySet aPropSet( getCell( rCellAddr ) );
325 0 : aPropSet.setProperty( PROP_NumberFormat, nIndex );
326 : }
327 0 : catch( Exception& )
328 : {
329 : }
330 0 : }
331 :
332 90 : void addIfNotInMyMap( StylesBuffer& rStyles, std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ApiCellRangeList& rRangeList )
333 : {
334 90 : Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
335 90 : if ( pXf1 )
336 : {
337 336 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rMap.begin(), it_end = rMap.end(); it != it_end; ++it )
338 : {
339 246 : if ( it->first.second == nFormatId )
340 : {
341 246 : Xf* pXf2 = rStyles.getCellXf( it->first.first ).get();
342 246 : if ( *pXf1 == *pXf2 ) // already exists
343 : {
344 : // add ranges from the rangelist to the existing rangelist for the
345 : // matching style ( should we check if they overlap ? )
346 0 : for ( ApiCellRangeList::const_iterator iter = rRangeList.begin(), iter_end = rRangeList.end(); iter != iter_end; ++iter )
347 0 : it->second.push_back( *iter );
348 90 : return;
349 : }
350 : }
351 : }
352 180 : rMap[ std::pair<sal_Int32, sal_Int32>( nXfId, nFormatId ) ] = rRangeList;
353 : }
354 : }
355 :
356 210 : void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ::com::sun::star::table::CellRangeAddress& rAddress, bool bProcessRowRange )
357 : {
358 210 : RowRangeStyle aStyleRows;
359 210 : aStyleRows.mnNumFmt.first = nXfId;
360 210 : aStyleRows.mnNumFmt.second = nFormatId;
361 210 : aStyleRows.mnStartRow = rAddress.StartRow;
362 210 : aStyleRows.mnEndRow = rAddress.EndRow;
363 535 : for ( sal_Int32 nCol = rAddress.StartColumn; nCol <= rAddress.EndColumn; ++nCol )
364 : {
365 325 : if ( !bProcessRowRange )
366 325 : maStylesPerColumn[ nCol ].insert( aStyleRows );
367 : else
368 : {
369 0 : RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
370 : // If the rowrange style includes rows already
371 : // allocated to a style then we need to split
372 : // the range style Rows into sections ( to
373 : // occupy only rows that have no style definition )
374 :
375 : // We dont want to set any rowstyle 'rows'
376 : // for rows where there is an existing 'style' )
377 0 : std::vector< RowRangeStyle > aRangeRowsSplits;
378 :
379 0 : RowStyles::iterator rows_it = rRowStyles.begin();
380 0 : RowStyles::iterator rows_end = rRowStyles.end();
381 0 : bool bAddRange = true;
382 0 : for ( ; rows_it != rows_end; ++rows_it )
383 : {
384 0 : const RowRangeStyle& r = *rows_it;
385 : // if row is completely within existing style, discard it
386 0 : if ( aStyleRows.mnStartRow >= r.mnStartRow && aStyleRows.mnEndRow <= r.mnEndRow )
387 0 : bAddRange = false;
388 0 : else if ( aStyleRows.mnStartRow <= r.mnStartRow )
389 : {
390 : // not intersecting at all?, if so finish as none left
391 : // to check ( row ranges are in ascending order
392 0 : if ( aStyleRows.mnEndRow < r.mnStartRow )
393 0 : break;
394 0 : else if ( aStyleRows.mnEndRow <= r.mnEndRow )
395 : {
396 0 : aStyleRows.mnEndRow = r.mnStartRow - 1;
397 0 : break;
398 : }
399 0 : if ( aStyleRows.mnStartRow < r.mnStartRow )
400 : {
401 0 : RowRangeStyle aSplit = aStyleRows;
402 0 : aSplit.mnEndRow = r.mnStartRow - 1;
403 0 : aRangeRowsSplits.push_back( aSplit );
404 : }
405 : }
406 : }
407 0 : std::vector< RowRangeStyle >::iterator splits_it = aRangeRowsSplits.begin();
408 0 : std::vector< RowRangeStyle >::iterator splits_end = aRangeRowsSplits.end();
409 0 : for ( ; splits_it != splits_end; ++splits_it )
410 0 : rRowStyles.insert( *splits_it );
411 0 : if ( bAddRange )
412 0 : rRowStyles.insert( aStyleRows );
413 : }
414 : }
415 210 : }
416 85 : void SheetDataBuffer::finalizeImport()
417 : {
418 : // insert all cells of all open cell blocks
419 85 : maCellBlocks.finalizeImport();
420 :
421 : // create all array formulas
422 85 : for( ArrayFormulaList::iterator aIt = maArrayFormulas.begin(), aEnd = maArrayFormulas.end(); aIt != aEnd; ++aIt )
423 0 : finalizeArrayFormula( aIt->first, aIt->second );
424 :
425 : // create all table operations
426 88 : for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
427 3 : finalizeTableOperation( aIt->first, aIt->second );
428 :
429 : // write default formatting of remaining row range
430 85 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
431 :
432 85 : std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList > rangeStyleListMap;
433 175 : for( XfIdRangeListMap::const_iterator aIt = maXfIdRangeLists.begin(), aEnd = maXfIdRangeLists.end(); aIt != aEnd; ++aIt )
434 : {
435 90 : addIfNotInMyMap( getStyles(), rangeStyleListMap, aIt->first.first, aIt->first.second, aIt->second );
436 : }
437 : // gather all ranges that have the same style and apply them in bulk
438 175 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rangeStyleListMap.begin(), it_end = rangeStyleListMap.end(); it != it_end; ++it )
439 : {
440 90 : const ApiCellRangeList& rRanges( it->second );
441 300 : for ( ApiCellRangeList::const_iterator it_range = rRanges.begin(), it_rangeend = rRanges.end(); it_range!=it_rangeend; ++it_range )
442 210 : addColXfStyle( it->first.first, it->first.second, *it_range );
443 : }
444 :
445 170 : for ( std::map< sal_Int32, std::vector< ValueRange > >::iterator it = maXfIdRowRangeList.begin(), it_end = maXfIdRowRangeList.end(); it != it_end; ++it )
446 : {
447 85 : ApiCellRangeList rangeList;
448 85 : AddressConverter& rAddrConv = getAddressConverter();
449 : // get all row ranges for id
450 170 : for ( std::vector< ValueRange >::iterator rangeIter = it->second.begin(), rangeIter_end = it->second.end(); rangeIter != rangeIter_end; ++rangeIter )
451 : {
452 85 : if ( it->first == -1 ) // it's a dud skip it
453 85 : continue;
454 0 : CellRangeAddress aRange( getSheetIndex(), 0, rangeIter->mnFirst, rAddrConv.getMaxApiAddress().Column, rangeIter->mnLast );
455 :
456 0 : addColXfStyle( it->first, -1, aRange, true );
457 : }
458 85 : }
459 :
460 85 : ScDocument& rDoc = getScDocument();
461 85 : StylesBuffer& rStyles = getStyles();
462 232 : for ( ColStyles::iterator col = maStylesPerColumn.begin(), col_end = maStylesPerColumn.end(); col != col_end; ++col )
463 : {
464 147 : RowStyles& rRowStyles = col->second;
465 147 : std::list<ScAttrEntry> aAttrs;
466 147 : SCCOL nScCol = static_cast< SCCOL >( col->first );
467 472 : for ( RowStyles::iterator rRows = rRowStyles.begin(), rRows_end = rRowStyles.end(); rRows != rRows_end; ++rRows )
468 : {
469 325 : Xf* pXf = rStyles.getCellXf( rRows->mnNumFmt.first ).get();
470 :
471 325 : if ( pXf )
472 325 : pXf->applyPatternToAttrList( aAttrs, rRows->mnStartRow, rRows->mnEndRow, rRows->mnNumFmt.second );
473 : }
474 147 : if (aAttrs.empty() || aAttrs.back().nRow != MAXROW)
475 : {
476 : ScAttrEntry aEntry;
477 147 : aEntry.nRow = MAXROW;
478 147 : aEntry.pPattern = rDoc.GetPattern(nScCol, 0, getSheetIndex());
479 147 : rDoc.GetPool()->Put(*aEntry.pPattern);
480 147 : aAttrs.push_back(aEntry);
481 : }
482 :
483 147 : size_t nAttrSize = aAttrs.size();
484 147 : ScAttrEntry* pData = new ScAttrEntry[nAttrSize];
485 147 : std::list<ScAttrEntry>::const_iterator itr = aAttrs.begin(), itrEnd = aAttrs.end();
486 727 : for (size_t i = 0; itr != itrEnd; ++itr, ++i)
487 580 : pData[i] = *itr;
488 :
489 147 : rDoc.SetAttrEntries(nScCol, getSheetIndex(), pData, static_cast<SCSIZE>(nAttrSize));
490 147 : }
491 :
492 : // merge all cached merged ranges and update right/bottom cell borders
493 87 : for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
494 2 : applyCellMerging( aIt->maRange );
495 85 : for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
496 85 : applyCellMerging( aIt->maRange );
497 85 : }
498 :
499 : // private --------------------------------------------------------------------
500 :
501 85 : SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
502 : maRowRange( -1 ),
503 85 : mnXfId( -1 )
504 : {
505 85 : }
506 :
507 0 : void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
508 : {
509 0 : maRowRange = ValueRange( nRow );
510 0 : mnXfId = nXfId;
511 0 : }
512 :
513 0 : bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
514 : {
515 0 : if( mnXfId == nXfId )
516 : {
517 0 : if( maRowRange.mnLast + 1 == nRow )
518 : {
519 0 : ++maRowRange.mnLast;
520 0 : return true;
521 : }
522 0 : if( maRowRange.mnFirst == nRow + 1 )
523 : {
524 0 : --maRowRange.mnFirst;
525 0 : return true;
526 : }
527 : }
528 0 : return false;
529 : }
530 :
531 :
532 2 : SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
533 : maRange( rRange ),
534 2 : mnHorAlign( XML_TOKEN_INVALID )
535 : {
536 2 : }
537 :
538 0 : SheetDataBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
539 : maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
540 0 : mnHorAlign( nHorAlign )
541 : {
542 0 : }
543 :
544 0 : bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
545 : {
546 0 : if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
547 0 : (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
548 : {
549 0 : ++maRange.EndColumn;
550 0 : return true;
551 : }
552 0 : return false;
553 : }
554 :
555 0 : void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
556 : {
557 0 : if( rTokens.hasElements() )
558 : {
559 0 : putFormulaTokens( rCellAddr, rTokens );
560 : }
561 0 : }
562 :
563 0 : ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const BinAddress& rMapKey ) const
564 : {
565 0 : sal_Int32 nTokenIndex = ContainerHelper::getMapElement( maSharedFormulas, rMapKey, -1 );
566 0 : return (nTokenIndex >= 0) ? getFormulaParser().convertNameToFormula( nTokenIndex ) : ApiTokenSequence();
567 : }
568 :
569 0 : void SheetDataBuffer::finalizeArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens ) const
570 : {
571 0 : Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
572 : OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
573 0 : if( xTokens.is() )
574 0 : xTokens->setArrayTokens( rTokens );
575 0 : }
576 :
577 3 : void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
578 : {
579 3 : if (rModel.mbRef1Deleted)
580 1 : return;
581 :
582 3 : if (rModel.maRef1.isEmpty())
583 0 : return;
584 :
585 3 : if (rRange.StartColumn <= 0 || rRange.StartRow <= 0)
586 0 : return;
587 :
588 3 : sal_Int16 nSheet = getSheetIndex();
589 :
590 3 : CellAddress aRef1;
591 3 : if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
592 0 : return;
593 :
594 3 : ScDocumentImport& rDoc = getDocImport();
595 3 : ScTabOpParam aParam;
596 :
597 3 : ScRange aScRange;
598 3 : ScUnoConversion::FillScRange(aScRange, rRange);
599 :
600 3 : if (rModel.mb2dTable)
601 : {
602 : // Two-variable data table.
603 1 : if (rModel.mbRef2Deleted)
604 0 : return;
605 :
606 1 : if (rModel.maRef2.isEmpty())
607 0 : return;
608 :
609 1 : CellAddress aRef2;
610 1 : if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
611 0 : return;
612 :
613 1 : aParam.meMode = ScTabOpParam::Both;
614 :
615 1 : aParam.aRefFormulaCell.Set(rRange.StartColumn-1, rRange.StartRow-1, nSheet, false, false, false);
616 1 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
617 :
618 1 : aScRange.aStart.IncRow(-1);
619 1 : aScRange.aStart.IncCol(-1);
620 :
621 : // Ref1 is row input cell and Ref2 is column input cell.
622 1 : aParam.aRefRowCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
623 1 : aParam.aRefColCell.Set(aRef2.Column, aRef2.Row, aRef2.Sheet, false, false, false);
624 1 : rDoc.setTableOpCells(aScRange, aParam);
625 :
626 1 : return;
627 : }
628 :
629 : // One-variable data table.
630 :
631 2 : if (rModel.mbRowTable)
632 : {
633 : // One-variable row input cell (horizontal).
634 1 : aParam.meMode = ScTabOpParam::Row;
635 1 : aParam.aRefRowCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
636 1 : aParam.aRefFormulaCell.Set(rRange.StartColumn-1, rRange.StartRow, nSheet, false, true, false);
637 1 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
638 1 : aScRange.aStart.IncRow(-1);
639 1 : rDoc.setTableOpCells(aScRange, aParam);
640 : }
641 : else
642 : {
643 : // One-variable column input cell (vertical).
644 1 : aParam.meMode = ScTabOpParam::Column;
645 1 : aParam.aRefColCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
646 1 : aParam.aRefFormulaCell.Set(rRange.StartColumn, rRange.StartRow-1, nSheet, true, false, false);
647 1 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
648 1 : aScRange.aStart.IncCol(-1);
649 1 : rDoc.setTableOpCells(aScRange, aParam);
650 : }
651 : }
652 :
653 1977 : void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
654 : {
655 1977 : if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
656 : {
657 1419 : ApiCellRangeList::reverse_iterator aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
658 1419 : ApiCellRangeList::reverse_iterator aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
659 : /* The xlsx sheet data contains row wise information.
660 : * It is sufficient to check if the row range size is one
661 : */
662 4167 : if( aIt != aItEnd &&
663 2658 : aIt->Sheet == rModel.maCellAddr.Sheet &&
664 2057 : aIt->StartRow == aIt->EndRow &&
665 2736 : aIt->StartRow == rModel.maCellAddr.Row &&
666 589 : (aIt->EndColumn+1) == rModel.maCellAddr.Column )
667 : {
668 543 : aIt->EndColumn++; // Expand Column
669 : }
670 : else
671 : {
672 1752 : maXfIdRangeLists[ XfIdNumFmtKey (rModel.mnXfId, nNumFmtId ) ].push_back(
673 : CellRangeAddress( rModel.maCellAddr.Sheet, rModel.maCellAddr.Column, rModel.maCellAddr.Row,
674 2628 : rModel.maCellAddr.Column, rModel.maCellAddr.Row ) );
675 : }
676 :
677 1419 : aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
678 1419 : aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
679 1419 : ApiCellRangeList::reverse_iterator aItM = aIt+1;
680 4302 : while( aItM != aItEnd )
681 : {
682 2454 : if( aIt->Sheet == aItM->Sheet )
683 : {
684 : /* Try to merge this with the previous range */
685 6798 : if( aIt->StartRow == (aItM->EndRow + 1) &&
686 3560 : aIt->StartColumn == aItM->StartColumn &&
687 1106 : aIt->EndColumn == aItM->EndColumn)
688 : {
689 666 : aItM->EndRow = aIt->EndRow;
690 666 : maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].pop_back();
691 666 : break;
692 : }
693 1788 : else if( aIt->StartRow > aItM->EndRow + 1 )
694 324 : break; // Un-necessary to check with any other rows
695 : }
696 : else
697 0 : break;
698 1464 : ++aItM;
699 : }
700 :
701 : // update merged ranges for 'center across selection' and 'fill'
702 1419 : if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
703 : {
704 1419 : sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
705 1419 : if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
706 : {
707 : /* start new merged range, if cell is not empty (#108781#),
708 : or try to expand last range with empty cell */
709 0 : if( rModel.mnCellType != XML_TOKEN_INVALID )
710 0 : maCenterFillRanges.push_back( MergedRange( rModel.maCellAddr, nHorAlign ) );
711 0 : else if( !maCenterFillRanges.empty() )
712 0 : maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
713 : }
714 : }
715 : }
716 1977 : }
717 :
718 3 : void lcl_SetBorderLine( ScDocument& rDoc, ScRange& rRange, SCTAB nScTab, sal_uInt16 nLine )
719 : {
720 3 : SCCOL nFromScCol = (nLine == BOX_LINE_RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
721 3 : SCROW nFromScRow = (nLine == BOX_LINE_BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
722 :
723 : const SvxBoxItem* pFromItem = static_cast< const SvxBoxItem* >(
724 3 : rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ) );
725 : const SvxBoxItem* pToItem = static_cast< const SvxBoxItem* >(
726 3 : rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ) );
727 :
728 3 : SvxBoxItem aNewItem( *pToItem );
729 3 : aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
730 3 : rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
731 3 : }
732 :
733 2 : void SheetDataBuffer::applyCellMerging( const CellRangeAddress& rRange )
734 : {
735 2 : bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
736 2 : bool bMultiRow = rRange.StartRow < rRange.EndRow;
737 :
738 2 : ScRange aRange;
739 2 : ScUnoConversion::FillScRange( aRange, rRange );
740 2 : const ScAddress& rStart = aRange.aStart;
741 2 : const ScAddress& rEnd = aRange.aEnd;
742 2 : ScDocument& rDoc = getScDocument();
743 : // set correct right border
744 2 : if( bMultiCol )
745 2 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_RIGHT );
746 : // set correct lower border
747 2 : if( bMultiRow )
748 1 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_BOTTOM );
749 : // do merge
750 2 : if( bMultiCol || bMultiRow )
751 2 : rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
752 : // #i93609# merged range in a single row: test if manual row height is needed
753 2 : if( !bMultiRow )
754 : {
755 1 : bool bTextWrap = static_cast< const SfxBoolItem* >( rDoc.GetAttr( rStart.Col(), rStart.Row(), rStart.Tab(), ATTR_LINEBREAK ) )->GetValue();
756 1 : if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
757 : {
758 0 : if (const EditTextObject* pEditObj = rDoc.GetEditText(rStart))
759 0 : bTextWrap = pEditObj->GetParagraphCount() > 1;
760 : }
761 : }
762 2 : }
763 :
764 : } // namespace xls
765 18 : } // namespace oox
766 :
767 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|