Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include "sheetdatabuffer.hxx"
30 : :
31 : : #include <algorithm>
32 : : #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
33 : : #include <com/sun/star/sheet/XCellRangeData.hpp>
34 : : #include <com/sun/star/sheet/XFormulaTokens.hpp>
35 : : #include <com/sun/star/sheet/XMultipleOperation.hpp>
36 : : #include <com/sun/star/table/XCell.hpp>
37 : : #include <com/sun/star/text/XText.hpp>
38 : : #include <com/sun/star/util/DateTime.hpp>
39 : : #include <com/sun/star/util/NumberFormat.hpp>
40 : : #include <com/sun/star/util/XMergeable.hpp>
41 : : #include <com/sun/star/util/XNumberFormatTypes.hpp>
42 : : #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
43 : : #include <rtl/ustrbuf.hxx>
44 : : #include <editeng/boxitem.hxx>
45 : : #include <editeng/editobj.hxx>
46 : : #include <svl/eitem.hxx>
47 : : #include "oox/helper/containerhelper.hxx"
48 : : #include "oox/helper/propertymap.hxx"
49 : : #include "oox/helper/propertyset.hxx"
50 : : #include "oox/token/tokens.hxx"
51 : : #include "addressconverter.hxx"
52 : : #include "biffinputstream.hxx"
53 : : #include "formulaparser.hxx"
54 : : #include "sharedstringsbuffer.hxx"
55 : : #include "unitconverter.hxx"
56 : : #include "convuno.hxx"
57 : : #include "markdata.hxx"
58 : : #include "rangelst.hxx"
59 : : #include "document.hxx"
60 : : #include "scitems.hxx"
61 : : #include "cell.hxx"
62 : :
63 : : namespace oox {
64 : : namespace xls {
65 : :
66 : : // ============================================================================
67 : :
68 : : using namespace ::com::sun::star::lang;
69 : : using namespace ::com::sun::star::sheet;
70 : : using namespace ::com::sun::star::table;
71 : : using namespace ::com::sun::star::text;
72 : : using namespace ::com::sun::star::uno;
73 : : using namespace ::com::sun::star::util;
74 : :
75 : : using ::rtl::OUString;
76 : : using ::rtl::OUStringBuffer;
77 : :
78 : : // ============================================================================
79 : :
80 : 60 : CellModel::CellModel() :
81 : : mnCellType( XML_TOKEN_INVALID ),
82 : : mnXfId( -1 ),
83 : 60 : mbShowPhonetic( false )
84 : : {
85 : 60 : }
86 : :
87 : : // ----------------------------------------------------------------------------
88 : :
89 : 60 : CellFormulaModel::CellFormulaModel() :
90 : : mnFormulaType( XML_TOKEN_INVALID ),
91 : 60 : mnSharedId( -1 )
92 : : {
93 : 60 : }
94 : :
95 : 0 : bool CellFormulaModel::isValidArrayRef( const CellAddress& rCellAddr )
96 : : {
97 : : return
98 : : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
99 : : (maFormulaRef.StartColumn == rCellAddr.Column) &&
100 [ # # ][ # # ]: 0 : (maFormulaRef.StartRow == rCellAddr.Row);
[ # # ]
101 : : }
102 : :
103 : 3 : bool CellFormulaModel::isValidSharedRef( const CellAddress& rCellAddr )
104 : : {
105 : : return
106 : : (maFormulaRef.Sheet == rCellAddr.Sheet) &&
107 : : (maFormulaRef.StartColumn <= rCellAddr.Column) && (rCellAddr.Column <= maFormulaRef.EndColumn) &&
108 [ + - ][ + - ]: 3 : (maFormulaRef.StartRow <= rCellAddr.Row) && (rCellAddr.Row <= maFormulaRef.EndRow);
[ + - ][ + - ]
[ + - ]
109 : : }
110 : :
111 : : // ----------------------------------------------------------------------------
112 : :
113 : 60 : DataTableModel::DataTableModel() :
114 : : mb2dTable( false ),
115 : : mbRowTable( false ),
116 : : mbRef1Deleted( false ),
117 : 60 : mbRef2Deleted( false )
118 : : {
119 : 60 : }
120 : :
121 : : // ============================================================================
122 : :
123 : 60 : CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
124 : : WorksheetHelper( rHelper ),
125 [ + - ]: 60 : mnCurrRow( -1 )
126 : : {
127 : 60 : }
128 : :
129 : 243 : void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
130 : : {
131 : : OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
132 : : OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
133 [ + - ][ + - ]: 243 : if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
[ + - ]
134 : 243 : maColSpans[ nRow ] = rColSpans.getRanges();
135 : 243 : }
136 : :
137 : 60 : void CellBlockBuffer::finalizeImport()
138 : : {
139 : 60 : }
140 : :
141 : : // ============================================================================
142 : :
143 : 60 : SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
144 : : WorksheetHelper( rHelper ),
145 : : maCellBlocks( rHelper ),
146 [ + - ][ + - ]: 60 : mbPendingSharedFmla( false )
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
147 : : {
148 : 60 : }
149 : :
150 : 243 : void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
151 : : {
152 : 243 : maCellBlocks.setColSpans( nRow, rColSpans );
153 : 243 : }
154 : :
155 : 0 : void SheetDataBuffer::setBlankCell( const CellModel& rModel )
156 : : {
157 : 0 : setCellFormat( rModel );
158 : 0 : }
159 : :
160 : 195 : void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
161 : : {
162 : 195 : putValue( rModel.maCellAddr, fValue );
163 : 195 : setCellFormat( rModel );
164 : 195 : }
165 : :
166 : 147 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
167 : : {
168 : 147 : putString( rModel.maCellAddr, rText );
169 : 147 : setCellFormat( rModel );
170 : 147 : }
171 : :
172 : 147 : void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
173 : : {
174 : : OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
175 [ + - ][ + - ]: 147 : const Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
[ + - ]
176 : 147 : OUString aText;
177 [ + - ][ + - ]: 147 : if( rxString->extractPlainString( aText, pFirstPortionFont ) )
178 : : {
179 [ + - ]: 147 : setStringCell( rModel, aText );
180 : : }
181 : : else
182 : : {
183 [ # # ]: 0 : putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
184 [ # # ]: 0 : setCellFormat( rModel );
185 : 147 : }
186 : 147 : }
187 : :
188 : 147 : void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
189 : : {
190 [ + - ][ + - ]: 147 : RichStringRef xString = getSharedStrings().getString( nStringId );
191 [ + - ]: 147 : if( xString.get() )
192 [ + - ]: 147 : setStringCell( rModel, xString );
193 : : else
194 [ # # ][ + - ]: 147 : setBlankCell( rModel );
195 : 147 : }
196 : :
197 : 0 : void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const ::com::sun::star::util::DateTime& rDateTime )
198 : : {
199 : : // write serial date/time value into the cell
200 : 0 : double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
201 : 0 : setValueCell( rModel, fSerial );
202 : : // set appropriate number format
203 : : using namespace ::com::sun::star::util::NumberFormat;
204 [ # # ][ # # ]: 0 : sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
[ # # ][ # # ]
205 : 0 : setStandardNumFmt( rModel.maCellAddr, nStdFmt );
206 : 0 : }
207 : :
208 : 0 : void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
209 : : {
210 [ # # ]: 0 : setCellFormula( rModel.maCellAddr, getFormulaParser().convertBoolToFormula( bValue ) );
211 : : // #108770# set 'Standard' number format for all Boolean cells
212 : 0 : setCellFormat( rModel, 0 );
213 : 0 : }
214 : :
215 : 0 : void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
216 : : {
217 : 0 : setErrorCell( rModel, getUnitConverter().calcBiffErrorCode( rErrorCode ) );
218 : 0 : }
219 : :
220 : 0 : void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
221 : : {
222 [ # # ]: 0 : setCellFormula( rModel.maCellAddr, getFormulaParser().convertErrorToFormula( nErrorCode ) );
223 : 0 : setCellFormat( rModel );
224 : 0 : }
225 : :
226 : 0 : void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
227 : : {
228 : 0 : mbPendingSharedFmla = false;
229 [ # # ]: 0 : ApiTokenSequence aTokens;
230 : :
231 : : /* Detect special token passed as placeholder for array formulas, shared
232 : : formulas, and table operations. In BIFF, these formulas are represented
233 : : by a single tExp resp. tTbl token. If the formula parser finds these
234 : : tokens, it puts a single OPCODE_BAD token with the base address and
235 : : formula type into the token sequence. This information will be
236 : : extracted here, and in case of a shared formula, the shared formula
237 : : buffer will generate the resulting formula token array. */
238 : 0 : ApiSpecialTokenInfo aTokenInfo;
239 [ # # ][ # # ]: 0 : if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
[ # # ][ # # ]
[ # # ]
240 : : {
241 : : /* The second member of the token info is set to true, if the formula
242 : : represents a table operation, which will be skipped. In BIFF12 it
243 : : is not possible to distinguish array and shared formulas
244 : : (BIFF5/BIFF8 provide this information with a special flag in the
245 : : FORMULA record). */
246 [ # # ]: 0 : if( !aTokenInfo.Second )
247 : : {
248 : : /* Construct the token array representing the shared formula. If
249 : : the returned sequence is empty, the definition of the shared
250 : : formula has not been loaded yet, or the cell is part of an
251 : : array formula. In this case, the cell will be remembered. After
252 : : reading the formula definition it will be retried to insert the
253 : : formula via retryPendingSharedFormulaCell(). */
254 : 0 : BinAddress aBaseAddr( aTokenInfo.First );
255 [ # # ][ # # ]: 0 : aTokens = resolveSharedFormula( aBaseAddr );
[ # # ]
256 [ # # ]: 0 : if( !aTokens.hasElements() )
257 : : {
258 : 0 : maSharedFmlaAddr = rModel.maCellAddr;
259 : 0 : maSharedBaseAddr = aBaseAddr;
260 : 0 : mbPendingSharedFmla = true;
261 : : }
262 : : }
263 : : }
264 : : else
265 : : {
266 : : // simple formula, use the passed token array
267 [ # # ]: 0 : aTokens = rTokens;
268 : : }
269 : :
270 [ # # ]: 0 : setCellFormula( rModel.maCellAddr, aTokens );
271 [ # # ][ # # ]: 0 : setCellFormat( rModel );
272 : 0 : }
273 : :
274 : 0 : void SheetDataBuffer::createArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens )
275 : : {
276 : : /* Array formulas will be inserted later in finalizeImport(). This is
277 : : needed to not disturb collecting all the cells, which will be put into
278 : : the sheet in large blocks to increase performance. */
279 [ # # ]: 0 : maArrayFormulas.push_back( ArrayFormula( rRange, rTokens ) );
280 : 0 : }
281 : :
282 : 0 : void SheetDataBuffer::createTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel )
283 : : {
284 : : /* Table operations will be inserted later in finalizeImport(). This is
285 : : needed to not disturb collecting all the cells, which will be put into
286 : : the sheet in large blocks to increase performance. */
287 [ # # ]: 0 : maTableOperations.push_back( TableOperation( rRange, rModel ) );
288 : 0 : }
289 : :
290 : 0 : void SheetDataBuffer::createSharedFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
291 : : {
292 [ # # ]: 0 : createSharedFormula( BinAddress( rCellAddr ), rTokens );
293 : 0 : }
294 : :
295 : 243 : void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
296 : : {
297 : : // set row formatting
298 [ - + ]: 243 : if( bCustomFormat )
299 : : {
300 : : // try to expand cached row range, if formatting is equal
301 [ # # ][ # # ]: 0 : if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
[ # # ]
302 : : {
303 : :
304 : 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
305 : 0 : maXfIdRowRange.set( nRow, nXfId );
306 : : }
307 : : }
308 [ - + ]: 243 : else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
309 : : {
310 : : // finish last cached row range
311 : 0 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
312 : 0 : maXfIdRowRange.set( -1, -1 );
313 : : }
314 : 243 : }
315 : :
316 : 3 : void SheetDataBuffer::setMergedRange( const CellRangeAddress& rRange )
317 : : {
318 [ + - ]: 3 : maMergedRanges.push_back( MergedRange( rRange ) );
319 : 3 : }
320 : :
321 : 0 : void SheetDataBuffer::setStandardNumFmt( const CellAddress& rCellAddr, sal_Int16 nStdNumFmt )
322 : : {
323 : : try
324 : : {
325 [ # # ][ # # ]: 0 : Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
326 [ # # ][ # # ]: 0 : Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
[ # # ]
327 [ # # ][ # # ]: 0 : sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdNumFmt, Locale() );
328 [ # # ][ # # ]: 0 : PropertySet aPropSet( getCell( rCellAddr ) );
329 [ # # ][ # # ]: 0 : aPropSet.setProperty( PROP_NumberFormat, nIndex );
[ # # ]
330 : : }
331 : 0 : catch( Exception& )
332 : : {
333 : : }
334 : 0 : }
335 : :
336 : 93 : void addIfNotInMyMap( StylesBuffer& rStyles, std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ApiCellRangeList& rRangeList )
337 : : {
338 : 93 : Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
339 [ + - ]: 93 : if ( pXf1 )
340 : : {
341 [ + + ]: 405 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rMap.begin(), it_end = rMap.end(); it != it_end; ++it )
342 : : {
343 [ + - ]: 312 : if ( it->first.second == nFormatId )
344 : : {
345 [ + - ][ + - ]: 312 : Xf* pXf2 = rStyles.getCellXf( it->first.first ).get();
346 [ + - ][ - + ]: 312 : if ( *pXf1 == *pXf2 ) // already exists
347 : : {
348 : : // add ranges from the rangelist to the existing rangelist for the
349 : : // matching style ( should we check if they overlap ? )
350 [ # # ][ # # ]: 0 : for ( ApiCellRangeList::const_iterator iter = rRangeList.begin(), iter_end = rRangeList.end(); iter != iter_end; ++iter )
351 [ # # ]: 0 : it->second.push_back( *iter );
352 : 93 : return;
353 : : }
354 : : }
355 : : }
356 [ + - ][ + - ]: 93 : rMap[ std::pair<sal_Int32, sal_Int32>( nXfId, nFormatId ) ] = rRangeList;
357 : : }
358 : : }
359 : :
360 : 60 : void SheetDataBuffer::finalizeImport()
361 : : {
362 : : // insert all cells of all open cell blocks
363 : 60 : maCellBlocks.finalizeImport();
364 : :
365 : : // create all array formulas
366 [ - + ]: 60 : for( ArrayFormulaList::iterator aIt = maArrayFormulas.begin(), aEnd = maArrayFormulas.end(); aIt != aEnd; ++aIt )
367 [ # # ]: 0 : finalizeArrayFormula( aIt->first, aIt->second );
368 : :
369 : : // create all table operations
370 [ - + ]: 60 : for( TableOperationList::iterator aIt = maTableOperations.begin(), aEnd = maTableOperations.end(); aIt != aEnd; ++aIt )
371 [ # # ]: 0 : finalizeTableOperation( aIt->first, aIt->second );
372 : :
373 : : // write default formatting of remaining row range
374 [ + - ][ + - ]: 60 : maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
375 [ + + ]: 120 : for ( std::map< sal_Int32, std::vector< ValueRange > >::iterator it = maXfIdRowRangeList.begin(), it_end = maXfIdRowRangeList.end(); it != it_end; ++it )
376 : : {
377 [ + - ]: 60 : ApiCellRangeList rangeList;
378 [ + - ]: 60 : AddressConverter& rAddrConv = getAddressConverter();
379 : : // get all row ranges for id
380 [ + - ][ + + ]: 120 : for ( std::vector< ValueRange >::iterator rangeIter = it->second.begin(), rangeIter_end = it->second.end(); rangeIter != rangeIter_end; ++rangeIter )
381 : : {
382 [ + - ]: 60 : CellRangeAddress aRange( getSheetIndex(), 0, rangeIter->mnFirst, rAddrConv.getMaxApiAddress().Column, rangeIter->mnLast );
383 [ + - ]: 60 : rangeList.push_back( aRange );
384 : : }
385 [ + - ]: 60 : ScRangeList aList;
386 [ + - ][ + - ]: 120 : for ( ApiCellRangeList::const_iterator itRange = rangeList.begin(), itRange_end = rangeList.end(); itRange!=itRange_end; ++itRange )
[ + - ][ + + ]
387 : : {
388 [ + - ]: 60 : ScRange* pRange = new ScRange();
389 : 60 : ScUnoConversion::FillScRange( *pRange, *itRange );
390 [ + - ]: 60 : aList.push_back( pRange );
391 : : }
392 [ + - ]: 60 : ScMarkData aMark;
393 [ + - ]: 60 : aMark.MarkFromRangeList( aList, false );
394 : :
395 [ + - ][ + - ]: 60 : getStyles().writeCellXfToMarkData( aMark, it->first, -1 );
396 [ + - ][ + - ]: 60 : }
397 [ + - ]: 60 : std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList > rangeStyleListMap;
398 : : // gather all ranges that have the same style and apply them in bulk
399 [ + + ]: 153 : for( XfIdRangeListMap::const_iterator aIt = maXfIdRangeLists.begin(), aEnd = maXfIdRangeLists.end(); aIt != aEnd; ++aIt )
400 [ + - ][ + - ]: 93 : addIfNotInMyMap( getStyles(), rangeStyleListMap, aIt->first.first, aIt->first.second, aIt->second );
401 [ + + ]: 153 : for ( std::map< std::pair< sal_Int32, sal_Int32 >, ApiCellRangeList >::iterator it = rangeStyleListMap.begin(), it_end = rangeStyleListMap.end(); it != it_end; ++it )
402 [ + - ]: 93 : writeXfIdRangeListProperties( it->first.first, it->first.second, it->second );
403 : : // merge all cached merged ranges and update right/bottom cell borders
404 [ + + ]: 63 : for( MergedRangeList::iterator aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt )
405 [ + - ]: 3 : applyCellMerging( aIt->maRange );
406 [ - + ]: 60 : for( MergedRangeList::iterator aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt )
407 [ # # ]: 60 : applyCellMerging( aIt->maRange );
408 : 60 : }
409 : :
410 : : // private --------------------------------------------------------------------
411 : :
412 : 60 : SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
413 : : maRowRange( -1 ),
414 : 60 : mnXfId( -1 )
415 : : {
416 : 60 : }
417 : :
418 : 0 : void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
419 : : {
420 : 0 : maRowRange = ValueRange( nRow );
421 : 0 : mnXfId = nXfId;
422 : 0 : }
423 : :
424 : 0 : bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
425 : : {
426 [ # # ]: 0 : if( mnXfId == nXfId )
427 : : {
428 [ # # ]: 0 : if( maRowRange.mnLast + 1 == nRow )
429 : : {
430 : 0 : ++maRowRange.mnLast;
431 : 0 : return true;
432 : : }
433 [ # # ]: 0 : if( maRowRange.mnFirst == nRow + 1 )
434 : : {
435 : 0 : --maRowRange.mnFirst;
436 : 0 : return true;
437 : : }
438 : : }
439 : 0 : return false;
440 : : }
441 : :
442 : :
443 : 3 : SheetDataBuffer::MergedRange::MergedRange( const CellRangeAddress& rRange ) :
444 : : maRange( rRange ),
445 : 3 : mnHorAlign( XML_TOKEN_INVALID )
446 : : {
447 : 3 : }
448 : :
449 : 0 : SheetDataBuffer::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) :
450 : : maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ),
451 : 0 : mnHorAlign( nHorAlign )
452 : : {
453 : 0 : }
454 : :
455 : 0 : bool SheetDataBuffer::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign )
456 : : {
457 [ # # ][ # # ]: 0 : if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) &&
[ # # ][ # # ]
458 : : (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) )
459 : : {
460 : 0 : ++maRange.EndColumn;
461 : 0 : return true;
462 : : }
463 : 0 : return false;
464 : : }
465 : :
466 : : // ----------------------------------------------------------------------------
467 : :
468 : 0 : void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTokenSequence& rTokens )
469 : : {
470 [ # # ]: 0 : if( rTokens.hasElements() )
471 : : {
472 : 0 : putFormulaTokens( rCellAddr, rTokens );
473 : : }
474 : 0 : }
475 : :
476 : 0 : void SheetDataBuffer::createSharedFormula( const BinAddress& rMapKey, const ApiTokenSequence& rTokens )
477 : : {
478 : : // create the defined name that will represent the shared formula
479 [ # # ]: 0 : OUString aName = OUStringBuffer().appendAscii( RTL_CONSTASCII_STRINGPARAM( "__shared_" ) ).
480 [ # # ][ # # ]: 0 : append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) ).
481 [ # # ][ # # ]: 0 : append( sal_Unicode( '_' ) ).append( rMapKey.mnRow ).
482 [ # # ][ # # ]: 0 : append( sal_Unicode( '_' ) ).append( rMapKey.mnCol ).makeStringAndClear();
[ # # ]
483 [ # # ]: 0 : ScRangeData* pScRangeData = createNamedRangeObject( aName, rTokens, 0 );
484 : 0 : pScRangeData->SetType(RT_SHARED);
485 : :
486 : : // get and store the token index of the defined name
487 : : OSL_ENSURE( maSharedFormulas.count( rMapKey ) == 0, "SheetDataBuffer::createSharedFormula - shared formula exists already" );
488 : 0 : sal_Int32 nTokenIndex = static_cast< sal_Int32 >( pScRangeData->GetIndex() );
489 [ # # ]: 0 : if( nTokenIndex >= 0 ) try
490 : : {
491 : : // store the token index in the map
492 [ # # ]: 0 : maSharedFormulas[ rMapKey ] = nTokenIndex;
493 : : // retry to insert a pending shared formula cell
494 [ # # ]: 0 : if( mbPendingSharedFmla )
495 [ # # ][ # # ]: 0 : setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
[ # # ][ # # ]
496 : : }
497 [ # # ]: 0 : catch( Exception& )
498 : : {
499 : : }
500 : 0 : mbPendingSharedFmla = false;
501 : 0 : }
502 : :
503 : 0 : ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const BinAddress& rMapKey ) const
504 : : {
505 [ # # ]: 0 : sal_Int32 nTokenIndex = ContainerHelper::getMapElement( maSharedFormulas, rMapKey, -1 );
506 [ # # ]: 0 : return (nTokenIndex >= 0) ? getFormulaParser().convertNameToFormula( nTokenIndex ) : ApiTokenSequence();
507 : : }
508 : :
509 : 0 : void SheetDataBuffer::finalizeArrayFormula( const CellRangeAddress& rRange, const ApiTokenSequence& rTokens ) const
510 : : {
511 [ # # ][ # # ]: 0 : Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
512 : : OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
513 [ # # ]: 0 : if( xTokens.is() )
514 [ # # ][ # # ]: 0 : xTokens->setArrayTokens( rTokens );
515 : 0 : }
516 : :
517 : 0 : void SheetDataBuffer::finalizeTableOperation( const CellRangeAddress& rRange, const DataTableModel& rModel ) const
518 : : {
519 [ # # ]: 0 : sal_Int16 nSheet = getSheetIndex();
520 : 0 : bool bOk = false;
521 [ # # ][ # # ]: 0 : if( !rModel.mbRef1Deleted && !rModel.maRef1.isEmpty() && (rRange.StartColumn > 0) && (rRange.StartRow > 0) )
[ # # ][ # # ]
[ # # ]
522 : : {
523 : 0 : CellRangeAddress aOpRange = rRange;
524 : 0 : CellAddress aRef1;
525 [ # # ][ # # ]: 0 : if( getAddressConverter().convertToCellAddress( aRef1, rModel.maRef1, nSheet, true ) ) try
[ # # ]
526 : : {
527 [ # # ]: 0 : if( rModel.mb2dTable )
528 : : {
529 : 0 : CellAddress aRef2;
530 [ # # ][ # # ]: 0 : if( !rModel.mbRef2Deleted && getAddressConverter().convertToCellAddress( aRef2, rModel.maRef2, nSheet, true ) )
[ # # ][ # # ]
[ # # ]
531 : : {
532 : : // API call expects input values inside operation range
533 : 0 : --aOpRange.StartColumn;
534 : 0 : --aOpRange.StartRow;
535 : : // formula range is top-left cell of operation range
536 : 0 : CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn, aOpRange.StartRow, aOpRange.StartColumn, aOpRange.StartRow );
537 : : // set multiple operation
538 [ # # ][ # # ]: 0 : Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
539 [ # # ][ # # ]: 0 : xMultOp->setTableOperation( aFormulaRange, TableOperationMode_BOTH, aRef2, aRef1 );
540 : 0 : bOk = true;
541 : : }
542 : : }
543 [ # # ]: 0 : else if( rModel.mbRowTable )
544 : : {
545 : : // formula range is column to the left of operation range
546 : 0 : CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn - 1, aOpRange.StartRow, aOpRange.StartColumn - 1, aOpRange.EndRow );
547 : : // API call expects input values (top row) inside operation range
548 : 0 : --aOpRange.StartRow;
549 : : // set multiple operation
550 [ # # ][ # # ]: 0 : Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
551 [ # # ][ # # ]: 0 : xMultOp->setTableOperation( aFormulaRange, TableOperationMode_ROW, aRef1, aRef1 );
552 : 0 : bOk = true;
553 : : }
554 : : else
555 : : {
556 : : // formula range is row above operation range
557 : 0 : CellRangeAddress aFormulaRange( nSheet, aOpRange.StartColumn, aOpRange.StartRow - 1, aOpRange.EndColumn, aOpRange.StartRow - 1 );
558 : : // API call expects input values (left column) inside operation range
559 : 0 : --aOpRange.StartColumn;
560 : : // set multiple operation
561 [ # # ][ # # ]: 0 : Reference< XMultipleOperation > xMultOp( getCellRange( aOpRange ), UNO_QUERY_THROW );
562 [ # # ][ # # ]: 0 : xMultOp->setTableOperation( aFormulaRange, TableOperationMode_COLUMN, aRef1, aRef1 );
563 [ # # ]: 0 : bOk = true;
564 : : }
565 : : }
566 [ # # ]: 0 : catch( Exception& )
567 : : {
568 : : }
569 : : }
570 : :
571 : : // on error: fill cell range with #REF! error codes
572 [ # # ]: 0 : if( !bOk ) try
573 : : {
574 [ # # ][ # # ]: 0 : Reference< XCellRangeData > xCellRangeData( getCellRange( rRange ), UNO_QUERY_THROW );
575 : 0 : size_t nWidth = static_cast< size_t >( rRange.EndColumn - rRange.StartColumn + 1 );
576 : 0 : size_t nHeight = static_cast< size_t >( rRange.EndRow - rRange.StartRow + 1 );
577 [ # # ][ # # ]: 0 : Matrix< Any > aErrorCells( nWidth, nHeight, Any( getFormulaParser().convertErrorToFormula( BIFF_ERR_REF ) ) );
[ # # ][ # # ]
[ # # ]
578 [ # # ][ # # ]: 0 : xCellRangeData->setDataArray( ContainerHelper::matrixToSequenceSequence( aErrorCells ) );
[ # # ][ # # ]
[ # # ]
579 : : }
580 [ # # ]: 0 : catch( Exception& )
581 : : {
582 : : }
583 : 0 : }
584 : :
585 : 444 : void SheetDataBuffer::setCellFormat( const CellModel& rModel, sal_Int32 nNumFmtId )
586 : : {
587 [ + + ][ - + ]: 444 : if( (rModel.mnXfId >= 0) || (nNumFmtId >= 0) )
588 : : {
589 [ + - ][ + - ]: 411 : ApiCellRangeList::reverse_iterator aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
590 [ + - ][ + - ]: 411 : ApiCellRangeList::reverse_iterator aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
591 : : /* The xlsx sheet data contains row wise information.
592 : : * It is sufficient to check if the row range size is one
593 : : */
594 [ + + ][ + - ]: 1527 : if( aIt != aItEnd &&
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ]
595 [ + - ]: 318 : aIt->Sheet == rModel.maCellAddr.Sheet &&
596 [ + - ][ + - ]: 318 : aIt->StartRow == aIt->EndRow &&
597 [ + - ]: 270 : aIt->StartRow == rModel.maCellAddr.Row &&
598 [ + - ]: 210 : (aIt->EndColumn+1) == rModel.maCellAddr.Column )
599 : : {
600 [ + - ]: 195 : aIt->EndColumn++; // Expand Column
601 : : }
602 : : else
603 : : {
604 [ + - ][ + - ]: 216 : maXfIdRangeLists[ XfIdNumFmtKey (rModel.mnXfId, nNumFmtId ) ].push_back(
605 : : CellRangeAddress( rModel.maCellAddr.Sheet, rModel.maCellAddr.Column, rModel.maCellAddr.Row,
606 [ + - ]: 432 : rModel.maCellAddr.Column, rModel.maCellAddr.Row ) );
607 : : }
608 : :
609 [ + - ][ + - ]: 411 : aIt = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rbegin();
610 [ + - ][ + - ]: 411 : aItEnd = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].rend();
611 [ + - ]: 411 : ApiCellRangeList::reverse_iterator aItM = aIt+1;
612 [ + - ][ + + ]: 594 : while( aItM != aItEnd )
613 : : {
614 [ + - ][ + - ]: 327 : if( aIt->Sheet == aItM->Sheet )
[ + - ]
615 : : {
616 : : /* Try to merge this with the previous range */
617 [ + - ][ + - ]: 750 : if( aIt->StartRow == (aItM->EndRow + 1) &&
[ + + ][ + + ]
[ + + ][ + + ]
618 [ + - ][ + - ]: 228 : aIt->StartColumn == aItM->StartColumn &&
619 [ + - ][ + - ]: 195 : aIt->EndColumn == aItM->EndColumn)
620 : : {
621 [ + - ][ + - ]: 69 : aItM->EndRow = aIt->EndRow;
622 [ + - ][ + - ]: 69 : maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, nNumFmtId ) ].pop_back();
[ + - ]
623 : 69 : break;
624 : : }
625 [ + - ][ + - ]: 258 : else if( aIt->StartRow > aItM->EndRow + 1 )
[ + + ]
626 : 75 : break; // Un-necessary to check with any other rows
627 : : }
628 : : else
629 : 0 : break;
630 [ + - ]: 183 : ++aItM;
631 : : }
632 : :
633 : : // update merged ranges for 'center across selection' and 'fill'
634 [ + - ][ + - ]: 411 : if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
[ + - ][ + - ]
635 : : {
636 : 411 : sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
637 [ - + ][ + - ]: 411 : if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
638 : : {
639 : : /* start new merged range, if cell is not empty (#108781#),
640 : : or try to expand last range with empty cell */
641 [ # # ]: 0 : if( rModel.mnCellType != XML_TOKEN_INVALID )
642 [ # # ]: 0 : maCenterFillRanges.push_back( MergedRange( rModel.maCellAddr, nHorAlign ) );
643 [ # # ]: 0 : else if( !maCenterFillRanges.empty() )
644 [ # # ]: 411 : maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
645 : : }
646 : : }
647 : : }
648 : 444 : }
649 : :
650 : 93 : void SheetDataBuffer::writeXfIdRangeListProperties( sal_Int32 nXfId, sal_Int32 nNumFmtId, const ApiCellRangeList& rRanges ) const
651 : : {
652 [ + - ]: 93 : StylesBuffer& rStyles = getStyles();
653 [ + - ]: 93 : ScRangeList aList;
654 [ + - ][ + + ]: 240 : for ( ApiCellRangeList::const_iterator it = rRanges.begin(), it_end = rRanges.end(); it!=it_end; ++it )
655 : : {
656 [ + - ]: 147 : ScRange* pRange = new ScRange();
657 : 147 : ScUnoConversion::FillScRange( *pRange, *it );
658 [ + - ]: 147 : aList.push_back( pRange );
659 : : }
660 [ + - ]: 93 : ScMarkData aMark;
661 [ + - ]: 93 : aMark.MarkFromRangeList( aList, false );
662 [ + - ][ + - ]: 93 : rStyles.writeCellXfToMarkData( aMark, nXfId, nNumFmtId );
[ + - ]
663 : 93 : }
664 : :
665 : 6 : void lcl_SetBorderLine( ScDocument& rDoc, ScRange& rRange, SCTAB nScTab, sal_uInt16 nLine )
666 : : {
667 [ + + ]: 6 : SCCOL nFromScCol = (nLine == BOX_LINE_RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
668 [ + + ]: 6 : SCROW nFromScRow = (nLine == BOX_LINE_BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
669 : :
670 : : const SvxBoxItem* pFromItem = static_cast< const SvxBoxItem* >(
671 [ + - ]: 6 : rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ) );
672 : : const SvxBoxItem* pToItem = static_cast< const SvxBoxItem* >(
673 [ + - ]: 6 : rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ) );
674 : :
675 [ + - ]: 6 : SvxBoxItem aNewItem( *pToItem );
676 [ + - ][ + - ]: 6 : aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
677 [ + - ][ + - ]: 6 : rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
678 : 6 : }
679 : :
680 : 3 : void SheetDataBuffer::applyCellMerging( const CellRangeAddress& rRange )
681 : : {
682 : 3 : bool bMultiCol = rRange.StartColumn < rRange.EndColumn;
683 : 3 : bool bMultiRow = rRange.StartRow < rRange.EndRow;
684 : :
685 : 3 : ScRange aRange;
686 : 3 : ScUnoConversion::FillScRange( aRange, rRange );
687 : 3 : const ScAddress& rStart = aRange.aStart;
688 : 3 : const ScAddress& rEnd = aRange.aEnd;
689 [ + - ]: 3 : ScDocument& rDoc = getScDocument();
690 : : // set correct right border
691 [ + - ]: 3 : if( bMultiCol )
692 [ + - ][ + - ]: 3 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_RIGHT );
693 : : // set correct lower border
694 [ + - ]: 3 : if( bMultiRow )
695 [ + - ][ + - ]: 3 : lcl_SetBorderLine( rDoc, aRange, getSheetIndex(), BOX_LINE_BOTTOM );
696 : : // do merge
697 [ - + ][ # # ]: 3 : if( bMultiCol || bMultiRow )
698 [ + - ][ + - ]: 3 : rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
699 : : // #i93609# merged range in a single row: test if manual row height is needed
700 [ - + ]: 3 : if( !bMultiRow )
701 : : {
702 [ # # ]: 0 : bool bTextWrap = static_cast< const SfxBoolItem* >( rDoc.GetAttr( rStart.Col(), rStart.Row(), rStart.Tab(), ATTR_LINEBREAK ) )->GetValue();
703 [ # # ][ # # ]: 0 : if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
[ # # ][ # # ]
704 : : {
705 [ # # ][ # # ]: 0 : if( const EditTextObject* pEditObj = static_cast< const ScEditCell* >( rDoc.GetCell( rStart ) )->GetData() )
706 [ # # ]: 0 : bTextWrap = pEditObj->GetParagraphCount() > 1;
707 : : }
708 : : }
709 : 3 : }
710 : :
711 : : } // namespace xls
712 [ + - ][ + - ]: 24 : } // namespace oox
713 : :
714 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|