File: | home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx |
Warning: | line 672, column 9 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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/XSpreadsheetDocument.hpp> | |||
25 | #include <com/sun/star/table/XCell.hpp> | |||
26 | #include <com/sun/star/table/XCellRange.hpp> | |||
27 | #include <com/sun/star/util/DateTime.hpp> | |||
28 | #include <com/sun/star/util/NumberFormat.hpp> | |||
29 | #include <com/sun/star/util/XNumberFormatTypes.hpp> | |||
30 | #include <com/sun/star/util/XNumberFormatsSupplier.hpp> | |||
31 | #include <sal/log.hxx> | |||
32 | #include <osl/diagnose.h> | |||
33 | #include <editeng/boxitem.hxx> | |||
34 | #include <oox/helper/containerhelper.hxx> | |||
35 | #include <oox/helper/propertyset.hxx> | |||
36 | #include <oox/token/properties.hxx> | |||
37 | #include <oox/token/tokens.hxx> | |||
38 | #include <addressconverter.hxx> | |||
39 | #include <formulaparser.hxx> | |||
40 | #include <sharedstringsbuffer.hxx> | |||
41 | #include <unitconverter.hxx> | |||
42 | #include <rangelst.hxx> | |||
43 | #include <document.hxx> | |||
44 | #include <scitems.hxx> | |||
45 | #include <docpool.hxx> | |||
46 | #include <paramisc.hxx> | |||
47 | #include <patattr.hxx> | |||
48 | #include <documentimport.hxx> | |||
49 | #include <formulabuffer.hxx> | |||
50 | #include <numformat.hxx> | |||
51 | #include <sax/tools/converter.hxx> | |||
52 | ||||
53 | namespace oox::xls { | |||
54 | ||||
55 | using namespace ::com::sun::star::lang; | |||
56 | using namespace ::com::sun::star::sheet; | |||
57 | using namespace ::com::sun::star::uno; | |||
58 | using namespace ::com::sun::star::util; | |||
59 | ||||
60 | CellModel::CellModel() : | |||
61 | mnCellType( XML_TOKEN_INVALID ), | |||
62 | mnXfId( -1 ), | |||
63 | mbShowPhonetic( false ) | |||
64 | { | |||
65 | } | |||
66 | ||||
67 | CellFormulaModel::CellFormulaModel() : | |||
68 | mnFormulaType( XML_TOKEN_INVALID ), | |||
69 | mnSharedId( -1 ) | |||
70 | { | |||
71 | } | |||
72 | ||||
73 | bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr ) | |||
74 | { | |||
75 | return (maFormulaRef.aStart == rCellAddr ); | |||
76 | } | |||
77 | ||||
78 | bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr ) | |||
79 | { | |||
80 | return | |||
81 | (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) && | |||
82 | (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) && | |||
83 | (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row()); | |||
84 | } | |||
85 | ||||
86 | DataTableModel::DataTableModel() : | |||
87 | mb2dTable( false ), | |||
88 | mbRowTable( false ), | |||
89 | mbRef1Deleted( false ), | |||
90 | mbRef2Deleted( false ) | |||
91 | { | |||
92 | } | |||
93 | ||||
94 | CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) : | |||
95 | WorksheetHelper( rHelper ), | |||
96 | mnCurrRow( -1 ) | |||
97 | { | |||
98 | } | |||
99 | ||||
100 | void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans ) | |||
101 | { | |||
102 | OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" )do { if (true && (!(maColSpans.count( nRow ) == 0))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl" ), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "102" ": "), "%s", "CellBlockBuffer::setColSpans - multiple column spans for the same row" ); } } while (false); | |||
103 | OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" )do { if (true && (!((mnCurrRow < nRow) && ( maColSpans.empty() || (maColSpans.rbegin()->first < nRow ))))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl" ), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "103" ": "), "%s", "CellBlockBuffer::setColSpans - rows are unsorted" ); } } while (false); | |||
104 | if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) ) | |||
105 | { | |||
106 | maColSpans[ nRow ] = rColSpans.getRanges(); | |||
107 | mnCurrRow = nRow; | |||
108 | } | |||
109 | } | |||
110 | ||||
111 | SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) : | |||
112 | WorksheetHelper( rHelper ), | |||
113 | maCellBlocks( rHelper ), | |||
114 | mbPendingSharedFmla( false ) | |||
115 | { | |||
116 | } | |||
117 | ||||
118 | void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans ) | |||
119 | { | |||
120 | maCellBlocks.setColSpans( nRow, rColSpans ); | |||
121 | } | |||
122 | ||||
123 | void SheetDataBuffer::setBlankCell( const CellModel& rModel ) | |||
124 | { | |||
125 | setCellFormat( rModel ); | |||
126 | } | |||
127 | ||||
128 | void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue ) | |||
129 | { | |||
130 | getDocImport().setNumericCell(rModel.maCellAddr, fValue); | |||
131 | setCellFormat( rModel ); | |||
132 | } | |||
133 | ||||
134 | void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText ) | |||
135 | { | |||
136 | if (!rText.isEmpty()) | |||
137 | getDocImport().setStringCell(rModel.maCellAddr, rText); | |||
138 | ||||
139 | setCellFormat( rModel ); | |||
140 | } | |||
141 | ||||
142 | void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString ) | |||
143 | { | |||
144 | OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" )do { if (true && (!(rxString))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "144" ": "), "%s", "SheetDataBuffer::setStringCell - missing rich string object" ); } } while (false); | |||
145 | const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get(); | |||
146 | OUString aText; | |||
147 | if( rxString->extractPlainString( aText, pFirstPortionFont ) ) | |||
148 | { | |||
149 | setStringCell( rModel, aText ); | |||
150 | } | |||
151 | else | |||
152 | { | |||
153 | putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont ); | |||
154 | setCellFormat( rModel ); | |||
155 | } | |||
156 | } | |||
157 | ||||
158 | void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId ) | |||
159 | { | |||
160 | RichStringRef xString = getSharedStrings().getString( nStringId ); | |||
161 | if( xString ) | |||
162 | setStringCell( rModel, xString ); | |||
163 | else | |||
164 | setBlankCell( rModel ); | |||
165 | } | |||
166 | ||||
167 | void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime ) | |||
168 | { | |||
169 | // write serial date/time value into the cell | |||
170 | double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime ); | |||
171 | setValueCell( rModel, fSerial ); | |||
172 | // set appropriate number format | |||
173 | using namespace ::com::sun::star::util::NumberFormat; | |||
174 | sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE); | |||
175 | // set number format | |||
176 | try | |||
177 | { | |||
178 | Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW ); | |||
179 | Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW ); | |||
180 | sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() ); | |||
181 | PropertySet aPropSet( getCell( rModel.maCellAddr ) ); | |||
182 | aPropSet.setProperty( PROP_NumberFormat, nIndex ); | |||
183 | } | |||
184 | catch( Exception& ) | |||
185 | { | |||
186 | } | |||
187 | } | |||
188 | ||||
189 | void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue ) | |||
190 | { | |||
191 | getFormulaBuffer().setCellFormula( | |||
192 | rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()")); | |||
193 | ||||
194 | // #108770# set 'Standard' number format for all Boolean cells | |||
195 | setCellFormat( rModel ); | |||
196 | } | |||
197 | ||||
198 | void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode ) | |||
199 | { | |||
200 | // Using the formula compiler now we can simply pass on the error string. | |||
201 | getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode); | |||
202 | setCellFormat( rModel ); | |||
203 | } | |||
204 | ||||
205 | void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode ) | |||
206 | { | |||
207 | setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode)); | |||
208 | } | |||
209 | ||||
210 | void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString ) | |||
211 | { | |||
212 | css::util::DateTime aDateTime; | |||
213 | if (!sax::Converter::parseDateTime( aDateTime, rDateString)) | |||
214 | { | |||
215 | SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString)do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "sc.filter")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult ( ::sal::detail::StreamStart() << "SheetDataBuffer::setDateCell - could not parse: " << rDateString) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "215" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "SheetDataBuffer::setDateCell - could not parse: " << rDateString), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "SheetDataBuffer::setDateCell - could not parse: " << rDateString; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "215" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "SheetDataBuffer::setDateCell - could not parse: " << rDateString) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "215" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "SheetDataBuffer::setDateCell - could not parse: " << rDateString), 0); } else { ::std::ostringstream sal_detail_stream ; sal_detail_stream << "SheetDataBuffer::setDateCell - could not parse: " << rDateString; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN ), ("sc.filter"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "215" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
216 | // At least don't lose data. | |||
217 | setStringCell( rModel, rDateString); | |||
218 | return; | |||
219 | } | |||
220 | ||||
221 | double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime); | |||
222 | setValueCell( rModel, fSerial); | |||
223 | } | |||
224 | ||||
225 | void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens) | |||
226 | { | |||
227 | BinAddress aAddr(rAddr); | |||
228 | maSharedFormulas[aAddr] = rTokens; | |||
229 | if( mbPendingSharedFmla ) | |||
230 | setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) ); | |||
231 | } | |||
232 | ||||
233 | void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens ) | |||
234 | { | |||
235 | mbPendingSharedFmla = false; | |||
236 | ApiTokenSequence aTokens; | |||
237 | ||||
238 | /* Detect special token passed as placeholder for array formulas, shared | |||
239 | formulas, and table operations. In BIFF, these formulas are represented | |||
240 | by a single tExp resp. tTbl token. If the formula parser finds these | |||
241 | tokens, it puts a single OPCODE_BAD token with the base address and | |||
242 | formula type into the token sequence. This information will be | |||
243 | extracted here, and in case of a shared formula, the shared formula | |||
244 | buffer will generate the resulting formula token array. */ | |||
245 | ApiSpecialTokenInfo aTokenInfo; | |||
246 | if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) ) | |||
| ||||
247 | { | |||
248 | /* The second member of the token info is set to true, if the formula | |||
249 | represents a table operation, which will be skipped. In BIFF12 it | |||
250 | is not possible to distinguish array and shared formulas | |||
251 | (BIFF5/BIFF8 provide this information with a special flag in the | |||
252 | FORMULA record). */ | |||
253 | if( !aTokenInfo.Second ) | |||
254 | { | |||
255 | /* Construct the token array representing the shared formula. If | |||
256 | the returned sequence is empty, the definition of the shared | |||
257 | formula has not been loaded yet, or the cell is part of an | |||
258 | array formula. In this case, the cell will be remembered. After | |||
259 | reading the formula definition it will be retried to insert the | |||
260 | formula via retryPendingSharedFormulaCell(). */ | |||
261 | ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet ); | |||
262 | aTokens = resolveSharedFormula( aTokenAddr ); | |||
263 | if( !aTokens.hasElements() ) | |||
264 | { | |||
265 | maSharedFmlaAddr = rModel.maCellAddr; | |||
266 | maSharedBaseAddr = aTokenAddr; | |||
267 | mbPendingSharedFmla = true; | |||
268 | } | |||
269 | } | |||
270 | } | |||
271 | else | |||
272 | { | |||
273 | // simple formula, use the passed token array | |||
274 | aTokens = rTokens; | |||
275 | } | |||
276 | ||||
277 | setCellFormula( rModel.maCellAddr, aTokens ); | |||
278 | setCellFormat( rModel ); | |||
279 | } | |||
280 | ||||
281 | void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) | |||
282 | { | |||
283 | /* Array formulas will be inserted later in finalizeImport(). This is | |||
284 | needed to not disturb collecting all the cells, which will be put into | |||
285 | the sheet in large blocks to increase performance. */ | |||
286 | maArrayFormulas.emplace_back( rRange, rTokens ); | |||
287 | } | |||
288 | ||||
289 | void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel ) | |||
290 | { | |||
291 | /* Table operations will be inserted later in finalizeImport(). This is | |||
292 | needed to not disturb collecting all the cells, which will be put into | |||
293 | the sheet in large blocks to increase performance. */ | |||
294 | maTableOperations.emplace_back( rRange, rModel ); | |||
295 | } | |||
296 | ||||
297 | void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat ) | |||
298 | { | |||
299 | // set row formatting | |||
300 | if( bCustomFormat ) | |||
301 | { | |||
302 | // try to expand cached row range, if formatting is equal | |||
303 | if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) ) | |||
304 | { | |||
305 | ||||
306 | maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange ); | |||
307 | maXfIdRowRange.set( nRow, nXfId ); | |||
308 | } | |||
309 | } | |||
310 | else if( maXfIdRowRange.maRowRange.mnLast >= 0 ) | |||
311 | { | |||
312 | // finish last cached row range | |||
313 | maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange ); | |||
314 | maXfIdRowRange.set( -1, -1 ); | |||
315 | } | |||
316 | } | |||
317 | ||||
318 | void SheetDataBuffer::setMergedRange( const ScRange& rRange ) | |||
319 | { | |||
320 | maMergedRanges.emplace_back( rRange ); | |||
321 | } | |||
322 | ||||
323 | typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair; | |||
324 | ||||
325 | static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList ) | |||
326 | { | |||
327 | Xf* pXf1 = rStyles.getCellXf( nXfId ).get(); | |||
328 | if ( !pXf1 ) | |||
329 | return; | |||
330 | ||||
331 | auto it = std::find_if(rMap.begin(), rMap.end(), | |||
332 | [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) { | |||
333 | if (rEntry.first.second != nFormatId) | |||
334 | return false; | |||
335 | Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get(); | |||
336 | return *pXf1 == *pXf2; | |||
337 | }); | |||
338 | if (it != rMap.end()) // already exists | |||
339 | { | |||
340 | // add ranges from the rangelist to the existing rangelist for the | |||
341 | // matching style ( should we check if they overlap ? ) | |||
342 | for (size_t i = 0, nSize = rRangeList.size(); i < nSize; ++i) | |||
343 | it->second.push_back(rRangeList[i]); | |||
344 | return; | |||
345 | } | |||
346 | rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList; | |||
347 | } | |||
348 | ||||
349 | void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange& rAddress, bool bProcessRowRange ) | |||
350 | { | |||
351 | RowRangeStyle aStyleRows; | |||
352 | aStyleRows.mnNumFmt.first = nXfId; | |||
353 | aStyleRows.mnNumFmt.second = nFormatId; | |||
354 | aStyleRows.mnStartRow = rAddress.aStart.Row(); | |||
355 | aStyleRows.mnEndRow = rAddress.aEnd.Row(); | |||
356 | for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol ) | |||
357 | { | |||
358 | if ( !bProcessRowRange ) | |||
359 | maStylesPerColumn[ nCol ].insert( aStyleRows ); | |||
360 | else | |||
361 | { | |||
362 | RowStyles& rRowStyles = maStylesPerColumn[ nCol ]; | |||
363 | // Reset row range for each column | |||
364 | aStyleRows.mnStartRow = rAddress.aStart.Row(); | |||
365 | aStyleRows.mnEndRow = rAddress.aEnd.Row(); | |||
366 | ||||
367 | // If aStyleRows includes rows already allocated to a style | |||
368 | // in rRowStyles, then we need to split it into parts. | |||
369 | // ( to occupy only rows that have no style definition) | |||
370 | ||||
371 | // Start iterating at the first element that is not completely before aStyleRows | |||
372 | RowStyles::iterator rows_it = rRowStyles.lower_bound(aStyleRows); | |||
373 | RowStyles::iterator rows_end = rRowStyles.end(); | |||
374 | bool bAddRange = true; | |||
375 | for ( ; rows_it != rows_end; ++rows_it ) | |||
376 | { | |||
377 | const RowRangeStyle& r = *rows_it; | |||
378 | ||||
379 | // Add the part of aStyleRows that does not overlap with r | |||
380 | if ( aStyleRows.mnStartRow < r.mnStartRow ) | |||
381 | { | |||
382 | RowRangeStyle aSplit = aStyleRows; | |||
383 | aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, r.mnStartRow - 1); | |||
384 | // Insert with hint that aSplit comes directly before the current position | |||
385 | rRowStyles.insert( rows_it, aSplit ); | |||
386 | } | |||
387 | ||||
388 | // Done if no part of aStyleRows extends beyond r | |||
389 | if ( aStyleRows.mnEndRow <= r.mnEndRow ) | |||
390 | { | |||
391 | bAddRange = false; | |||
392 | break; | |||
393 | } | |||
394 | ||||
395 | // Cut off the part aStyleRows that was handled above | |||
396 | aStyleRows.mnStartRow = r.mnEndRow + 1; | |||
397 | } | |||
398 | if ( bAddRange ) | |||
399 | rRowStyles.insert( aStyleRows ); | |||
400 | } | |||
401 | } | |||
402 | } | |||
403 | ||||
404 | void SheetDataBuffer::finalizeImport() | |||
405 | { | |||
406 | // create all array formulas | |||
407 | for( const auto& [rRange, rTokens] : maArrayFormulas ) | |||
408 | finalizeArrayFormula( rRange, rTokens ); | |||
409 | ||||
410 | // create all table operations | |||
411 | for( const auto& [rRange, rModel] : maTableOperations ) | |||
412 | finalizeTableOperation( rRange, rModel ); | |||
413 | ||||
414 | // write default formatting of remaining row range | |||
415 | maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange ); | |||
416 | ||||
417 | std::map< FormatKeyPair, ScRangeList > rangeStyleListMap; | |||
418 | for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists ) | |||
419 | { | |||
420 | addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList ); | |||
421 | } | |||
422 | // gather all ranges that have the same style and apply them in bulk | |||
423 | for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap ) | |||
424 | { | |||
425 | for (size_t i = 0, nSize = rRanges.size(); i < nSize; ++i) | |||
426 | addColXfStyle( rFormatKeyPair.first, rFormatKeyPair.second, rRanges[i]); | |||
427 | } | |||
428 | ||||
429 | for ( const auto& [rXfId, rRowRangeList] : maXfIdRowRangeList ) | |||
430 | { | |||
431 | if ( rXfId == -1 ) // it's a dud skip it | |||
432 | continue; | |||
433 | AddressConverter& rAddrConv = getAddressConverter(); | |||
434 | // get all row ranges for id | |||
435 | for ( const auto& rRange : rRowRangeList ) | |||
436 | { | |||
437 | ScRange aRange( 0, rRange.mnFirst, getSheetIndex(), | |||
438 | rAddrConv.getMaxApiAddress().Col(), rRange.mnLast, getSheetIndex() ); | |||
439 | ||||
440 | addColXfStyle( rXfId, -1, aRange, true ); | |||
441 | } | |||
442 | } | |||
443 | ||||
444 | ScDocumentImport& rDocImport = getDocImport(); | |||
445 | ScDocument& rDoc = rDocImport.getDoc(); | |||
446 | StylesBuffer& rStyles = getStyles(); | |||
447 | for ( const auto& [rCol, rRowStyles] : maStylesPerColumn ) | |||
448 | { | |||
449 | SCCOL nScCol = static_cast< SCCOL >( rCol ); | |||
450 | ||||
451 | // tdf#91567 Get pattern from the first row without AutoFilter | |||
452 | const ScPatternAttr* pDefPattern = nullptr; | |||
453 | bool bAutoFilter = true; | |||
454 | SCROW nScRow = 0; | |||
455 | while ( bAutoFilter && nScRow < rDoc.MaxRow() ) | |||
456 | { | |||
457 | pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() ); | |||
458 | if ( pDefPattern ) | |||
459 | { | |||
460 | const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG ); | |||
461 | bAutoFilter = pAttr->HasAutoFilter(); | |||
462 | } | |||
463 | else | |||
464 | break; | |||
465 | nScRow++; | |||
466 | } | |||
467 | if ( !pDefPattern || nScRow == rDoc.MaxRow() ) | |||
468 | pDefPattern = rDoc.GetDefPattern(); | |||
469 | ||||
470 | Xf::AttrList aAttrs(pDefPattern); | |||
471 | for ( const auto& rRowStyle : rRowStyles ) | |||
472 | { | |||
473 | Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get(); | |||
474 | ||||
475 | if ( pXf ) | |||
476 | pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second ); | |||
477 | } | |||
478 | if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow()) | |||
479 | { | |||
480 | ScAttrEntry aEntry; | |||
481 | aEntry.nEndRow = rDoc.MaxRow(); | |||
482 | aEntry.pPattern = pDefPattern; | |||
483 | rDoc.GetPool()->Put(*aEntry.pPattern); | |||
484 | aAttrs.maAttrs.push_back(aEntry); | |||
485 | ||||
486 | if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc)) | |||
487 | aAttrs.mbLatinNumFmtOnly = false; | |||
488 | } | |||
489 | ||||
490 | ScDocumentImport::Attrs aAttrParam; | |||
491 | aAttrParam.mvData.swap(aAttrs.maAttrs); | |||
492 | aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly; | |||
493 | ||||
494 | rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam)); | |||
495 | } | |||
496 | ||||
497 | // merge all cached merged ranges and update right/bottom cell borders | |||
498 | for( const auto& rMergedRange : maMergedRanges ) | |||
499 | applyCellMerging( rMergedRange.maRange ); | |||
500 | for( const auto& rCenterFillRange : maCenterFillRanges ) | |||
501 | applyCellMerging( rCenterFillRange.maRange ); | |||
502 | } | |||
503 | ||||
504 | // private -------------------------------------------------------------------- | |||
505 | ||||
506 | SheetDataBuffer::XfIdRowRange::XfIdRowRange() : | |||
507 | maRowRange( -1 ), | |||
508 | mnXfId( -1 ) | |||
509 | { | |||
510 | } | |||
511 | ||||
512 | void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId ) | |||
513 | { | |||
514 | maRowRange = ValueRange( nRow ); | |||
515 | mnXfId = nXfId; | |||
516 | } | |||
517 | ||||
518 | bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId ) | |||
519 | { | |||
520 | if( mnXfId == nXfId ) | |||
521 | { | |||
522 | if( maRowRange.mnLast + 1 == nRow ) | |||
523 | { | |||
524 | ++maRowRange.mnLast; | |||
525 | return true; | |||
526 | } | |||
527 | if( maRowRange.mnFirst == nRow + 1 ) | |||
528 | { | |||
529 | --maRowRange.mnFirst; | |||
530 | return true; | |||
531 | } | |||
532 | } | |||
533 | return false; | |||
534 | } | |||
535 | ||||
536 | SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) : | |||
537 | maRange( rRange ), | |||
538 | mnHorAlign( XML_TOKEN_INVALID ) | |||
539 | { | |||
540 | } | |||
541 | ||||
542 | SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) : | |||
543 | maRange( rAddress, rAddress ), | |||
544 | mnHorAlign( nHorAlign ) | |||
545 | { | |||
546 | } | |||
547 | ||||
548 | bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign ) | |||
549 | { | |||
550 | if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) && | |||
551 | (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) ) | |||
552 | { | |||
553 | maRange.aEnd.IncCol(); | |||
554 | return true; | |||
555 | } | |||
556 | return false; | |||
557 | } | |||
558 | ||||
559 | void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens ) | |||
560 | { | |||
561 | if( rTokens.hasElements() ) | |||
562 | { | |||
563 | putFormulaTokens( rCellAddr, rTokens ); | |||
564 | } | |||
565 | } | |||
566 | ||||
567 | ||||
568 | ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const | |||
569 | { | |||
570 | BinAddress aAddr(rAddr); | |||
571 | ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() ); | |||
572 | return aTokens; | |||
573 | } | |||
574 | ||||
575 | void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const | |||
576 | { | |||
577 | Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY ); | |||
578 | OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" )do { if (true && (!(xTokens.is()))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/filter/oox/sheetdatabuffer.cxx" ":" "578" ": "), "%s", "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" ); } } while (false); | |||
579 | if( xTokens.is() ) | |||
580 | xTokens->setArrayTokens( rTokens ); | |||
581 | } | |||
582 | ||||
583 | void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel ) | |||
584 | { | |||
585 | if (rModel.mbRef1Deleted) | |||
586 | return; | |||
587 | ||||
588 | if (rModel.maRef1.isEmpty()) | |||
589 | return; | |||
590 | ||||
591 | if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0) | |||
592 | return; | |||
593 | ||||
594 | sal_Int16 nSheet = getSheetIndex(); | |||
595 | ||||
596 | ScAddress aRef1( 0, 0, 0 ); | |||
597 | if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true)) | |||
598 | return; | |||
599 | ||||
600 | ScDocumentImport& rDoc = getDocImport(); | |||
601 | ScTabOpParam aParam; | |||
602 | ||||
603 | ScRange aScRange(rRange); | |||
604 | ||||
605 | if (rModel.mb2dTable) | |||
606 | { | |||
607 | // Two-variable data table. | |||
608 | if (rModel.mbRef2Deleted) | |||
609 | return; | |||
610 | ||||
611 | if (rModel.maRef2.isEmpty()) | |||
612 | return; | |||
613 | ||||
614 | ScAddress aRef2( 0, 0, 0 ); | |||
615 | if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true)) | |||
616 | return; | |||
617 | ||||
618 | aParam.meMode = ScTabOpParam::Both; | |||
619 | ||||
620 | aScRange.aStart.IncCol(-1); | |||
621 | aScRange.aStart.IncRow(-1); | |||
622 | ||||
623 | aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false); | |||
624 | aParam.aRefFormulaEnd = aParam.aRefFormulaCell; | |||
625 | ||||
626 | // Ref1 is row input cell and Ref2 is column input cell. | |||
627 | aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false); | |||
628 | aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false); | |||
629 | rDoc.setTableOpCells(aScRange, aParam); | |||
630 | ||||
631 | return; | |||
632 | } | |||
633 | ||||
634 | // One-variable data table. | |||
635 | ||||
636 | if (rModel.mbRowTable) | |||
637 | { | |||
638 | // One-variable row input cell (horizontal). | |||
639 | aParam.meMode = ScTabOpParam::Row; | |||
640 | aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false); | |||
641 | aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false); | |||
642 | aParam.aRefFormulaEnd = aParam.aRefFormulaCell; | |||
643 | aScRange.aStart.IncRow(-1); | |||
644 | rDoc.setTableOpCells(aScRange, aParam); | |||
645 | } | |||
646 | else | |||
647 | { | |||
648 | // One-variable column input cell (vertical). | |||
649 | aParam.meMode = ScTabOpParam::Column; | |||
650 | aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false); | |||
651 | aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false); | |||
652 | aParam.aRefFormulaEnd = aParam.aRefFormulaCell; | |||
653 | aScRange.aStart.IncCol(-1); | |||
654 | rDoc.setTableOpCells(aScRange, aParam); | |||
655 | } | |||
656 | } | |||
657 | ||||
658 | void SheetDataBuffer::setCellFormat( const CellModel& rModel ) | |||
659 | { | |||
660 | if( rModel.mnXfId < 0 ) | |||
661 | return; | |||
662 | ||||
663 | ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ]; | |||
664 | ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back(); | |||
665 | /* The xlsx sheet data contains row wise information. | |||
666 | * It is sufficient to check if the row range size is one | |||
667 | */ | |||
668 | if (!rRangeList.empty() && | |||
669 | *pLastRange == rModel.maCellAddr) | |||
670 | ; // do nothing - this probably bad data | |||
671 | else if (!rRangeList.empty() && | |||
672 | pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() && | |||
| ||||
673 | pLastRange->aStart.Row() == pLastRange->aEnd.Row() && | |||
674 | pLastRange->aStart.Row() == rModel.maCellAddr.Row() && | |||
675 | pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col()) | |||
676 | { | |||
677 | pLastRange->aEnd.IncCol(); // Expand Column | |||
678 | } | |||
679 | else | |||
680 | { | |||
681 | rRangeList.push_back(ScRange(rModel.maCellAddr)); | |||
682 | pLastRange = &rRangeList.back(); | |||
683 | } | |||
684 | ||||
685 | if (rRangeList.size() > 1) | |||
686 | { | |||
687 | for (size_t i = rRangeList.size() - 1; i != 0; --i) | |||
688 | { | |||
689 | ScRange& rMergeRange = rRangeList[i - 1]; | |||
690 | if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab()) | |||
691 | break; | |||
692 | ||||
693 | /* Try to merge this with the previous range */ | |||
694 | if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) && | |||
695 | pLastRange->aStart.Col() == rMergeRange.aStart.Col() && | |||
696 | pLastRange->aEnd.Col() == rMergeRange.aEnd.Col()) | |||
697 | { | |||
698 | rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row()); | |||
699 | rRangeList.Remove(rRangeList.size() - 1); | |||
700 | break; | |||
701 | } | |||
702 | else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1)) | |||
703 | break; // Un-necessary to check with any other rows | |||
704 | } | |||
705 | } | |||
706 | // update merged ranges for 'center across selection' and 'fill' | |||
707 | const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get(); | |||
708 | if( !pXf ) | |||
709 | return; | |||
710 | ||||
711 | sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign; | |||
712 | if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) ) | |||
713 | { | |||
714 | /* start new merged range, if cell is not empty (#108781#), | |||
715 | or try to expand last range with empty cell */ | |||
716 | if( rModel.mnCellType != XML_TOKEN_INVALID ) | |||
717 | maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign ); | |||
718 | else if( !maCenterFillRanges.empty() ) | |||
719 | maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign ); | |||
720 | } | |||
721 | } | |||
722 | ||||
723 | static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine ) | |||
724 | { | |||
725 | SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col(); | |||
726 | SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row(); | |||
727 | ||||
728 | const SvxBoxItem* pFromItem = | |||
729 | rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER ); | |||
730 | const SvxBoxItem* pToItem = | |||
731 | rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER ); | |||
732 | ||||
733 | SvxBoxItem aNewItem( *pToItem ); | |||
734 | aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine ); | |||
735 | rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem ); | |||
736 | } | |||
737 | ||||
738 | void SheetDataBuffer::applyCellMerging( const ScRange& rRange ) | |||
739 | { | |||
740 | bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col(); | |||
741 | bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row(); | |||
742 | ||||
743 | const ScAddress& rStart = rRange.aStart; | |||
744 | const ScAddress& rEnd = rRange.aEnd; | |||
745 | ScDocument& rDoc = getScDocument(); | |||
746 | // set correct right border | |||
747 | if( bMultiCol ) | |||
748 | lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT ); | |||
749 | // set correct lower border | |||
750 | if( bMultiRow ) | |||
751 | lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM ); | |||
752 | // do merge | |||
753 | if( bMultiCol || bMultiRow ) | |||
754 | rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() ); | |||
755 | } | |||
756 | ||||
757 | } // namespace oox | |||
758 | ||||
759 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |