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