Bug Summary

File:home/maarten/src/libreoffice/core/sc/inc/attarray.hxx
Warning:line 147, column 118
Potential leak of memory pointed to by 'pPattern._M_t._M_t._M_head_impl'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name attarray.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SC_DLLIMPLEMENTATION -D SC_INFO_OSVERSION="LINUX" -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/clew/source/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sc/source/core/inc -I /home/maarten/src/libreoffice/core/sc/source/filter/inc -I /home/maarten/src/libreoffice/core/sc/source/ui/inc -I /home/maarten/src/libreoffice/core/sc/inc -I /home/maarten/src/libreoffice/core/workdir/SdiTarget/sc/sdi -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx

/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx

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 <attarray.hxx>
21#include <scitems.hxx>
22#include <editeng/borderline.hxx>
23#include <editeng/boxitem.hxx>
24#include <editeng/lineitem.hxx>
25#include <editeng/shaditem.hxx>
26#include <editeng/editobj.hxx>
27#include <editeng/justifyitem.hxx>
28#include <osl/diagnose.h>
29#include <svl/poolcach.hxx>
30#include <sfx2/objsh.hxx>
31
32#include <global.hxx>
33#include <document.hxx>
34#include <docpool.hxx>
35#include <patattr.hxx>
36#include <stlsheet.hxx>
37#include <stlpool.hxx>
38#include <markarr.hxx>
39#include <globstr.hrc>
40#include <scresid.hxx>
41#include <segmenttree.hxx>
42#include <editdataarray.hxx>
43#include <cellvalue.hxx>
44#include <editutil.hxx>
45#include <mtvelements.hxx>
46#include <memory>
47
48using ::editeng::SvxBorderLine;
49
50ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, ScAttrArray* pDefaultColAttrArray ) :
51 nCol( nNewCol ),
52 nTab( nNewTab ),
53 rDocument( rDoc )
54{
55 if ( nCol == -1 || !pDefaultColAttrArray || pDefaultColAttrArray->mvData.empty() )
56 return;
57
58 ScAddress aAdrStart( nCol, 0, nTab );
59 ScAddress aAdrEnd( nCol, 0, nTab );
60 mvData.resize( pDefaultColAttrArray->mvData.size() );
61 for ( size_t nIdx = 0; nIdx < pDefaultColAttrArray->mvData.size(); ++nIdx )
62 {
63 mvData[nIdx].nEndRow = pDefaultColAttrArray->mvData[nIdx].nEndRow;
64 ScPatternAttr aNewPattern( *(pDefaultColAttrArray->mvData[nIdx].pPattern) );
65 mvData[nIdx].pPattern = &rDocument.GetPool()->Put( aNewPattern );
66 bool bNumFormatChanged = false;
67 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
68 mvData[nIdx].pPattern->GetItemSet(), rDocument.GetDefPattern()->GetItemSet() ) )
69 {
70 aAdrStart.SetRow( nIdx ? mvData[nIdx-1].nEndRow+1 : 0 );
71 aAdrEnd.SetRow( mvData[nIdx].nEndRow );
72 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
73 }
74 }
75}
76
77ScAttrArray::~ScAttrArray()
78{
79#if DEBUG_SC_TESTATTRARRAY0
80 TestData();
81#endif
82
83 ScDocumentPool* pDocPool = rDocument.GetPool();
84 for (auto const & rEntry : mvData)
85 pDocPool->Remove(*rEntry.pPattern);
86}
87
88#if DEBUG_SC_TESTATTRARRAY0
89void ScAttrArray::TestData() const
90{
91
92 sal_uInt16 nErr = 0;
93 SCSIZE nPos;
94 for (nPos=0; nPos<nCount; nPos++)
95 {
96 if (nPos > 0)
97 if (mvData[nPos].pPattern == mvData[nPos-1].pPattern || mvData[nPos].nRow <= mvData[nPos-1].nRow)
98 ++nErr;
99 if (mvData[nPos].pPattern->Which() != ATTR_PATTERN)
100 ++nErr;
101 }
102 if ( nPos && mvData[nPos-1].nRow != rDocument.MaxRow() )
103 ++nErr;
104
105 SAL_WARN_IF( nErr, "sc", nErr << " errors in attribute array, column " << nCol )do { if (true && (nErr)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_WARN, "sc")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << nErr <<
" errors in attribute array, column " << nCol) == 1) {
::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "105" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << nErr << " errors in attribute array, column "
<< nCol), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << nErr << " errors in attribute array, column "
<< nCol; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "105" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << nErr << " errors in attribute array, column "
<< nCol) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "105" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << nErr << " errors in attribute array, column "
<< nCol), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << nErr << " errors in attribute array, column "
<< nCol; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "105" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
106}
107#endif
108
109void ScAttrArray::SetDefaultIfNotInit( SCSIZE nNeeded )
110{
111 if ( !mvData.empty() )
112 return;
113
114 SCSIZE nNewLimit = std::max<SCSIZE>( SC_ATTRARRAY_DELTA4, nNeeded );
115 mvData.reserve( nNewLimit );
116 mvData.emplace_back();
117 mvData[0].nEndRow = rDocument.MaxRow();
118 mvData[0].pPattern = rDocument.GetDefPattern(); // no put
119}
120
121void ScAttrArray::Reset( const ScPatternAttr* pPattern )
122{
123 ScDocumentPool* pDocPool = rDocument.GetPool();
124 ScAddress aAdrStart( nCol, 0, nTab );
125 ScAddress aAdrEnd ( nCol, 0, nTab );
126
127 for (SCSIZE i=0; i<mvData.size(); i++)
128 {
129 // ensure that attributing changes text width of cell
130 const ScPatternAttr* pOldPattern = mvData[i].pPattern;
131 if ( nCol != -1 )
132 {
133 bool bNumFormatChanged;
134 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
135 pPattern->GetItemSet(), pOldPattern->GetItemSet() ) )
136 {
137 aAdrStart.SetRow( i ? mvData[i-1].nEndRow+1 : 0 );
138 aAdrEnd .SetRow( mvData[i].nEndRow );
139 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
140 }
141 }
142 pDocPool->Remove(*pOldPattern);
143 }
144 mvData.resize(0);
145
146 rDocument.SetStreamValid(nTab, false);
147
148 mvData.resize(1);
149 const ScPatternAttr* pNewPattern = &pDocPool->Put(*pPattern);
150 mvData[0].nEndRow = rDocument.MaxRow();
151 mvData[0].pPattern = pNewPattern;
152}
153
154bool ScAttrArray::Concat(SCSIZE nPos)
155{
156 bool bRet = false;
157 if (nPos < mvData.size())
158 {
159 if (nPos > 0)
160 {
161 if (mvData[nPos - 1].pPattern == mvData[nPos].pPattern)
162 {
163 mvData[nPos - 1].nEndRow = mvData[nPos].nEndRow;
164 rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
165 mvData.erase(mvData.begin() + nPos);
166 nPos--;
167 bRet = true;
168 }
169 }
170 if (nPos + 1 < mvData.size())
171 {
172 if (mvData[nPos + 1].pPattern == mvData[nPos].pPattern)
173 {
174 mvData[nPos].nEndRow = mvData[nPos + 1].nEndRow;
175 rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
176 mvData.erase(mvData.begin() + nPos + 1);
177 bRet = true;
178 }
179 }
180 }
181 return bRet;
182}
183
184/*
185 * nCount is the number of runs of different attribute combinations;
186 * no attribute in a column => nCount==1, one attribute somewhere => nCount == 3
187 * (ie. one run with no attribute + one attribute + another run with no attribute)
188 * so a range of identical attributes is only one entry in ScAttrArray.
189 *
190 * Iterative implementation of Binary Search
191 * The same implementation was used inside ScMarkArray::Search().
192 */
193
194bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex ) const
195{
196/* auto it = std::lower_bound(mvData.begin(), mvData.end(), nRow,
197 [] (const ScAttrEntry &r1, SCROW nRow)
198 { return r1.nEndRow < nRow; } );
199 if (it != mvData.end())
200 nIndex = it - mvData.begin();
201 return it != mvData.end(); */
202
203 if (mvData.size() == 1)
204 {
205 nIndex = 0;
206 return true;
207 }
208
209 long nHi = static_cast<long>(mvData.size()) - 1;
210 long i = 0;
211 long nLo = 0;
212
213 while ( nLo <= nHi )
214 {
215 i = (nLo + nHi) / 2;
216
217 if (mvData[i].nEndRow < nRow)
218 {
219 // If [nRow] greater, ignore left half
220 nLo = i + 1;
221 }
222 else if ((i > 0) && (mvData[i - 1].nEndRow >= nRow))
223 {
224 // If [nRow] is smaller, ignore right half
225 nHi = i - 1;
226 }
227 else
228 {
229 // found
230 nIndex=static_cast<SCSIZE>(i);
231 return true;
232 }
233 }
234
235 nIndex=0;
236 return false;
237}
238
239const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const
240{
241 if ( mvData.empty() )
242 {
243 if ( !rDocument.ValidRow(nRow) )
244 return nullptr;
245 return rDocument.GetDefPattern();
246 }
247 SCSIZE i;
248 if (Search( nRow, i ))
249 return mvData[i].pPattern;
250 else
251 return nullptr;
252}
253
254const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow,
255 SCROW& rEndRow, SCROW nRow ) const
256{
257 if ( mvData.empty() )
258 {
259 if ( !rDocument.ValidRow( nRow ) )
260 return nullptr;
261 rStartRow = 0;
262 rEndRow = rDocument.MaxRow();
263 return rDocument.GetDefPattern();
264 }
265 SCSIZE nIndex;
266 if ( Search( nRow, nIndex ) )
267 {
268 if ( nIndex > 0 )
269 rStartRow = mvData[nIndex-1].nEndRow + 1;
270 else
271 rStartRow = 0;
272 rEndRow = mvData[nIndex].nEndRow;
273 return mvData[nIndex].pPattern;
274 }
275 return nullptr;
276}
277
278void ScAttrArray::AddCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
279{
280 if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
281 return;
282
283 if(nEndRow < nStartRow)
284 return;
285
286 SCROW nTempStartRow = nStartRow;
287 SCROW nTempEndRow = nEndRow;
288
289 do
290 {
291 const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
292
293 std::unique_ptr<ScPatternAttr> pNewPattern;
294 if(pPattern)
295 {
296 pNewPattern.reset( new ScPatternAttr(*pPattern) );
297 SCROW nPatternStartRow;
298 SCROW nPatternEndRow;
299 GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
300
301 nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
302 const SfxPoolItem* pItem = nullptr;
303 pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pItem );
304 if(pItem)
305 {
306 ScCondFormatIndexes const & rCondFormatData = static_cast<const ScCondFormatItem*>(pItem)->GetCondFormatData();
307 if (rCondFormatData.find(nIndex) == rCondFormatData.end())
308 {
309 ScCondFormatIndexes aNewCondFormatData;
310 aNewCondFormatData.reserve(rCondFormatData.size()+1);
311 aNewCondFormatData = rCondFormatData;
312 aNewCondFormatData.insert(nIndex);
313 ScCondFormatItem aItem( std::move(aNewCondFormatData) );
314 pNewPattern->GetItemSet().Put( aItem );
315 }
316 }
317 else
318 {
319 ScCondFormatItem aItem(nIndex);
320 pNewPattern->GetItemSet().Put( aItem );
321 }
322 }
323 else
324 {
325 pNewPattern.reset( new ScPatternAttr( rDocument.GetPool() ) );
326 ScCondFormatItem aItem(nIndex);
327 pNewPattern->GetItemSet().Put( aItem );
328 nTempEndRow = nEndRow;
329 }
330
331 SetPatternArea( nTempStartRow, nTempEndRow, std::move(pNewPattern), true );
332 nTempStartRow = nTempEndRow + 1;
333 }
334 while(nTempEndRow < nEndRow);
335
336}
337
338void ScAttrArray::RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
339{
340 if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
341 return;
342
343 if(nEndRow < nStartRow)
344 return;
345
346 SCROW nTempStartRow = nStartRow;
347 SCROW nTempEndRow = nEndRow;
348
349 do
350 {
351 const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
352
353 if(pPattern)
354 {
355 SCROW nPatternStartRow;
356 SCROW nPatternEndRow;
357 GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
358
359 nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
360 const SfxPoolItem* pItem = nullptr;
361 pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pItem );
362 if(pItem)
363 {
364 auto pPatternAttr = std::make_unique<ScPatternAttr>( *pPattern );
365 if (nIndex == 0)
366 {
367 ScCondFormatItem aItem;
368 pPatternAttr->GetItemSet().Put( aItem );
369 SetPatternArea( nTempStartRow, nTempEndRow, std::move(pPatternAttr), true );
370 }
371 else
372 {
373 ScCondFormatIndexes const & rCondFormatData = static_cast<const ScCondFormatItem*>(pItem)->GetCondFormatData();
374 auto itr = rCondFormatData.find(nIndex);
375 if(itr != rCondFormatData.end())
376 {
377 ScCondFormatIndexes aNewCondFormatData(rCondFormatData);
378 aNewCondFormatData.erase(nIndex);
379 ScCondFormatItem aItem( std::move(aNewCondFormatData) );
380 pPatternAttr->GetItemSet().Put( aItem );
381 SetPatternArea( nTempStartRow, nTempEndRow, std::move(pPatternAttr), true );
382 }
383 }
384 }
385 }
386 else
387 {
388 return;
389 }
390
391 nTempStartRow = nTempEndRow + 1;
392 }
393 while(nTempEndRow < nEndRow);
394
395}
396
397void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow,
398 const ScPatternAttr* pPattern, ScEditDataArray* pDataArray )
399{
400 assert( nCol != -1 )(static_cast <bool> (nCol != -1) ? void (0) : __assert_fail
("nCol != -1", "/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
, 400, __extension__ __PRETTY_FUNCTION__))
;
401 // cache mdds position, this doesn't modify the mdds container, just EditTextObject's
402 sc::ColumnBlockPosition blockPos;
403 rDocument.InitColumnBlockPosition( blockPos, nTab, nCol );
404 for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
405 {
406 ScAddress aPos(nCol, nRow, nTab);
407 ScRefCellValue aCell(rDocument, aPos, blockPos);
408 if (aCell.meType != CELLTYPE_EDIT || !aCell.mpEditText)
409 continue;
410
411 std::unique_ptr<EditTextObject> pOldData;
412 if (pDataArray)
413 pOldData = aCell.mpEditText->Clone();
414
415 // Direct modification of cell content - something to watch out for if
416 // we decide to share edit text instances in the future.
417 ScEditUtil::RemoveCharAttribs(const_cast<EditTextObject&>(*aCell.mpEditText), *pPattern);
418
419 if (pDataArray)
420 {
421 std::unique_ptr<EditTextObject> pNewData = aCell.mpEditText->Clone();
422 pDataArray->AddItem(nTab, nCol, nRow, std::move(pOldData), std::move(pNewData));
423 }
424 }
425}
426
427bool ScAttrArray::Reserve( SCSIZE nReserve )
428{
429 if ( mvData.empty() && nReserve )
430 {
431 try {
432 mvData.reserve(nReserve);
433 mvData.emplace_back();
434 mvData[0].nEndRow = rDocument.MaxRow();
435 mvData[0].pPattern = rDocument.GetDefPattern(); // no put
436 return true;
437 } catch (std::bad_alloc const &) {
438 return false;
439 }
440 }
441 else if ( mvData.capacity() < nReserve )
442 {
443 try {
444 mvData.reserve(nReserve);
445 return true;
446 } catch (std::bad_alloc const &) {
447 return false;
448 }
449 }
450 else
451 return false;
452}
453
454const ScPatternAttr* ScAttrArray::SetPatternAreaImpl(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr* pPattern,
455 bool bPutToPool, ScEditDataArray* pDataArray, bool bPassingOwnership )
456{
457 if (rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow))
458 {
459 if (bPutToPool)
460 {
461 if (bPassingOwnership)
462 pPattern = &rDocument.GetPool()->Put(std::unique_ptr<ScPatternAttr>(const_cast<ScPatternAttr*>(pPattern)));
463 else
464 pPattern = &rDocument.GetPool()->Put(*pPattern);
465 }
466 if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
467 Reset(pPattern);
468 else
469 {
470 SCSIZE nNeeded = mvData.size() + 2;
471 SetDefaultIfNotInit( nNeeded );
472
473 ScAddress aAdrStart( nCol, 0, nTab );
474 ScAddress aAdrEnd ( nCol, 0, nTab );
475
476 SCSIZE ni = 0; // number of entries in beginning
477 SCSIZE nx = 0; // track position
478 SCROW ns = 0; // start row of track position
479 if ( nStartRow > 0 )
480 {
481 // skip beginning
482 SCSIZE nIndex;
483 Search( nStartRow, nIndex );
484 ni = nIndex;
485
486 if ( ni > 0 )
487 {
488 nx = ni;
489 ns = mvData[ni-1].nEndRow+1;
490 }
491 }
492
493 // ensure that attributing changes text width of cell
494 // otherwise, conditional formats need to be reset or deleted
495 bool bIsLoading = !rDocument.GetDocumentShell() || rDocument.GetDocumentShell()->IsLoading();
496 while ( ns <= nEndRow )
497 {
498 if ( nCol != -1 && !bIsLoading )
499 {
500 const SfxItemSet& rNewSet = pPattern->GetItemSet();
501 const SfxItemSet& rOldSet = mvData[nx].pPattern->GetItemSet();
502 bool bNumFormatChanged;
503 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
504 rNewSet, rOldSet ) )
505 {
506 aAdrStart.SetRow( std::max(nStartRow,ns) );
507 aAdrEnd .SetRow( std::min(nEndRow,mvData[nx].nEndRow) );
508 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
509 }
510 }
511 ns = mvData[nx].nEndRow + 1;
512 nx++;
513 }
514
515 // continue modifying data array
516
517 SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert)
518 bool bCombined = false;
519 bool bSplit = false;
520 if ( nStartRow > 0 )
521 {
522 nInsert = rDocument.MaxRow() + 1;
523 if ( mvData[ni].pPattern != pPattern )
524 {
525 if ( ni == 0 || (mvData[ni-1].nEndRow < nStartRow - 1) )
526 { // may be a split or a simple insert or just a shrink,
527 // row adjustment is done further down
528 if ( mvData[ni].nEndRow > nEndRow )
529 bSplit = true;
530 ni++;
531 nInsert = ni;
532 }
533 else if (mvData[ni - 1].nEndRow == nStartRow - 1)
534 nInsert = ni;
535 }
536 if ( ni > 0 && mvData[ni-1].pPattern == pPattern )
537 { // combine
538 mvData[ni-1].nEndRow = nEndRow;
539 nInsert = rDocument.MaxRow() + 1;
540 bCombined = true;
541 }
542 }
543 else
544 nInsert = 0;
545
546 SCSIZE nj = ni; // stop position of range to replace
547 while ( nj < mvData.size() && mvData[nj].nEndRow <= nEndRow )
548 nj++;
549 if ( !bSplit )
550 {
551 if ( nj < mvData.size() && mvData[nj].pPattern == pPattern )
552 { // combine
553 if ( ni > 0 )
554 {
555 if ( mvData[ni-1].pPattern == pPattern )
556 { // adjacent entries
557 mvData[ni-1].nEndRow = mvData[nj].nEndRow;
558 nj++;
559 }
560 else if ( ni == nInsert )
561 mvData[ni-1].nEndRow = nStartRow - 1; // shrink
562 }
563 nInsert = rDocument.MaxRow() + 1;
564 bCombined = true;
565 }
566 else if ( ni > 0 && ni == nInsert )
567 mvData[ni-1].nEndRow = nStartRow - 1; // shrink
568 }
569 ScDocumentPool* pDocPool = rDocument.GetPool();
570 if ( bSplit )
571 { // duplicate split entry in pool
572 pDocPool->Put( *mvData[ni-1].pPattern );
573 }
574 if ( ni < nj )
575 { // remove middle entries
576 for ( SCSIZE nk=ni; nk<nj; nk++)
577 { // remove entries from pool
578 pDocPool->Remove( *mvData[nk].pPattern );
579 }
580 if ( !bCombined )
581 { // replace one entry
582 mvData[ni].nEndRow = nEndRow;
583 mvData[ni].pPattern = pPattern;
584 ni++;
585 nInsert = rDocument.MaxRow() + 1;
586 }
587 if ( ni < nj )
588 { // remove entries
589 mvData.erase( mvData.begin() + ni, mvData.begin() + nj);
590 }
591 }
592
593 if ( nInsert < sal::static_int_cast<SCSIZE>(rDocument.MaxRow() + 1) )
594 { // insert or append new entry
595 if ( nInsert <= mvData.size() )
596 {
597 if ( !bSplit )
598 mvData.emplace(mvData.begin() + nInsert);
599 else
600 {
601 mvData.insert(mvData.begin() + nInsert, 2, ScAttrEntry());
602 mvData[nInsert+1] = mvData[nInsert-1];
603 }
604 }
605 if ( nInsert )
606 mvData[nInsert-1].nEndRow = nStartRow - 1;
607 mvData[nInsert].nEndRow = nEndRow;
608 mvData[nInsert].pPattern = pPattern;
609
610 // Remove character attributes from these cells if the pattern
611 // is applied during normal session.
612 if (pDataArray && nCol != -1)
613 RemoveCellCharAttribs(nStartRow, nEndRow, pPattern, pDataArray);
614 }
615
616 rDocument.SetStreamValid(nTab, false);
617 }
618 }
619
620#if DEBUG_SC_TESTATTRARRAY0
621 TestData();
622#endif
623 return pPattern;
624}
625
626void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle )
627{
628 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
629 return;
630
631 SetDefaultIfNotInit();
632 SCSIZE nPos;
633 SCROW nStart=0;
634 if (!Search( nStartRow, nPos ))
635 {
636 OSL_FAIL("Search Failure")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "636" ": "), "%s", "Search Failure"); } } while (false)
;
637 return;
638 }
639
640 ScAddress aAdrStart( nCol, 0, nTab );
641 ScAddress aAdrEnd ( nCol, 0, nTab );
642
643 do
644 {
645 const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
646 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
647 pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(&rStyle));
648 SCROW nY1 = nStart;
649 SCROW nY2 = mvData[nPos].nEndRow;
650 nStart = mvData[nPos].nEndRow + 1;
651
652 if ( *pNewPattern == *pOldPattern )
653 {
654 // keep the original pattern (might be default)
655 // pNewPattern is deleted below
656 nPos++;
657 }
658 else if ( nY1 < nStartRow || nY2 > nEndRow )
659 {
660 if (nY1 < nStartRow) nY1=nStartRow;
661 if (nY2 > nEndRow) nY2=nEndRow;
662 SetPatternArea( nY1, nY2, std::move(pNewPattern), true );
663 Search( nStart, nPos );
664 }
665 else
666 {
667 if ( nCol != -1 )
668 {
669 // ensure attributing changes text width of cell; otherwise
670 // there aren't (yet) template format changes
671 const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
672 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
673
674 bool bNumFormatChanged;
675 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
676 rNewSet, rOldSet ) )
677 {
678 aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
679 aAdrEnd .SetRow( mvData[nPos].nEndRow );
680 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
681 }
682 }
683
684 rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
685 mvData[nPos].pPattern = &rDocument.GetPool()->Put(*pNewPattern);
686 if (Concat(nPos))
687 Search(nStart, nPos);
688 else
689 nPos++;
690 }
691 }
692 while ((nStart <= nEndRow) && (nPos < mvData.size()));
693
694 rDocument.SetStreamValid(nTab, false);
695
696#if DEBUG_SC_TESTATTRARRAY0
697 TestData();
698#endif
699}
700
701 // const cast, otherwise it will be too inefficient/complicated
702static void SetLineColor(SvxBorderLine const * dest, Color c)
703{
704 if (dest)
705 {
706 const_cast<SvxBorderLine*>(dest)->SetColor(c);
707 }
708}
709
710static void SetLine(const SvxBorderLine* dest, const SvxBorderLine* src)
711{
712 if (dest)
713 {
714 SvxBorderLine* pCast = const_cast<SvxBorderLine*>(dest);
715 pCast->SetBorderLineStyle( src->GetBorderLineStyle() );
716 pCast->SetWidth( src->GetWidth() );
717 }
718}
719
720void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
721 const SvxBorderLine* pLine, bool bColorOnly )
722{
723 if ( bColorOnly && !pLine )
724 return;
725
726 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
727 return;
728
729 SCSIZE nPos;
730 SCROW nStart=0;
731 SetDefaultIfNotInit();
732 if (!Search( nStartRow, nPos ))
733 {
734 OSL_FAIL("Search failure")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "734" ": "), "%s", "Search failure"); } } while (false)
;
735 return;
736 }
737
738 do
739 {
740 const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
741 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
742 const SfxPoolItem* pBoxItem = nullptr;
743 SfxItemState eState = rOldSet.GetItemState( ATTR_BORDER, true, &pBoxItem );
744 const SfxPoolItem* pTLBRItem = nullptr;
745 SfxItemState eTLBRState = rOldSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem );
746 const SfxPoolItem* pBLTRItem = nullptr;
747 SfxItemState eBLTRState = rOldSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem );
748
749 if ( (SfxItemState::SET == eState) || (SfxItemState::SET == eTLBRState) || (SfxItemState::SET == eBLTRState) )
750 {
751 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
752 SfxItemSet& rNewSet = pNewPattern->GetItemSet();
753 SCROW nY1 = nStart;
754 SCROW nY2 = mvData[nPos].nEndRow;
755
756 std::unique_ptr<SvxBoxItem> pNewBoxItem( pBoxItem ? static_cast<SvxBoxItem*>(pBoxItem->Clone()) : nullptr);
757 std::unique_ptr<SvxLineItem> pNewTLBRItem( pTLBRItem ? static_cast<SvxLineItem*>(pTLBRItem->Clone()) : nullptr);
758 std::unique_ptr<SvxLineItem> pNewBLTRItem(pBLTRItem ? static_cast<SvxLineItem*>(pBLTRItem->Clone()) : nullptr);
759
760 // fetch line and update attributes with parameters
761
762 if ( !pLine )
763 {
764 if( pNewBoxItem )
765 {
766 if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::TOP );
767 if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::BOTTOM );
768 if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::LEFT );
769 if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::RIGHT );
770 }
771 if( pNewTLBRItem && pNewTLBRItem->GetLine() )
772 pNewTLBRItem->SetLine( nullptr );
773 if( pNewBLTRItem && pNewBLTRItem->GetLine() )
774 pNewBLTRItem->SetLine( nullptr );
775 }
776 else
777 {
778 if ( bColorOnly )
779 {
780 Color aColor( pLine->GetColor() );
781 if( pNewBoxItem )
782 {
783 SetLineColor( pNewBoxItem->GetTop(), aColor );
784 SetLineColor( pNewBoxItem->GetBottom(), aColor );
785 SetLineColor( pNewBoxItem->GetLeft(), aColor );
786 SetLineColor( pNewBoxItem->GetRight(), aColor );
787 }
788 if( pNewTLBRItem )
789 SetLineColor( pNewTLBRItem->GetLine(), aColor );
790 if( pNewBLTRItem )
791 SetLineColor( pNewBLTRItem->GetLine(), aColor );
792 }
793 else
794 {
795 if( pNewBoxItem )
796 {
797 SetLine( pNewBoxItem->GetTop(), pLine );
798 SetLine( pNewBoxItem->GetBottom(), pLine );
799 SetLine( pNewBoxItem->GetLeft(), pLine );
800 SetLine( pNewBoxItem->GetRight(), pLine );
801 }
802 if( pNewTLBRItem )
803 SetLine( pNewTLBRItem->GetLine(), pLine );
804 if( pNewBLTRItem )
805 SetLine( pNewBLTRItem->GetLine(), pLine );
806 }
807 }
808 if( pNewBoxItem ) rNewSet.Put( *pNewBoxItem );
809 if( pNewTLBRItem ) rNewSet.Put( *pNewTLBRItem );
810 if( pNewBLTRItem ) rNewSet.Put( *pNewBLTRItem );
811
812 nStart = mvData[nPos].nEndRow + 1;
813
814 if ( nY1 < nStartRow || nY2 > nEndRow )
815 {
816 if (nY1 < nStartRow) nY1=nStartRow;
817 if (nY2 > nEndRow) nY2=nEndRow;
818 SetPatternArea( nY1, nY2, std::move(pNewPattern), true );
819 Search( nStart, nPos );
820 }
821 else
822 {
823 // remove from pool ?
824 rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
825 mvData[nPos].pPattern =
826 &rDocument.GetPool()->Put(std::move(pNewPattern));
827
828 if (Concat(nPos))
829 Search(nStart, nPos);
830 else
831 nPos++;
832 }
833 }
834 else
835 {
836 nStart = mvData[nPos].nEndRow + 1;
837 nPos++;
838 }
839 }
840 while ((nStart <= nEndRow) && (nPos < mvData.size()));
841}
842
843void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache, ScEditDataArray* pDataArray, bool* const pIsChanged )
844{
845#if DEBUG_SC_TESTATTRARRAY0
846 TestData();
847#endif
848
849 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
850 return;
851
852 SCSIZE nPos;
853 SCROW nStart=0;
854 SetDefaultIfNotInit();
855 if (!Search( nStartRow, nPos ))
856 {
857 OSL_FAIL("Search Failure")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "857" ": "), "%s", "Search Failure"); } } while (false)
;
858 return;
859 }
860
861 ScAddress aAdrStart( nCol, 0, nTab );
862 ScAddress aAdrEnd ( nCol, 0, nTab );
863
864 do
865 {
866 const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
867 const ScPatternAttr* pNewPattern = static_cast<const ScPatternAttr*>( &pCache->ApplyTo( *pOldPattern ) );
868 if (pNewPattern != pOldPattern)
869 {
870 SCROW nY1 = nStart;
871 SCROW nY2 = mvData[nPos].nEndRow;
872 nStart = mvData[nPos].nEndRow + 1;
873
874 if(pIsChanged)
875 *pIsChanged = true;
876
877 if ( nY1 < nStartRow || nY2 > nEndRow )
878 {
879 if (nY1 < nStartRow) nY1=nStartRow;
880 if (nY2 > nEndRow) nY2=nEndRow;
881 SetPatternArea( nY1, nY2, pNewPattern, false, pDataArray );
882 Search( nStart, nPos );
883 }
884 else
885 {
886 if ( nCol != -1 )
887 {
888 // ensure attributing changes text-width of cell
889
890 const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
891 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
892
893 bool bNumFormatChanged;
894 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
895 rNewSet, rOldSet ) )
896 {
897 aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
898 aAdrEnd .SetRow( mvData[nPos].nEndRow );
899 rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
900 }
901 }
902
903 rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
904 mvData[nPos].pPattern = pNewPattern;
905 if (Concat(nPos))
906 Search(nStart, nPos);
907 else
908 ++nPos;
909 }
910 }
911 else
912 {
913 nStart = mvData[nPos].nEndRow + 1;
914 ++nPos;
915 }
916 }
917 while (nStart <= nEndRow);
918
919 rDocument.SetStreamValid(nTab, false);
920
921#if DEBUG_SC_TESTATTRARRAY0
922 TestData();
923#endif
924}
925
926void ScAttrArray::SetAttrEntries(std::vector<ScAttrEntry> && vNewData)
927{
928 ScDocumentPool* pDocPool = rDocument.GetPool();
929 for (auto const & rEntry : mvData)
930 pDocPool->Remove(*rEntry.pPattern);
931
932 mvData = std::move(vNewData);
933}
934
935static void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource )
936{
937 const SfxPoolItem* pNewItem;
938 const SfxPoolItem* pOldItem;
939 for (sal_uInt16 nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++)
940 {
941 // pMergeSet has no parent
942 SfxItemState eOldState = rMergeSet.GetItemState( nId, false, &pOldItem );
943
944 if ( eOldState == SfxItemState::DEFAULT )
945 {
946 SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
947 if ( eNewState == SfxItemState::SET )
948 {
949 if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) )
950 rMergeSet.InvalidateItem( nId );
951 }
952 }
953 else if ( eOldState == SfxItemState::SET ) // Item set
954 {
955 SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
956 if ( eNewState == SfxItemState::SET )
957 {
958 if ( pNewItem != pOldItem ) // Both pulled
959 rMergeSet.InvalidateItem( nId );
960 }
961 else // Default
962 {
963 if ( *pOldItem != rSource.GetPool()->GetDefaultItem(nId) )
964 rMergeSet.InvalidateItem( nId );
965 }
966 }
967 // Dontcare remains Dontcare
968 }
969}
970
971void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow,
972 ScMergePatternState& rState, bool bDeep ) const
973{
974 if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
975 return;
976
977 SCSIZE nPos = 0;
978 SCROW nStart=0;
979 if ( !mvData.empty() && !Search( nStartRow, nPos ) )
980 {
981 OSL_FAIL("Search failure")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "981" ": "), "%s", "Search failure"); } } while (false)
;
982 return;
983 }
984
985 do
986 {
987 // similar patterns must not be repeated
988 const ScPatternAttr* pPattern = nullptr;
989 if ( !mvData.empty() )
990 pPattern = mvData[nPos].pPattern;
991 else
992 pPattern = rDocument.GetDefPattern();
993 if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 )
994 {
995 const SfxItemSet& rThisSet = pPattern->GetItemSet();
996 if (rState.pItemSet)
997 {
998 rState.mbValidPatternId = false;
999 if (bDeep)
1000 lcl_MergeDeep( *rState.pItemSet, rThisSet );
1001 else
1002 rState.pItemSet->MergeValues( rThisSet );
1003 }
1004 else
1005 {
1006 // first pattern - copied from parent
1007 rState.pItemSet = std::make_unique<SfxItemSet>( *rThisSet.GetPool(), rThisSet.GetRanges() );
1008 rState.pItemSet->Set( rThisSet, bDeep );
1009 rState.mnPatternId = pPattern->GetKey();
1010 }
1011
1012 rState.pOld2 = rState.pOld1;
1013 rState.pOld1 = pPattern;
1014 }
1015
1016 if ( !mvData.empty() )
1017 nStart = mvData[nPos].nEndRow + 1;
1018 else
1019 nStart = rDocument.MaxRow() + 1;
1020 ++nPos;
1021 }
1022 while (nStart <= nEndRow);
1023}
1024
1025// assemble border
1026
1027static bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
1028 sal_uInt8& rModified, const SvxBorderLine*& rpNew )
1029{
1030 if (rModified == SC_LINE_DONTCARE2)
1031 return false; // don't go again
1032
1033 if (rModified == SC_LINE_EMPTY0)
1034 {
1035 rModified = SC_LINE_SET1;
1036 rpNew = pNewLine;
1037 return true; // initial value
1038 }
1039
1040 if (pOldLine == pNewLine)
1041 {
1042 rpNew = pOldLine;
1043 return false;
1044 }
1045
1046 if (pOldLine && pNewLine)
1047 if (*pOldLine == *pNewLine)
1048 {
1049 rpNew = pOldLine;
1050 return false;
1051 }
1052
1053 rModified = SC_LINE_DONTCARE2;
1054 rpNew = nullptr;
1055 return true; // another line -> don't care
1056}
1057
1058static void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
1059 ScLineFlags& rFlags, const ScPatternAttr* pPattern,
1060 bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
1061{
1062 // right/bottom border set when connected together
1063 const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
1064 if ( rMerge.GetColMerge() == nDistRight + 1 )
1065 nDistRight = 0;
1066 if ( rMerge.GetRowMerge() == nDistBottom + 1 )
1067 nDistBottom = 0;
1068
1069 const SvxBoxItem* pCellFrame = &pPattern->GetItemSet().Get( ATTR_BORDER );
1070 const SvxBorderLine* pLeftAttr = pCellFrame->GetLeft();
1071 const SvxBorderLine* pRightAttr = pCellFrame->GetRight();
1072 const SvxBorderLine* pTopAttr = pCellFrame->GetTop();
1073 const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom();
1074 const SvxBorderLine* pNew;
1075
1076 if (bTop)
1077 {
1078 if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew ))
1079 pLineOuter->SetLine( pNew, SvxBoxItemLine::TOP );
1080 }
1081 else
1082 {
1083 if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew ))
1084 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
1085 }
1086
1087 if (nDistBottom == 0)
1088 {
1089 if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew ))
1090 pLineOuter->SetLine( pNew, SvxBoxItemLine::BOTTOM );
1091 }
1092 else
1093 {
1094 if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew ))
1095 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
1096 }
1097
1098 if (bLeft)
1099 {
1100 if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew ))
1101 pLineOuter->SetLine( pNew, SvxBoxItemLine::LEFT );
1102 }
1103 else
1104 {
1105 if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew ))
1106 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
1107 }
1108
1109 if (nDistRight == 0)
1110 {
1111 if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew ))
1112 pLineOuter->SetLine( pNew, SvxBoxItemLine::RIGHT );
1113 }
1114 else
1115 {
1116 if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew ))
1117 pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
1118 }
1119}
1120
1121void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
1122 ScLineFlags& rFlags,
1123 SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight ) const
1124{
1125 const ScPatternAttr* pPattern;
1126
1127 if (nStartRow == nEndRow)
1128 {
1129 pPattern = GetPattern( nStartRow );
1130 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true, 0 );
1131 }
1132 else if ( !mvData.empty() ) // non-default pattern
1133 {
1134 pPattern = GetPattern( nStartRow );
1135 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true,
1136 nEndRow-nStartRow );
1137
1138 SCSIZE nStartIndex;
1139 SCSIZE nEndIndex;
1140 Search( nStartRow+1, nStartIndex );
1141 Search( nEndRow-1, nEndIndex );
1142 for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1143 {
1144 pPattern = mvData[i].pPattern;
1145 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false,
1146 nEndRow - std::min( mvData[i].nEndRow, static_cast<SCROW>(nEndRow-1) ) );
1147 // nDistBottom here always > 0
1148 }
1149
1150 pPattern = GetPattern( nEndRow );
1151 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, 0 );
1152 }
1153 else
1154 {
1155 lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, rDocument.GetDefPattern(), bLeft, nDistRight, true, 0 );
1156 }
1157}
1158
1159// apply border
1160
1161// ApplyFrame - on an entry into the array
1162
1163bool ScAttrArray::ApplyFrame( const SvxBoxItem* pBoxItem,
1164 const SvxBoxInfoItem* pBoxInfoItem,
1165 SCROW nStartRow, SCROW nEndRow,
1166 bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
1167{
1168 OSL_ENSURE( pBoxItem && pBoxInfoItem, "Missing line attributes!" )do { if (true && (!(pBoxItem && pBoxInfoItem)
)) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"
), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "1168" ": "), "%s", "Missing line attributes!"); } } while
(false)
;
1169
1170 const ScPatternAttr* pPattern = GetPattern( nStartRow );
1171 const SvxBoxItem* pOldFrame = &pPattern->GetItemSet().Get( ATTR_BORDER );
1172
1173 // right/bottom border set when connected together
1174 const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
1175 if ( rMerge.GetColMerge() == nDistRight + 1 )
1176 nDistRight = 0;
1177 if ( rMerge.GetRowMerge() == nDistBottom + 1 )
1178 nDistBottom = 0;
1179
1180 SvxBoxItem aNewFrame( *pOldFrame );
1181 bool bRTL=rDocument.IsLayoutRTL(nTab);
1182 // fdo#37464 check if the sheet are RTL then replace right <=> left
1183 if (bRTL)
1184 {
1185 if( bLeft && nDistRight==0)
1186 {
1187 if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
1188 aNewFrame.SetLine( pBoxItem->GetLeft(), SvxBoxItemLine::RIGHT );
1189 if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
1190 aNewFrame.SetLine( pBoxItem->GetRight(), SvxBoxItemLine::LEFT );
1191 }
1192 else
1193 {
1194 if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1195 aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(),
1196 SvxBoxItemLine::RIGHT );
1197 if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1198 aNewFrame.SetLine( bLeft ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(),
1199 SvxBoxItemLine::LEFT );
1200 }
1201 }
1202 else
1203 {
1204 if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1205 aNewFrame.SetLine( bLeft ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(),
1206 SvxBoxItemLine::LEFT );
1207 if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
1208 aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(),
1209 SvxBoxItemLine::RIGHT );
1210 }
1211 if ( bTop ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
1212 aNewFrame.SetLine( bTop ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(),
1213 SvxBoxItemLine::TOP );
1214 if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
1215 aNewFrame.SetLine( (nDistBottom==0) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(),
1216 SvxBoxItemLine::BOTTOM );
1217
1218 if (aNewFrame == *pOldFrame)
1219 {
1220 // nothing to do
1221 return false;
1222 }
1223 else
1224 {
1225 SfxItemPoolCache aCache( rDocument.GetPool(), &aNewFrame );
1226 ApplyCacheArea( nStartRow, nEndRow, &aCache );
1227
1228 return true;
1229 }
1230}
1231
1232void ScAttrArray::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
1233 SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight)
1234{
1235 if (nStartRow == nEndRow)
1236 ApplyFrame(&rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0);
1237 else if ( !mvData.empty() )
1238 {
1239 ApplyFrame(&rLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight,
1240 true, nEndRow-nStartRow);
1241
1242 if ( nEndRow > nStartRow+1 ) // inner part available?
1243 {
1244 SCSIZE nStartIndex;
1245 SCSIZE nEndIndex;
1246 Search( nStartRow+1, nStartIndex );
1247 Search( nEndRow-1, nEndIndex );
1248 SCROW nTmpStart = nStartRow+1;
1249 SCROW nTmpEnd;
1250 for (SCSIZE i=nStartIndex; i<=nEndIndex;)
1251 {
1252 nTmpEnd = std::min( static_cast<SCROW>(nEndRow-1), mvData[i].nEndRow );
1253 bool bChanged = ApplyFrame(&rLineOuter, pLineInner, nTmpStart, nTmpEnd,
1254 bLeft, nDistRight, false, nEndRow - nTmpEnd);
1255 nTmpStart = nTmpEnd+1;
1256 if (bChanged)
1257 {
1258 Search(nTmpStart, i);
1259 Search(nEndRow-1, nEndIndex);
1260 }
1261 else
1262 i++;
1263 }
1264 }
1265
1266 ApplyFrame(&rLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, false, 0);
1267 }
1268 else
1269 {
1270 ApplyFrame(&rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0);
1271 }
1272}
1273
1274bool ScAttrArray::HasAttrib_Impl(const ScPatternAttr* pPattern, HasAttrFlags nMask, SCROW nRow1, SCROW nRow2, SCSIZE i) const
1275{
1276 bool bFound = false;
1277 if ( nMask & HasAttrFlags::Merged )
1278 {
1279 const ScMergeAttr* pMerge = &pPattern->GetItem( ATTR_MERGE );
1280 if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
1281 bFound = true;
1282 }
1283 if ( nMask & ( HasAttrFlags::Overlapped | HasAttrFlags::NotOverlapped | HasAttrFlags::AutoFilter ) )
1284 {
1285 const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem( ATTR_MERGE_FLAG );
1286 if ( (nMask & HasAttrFlags::Overlapped) && pMergeFlag->IsOverlapped() )
1287 bFound = true;
1288 if ( (nMask & HasAttrFlags::NotOverlapped) && !pMergeFlag->IsOverlapped() )
1289 bFound = true;
1290 if ( (nMask & HasAttrFlags::AutoFilter) && pMergeFlag->HasAutoFilter() )
1291 bFound = true;
1292 }
1293 if ( nMask & HasAttrFlags::Lines )
1294 {
1295 const SvxBoxItem* pBox = &pPattern->GetItem( ATTR_BORDER );
1296 if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() )
1297 bFound = true;
1298 }
1299 if ( nMask & HasAttrFlags::Shadow )
1300 {
1301 const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
1302 if ( pShadow->GetLocation() != SvxShadowLocation::NONE )
1303 bFound = true;
1304 }
1305 if ( nMask & HasAttrFlags::Conditional )
1306 {
1307 bool bContainsCondFormat = pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty();
1308 if ( bContainsCondFormat )
1309 bFound = true;
1310 }
1311 if ( nMask & HasAttrFlags::Protected )
1312 {
1313 const ScProtectionAttr* pProtect = &pPattern->GetItem( ATTR_PROTECTION );
1314 bool bFoundTemp = false;
1315 if ( pProtect->GetProtection() || pProtect->GetHideCell() )
1316 bFoundTemp = true;
1317
1318 bool bContainsCondFormat = !mvData.empty() &&
1319 !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty();
1320 if ( bContainsCondFormat && nCol != -1 ) // rDocument.GetCondResult() is valid only for real columns.
1321 {
1322 SCROW nRowStartCond = std::max<SCROW>( nRow1, i ? mvData[i-1].nEndRow + 1: 0 );
1323 SCROW nRowEndCond = std::min<SCROW>( nRow2, mvData[i].nEndRow );
1324 bool bFoundCond = false;
1325 for(SCROW nRowCond = nRowStartCond; nRowCond <= nRowEndCond && !bFoundCond; ++nRowCond)
1326 {
1327 const SfxItemSet* pSet = rDocument.GetCondResult( nCol, nRowCond, nTab );
1328
1329 const SfxPoolItem* pItem;
1330 if( pSet && pSet->GetItemState( ATTR_PROTECTION, true, &pItem ) == SfxItemState::SET )
1331 {
1332 const ScProtectionAttr* pCondProtect = static_cast<const ScProtectionAttr*>(pItem);
1333 if( pCondProtect->GetProtection() || pCondProtect->GetHideCell() )
1334 bFoundCond = true;
1335 else
1336 break;
1337 }
1338 else
1339 {
1340 // well it is not true that we found one
1341 // but existing one + cell where conditional
1342 // formatting does not remove it
1343 // => we should use the existing protection setting
1344 bFoundCond = bFoundTemp;
1345 }
1346 }
1347 bFoundTemp = bFoundCond;
1348 }
1349
1350 if(bFoundTemp)
1351 bFound = true;
1352 }
1353 if ( nMask & HasAttrFlags::Rotate )
1354 {
1355 const ScRotateValueItem* pRotate = &pPattern->GetItem( ATTR_ROTATE_VALUE );
1356 // 90 or 270 degrees is former SvxOrientationItem - only look for other values
1357 // (see ScPatternAttr::GetCellOrientation)
1358 sal_Int32 nAngle = pRotate->GetValue();
1359 if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 )
1360 bFound = true;
1361 }
1362 if ( nMask & HasAttrFlags::NeedHeight )
1363 {
1364 if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
1365 bFound = true;
1366 else if (pPattern->GetItem( ATTR_LINEBREAK ).GetValue())
1367 bFound = true;
1368 else if (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block)
1369 bFound = true;
1370
1371 else if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
1372 bFound = true;
1373 else if (pPattern->GetItem( ATTR_ROTATE_VALUE ).GetValue())
1374 bFound = true;
1375 }
1376 if ( nMask & ( HasAttrFlags::ShadowRight | HasAttrFlags::ShadowDown ) )
1377 {
1378 const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
1379 SvxShadowLocation eLoc = pShadow->GetLocation();
1380 if ( nMask & HasAttrFlags::ShadowRight )
1381 if ( eLoc == SvxShadowLocation::TopRight || eLoc == SvxShadowLocation::BottomRight )
1382 bFound = true;
1383 if ( nMask & HasAttrFlags::ShadowDown )
1384 if ( eLoc == SvxShadowLocation::BottomLeft || eLoc == SvxShadowLocation::BottomRight )
1385 bFound = true;
1386 }
1387 if ( nMask & HasAttrFlags::RightOrCenter )
1388 {
1389 // called only if the sheet is LTR, so physical=logical alignment can be assumed
1390 SvxCellHorJustify eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
1391 if ( eHorJust == SvxCellHorJustify::Right || eHorJust == SvxCellHorJustify::Center )
1392 bFound = true;
1393 }
1394
1395 return bFound;
1396}
1397
1398// Test if field contains specific attribute
1399bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) const
1400{
1401 if (mvData.empty())
1402 {
1403 return HasAttrib_Impl(rDocument.GetDefPattern(), nMask, 0, rDocument.MaxRow(), 0);
1404 }
1405
1406 SCSIZE nStartIndex;
1407 SCSIZE nEndIndex;
1408 Search( nRow1, nStartIndex );
1409 if (nRow1 != nRow2)
1410 Search( nRow2, nEndIndex );
1411 else
1412 nEndIndex = nStartIndex;
1413 bool bFound = false;
1414
1415 for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++)
1416 {
1417 const ScPatternAttr* pPattern = mvData[i].pPattern;
1418 bFound = HasAttrib_Impl(pPattern, nMask, nRow1, nRow2, i);
1419 }
1420
1421 return bFound;
1422}
1423
1424bool ScAttrArray::IsMerged( SCROW nRow ) const
1425{
1426 if ( !mvData.empty() )
1427 {
1428 SCSIZE nIndex;
1429 Search(nRow, nIndex);
1430 const ScMergeAttr& rItem = mvData[nIndex].pPattern->GetItem(ATTR_MERGE);
1431
1432 return rItem.IsMerged();
1433 }
1434
1435 return rDocument.GetDefPattern()->GetItem(ATTR_MERGE).IsMerged();
1436}
1437
1438/**
1439 * Area around any given summaries expand and adapt any MergeFlag (bRefresh)
1440 */
1441bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
1442 SCCOL& rPaintCol, SCROW& rPaintRow,
1443 bool bRefresh )
1444{
1445 assert( nCol != -1 )(static_cast <bool> (nCol != -1) ? void (0) : __assert_fail
("nCol != -1", "/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
, 1445, __extension__ __PRETTY_FUNCTION__))
;
1446 SetDefaultIfNotInit();
1447 const ScPatternAttr* pPattern;
1448 const ScMergeAttr* pItem;
1449 SCSIZE nStartIndex;
1450 SCSIZE nEndIndex;
1451 Search( nStartRow, nStartIndex );
1452 Search( nEndRow, nEndIndex );
1453 bool bFound = false;
1454
1455 for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
1456 {
1457 pPattern = mvData[i].pPattern;
1458 pItem = &pPattern->GetItem( ATTR_MERGE );
1459 SCCOL nCountX = pItem->GetColMerge();
1460 SCROW nCountY = pItem->GetRowMerge();
1461 if (nCountX>1 || nCountY>1)
1462 {
1463 SCROW nThisRow = (i>0) ? mvData[i-1].nEndRow+1 : 0;
1464 SCCOL nMergeEndCol = nThisCol + nCountX - 1;
1465 SCROW nMergeEndRow = nThisRow + nCountY - 1;
1466 if (nMergeEndCol > rPaintCol && nMergeEndCol <= rDocument.MaxCol())
1467 rPaintCol = nMergeEndCol;
1468 if (nMergeEndRow > rPaintRow && nMergeEndRow <= rDocument.MaxRow())
1469 rPaintRow = nMergeEndRow;
1470 bFound = true;
1471
1472 if (bRefresh)
1473 {
1474 if ( nMergeEndCol > nThisCol )
1475 rDocument.ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, mvData[i].nEndRow,
1476 nTab, ScMF::Hor );
1477 if ( nMergeEndRow > nThisRow )
1478 rDocument.ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow,
1479 nTab, ScMF::Ver );
1480 if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow )
1481 rDocument.ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow,
1482 nTab, ScMF::Hor | ScMF::Ver );
1483
1484 Search( nThisRow, i ); // Data changed
1485 Search( nStartRow, nStartIndex );
1486 Search( nEndRow, nEndIndex );
1487 }
1488 }
1489 }
1490
1491 return bFound;
1492}
1493
1494void ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
1495{
1496 assert( nCol != -1 )(static_cast <bool> (nCol != -1) ? void (0) : __assert_fail
("nCol != -1", "/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
, 1496, __extension__ __PRETTY_FUNCTION__))
;
1497 SetDefaultIfNotInit();
1498 const ScPatternAttr* pPattern;
1499 const ScMergeAttr* pItem;
1500 SCSIZE nIndex;
1501
1502 Search( nStartRow, nIndex );
1503 SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1504 if (nThisStart < nStartRow)
1505 nThisStart = nStartRow;
1506
1507 while ( nThisStart <= nEndRow )
1508 {
1509 SCROW nThisEnd = mvData[nIndex].nEndRow;
1510 if (nThisEnd > nEndRow)
1511 nThisEnd = nEndRow;
1512
1513 pPattern = mvData[nIndex].pPattern;
1514 pItem = &pPattern->GetItem( ATTR_MERGE );
1515 SCCOL nCountX = pItem->GetColMerge();
1516 SCROW nCountY = pItem->GetRowMerge();
1517 if (nCountX>1 || nCountY>1)
1518 {
1519 const ScMergeAttr* pAttr = &rDocument.GetPool()->GetDefaultItem( ATTR_MERGE );
1520 const ScMergeFlagAttr* pFlagAttr = &rDocument.GetPool()->GetDefaultItem( ATTR_MERGE_FLAG );
1521
1522 OSL_ENSURE( nCountY==1 || nThisStart==nThisEnd, "What's up?" )do { if (true && (!(nCountY==1 || nThisStart==nThisEnd
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"
), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "1522" ": "), "%s", "What's up?"); } } while (false)
;
1523
1524 SCCOL nThisCol = nCol;
1525 SCCOL nMergeEndCol = nThisCol + nCountX - 1;
1526 SCROW nMergeEndRow = nThisEnd + nCountY - 1;
1527
1528 // ApplyAttr for areas
1529 for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++)
1530 rDocument.ApplyAttr( nThisCol, nThisRow, nTab, *pAttr );
1531
1532 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( rDocument.GetPool() ));
1533 SfxItemSet* pSet = &pNewPattern->GetItemSet();
1534 pSet->Put( *pFlagAttr );
1535 rDocument.ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow,
1536 nTab, *pNewPattern );
1537 pNewPattern.reset();
1538
1539 Search( nThisEnd, nIndex ); // data changed
1540 }
1541
1542 ++nIndex;
1543 if ( nIndex < mvData.size() )
1544 nThisStart = mvData[nIndex-1].nEndRow+1;
1545 else
1546 nThisStart = rDocument.MaxRow()+1; // End
1547 }
1548}
1549
1550void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow,
1551 const ScPatternAttr* pWantedPattern, bool bDefault )
1552{
1553 SetDefaultIfNotInit();
1554 const ScPatternAttr* pOldPattern;
1555 const ScMergeFlagAttr* pItem;
1556
1557 SCSIZE nIndex;
1558 SCROW nRow;
1559 SCROW nThisRow;
1560 bool bFirstUse = true;
1561
1562 Search( nStartRow, nIndex );
1563 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
7
Assuming 'nIndex' is <= 0
8
'?' condition is false
1564 while ( nThisRow <= nEndRow )
9
Assuming 'nThisRow' is <= 'nEndRow'
10
Loop condition is true. Entering loop body
1565 {
1566 pOldPattern = mvData[nIndex].pPattern;
1567 if (pOldPattern != pWantedPattern) // FIXME: else-branch?
11
Assuming 'pOldPattern' is not equal to 'pWantedPattern'
12
Taking true branch
1568 {
1569 if (nThisRow < nStartRow) nThisRow = nStartRow;
13
Assuming 'nThisRow' is >= 'nStartRow'
14
Taking false branch
1570 nRow = mvData[nIndex].nEndRow;
1571 SCROW nAttrRow = std::min( nRow, nEndRow );
1572 pItem = &pOldPattern->GetItem( ATTR_MERGE_FLAG );
1573
1574 if (pItem->IsOverlapped() || pItem->HasAutoFilter())
1575 {
1576 // default-constructing a ScPatternAttr for DeleteArea doesn't work
1577 // because it would have no cell style information.
1578 // Instead, the document's GetDefPattern is copied. Since it is passed as
1579 // pWantedPattern, no special treatment of default is needed here anymore.
1580 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( *pWantedPattern ));
15
Memory is allocated
1581 pNewPattern->GetItemSet().Put( *pItem );
1582 SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
16
Calling 'ScAttrArray::SetPatternArea'
1583 }
1584 else
1585 {
1586 if ( !bDefault )
1587 {
1588 if (bFirstUse)
1589 bFirstUse = false;
1590 else
1591 // it's in the pool
1592 rDocument.GetPool()->Put( *pWantedPattern );
1593 }
1594 SetPatternArea( nThisRow, nAttrRow, pWantedPattern );
1595 }
1596
1597 Search( nThisRow, nIndex ); // data changed
1598 }
1599
1600 ++nIndex;
1601 nThisRow = mvData[nIndex-1].nEndRow+1;
1602 }
1603}
1604
1605bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
1606{
1607 SetDefaultIfNotInit();
1608 const ScPatternAttr* pOldPattern;
1609
1610 ScMF nOldValue;
1611 SCSIZE nIndex;
1612 SCROW nRow;
1613 SCROW nThisRow;
1614 bool bChanged = false;
1615
1616 Search( nStartRow, nIndex );
1617 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1618 if (nThisRow < nStartRow) nThisRow = nStartRow;
1619
1620 while ( nThisRow <= nEndRow )
1621 {
1622 pOldPattern = mvData[nIndex].pPattern;
1623 nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
1624 if ( (nOldValue | nFlags) != nOldValue )
1625 {
1626 nRow = mvData[nIndex].nEndRow;
1627 SCROW nAttrRow = std::min( nRow, nEndRow );
1628 auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
1629 pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) );
1630 SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
1631 Search( nThisRow, nIndex ); // data changed
1632 bChanged = true;
1633 }
1634
1635 ++nIndex;
1636 nThisRow = mvData[nIndex-1].nEndRow+1;
1637 }
1638
1639 return bChanged;
1640}
1641
1642bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
1643{
1644 SetDefaultIfNotInit();
1645 const ScPatternAttr* pOldPattern;
1646
1647 ScMF nOldValue;
1648 SCSIZE nIndex;
1649 SCROW nRow;
1650 SCROW nThisRow;
1651 bool bChanged = false;
1652
1653 Search( nStartRow, nIndex );
1654 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1655 if (nThisRow < nStartRow) nThisRow = nStartRow;
1656
1657 while ( nThisRow <= nEndRow )
1658 {
1659 pOldPattern = mvData[nIndex].pPattern;
1660 nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
1661 if ( (nOldValue & ~nFlags) != nOldValue )
1662 {
1663 nRow = mvData[nIndex].nEndRow;
1664 SCROW nAttrRow = std::min( nRow, nEndRow );
1665 auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
1666 pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) );
1667 SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
1668 Search( nThisRow, nIndex ); // data changed
1669 bChanged = true;
1670 }
1671
1672 ++nIndex;
1673 nThisRow = mvData[nIndex-1].nEndRow+1;
1674 }
1675
1676 return bChanged;
1677}
1678
1679void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
1680{
1681 SetDefaultIfNotInit();
1682 SCSIZE nIndex;
1683 SCROW nRow;
1684 SCROW nThisRow;
1685
1686 Search( nStartRow, nIndex );
1687 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1688 if (nThisRow < nStartRow) nThisRow = nStartRow;
1689
1690 while ( nThisRow <= nEndRow )
1691 {
1692 const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
1693 if ( pOldPattern->HasItemsSet( pWhich ) )
1694 {
1695 auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
1696 pNewPattern->ClearItems( pWhich );
1697
1698 nRow = mvData[nIndex].nEndRow;
1699 SCROW nAttrRow = std::min( nRow, nEndRow );
1700 SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
1701 Search( nThisRow, nIndex ); // data changed
1702 }
1703
1704 ++nIndex;
1705 nThisRow = mvData[nIndex-1].nEndRow+1;
1706 }
1707}
1708
1709void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, bool bIncrement )
1710{
1711 SetDefaultIfNotInit();
1712 SCSIZE nIndex;
1713 Search( nStartRow, nIndex );
1714 SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
1715 if (nThisStart < nStartRow) nThisStart = nStartRow;
1716
1717 while ( nThisStart <= nEndRow )
1718 {
1719 const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
1720 const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
1721 const SfxPoolItem* pItem;
1722
1723 bool bNeedJust = ( rOldSet.GetItemState( ATTR_HOR_JUSTIFY, false, &pItem ) != SfxItemState::SET
1724 || (static_cast<const SvxHorJustifyItem*>(pItem)->GetValue() != SvxCellHorJustify::Left &&
1725 static_cast<const SvxHorJustifyItem*>(pItem)->GetValue() != SvxCellHorJustify::Right ));
1726 sal_uInt16 nOldValue = rOldSet.Get( ATTR_INDENT ).GetValue();
1727 sal_uInt16 nNewValue = nOldValue;
1728 // To keep Increment indent from running outside the cell1659
1729 long nColWidth = static_cast<long>(rDocument.GetColWidth(nCol,nTab));
1730 if ( bIncrement )
1731 {
1732 if ( nNewValue < nColWidth-SC_INDENT_STEP200 )
1733 {
1734 nNewValue += SC_INDENT_STEP200;
1735 if ( nNewValue > nColWidth-SC_INDENT_STEP200 ) nNewValue = nColWidth-SC_INDENT_STEP200;
1736 }
1737 }
1738 else
1739 {
1740 if ( nNewValue > 0 )
1741 {
1742 if ( nNewValue > SC_INDENT_STEP200 )
1743 nNewValue -= SC_INDENT_STEP200;
1744 else
1745 nNewValue = 0;
1746 }
1747 }
1748
1749 if ( bNeedJust || nNewValue != nOldValue )
1750 {
1751 SCROW nThisEnd = mvData[nIndex].nEndRow;
1752 SCROW nAttrRow = std::min( nThisEnd, nEndRow );
1753 auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
1754 pNewPattern->GetItemSet().Put( ScIndentItem( nNewValue ) );
1755 if ( bNeedJust )
1756 pNewPattern->GetItemSet().Put(
1757 SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
1758 SetPatternArea( nThisStart, nAttrRow, std::move(pNewPattern), true );
1759
1760 nThisStart = nThisEnd + 1;
1761 Search( nThisStart, nIndex ); // data changed
1762 }
1763 else
1764 {
1765 nThisStart = mvData[nIndex].nEndRow + 1;
1766 ++nIndex;
1767 }
1768 }
1769}
1770
1771SCROW ScAttrArray::GetNextUnprotected( SCROW nRow, bool bUp ) const
1772{
1773 long nRet = nRow;
1774 if (rDocument.ValidRow(nRow))
1775 {
1776 if ( mvData.empty() )
1777 {
1778 if ( bUp )
1779 return -1;
1780 else
1781 return rDocument.MaxRow()+1;
1782 }
1783
1784 SCSIZE nIndex;
1785 Search(nRow, nIndex);
1786 while (mvData[nIndex].pPattern->
1787 GetItem(ATTR_PROTECTION).GetProtection())
1788 {
1789 if (bUp)
1790 {
1791 if (nIndex==0)
1792 return -1; // not found
1793 --nIndex;
1794 nRet = mvData[nIndex].nEndRow;
1795 }
1796 else
1797 {
1798 nRet = mvData[nIndex].nEndRow+1;
1799 ++nIndex;
1800 if (nIndex >= mvData.size())
1801 return rDocument.MaxRow()+1; // not found
1802 }
1803 }
1804 }
1805 return nRet;
1806}
1807
1808void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
1809{
1810 SetDefaultIfNotInit();
1811 SCROW nStart = 0;
1812 SCSIZE nPos = 0;
1813 while (nPos < mvData.size())
1814 {
1815 SCROW nEnd = mvData[nPos].nEndRow;
1816 if (mvData[nPos].pPattern->GetStyleSheet() == pStyleSheet)
1817 {
1818 rUsedRows.setTrue(nStart, nEnd);
1819
1820 if (bReset)
1821 {
1822 std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*mvData[nPos].pPattern));
1823 rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
1824 pNewPattern->SetStyleSheet( static_cast<ScStyleSheet*>(
1825 rDocument.GetStyleSheetPool()->
1826 Find( ScResId(STR_STYLENAME_STANDARD_CELLreinterpret_cast<char const *>("STR_STYLENAME_STANDARD"
"\004" u8"Default Cell Style")
),
1827 SfxStyleFamily::Para,
1828 SfxStyleSearchBits::Auto | SfxStyleSearchBits::ScStandard ) ) );
1829 mvData[nPos].pPattern = &rDocument.GetPool()->Put(*pNewPattern);
1830 pNewPattern.reset();
1831
1832 if (Concat(nPos))
1833 {
1834 Search(nStart, nPos);
1835 --nPos; // because ++ at end
1836 }
1837 }
1838 }
1839 nStart = nEnd + 1;
1840 ++nPos;
1841 }
1842}
1843
1844bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
1845{
1846 if ( mvData.empty() )
1847 {
1848 const ScStyleSheet* pStyle = rDocument.GetDefPattern()->GetStyleSheet();
1849 if ( pStyle )
1850 {
1851 pStyle->SetUsage( ScStyleSheet::Usage::USED );
1852 if ( pStyle == &rStyle )
1853 return true;
1854 }
1855 return false;
1856 }
1857
1858 bool bIsUsed = false;
1859 SCSIZE nPos = 0;
1860
1861 while ( nPos < mvData.size() )
1862 {
1863 const ScStyleSheet* pStyle = mvData[nPos].pPattern->GetStyleSheet();
1864 if ( pStyle )
1865 {
1866 pStyle->SetUsage( ScStyleSheet::Usage::USED );
1867 if ( pStyle == &rStyle )
1868 {
1869 bIsUsed = true;
1870 }
1871 }
1872 nPos++;
1873 }
1874
1875 return bIsUsed;
1876}
1877
1878bool ScAttrArray::IsEmpty() const
1879{
1880 if ( mvData.empty() )
1881 return true;
1882
1883 if (mvData.size() == 1)
1884 {
1885 return mvData[0].pPattern == rDocument.GetDefPattern();
1886 }
1887 else
1888 return false;
1889}
1890
1891bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
1892{
1893 if ( mvData.empty() )
1894 return false;
1895
1896 bool bFound = false;
1897 SCSIZE nStart = 0;
1898
1899 // Skip first entry if more than 1 row.
1900 // Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr.
1901
1902 SCSIZE nVisStart = 1;
1903 while ( nVisStart < mvData.size() && mvData[nVisStart].pPattern->IsVisibleEqual(*mvData[nVisStart-1].pPattern) )
1904 ++nVisStart;
1905 if ( nVisStart >= mvData.size() || mvData[nVisStart-1].nEndRow > 0 ) // more than 1 row?
1906 nStart = nVisStart;
1907
1908 while ( nStart < mvData.size() && !bFound )
1909 {
1910 if ( mvData[nStart].pPattern->IsVisible() )
1911 {
1912 rFirstRow = nStart ? ( mvData[nStart-1].nEndRow + 1 ) : 0;
1913 bFound = true;
1914 }
1915 else
1916 ++nStart;
1917 }
1918
1919 return bFound;
1920}
1921
1922// size (rows) of a range of attributes after cell content where the search is stopped
1923// (more than a default page size, 2*42 because it's as good as any number)
1924
1925const SCROW SC_VISATTR_STOP = 84;
1926
1927bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const
1928{
1929 if ( mvData.empty() )
1930 {
1931 rLastRow = nLastData;
1932 return false;
1933 }
1934
1935 // #i30830# changed behavior:
1936 // ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows
1937 // below the last content cell
1938
1939 if ( nLastData == rDocument.MaxRow() )
1940 {
1941 rLastRow = rDocument.MaxRow(); // can't look for attributes below rDocument.MaxRow()
1942 return true;
1943 }
1944
1945 // Quick check: last data row in or immediately preceding a run that is the
1946 // last attribution down to the end, e.g. default style or column style.
1947 SCSIZE nPos = mvData.size() - 1;
1948 SCROW nStartRow = (nPos ? mvData[nPos-1].nEndRow + 1 : 0);
1949 if (nStartRow <= nLastData + 1)
1950 {
1951 // Ignore here a few rows if data happens to end within
1952 // SC_VISATTR_STOP rows before rDocument.MaxRow().
1953 rLastRow = nLastData;
1954 return false;
1955 }
1956
1957 // Find a run below last data row.
1958 bool bFound = false;
1959 Search( nLastData, nPos );
1960 while ( nPos < mvData.size() )
1961 {
1962 // find range of visually equal formats
1963 SCSIZE nEndPos = nPos;
1964 while ( nEndPos < mvData.size()-1 &&
1965 mvData[nEndPos].pPattern->IsVisibleEqual( *mvData[nEndPos+1].pPattern))
1966 ++nEndPos;
1967 SCROW nAttrStartRow = ( nPos > 0 ) ? ( mvData[nPos-1].nEndRow + 1 ) : 0;
1968 if ( nAttrStartRow <= nLastData )
1969 nAttrStartRow = nLastData + 1;
1970 SCROW nAttrSize = mvData[nEndPos].nEndRow + 1 - nAttrStartRow;
1971 if ( nAttrSize >= SC_VISATTR_STOP )
1972 break; // while, ignore this range and below
1973 else if ( mvData[nEndPos].pPattern->IsVisible() )
1974 {
1975 rLastRow = mvData[nEndPos].nEndRow;
1976 bFound = true;
1977 }
1978 nPos = nEndPos + 1;
1979 }
1980
1981 return bFound;
1982}
1983
1984bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
1985{
1986 if ( mvData.empty() )
1987 return rDocument.GetDefPattern()->IsVisible();
1988
1989 SCSIZE nIndex;
1990 Search( nStartRow, nIndex );
1991 SCROW nThisStart = nStartRow;
1992 bool bFound = false;
1993 while ( nIndex < mvData.size() && nThisStart <= nEndRow && !bFound )
1994 {
1995 if ( mvData[nIndex].pPattern->IsVisible() )
1996 bFound = true;
1997
1998 nThisStart = mvData[nIndex].nEndRow + 1;
1999 ++nIndex;
2000 }
2001
2002 return bFound;
2003}
2004
2005bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
2006 SCROW nStartRow, SCROW nEndRow ) const
2007{
2008 if ( mvData.empty() && rOther.mvData.empty() )
2009 {
2010 const ScPatternAttr* pDefPattern1 = rDocument.GetDefPattern();
2011 const ScPatternAttr* pDefPattern2 = rOther.rDocument.GetDefPattern();
2012 return ( pDefPattern1 == pDefPattern2 || pDefPattern1->IsVisibleEqual( *pDefPattern2 ) );
2013 }
2014
2015 {
2016 const ScAttrArray* pNonDefault = nullptr;
2017 const ScPatternAttr* pDefPattern = nullptr;
2018 bool bDefNonDefCase = false;
2019 if ( mvData.empty() && !rOther.mvData.empty() )
2020 {
2021 pNonDefault = &rOther;
2022 pDefPattern = rDocument.GetDefPattern();
2023 bDefNonDefCase = true;
2024 }
2025 else if ( !mvData.empty() && rOther.mvData.empty() )
2026 {
2027 pNonDefault = this;
2028 pDefPattern = rOther.rDocument.GetDefPattern();
2029 bDefNonDefCase = true;
2030 }
2031
2032 if ( bDefNonDefCase )
2033 {
2034 bool bEqual = true;
2035 SCSIZE nPos = 0;
2036 if ( nStartRow > 0 )
2037 pNonDefault->Search( nStartRow, nPos );
2038
2039 while ( nPos < pNonDefault->Count() && bEqual )
2040 {
2041 const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].pPattern;
2042 bEqual = ( pNonDefPattern == pDefPattern ||
2043 pNonDefPattern->IsVisibleEqual( *pDefPattern ) );
2044
2045 if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
2046 ++nPos;
2047 }
2048 return bEqual;
2049 }
2050 }
2051
2052 bool bEqual = true;
2053 SCSIZE nThisPos = 0;
2054 SCSIZE nOtherPos = 0;
2055 if ( nStartRow > 0 )
2056 {
2057 Search( nStartRow, nThisPos );
2058 rOther.Search( nStartRow, nOtherPos );
2059 }
2060
2061 while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
2062 {
2063 SCROW nThisRow = mvData[nThisPos].nEndRow;
2064 SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
2065 const ScPatternAttr* pThisPattern = mvData[nThisPos].pPattern;
2066 const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].pPattern;
2067 bEqual = ( pThisPattern == pOtherPattern ||
2068 pThisPattern->IsVisibleEqual(*pOtherPattern) );
2069
2070 if ( nThisRow >= nOtherRow )
2071 {
2072 if ( nOtherRow >= nEndRow ) break;
2073 ++nOtherPos;
2074 }
2075 if ( nThisRow <= nOtherRow )
2076 {
2077 if ( nThisRow >= nEndRow ) break;
2078 ++nThisPos;
2079 }
2080 }
2081
2082 return bEqual;
2083}
2084
2085bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
2086{
2087 // summarised with IsVisibleEqual
2088 if ( mvData.empty() && rOther.mvData.empty() )
2089 {
2090 const ScPatternAttr* pDefPattern1 = rDocument.GetDefPattern();
2091 const ScPatternAttr* pDefPattern2 = rOther.rDocument.GetDefPattern();
2092 return ( pDefPattern1 == pDefPattern2 );
2093 }
2094
2095 {
2096 const ScAttrArray* pNonDefault = nullptr;
2097 const ScPatternAttr* pDefPattern = nullptr;
2098 bool bDefNonDefCase = false;
2099 if ( mvData.empty() && !rOther.mvData.empty() )
2100 {
2101 pNonDefault = &rOther;
2102 pDefPattern = rDocument.GetDefPattern();
2103 bDefNonDefCase = true;
2104 }
2105 else if ( !mvData.empty() && rOther.mvData.empty() )
2106 {
2107 pNonDefault = this;
2108 pDefPattern = rOther.rDocument.GetDefPattern();
2109 bDefNonDefCase = true;
2110 }
2111
2112 if ( bDefNonDefCase )
2113 {
2114 bool bEqual = true;
2115 SCSIZE nPos = 0;
2116 if ( nStartRow > 0 )
2117 pNonDefault->Search( nStartRow, nPos );
2118
2119 while ( nPos < pNonDefault->Count() && bEqual )
2120 {
2121 const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].pPattern;
2122 bEqual = ( pNonDefPattern == pDefPattern );
2123
2124 if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
2125 ++nPos;
2126 }
2127 return bEqual;
2128 }
2129 }
2130
2131 bool bEqual = true;
2132 SCSIZE nThisPos = 0;
2133 SCSIZE nOtherPos = 0;
2134 if ( nStartRow > 0 )
2135 {
2136 Search( nStartRow, nThisPos );
2137 rOther.Search( nStartRow, nOtherPos );
2138 }
2139
2140 while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
2141 {
2142 SCROW nThisRow = mvData[nThisPos].nEndRow;
2143 SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
2144 const ScPatternAttr* pThisPattern = mvData[nThisPos].pPattern;
2145 const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].pPattern;
2146 bEqual = ( pThisPattern == pOtherPattern );
2147
2148 if ( nThisRow >= nOtherRow )
2149 {
2150 if ( nOtherRow >= nEndRow ) break;
2151 ++nOtherPos;
2152 }
2153 if ( nThisRow <= nOtherRow )
2154 {
2155 if ( nThisRow >= nEndRow ) break;
2156 ++nThisPos;
2157 }
2158 }
2159
2160 return bEqual;
2161}
2162
2163bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
2164{
2165 // Horizontal aggregate are not allowed to be moved out; if whole summary,
2166 // here is not recognized
2167
2168 bool bTest = true;
2169 if (!IsEmpty())
2170 {
2171 SCSIZE nIndex = 0;
2172 if ( nStartRow > 0 )
2173 Search( nStartRow, nIndex );
2174
2175 for ( ; nIndex < mvData.size(); nIndex++ )
2176 {
2177 if ( mvData[nIndex].pPattern->
2178 GetItem(ATTR_MERGE_FLAG).IsHorOverlapped() )
2179 {
2180 bTest = false; // may not be pushed out
2181 break;
2182 }
2183 if ( mvData[nIndex].nEndRow >= nEndRow ) // end of range
2184 break;
2185 }
2186 }
2187 return bTest;
2188}
2189
2190bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
2191{
2192 // if 1st row pushed out is vertically overlapped, summary would be broken
2193
2194 // rDocument.MaxRow() + 1 - nSize = 1st row pushed out
2195
2196 if ( mvData.empty() )
2197 return !rDocument.GetDefPattern()->
2198 GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
2199
2200 SCSIZE nFirstLost = mvData.size()-1;
2201 while ( nFirstLost && mvData[nFirstLost-1].nEndRow >= sal::static_int_cast<SCROW>(rDocument.MaxRow() + 1 - nSize) )
2202 --nFirstLost;
2203
2204 return !mvData[nFirstLost].pPattern->
2205 GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
2206}
2207
2208void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
2209{
2210 SetDefaultIfNotInit();
2211
2212 SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // expand predecessor
2213 SCSIZE nIndex;
2214 Search( nSearch, nIndex );
2215
2216 // set ScMergeAttr may not be extended (so behind delete again)
2217
2218 bool bDoMerge = mvData[nIndex].pPattern->GetItem(ATTR_MERGE).IsMerged();
2219
2220 assert( !bDoMerge || nCol != -1 )(static_cast <bool> (!bDoMerge || nCol != -1) ? void (0
) : __assert_fail ("!bDoMerge || nCol != -1", "/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
, 2220, __extension__ __PRETTY_FUNCTION__))
;
2221
2222 SCSIZE nRemove = 0;
2223 SCSIZE i;
2224 for (i = nIndex; i < mvData.size()-1; i++)
2225 {
2226 SCROW nNew = mvData[i].nEndRow + nSize;
2227 if ( nNew >= rDocument.MaxRow() ) // at end?
2228 {
2229 nNew = rDocument.MaxRow();
2230 if (!nRemove)
2231 nRemove = i+1; // remove the following?
2232 }
2233 mvData[i].nEndRow = nNew;
2234 }
2235
2236 // Remove entries at end ?
2237
2238 if (nRemove && nRemove < mvData.size())
2239 DeleteRange( nRemove, mvData.size()-1 );
2240
2241 if (bDoMerge) // extensively repair (again) ScMergeAttr
2242 {
2243 // ApplyAttr for areas
2244
2245 const SfxPoolItem& rDef = rDocument.GetPool()->GetDefaultItem( ATTR_MERGE );
2246 for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
2247 rDocument.ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
2248
2249 // reply inserts in this area not summarized
2250 }
2251
2252 // Don't duplicate the merge flags in the inserted row.
2253 // #i108488# ScMF::Scenario has to be allowed.
2254 RemoveFlags( nStartRow, nStartRow+nSize-1, ScMF::Hor | ScMF::Ver | ScMF::Auto | ScMF::Button );
2255}
2256
2257void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
2258{
2259 SetDefaultIfNotInit();
2260 bool bFirst=true;
2261 SCSIZE nStartIndex = 0;
2262 SCSIZE nEndIndex = 0;
2263 SCSIZE i;
2264
2265 for ( i = 0; i < mvData.size()-1; i++)
2266 if (mvData[i].nEndRow >= nStartRow && mvData[i].nEndRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
2267 {
2268 if (bFirst)
2269 {
2270 nStartIndex = i;
2271 bFirst = false;
2272 }
2273 nEndIndex = i;
2274 }
2275 if (!bFirst)
2276 {
2277 SCROW nStart;
2278 if (nStartIndex==0)
2279 nStart = 0;
2280 else
2281 nStart = mvData[nStartIndex-1].nEndRow + 1;
2282
2283 if (nStart < nStartRow)
2284 {
2285 mvData[nStartIndex].nEndRow = nStartRow - 1;
2286 ++nStartIndex;
2287 }
2288 if (nEndIndex >= nStartIndex)
2289 {
2290 DeleteRange( nStartIndex, nEndIndex );
2291 if (nStartIndex > 0)
2292 if ( mvData[nStartIndex-1].pPattern == mvData[nStartIndex].pPattern )
2293 DeleteRange( nStartIndex-1, nStartIndex-1 );
2294 }
2295 }
2296 for (i = 0; i < mvData.size()-1; i++)
2297 if (mvData[i].nEndRow >= nStartRow)
2298 mvData[i].nEndRow -= nSize;
2299
2300 // Below does not follow the pattern to detect pressure ranges;
2301 // instead, only remove merge flags.
2302 RemoveFlags( rDocument.MaxRow()-nSize+1, rDocument.MaxRow(), ScMF::Hor | ScMF::Ver | ScMF::Auto );
2303}
2304
2305void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
2306{
2307 SetDefaultIfNotInit();
2308 ScDocumentPool* pDocPool = rDocument.GetPool();
2309 for (SCSIZE i = nStartIndex; i <= nEndIndex; i++)
2310 pDocPool->Remove(*mvData[i].pPattern);
2311
2312 mvData.erase(mvData.begin() + nStartIndex, mvData.begin() + nEndIndex + 1);
2313}
2314
2315void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
2316{
2317 SetDefaultIfNotInit();
2318 if ( nCol != -1 )
2319 RemoveAreaMerge( nStartRow, nEndRow ); // remove from combined flags
2320
2321 if ( !HasAttrib( nStartRow, nEndRow, HasAttrFlags::Overlapped | HasAttrFlags::AutoFilter) )
2322 SetPatternArea( nStartRow, nEndRow, rDocument.GetDefPattern() );
2323 else
2324 SetPatternAreaSafe( nStartRow, nEndRow, rDocument.GetDefPattern(), true ); // leave merge flags
2325}
2326
2327void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
2328{
2329 SetDefaultIfNotInit();
2330 const ScPatternAttr* pDefPattern = rDocument.GetDefPattern();
2331
2332 SCSIZE nIndex;
2333 SCROW nRow;
2334 SCROW nThisRow;
2335
2336 Search( nStartRow, nIndex );
2337 nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
2338 if (nThisRow < nStartRow) nThisRow = nStartRow;
2339
2340 while ( nThisRow <= nEndRow )
2341 {
2342 const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
2343
2344 if ( pOldPattern->GetItemSet().Count() ) // hard attributes ?
2345 {
2346 nRow = mvData[nIndex].nEndRow;
2347 SCROW nAttrRow = std::min( nRow, nEndRow );
2348
2349 auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
2350 SfxItemSet& rSet = pNewPattern->GetItemSet();
2351 for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
2352 if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
2353 rSet.ClearItem(nId);
2354
2355 if ( *pNewPattern == *pDefPattern )
2356 SetPatternArea( nThisRow, nAttrRow, pDefPattern );
2357 else
2358 SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
2359
2360 Search( nThisRow, nIndex ); // data changed
2361 }
2362
2363 ++nIndex;
2364 nThisRow = mvData[nIndex-1].nEndRow+1;
2365 }
2366}
2367
2368/**
2369 * Move within a document
2370 */
2371void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
2372{
2373 SetDefaultIfNotInit();
2374 SCROW nStart = nStartRow;
2375 for (SCSIZE i = 0; i < mvData.size(); i++)
2376 {
2377 if ((mvData[i].nEndRow >= nStartRow) && (i == 0 || mvData[i-1].nEndRow < nEndRow))
2378 {
2379 // copy (bPutToPool=TRUE)
2380 rAttrArray.SetPatternArea( nStart, std::min( mvData[i].nEndRow, nEndRow ),
2381 mvData[i].pPattern, true );
2382 }
2383 nStart = std::max( nStart, mvData[i].nEndRow + 1 );
2384 }
2385 DeleteArea(nStartRow, nEndRow);
2386}
2387
2388/**
2389 * Copy between documents (Clipboard)
2390 */
2391void ScAttrArray::CopyArea(
2392 SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags) const
2393{
2394 nStartRow -= nDy; // Source
2395 nEndRow -= nDy;
2396
2397 SCROW nDestStart = std::max(static_cast<long>(static_cast<long>(nStartRow) + nDy), long(0));
2398 SCROW nDestEnd = std::min(static_cast<long>(static_cast<long>(nEndRow) + nDy), long(rDocument.MaxRow()));
2399
2400 ScDocumentPool* pSourceDocPool = rDocument.GetPool();
2401 ScDocumentPool* pDestDocPool = rAttrArray.rDocument.GetPool();
2402 bool bSamePool = (pSourceDocPool==pDestDocPool);
2403
2404 if ( mvData.empty() )
2405 {
2406 const ScPatternAttr* pNewPattern = &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
2407 rAttrArray.SetPatternArea(nDestStart, nDestEnd, pNewPattern);
2408 return;
2409 }
2410
2411 for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
2412 {
2413 if (mvData[i].nEndRow >= nStartRow)
2414 {
2415 const ScPatternAttr* pOldPattern = mvData[i].pPattern;
2416 const ScPatternAttr* pNewPattern;
2417
2418 if (IsDefaultItem( pOldPattern ))
2419 {
2420 // default: nothing changed
2421
2422 pNewPattern = &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
2423 }
2424 else if ( nStripFlags != ScMF::NONE )
2425 {
2426 std::unique_ptr<ScPatternAttr> pTmpPattern(new ScPatternAttr( *pOldPattern ));
2427 ScMF nNewFlags = ScMF::NONE;
2428 if ( nStripFlags != ScMF::All )
2429 nNewFlags = pTmpPattern->GetItem(ATTR_MERGE_FLAG).GetValue() & ~nStripFlags;
2430
2431 if ( nNewFlags != ScMF::NONE )
2432 pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
2433 else
2434 pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG );
2435
2436 if (bSamePool)
2437 pNewPattern = &pDestDocPool->Put(*pTmpPattern);
2438 else
2439 pNewPattern = pTmpPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
2440 }
2441 else
2442 {
2443 if (bSamePool)
2444 pNewPattern = &pDestDocPool->Put(*pOldPattern);
2445 else
2446 pNewPattern = pOldPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
2447 }
2448
2449 rAttrArray.SetPatternArea(nDestStart,
2450 std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), pNewPattern);
2451 }
2452
2453 // when pasting from clipboard and skipping filtered rows, the adjusted
2454 // end position can be negative
2455 nDestStart = std::max(static_cast<long>(nDestStart), static_cast<long>(mvData[i].nEndRow + nDy + 1));
2456 }
2457}
2458
2459/**
2460 * Leave flags
2461 * summarized with CopyArea
2462 */
2463void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray )
2464{
2465 nStartRow -= nDy; // Source
2466 nEndRow -= nDy;
2467
2468 SCROW nDestStart = std::max(static_cast<long>(static_cast<long>(nStartRow) + nDy), long(0));
2469 SCROW nDestEnd = std::min(static_cast<long>(static_cast<long>(nEndRow) + nDy), long(rDocument.MaxRow()));
2470
2471 if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HasAttrFlags::Overlapped ) )
1
Taking false branch
2472 {
2473 CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
2474 return;
2475 }
2476
2477 ScDocumentPool* pSourceDocPool = rDocument.GetPool();
2478 ScDocumentPool* pDestDocPool = rAttrArray.rDocument.GetPool();
2479 bool bSamePool = (pSourceDocPool==pDestDocPool);
2
Assuming 'pSourceDocPool' is not equal to 'pDestDocPool'
2480
2481 if ( mvData.empty() )
3
Assuming the condition is true
4
Taking true branch
2482 {
2483 const ScPatternAttr* pNewPattern;
2484 if (bSamePool
4.1
'bSamePool' is false
4.1
'bSamePool' is false
)
5
Taking false branch
2485 pNewPattern = &pDestDocPool->Put(*rDocument.GetDefPattern());
2486 else
2487 pNewPattern = rDocument.GetDefPattern()->PutInPool( &rAttrArray.rDocument, &rDocument );
2488
2489 rAttrArray.SetPatternAreaSafe(nDestStart, nDestEnd, pNewPattern, false);
6
Calling 'ScAttrArray::SetPatternAreaSafe'
2490 return;
2491 }
2492
2493
2494 for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
2495 {
2496 if (mvData[i].nEndRow >= nStartRow)
2497 {
2498 const ScPatternAttr* pOldPattern = mvData[i].pPattern;
2499 const ScPatternAttr* pNewPattern;
2500
2501 if (bSamePool)
2502 pNewPattern = &pDestDocPool->Put(*pOldPattern);
2503 else
2504 pNewPattern = pOldPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
2505
2506 rAttrArray.SetPatternAreaSafe(nDestStart,
2507 std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), pNewPattern, false);
2508 }
2509
2510 // when pasting from clipboard and skipping filtered rows, the adjusted
2511 // end position can be negative
2512 nDestStart = std::max(static_cast<long>(nDestStart), static_cast<long>(mvData[i].nEndRow + nDy + 1));
2513 }
2514}
2515
2516SCROW ScAttrArray::SearchStyle(
2517 SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp,
2518 const ScMarkArray* pMarkArray) const
2519{
2520 bool bFound = false;
2521
2522 if (pMarkArray)
2523 {
2524 nRow = pMarkArray->GetNextMarked( nRow, bUp );
2525 if (!rDocument.ValidRow(nRow))
2526 return nRow;
2527 }
2528
2529 if ( mvData.empty() )
2530 {
2531 if (rDocument.GetDefPattern()->GetStyleSheet() == pSearchStyle)
2532 return nRow;
2533
2534 nRow = bUp ? -1 : rDocument.MaxRow() + 1;
2535 return nRow;
2536 }
2537
2538 SCSIZE nIndex;
2539 Search(nRow, nIndex);
2540 const ScPatternAttr* pPattern = mvData[nIndex].pPattern;
2541
2542 while (nIndex < mvData.size() && !bFound)
2543 {
2544 if (pPattern->GetStyleSheet() == pSearchStyle)
2545 {
2546 if (pMarkArray)
2547 {
2548 nRow = pMarkArray->GetNextMarked( nRow, bUp );
2549 SCROW nStart = nIndex ? mvData[nIndex-1].nEndRow+1 : 0;
2550 if (nRow >= nStart && nRow <= mvData[nIndex].nEndRow)
2551 bFound = true;
2552 }
2553 else
2554 bFound = true;
2555 }
2556
2557 if (!bFound)
2558 {
2559 if (bUp)
2560 {
2561 if (nIndex==0)
2562 {
2563 nIndex = mvData.size();
2564 nRow = -1;
2565 }
2566 else
2567 {
2568 --nIndex;
2569 nRow = mvData[nIndex].nEndRow;
2570 pPattern = mvData[nIndex].pPattern;
2571 }
2572 }
2573 else
2574 {
2575 nRow = mvData[nIndex].nEndRow+1;
2576 ++nIndex;
2577 if (nIndex<mvData.size())
2578 pPattern = mvData[nIndex].pPattern;
2579 }
2580 }
2581 }
2582
2583 OSL_ENSURE( bFound || !rDocument.ValidRow(nRow), "Internal failure in ScAttrArray::SearchStyle" )do { if (true && (!(bFound || !rDocument.ValidRow(nRow
)))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"
), ("/home/maarten/src/libreoffice/core/sc/source/core/data/attarray.cxx"
":" "2583" ": "), "%s", "Internal failure in ScAttrArray::SearchStyle"
); } } while (false)
;
2584
2585 return nRow;
2586}
2587
2588bool ScAttrArray::SearchStyleRange(
2589 SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
2590 const ScMarkArray* pMarkArray) const
2591{
2592 SCROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
2593 if (rDocument.ValidRow(nStartRow))
2594 {
2595 if ( mvData.empty() )
2596 {
2597 rRow = nStartRow;
2598 if (bUp)
2599 {
2600 rEndRow = 0;
2601 if (pMarkArray)
2602 {
2603 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
2604 if (nMarkEnd>rEndRow)
2605 rEndRow = nMarkEnd;
2606 }
2607 }
2608 else
2609 {
2610 rEndRow = rDocument.MaxRow();
2611 if (pMarkArray)
2612 {
2613 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
2614 if (nMarkEnd<rEndRow)
2615 rEndRow = nMarkEnd;
2616 }
2617 }
2618
2619 return true;
2620 }
2621
2622 SCSIZE nIndex;
2623 Search(nStartRow,nIndex);
2624
2625 rRow = nStartRow;
2626 if (bUp)
2627 {
2628 if (nIndex>0)
2629 rEndRow = mvData[nIndex-1].nEndRow + 1;
2630 else
2631 rEndRow = 0;
2632 if (pMarkArray)
2633 {
2634 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
2635 if (nMarkEnd>rEndRow)
2636 rEndRow = nMarkEnd;
2637 }
2638 }
2639 else
2640 {
2641 rEndRow = mvData[nIndex].nEndRow;
2642 if (pMarkArray)
2643 {
2644 SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
2645 if (nMarkEnd<rEndRow)
2646 rEndRow = nMarkEnd;
2647 }
2648 }
2649
2650 return true;
2651 }
2652 else
2653 return false;
2654}
2655
2656SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW nEndRow ) const
2657{
2658 if ( mvData.empty() )
2659 return 1;
2660
2661 SCSIZE nIndex1, nIndex2;
2662
2663 if( !Search( nStartRow, nIndex1 ) )
2664 return 0;
2665
2666 if( !Search( nEndRow, nIndex2 ) )
2667 nIndex2 = mvData.size() - 1;
2668
2669 return nIndex2 - nIndex1 + 1;
2670}
2671
2672/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/sc/inc/attarray.hxx

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#ifndef INCLUDED_SC_INC_ATTARRAY_HXX
21#define INCLUDED_SC_INC_ATTARRAY_HXX
22
23#include "global.hxx"
24#include "attrib.hxx"
25#include "patattr.hxx"
26
27#include <algorithm>
28#include <memory>
29
30#include <svl/itemset.hxx>
31
32class ScDocument;
33class ScEditDataArray;
34class ScMarkArray;
35class ScStyleSheet;
36class ScFlatBoolRowSegments;
37
38class SfxItemPoolCache;
39class SfxStyleSheetBase;
40class SvxBoxItem;
41class SvxBoxInfoItem;
42
43namespace editeng { class SvxBorderLine; }
44
45#define SC_LINE_EMPTY0 0
46#define SC_LINE_SET1 1
47#define SC_LINE_DONTCARE2 2
48
49#define SC_ATTRARRAY_DELTA4 4
50
51#define DEBUG_SC_TESTATTRARRAY0 0
52
53struct ScLineFlags
54{
55 sal_uInt8 nLeft;
56 sal_uInt8 nRight;
57 sal_uInt8 nTop;
58 sal_uInt8 nBottom;
59 sal_uInt8 nHori;
60 sal_uInt8 nVert;
61
62 ScLineFlags() : nLeft(SC_LINE_EMPTY0),nRight(SC_LINE_EMPTY0),nTop(SC_LINE_EMPTY0),
63 nBottom(SC_LINE_EMPTY0),nHori(SC_LINE_EMPTY0),nVert(SC_LINE_EMPTY0) {}
64};
65
66struct ScMergePatternState
67{
68 std::unique_ptr<SfxItemSet> pItemSet;
69 const ScPatternAttr* pOld1; ///< existing objects, temporary
70 const ScPatternAttr* pOld2;
71
72 bool mbValidPatternId;
73 sal_uInt64 mnPatternId;
74
75 ScMergePatternState() : pOld1(nullptr), pOld2(nullptr),
76 mbValidPatternId(true), mnPatternId(0) {}
77};
78
79// we store an array of these where the pattern applies to all rows up till nEndRow
80struct ScAttrEntry
81{
82 SCROW nEndRow;
83 const ScPatternAttr* pPattern;
84};
85
86class ScAttrArray
87{
88private:
89 SCCOL nCol;
90 SCTAB nTab;
91 ScDocument& rDocument;
92
93 std::vector<ScAttrEntry> mvData;
94
95friend class ScDocument; // for FillInfo
96friend class ScDocumentIterator;
97friend class ScAttrIterator;
98friend class ScHorizontalAttrIterator;
99
100 bool ApplyFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner,
101 SCROW nStartRow, SCROW nEndRow,
102 bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom );
103
104 void RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow,
105 const ScPatternAttr* pPattern, ScEditDataArray* pDataArray );
106 void SetDefaultIfNotInit( SCSIZE nNeeded = 1 );
107 bool HasAttrib_Impl(const ScPatternAttr* pPattern, HasAttrFlags nMask, SCROW nRow1, SCROW nRow2, SCSIZE i) const;
108
109 ScAttrArray(const ScAttrArray&) = delete;
110 ScAttrArray& operator=(const ScAttrArray&) = delete;
111
112public:
113 ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, ScAttrArray* pNextColAttrArray );
114 ~ScAttrArray();
115
116 ScDocument& GetDoc() { return rDocument; }
117 void SetTab(SCTAB nNewTab) { nTab = nNewTab; }
118 void SetCol(SCCOL nNewCol) { nCol = nNewCol; }
119#if DEBUG_SC_TESTATTRARRAY0
120 void TestData() const;
121#endif
122 void Reset( const ScPatternAttr* pPattern);
123 bool Concat(SCSIZE nPos);
124
125 const ScPatternAttr* GetPattern( SCROW nRow ) const;
126
127 /** Returns if you search for attributes at nRow the range from rStartRow
128 to rEndRow where that attribute combination (ScPatternAttr) is applied.
129 The next ScPatternAttr different from this one starts at rEndRow+1
130 (if that is <= MAXROW).
131 */
132 const ScPatternAttr* GetPatternRange( SCROW& rStartRow, SCROW& rEndRow, SCROW nRow ) const;
133
134 void MergePatternArea( SCROW nStartRow, SCROW nEndRow, ScMergePatternState& rState, bool bDeep ) const;
135
136 void MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, ScLineFlags& rFlags,
137 SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight ) const;
138 void ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
139 SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight);
140
141 void SetPattern( SCROW nRow, const ScPatternAttr* pPattern, bool bPutToPool = false )
142 { SetPatternAreaImpl(nRow, nRow, pPattern, bPutToPool, nullptr, /*bPassingOwnership*/false); }
143 const ScPatternAttr* SetPattern( SCROW nRow, std::unique_ptr<ScPatternAttr> pPattern, bool bPutToPool = false )
144 { return SetPatternAreaImpl(nRow, nRow, pPattern.release(), bPutToPool, nullptr, /*bPassingOwnership*/true); }
145 void SetPatternArea( SCROW nStartRow, SCROW nEndRow, std::unique_ptr<ScPatternAttr> pPattern,
146 bool bPutToPool = false, ScEditDataArray* pDataArray = nullptr)
147 { SetPatternAreaImpl(nStartRow, nEndRow, pPattern.release(), bPutToPool, pDataArray, /*bPassingOwnership*/true); }
17
Potential leak of memory pointed to by 'pPattern._M_t._M_t._M_head_impl'
148 void SetPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr* pPattern,
149 bool bPutToPool = false, ScEditDataArray* pDataArray = nullptr)
150 { SetPatternAreaImpl(nStartRow, nEndRow, pPattern, bPutToPool, pDataArray, /*bPassingOwnership*/false); }
151 void ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle );
152 void ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache,
153 ScEditDataArray* pDataArray = nullptr, bool* const pIsChanged = nullptr );
154 void SetAttrEntries(std::vector<ScAttrEntry> && vNewData);
155 void ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
156 const ::editeng::SvxBorderLine* pLine, bool bColorOnly );
157
158 void AddCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex );
159 /// if nIndex == 0, remove all conditional format data
160 void RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex );
161
162 void ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich );
163 void ChangeIndent( SCROW nStartRow, SCROW nEndRow, bool bIncrement );
164
165 /// Including current, may return -1
166 SCROW GetNextUnprotected( SCROW nRow, bool bUp ) const;
167
168 /// May return -1 if not found
169 SCROW SearchStyle(
170 SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp,
171 const ScMarkArray* pMarkArray = nullptr) const;
172
173 bool SearchStyleRange(
174 SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
175 const ScMarkArray* pMarkArray = nullptr) const;
176
177 bool ApplyFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags );
178 bool RemoveFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags );
179
180 bool Search( SCROW nRow, SCSIZE& nIndex ) const;
181
182 bool HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) const;
183 bool IsMerged( SCROW nRow ) const;
184 bool ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
185 SCCOL& rPaintCol, SCROW& rPaintRow,
186 bool bRefresh );
187 void RemoveAreaMerge( SCROW nStartRow, SCROW nEndRow );
188
189 void FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset );
190 bool IsStyleSheetUsed( const ScStyleSheet& rStyle ) const;
191
192 void SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow,
193 const ScPatternAttr* pWantedPattern, bool bDefault );
194 void CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray );
195
196 bool IsEmpty() const;
197
198 bool GetFirstVisibleAttr( SCROW& rFirstRow ) const;
199 bool GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const;
200 bool HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const;
201 bool IsVisibleEqual( const ScAttrArray& rOther,
202 SCROW nStartRow, SCROW nEndRow ) const;
203 bool IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const;
204
205 bool TestInsertCol( SCROW nStartRow, SCROW nEndRow) const;
206 bool TestInsertRow( SCSIZE nSize ) const;
207 void InsertRow( SCROW nStartRow, SCSIZE nSize );
208 void DeleteRow( SCROW nStartRow, SCSIZE nSize );
209 void DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex );
210 void DeleteArea( SCROW nStartRow, SCROW nEndRow );
211 void MoveTo( SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray );
212 void CopyArea(
213 SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags = ScMF::NONE) const;
214
215 void DeleteHardAttr( SCROW nStartRow, SCROW nEndRow );
216
217 /* i123909: Pre-calculate needed memory, and pre-reserve enough memory */
218 bool Reserve( SCSIZE nReserve );
219 SCSIZE Count() const { return mvData.size(); }
220 SCSIZE Count( SCROW nRow1, SCROW nRow2 ) const;
221
222private:
223 const ScPatternAttr* SetPatternAreaImpl( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr* pPattern,
224 bool bPutToPool = false, ScEditDataArray* pDataArray = nullptr,
225 bool bPassingPatternOwnership = false );
226};
227
228// Iterator for attributes
229
230class ScAttrIterator
231{
232 const ScAttrArray* pArray;
233 const ScPatternAttr* pDefPattern;
234 SCSIZE nPos;
235 SCROW nRow;
236 SCROW nEndRow;
237public:
238 inline ScAttrIterator( const ScAttrArray* pNewArray, SCROW nStart, SCROW nEnd, const ScPatternAttr* pDefaultPattern );
239 inline const ScPatternAttr* Next( SCROW& rTop, SCROW& rBottom );
240 inline const ScPatternAttr* Resync( SCROW nRow, SCROW& rTop, SCROW& rBottom );
241 SCROW GetNextRow() const { return nRow; }
242};
243
244inline ScAttrIterator::ScAttrIterator( const ScAttrArray* pNewArray, SCROW nStart, SCROW nEnd, const ScPatternAttr* pDefaultPattern ) :
245 pArray( pNewArray ),
246 pDefPattern( pDefaultPattern ),
247 nRow( nStart ),
248 nEndRow( nEnd )
249{
250 if ( pArray->Count() )
251 {
252 if ( nStart > 0 )
253 pArray->Search( nStart, nPos );
254 else
255 nPos = 0;
256 }
257 else
258 nPos = 0;
259}
260
261inline const ScPatternAttr* ScAttrIterator::Next( SCROW& rTop, SCROW& rBottom )
262{
263 const ScPatternAttr* pRet;
264 if ( !pArray->Count() )
265 {
266 if ( !nPos )
267 {
268 ++nPos;
269 if ( nRow > MAXROW )
270 return nullptr;
271 rTop = nRow;
272 rBottom = std::min( nEndRow, MAXROW );
273 nRow = rBottom + 1;
274 return pDefPattern;
275 }
276 return nullptr;
277 }
278
279 if ( nPos < pArray->Count() && nRow <= nEndRow )
280 {
281 rTop = nRow;
282 rBottom = std::min( pArray->mvData[nPos].nEndRow, nEndRow );
283 pRet = pArray->mvData[nPos].pPattern;
284 nRow = rBottom + 1;
285 ++nPos;
286 }
287 else
288 pRet = nullptr;
289 return pRet;
290}
291
292inline const ScPatternAttr* ScAttrIterator::Resync( SCROW nRowP, SCROW& rTop, SCROW& rBottom )
293{
294 nRow = nRowP;
295 if ( !pArray->Count() )
296 {
297 nPos = 0;
298 return Next( rTop, rBottom );
299 }
300 // Chances are high that the pattern changed on nRowP introduced a span
301 // starting right there. Assume that Next() was called so nPos already
302 // advanced. Another high chance is that the change extended a previous or
303 // next pattern. In all these cases we don't need to search.
304 if (3 <= nPos && nPos <= pArray->Count() && pArray->mvData[nPos-3].nEndRow < nRowP &&
305 nRowP <= pArray->mvData[nPos-2].nEndRow)
306 nPos -= 2;
307 else if (2 <= nPos && nPos <= pArray->Count() && pArray->mvData[nPos-2].nEndRow < nRowP &&
308 nRowP <= pArray->mvData[nPos-1].nEndRow)
309 --nPos;
310 else if (pArray->Count() > 0 && nRowP <= pArray->mvData[0].nEndRow)
311 nPos = 0;
312 else
313 pArray->Search( nRowP, nPos );
314 return Next( rTop, rBottom);
315}
316
317#endif
318
319/* vim:set shiftwidth=4 softtabstop=4 expandtab: */