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