1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <scitems.hxx>
21 #include <comphelper/string.hxx>
22 #include <editeng/boxitem.hxx>
23 #include <editeng/editeng.hxx>
24 #include <editeng/eeitem.hxx>
25 #include <editeng/escapementitem.hxx>
26 #include <editeng/brushitem.hxx>
27 #include <svl/numformat.hxx>
28 #include <svl/zforlist.hxx>
29 #include <vcl/keycodes.hxx>
30 #include <rtl/math.hxx>
31 #include <unotools/charclass.hxx>
32 #include <tools/duration.hxx>
33 #include <osl/diagnose.h>
36 #include <patattr.hxx>
37 #include <formulacell.hxx>
40 #include <document.hxx>
41 #include <autoform.hxx>
42 #include <userlist.hxx>
43 #include <zforauto.hxx>
44 #include <subtotal.hxx>
45 #include <formula/errorcodes.hxx>
46 #include <docpool.hxx>
47 #include <progress.hxx>
48 #include <conditio.hxx>
49 #include <editutil.hxx>
50 #include <listenercontext.hxx>
51 #include <scopetools.hxx>
52 #include <o3tl/string_view.hxx>
57 #include <string_view>
59 #define D_MAX_LONG_ double(0x7fffffff)
63 short lcl_DecompValueString( OUString
& rValue
, sal_Int32
& nVal
, sal_uInt16
* pMinDigits
= nullptr )
65 if ( rValue
.isEmpty() )
70 const sal_Unicode
* p
= rValue
.getStr();
73 if ( p
[nNum
] == '-' || p
[nNum
] == '+' )
75 while ( p
[nNum
] && CharClass::isAsciiNumeric( std::u16string_view(&p
[nNum
], 1) ) )
78 sal_Unicode cNext
= p
[nNum
]; // 0 if at the end
79 sal_Unicode cLast
= p
[rValue
.getLength()-1];
81 // #i5550# If there are numbers at the beginning and the end,
82 // prefer the one at the beginning only if it's followed by a space.
83 // Otherwise, use the number at the end, to enable things like IP addresses.
84 if ( nNum
> nSign
&& ( cNext
== 0 || cNext
== ' ' || !CharClass::isAsciiNumeric(std::u16string_view(&cLast
, 1)) ) )
85 { // number at the beginning
86 nVal
= o3tl::toInt32(rValue
.subView( 0, nNum
));
87 // any number with a leading zero sets the minimum number of digits
88 if ( p
[nSign
] == '0' && pMinDigits
&& ( nNum
- nSign
> *pMinDigits
) )
89 *pMinDigits
= nNum
- nSign
;
90 rValue
= rValue
.copy(nNum
);
96 sal_Int32 nEnd
= nNum
= rValue
.getLength() - 1;
97 while ( nNum
&& CharClass::isAsciiNumeric( std::u16string_view(&p
[nNum
], 1) ) )
99 if ( p
[nNum
] == '-' || p
[nNum
] == '+' )
104 if ( nNum
< nEnd
- nSign
)
105 { // number at the end
106 nVal
= o3tl::toInt32(rValue
.subView( nNum
+ 1 ));
107 // any number with a leading zero sets the minimum number of digits
108 if ( p
[nNum
+1+nSign
] == '0' && pMinDigits
&& ( nEnd
- nNum
- nSign
> *pMinDigits
) )
109 *pMinDigits
= nEnd
- nNum
- nSign
;
110 rValue
= rValue
.copy(0, nNum
+ 1);
111 if (nSign
) // use the return value = 2 to put back the '+'
121 OUString
lcl_ValueString( sal_Int32 nValue
, sal_uInt16 nMinDigits
)
123 if ( nMinDigits
<= 1 )
124 return OUString::number( nValue
); // simple case...
127 OUString aStr
= OUString::number( std::abs( nValue
) );
128 if ( aStr
.getLength() < nMinDigits
)
130 OUStringBuffer
aZero(nMinDigits
);
131 comphelper::string::padToLength(aZero
, nMinDigits
- aStr
.getLength(), '0');
132 aStr
= aZero
.append(aStr
).makeStringAndClear();
134 // nMinDigits doesn't include the '-' sign -> add after inserting zeros
142 ScColumn
& rColumn
, SCROW nRow
, sal_Int32 nValue
, sal_uInt16 nDigits
,
143 std::u16string_view rSuffix
,
144 CellType eCellType
, bool bIsOrdinalSuffix
)
146 ScDocument
& rDoc
= rColumn
.GetDoc();
147 OUString aValue
= lcl_ValueString(nValue
, nDigits
);
148 if (!bIsOrdinalSuffix
)
151 rColumn
.SetRawString(nRow
, aValue
);
155 OUString aOrdinalSuffix
= ScGlobal::GetOrdinalSuffix(nValue
);
156 if (eCellType
!= CELLTYPE_EDIT
)
158 aValue
+= aOrdinalSuffix
;
159 rColumn
.SetRawString(nRow
, aValue
);
163 EditEngine
aEngine(rDoc
.GetEnginePool());
164 aEngine
.SetEditTextObjectPool(rDoc
.GetEditPool());
166 SfxItemSet aAttr
= aEngine
.GetEmptyItemSet();
167 aAttr
.Put( SvxEscapementItem( SvxEscapement::Superscript
, EE_CHAR_ESCAPEMENT
));
168 aEngine
.SetText( aValue
);
169 aEngine
.QuickInsertText(
171 ESelection(0, aValue
.getLength(), 0, aValue
.getLength() + aOrdinalSuffix
.getLength()));
173 aEngine
.QuickSetAttribs(
175 ESelection(0, aValue
.getLength(), 0, aValue
.getLength() + aOrdinalSuffix
.getLength()));
177 // Text object instance will be owned by the cell.
178 rColumn
.SetEditText(nRow
, aEngine
.CreateTextObject());
184 /* TODO: move this to rtl::math::approxDiff() ? Though the name is funny, the
185 * approx is expected to be more correct than the raw diff. */
186 /** Calculate a-b trying to diminish precision errors such as for 0.11-0.12
187 not return -0.009999999999999995 but -0.01 instead.
189 double approxDiff( double a
, double b
)
197 const double c
= a
- b
;
198 const double aa
= fabs(a
);
199 const double ab
= fabs(b
);
200 if (aa
< 1e-16 || aa
> 1e+16 || ab
< 1e-16 || ab
> 1e+16)
201 // This is going nowhere, live with the result.
204 const double q
= aa
< ab
? b
/ a
: a
/ b
;
205 const double d
= (a
* q
- b
* q
) / q
;
207 // No differing error, live with the result.
210 // We now have two subtractions with a similar but not equal error. Obtain
211 // the exponent of the error magnitude and round accordingly.
212 const double e
= fabs(d
- c
);
213 const int nExp
= static_cast<int>(floor(log10(e
))) + 1;
214 // tdf#129606: Limit precision to the 16th significant digit of the least precise argument.
215 // Cf. mnMaxGeneralPrecision in sc/source/core/data/column3.cxx.
216 const int nExpArg
= static_cast<int>(floor(log10(std::max(aa
, ab
)))) - 15;
217 return rtl::math::round(c
, -std::max(nExp
, nExpArg
));
220 double approxTypedDiff( double a
, double b
, bool bTime
, tools::Duration
& rDuration
)
224 rDuration
= tools::Duration(a
- b
);
225 return rDuration
.GetInDays();
227 return approxDiff( a
, b
);
231 void ScTable::FillAnalyse( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
232 FillCmd
& rCmd
, FillDateCmd
& rDateCmd
,
233 double& rInc
, tools::Duration
& rDuration
, sal_uInt16
& rMinDigits
,
234 ScUserListData
*& rListData
, sal_uInt16
& rListIndex
,
235 bool bHasFiltered
, bool& rSkipOverlappedCells
,
236 std::vector
<sal_Int32
>& rNonOverlappedCellIdx
)
238 OSL_ENSURE( nCol1
==nCol2
|| nRow1
==nRow2
, "FillAnalyse: invalid range" );
241 rDuration
= tools::Duration();
245 rSkipOverlappedCells
= false;
246 if ( nScFillModeMouseModifier
& KEY_MOD1
)
247 return ; // Ctrl-key: Copy
256 nCount
= static_cast<SCSIZE
>(nRow2
- nRow1
+ 1);
262 nCount
= static_cast<SCSIZE
>(nCol2
- nCol1
+ 1);
265 // Try to analyse the merged cells only if there are no filtered rows in the destination area
266 // Else fallback to the old way to avoid regression.
267 // Filling merged cells into an area with filtered (hidden) rows, is a very complex task
268 // that is not implemented, but not even decided how to do, even excel can't handle that well
271 bool bHasOverlappedCells
= false;
272 bool bSkipOverlappedCells
= true;
273 SCCOL nColCurr
= nCol1
;
274 SCROW nRowCurr
= nRow1
;
276 // collect cells that are not empty or not overlapped
277 rNonOverlappedCellIdx
.resize(nCount
);
278 SCSIZE nValueCount
= 0;
279 for (SCSIZE i
= 0; i
< nCount
; ++i
)
281 const ScPatternAttr
* pPattern
= GetPattern(nColCurr
, nRowCurr
);
282 const ScMergeFlagAttr
* pMergeFlagItem
= nullptr;
284 = pPattern
->GetItemSet().GetItemState(ATTR_MERGE_FLAG
, false, &pMergeFlagItem
) == SfxItemState::SET
285 && pMergeFlagItem
->IsOverlapped();
288 bHasOverlappedCells
= true;
290 if (!bOverlapped
|| GetCellValue(nColCurr
, nRowCurr
).getType() != CELLTYPE_NONE
)
292 rNonOverlappedCellIdx
[nValueCount
++] = i
;
293 // if there is at least 1 non empty overlapped cell, then no cell should be skipped
295 bSkipOverlappedCells
= false;
301 rNonOverlappedCellIdx
.resize(nValueCount
);
303 // if all the values are overlapped CELLTYPE_NONE, then there is no need to analyse it.
304 if (nValueCount
== 0)
307 // if there is no overlapped cells, there is nothing to skip
308 if (!bHasOverlappedCells
)
309 bSkipOverlappedCells
= false;
311 if (bSkipOverlappedCells
)
313 nColCurr
= nCol1
+ rNonOverlappedCellIdx
[0] * nAddX
;
314 nRowCurr
= nRow1
+ rNonOverlappedCellIdx
[0] * nAddY
;
315 ScRefCellValue aPrevCell
, aCurrCell
;
316 aCurrCell
= GetCellValue(nColCurr
, nRowCurr
);
317 CellType eCellType
= aCurrCell
.getType();
318 if (eCellType
== CELLTYPE_VALUE
)
322 SvNumFormatType nCurrCellFormatType
323 = rDocument
.GetFormatTable()->GetType(GetNumberFormat(nColCurr
, nRowCurr
));
324 if (nCurrCellFormatType
== SvNumFormatType::DATE
)
326 if (nValueCount
>= 2)
328 tools::Long nCmpInc
= 0;
329 FillDateCmd eType
= FILL_YEAR
; // just some temporary default values
330 tools::Long nDDiff
= 0, nMDiff
= 0, nYDiff
= 0; // to avoid warnings
331 Date aNullDate
= rDocument
.GetFormatTable()->GetNullDate();
332 Date aCurrDate
= aNullDate
, aPrevDate
= aNullDate
;
333 aCurrDate
.AddDays(aCurrCell
.getDouble());
334 for (SCSIZE i
= 1; i
< nValueCount
&& bVal
; i
++)
336 aPrevCell
= aCurrCell
;
337 aPrevDate
= aCurrDate
;
338 nColCurr
= nCol1
+ rNonOverlappedCellIdx
[i
] * nAddX
;
339 nRowCurr
= nRow1
+ rNonOverlappedCellIdx
[i
] * nAddY
;
340 aCurrCell
= GetCellValue(nColCurr
, nRowCurr
);
341 if (aCurrCell
.getType() == CELLTYPE_VALUE
)
343 aCurrDate
= aNullDate
+ static_cast<sal_Int32
>(aCurrCell
.getDouble());
344 if (eType
!= FILL_DAY
) {
345 nDDiff
= aCurrDate
.GetDay()
346 - static_cast<tools::Long
>(aPrevDate
.GetDay());
347 nMDiff
= aCurrDate
.GetMonth()
348 - static_cast<tools::Long
>(aPrevDate
.GetMonth());
349 nYDiff
= aCurrDate
.GetYear()
350 - static_cast<tools::Long
>(aPrevDate
.GetYear());
357 nCmpInc
= aCurrDate
- aPrevDate
;
362 nCmpInc
= nMDiff
+ 12 * nYDiff
;
365 else if (eType
== FILL_DAY
)
367 if (aCurrDate
- aPrevDate
!= nCmpInc
)
372 if (nDDiff
|| (nMDiff
+ 12 * nYDiff
!= nCmpInc
))
377 bVal
= false; // No date is also not ok
381 if (eType
== FILL_MONTH
&& (nCmpInc
% 12 == 0))
389 rSkipOverlappedCells
= true;
398 rSkipOverlappedCells
= true;
402 else if (nCurrCellFormatType
== SvNumFormatType::LOGICAL
403 && ((fVal
= aCurrCell
.getDouble()) == 0.0 || fVal
== 1.0))
406 else if (nValueCount
>= 2)
408 tools::Duration aDuration
;
409 for (SCSIZE i
= 1; i
< nValueCount
&& bVal
; i
++)
411 aPrevCell
= aCurrCell
;
412 nColCurr
= nCol1
+ rNonOverlappedCellIdx
[i
] * nAddX
;
413 nRowCurr
= nRow1
+ rNonOverlappedCellIdx
[i
] * nAddY
;
414 aCurrCell
= GetCellValue(nColCurr
, nRowCurr
);
415 if (aCurrCell
.getType() == CELLTYPE_VALUE
)
417 const bool bTime
= (nCurrCellFormatType
== SvNumFormatType::TIME
||
418 nCurrCellFormatType
== SvNumFormatType::DATETIME
);
419 double nDiff
= approxTypedDiff(aCurrCell
.getDouble(), aPrevCell
.getDouble(),
425 rDuration
= aDuration
;
427 if (!::rtl::math::approxEqual(nDiff
, rInc
, 13))
429 else if ((aCurrCell
.getDouble() == 0.0 || aCurrCell
.getDouble() == 1.0)
430 && (rDocument
.GetFormatTable()->GetType(
431 GetNumberFormat(nColCurr
, nRowCurr
))
432 == SvNumFormatType::LOGICAL
))
441 rSkipOverlappedCells
= true;
446 else if (eCellType
== CELLTYPE_STRING
|| eCellType
== CELLTYPE_EDIT
)
448 OUString aStr
= GetString(nColCurr
, nRowCurr
);
451 rListData
= const_cast<ScUserListData
*>(ScGlobal::GetUserList().GetData(aStr
));
454 bool bMatchCase
= false;
455 (void)rListData
->GetSubIndex(aStr
, rListIndex
, bMatchCase
);
456 size_t nListStrCount
= rListData
->GetSubCount();
457 sal_uInt16 nPrevListIndex
, nInc
= 1;
458 for (SCSIZE i
= 1; i
< nValueCount
&& rListData
; i
++)
460 nColCurr
= nCol1
+ rNonOverlappedCellIdx
[i
] * nAddX
;
461 nRowCurr
= nRow1
+ rNonOverlappedCellIdx
[i
] * nAddY
;
462 aStr2
= GetString(nColCurr
, nRowCurr
);
464 nPrevListIndex
= rListIndex
;
465 if (!rListData
->GetSubIndex(aStr2
, rListIndex
, bMatchCase
))
469 sal_Int32 nIncCurr
= rListIndex
- nPrevListIndex
;
471 nIncCurr
+= nListStrCount
;
474 else if (nInc
!= nIncCurr
)
480 rSkipOverlappedCells
= true;
484 short nFlag1
, nFlag2
;
485 sal_Int32 nVal1
, nVal2
;
486 nFlag1
= lcl_DecompValueString(aStr
, nVal1
, &rMinDigits
);
491 for (SCSIZE i
= 1; i
< nValueCount
&& bVal
; i
++)
493 nColCurr
= nCol1
+ rNonOverlappedCellIdx
[i
] * nAddX
;
494 nRowCurr
= nRow1
+ rNonOverlappedCellIdx
[i
] * nAddY
;
495 ScRefCellValue aCell
= GetCellValue(nColCurr
, nRowCurr
);
496 CellType eType
= aCell
.getType();
497 if (eType
== CELLTYPE_STRING
|| eType
== CELLTYPE_EDIT
)
499 aStr2
= aCell
.getString(&rDocument
);
500 nFlag2
= lcl_DecompValueString(aStr2
, nVal2
, &rMinDigits
);
501 if (nFlag1
== nFlag2
&& aStr
== aStr2
)
503 double nDiff
= approxDiff(nVal2
, nVal1
);
506 else if (!::rtl::math::approxEqual(nDiff
, rInc
, 13))
519 rSkipOverlappedCells
= true;
527 //if it is not a FILL_LINEAR - CELLTYPE_VALUE - with merged cells [without hidden values]
528 //then do it in the old way
533 ScRefCellValue aFirstCell
= GetCellValue(nCol
, nRow
);
534 CellType eCellType
= aFirstCell
.getType();
536 if (eCellType
== CELLTYPE_VALUE
)
539 sal_uInt32 nFormat
= GetAttr(nCol
,nRow
,ATTR_VALUE_FORMAT
)->GetValue();
540 const SvNumFormatType nFormatType
= rDocument
.GetFormatTable()->GetType(nFormat
);
541 bool bDate
= (nFormatType
== SvNumFormatType::DATE
); // date without time
542 bool bTime
= (nFormatType
== SvNumFormatType::TIME
|| nFormatType
== SvNumFormatType::DATETIME
);
543 bool bBooleanCell
= (nFormatType
== SvNumFormatType::LOGICAL
);
549 Date aNullDate
= rDocument
.GetFormatTable()->GetNullDate();
550 Date aDate1
= aNullDate
;
551 nVal
= aFirstCell
.getDouble();
552 aDate1
.AddDays(nVal
);
553 Date aDate2
= aNullDate
;
554 nVal
= GetValue(nCol
+nAddX
, nRow
+nAddY
);
555 aDate2
.AddDays(nVal
);
556 if ( aDate1
!= aDate2
)
558 tools::Long nCmpInc
= 0;
560 tools::Long nDDiff
= aDate2
.GetDay() - static_cast<tools::Long
>(aDate1
.GetDay());
561 tools::Long nMDiff
= aDate2
.GetMonth() - static_cast<tools::Long
>(aDate1
.GetMonth());
562 tools::Long nYDiff
= aDate2
.GetYear() - static_cast<tools::Long
>(aDate1
.GetYear());
563 if (nMDiff
&& aDate1
.IsEndOfMonth() && aDate2
.IsEndOfMonth())
565 eType
= FILL_END_OF_MONTH
;
566 nCmpInc
= nMDiff
+ 12 * nYDiff
;
571 nCmpInc
= aDate2
- aDate1
;
576 nCmpInc
= nMDiff
+ 12 * nYDiff
;
579 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
580 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
582 for (SCSIZE i
=1; i
<nCount
&& bVal
; i
++)
584 ScRefCellValue aCell
= GetCellValue(nCol
,nRow
);
585 if (aCell
.getType() == CELLTYPE_VALUE
)
587 nVal
= aCell
.getDouble();
588 aDate2
= aNullDate
+ static_cast<sal_Int32
>(nVal
);
589 if ( eType
== FILL_DAY
)
591 if ( aDate2
-aDate1
!= nCmpInc
)
596 nDDiff
= aDate2
.GetDay() - static_cast<tools::Long
>(aDate1
.GetDay());
597 nMDiff
= aDate2
.GetMonth() - static_cast<tools::Long
>(aDate1
.GetMonth());
598 nYDiff
= aDate2
.GetYear() - static_cast<tools::Long
>(aDate1
.GetYear());
599 if ((nDDiff
&& !aDate1
.IsEndOfMonth() && !aDate2
.IsEndOfMonth())
600 || (nMDiff
+ 12 * nYDiff
!= nCmpInc
))
604 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
605 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
608 bVal
= false; // No date is also not ok
612 if ((eType
== FILL_MONTH
|| eType
== FILL_END_OF_MONTH
)
613 && (nCmpInc
% 12 == 0))
625 // tdf#89754 - don't increment non different consecutive date cells
631 else // single date -> increment by days
638 else if (bBooleanCell
&& ((fVal
= aFirstCell
.getDouble()) == 0.0 || fVal
== 1.0))
640 // Nothing, rInc stays 0.0, no specific fill mode.
646 tools::Duration aDuration
;
647 double nVal1
= aFirstCell
.getDouble();
648 double nVal2
= GetValue(nCol
+nAddX
, nRow
+nAddY
);
649 rInc
= approxTypedDiff( nVal2
, nVal1
, bTime
, aDuration
);
650 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
651 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
653 for (SCSIZE i
=1; i
<nCount
&& bVal
; i
++)
655 ScRefCellValue aCell
= GetCellValue(nCol
,nRow
);
656 if (aCell
.getType() == CELLTYPE_VALUE
)
658 nVal2
= aCell
.getDouble();
659 double nDiff
= approxTypedDiff( nVal2
, nVal1
, bTime
, aDuration
);
660 if ( !::rtl::math::approxEqual( nDiff
, rInc
, 13 ) )
662 else if ((nVal2
== 0.0 || nVal2
== 1.0) &&
663 (rDocument
.GetFormatTable()->GetType(GetNumberFormat(nCol
,nRow
)) ==
664 SvNumFormatType::LOGICAL
))
670 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
671 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
673 rDuration
= aDuration
;
678 else if(nFormatType
== SvNumFormatType::PERCENT
)
680 rInc
= 0.01; // tdf#89998 increment by 1% at a time
684 else if (eCellType
== CELLTYPE_STRING
|| eCellType
== CELLTYPE_EDIT
)
686 OUString aStr
= GetString(nCol
, nRow
);
688 rListData
= const_cast<ScUserListData
*>(ScGlobal::GetUserList().GetData(aStr
));
691 bool bMatchCase
= false;
692 (void)rListData
->GetSubIndex(aStr
, rListIndex
, bMatchCase
);
693 size_t nListStrCount
= rListData
->GetSubCount();
694 sal_uInt16 nPrevListIndex
, nInc
= 1;
695 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
696 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
697 for (SCSIZE i
=1; i
<nCount
&& rListData
; i
++)
699 nPrevListIndex
= rListIndex
;
700 aStr
= GetString(nCol
, nRow
);
701 if (!rListData
->GetSubIndex(aStr
, rListIndex
, bMatchCase
))
705 sal_Int32 nIncCurr
= rListIndex
- nPrevListIndex
;
707 nIncCurr
+= nListStrCount
;
710 else if (nInc
!= nIncCurr
)
713 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
714 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
719 else if ( nCount
> 1 )
721 // pass rMinDigits to all DecompValueString calls
722 // -> longest number defines rMinDigits
725 short nFlag1
= lcl_DecompValueString( aStr
, nVal1
, &rMinDigits
);
729 aStr
= GetString( nCol
+nAddX
, nRow
+nAddY
);
730 short nFlag2
= lcl_DecompValueString( aStr
, nVal2
, &rMinDigits
);
731 if ( nFlag1
== nFlag2
)
733 rInc
= approxDiff( nVal2
, nVal1
);
734 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
735 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
737 for (SCSIZE i
=1; i
<nCount
&& bVal
; i
++)
739 ScRefCellValue aCell
= GetCellValue(nCol
, nRow
);
740 CellType eType
= aCell
.getType();
741 if ( eType
== CELLTYPE_STRING
|| eType
== CELLTYPE_EDIT
)
743 aStr
= aCell
.getString(&rDocument
);
744 nFlag2
= lcl_DecompValueString( aStr
, nVal2
, &rMinDigits
);
745 if ( nFlag1
== nFlag2
)
747 double nDiff
= approxDiff( nVal2
, nVal1
);
748 if ( !::rtl::math::approxEqual( nDiff
, rInc
, 13 ) )
757 nCol
= sal::static_int_cast
<SCCOL
>( nCol
+ nAddX
);
758 nRow
= sal::static_int_cast
<SCROW
>( nRow
+ nAddY
);
767 // call DecompValueString to set rMinDigits
769 lcl_DecompValueString( aStr
, nDummy
, &rMinDigits
);
774 void ScTable::FillFormula(
775 const ScFormulaCell
* pSrcCell
, SCCOL nDestCol
, SCROW nDestRow
, bool bLast
)
778 rDocument
.SetNoListening( true ); // still the wrong reference
779 ScAddress
aAddr( nDestCol
, nDestRow
, nTab
);
780 ScFormulaCell
* pDestCell
= new ScFormulaCell( *pSrcCell
, rDocument
, aAddr
);
781 aCol
[nDestCol
].SetFormulaCell(nDestRow
, pDestCell
);
783 if ( bLast
&& pDestCell
->GetMatrixFlag() != ScMatrixMode::NONE
)
786 if ( pDestCell
->GetMatrixOrigin( GetDoc(), aOrg
) )
788 if ( nDestCol
>= aOrg
.Col() && nDestRow
>= aOrg
.Row() )
790 ScFormulaCell
* pOrgCell
= rDocument
.GetFormulaCell(aOrg
);
791 if (pOrgCell
&& pOrgCell
->GetMatrixFlag() == ScMatrixMode::Formula
)
793 pOrgCell
->SetMatColsRows(
794 nDestCol
- aOrg
.Col() + 1,
795 nDestRow
- aOrg
.Row() + 1 );
799 OSL_FAIL( "FillFormula: MatrixOrigin no formula cell with ScMatrixMode::Formula" );
804 OSL_FAIL( "FillFormula: MatrixOrigin bottom right" );
809 OSL_FAIL( "FillFormula: no MatrixOrigin" );
812 rDocument
.SetNoListening( false );
813 pDestCell
->StartListeningTo( rDocument
);
816 void ScTable::FillAuto( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
817 sal_uInt64 nFillCount
, FillDir eFillDir
, ScProgress
* pProgress
)
819 if ( (nFillCount
== 0) || !ValidColRow(nCol1
, nRow1
) || !ValidColRow(nCol2
, nRow2
) )
824 bool bVertical
= (eFillDir
== FILL_TO_BOTTOM
|| eFillDir
== FILL_TO_TOP
);
825 bool bPositive
= (eFillDir
== FILL_TO_BOTTOM
|| eFillDir
== FILL_TO_RIGHT
);
829 SCCOLROW
& rInner
= bVertical
? nRow
: nCol
; // loop variables
830 SCCOLROW
& rOuter
= bVertical
? nCol
: nRow
;
848 nIEnd
= nRow2
+ nFillCount
;
849 aFillRange
= ScRange(nCol1
, nRow2
+1, 0, nCol2
, nRow2
+ nFillCount
, 0);
856 nIEnd
= nRow1
- nFillCount
;
857 aFillRange
= ScRange(nCol1
, nRow1
-1, 0, nCol2
, nRow2
- nFillCount
, 0);
869 nIEnd
= nCol2
+ nFillCount
;
870 aFillRange
= ScRange(nCol2
+ 1, nRow1
, 0, nCol2
+ nFillCount
, nRow2
, 0);
877 nIEnd
= nCol1
- nFillCount
;
878 aFillRange
= ScRange(nCol1
- 1, nRow1
, 0, nCol1
- nFillCount
, nRow2
, 0);
881 sal_uInt64 nIMin
= nIStart
;
882 sal_uInt64 nIMax
= nIEnd
;
883 PutInOrder(nIMin
,nIMax
);
884 bool bHasFiltered
= IsDataFiltered(aFillRange
);
889 DeleteArea(nCol1
, static_cast<SCROW
>(nIMin
), nCol2
, static_cast<SCROW
>(nIMax
), InsertDeleteFlags::AUTOFILL
);
891 DeleteArea(static_cast<SCCOL
>(nIMin
), nRow1
, static_cast<SCCOL
>(nIMax
), nRow2
, InsertDeleteFlags::AUTOFILL
);
894 sal_uInt64 nProgress
= 0;
896 nProgress
= pProgress
->GetState();
898 // Avoid possible repeated calls to StartListeningFormulaCells() (tdf#132165).
899 std::list
< sc::DelayStartListeningFormulaCells
> delayStartListening
;
900 SCCOL delayStartColumn
, delayEndColumn
;
903 delayStartColumn
= std::min( nOStart
, nOEnd
);
904 delayEndColumn
= std::max( nOStart
, nOEnd
);
908 delayStartColumn
= std::min( nIStart
, nIEnd
);
909 delayEndColumn
= std::max( nIStart
, nIEnd
);
911 for( SCROW col
= delayStartColumn
; col
<= delayEndColumn
; ++col
)
913 if( ScColumn
* column
= FetchColumn( col
))
914 delayStartListening
.emplace_back( *column
, true );
919 sal_uInt64 nActFormCnt
= 0;
920 for (rOuter
= nOStart
; rOuter
<= nOEnd
; rOuter
++)
922 sal_uInt64 nMaxFormCnt
= 0; // for formulas
924 // transfer attributes
926 const ScPatternAttr
* pSrcPattern
= nullptr;
927 const ScStyleSheet
* pStyleSheet
= nullptr;
928 SCCOLROW nAtSrc
= nISrcStart
;
929 std::unique_ptr
<ScPatternAttr
> pNewPattern
;
930 bool bGetPattern
= true;
932 while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
934 if (!ColHidden(nCol
) && !RowHidden(nRow
))
938 if (bVertical
) // rInner&:=nRow, rOuter&:=nCol
939 pSrcPattern
= GetColumnData(nCol
).GetPattern(static_cast<SCROW
>(nAtSrc
));
940 else // rInner&:=nCol, rOuter&:=nRow
941 pSrcPattern
= GetColumnData(nAtSrc
).GetPattern(static_cast<SCROW
>(nRow
));
943 pStyleSheet
= pSrcPattern
->GetStyleSheet();
944 // do transfer ATTR_MERGE / ATTR_MERGE_FLAG
946 // Note: ATTR_MERGE is an attribute of the top left cell of a merged area
947 // containing the size of the area. ATTR_MERGE_FLAGs are attributes of the
948 // other cells of a merged area, containing the information about also
949 // overlapping, i.e. visibility of their content.
951 // TODO: extend the similar incomplete selections to a bounding rectangle to
952 // avoid incomplete fill, where not all AUTO_MERGE_FLAGs are synchronized with
953 // the copied ATTR_MERGE, resulting broken grid and visibility during run-time.
963 // TODO: protect incompatible merged cells of the destination area, for example
964 // by skipping the fill operation.
966 // TODO: by dragging the fill handle select only the multiples of the height
967 // of the originally selected area which is merged vertically to avoid of
980 // Other things stored in ATTR_MERGE_FLAG, like autofilter button, will be
981 // deleted now, but may need to be repaired later, like at ScDocument::Fill.
982 const SfxItemSet
& rSet
= pSrcPattern
->GetItemSet();
983 if ( rSet
.GetItemState(ATTR_MERGE_FLAG
, false) == SfxItemState::SET
)
985 ScMF nOldValue
= pSrcPattern
->GetItem(ATTR_MERGE_FLAG
).GetValue();
986 ScMF nOldValueMerge
= nOldValue
& (ScMF::Hor
| ScMF::Ver
);
987 // keep only the merge flags
988 if ( nOldValue
!= nOldValueMerge
)
990 pNewPattern
.reset(new ScPatternAttr(*pSrcPattern
));
991 SfxItemSet
& rNewSet
= pNewPattern
->GetItemSet();
992 if ( nOldValueMerge
== ScMF::NONE
)
993 rNewSet
.ClearItem(ATTR_MERGE_FLAG
);
995 rNewSet
.Put(ScMergeFlagAttr(nOldValueMerge
));
1001 pNewPattern
.reset();
1004 const ScCondFormatItem
& rCondFormatItem
= pSrcPattern
->GetItem(ATTR_CONDITIONAL
);
1005 const ScCondFormatIndexes
& rCondFormatIndex
= rCondFormatItem
.GetCondFormatData();
1007 if ( bVertical
&& nISrcStart
== nISrcEnd
&& !bHasFiltered
)
1009 // set all attributes at once (en bloc)
1010 if (pNewPattern
|| !pSrcPattern
->isDefault())
1012 // Default is already present (DeleteArea)
1013 SCROW nY1
= static_cast<SCROW
>(std::min( nIStart
, nIEnd
));
1014 SCROW nY2
= static_cast<SCROW
>(std::max( nIStart
, nIEnd
));
1016 aCol
[nCol
].ApplyStyleArea( nY1
, nY2
, *pStyleSheet
);
1018 aCol
[nCol
].ApplyPatternArea( nY1
, nY2
, *pNewPattern
);
1020 aCol
[nCol
].ApplyPatternArea( nY1
, nY2
, *pSrcPattern
);
1022 for(const auto& rIndex
: rCondFormatIndex
)
1024 ScConditionalFormat
* pCondFormat
= mpCondFormatList
->GetFormat(rIndex
);
1027 ScRangeList aRange
= pCondFormat
->GetRange();
1028 aRange
.Join(ScRange(nCol
, nY1
, nTab
, nCol
, nY2
, nTab
));
1029 pCondFormat
->SetRange(aRange
);
1038 DeleteArea(static_cast<SCCOL
>(nCol
), static_cast<SCROW
>(nRow
),
1039 static_cast<SCCOL
>(nCol
), static_cast<SCROW
>(nRow
), InsertDeleteFlags::AUTOFILL
);
1041 if ( !ScPatternAttr::areSame(pSrcPattern
, aCol
[nCol
].GetPattern( static_cast<SCROW
>(nRow
) ) ) )
1043 // Transfer template too
1044 //TODO: Merge ApplyPattern to AttrArray ??
1046 aCol
[nCol
].ApplyStyle( static_cast<SCROW
>(nRow
), pStyleSheet
);
1048 // Use ApplyPattern instead of SetPattern to keep old MergeFlags
1050 aCol
[nCol
].ApplyPattern( static_cast<SCROW
>(nRow
), *pNewPattern
);
1052 aCol
[nCol
].ApplyPattern( static_cast<SCROW
>(nRow
), *pSrcPattern
);
1054 for(const auto& rIndex
: rCondFormatIndex
)
1056 ScConditionalFormat
* pCondFormat
= mpCondFormatList
->GetFormat(rIndex
);
1059 ScRangeList aRange
= pCondFormat
->GetRange();
1060 aRange
.Join(ScRange(nCol
, nRow
, nTab
, nCol
, nRow
, nTab
));
1061 pCondFormat
->SetRange(aRange
);
1066 if (nAtSrc
==nISrcEnd
)
1068 if ( nAtSrc
!= nISrcStart
)
1069 { // More than one source cell
1070 nAtSrc
= nISrcStart
;
1086 if (rInner
== nIEnd
) break;
1087 if (bPositive
) ++rInner
; else --rInner
;
1089 pNewPattern
.reset();
1094 FillDateCmd eDateCmd
= {};
1096 tools::Duration aDurationInc
;
1097 sal_uInt16 nMinDigits
;
1098 ScUserListData
* pListData
= nullptr;
1099 sal_uInt16 nListIndex
;
1100 bool bSkipOverlappedCells
;
1101 std::vector
<sal_Int32
> aNonOverlappedCellIdx
;
1103 FillAnalyse(static_cast<SCCOL
>(nCol
),nRow1
,
1104 static_cast<SCCOL
>(nCol
),nRow2
, eFillCmd
,eDateCmd
,
1105 nInc
, aDurationInc
, nMinDigits
, pListData
, nListIndex
,
1106 bHasFiltered
, bSkipOverlappedCells
, aNonOverlappedCellIdx
);
1108 FillAnalyse(nCol1
,static_cast<SCROW
>(nRow
),
1109 nCol2
,static_cast<SCROW
>(nRow
), eFillCmd
,eDateCmd
,
1110 nInc
, aDurationInc
, nMinDigits
, pListData
, nListIndex
,
1111 bHasFiltered
, bSkipOverlappedCells
, aNonOverlappedCellIdx
);
1115 sal_uInt16 nListCount
= pListData
->GetSubCount();
1116 if (bSkipOverlappedCells
)
1118 int nFillerCount
= 1 + ( nISrcEnd
- nISrcStart
) * (bPositive
? 1 : -1);
1119 std::vector
<bool> aIsNonEmptyCell(nFillerCount
, false);
1120 SCCOLROW nLastValueIdx
;
1123 nLastValueIdx
= nISrcEnd
- (nFillerCount
- 1 - aNonOverlappedCellIdx
.back());
1124 for (auto i
: aNonOverlappedCellIdx
)
1125 aIsNonEmptyCell
[i
] = true;
1129 nLastValueIdx
= nISrcEnd
+ aNonOverlappedCellIdx
[0];
1130 for (auto i
: aNonOverlappedCellIdx
)
1131 aIsNonEmptyCell
[nFillerCount
- 1 - i
] = true;
1136 aStr
= GetString(rOuter
, nLastValueIdx
);
1138 aStr
= GetString(nLastValueIdx
, rOuter
);
1140 bool bMatchCase
= false;
1141 (void)pListData
->GetSubIndex(aStr
, nListIndex
, bMatchCase
);
1143 sal_Int32 nFillerIdx
= 0;
1147 if (aIsNonEmptyCell
[nFillerIdx
])
1152 if (nListIndex
>= nListCount
) nListIndex
-= nListCount
;
1156 if (nListIndex
< nInc
) nListIndex
+= nListCount
;
1159 aCol
[nCol
].SetRawString(static_cast<SCROW
>(nRow
), pListData
->GetSubStr(nListIndex
));
1162 if (rInner
== nIEnd
) break;
1163 nFillerIdx
= (nFillerIdx
+ 1) % nFillerCount
;
1174 // nListIndex of FillAnalyse points to the last entry -> adjust
1175 sal_Int64 nAdjust
= nListIndex
- (nISrcStart
- nISrcEnd
) * nInc
;
1176 nAdjust
= nAdjust
% nListCount
;
1178 nAdjust
+= nListCount
;
1179 nListIndex
= nAdjust
;
1183 while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
1185 if (!ColHidden(nCol
) && !RowHidden(nRow
))
1190 if (nListIndex
>= nListCount
) nListIndex
-= nListCount
;
1194 if (nListIndex
< nInc
) nListIndex
+= nListCount
;
1197 aCol
[nCol
].SetRawString(static_cast<SCROW
>(nRow
), pListData
->GetSubStr(nListIndex
));
1200 if (rInner
== nIEnd
) break;
1201 if (bPositive
) ++rInner
; else --rInner
;
1206 nProgress
+= nIMax
- nIMin
+ 1;
1207 pProgress
->SetStateOnPercent( nProgress
);
1210 else if (eFillCmd
== FILL_SIMPLE
) // fill with pattern/sample
1213 nISrcStart
, nISrcEnd
, nIStart
, nIEnd
, rInner
, nCol
, nRow
,
1214 nActFormCnt
, nMaxFormCnt
, bHasFiltered
, bVertical
, bPositive
, pProgress
, nProgress
);
1221 aDurationInc
= -aDurationInc
;
1223 double nEndVal
= (nInc
>=0.0) ? MAXDOUBLE
: -MAXDOUBLE
;
1225 FillSeries( static_cast<SCCOL
>(nCol
), nRow1
,
1226 static_cast<SCCOL
>(nCol
), nRow2
, nFillCount
, eFillDir
,
1227 eFillCmd
, eDateCmd
, nInc
, aDurationInc
, nEndVal
, nMinDigits
, false,
1228 pProgress
, bSkipOverlappedCells
, &aNonOverlappedCellIdx
);
1230 FillSeries( nCol1
, static_cast<SCROW
>(nRow
), nCol2
,
1231 static_cast<SCROW
>(nRow
), nFillCount
, eFillDir
,
1232 eFillCmd
, eDateCmd
, nInc
, aDurationInc
, nEndVal
, nMinDigits
, false,
1233 pProgress
, bSkipOverlappedCells
, &aNonOverlappedCellIdx
);
1235 nProgress
= pProgress
->GetState();
1239 FillSparkline(bVertical
, nCol
, nRow1
, nRow2
, nIStart
, nIEnd
);
1241 FillSparkline(bVertical
, nRow
, nCol1
, nCol2
, nIStart
, nIEnd
);
1243 nActFormCnt
+= nMaxFormCnt
;
1247 void ScTable::FillSparkline(bool bVertical
, SCCOLROW nFixed
,
1248 SCCOLROW nStart
, SCCOLROW nEnd
,
1249 SCCOLROW nFillStart
, SCCOLROW nFillEnd
)
1251 bool bHasSparklines
= false;
1252 std::vector
<std::shared_ptr
<sc::Sparkline
>> aSparklineSeries
;
1254 for (SCROW nCurrent
= nStart
; nCurrent
<= nEnd
; nCurrent
++)
1256 auto pSparkline
= bVertical
? GetSparkline(nFixed
, nCurrent
) : GetSparkline(nCurrent
, nFixed
);
1257 bHasSparklines
= bHasSparklines
|| pSparkline
;
1258 aSparklineSeries
.push_back(pSparkline
);
1263 for (SCCOLROW nCurrent
= nFillStart
; nCurrent
<= nFillEnd
; nCurrent
++)
1265 size_t nIndex
= size_t(nFillStart
- nCurrent
) % aSparklineSeries
.size();
1266 if (auto& rpSparkline
= aSparklineSeries
[nIndex
])
1268 auto pGroup
= rpSparkline
->getSparklineGroup();
1270 auto* pNewSparkline
= bVertical
? CreateSparkline(nFixed
, nCurrent
, pGroup
)
1271 : CreateSparkline(nCurrent
, nFixed
, pGroup
);
1274 SCCOLROW nPosition
= bVertical
? rpSparkline
->getRow()
1275 : rpSparkline
->getColumn();
1276 SCCOLROW nDelta
= nCurrent
- nPosition
;
1277 ScRangeList
aRangeList(rpSparkline
->getInputRange());
1278 for (ScRange
& rRange
: aRangeList
)
1282 rRange
.aStart
.IncRow(nDelta
);
1283 rRange
.aEnd
.IncRow(nDelta
);
1287 rRange
.aStart
.IncCol(nDelta
);
1288 rRange
.aEnd
.IncCol(nDelta
);
1291 pNewSparkline
->setInputRange(aRangeList
);
1298 void ScTable::GetBackColorArea(SCCOL
& rStartCol
, SCROW
& /*rStartRow*/,
1299 SCCOL
& rEndCol
, SCROW
& rEndRow
) const
1302 const SvxBrushItem
* pDefBackground
= &rDocument
.GetPool()->GetUserOrPoolDefaultItem(ATTR_BACKGROUND
);
1304 rStartCol
= std::min
<SCCOL
>(rStartCol
, aCol
.size() - 1);
1305 rEndCol
= std::min
<SCCOL
>(rEndCol
, aCol
.size() - 1);
1311 if (rEndRow
< rDocument
.MaxRow())
1313 for (SCCOL nCol
= rStartCol
; nCol
<= rEndCol
; ++nCol
)
1315 const ScPatternAttr
* pPattern
= GetColumnData(nCol
).GetPattern(rEndRow
+ 1);
1316 const SvxBrushItem
* pBackground
= &pPattern
->GetItem(ATTR_BACKGROUND
);
1317 if (!pPattern
->GetItem(ATTR_CONDITIONAL
).GetCondFormatData().empty() ||
1318 (pBackground
->GetColor() != COL_TRANSPARENT
&& pBackground
!= pDefBackground
))
1331 OUString
ScTable::GetAutoFillPreview( const ScRange
& rSource
, SCCOL nEndX
, SCROW nEndY
)
1335 SCCOL nCol1
= rSource
.aStart
.Col();
1336 SCROW nRow1
= rSource
.aStart
.Row();
1337 SCCOL nCol2
= rSource
.aEnd
.Col();
1338 SCROW nRow2
= rSource
.aEnd
.Row();
1340 tools::Long nIndex
= 0;
1341 sal_uInt64 nSrcCount
= 0;
1342 FillDir eFillDir
= FILL_TO_BOTTOM
;
1343 if ( nEndX
== nCol2
&& nEndY
== nRow2
) // empty
1345 else if ( nEndX
== nCol2
) // to up / down
1347 nCol2
= nCol1
; // use only first column
1348 nSrcCount
= nRow2
- nRow1
+ 1;
1349 nIndex
= static_cast<tools::Long
>(nEndY
) - nRow1
; // can be negative
1350 if ( nEndY
>= nRow1
)
1351 eFillDir
= FILL_TO_BOTTOM
;
1353 eFillDir
= FILL_TO_TOP
;
1355 else if ( nEndY
== nRow2
) // to left / right
1357 nEndY
= nRow2
= nRow1
; // use only first row
1358 nSrcCount
= nCol2
- nCol1
+ 1;
1359 nIndex
= static_cast<tools::Long
>(nEndX
) - nCol1
; // can be negative
1360 if ( nEndX
>= nCol1
)
1361 eFillDir
= FILL_TO_RIGHT
;
1363 eFillDir
= FILL_TO_LEFT
;
1365 else // direction not clear
1370 tools::Long nBegin
= 0;
1371 tools::Long nEnd
= 0;
1372 tools::Long nHidden
= 0;
1373 if (eFillDir
== FILL_TO_BOTTOM
|| eFillDir
== FILL_TO_TOP
)
1386 tools::Long nVisible
= CountVisibleRows(nBegin
, nEnd
);
1387 nHidden
= nEnd
+ 1 - nBegin
- nVisible
;
1402 tools::Long nVisible
= CountVisibleCols(nBegin
, nEnd
);
1403 nHidden
= nEnd
+ 1 - nBegin
- nVisible
;
1408 nIndex
= nIndex
- nHidden
;
1410 nIndex
= nIndex
+ nHidden
;
1414 FillDateCmd eDateCmd
;
1416 tools::Duration aDurationInc
;
1417 sal_uInt16 nMinDigits
;
1418 ScUserListData
* pListData
= nullptr;
1419 sal_uInt16 nListIndex
;
1420 bool bSkipOverlappedCells
;
1421 std::vector
<sal_Int32
> aNonOverlappedCellIdx
;
1423 // Todo: update this function to calculate with merged cell fills,
1424 // after FillAnalyse / FillSeries fully handle them.
1425 // Now FillAnalyse called as if there are filtered rows, so it will work in the old way.
1426 FillAnalyse(nCol1
, nRow1
, nCol2
, nRow2
, eFillCmd
, eDateCmd
,
1427 nInc
, aDurationInc
, nMinDigits
, pListData
, nListIndex
,
1428 true, bSkipOverlappedCells
, aNonOverlappedCellIdx
);
1430 if ( pListData
) // user defined list
1432 sal_uInt16 nListCount
= pListData
->GetSubCount();
1435 sal_uInt64 nSub
= nSrcCount
- 1; // nListIndex is from last source entry
1436 while ( nIndex
< sal::static_int_cast
<tools::Long
>(nSub
) )
1437 nIndex
+= nListCount
;
1438 sal_uInt64 nPos
= ( nListIndex
+ nIndex
- nSub
) % nListCount
;
1439 aValue
= pListData
->GetSubStr(sal::static_int_cast
<sal_uInt16
>(nPos
));
1442 else if ( eFillCmd
== FILL_SIMPLE
) // fill with pattern/sample
1444 tools::Long nPosIndex
= nIndex
;
1445 while ( nPosIndex
< 0 )
1446 nPosIndex
+= nSrcCount
;
1447 sal_uInt64 nPos
= nPosIndex
% nSrcCount
;
1448 SCCOL nSrcX
= nCol1
;
1449 SCROW nSrcY
= nRow1
;
1450 if ( eFillDir
== FILL_TO_TOP
|| eFillDir
== FILL_TO_BOTTOM
)
1451 nSrcY
= sal::static_int_cast
<SCROW
>( nSrcY
+ static_cast<SCROW
>(nPos
) );
1453 nSrcX
= sal::static_int_cast
<SCCOL
>( nSrcX
+ static_cast<SCCOL
>(nPos
) );
1455 ScRefCellValue aCell
= GetCellValue(nSrcX
, nSrcY
);
1456 if (!aCell
.isEmpty())
1460 nDelta
= nIndex
/ nSrcCount
;
1462 nDelta
= ( nIndex
- nSrcCount
+ 1 ) / nSrcCount
; // -1 -> -1
1464 CellType eType
= aCell
.getType();
1467 case CELLTYPE_STRING
:
1470 aValue
= aCell
.getString(&rDocument
);
1472 if ( !(nScFillModeMouseModifier
& KEY_MOD1
) )
1475 sal_uInt16 nCellDigits
= 0; // look at each source cell individually
1476 short nFlag
= lcl_DecompValueString( aValue
, nVal
, &nCellDigits
);
1479 if (aValue
== ScGlobal::GetOrdinalSuffix( nVal
))
1480 aValue
= ScGlobal::GetOrdinalSuffix( nVal
+ nDelta
);
1481 aValue
= lcl_ValueString( nVal
+ nDelta
, nCellDigits
) + aValue
;
1483 else if ( nFlag
> 0 )
1485 sal_Int32 nNextValue
;
1487 nNextValue
= nVal
- nDelta
;
1489 nNextValue
= nVal
+ nDelta
;
1490 if ( nFlag
== 2 && nNextValue
>= 0 ) // Put back the '+'
1492 aValue
+= lcl_ValueString( nNextValue
, nCellDigits
);
1497 case CELLTYPE_VALUE
:
1499 sal_uInt32 nNumFmt
= GetNumberFormat( nSrcX
, nSrcY
);
1500 // overflow is possible...
1501 double nVal
= aCell
.getDouble();
1502 if ( !(nScFillModeMouseModifier
& KEY_MOD1
) )
1504 const SvNumFormatType nFormatType
= rDocument
.GetFormatTable()->GetType(nNumFmt
);
1505 bool bPercentCell
= (nFormatType
== SvNumFormatType::PERCENT
);
1508 // tdf#89998 increment by 1% at a time
1509 nVal
+= static_cast<double>(nDelta
) * 0.01;
1511 else if (nVal
== 0.0 || nVal
== 1.0)
1513 bool bBooleanCell
= (nFormatType
== SvNumFormatType::LOGICAL
);
1515 nVal
+= static_cast<double>(nDelta
);
1519 nVal
+= static_cast<double>(nDelta
);
1523 const Color
* pColor
;
1524 rDocument
.GetFormatTable()->GetOutputString( nVal
, nNumFmt
, aValue
, &pColor
);
1530 // added to avoid warnings
1535 else if ( eFillCmd
== FILL_LINEAR
|| eFillCmd
== FILL_DATE
) // values
1540 short nHeadNoneTail
= 0;
1541 ScRefCellValue aCell
= GetCellValue(nCol1
, nRow1
);
1542 if (!aCell
.isEmpty())
1544 CellType eType
= aCell
.getType();
1547 case CELLTYPE_STRING
:
1550 aValue
= aCell
.getString(&rDocument
);
1551 nHeadNoneTail
= lcl_DecompValueString( aValue
, nVal
);
1552 if ( nHeadNoneTail
)
1553 nStart
= static_cast<double>(nVal
);
1558 case CELLTYPE_VALUE
:
1559 nStart
= aCell
.getDouble();
1561 case CELLTYPE_FORMULA
:
1562 nStart
= aCell
.getFormula()->GetValue();
1570 if ( eFillCmd
== FILL_LINEAR
)
1575 tools::Duration
aDuration( aDurationInc
.Mult( nIndex
, bOverflow
));
1576 bValueOk
= SubTotal::SafePlus( nStart
, aDuration
.GetInDays()) && !bOverflow
;
1581 bValueOk
= ( SubTotal::SafeMult( nAdd
, static_cast<double>(nIndex
) ) &&
1582 SubTotal::SafePlus( nStart
, nAdd
) );
1588 sal_uInt16 nDayOfMonth
= 0;
1594 for (tools::Long i
=0; i
<nIndex
; i
++)
1595 IncDate( nStart
, nDayOfMonth
, nInc
, eDateCmd
);
1600 if ( nHeadNoneTail
)
1602 if ( nHeadNoneTail
< 0 )
1604 if (aValue
== ScGlobal::GetOrdinalSuffix( nVal
))
1605 aValue
= ScGlobal::GetOrdinalSuffix( static_cast<sal_Int32
>(nStart
) );
1607 aValue
= lcl_ValueString( static_cast<sal_Int32
>(nStart
), nMinDigits
) + aValue
;
1611 if ( nHeadNoneTail
== 2 && nStart
>= 0 ) // Put back the '+'
1613 aValue
+= lcl_ValueString( static_cast<sal_Int32
>(nStart
), nMinDigits
);
1618 //TODO: get number format according to Index?
1619 const Color
* pColor
;
1620 sal_uInt32 nNumFmt
= GetNumberFormat( nCol1
, nRow1
);
1621 rDocument
.GetFormatTable()->GetOutputString( nStart
, nNumFmt
, aValue
, &pColor
);
1627 OSL_FAIL("GetAutoFillPreview: invalid mode");
1634 void ScTable::IncDate(double& rVal
, sal_uInt16
& nDayOfMonth
, double nStep
, FillDateCmd eCmd
)
1636 if (eCmd
== FILL_DAY
)
1642 // class Date limits
1643 const sal_uInt16 nMinYear
= 1583;
1644 const sal_uInt16 nMaxYear
= 9956;
1646 tools::Long nInc
= static_cast<tools::Long
>(nStep
); // upper/lower limits ?
1647 Date aNullDate
= rDocument
.GetFormatTable()->GetNullDate();
1648 Date aDate
= aNullDate
;
1649 aDate
.AddDays(rVal
);
1654 aDate
.AddDays(nInc
);
1655 DayOfWeek eWeekDay
= aDate
.GetDayOfWeek();
1658 if (eWeekDay
== SATURDAY
)
1660 else if (eWeekDay
== SUNDAY
)
1665 if (eWeekDay
== SATURDAY
)
1667 else if (eWeekDay
== SUNDAY
)
1673 case FILL_END_OF_MONTH
:
1675 if ( nDayOfMonth
== 0 )
1676 nDayOfMonth
= aDate
.GetDay(); // init
1677 tools::Long nMonth
= aDate
.GetMonth();
1678 tools::Long nYear
= aDate
.GetYear();
1686 tools::Long nYAdd
= (nMonth
-1) / 12;
1687 nMonth
-= nYAdd
* 12;
1695 tools::Long nYAdd
= 1 - nMonth
/ 12; // positive
1696 nMonth
+= nYAdd
* 12;
1701 if ( nYear
< nMinYear
)
1702 aDate
= Date( 1,1, nMinYear
);
1703 else if ( nYear
> nMaxYear
)
1704 aDate
= Date( 31,12, nMaxYear
);
1707 aDate
.SetMonth(static_cast<sal_uInt16
>(nMonth
));
1708 aDate
.SetYear(static_cast<sal_uInt16
>(nYear
));
1709 if (eCmd
== FILL_END_OF_MONTH
)
1711 aDate
.SetDay(Date::GetDaysInMonth(nMonth
, nYear
));
1715 aDate
.SetDay(std::min(Date::GetDaysInMonth(nMonth
, nYear
), nDayOfMonth
));
1722 tools::Long nYear
= aDate
.GetYear();
1724 if ( nYear
< nMinYear
)
1725 aDate
= Date( 1,1, nMinYear
);
1726 else if ( nYear
> nMaxYear
)
1727 aDate
= Date( 31,12, nMaxYear
);
1729 aDate
.SetYear(static_cast<sal_uInt16
>(nYear
));
1734 // added to avoid warnings
1738 rVal
= aDate
- aNullDate
;
1743 bool HiddenRowColumn(const ScTable
* pTable
, SCCOLROW nRowColumn
, bool bVertical
, SCCOLROW
& rLastPos
)
1745 bool bHidden
= false;
1749 bHidden
= pTable
->RowHidden(nRowColumn
, nullptr, &nLast
);
1755 bHidden
= pTable
->ColHidden(static_cast<SCCOL
>(nRowColumn
), nullptr, &nLast
);
1763 void ScTable::FillFormulaVertical(
1764 const ScFormulaCell
& rSrcCell
,
1765 SCCOLROW
& rInner
, SCCOL nCol
, SCROW nRow1
, SCROW nRow2
,
1766 ScProgress
* pProgress
, sal_uInt64
& rProgress
)
1768 // rInner is the row position when filling vertically. Also, when filling
1769 // across hidden regions, it may create multiple dis-jointed spans of
1772 bool bHidden
= false;
1773 SCCOLROW nHiddenLast
= -1;
1775 SCCOLROW nRowStart
= -1, nRowEnd
= -1;
1776 std::vector
<sc::RowSpan
> aSpans
;
1777 PutInOrder(nRow1
, nRow2
);
1778 for (rInner
= nRow1
; rInner
<= nRow2
; ++rInner
)
1780 if (rInner
> nHiddenLast
)
1781 bHidden
= HiddenRowColumn(this, rInner
, true, nHiddenLast
);
1787 nRowEnd
= rInner
- 1;
1788 aSpans
.emplace_back(nRowStart
, nRowEnd
);
1791 rInner
= nHiddenLast
;
1801 nRowEnd
= rInner
- 1;
1802 aSpans
.emplace_back(nRowStart
, nRowEnd
);
1808 aCol
[nCol
].DeleteRanges(aSpans
, InsertDeleteFlags::VALUE
| InsertDeleteFlags::DATETIME
| InsertDeleteFlags::STRING
| InsertDeleteFlags::FORMULA
| InsertDeleteFlags::OUTLINE
);
1809 aCol
[nCol
].CloneFormulaCell(rSrcCell
, sc::CellTextAttr(), aSpans
);
1811 const auto pSet
= std::make_shared
<sc::ColumnBlockPositionSet
>(rDocument
);
1812 sc::StartListeningContext
aStartCxt(rDocument
, pSet
);
1813 sc::EndListeningContext
aEndCxt(rDocument
, pSet
);
1815 SCROW nStartRow
= aSpans
.front().mnRow1
;
1816 SCROW nEndRow
= aSpans
.back().mnRow2
;
1817 aCol
[nCol
].EndListeningFormulaCells(aEndCxt
, nStartRow
, nEndRow
, &nStartRow
, &nEndRow
);
1818 aCol
[nCol
].StartListeningFormulaCells(aStartCxt
, aEndCxt
, nStartRow
, nEndRow
);
1820 for (const auto& rSpan
: aSpans
)
1821 aCol
[nCol
].SetDirty(rSpan
.mnRow1
, rSpan
.mnRow2
, ScColumn::BROADCAST_NONE
);
1823 rProgress
+= nRow2
- nRow1
+ 1;
1825 pProgress
->SetStateOnPercent(rProgress
);
1828 void ScTable::FillSeriesSimple(
1829 const ScCellValue
& rSrcCell
, SCCOLROW
& rInner
, SCCOLROW nIMin
, SCCOLROW nIMax
,
1830 const SCCOLROW
& rCol
, const SCCOLROW
& rRow
, bool bVertical
, ScProgress
* pProgress
, sal_uInt64
& rProgress
)
1832 bool bHidden
= false;
1833 SCCOLROW nHiddenLast
= -1;
1837 switch (rSrcCell
.getType())
1839 case CELLTYPE_FORMULA
:
1841 FillFormulaVertical(
1842 *rSrcCell
.getFormula(), rInner
, rCol
, nIMin
, nIMax
, pProgress
, rProgress
);
1847 for (rInner
= nIMin
; rInner
<= nIMax
; ++rInner
)
1849 if (rInner
> nHiddenLast
)
1850 bHidden
= HiddenRowColumn(this, rInner
, bVertical
, nHiddenLast
);
1854 rInner
= nHiddenLast
;
1858 ScAddress
aDestPos(rCol
, rRow
, nTab
);
1859 rSrcCell
.commit(aCol
[rCol
], aDestPos
.Row());
1861 rProgress
+= nIMax
- nIMin
+ 1;
1863 pProgress
->SetStateOnPercent(rProgress
);
1869 switch (rSrcCell
.getType())
1871 case CELLTYPE_FORMULA
:
1873 for (rInner
= nIMin
; rInner
<= nIMax
; ++rInner
)
1875 if (rInner
> nHiddenLast
)
1876 bHidden
= HiddenRowColumn(this, rInner
, bVertical
, nHiddenLast
);
1881 FillFormula(rSrcCell
.getFormula(), rCol
, rRow
, (rInner
== nIMax
));
1883 pProgress
->SetStateOnPercent(++rProgress
);
1889 for (rInner
= nIMin
; rInner
<= nIMax
; ++rInner
)
1891 if (rInner
> nHiddenLast
)
1892 bHidden
= HiddenRowColumn(this, rInner
, bVertical
, nHiddenLast
);
1897 ScAddress
aDestPos(rCol
, rRow
, nTab
);
1898 rSrcCell
.commit(aCol
[rCol
], aDestPos
.Row());
1900 rProgress
+= nIMax
- nIMin
+ 1;
1902 pProgress
->SetStateOnPercent(rProgress
);
1908 void ScTable::FillAutoSimple(
1909 SCCOLROW nISrcStart
, SCCOLROW nISrcEnd
, SCCOLROW nIStart
, SCCOLROW nIEnd
,
1910 SCCOLROW
& rInner
, const SCCOLROW
& rCol
, const SCCOLROW
& rRow
, sal_uInt64 nActFormCnt
,
1911 sal_uInt64 nMaxFormCnt
, bool bHasFiltered
, bool bVertical
, bool bPositive
,
1912 ScProgress
* pProgress
, sal_uInt64
& rProgress
)
1914 SCCOLROW nSource
= nISrcStart
;
1916 if ( nScFillModeMouseModifier
& KEY_MOD1
)
1918 else if ( bPositive
)
1922 sal_uInt64 nFormulaCounter
= nActFormCnt
;
1923 bool bGetCell
= true;
1924 bool bBooleanCell
= false;
1925 bool bPercentCell
= false;
1926 sal_uInt16 nCellDigits
= 0;
1927 short nHeadNoneTail
= 0;
1928 sal_Int32 nStringValue
= 0;
1930 ScCellValue aSrcCell
;
1931 bool bIsOrdinalSuffix
= false;
1933 bool bColHidden
= false, bRowHidden
= false;
1934 SCCOL nColHiddenFirst
= rDocument
.MaxCol();
1935 SCCOL nColHiddenLast
= -1;
1936 SCROW nRowHiddenFirst
= rDocument
.MaxRow();
1937 SCROW nRowHiddenLast
= -1;
1940 while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
1944 if (rCol
> nColHiddenLast
)
1945 bColHidden
= ColHidden(rCol
, nullptr, &nColHiddenLast
);
1946 if (rRow
> nRowHiddenLast
)
1947 bRowHidden
= RowHidden(rRow
, nullptr, &nRowHiddenLast
);
1951 if (rCol
< nColHiddenFirst
)
1952 bColHidden
= ColHidden(rCol
, &nColHiddenFirst
);
1953 if (rRow
< nRowHiddenFirst
)
1954 bRowHidden
= RowHidden(rRow
, &nRowHiddenFirst
);
1957 if (!bColHidden
&& !bRowHidden
)
1961 if (bVertical
) // rInner&:=nRow, rOuter&:=nCol
1963 aSrcCell
= GetCellValue(rCol
, nSource
);
1964 if (nISrcStart
== nISrcEnd
&& aSrcCell
.getType() == CELLTYPE_FORMULA
)
1966 FillFormulaVertical(*aSrcCell
.getFormula(), rInner
, rCol
, nIStart
, nIEnd
, pProgress
, rProgress
);
1969 const SvNumFormatType nFormatType
= rDocument
.GetFormatTable()->GetType(
1970 GetColumnData(rCol
).GetNumberFormat( rDocument
.GetNonThreadedContext(), nSource
));
1971 bBooleanCell
= (nFormatType
== SvNumFormatType::LOGICAL
);
1972 bPercentCell
= (nFormatType
== SvNumFormatType::PERCENT
);
1975 else // rInner&:=nCol, rOuter&:=nRow
1977 aSrcCell
= GetCellValue(nSource
, rRow
);
1978 const SvNumFormatType nFormatType
= rDocument
.GetFormatTable()->GetType(
1979 GetColumnData(nSource
).GetNumberFormat( rDocument
.GetNonThreadedContext(), rRow
));
1980 bBooleanCell
= (nFormatType
== SvNumFormatType::LOGICAL
);
1981 bPercentCell
= (nFormatType
== SvNumFormatType::PERCENT
);
1985 if (!aSrcCell
.isEmpty())
1987 switch (aSrcCell
.getType())
1989 case CELLTYPE_STRING
:
1991 if (aSrcCell
.getType() == CELLTYPE_STRING
)
1992 aValue
= aSrcCell
.getSharedString()->getString();
1994 aValue
= ScEditUtil::GetString(*aSrcCell
.getEditText(), &rDocument
);
1995 if ( !(nScFillModeMouseModifier
& KEY_MOD1
) && !bHasFiltered
)
1997 nCellDigits
= 0; // look at each source cell individually
1998 nHeadNoneTail
= lcl_DecompValueString(
1999 aValue
, nStringValue
, &nCellDigits
);
2001 bIsOrdinalSuffix
= aValue
==
2002 ScGlobal::GetOrdinalSuffix(nStringValue
);
2007 // added to avoid warnings
2013 switch (aSrcCell
.getType())
2015 case CELLTYPE_VALUE
:
2018 if (bBooleanCell
&& ((fVal
= aSrcCell
.getDouble()) == 0.0 || fVal
== 1.0))
2019 aCol
[rCol
].SetValue(rRow
, aSrcCell
.getDouble());
2020 else if(bPercentCell
)
2021 aCol
[rCol
].SetValue(rRow
, aSrcCell
.getDouble() + nDelta
* 0.01); // tdf#89998 increment by 1% at a time
2023 aCol
[rCol
].SetValue(rRow
, aSrcCell
.getDouble() + nDelta
);
2026 case CELLTYPE_STRING
:
2028 if ( nHeadNoneTail
)
2030 sal_Int32 nNextValue
;
2031 if (nStringValue
< 0)
2032 nNextValue
= nStringValue
- static_cast<sal_Int32
>(nDelta
);
2034 nNextValue
= nStringValue
+ static_cast<sal_Int32
>(nDelta
);
2036 if ( nHeadNoneTail
< 0 )
2040 nNextValue
, nCellDigits
, aValue
,
2041 aSrcCell
.getType(), bIsOrdinalSuffix
);
2046 if (nHeadNoneTail
== 2 && nNextValue
>= 0) // Put back the '+'
2047 aStr
= aValue
+ "+" + lcl_ValueString(nNextValue
, nCellDigits
);
2049 aStr
= aValue
+ lcl_ValueString(nNextValue
, nCellDigits
);
2051 aCol
[rCol
].SetRawString(rRow
, aStr
);
2055 aSrcCell
.commit(aCol
[rCol
], rRow
);
2058 case CELLTYPE_FORMULA
:
2060 aSrcCell
.getFormula(), rCol
, rRow
, (rInner
== nIEnd
));
2061 if (nFormulaCounter
- nActFormCnt
> nMaxFormCnt
)
2062 nMaxFormCnt
= nFormulaCounter
- nActFormCnt
;
2066 // added to avoid warnings
2070 if (nSource
== nISrcEnd
)
2072 if ( nSource
!= nISrcStart
)
2073 { // More than one source cell
2074 nSource
= nISrcStart
;
2077 if ( !(nScFillModeMouseModifier
& KEY_MOD1
) )
2084 nFormulaCounter
= nActFormCnt
;
2098 if (rInner
== nIEnd
)
2105 // Progress in inner loop only for expensive cells,
2106 // and even then not individually for each one
2109 if ( pProgress
&& (aSrcCell
.getType() == CELLTYPE_FORMULA
|| aSrcCell
.getType() == CELLTYPE_EDIT
) )
2110 pProgress
->SetStateOnPercent( rProgress
);
2114 pProgress
->SetStateOnPercent( rProgress
);
2119 // Target value exceeded?
2120 inline bool isOverflow( const double& rVal
, const double& rMax
, const double& rStep
,
2121 const double& rStartVal
, FillCmd eFillCmd
)
2136 // Growing away from zero, including zero growth (1.0).
2144 // Shrinking towards zero.
2151 else if (rStep
< 0.0)
2153 // Alternating positive and negative values.
2156 // Growing away from zero, including zero growth (-1.0).
2162 // Regard negative rMax as lower limit, which will
2163 // be reached only by a negative rVal.
2171 // Regard positive rMax as upper limit, which will
2172 // be reached only by a positive rVal.
2178 // Shrinking towards zero.
2185 else // if (rStep == 0.0)
2187 // All values become zero.
2188 // Corresponds with bEntireArea in FillSeries().
2190 return rMax
< rStartVal
;
2191 else if (rMax
< 0.0)
2192 return rStartVal
< rMax
;
2196 assert(!"eFillCmd");
2202 void ScTable::FillSeries( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
2203 sal_uInt64 nFillCount
, FillDir eFillDir
, FillCmd eFillCmd
, FillDateCmd eFillDateCmd
,
2204 double nStepValue
, const tools::Duration
& rDurationStep
,
2205 double nMaxValue
, sal_uInt16 nArgMinDigits
,
2206 bool bAttribs
, ScProgress
* pProgress
,
2207 bool bSkipOverlappedCells
, std::vector
<sal_Int32
>* pNonOverlappedCellIdx
)
2209 // The term 'inner' here refers to the loop in the filling direction i.e.
2210 // when filling vertically, the inner position is the row position whereas
2211 // when filling horizontally the column position becomes the inner
2212 // position. The term 'outer' refers to the column position when filling
2213 // vertically, or the row position when filling horizontally. The fill is
2214 // performed once in each 'outer' position e.g. when filling vertically,
2215 // we perform the fill once in each column.
2219 bool bVertical
= (eFillDir
== FILL_TO_BOTTOM
|| eFillDir
== FILL_TO_TOP
);
2220 bool bPositive
= (eFillDir
== FILL_TO_BOTTOM
|| eFillDir
== FILL_TO_RIGHT
);
2224 SCCOLROW
& rInner
= bVertical
? nRow
: nCol
; // loop variables
2225 SCCOLROW
& rOuter
= bVertical
? nCol
: nRow
;
2232 sal_uInt64 nFillerCount
;
2233 std::vector
<bool> aIsNonEmptyCell
;
2237 nFillerCount
= (nRow2
- nRow1
) + 1;
2238 nFillCount
+= (nRow2
- nRow1
);
2239 if (nFillCount
== 0)
2246 nISource
= nRow1
; // top row of the source range.
2247 nIStart
= nRow1
+ 1; // first row where we start filling.
2248 nIEnd
= nRow1
+ nFillCount
;
2249 aFillRange
= ScRange(nCol1
, nRow1
+ 1, nTab
, nCol2
, nRow1
+ nFillCount
, nTab
);
2255 nIStart
= nRow2
- 1;
2256 nIEnd
= nRow2
- nFillCount
;
2257 aFillRange
= ScRange(nCol1
, nRow2
-1, nTab
, nCol2
, nRow2
- nFillCount
, nTab
);
2262 nFillerCount
= (nCol2
- nCol1
) + 1;
2263 nFillCount
+= (nCol2
- nCol1
);
2264 if (nFillCount
== 0)
2272 nIStart
= nCol1
+ 1;
2273 nIEnd
= nCol1
+ nFillCount
;
2274 aFillRange
= ScRange(nCol1
+ 1, nRow1
, nTab
, nCol1
+ nFillCount
, nRow2
, nTab
);
2280 nIStart
= nCol2
- 1;
2281 nIEnd
= nCol2
- nFillCount
;
2282 aFillRange
= ScRange(nCol2
- 1, nRow1
, nTab
, nCol2
- nFillCount
, nRow2
, nTab
);
2286 SCCOLROW nIMin
= nIStart
;
2287 SCCOLROW nIMax
= nIEnd
;
2288 PutInOrder(nIMin
,nIMax
);
2290 const bool bIsFiltered
= IsDataFiltered(aFillRange
);
2291 bool bEntireArea
= (!bIsFiltered
&& eFillCmd
== FILL_SIMPLE
);
2292 if (!bIsFiltered
&& !bEntireArea
&& (eFillCmd
== FILL_LINEAR
|| eFillCmd
== FILL_GROWTH
)
2293 && (nOEnd
- nOStart
== 0))
2295 // For the usual case of one col/row determine if a numeric series is
2296 // at least as long as the area to be filled and does not end earlier,
2297 // so we can treat it as entire area for performance reasons at least
2298 // in the vertical case.
2299 // This is not exact in case of merged cell fills with skipping overlapped parts, but
2300 // it is still a good upper estimation.
2301 ScCellValue aSrcCell
;
2303 aSrcCell
= GetCellValue(static_cast<SCCOL
>(nOStart
), static_cast<SCROW
>(nISource
));
2305 aSrcCell
= GetCellValue(static_cast<SCCOL
>(nISource
), static_cast<SCROW
>(nOStart
));
2306 // Same logic as for the actual series.
2307 if (!aSrcCell
.isEmpty() && (aSrcCell
.getType() == CELLTYPE_VALUE
|| aSrcCell
.getType() == CELLTYPE_FORMULA
))
2310 if (aSrcCell
.getType() == CELLTYPE_VALUE
)
2311 nStartVal
= aSrcCell
.getDouble();
2313 nStartVal
= aSrcCell
.getFormula()->GetValue();
2314 if (eFillCmd
== FILL_LINEAR
)
2316 if (nStepValue
== 0.0)
2317 bEntireArea
= (nStartVal
<= nMaxValue
); // fill with same value
2318 else if (((nMaxValue
- nStartVal
) / nStepValue
) >= nFillCount
)
2321 else if (eFillCmd
== FILL_GROWTH
)
2323 if (nStepValue
== 1.0)
2324 bEntireArea
= (nStartVal
<= nMaxValue
); // fill with same value
2325 else if (nStepValue
== -1.0)
2326 bEntireArea
= (fabs(nStartVal
) <= fabs(nMaxValue
)); // fill with alternating value
2327 else if (nStepValue
== 0.0)
2328 bEntireArea
= (nStartVal
== 0.0
2329 || (nStartVal
< 0.0 && nMaxValue
>= 0.0)
2330 || (nStartVal
> 0.0 && nMaxValue
<= 0.0)); // fill with 0.0
2336 InsertDeleteFlags nDel
= (bAttribs
? InsertDeleteFlags::AUTOFILL
:
2337 (InsertDeleteFlags::AUTOFILL
& InsertDeleteFlags::CONTENTS
));
2339 DeleteArea(nCol1
, static_cast<SCROW
>(nIMin
), nCol2
, static_cast<SCROW
>(nIMax
), nDel
);
2341 DeleteArea(static_cast<SCCOL
>(nIMin
), nRow1
, static_cast<SCCOL
>(nIMax
), nRow2
, nDel
);
2344 sal_uInt64 nProgress
= 0;
2346 nProgress
= pProgress
->GetState();
2348 // Perform the fill once per each 'outer' position i.e. one per column
2349 // when filling vertically.
2351 for (rOuter
= nOStart
; rOuter
<= nOEnd
; rOuter
++)
2355 CreateColumnIfNotExists(nCol
);
2357 // Source cell value. We need to clone the value since it may be inserted repeatedly.
2358 ScCellValue aSrcCell
= GetCellValue(nCol
, static_cast<SCROW
>(nRow
));
2360 // Maybe another source cell need to be searched, if the fill is going through merged cells,
2361 // where overlapped parts does not contain any information, so they can be skipped.
2362 if (bSkipOverlappedCells
)
2364 // create a vector to make it easier to decide if a cell need to be filled, or skipped.
2365 aIsNonEmptyCell
.resize(nFillerCount
, false);
2367 SCCOLROW nFirstValueIdx
;
2370 nFirstValueIdx
= nISource
+ (*pNonOverlappedCellIdx
)[0];
2371 for (auto i
: (*pNonOverlappedCellIdx
))
2372 aIsNonEmptyCell
[i
] = true;
2376 nFirstValueIdx
= nISource
- (nFillerCount
- 1 - (*pNonOverlappedCellIdx
).back());
2377 for (auto i
: (*pNonOverlappedCellIdx
))
2378 aIsNonEmptyCell
[nFillerCount
- 1 - i
] = true;
2381 //Set the real source cell
2383 aSrcCell
= GetCellValue(nOStart
, static_cast<SCROW
>(nFirstValueIdx
));
2385 aSrcCell
= GetCellValue(nFirstValueIdx
, static_cast<SCROW
>(nOStart
));
2388 const ScPatternAttr
* pSrcPattern
= aCol
[nCol
].GetPattern(static_cast<SCROW
>(nRow
));
2389 const ScCondFormatItem
& rCondFormatItem
= pSrcPattern
->GetItem(ATTR_CONDITIONAL
);
2390 const ScCondFormatIndexes
& rCondFormatIndex
= rCondFormatItem
.GetCondFormatData();
2396 // If entire area (not filtered and simple fill) use the faster
2397 // method, else hidden cols/rows should be skipped and series
2398 // fill needs to determine the end row dynamically.
2401 SetPatternAreaCondFormat( nCol
, static_cast<SCROW
>(nIMin
),
2402 static_cast<SCROW
>(nIMax
), *pSrcPattern
, rCondFormatIndex
);
2404 else if (eFillCmd
== FILL_SIMPLE
)
2406 assert(bIsFiltered
);
2407 for(SCROW nAtRow
= static_cast<SCROW
>(nIMin
); nAtRow
<= static_cast<SCROW
>(nIMax
); ++nAtRow
)
2409 if(!RowHidden(nAtRow
))
2411 SetPatternAreaCondFormat( nCol
, nAtRow
, nAtRow
, *pSrcPattern
, rCondFormatIndex
);
2417 else if (bEntireArea
|| eFillCmd
== FILL_SIMPLE
)
2419 for (SCCOL nAtCol
= static_cast<SCCOL
>(nIMin
); nAtCol
<= sal::static_int_cast
<SCCOL
>(nIMax
); nAtCol
++)
2421 if(!ColHidden(nAtCol
))
2423 SetPatternAreaCondFormat( nAtCol
, nRow
, nRow
, *pSrcPattern
, rCondFormatIndex
);
2429 if (!aSrcCell
.isEmpty())
2431 CellType eCellType
= aSrcCell
.getType();
2433 if (eFillCmd
== FILL_SIMPLE
) // copy
2435 FillSeriesSimple(aSrcCell
, rInner
, nIMin
, nIMax
, nCol
, nRow
, bVertical
, pProgress
, nProgress
);
2437 else if (eCellType
== CELLTYPE_VALUE
|| eCellType
== CELLTYPE_FORMULA
)
2439 const double nStartVal
= (eCellType
== CELLTYPE_VALUE
? aSrcCell
.getDouble() :
2440 aSrcCell
.getFormula()->GetValue());
2441 double nVal
= nStartVal
;
2442 tools::Long nIndex
= 0;
2444 bool bError
= false;
2445 bool bOverflow
= false;
2446 bool bNonEmpty
= true;
2448 sal_uInt16 nDayOfMonth
= 0;
2449 sal_Int32 nFillerIdx
= 0;
2450 if (bSkipOverlappedCells
&& !aIsNonEmptyCell
[0])
2455 if (bSkipOverlappedCells
)
2457 nFillerIdx
= (nFillerIdx
+ 1) % nFillerCount
;
2458 bNonEmpty
= aIsNonEmptyCell
[nFillerIdx
];
2461 if(!ColHidden(nCol
) && !RowHidden(nRow
))
2463 if (!bError
&& bNonEmpty
)
2469 // use multiplication instead of repeated addition
2470 // to avoid accumulating rounding errors
2474 tools::Duration
aDuration( rDurationStep
.Mult( ++nIndex
, bError
));
2475 bError
|= !SubTotal::SafePlus( nVal
, aDuration
.GetInDays());
2479 double nAdd
= nStepValue
;
2480 if ( !SubTotal::SafeMult( nAdd
, static_cast<double>(++nIndex
) ) ||
2481 !SubTotal::SafePlus( nVal
, nAdd
) )
2487 if (!SubTotal::SafeMult(nVal
, nStepValue
))
2491 if (fabs(nVal
) > D_MAX_LONG_
)
2494 IncDate(nVal
, nDayOfMonth
, nStepValue
, eFillDateCmd
);
2498 // added to avoid warnings
2503 bOverflow
= isOverflow( nVal
, nMaxValue
, nStepValue
, nStartVal
, eFillCmd
);
2506 CreateColumnIfNotExists(nCol
);
2508 aCol
[nCol
].SetError(static_cast<SCROW
>(nRow
), FormulaError::NoValue
);
2509 else if (!bOverflow
&& bNonEmpty
)
2510 aCol
[nCol
].SetValue(static_cast<SCROW
>(nRow
), nVal
);
2512 if (bAttribs
&& !bEntireArea
&& !bOverflow
)
2513 SetPatternAreaCondFormat( nCol
, nRow
, nRow
, *pSrcPattern
, rCondFormatIndex
);
2516 if (rInner
== nIEnd
|| bOverflow
)
2527 nProgress
+= nIMax
- nIMin
+ 1;
2529 pProgress
->SetStateOnPercent( nProgress
);
2531 else if (eCellType
== CELLTYPE_STRING
|| eCellType
== CELLTYPE_EDIT
)
2533 if ( nStepValue
>= 0 )
2535 if ( nMaxValue
>= double(LONG_MAX
) )
2536 nMaxValue
= double(LONG_MAX
) - 1;
2540 if ( nMaxValue
<= double(LONG_MIN
) )
2541 nMaxValue
= double(LONG_MIN
) + 1;
2544 if (eCellType
== CELLTYPE_STRING
)
2545 aValue
= aSrcCell
.getSharedString()->getString();
2547 aValue
= ScEditUtil::GetString(*aSrcCell
.getEditText(), &rDocument
);
2548 sal_Int32 nStringValue
;
2549 sal_uInt16 nMinDigits
= nArgMinDigits
;
2550 short nHeadNoneTail
= lcl_DecompValueString( aValue
, nStringValue
, &nMinDigits
);
2551 if ( nHeadNoneTail
)
2553 const double nStartVal
= static_cast<double>(nStringValue
);
2554 double nVal
= nStartVal
;
2555 tools::Long nIndex
= 0;
2556 bool bError
= false;
2557 bool bOverflow
= false;
2558 bool bNonEmpty
= true;
2560 bool bIsOrdinalSuffix
= aValue
== ScGlobal::GetOrdinalSuffix(
2561 static_cast<sal_Int32
>(nStartVal
));
2563 sal_Int32 nFillerIdx
= 0;
2564 if (bSkipOverlappedCells
&& !aIsNonEmptyCell
[0])
2569 if (bSkipOverlappedCells
)
2571 nFillerIdx
= (nFillerIdx
+ 1) % nFillerCount
;
2572 bNonEmpty
= aIsNonEmptyCell
[nFillerIdx
];
2574 if(!ColHidden(nCol
) && !RowHidden(nRow
))
2576 if (!bError
&& bNonEmpty
)
2582 // use multiplication instead of repeated addition
2583 // to avoid accumulating rounding errors
2587 tools::Duration
aDuration( rDurationStep
.Mult( ++nIndex
, bError
));
2588 bError
|= !SubTotal::SafePlus( nVal
, aDuration
.GetInDays());
2592 double nAdd
= nStepValue
;
2593 if ( !SubTotal::SafeMult( nAdd
, static_cast<double>(++nIndex
) ) ||
2594 !SubTotal::SafePlus( nVal
, nAdd
) )
2600 if (!SubTotal::SafeMult(nVal
, nStepValue
))
2605 // added to avoid warnings
2610 bOverflow
= isOverflow( nVal
, nMaxValue
, nStepValue
, nStartVal
, eFillCmd
);
2614 aCol
[nCol
].SetError(static_cast<SCROW
>(nRow
), FormulaError::NoValue
);
2615 else if (!bOverflow
&& bNonEmpty
)
2617 nStringValue
= static_cast<sal_Int32
>(nVal
);
2618 if ( nHeadNoneTail
< 0 )
2621 aCol
[nCol
], static_cast<SCROW
>(nRow
),
2622 nStringValue
, nMinDigits
, aValue
,
2623 eCellType
, bIsOrdinalSuffix
);
2628 if (nHeadNoneTail
== 2 && nStringValue
>= 0) // Put back the '+'
2629 aStr
= aValue
+ "+";
2632 aStr
+= lcl_ValueString( nStringValue
, nMinDigits
);
2633 aCol
[nCol
].SetRawString(static_cast<SCROW
>(nRow
), aStr
);
2637 if (bAttribs
&& !bEntireArea
&& !bOverflow
)
2638 SetPatternAreaCondFormat( nCol
, nRow
, nRow
, *pSrcPattern
, rCondFormatIndex
);
2641 if (rInner
== nIEnd
|| bOverflow
)
2651 nProgress
+= nIMax
- nIMin
+ 1;
2652 pProgress
->SetStateOnPercent( nProgress
);
2658 nProgress
+= nIMax
- nIMin
+ 1;
2659 pProgress
->SetStateOnPercent( nProgress
);
2664 void ScTable::Fill( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
2665 sal_uInt64 nFillCount
, FillDir eFillDir
, FillCmd eFillCmd
, FillDateCmd eFillDateCmd
,
2666 double nStepValue
, const tools::Duration
& rDurationStep
,
2667 double nMaxValue
, ScProgress
* pProgress
)
2669 if (eFillCmd
== FILL_AUTO
)
2670 FillAuto(nCol1
, nRow1
, nCol2
, nRow2
, nFillCount
, eFillDir
, pProgress
);
2672 FillSeries(nCol1
, nRow1
, nCol2
, nRow2
, nFillCount
, eFillDir
,
2673 eFillCmd
, eFillDateCmd
, nStepValue
, rDurationStep
, nMaxValue
, 0, true, pProgress
);
2676 void ScTable::AutoFormatArea(SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
,
2677 const ScPatternAttr
& rAttr
, sal_uInt16 nFormatNo
)
2679 ScAutoFormat
& rFormat
= *ScGlobal::GetOrCreateAutoFormat();
2680 ScAutoFormatData
* pData
= rFormat
.findByIndex(nFormatNo
);
2683 ApplyPatternArea(nStartCol
, nStartRow
, nEndCol
, nEndRow
, rAttr
);
2687 void ScTable::AutoFormat( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
,
2688 sal_uInt16 nFormatNo
, ScProgress
* pProgress
)
2690 if (!(ValidColRow(nStartCol
, nStartRow
) && ValidColRow(nEndCol
, nEndRow
)))
2693 ScAutoFormat
& rFormat
= *ScGlobal::GetOrCreateAutoFormat();
2694 ScAutoFormatData
* pData
= rFormat
.findByIndex(nFormatNo
);
2698 std::unique_ptr
<ScPatternAttr
> pPatternAttrs
[16];
2699 for (sal_uInt8 i
= 0; i
< 16; ++i
)
2701 pPatternAttrs
[i
].reset(new ScPatternAttr(rDocument
.getCellAttributeHelper()));
2702 pData
->FillToItemSet(i
, pPatternAttrs
[i
]->GetItemSet(), rDocument
);
2705 // Important special case: when the whole rows are selected. Then applying autoformat to right
2706 // column individually would create all columns. In this case, assume that right column isn't
2707 // needed, to allow "to the end of row" format optimization (which doesn't create columns).
2708 // Keep left column in this case, because it may be pre-formatted for categories. To enable the
2709 // optimization, apply uniform format row by row where possible, not column by column.
2711 const bool isWholeRows
= nStartCol
== 0 && nEndCol
== rDocument
.MaxCol();
2713 ////////////////////////////////////////////////////////////////////////////////////////////////
2714 // Top row data indexes:
2715 // 0 - left corner style
2716 // 1 - odd columns style
2717 // 2 - even column style
2718 // 3 - right corner style
2719 ////////////////////////////////////////////////////////////////////////////////////////////////
2721 SCCOL startOffset
= 1;
2723 if (pData
->HasSameData(0, 1) && pData
->HasSameData(0, 2))
2724 startOffset
= 0; // Left corner is same as the rest of the row
2726 AutoFormatArea(nStartCol
, nStartRow
, nStartCol
, nStartRow
, *pPatternAttrs
[0], nFormatNo
);
2728 SCCOL endOffset
= 1;
2729 // Right top corner: ignore when whole rows selected
2730 if (isWholeRows
|| (pData
->HasSameData(3, 1) && pData
->HasSameData(3, 2)))
2731 endOffset
= 0; // Right corner is same as the rest of the row (most important case)
2733 AutoFormatArea(nEndCol
, nStartRow
, nEndCol
, nStartRow
, *pPatternAttrs
[3], nFormatNo
);
2736 if (pData
->HasSameData(1, 2))
2737 AutoFormatArea(nStartCol
+ startOffset
, nStartRow
, nEndCol
- endOffset
, nStartRow
, *pPatternAttrs
[1], nFormatNo
);
2740 sal_uInt16 nIndex
= 1;
2741 for (SCCOL nCol
= nStartCol
+ startOffset
; nCol
<= nEndCol
- endOffset
; nCol
++)
2743 AutoFormatArea(nCol
, nStartRow
, nCol
, nStartRow
, *pPatternAttrs
[nIndex
], nFormatNo
);
2751 ////////////////////////////////////////////////////////////////////////////////////////////////
2752 // Body data indexes:
2753 // 4 - left column odd row style
2754 // 8 - left column even row style
2755 // 7 - right column odd row style
2756 // 11 - right column even row style
2757 // 5 - body odd column odd row style
2758 // 6 - body even column odd row style
2759 // 9 - body odd column even row style
2760 // 10 - body even column even row style
2761 ////////////////////////////////////////////////////////////////////////////////////////////////
2765 if (pData
->HasSameData(4, 5) && pData
->HasSameData(4, 6) && pData
->HasSameData(8, 9) && pData
->HasSameData(8, 10))
2766 startOffset
= 0; // Left column is same as the lines of the body
2769 if (pData
->HasSameData(4, 8)) // even and odd rows are same
2770 AutoFormatArea(nStartCol
, nStartRow
+ 1, nStartCol
, nEndRow
- 1, *pPatternAttrs
[4], nFormatNo
);
2773 sal_uInt16 nIndex
= 4;
2774 for (SCROW nRow
= nStartRow
+ 1; nRow
< nEndRow
; nRow
++)
2776 AutoFormatArea(nStartCol
, nRow
, nStartCol
, nRow
, *pPatternAttrs
[nIndex
], nFormatNo
);
2786 // Right column: ignore when whole rows selected
2787 if (isWholeRows
|| (pData
->HasSameData(7, 5) && pData
->HasSameData(7, 6) && pData
->HasSameData(11, 9) && pData
->HasSameData(11, 10)))
2788 endOffset
= 0; // Right column is same as the lines of the body (most important case)
2791 if (pData
->HasSameData(7, 11)) // even and odd rows are same
2792 AutoFormatArea(nEndCol
, nStartRow
+ 1, nEndCol
, nEndRow
- 1, *pPatternAttrs
[7], nFormatNo
);
2795 sal_uInt16 nIndex
= 7;
2796 for (SCROW nRow
= nStartRow
+ 1; nRow
< nEndRow
; nRow
++)
2798 AutoFormatArea(nEndCol
, nRow
, nEndCol
, nRow
, *pPatternAttrs
[nIndex
], nFormatNo
);
2808 if (pData
->HasSameData(5, 6) && pData
->HasSameData(9, 10)) // Odd and even columns are same (most important case)
2810 if (pData
->HasSameData(5, 9)) // Everything is the same
2811 AutoFormatArea(nStartCol
+ startOffset
, nStartRow
+ 1, nEndCol
- endOffset
, nEndRow
- 1, *pPatternAttrs
[5], nFormatNo
);
2812 else // Odd and even rows differ
2815 pProgress
->SetState(1, nEndRow
- nStartRow
+ 3); // account for elements outside the "Body" block
2816 sal_uInt16 nIndex
= 5;
2817 for (SCROW nRow
= nStartRow
+ 1; nRow
< nEndRow
; nRow
++)
2819 AutoFormatArea(nStartCol
+ startOffset
, nRow
, nEndCol
- endOffset
, nRow
, *pPatternAttrs
[nIndex
], nFormatNo
);
2825 pProgress
->SetStateOnPercent(nRow
- nStartRow
+ 1);
2829 else if (pData
->HasSameData(5, 9) && pData
->HasSameData(6, 10)) // odd and even rows are same
2832 pProgress
->SetState(1, nEndCol
- nStartCol
+ 3); // account for elements outside the "Body" block
2833 sal_uInt16 nIndex
= 5;
2834 for (SCCOL nCol
= nStartCol
+ startOffset
; nCol
<= nEndCol
- endOffset
; nCol
++)
2836 AutoFormatArea(nCol
, nStartRow
+ 1, nCol
, nEndRow
- 1, *pPatternAttrs
[nIndex
], nFormatNo
);
2842 pProgress
->SetStateOnPercent(nCol
- nStartCol
+ 1);
2845 else // Everything is different
2848 pProgress
->SetState(1, nEndCol
- nStartCol
+ 3); // account for elements outside the "Body" block
2849 sal_uInt16 nIndex
= 5;
2850 for (SCCOL nCol
= nStartCol
+ startOffset
; nCol
<= nEndCol
- endOffset
; nCol
++)
2852 for (SCROW nRow
= nStartRow
+ 1; nRow
< nEndRow
; nRow
++)
2854 AutoFormatArea(nCol
, nRow
, nCol
, nRow
, *pPatternAttrs
[nIndex
], nFormatNo
);
2855 if ((nIndex
== 5) || (nIndex
== 9))
2870 if ((nIndex
== 5) || (nIndex
== 9))
2875 pProgress
->SetStateOnPercent(nCol
- nStartCol
+ 1);
2878 } // if not all equal
2880 ////////////////////////////////////////////////////////////////////////////////////////////////
2881 // Bottom row data indexes:
2882 // 12 - left corner style
2883 // 13 - odd columns style
2884 // 14 - even column style
2885 // 15 - right corner style
2886 ////////////////////////////////////////////////////////////////////////////////////////////////
2889 // Left bottom corner
2890 if (pData
->HasSameData(12, 13) && pData
->HasSameData(12, 14))
2893 AutoFormatArea(nStartCol
, nEndRow
, nStartCol
, nEndRow
, *pPatternAttrs
[12], nFormatNo
);
2896 // Right bottom corner: ignore when whole rows selected
2897 if (isWholeRows
|| (pData
->HasSameData(15, 13) && pData
->HasSameData(15, 14)))
2900 AutoFormatArea(nEndCol
, nEndRow
, nEndCol
, nEndRow
, *pPatternAttrs
[15], nFormatNo
);
2903 if (pData
->HasSameData(13, 14))
2904 AutoFormatArea(nStartCol
+ startOffset
, nEndRow
, nEndCol
- endOffset
, nEndRow
, *pPatternAttrs
[13], nFormatNo
);
2907 sal_uInt16 nIndex
= 13;
2908 for (SCCOL nCol
= nStartCol
+ startOffset
; nCol
<= nEndCol
- endOffset
; nCol
++)
2910 AutoFormatArea(nCol
, nEndRow
, nCol
, nEndRow
, *pPatternAttrs
[nIndex
], nFormatNo
);
2919 void ScTable::GetAutoFormatAttr(SCCOL nCol
, SCROW nRow
, sal_uInt16 nIndex
, ScAutoFormatData
& rData
)
2921 sal_uInt32 nFormatIndex
= GetNumberFormat( nCol
, nRow
);
2922 ScNumFormatAbbrev
aNumFormat( nFormatIndex
, *rDocument
.GetFormatTable() );
2923 rData
.GetFromItemSet( nIndex
, GetPattern( nCol
, nRow
)->GetItemSet(), aNumFormat
);
2930 #define LF_ALL (LF_LEFT | LF_TOP | LF_RIGHT | LF_BOTTOM)
2932 void ScTable::GetAutoFormatFrame(SCCOL nCol
, SCROW nRow
, sal_uInt16 nFlags
, sal_uInt16 nIndex
, ScAutoFormatData
& rData
)
2934 const SvxBoxItem
* pTheBox
= GetAttr(nCol
, nRow
, ATTR_BORDER
);
2935 const SvxBoxItem
* pLeftBox
= GetAttr(nCol
- 1, nRow
, ATTR_BORDER
);
2936 const SvxBoxItem
* pTopBox
= GetAttr(nCol
, nRow
- 1, ATTR_BORDER
);
2937 const SvxBoxItem
* pRightBox
= GetAttr(nCol
+ 1, nRow
, ATTR_BORDER
);
2938 const SvxBoxItem
* pBottomBox
= GetAttr(nCol
, nRow
+ 1, ATTR_BORDER
);
2940 SvxBoxItem
aBox( ATTR_BORDER
);
2941 if (nFlags
& LF_LEFT
)
2945 if (ScHasPriority(pTheBox
->GetLeft(), pLeftBox
->GetRight()))
2946 aBox
.SetLine(pTheBox
->GetLeft(), SvxBoxItemLine::LEFT
);
2948 aBox
.SetLine(pLeftBox
->GetRight(), SvxBoxItemLine::LEFT
);
2951 aBox
.SetLine(pTheBox
->GetLeft(), SvxBoxItemLine::LEFT
);
2953 if (nFlags
& LF_TOP
)
2957 if (ScHasPriority(pTheBox
->GetTop(), pTopBox
->GetBottom()))
2958 aBox
.SetLine(pTheBox
->GetTop(), SvxBoxItemLine::TOP
);
2960 aBox
.SetLine(pTopBox
->GetBottom(), SvxBoxItemLine::TOP
);
2963 aBox
.SetLine(pTheBox
->GetTop(), SvxBoxItemLine::TOP
);
2965 if (nFlags
& LF_RIGHT
)
2969 if (ScHasPriority(pTheBox
->GetRight(), pRightBox
->GetLeft()))
2970 aBox
.SetLine(pTheBox
->GetRight(), SvxBoxItemLine::RIGHT
);
2972 aBox
.SetLine(pRightBox
->GetLeft(), SvxBoxItemLine::RIGHT
);
2975 aBox
.SetLine(pTheBox
->GetRight(), SvxBoxItemLine::RIGHT
);
2977 if (nFlags
& LF_BOTTOM
)
2981 if (ScHasPriority(pTheBox
->GetBottom(), pBottomBox
->GetTop()))
2982 aBox
.SetLine(pTheBox
->GetBottom(), SvxBoxItemLine::BOTTOM
);
2984 aBox
.SetLine(pBottomBox
->GetTop(), SvxBoxItemLine::BOTTOM
);
2987 aBox
.SetLine(pTheBox
->GetBottom(), SvxBoxItemLine::BOTTOM
);
2989 rData
.PutItem( nIndex
, aBox
);
2992 void ScTable::GetAutoFormatData(SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
, ScAutoFormatData
& rData
)
2994 if (!(ValidColRow(nStartCol
, nStartRow
) && ValidColRow(nEndCol
, nEndRow
)))
2997 if ((nEndCol
- nStartCol
< 3) || (nEndRow
- nStartRow
< 3))
3001 GetAutoFormatAttr(nStartCol
, nStartRow
, 0, rData
);
3002 GetAutoFormatFrame(nStartCol
, nStartRow
, LF_ALL
, 0, rData
);
3004 GetAutoFormatAttr(nStartCol
, nStartRow
+ 1, 4, rData
);
3005 GetAutoFormatAttr(nStartCol
, nStartRow
+ 2, 8, rData
);
3006 GetAutoFormatFrame(nStartCol
, nStartRow
+ 1, LF_LEFT
| LF_RIGHT
| LF_BOTTOM
, 4, rData
);
3007 if (nEndRow
- nStartRow
>= 4)
3008 GetAutoFormatFrame(nStartCol
, nStartRow
+ 2, LF_LEFT
| LF_RIGHT
| LF_BOTTOM
, 8, rData
);
3010 rData
.CopyItem( 8, 4, ATTR_BORDER
);
3011 // Left bottom corner
3012 GetAutoFormatAttr(nStartCol
, nEndRow
, 12, rData
);
3013 GetAutoFormatFrame(nStartCol
, nEndRow
, LF_ALL
, 12, rData
);
3015 GetAutoFormatAttr(nEndCol
, nStartRow
, 3, rData
);
3016 GetAutoFormatFrame(nEndCol
, nStartRow
, LF_ALL
, 3, rData
);
3018 GetAutoFormatAttr(nEndCol
, nStartRow
+ 1, 7, rData
);
3019 GetAutoFormatAttr(nEndCol
, nStartRow
+ 2, 11, rData
);
3020 GetAutoFormatFrame(nEndCol
, nStartRow
+ 1, LF_LEFT
| LF_RIGHT
| LF_BOTTOM
, 7, rData
);
3021 if (nEndRow
- nStartRow
>= 4)
3022 GetAutoFormatFrame(nEndCol
, nStartRow
+ 2, LF_LEFT
| LF_RIGHT
| LF_BOTTOM
, 11, rData
);
3024 rData
.CopyItem( 11, 7, ATTR_BORDER
);
3025 // Right bottom corner
3026 GetAutoFormatAttr(nEndCol
, nEndRow
, 15, rData
);
3027 GetAutoFormatFrame(nEndCol
, nEndRow
, LF_ALL
, 15, rData
);
3029 GetAutoFormatAttr(nStartCol
+ 1, nStartRow
, 1, rData
);
3030 GetAutoFormatAttr(nStartCol
+ 2, nStartRow
, 2, rData
);
3031 GetAutoFormatFrame(nStartCol
+ 1, nStartRow
, LF_TOP
| LF_BOTTOM
| LF_RIGHT
, 1, rData
);
3032 if (nEndCol
- nStartCol
>= 4)
3033 GetAutoFormatFrame(nStartCol
+ 2, nStartRow
, LF_TOP
| LF_BOTTOM
| LF_RIGHT
, 2, rData
);
3035 rData
.CopyItem( 2, 1, ATTR_BORDER
);
3037 GetAutoFormatAttr(nStartCol
+ 1, nEndRow
, 13, rData
);
3038 GetAutoFormatAttr(nStartCol
+ 2, nEndRow
, 14, rData
);
3039 GetAutoFormatFrame(nStartCol
+ 1, nEndRow
, LF_TOP
| LF_BOTTOM
| LF_RIGHT
, 13, rData
);
3040 if (nEndCol
- nStartCol
>= 4)
3041 GetAutoFormatFrame(nStartCol
+ 2, nEndRow
, LF_TOP
| LF_BOTTOM
| LF_RIGHT
, 14, rData
);
3043 rData
.CopyItem( 14, 13, ATTR_BORDER
);
3045 GetAutoFormatAttr(nStartCol
+ 1, nStartRow
+ 1, 5, rData
);
3046 GetAutoFormatAttr(nStartCol
+ 2, nStartRow
+ 1, 6, rData
);
3047 GetAutoFormatAttr(nStartCol
+ 1, nStartRow
+ 2, 9, rData
);
3048 GetAutoFormatAttr(nStartCol
+ 2, nStartRow
+ 2, 10, rData
);
3049 GetAutoFormatFrame(nStartCol
+ 1, nStartRow
+ 1, LF_RIGHT
| LF_BOTTOM
, 5, rData
);
3050 if ((nEndCol
- nStartCol
>= 4) && (nEndRow
- nStartRow
>= 4))
3052 GetAutoFormatFrame(nStartCol
+ 2, nStartRow
+ 1, LF_RIGHT
| LF_BOTTOM
, 6, rData
);
3053 GetAutoFormatFrame(nStartCol
+ 1, nStartRow
+ 2, LF_RIGHT
| LF_BOTTOM
, 9, rData
);
3054 GetAutoFormatFrame(nStartCol
+ 2, nStartRow
+ 2, LF_RIGHT
| LF_BOTTOM
, 10, rData
);
3058 rData
.CopyItem( 6, 5, ATTR_BORDER
);
3059 rData
.CopyItem( 9, 5, ATTR_BORDER
);
3060 rData
.CopyItem( 10, 5, ATTR_BORDER
);
3064 void ScTable::SetError( SCCOL nCol
, SCROW nRow
, FormulaError nError
)
3066 if (ValidColRow(nCol
, nRow
))
3067 CreateColumnIfNotExists(nCol
).SetError(nRow
, nError
);
3070 void ScTable::UpdateInsertTabAbs(SCTAB nTable
)
3072 for (SCCOL i
=0; i
< aCol
.size(); i
++)
3073 aCol
[i
].UpdateInsertTabAbs(nTable
);
3076 bool ScTable::GetNextSpellingCell(SCCOL
& rCol
, SCROW
& rRow
, bool bInSel
,
3077 const ScMarkData
& rMark
) const
3079 if (rRow
== rDocument
.MaxRow()+2) // end of table
3087 if (rRow
== rDocument
.MaxRow()+1)
3093 if (rCol
== rDocument
.MaxCol()+1)
3097 if (!ValidCol(rCol
))
3099 if (rCol
>= GetAllocatedColumnsCount())
3101 if (aCol
[rCol
].GetNextSpellingCell(rRow
, bInSel
, rMark
))
3103 /*else (rRow == rDocument.MaxRow()+1) */
3109 void ScTable::TestTabRefAbs(SCTAB nTable
) const
3111 for (SCCOL i
=0; i
< aCol
.size(); i
++)
3112 if (aCol
[i
].TestTabRefAbs(nTable
))
3116 void ScTable::CompileDBFormula( sc::CompileFormulaContext
& rCxt
)
3118 for (SCCOL i
= 0; i
< aCol
.size(); ++i
)
3119 aCol
[i
].CompileDBFormula(rCxt
);
3122 void ScTable::CompileColRowNameFormula( sc::CompileFormulaContext
& rCxt
)
3124 for (SCCOL i
= 0; i
< aCol
.size(); ++i
)
3125 aCol
[i
].CompileColRowNameFormula(rCxt
);
3128 SCSIZE
ScTable::GetPatternCount( SCCOL nCol
) const
3130 if( ValidCol( nCol
) )
3131 return aCol
[nCol
].GetPatternCount();
3136 SCSIZE
ScTable::GetPatternCount( SCCOL nCol
, SCROW nRow1
, SCROW nRow2
) const
3138 if( ValidCol( nCol
) && ValidRow( nRow1
) && ValidRow( nRow2
) )
3139 return aCol
[nCol
].GetPatternCount( nRow1
, nRow2
);
3144 bool ScTable::ReservePatternCount( SCCOL nCol
, SCSIZE nReserve
)
3146 if( ValidCol( nCol
) )
3147 return aCol
[nCol
].ReservePatternCount( nReserve
);
3152 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */