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 0 : CellModel::CellModel() :
70 : mnCellType( XML_TOKEN_INVALID ),
71 : mnXfId( -1 ),
72 0 : mbShowPhonetic( false )
73 : {
74 0 : }
75 :
76 0 : CellFormulaModel::CellFormulaModel() :
77 : mnFormulaType( XML_TOKEN_INVALID ),
78 0 : mnSharedId( -1 )
79 : {
80 0 : }
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 0 : bool CellFormulaModel::isValidSharedRef( const CellAddress& rCellAddr )
91 : {
92 : return
93 0 : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
94 0 : (maFormulaRef.StartColumn <= rCellAddr.Column) && (rCellAddr.Column <= maFormulaRef.EndColumn) &&
95 0 : (maFormulaRef.StartRow <= rCellAddr.Row) && (rCellAddr.Row <= maFormulaRef.EndRow);
96 : }
97 :
98 0 : DataTableModel::DataTableModel() :
99 : mb2dTable( false ),
100 : mbRowTable( false ),
101 : mbRef1Deleted( false ),
102 0 : mbRef2Deleted( false )
103 : {
104 0 : }
105 :
106 0 : CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
107 : WorksheetHelper( rHelper ),
108 0 : mnCurrRow( -1 )
109 : {
110 0 : }
111 :
112 0 : 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 0 : if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
117 0 : maColSpans[ nRow ] = rColSpans.getRanges();
118 0 : }
119 :
120 0 : void CellBlockBuffer::finalizeImport()
121 : {
122 0 : }
123 :
124 0 : SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
125 : WorksheetHelper( rHelper ),
126 : maCellBlocks( rHelper ),
127 0 : mbPendingSharedFmla( false )
128 : {
129 0 : }
130 :
131 0 : void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
132 : {
133 0 : maCellBlocks.setColSpans( nRow, rColSpans );
134 0 : }
135 :
136 0 : void SheetDataBuffer::setBlankCell( const CellModel& rModel )
137 : {
138 0 : setCellFormat( rModel );
139 0 : }
140 :
141 0 : void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
142 : {
143 0 : putValue( rModel.maCellAddr, fValue );
144 0 : setCellFormat( rModel );
145 0 : }
146 :
147 0 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
148 : {
149 0 : putString( rModel.maCellAddr, rText );
150 0 : setCellFormat( rModel );
151 0 : }
152 :
153 0 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
154 : {
155 : OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
156 0 : const Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
157 0 : OUString aText;
158 0 : if( rxString->extractPlainString( aText, pFirstPortionFont ) )
159 : {
160 0 : setStringCell( rModel, aText );
161 : }
162 : else
163 : {
164 0 : putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
165 0 : setCellFormat( rModel );
166 0 : }
167 0 : }
168 :
169 0 : void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
170 : {
171 0 : RichStringRef xString = getSharedStrings().getString( nStringId );
172 0 : if( xString.get() )
173 0 : setStringCell( rModel, xString );
174 : else
175 0 : setBlankCell( rModel );
176 0 : }
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 0 : 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 0 : maTableOperations.push_back( TableOperation( rRange, rModel ) );
289 0 : }
290 :
291 0 : void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
292 : {
293 : // set row formatting
294 0 : 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 0 : 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 0 : }
311 :
312 0 : void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
313 : {
314 0 : maMergedRanges.push_back( MergedRange( rRange ) );
315 0 : }
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 0 : 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 0 : Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
335 0 : if ( pXf1 )
336 : {
337 0 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rMap.begin(), it_end = rMap.end(); it != it_end; ++it )
338 : {
339 0 : if ( it->first.second == nFormatId )
340 : {
341 0 : Xf* pXf2 = rStyles.getCellXf( it->first.first ).get();
342 0 : 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 0 : return;
349 : }
350 : }
351 : }
352 0 : rMap[ std::pair<sal_Int32, sal_Int32>( nXfId, nFormatId ) ] = rRangeList;
353 : }
354 : }
355 :
356 0 : void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ::com::sun::star::table::CellRangeAddress& rAddress, bool bProcessRowRange )
357 : {
358 0 : RowRangeStyle aStyleRows;
359 0 : aStyleRows.mnNumFmt.first = nXfId;
360 0 : aStyleRows.mnNumFmt.second = nFormatId;
361 0 : aStyleRows.mnStartRow = rAddress.StartRow;
362 0 : aStyleRows.mnEndRow = rAddress.EndRow;
363 0 : for ( sal_Int32 nCol = rAddress.StartColumn; nCol <= rAddress.EndColumn; ++nCol )
364 : {
365 0 : if ( !bProcessRowRange )
366 0 : 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 0 : }
416 0 : void SheetDataBuffer::finalizeImport()
417 : {
418 : // insert all cells of all open cell blocks
419 0 : maCellBlocks.finalizeImport();
420 :
421 : // create all array formulas
422 0 : 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 0 : for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
427 0 : finalizeTableOperation( aIt->first, aIt->second );
428 :
429 : // write default formatting of remaining row range
430 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
431 :
432 0 : std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList > rangeStyleListMap;
433 0 : for( XfIdRangeListMap::const_iterator aIt = maXfIdRangeLists.begin(), aEnd = maXfIdRangeLists.end(); aIt != aEnd; ++aIt )
434 : {
435 0 : 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 0 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rangeStyleListMap.begin(), it_end = rangeStyleListMap.end(); it != it_end; ++it )
439 : {
440 0 : const ApiCellRangeList& rRanges( it->second );
441 0 : for ( ApiCellRangeList::const_iterator it_range = rRanges.begin(), it_rangeend = rRanges.end(); it_range!=it_rangeend; ++it_range )
442 0 : addColXfStyle( it->first.first, it->first.second, *it_range );
443 : }
444 :
445 0 : for ( std::map< sal_Int32, std::vector< ValueRange > >::iterator it = maXfIdRowRangeList.begin(), it_end = maXfIdRowRangeList.end(); it != it_end; ++it )
446 : {
447 0 : ApiCellRangeList rangeList;
448 0 : AddressConverter& rAddrConv = getAddressConverter();
449 : // get all row ranges for id
450 0 : for ( std::vector< ValueRange >::iterator rangeIter = it->second.begin(), rangeIter_end = it->second.end(); rangeIter != rangeIter_end; ++rangeIter )
451 : {
452 0 : if ( it->first == -1 ) // it's a dud skip it
453 0 : 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 0 : }
459 :
460 0 : ScDocument& rDoc = getScDocument();
461 0 : StylesBuffer& rStyles = getStyles();
462 0 : for ( ColStyles::iterator col = maStylesPerColumn.begin(), col_end = maStylesPerColumn.end(); col != col_end; ++col )
463 : {
464 0 : RowStyles& rRowStyles = col->second;
465 0 : std::list<ScAttrEntry> aAttrs;
466 0 : SCCOL nScCol = static_cast< SCCOL >( col->first );
467 0 : for ( RowStyles::iterator rRows = rRowStyles.begin(), rRows_end = rRowStyles.end(); rRows != rRows_end; ++rRows )
468 : {
469 0 : Xf* pXf = rStyles.getCellXf( rRows->mnNumFmt.first ).get();
470 :
471 0 : if ( pXf )
472 0 : pXf->applyPatternToAttrList( aAttrs, rRows->mnStartRow, rRows->mnEndRow, rRows->mnNumFmt.second );
473 : }
474 0 : if (aAttrs.empty() || aAttrs.back().nRow != MAXROW)
475 : {
476 : ScAttrEntry aEntry;
477 0 : aEntry.nRow = MAXROW;
478 0 : aEntry.pPattern = rDoc.GetPattern(nScCol, 0, getSheetIndex());
479 0 : rDoc.GetPool()->Put(*aEntry.pPattern);
480 0 : aAttrs.push_back(aEntry);
481 : }
482 :
483 0 : size_t nAttrSize = aAttrs.size();
484 0 : ScAttrEntry* pData = new ScAttrEntry[nAttrSize];
485 0 : std::list<ScAttrEntry>::const_iterator itr = aAttrs.begin(), itrEnd = aAttrs.end();
486 0 : for (size_t i = 0; itr != itrEnd; ++itr, ++i)
487 0 : pData[i] = *itr;
488 :
489 0 : rDoc.SetAttrEntries(nScCol, getSheetIndex(), pData, static_cast<SCSIZE>(nAttrSize));
490 0 : }
491 :
492 : // merge all cached merged ranges and update right/bottom cell borders
493 0 : for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
494 0 : applyCellMerging( aIt->maRange );
495 0 : for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
496 0 : applyCellMerging( aIt->maRange );
497 0 : }
498 :
499 : // private --------------------------------------------------------------------
500 :
501 0 : SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
502 : maRowRange( -1 ),
503 0 : mnXfId( -1 )
504 : {
505 0 : }
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 0 : SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
533 : maRange( rRange ),
534 0 : mnHorAlign( XML_TOKEN_INVALID )
535 : {
536 0 : }
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 0 : void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
578 : {
579 0 : if (rModel.mbRef1Deleted)
580 0 : return;
581 :
582 0 : if (rModel.maRef1.isEmpty())
583 0 : return;
584 :
585 0 : if (rRange.StartColumn <= 0 || rRange.StartRow <= 0)
586 0 : return;
587 :
588 0 : sal_Int16 nSheet = getSheetIndex();
589 :
590 0 : CellAddress aRef1;
591 0 : if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
592 0 : return;
593 :
594 0 : ScDocumentImport& rDoc = getDocImport();
595 0 : ScTabOpParam aParam;
596 :
597 0 : ScRange aScRange;
598 0 : ScUnoConversion::FillScRange(aScRange, rRange);
599 :
600 0 : if (rModel.mb2dTable)
601 : {
602 : // Two-variable data table.
603 0 : if (rModel.mbRef2Deleted)
604 0 : return;
605 :
606 0 : if (rModel.maRef2.isEmpty())
607 0 : return;
608 :
609 0 : CellAddress aRef2;
610 0 : if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
611 0 : return;
612 :
613 0 : aParam.meMode = ScTabOpParam::Both;
614 :
615 0 : aParam.aRefFormulaCell.Set(rRange.StartColumn-1, rRange.StartRow-1, nSheet, false, false, false);
616 0 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
617 :
618 0 : aScRange.aStart.IncRow(-1);
619 0 : aScRange.aStart.IncCol(-1);
620 :
621 : // Ref1 is row input cell and Ref2 is column input cell.
622 0 : aParam.aRefRowCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
623 0 : aParam.aRefColCell.Set(aRef2.Column, aRef2.Row, aRef2.Sheet, false, false, false);
624 0 : rDoc.setTableOpCells(aScRange, aParam);
625 :
626 0 : return;
627 : }
628 :
629 : // One-variable data table.
630 :
631 0 : if (rModel.mbRowTable)
632 : {
633 : // One-variable row input cell (horizontal).
634 0 : aParam.meMode = ScTabOpParam::Row;
635 0 : aParam.aRefRowCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
636 0 : aParam.aRefFormulaCell.Set(rRange.StartColumn-1, rRange.StartRow, nSheet, false, true, false);
637 0 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
638 0 : aScRange.aStart.IncRow(-1);
639 0 : rDoc.setTableOpCells(aScRange, aParam);
640 : }
641 : else
642 : {
643 : // One-variable column input cell (vertical).
644 0 : aParam.meMode = ScTabOpParam::Column;
645 0 : aParam.aRefColCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
646 0 : aParam.aRefFormulaCell.Set(rRange.StartColumn, rRange.StartRow-1, nSheet, true, false, false);
647 0 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
648 0 : aScRange.aStart.IncCol(-1);
649 0 : rDoc.setTableOpCells(aScRange, aParam);
650 : }
651 : }
652 :
653 0 : void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
654 : {
655 0 : if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
656 : {
657 0 : ApiCellRangeList::reverse_iterator aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
658 0 : 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 0 : if( aIt != aItEnd &&
663 0 : aIt->Sheet == rModel.maCellAddr.Sheet &&
664 0 : aIt->StartRow == aIt->EndRow &&
665 0 : aIt->StartRow == rModel.maCellAddr.Row &&
666 0 : (aIt->EndColumn+1) == rModel.maCellAddr.Column )
667 : {
668 0 : aIt->EndColumn++; // Expand Column
669 : }
670 : else
671 : {
672 0 : maXfIdRangeLists[ XfIdNumFmtKey (rModel.mnXfId, nNumFmtId ) ].push_back(
673 : CellRangeAddress( rModel.maCellAddr.Sheet, rModel.maCellAddr.Column, rModel.maCellAddr.Row,
674 0 : rModel.maCellAddr.Column, rModel.maCellAddr.Row ) );
675 : }
676 :
677 0 : aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
678 0 : aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
679 0 : ApiCellRangeList::reverse_iterator aItM = aIt+1;
680 0 : while( aItM != aItEnd )
681 : {
682 0 : if( aIt->Sheet == aItM->Sheet )
683 : {
684 : /* Try to merge this with the previous range */
685 0 : if( aIt->StartRow == (aItM->EndRow + 1) &&
686 0 : aIt->StartColumn == aItM->StartColumn &&
687 0 : aIt->EndColumn == aItM->EndColumn)
688 : {
689 0 : aItM->EndRow = aIt->EndRow;
690 0 : maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].pop_back();
691 0 : break;
692 : }
693 0 : else if( aIt->StartRow > aItM->EndRow + 1 )
694 0 : break; // Un-necessary to check with any other rows
695 : }
696 : else
697 0 : break;
698 0 : ++aItM;
699 : }
700 :
701 : // update merged ranges for 'center across selection' and 'fill'
702 0 : if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
703 : {
704 0 : sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
705 0 : 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 0 : }
717 :
718 0 : void lcl_SetBorderLine( ScDocument& rDoc, ScRange& rRange, SCTAB nScTab, sal_uInt16 nLine )
719 : {
720 0 : SCCOL nFromScCol = (nLine == BOX_LINE_RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
721 0 : SCROW nFromScRow = (nLine == BOX_LINE_BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
722 :
723 : const SvxBoxItem* pFromItem = static_cast< const SvxBoxItem* >(
724 0 : rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ) );
725 : const SvxBoxItem* pToItem = static_cast< const SvxBoxItem* >(
726 0 : rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ) );
727 :
728 0 : SvxBoxItem aNewItem( *pToItem );
729 0 : aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
730 0 : rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
731 0 : }
732 :
733 0 : void SheetDataBuffer::applyCellMerging( const CellRangeAddress& rRange )
734 : {
735 0 : bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
736 0 : bool bMultiRow = rRange.StartRow < rRange.EndRow;
737 :
738 0 : ScRange aRange;
739 0 : ScUnoConversion::FillScRange( aRange, rRange );
740 0 : const ScAddress& rStart = aRange.aStart;
741 0 : const ScAddress& rEnd = aRange.aEnd;
742 0 : ScDocument& rDoc = getScDocument();
743 : // set correct right border
744 0 : if( bMultiCol )
745 0 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_RIGHT );
746 : // set correct lower border
747 0 : if( bMultiRow )
748 0 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_BOTTOM );
749 : // do merge
750 0 : if( bMultiCol || bMultiRow )
751 0 : 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 0 : if( !bMultiRow )
754 : {
755 0 : bool bTextWrap = static_cast< const SfxBoolItem* >( rDoc.GetAttr( rStart.Col(), rStart.Row(), rStart.Tab(), ATTR_LINEBREAK ) )->GetValue();
756 0 : 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 0 : }
763 :
764 : } // namespace xls
765 0 : } // namespace oox
766 :
767 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|