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 260 : CellModel::CellModel() :
70 : mnCellType( XML_TOKEN_INVALID ),
71 : mnXfId( -1 ),
72 260 : mbShowPhonetic( false )
73 : {
74 260 : }
75 :
76 260 : CellFormulaModel::CellFormulaModel() :
77 : mnFormulaType( XML_TOKEN_INVALID ),
78 260 : mnSharedId( -1 )
79 : {
80 260 : }
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 126 : bool CellFormulaModel::isValidSharedRef( const CellAddress& rCellAddr )
91 : {
92 : return
93 252 : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
94 378 : (maFormulaRef.StartColumn <= rCellAddr.Column) && (rCellAddr.Column <= maFormulaRef.EndColumn) &&
95 378 : (maFormulaRef.StartRow <= rCellAddr.Row) && (rCellAddr.Row <= maFormulaRef.EndRow);
96 : }
97 :
98 260 : DataTableModel::DataTableModel() :
99 : mb2dTable( false ),
100 : mbRowTable( false ),
101 : mbRef1Deleted( false ),
102 260 : mbRef2Deleted( false )
103 : {
104 260 : }
105 :
106 260 : CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
107 : WorksheetHelper( rHelper ),
108 260 : mnCurrRow( -1 )
109 : {
110 260 : }
111 :
112 2180 : 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 2180 : if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
117 2180 : maColSpans[ nRow ] = rColSpans.getRanges();
118 2180 : }
119 :
120 260 : void CellBlockBuffer::finalizeImport()
121 : {
122 260 : }
123 :
124 260 : SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
125 : WorksheetHelper( rHelper ),
126 : maCellBlocks( rHelper ),
127 260 : mbPendingSharedFmla( false )
128 : {
129 260 : }
130 :
131 2180 : void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
132 : {
133 2180 : maCellBlocks.setColSpans( nRow, rColSpans );
134 2180 : }
135 :
136 2018 : void SheetDataBuffer::setBlankCell( const CellModel& rModel )
137 : {
138 2018 : setCellFormat( rModel );
139 2018 : }
140 :
141 5162 : void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
142 : {
143 5162 : putValue( rModel.maCellAddr, fValue );
144 5162 : setCellFormat( rModel );
145 5162 : }
146 :
147 1608 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
148 : {
149 1608 : putString( rModel.maCellAddr, rText );
150 1608 : setCellFormat( rModel );
151 1608 : }
152 :
153 1610 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
154 : {
155 : OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
156 1610 : const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
157 1610 : OUString aText;
158 1610 : if( rxString->extractPlainString( aText, pFirstPortionFont ) )
159 : {
160 1608 : setStringCell( rModel, aText );
161 : }
162 : else
163 : {
164 2 : putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
165 2 : setCellFormat( rModel );
166 1610 : }
167 1610 : }
168 :
169 1610 : void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
170 : {
171 1610 : RichStringRef xString = getSharedStrings().getString( nStringId );
172 1610 : if( xString.get() )
173 1610 : setStringCell( rModel, xString );
174 : else
175 0 : setBlankCell( rModel );
176 1610 : }
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 2 : void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
190 : {
191 2 : getFormulaBuffer().setCellFormula(
192 4 : rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
193 :
194 : // #108770# set 'Standard' number format for all Boolean cells
195 2 : setCellFormat( rModel, 0 );
196 2 : }
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 6 : 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 6 : maTableOperations.push_back( TableOperation( rRange, rModel ) );
289 6 : }
290 :
291 2180 : void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
292 : {
293 : // set row formatting
294 2180 : 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 2180 : 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 2180 : }
311 :
312 28 : void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
313 : {
314 28 : maMergedRanges.push_back( MergedRange( rRange ) );
315 28 : }
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 380 : 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 380 : Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
335 380 : if ( pXf1 )
336 : {
337 2102 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rMap.begin(), it_end = rMap.end(); it != it_end; ++it )
338 : {
339 1724 : if ( it->first.second == nFormatId )
340 : {
341 1724 : Xf* pXf2 = rStyles.getCellXf( it->first.first ).get();
342 1724 : 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 ( ::std::vector< CellRangeAddress >::const_iterator iter = rRangeList.begin(), iter_end = rRangeList.end(); iter != iter_end; ++iter )
347 0 : it->second.push_back( *iter );
348 380 : return;
349 : }
350 : }
351 : }
352 756 : rMap[ std::pair<sal_Int32, sal_Int32>( nXfId, nFormatId ) ] = rRangeList;
353 : }
354 : }
355 :
356 768 : void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ::com::sun::star::table::CellRangeAddress& rAddress, bool bProcessRowRange )
357 : {
358 768 : RowRangeStyle aStyleRows;
359 768 : aStyleRows.mnNumFmt.first = nXfId;
360 768 : aStyleRows.mnNumFmt.second = nFormatId;
361 768 : aStyleRows.mnStartRow = rAddress.StartRow;
362 768 : aStyleRows.mnEndRow = rAddress.EndRow;
363 2134 : for ( sal_Int32 nCol = rAddress.StartColumn; nCol <= rAddress.EndColumn; ++nCol )
364 : {
365 1366 : if ( !bProcessRowRange )
366 1366 : 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 768 : }
416 260 : void SheetDataBuffer::finalizeImport()
417 : {
418 : // insert all cells of all open cell blocks
419 260 : maCellBlocks.finalizeImport();
420 :
421 : // create all array formulas
422 260 : 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 266 : for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
427 6 : finalizeTableOperation( aIt->first, aIt->second );
428 :
429 : // write default formatting of remaining row range
430 260 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
431 :
432 260 : std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList > rangeStyleListMap;
433 640 : for( XfIdRangeListMap::const_iterator aIt = maXfIdRangeLists.begin(), aEnd = maXfIdRangeLists.end(); aIt != aEnd; ++aIt )
434 : {
435 380 : 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 638 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rangeStyleListMap.begin(), it_end = rangeStyleListMap.end(); it != it_end; ++it )
439 : {
440 378 : const ApiCellRangeList& rRanges( it->second );
441 1146 : for ( ::std::vector< CellRangeAddress >::const_iterator it_range = rRanges.begin(), it_rangeend = rRanges.end(); it_range!=it_rangeend; ++it_range )
442 768 : addColXfStyle( it->first.first, it->first.second, *it_range );
443 : }
444 :
445 520 : for ( std::map< sal_Int32, std::vector< ValueRange > >::iterator it = maXfIdRowRangeList.begin(), it_end = maXfIdRowRangeList.end(); it != it_end; ++it )
446 : {
447 260 : ApiCellRangeList rangeList;
448 260 : AddressConverter& rAddrConv = getAddressConverter();
449 : // get all row ranges for id
450 520 : for ( std::vector< ValueRange >::iterator rangeIter = it->second.begin(), rangeIter_end = it->second.end(); rangeIter != rangeIter_end; ++rangeIter )
451 : {
452 260 : if ( it->first == -1 ) // it's a dud skip it
453 260 : 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 260 : }
459 :
460 260 : ScDocumentImport& rDoc = getDocImport();
461 260 : StylesBuffer& rStyles = getStyles();
462 752 : for ( ColStyles::iterator col = maStylesPerColumn.begin(), col_end = maStylesPerColumn.end(); col != col_end; ++col )
463 : {
464 492 : RowStyles& rRowStyles = col->second;
465 492 : Xf::AttrList aAttrs;
466 492 : SCCOL nScCol = static_cast< SCCOL >( col->first );
467 1858 : for ( RowStyles::iterator rRows = rRowStyles.begin(), rRows_end = rRowStyles.end(); rRows != rRows_end; ++rRows )
468 : {
469 1366 : Xf* pXf = rStyles.getCellXf( rRows->mnNumFmt.first ).get();
470 :
471 1366 : if ( pXf )
472 1366 : pXf->applyPatternToAttrList( aAttrs, rRows->mnStartRow, rRows->mnEndRow, rRows->mnNumFmt.second );
473 : }
474 492 : if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nRow != MAXROW)
475 : {
476 : ScAttrEntry aEntry;
477 492 : aEntry.nRow = MAXROW;
478 492 : aEntry.pPattern = rDoc.getDoc().GetPattern(nScCol, 0, getSheetIndex());
479 492 : rDoc.getDoc().GetPool()->Put(*aEntry.pPattern);
480 492 : aAttrs.maAttrs.push_back(aEntry);
481 :
482 492 : if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc.getDoc()))
483 0 : aAttrs.mbLatinNumFmtOnly = false;
484 : }
485 :
486 492 : ScDocumentImport::Attrs aAttrParam;
487 492 : aAttrParam.mnSize = aAttrs.maAttrs.size();
488 492 : aAttrParam.mpData = new ScAttrEntry[aAttrParam.mnSize];
489 492 : aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
490 492 : std::list<ScAttrEntry>::const_iterator itr = aAttrs.maAttrs.begin(), itrEnd = aAttrs.maAttrs.end();
491 2852 : for (size_t i = 0; itr != itrEnd; ++itr, ++i)
492 2360 : aAttrParam.mpData[i] = *itr;
493 :
494 492 : rDoc.setAttrEntries(getSheetIndex(), nScCol, aAttrParam);
495 492 : }
496 :
497 : // merge all cached merged ranges and update right/bottom cell borders
498 288 : for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
499 28 : applyCellMerging( aIt->maRange );
500 260 : for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
501 260 : applyCellMerging( aIt->maRange );
502 260 : }
503 :
504 : // private --------------------------------------------------------------------
505 :
506 260 : SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
507 : maRowRange( -1 ),
508 260 : mnXfId( -1 )
509 : {
510 260 : }
511 :
512 0 : void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
513 : {
514 0 : maRowRange = ValueRange( nRow );
515 0 : mnXfId = nXfId;
516 0 : }
517 :
518 0 : bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
519 : {
520 0 : if( mnXfId == nXfId )
521 : {
522 0 : if( maRowRange.mnLast + 1 == nRow )
523 : {
524 0 : ++maRowRange.mnLast;
525 0 : return true;
526 : }
527 0 : if( maRowRange.mnFirst == nRow + 1 )
528 : {
529 0 : --maRowRange.mnFirst;
530 0 : return true;
531 : }
532 : }
533 0 : return false;
534 : }
535 :
536 28 : SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
537 : maRange( rRange ),
538 28 : mnHorAlign( XML_TOKEN_INVALID )
539 : {
540 28 : }
541 :
542 0 : SheetDataBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
543 : maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
544 0 : mnHorAlign( nHorAlign )
545 : {
546 0 : }
547 :
548 0 : bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
549 : {
550 0 : if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
551 0 : (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
552 : {
553 0 : ++maRange.EndColumn;
554 0 : return true;
555 : }
556 0 : return false;
557 : }
558 :
559 0 : void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
560 : {
561 0 : if( rTokens.hasElements() )
562 : {
563 0 : putFormulaTokens( rCellAddr, rTokens );
564 : }
565 0 : }
566 :
567 0 : ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const BinAddress& rMapKey ) const
568 : {
569 0 : sal_Int32 nTokenIndex = ContainerHelper::getMapElement( maSharedFormulas, rMapKey, -1 );
570 0 : return (nTokenIndex >= 0) ? getFormulaParser().convertNameToFormula( nTokenIndex ) : ApiTokenSequence();
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 6 : void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
582 : {
583 6 : if (rModel.mbRef1Deleted)
584 2 : return;
585 :
586 6 : if (rModel.maRef1.isEmpty())
587 0 : return;
588 :
589 6 : if (rRange.StartColumn <= 0 || rRange.StartRow <= 0)
590 0 : return;
591 :
592 6 : sal_Int16 nSheet = getSheetIndex();
593 :
594 6 : CellAddress aRef1;
595 6 : if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
596 0 : return;
597 :
598 6 : ScDocumentImport& rDoc = getDocImport();
599 6 : ScTabOpParam aParam;
600 :
601 6 : ScRange aScRange;
602 6 : ScUnoConversion::FillScRange(aScRange, rRange);
603 :
604 6 : if (rModel.mb2dTable)
605 : {
606 : // Two-variable data table.
607 2 : if (rModel.mbRef2Deleted)
608 0 : return;
609 :
610 2 : if (rModel.maRef2.isEmpty())
611 0 : return;
612 :
613 2 : CellAddress aRef2;
614 2 : if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
615 0 : return;
616 :
617 2 : aParam.meMode = ScTabOpParam::Both;
618 :
619 2 : aParam.aRefFormulaCell.Set(rRange.StartColumn-1, rRange.StartRow-1, nSheet, false, false, false);
620 2 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
621 :
622 2 : aScRange.aStart.IncRow(-1);
623 2 : aScRange.aStart.IncCol(-1);
624 :
625 : // Ref1 is row input cell and Ref2 is column input cell.
626 2 : aParam.aRefRowCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
627 2 : aParam.aRefColCell.Set(aRef2.Column, aRef2.Row, aRef2.Sheet, false, false, false);
628 2 : rDoc.setTableOpCells(aScRange, aParam);
629 :
630 2 : return;
631 : }
632 :
633 : // One-variable data table.
634 :
635 4 : if (rModel.mbRowTable)
636 : {
637 : // One-variable row input cell (horizontal).
638 2 : aParam.meMode = ScTabOpParam::Row;
639 2 : aParam.aRefRowCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
640 2 : aParam.aRefFormulaCell.Set(rRange.StartColumn-1, rRange.StartRow, nSheet, false, true, false);
641 2 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
642 2 : aScRange.aStart.IncRow(-1);
643 2 : rDoc.setTableOpCells(aScRange, aParam);
644 : }
645 : else
646 : {
647 : // One-variable column input cell (vertical).
648 2 : aParam.meMode = ScTabOpParam::Column;
649 2 : aParam.aRefColCell.Set(aRef1.Column, aRef1.Row, aRef1.Sheet, false, false, false);
650 2 : aParam.aRefFormulaCell.Set(rRange.StartColumn, rRange.StartRow-1, nSheet, true, false, false);
651 2 : aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
652 2 : aScRange.aStart.IncCol(-1);
653 2 : rDoc.setTableOpCells(aScRange, aParam);
654 : }
655 : }
656 :
657 13164 : void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
658 : {
659 13164 : if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
660 : {
661 8060 : ::std::vector< CellRangeAddress >::reverse_iterator aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
662 8060 : ::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 23800 : if( aIt != aItEnd &&
667 15360 : aIt->Sheet == rModel.maCellAddr.Sheet &&
668 12090 : aIt->StartRow == aIt->EndRow &&
669 16380 : aIt->StartRow == rModel.maCellAddr.Row &&
670 3910 : (aIt->EndColumn+1) == rModel.maCellAddr.Column )
671 : {
672 3774 : aIt->EndColumn++; // Expand Column
673 : }
674 : else
675 : {
676 8572 : maXfIdRangeLists[ XfIdNumFmtKey (rModel.mnXfId, nNumFmtId ) ].push_back(
677 : CellRangeAddress( rModel.maCellAddr.Sheet, rModel.maCellAddr.Column, rModel.maCellAddr.Row,
678 12858 : rModel.maCellAddr.Column, rModel.maCellAddr.Row ) );
679 : }
680 :
681 8060 : aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
682 8060 : aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
683 8060 : ::std::vector< CellRangeAddress >::reverse_iterator aItM = aIt+1;
684 23042 : while( aItM != aItEnd )
685 : {
686 13652 : if( aIt->Sheet == aItM->Sheet )
687 : {
688 : /* Try to merge this with the previous range */
689 36268 : if( aIt->StartRow == (aItM->EndRow + 1) &&
690 20388 : aIt->StartColumn == aItM->StartColumn &&
691 6736 : aIt->EndColumn == aItM->EndColumn)
692 : {
693 3516 : aItM->EndRow = aIt->EndRow;
694 3516 : maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].pop_back();
695 3516 : break;
696 : }
697 10136 : else if( aIt->StartRow > aItM->EndRow + 1 )
698 3214 : break; // Un-necessary to check with any other rows
699 : }
700 : else
701 0 : break;
702 6922 : ++aItM;
703 : }
704 :
705 : // update merged ranges for 'center across selection' and 'fill'
706 8060 : if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
707 : {
708 8058 : sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
709 8058 : 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 13164 : }
721 :
722 32 : void lcl_SetBorderLine( ScDocument& rDoc, ScRange& rRange, SCTAB nScTab, sal_uInt16 nLine )
723 : {
724 32 : SCCOL nFromScCol = (nLine == BOX_LINE_RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
725 32 : SCROW nFromScRow = (nLine == BOX_LINE_BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
726 :
727 : const SvxBoxItem* pFromItem = static_cast< const SvxBoxItem* >(
728 32 : rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ) );
729 : const SvxBoxItem* pToItem = static_cast< const SvxBoxItem* >(
730 32 : rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ) );
731 :
732 32 : SvxBoxItem aNewItem( *pToItem );
733 32 : aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
734 32 : rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
735 32 : }
736 :
737 28 : void SheetDataBuffer::applyCellMerging( const CellRangeAddress& rRange )
738 : {
739 28 : bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
740 28 : bool bMultiRow = rRange.StartRow < rRange.EndRow;
741 :
742 28 : ScRange aRange;
743 28 : ScUnoConversion::FillScRange( aRange, rRange );
744 28 : const ScAddress& rStart = aRange.aStart;
745 28 : const ScAddress& rEnd = aRange.aEnd;
746 28 : ScDocument& rDoc = getScDocument();
747 : // set correct right border
748 28 : if( bMultiCol )
749 12 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_RIGHT );
750 : // set correct lower border
751 28 : if( bMultiRow )
752 20 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_BOTTOM );
753 : // do merge
754 28 : if( bMultiCol || bMultiRow )
755 28 : 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 28 : if( !bMultiRow )
758 : {
759 8 : bool bTextWrap = static_cast< const SfxBoolItem* >( rDoc.GetAttr( rStart.Col(), rStart.Row(), rStart.Tab(), ATTR_LINEBREAK ) )->GetValue();
760 8 : 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 28 : }
767 :
768 : } // namespace xls
769 48 : } // namespace oox
770 :
771 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|