1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: table3.cxx,v $
10 * $Revision: 1.30.128.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 #include <rtl/math.hxx>
35 #include <unotools/textsearch.hxx>
36 #include <svtools/zforlist.hxx>
37 #include <unotools/charclass.hxx>
38 #include <unotools/collatorwrapper.hxx>
39 #include <com/sun/star/i18n/CollatorOptions.hpp>
41 #include <unotools/transliterationwrapper.hxx>
44 #include "scitems.hxx"
45 #include "collect.hxx"
48 #include "document.hxx"
49 #include "globstr.hrc"
51 #include "stlpool.hxx"
52 #include "compiler.hxx"
53 #include "patattr.hxx"
54 #include "subtotal.hxx"
55 #include "docoptio.hxx"
56 #include "markdata.hxx"
57 #include "rangelst.hxx"
58 #include "attarray.hxx"
59 #include "userlist.hxx"
60 #include "progress.hxx"
61 #include "cellform.hxx"
63 #include "queryparam.hxx"
64 #include "svtools/zformat.hxx"
68 using namespace ::com::sun::star
;
70 namespace naturalsort
{
72 using namespace ::com::sun::star::i18n
;
74 /** Splits a given string into three parts: the prefix, number string, and
78 Original string to be split into pieces
81 Prefix string that consists of the part before the first number token
84 String after the last number token. This may still contain number strings.
87 Number converted from the middle number string
89 @return Returns TRUE if a numeral element is found in a given string, or
90 FALSE if no numeral element is found.
92 bool SplitString( const rtl::OUString
&sWhole
,
93 rtl::OUString
&sPrefix
, rtl::OUString
&sSuffix
, double &fNum
)
95 i18n::LocaleDataItem aLocaleItem
= ScGlobal::pLocaleData
->getLocaleItem();
98 rtl::OUString sEmpty
, sUser
= rtl::OUString::createFromAscii( "-" );
99 ParseResult aPRPre
= ScGlobal::pCharClass
->parsePredefinedToken(
100 KParseType::IDENTNAME
, sWhole
, 0,
101 KParseTokens::ANY_LETTER
, sUser
, KParseTokens::ANY_LETTER
, sUser
);
102 sPrefix
= sWhole
.copy( 0, aPRPre
.EndPos
);
104 // Return FALSE if no numeral element is found
105 if ( aPRPre
.EndPos
== sWhole
.getLength() )
108 // Get numeral element
109 sUser
= aLocaleItem
.decimalSeparator
;
110 ParseResult aPRNum
= ScGlobal::pCharClass
->parsePredefinedToken(
111 KParseType::ANY_NUMBER
, sWhole
, aPRPre
.EndPos
,
112 KParseTokens::ANY_NUMBER
, sEmpty
, KParseTokens::ANY_NUMBER
, sUser
);
114 if ( aPRNum
.EndPos
== aPRPre
.EndPos
)
118 sSuffix
= sWhole
.copy( aPRNum
.EndPos
);
123 /** Naturally compares two given strings.
125 This is the main function that should be called externally. It returns
126 either 1, 0, or -1 depending on the comparison result of given two strings.
135 Boolean value for case sensitivity
138 Pointer to user defined sort list
141 Pointer to collator wrapper for normal string comparison
143 @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
146 short Compare( const String
&sInput1
, const String
&sInput2
,
147 const BOOL bCaseSens
, const ScUserListData
* pData
, const CollatorWrapper
*pCW
)
149 rtl::OUString
sStr1( sInput1
), sStr2( sInput2
), sPre1
, sSuf1
, sPre2
, sSuf2
;
154 BOOL bNumFound1
= SplitString( sStr1
, sPre1
, sSuf1
, nNum1
);
155 BOOL bNumFound2
= SplitString( sStr2
, sPre2
, sSuf2
, nNum2
);
157 short nPreRes
; // Prefix comparison result
162 if ( !bNumFound1
|| !bNumFound2
)
163 return static_cast<short>(pData
->Compare( sStr1
, sStr2
));
165 nPreRes
= pData
->Compare( sPre1
, sPre2
);
169 if ( !bNumFound1
|| !bNumFound2
)
170 return static_cast<short>(pData
->ICompare( sStr1
, sStr2
));
172 nPreRes
= pData
->ICompare( sPre1
, sPre2
);
177 if ( !bNumFound1
|| !bNumFound2
)
178 return static_cast<short>(pCW
->compareString( sStr1
, sStr2
));
180 nPreRes
= static_cast<short>(pCW
->compareString( sPre1
, sPre2
));
183 // Prefix strings differ. Return immediately.
184 if ( nPreRes
!= 0 ) return nPreRes
;
186 if ( nNum1
!= nNum2
)
188 if ( nNum1
< nNum2
) return -1;
189 return static_cast<short>( nNum1
> nNum2
);
192 // The prefix and the first numerical elements are equal, but the suffix
193 // strings may still differ. Stay in the loop.
205 // STATIC DATA -----------------------------------------------------------
207 const USHORT nMaxSorts
= 3; // maximale Anzahl Sortierkriterien in aSortParam
213 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo
);
215 const USHORT nMemPoolSortInfo
= (0x8000 - 64) / sizeof(ScSortInfo
);
216 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo
, nMemPoolSortInfo
, nMemPoolSortInfo
)
218 // END OF STATIC DATA -----------------------------------------------------
221 class ScSortInfoArray
224 ScSortInfo
** pppInfo
[nMaxSorts
];
230 ScSortInfoArray( USHORT nSorts
, SCCOLROW nInd1
, SCCOLROW nInd2
) :
231 nCount( nInd2
- nInd1
+ 1 ), nStart( nInd1
),
232 nUsedSorts( Min( nSorts
, nMaxSorts
) )
234 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
236 ScSortInfo
** ppInfo
= new ScSortInfo
* [nCount
];
237 for ( SCSIZE j
= 0; j
< nCount
; j
++ )
238 ppInfo
[j
] = new ScSortInfo
;
239 pppInfo
[nSort
] = ppInfo
;
244 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
246 ScSortInfo
** ppInfo
= pppInfo
[nSort
];
247 for ( SCSIZE j
= 0; j
< nCount
; j
++ )
252 ScSortInfo
* Get( USHORT nSort
, SCCOLROW nInd
)
253 { return (pppInfo
[nSort
])[ nInd
- nStart
]; }
254 void Swap( SCCOLROW nInd1
, SCCOLROW nInd2
)
256 SCSIZE n1
= static_cast<SCSIZE
>(nInd1
- nStart
);
257 SCSIZE n2
= static_cast<SCSIZE
>(nInd2
- nStart
);
258 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
260 ScSortInfo
** ppInfo
= pppInfo
[nSort
];
261 ScSortInfo
* pTmp
= ppInfo
[n1
];
262 ppInfo
[n1
] = ppInfo
[n2
];
266 USHORT
GetUsedSorts() { return nUsedSorts
; }
267 ScSortInfo
** GetFirstArray() { return pppInfo
[0]; }
268 SCCOLROW
GetStart() { return nStart
; }
269 SCSIZE
GetCount() { return nCount
; }
272 ScSortInfoArray
* ScTable::CreateSortInfoArray( SCCOLROW nInd1
, SCCOLROW nInd2
)
274 USHORT nUsedSorts
= 1;
275 while ( nUsedSorts
< nMaxSorts
&& aSortParam
.bDoSort
[nUsedSorts
] )
277 ScSortInfoArray
* pArray
= new ScSortInfoArray( nUsedSorts
, nInd1
, nInd2
);
278 if ( aSortParam
.bByRow
)
280 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
282 SCCOL nCol
= static_cast<SCCOL
>(aSortParam
.nField
[nSort
]);
283 ScColumn
* pCol
= &aCol
[nCol
];
284 for ( SCROW nRow
= nInd1
; nRow
<= nInd2
; nRow
++ )
286 //2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell
287 ScSortInfo
* pInfo
= pArray
->Get( nSort
, nRow
);
288 pInfo
->pCell
= pCol
->GetCell( nRow
);
295 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
297 SCROW nRow
= aSortParam
.nField
[nSort
];
298 for ( SCCOL nCol
= static_cast<SCCOL
>(nInd1
);
299 nCol
<= static_cast<SCCOL
>(nInd2
); nCol
++ )
301 ScSortInfo
* pInfo
= pArray
->Get( nSort
, nCol
);
302 pInfo
->pCell
= GetCell( nCol
, nRow
);
311 BOOL
ScTable::IsSortCollatorGlobal() const
313 return pSortCollator
== ScGlobal::GetCollator() ||
314 pSortCollator
== ScGlobal::GetCaseCollator();
318 void ScTable::InitSortCollator( const ScSortParam
& rPar
)
320 if ( rPar
.aCollatorLocale
.Language
.getLength() )
322 if ( !pSortCollator
|| IsSortCollatorGlobal() )
323 pSortCollator
= new CollatorWrapper( pDocument
->GetServiceManager() );
324 pSortCollator
->loadCollatorAlgorithm( rPar
.aCollatorAlgorithm
,
325 rPar
.aCollatorLocale
, (rPar
.bCaseSens
? 0 : SC_COLLATOR_IGNORES
) );
329 DestroySortCollator();
330 pSortCollator
= (rPar
.bCaseSens
? ScGlobal::GetCaseCollator() :
331 ScGlobal::GetCollator());
336 void ScTable::DestroySortCollator()
340 if ( !IsSortCollatorGlobal() )
341 delete pSortCollator
;
342 pSortCollator
= NULL
;
347 void ScTable::SortReorder( ScSortInfoArray
* pArray
, ScProgress
& rProgress
)
349 BOOL bByRow
= aSortParam
.bByRow
;
350 SCSIZE nCount
= pArray
->GetCount();
351 SCCOLROW nStart
= pArray
->GetStart();
352 ScSortInfo
** ppInfo
= pArray
->GetFirstArray();
353 ::std::vector
<ScSortInfo
*> aTable(nCount
);
355 for ( nPos
= 0; nPos
< nCount
; nPos
++ )
356 aTable
[ppInfo
[nPos
]->nOrg
- nStart
] = ppInfo
[nPos
];
358 SCCOLROW nDest
= nStart
;
359 for ( nPos
= 0; nPos
< nCount
; nPos
++, nDest
++ )
361 SCCOLROW nOrg
= ppInfo
[nPos
]->nOrg
;
365 SwapRow( nDest
, nOrg
);
367 SwapCol( static_cast<SCCOL
>(nDest
), static_cast<SCCOL
>(nOrg
) );
368 // neue Position des weggeswapten eintragen
369 ScSortInfo
* p
= ppInfo
[nPos
];
371 ::std::swap(p
, aTable
[nDest
-nStart
]);
373 ::std::swap(p
, aTable
[nOrg
-nStart
]);
374 DBG_ASSERT( p
== ppInfo
[nPos
], "SortReorder: nOrg MisMatch" );
376 rProgress
.SetStateOnPercent( nPos
);
380 short ScTable::CompareCell( USHORT nSort
,
381 ScBaseCell
* pCell1
, SCCOL nCell1Col
, SCROW nCell1Row
,
382 ScBaseCell
* pCell2
, SCCOL nCell2Col
, SCROW nCell2Row
)
386 CellType eType1
= CELLTYPE_NONE
, eType2
= CELLTYPE_NONE
;
389 eType1
= pCell1
->GetCellType();
390 if (eType1
== CELLTYPE_NOTE
)
395 eType2
= pCell2
->GetCellType();
396 if (eType2
== CELLTYPE_NOTE
)
404 BOOL bStr1
= ( eType1
!= CELLTYPE_VALUE
);
405 if ( eType1
== CELLTYPE_FORMULA
&& ((ScFormulaCell
*)pCell1
)->IsValue() )
407 BOOL bStr2
= ( eType2
!= CELLTYPE_VALUE
);
408 if ( eType2
== CELLTYPE_FORMULA
&& ((ScFormulaCell
*)pCell2
)->IsValue() )
411 if ( bStr1
&& bStr2
) // nur Strings untereinander als String vergleichen!
415 if (eType1
== CELLTYPE_STRING
)
416 ((ScStringCell
*)pCell1
)->GetString(aStr1
);
418 GetString(nCell1Col
, nCell1Row
, aStr1
);
419 if (eType2
== CELLTYPE_STRING
)
420 ((ScStringCell
*)pCell2
)->GetString(aStr2
);
422 GetString(nCell2Col
, nCell2Row
, aStr2
);
424 BOOL bUserDef
= aSortParam
.bUserDef
; // custom sort order
425 BOOL bNaturalSort
= aSortParam
.bNaturalSort
; // natural sort
426 BOOL bCaseSens
= aSortParam
.bCaseSens
; // case sensitivity
430 ScUserListData
* pData
=
431 static_cast<ScUserListData
*>( (ScGlobal::GetUserList()->At(
432 aSortParam
.nUserIndex
)) );
437 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, pData
, pSortCollator
);
441 nRes
= sal::static_int_cast
<short>( pData
->Compare(aStr1
, aStr2
) );
443 nRes
= sal::static_int_cast
<short>( pData
->ICompare(aStr1
, aStr2
) );
453 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, NULL
, pSortCollator
);
455 nRes
= static_cast<short>( pSortCollator
->compareString( aStr1
, aStr2
) );
458 else if ( bStr1
) // String <-> Zahl
459 nRes
= 1; // Zahl vorne
460 else if ( bStr2
) // Zahl <-> String
461 nRes
= -1; // Zahl vorne
462 else // Zahlen untereinander
466 if (eType1
== CELLTYPE_VALUE
)
467 nVal1
= ((ScValueCell
*)pCell1
)->GetValue();
468 else if (eType1
== CELLTYPE_FORMULA
)
469 nVal1
= ((ScFormulaCell
*)pCell1
)->GetValue();
472 if (eType2
== CELLTYPE_VALUE
)
473 nVal2
= ((ScValueCell
*)pCell2
)->GetValue();
474 else if (eType2
== CELLTYPE_FORMULA
)
475 nVal2
= ((ScFormulaCell
*)pCell2
)->GetValue();
480 else if (nVal1
> nVal2
)
483 if ( !aSortParam
.bAscending
[nSort
] )
494 nRes
= 0; // beide leer
499 short ScTable::Compare( ScSortInfoArray
* pArray
, SCCOLROW nIndex1
, SCCOLROW nIndex2
)
505 ScSortInfo
* pInfo1
= pArray
->Get( nSort
, nIndex1
);
506 ScSortInfo
* pInfo2
= pArray
->Get( nSort
, nIndex2
);
507 if ( aSortParam
.bByRow
)
508 nRes
= CompareCell( nSort
,
509 pInfo1
->pCell
, static_cast<SCCOL
>(aSortParam
.nField
[nSort
]), pInfo1
->nOrg
,
510 pInfo2
->pCell
, static_cast<SCCOL
>(aSortParam
.nField
[nSort
]), pInfo2
->nOrg
);
512 nRes
= CompareCell( nSort
,
513 pInfo1
->pCell
, static_cast<SCCOL
>(pInfo1
->nOrg
), aSortParam
.nField
[nSort
],
514 pInfo2
->pCell
, static_cast<SCCOL
>(pInfo2
->nOrg
), aSortParam
.nField
[nSort
] );
515 } while ( nRes
== 0 && ++nSort
< pArray
->GetUsedSorts() );
518 ScSortInfo
* pInfo1
= pArray
->Get( 0, nIndex1
);
519 ScSortInfo
* pInfo2
= pArray
->Get( 0, nIndex2
);
520 if( pInfo1
->nOrg
< pInfo2
->nOrg
)
522 else if( pInfo1
->nOrg
> pInfo2
->nOrg
)
528 void ScTable::QuickSort( ScSortInfoArray
* pArray
, SCsCOLROW nLo
, SCsCOLROW nHi
)
530 if ((nHi
- nLo
) == 1)
532 if (Compare(pArray
, nLo
, nHi
) > 0)
533 pArray
->Swap( nLo
, nHi
);
541 while ((ni
<= nHi
) && (Compare(pArray
, ni
, nLo
)) < 0)
543 while ((nj
>= nLo
) && (Compare(pArray
, nLo
, nj
)) < 0)
548 pArray
->Swap( ni
, nj
);
553 if ((nj
- nLo
) < (nHi
- ni
))
556 QuickSort(pArray
, nLo
, nj
);
558 QuickSort(pArray
, ni
, nHi
);
563 QuickSort(pArray
, ni
, nHi
);
565 QuickSort(pArray
, nLo
, nj
);
570 void ScTable::SwapCol(SCCOL nCol1
, SCCOL nCol2
)
572 for (SCROW nRow
= aSortParam
.nRow1
; nRow
<= aSortParam
.nRow2
; nRow
++)
574 aCol
[nCol1
].SwapCell(nRow
, aCol
[nCol2
]);
575 if (aSortParam
.bIncludePattern
)
577 const ScPatternAttr
* pPat1
= GetPattern(nCol1
, nRow
);
578 const ScPatternAttr
* pPat2
= GetPattern(nCol2
, nRow
);
581 SetPattern(nCol1
, nRow
, *pPat2
, TRUE
);
582 SetPattern(nCol2
, nRow
, *pPat1
, TRUE
);
588 void ScTable::SwapRow(SCROW nRow1
, SCROW nRow2
)
590 for (SCCOL nCol
= aSortParam
.nCol1
; nCol
<= aSortParam
.nCol2
; nCol
++)
592 aCol
[nCol
].SwapRow(nRow1
, nRow2
);
593 if (aSortParam
.bIncludePattern
)
595 const ScPatternAttr
* pPat1
= GetPattern(nCol
, nRow1
);
596 const ScPatternAttr
* pPat2
= GetPattern(nCol
, nRow2
);
599 SetPattern(nCol
, nRow1
, *pPat2
, TRUE
);
600 SetPattern(nCol
, nRow2
, *pPat1
, TRUE
);
604 if (bGlobalKeepQuery
)
606 bool bRow1Hidden
= RowHidden(nRow1
);
607 bool bRow2Hidden
= RowHidden(nRow2
);
608 SetRowHidden(nRow1
, nRow1
, bRow2Hidden
);
609 SetRowHidden(nRow2
, nRow2
, bRow1Hidden
);
611 bool bRow1Filtered
= RowFiltered(nRow1
);
612 bool bRow2Filtered
= RowFiltered(nRow2
);
613 SetRowFiltered(nRow1
, nRow1
, bRow2Filtered
);
614 SetRowFiltered(nRow2
, nRow2
, bRow1Filtered
);
618 short ScTable::Compare(SCCOLROW nIndex1
, SCCOLROW nIndex2
)
622 if (aSortParam
.bByRow
)
626 SCCOL nCol
= static_cast<SCCOL
>(aSortParam
.nField
[nSort
]);
627 ScBaseCell
* pCell1
= aCol
[nCol
].GetCell( nIndex1
);
628 ScBaseCell
* pCell2
= aCol
[nCol
].GetCell( nIndex2
);
629 nRes
= CompareCell( nSort
, pCell1
, nCol
, nIndex1
, pCell2
, nCol
, nIndex2
);
630 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.bDoSort
[nSort
] );
636 SCROW nRow
= aSortParam
.nField
[nSort
];
637 ScBaseCell
* pCell1
= aCol
[nIndex1
].GetCell( nRow
);
638 ScBaseCell
* pCell2
= aCol
[nIndex2
].GetCell( nRow
);
639 nRes
= CompareCell( nSort
, pCell1
, static_cast<SCCOL
>(nIndex1
),
640 nRow
, pCell2
, static_cast<SCCOL
>(nIndex2
), nRow
);
641 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.bDoSort
[nSort
] );
646 BOOL
ScTable::IsSorted( SCCOLROW nStart
, SCCOLROW nEnd
) // ueber aSortParam
648 for (SCCOLROW i
=nStart
; i
<nEnd
; i
++)
650 if (Compare( i
, i
+1 ) > 0)
656 void ScTable::DecoladeRow( ScSortInfoArray
* pArray
, SCROW nRow1
, SCROW nRow2
)
659 SCROW nMax
= nRow2
- nRow1
;
660 for (SCROW i
= nRow1
; (i
+ 4) <= nRow2
; i
+= 4)
662 nRow
= rand() % nMax
;
663 pArray
->Swap(i
, nRow1
+ nRow
);
667 void ScTable::Sort(const ScSortParam
& rSortParam
, BOOL bKeepQuery
)
669 aSortParam
= rSortParam
;
670 InitSortCollator( rSortParam
);
671 bGlobalKeepQuery
= bKeepQuery
;
672 if (rSortParam
.bByRow
)
675 for (SCCOL nCol
= aSortParam
.nCol1
; nCol
<= aSortParam
.nCol2
; nCol
++)
676 nLastRow
= Max(nLastRow
, aCol
[nCol
].GetLastDataPos());
677 nLastRow
= Min(nLastRow
, aSortParam
.nRow2
);
678 SCROW nRow1
= (rSortParam
.bHasHeader
?
679 aSortParam
.nRow1
+ 1 : aSortParam
.nRow1
);
680 if (!IsSorted(nRow1
, nLastRow
))
682 ScProgress
aProgress( pDocument
->GetDocumentShell(),
683 ScGlobal::GetRscString(STR_PROGRESS_SORTING
), nLastRow
- nRow1
);
684 ScSortInfoArray
* pArray
= CreateSortInfoArray( nRow1
, nLastRow
);
685 if ( nLastRow
- nRow1
> 255 )
686 DecoladeRow( pArray
, nRow1
, nLastRow
);
687 QuickSort( pArray
, nRow1
, nLastRow
);
688 SortReorder( pArray
, aProgress
);
690 // #158377# #i59745# update position of caption objects of cell notes
691 ScNoteUtil::UpdateCaptionPositions( *pDocument
, ScRange( aSortParam
.nCol1
, nRow1
, nTab
, aSortParam
.nCol2
, nLastRow
, nTab
) );
697 for (nLastCol
= aSortParam
.nCol2
;
698 (nLastCol
> aSortParam
.nCol1
) && aCol
[nLastCol
].IsEmptyBlock(aSortParam
.nRow1
, aSortParam
.nRow2
); nLastCol
--)
701 SCCOL nCol1
= (rSortParam
.bHasHeader
?
702 aSortParam
.nCol1
+ 1 : aSortParam
.nCol1
);
703 if (!IsSorted(nCol1
, nLastCol
))
705 ScProgress
aProgress( pDocument
->GetDocumentShell(),
706 ScGlobal::GetRscString(STR_PROGRESS_SORTING
), nLastCol
- nCol1
);
707 ScSortInfoArray
* pArray
= CreateSortInfoArray( nCol1
, nLastCol
);
708 QuickSort( pArray
, nCol1
, nLastCol
);
709 SortReorder( pArray
, aProgress
);
711 // #158377# #i59745# update position of caption objects of cell notes
712 ScNoteUtil::UpdateCaptionPositions( *pDocument
, ScRange( nCol1
, aSortParam
.nRow1
, nTab
, nLastCol
, aSortParam
.nRow2
, nTab
) );
715 DestroySortCollator();
719 // Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden
720 // (fuer Hinweis-Box)
722 BOOL
ScTable::TestRemoveSubTotals( const ScSubTotalParam
& rParam
)
724 SCCOL nStartCol
= rParam
.nCol1
;
725 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
726 SCCOL nEndCol
= rParam
.nCol2
;
727 SCROW nEndRow
= rParam
.nRow2
;
733 BOOL bWillDelete
= FALSE
;
734 for ( nCol
=nStartCol
; nCol
<=nEndCol
&& !bWillDelete
; nCol
++ )
736 ScColumnIterator
aIter( &aCol
[nCol
],nStartRow
,nEndRow
);
737 while ( aIter
.Next( nRow
, pCell
) && !bWillDelete
)
739 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
740 if (((ScFormulaCell
*)pCell
)->IsSubTotal())
742 for (SCCOL nTestCol
=0; nTestCol
<=MAXCOL
; nTestCol
++)
743 if (nTestCol
<nStartCol
|| nTestCol
>nEndCol
)
744 if (aCol
[nTestCol
].HasDataAt(nRow
))
752 // alte Ergebnisse loeschen
753 // rParam.nRow2 wird veraendert !
755 void ScTable::RemoveSubTotals( ScSubTotalParam
& rParam
)
757 SCCOL nStartCol
= rParam
.nCol1
;
758 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
759 SCCOL nEndCol
= rParam
.nCol2
;
760 SCROW nEndRow
= rParam
.nRow2
; // wird veraendert
766 for ( nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++ )
768 ScColumnIterator
aIter( &aCol
[nCol
],nStartRow
,nEndRow
);
769 while ( aIter
.Next( nRow
, pCell
) )
771 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
772 if (((ScFormulaCell
*)pCell
)->IsSubTotal())
774 RemoveRowBreak(nRow
+1, false, true);
775 pDocument
->DeleteRow( 0,nTab
, MAXCOL
,nTab
, nRow
, 1 );
777 aIter
= ScColumnIterator( &aCol
[nCol
],nRow
,nEndRow
);
782 rParam
.nRow2
= nEndRow
; // neues Ende
785 // harte Zahlenformate loeschen (fuer Ergebnisformeln)
787 void lcl_RemoveNumberFormat( ScTable
* pTab
, SCCOL nCol
, SCROW nRow
)
789 const ScPatternAttr
* pPattern
= pTab
->GetPattern( nCol
, nRow
);
790 if ( pPattern
->GetItemSet().GetItemState( ATTR_VALUE_FORMAT
, FALSE
)
793 ScPatternAttr
aNewPattern( *pPattern
);
794 SfxItemSet
& rSet
= aNewPattern
.GetItemSet();
795 rSet
.ClearItem( ATTR_VALUE_FORMAT
);
796 rSet
.ClearItem( ATTR_LANGUAGE_FORMAT
);
797 pTab
->SetPattern( nCol
, nRow
, aNewPattern
, TRUE
);
802 // at least MSC needs this at linkage level to be able to use it in a template
803 typedef struct lcl_ScTable_DoSubTotals_RowEntry
812 // neue Zwischenergebnisse
813 // rParam.nRow2 wird veraendert !
815 BOOL
ScTable::DoSubTotals( ScSubTotalParam
& rParam
)
817 SCCOL nStartCol
= rParam
.nCol1
;
818 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
819 SCCOL nEndCol
= rParam
.nCol2
;
820 SCROW nEndRow
= rParam
.nRow2
; // wird veraendert
823 // Leerzeilen am Ende weglassen,
824 // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#)
825 // Wenn sortiert wurde, sind alle Leerzeilen am Ende.
826 SCSIZE nEmpty
= GetEmptyLinesInBlock( nStartCol
, nStartRow
, nEndCol
, nEndRow
, DIR_BOTTOM
);
829 USHORT nLevelCount
= 0; // Anzahl Gruppierungen
831 for (i
=0; i
<MAXSUBTOTAL
&& bDoThis
; i
++)
832 if (rParam
.bGroupActive
[i
])
837 if (nLevelCount
==0) // nichts tun
840 SCCOL
* nGroupCol
= rParam
.nField
; // Spalten nach denen
843 // #44444# Durch (leer) als eigene Kategorie muss immer auf
844 // Teilergebniszeilen aus den anderen Spalten getestet werden
845 // (frueher nur, wenn eine Spalte mehrfach vorkam)
846 BOOL bTestPrevSub
= ( nLevelCount
> 1 );
851 BOOL bIgnoreCase
= !rParam
.bCaseSens
;
853 String
*pCompString
[MAXSUBTOTAL
]; // Pointer wegen Compiler-Problemen
854 for (i
=0; i
<MAXSUBTOTAL
; i
++)
855 pCompString
[i
] = new String
;
859 ScStyleSheet
* pStyle
= (ScStyleSheet
*) pDocument
->GetStyleSheetPool()->Find(
860 ScGlobal::GetRscString(STR_STYLENAME_RESULT
), SFX_STYLE_FAMILY_PARA
);
862 BOOL bSpaceLeft
= TRUE
; // Erfolg beim Einfuegen?
864 // #90279# For performance reasons collect formula entries so their
865 // references don't have to be tested for updates each time a new row is
868 ::std::vector
< RowEntry
> aRowVector
;
870 for (USHORT nLevel
=0; nLevel
<=nLevelCount
&& bSpaceLeft
; nLevel
++) // incl. Gesamtergebnis
872 BOOL bTotal
= ( nLevel
== nLevelCount
);
873 aRowEntry
.nGroupNo
= bTotal
? 0 : (nLevelCount
-nLevel
-1);
875 // how many results per level
876 SCCOL nResCount
= rParam
.nSubTotals
[aRowEntry
.nGroupNo
];
878 ScSubTotalFunc
* eResFunc
= rParam
.pFunctions
[aRowEntry
.nGroupNo
];
880 if (nResCount
> 0) // sonst nur sortieren
882 for (i
=0; i
<=aRowEntry
.nGroupNo
; i
++)
884 GetString( nGroupCol
[i
], nStartRow
, aSubString
);
886 *pCompString
[i
] = ScGlobal::pCharClass
->upper( aSubString
);
888 *pCompString
[i
] = aSubString
;
889 } // aSubString bleibt auf dem letzten stehen
891 BOOL bBlockVis
= FALSE
; // Gruppe eingeblendet?
892 aRowEntry
.nSubStartRow
= nStartRow
;
893 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
+1 && bSpaceLeft
; nRow
++)
904 for (i
=0; i
<=aRowEntry
.nGroupNo
&& !bChanged
; i
++)
906 GetString( nGroupCol
[i
], nRow
, aString
);
908 ScGlobal::pCharClass
->toUpper( aString
);
909 // #41427# wenn sortiert, ist "leer" eine eigene Gruppe
910 // sonst sind leere Zellen unten erlaubt
911 bChanged
= ( ( aString
.Len() || rParam
.bDoSort
) &&
912 aString
!= *pCompString
[i
] );
914 if ( bChanged
&& bTestPrevSub
)
916 // No group change on rows that will contain subtotal formulas
917 for ( ::std::vector
< RowEntry
>::const_iterator
918 iEntry( aRowVector
.begin());
919 iEntry
!= aRowVector
.end(); ++iEntry
)
921 if ( iEntry
->nDestRow
== nRow
)
932 aRowEntry
.nDestRow
= nRow
;
933 aRowEntry
.nFuncStart
= aRowEntry
.nSubStartRow
;
934 aRowEntry
.nFuncEnd
= nRow
-1;
936 bSpaceLeft
= pDocument
->InsertRow( 0, nTab
, MAXCOL
, nTab
,
937 aRowEntry
.nDestRow
, 1 );
938 DBShowRow( aRowEntry
.nDestRow
, bBlockVis
);
940 if ( rParam
.bPagebreak
&& nRow
< MAXROW
&&
941 aRowEntry
.nSubStartRow
!= nStartRow
&& nLevel
== 0)
942 SetRowBreak(aRowEntry
.nSubStartRow
, false, true);
946 for ( ::std::vector
< RowEntry
>::iterator
iMove(
947 aRowVector
.begin() );
948 iMove
!= aRowVector
.end(); ++iMove
)
950 if ( aRowEntry
.nDestRow
<= iMove
->nSubStartRow
)
951 ++iMove
->nSubStartRow
;
952 if ( aRowEntry
.nDestRow
<= iMove
->nDestRow
)
954 if ( aRowEntry
.nDestRow
<= iMove
->nFuncStart
)
956 if ( aRowEntry
.nDestRow
<= iMove
->nFuncEnd
)
959 // collect formula positions
960 aRowVector
.push_back( aRowEntry
);
962 if (bTotal
) // "Gesamtergebnis"
963 aOutString
= ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS
);
966 aOutString
= aSubString
;
967 if (!aOutString
.Len())
968 aOutString
= ScGlobal::GetRscString( STR_EMPTYDATA
);
970 USHORT nStrId
= STR_TABLE_ERGEBNIS
;
971 if ( nResCount
== 1 )
972 switch ( eResFunc
[0] )
974 case SUBTOTAL_FUNC_AVE
: nStrId
= STR_FUN_TEXT_AVG
; break;
975 case SUBTOTAL_FUNC_CNT
:
976 case SUBTOTAL_FUNC_CNT2
: nStrId
= STR_FUN_TEXT_COUNT
; break;
977 case SUBTOTAL_FUNC_MAX
: nStrId
= STR_FUN_TEXT_MAX
; break;
978 case SUBTOTAL_FUNC_MIN
: nStrId
= STR_FUN_TEXT_MIN
; break;
979 case SUBTOTAL_FUNC_PROD
: nStrId
= STR_FUN_TEXT_PRODUCT
; break;
980 case SUBTOTAL_FUNC_STD
:
981 case SUBTOTAL_FUNC_STDP
: nStrId
= STR_FUN_TEXT_STDDEV
; break;
982 case SUBTOTAL_FUNC_SUM
: nStrId
= STR_FUN_TEXT_SUM
; break;
983 case SUBTOTAL_FUNC_VAR
:
984 case SUBTOTAL_FUNC_VARP
: nStrId
= STR_FUN_TEXT_VAR
; break;
987 // added to avoid warnings
990 aOutString
+= ScGlobal::GetRscString( nStrId
);
992 SetString( nGroupCol
[aRowEntry
.nGroupNo
], aRowEntry
.nDestRow
, nTab
, aOutString
);
993 ApplyStyle( nGroupCol
[aRowEntry
.nGroupNo
], aRowEntry
.nDestRow
, *pStyle
);
997 aRowEntry
.nSubStartRow
= nRow
;
998 for (i
=0; i
<=aRowEntry
.nGroupNo
; i
++)
1000 GetString( nGroupCol
[i
], nRow
, aSubString
);
1002 *pCompString
[i
] = ScGlobal::pCharClass
->upper( aSubString
);
1004 *pCompString
[i
] = aSubString
;
1008 bBlockVis
= !RowFiltered(nRow
);
1013 // DBG_ERROR( "nSubTotals==0 bei DoSubTotals" );
1017 // now insert the formulas
1018 ScComplexRefData aRef
;
1020 aRef
.Ref1
.nTab
= nTab
;
1021 aRef
.Ref2
.nTab
= nTab
;
1022 for ( ::std::vector
< RowEntry
>::const_iterator
iEntry( aRowVector
.begin());
1023 iEntry
!= aRowVector
.end(); ++iEntry
)
1025 SCCOL nResCount
= rParam
.nSubTotals
[iEntry
->nGroupNo
];
1026 SCCOL
* nResCols
= rParam
.pSubTotals
[iEntry
->nGroupNo
];
1027 ScSubTotalFunc
* eResFunc
= rParam
.pFunctions
[iEntry
->nGroupNo
];
1028 for ( SCCOL nResult
=0; nResult
< nResCount
; ++nResult
)
1030 aRef
.Ref1
.nCol
= nResCols
[nResult
];
1031 aRef
.Ref1
.nRow
= iEntry
->nFuncStart
;
1032 aRef
.Ref2
.nCol
= nResCols
[nResult
];
1033 aRef
.Ref2
.nRow
= iEntry
->nFuncEnd
;
1036 aArr
.AddOpCode( ocSubTotal
);
1037 aArr
.AddOpCode( ocOpen
);
1038 aArr
.AddDouble( (double) eResFunc
[nResult
] );
1039 aArr
.AddOpCode( ocSep
);
1040 aArr
.AddDoubleReference( aRef
);
1041 aArr
.AddOpCode( ocClose
);
1042 aArr
.AddOpCode( ocStop
);
1043 ScBaseCell
* pCell
= new ScFormulaCell( pDocument
, ScAddress(
1044 nResCols
[nResult
], iEntry
->nDestRow
, nTab
), &aArr
);
1045 PutCell( nResCols
[nResult
], iEntry
->nDestRow
, pCell
);
1047 if ( nResCols
[nResult
] != nGroupCol
[iEntry
->nGroupNo
] )
1049 ApplyStyle( nResCols
[nResult
], iEntry
->nDestRow
, *pStyle
);
1051 // Zahlformat loeschen
1052 lcl_RemoveNumberFormat( this, nResCols
[nResult
], iEntry
->nDestRow
);
1058 //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ?
1060 //! Outlines direkt erzeugen?
1063 DoAutoOutline( nStartCol
, nStartRow
, nEndCol
, nEndRow
);
1065 for (i
=0; i
<MAXSUBTOTAL
; i
++)
1066 delete pCompString
[i
];
1068 rParam
.nRow2
= nEndRow
; // neues Ende
1073 BOOL
ScTable::ValidQuery(SCROW nRow
, const ScQueryParam
& rParam
,
1074 BOOL
* pSpecial
/* =NULL */ , ScBaseCell
* pCell
/* =NULL */ ,
1075 BOOL
* pbTestEqualCondition
/* = NULL */ )
1077 if (!rParam
.GetEntry(0).bDoQuery
)
1080 //---------------------------------------------------------------
1082 const SCSIZE nFixedBools
= 32;
1083 BOOL aBool
[nFixedBools
];
1084 BOOL aTest
[nFixedBools
];
1085 SCSIZE nEntryCount
= rParam
.GetEntryCount();
1086 BOOL
* pPasst
= ( nEntryCount
<= nFixedBools
? &aBool
[0] : new BOOL
[nEntryCount
] );
1087 BOOL
* pTest
= ( nEntryCount
<= nFixedBools
? &aTest
[0] : new BOOL
[nEntryCount
] );
1091 BOOL bMatchWholeCell
= pDocument
->GetDocOptions().IsMatchWholeCell();
1092 CollatorWrapper
* pCollator
= (rParam
.bCaseSens
? ScGlobal::GetCaseCollator() :
1093 ScGlobal::GetCollator());
1094 ::utl::TransliterationWrapper
* pTransliteration
= (rParam
.bCaseSens
?
1095 ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration());
1097 while ( (i
< nEntryCount
) && rParam
.GetEntry(i
).bDoQuery
)
1099 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
1100 // we can only handle one single direct query
1101 if ( !pCell
|| i
> 0 )
1102 pCell
= GetCell( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1105 BOOL bTestEqual
= FALSE
;
1107 if ( pSpecial
&& pSpecial
[i
] )
1109 if (rEntry
.nVal
== SC_EMPTYFIELDS
)
1110 bOk
= !( aCol
[rEntry
.nField
].HasDataAt( nRow
) );
1111 else // if (rEntry.nVal == SC_NONEMPTYFIELDS)
1112 bOk
= aCol
[rEntry
.nField
].HasDataAt( nRow
);
1114 else if ( !rEntry
.bQueryByString
&& (pCell
? pCell
->HasValueData() :
1115 HasValueData( static_cast<SCCOL
>(rEntry
.nField
), nRow
)))
1120 switch ( pCell
->GetCellType() )
1122 case CELLTYPE_VALUE
:
1123 nCellVal
= ((ScValueCell
*)pCell
)->GetValue();
1125 case CELLTYPE_FORMULA
:
1126 nCellVal
= ((ScFormulaCell
*)pCell
)->GetValue();
1134 nCellVal
= GetValue( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1136 if (rEntry
.bQueryByDate
)
1138 sal_uInt32 nNumFmt
= GetNumberFormat(static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1139 const SvNumberformat
* pEntry
= pDocument
->GetFormatTable()->GetEntry(nNumFmt
);
1142 short nNumFmtType
= pEntry
->GetType();
1143 if ((nNumFmtType
& NUMBERFORMAT_DATE
) && !(nNumFmtType
& NUMBERFORMAT_TIME
))
1144 // The format is of date type. Strip off the time element.
1145 nCellVal
= ::rtl::math::approxFloor(nCellVal
);
1152 bOk
= ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1155 bOk
= (nCellVal
< rEntry
.nVal
) && !::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1158 bOk
= (nCellVal
> rEntry
.nVal
) && !::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1160 case SC_LESS_EQUAL
:
1161 bOk
= (nCellVal
< rEntry
.nVal
) || ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1162 if ( bOk
&& pbTestEqualCondition
)
1163 bTestEqual
= ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1165 case SC_GREATER_EQUAL
:
1166 bOk
= (nCellVal
> rEntry
.nVal
) || ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1167 if ( bOk
&& pbTestEqualCondition
)
1168 bTestEqual
= ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1171 bOk
= !::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1175 // added to avoid warnings
1179 else if ( (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
) ||
1180 (rEntry
.eOp
== SC_CONTAINS
|| rEntry
.eOp
== SC_DOES_NOT_CONTAIN
||
1181 rEntry
.eOp
== SC_BEGINS_WITH
|| rEntry
.eOp
== SC_ENDS_WITH
||
1182 rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
) ||
1183 (rEntry
.bQueryByString
&& (pCell
? pCell
->HasStringData() :
1185 static_cast<SCCOL
>(rEntry
.nField
),
1189 if( rEntry
.eOp
== SC_CONTAINS
|| rEntry
.eOp
== SC_DOES_NOT_CONTAIN
1190 || rEntry
.eOp
== SC_BEGINS_WITH
|| rEntry
.eOp
== SC_ENDS_WITH
1191 || rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
1192 bMatchWholeCell
= FALSE
;
1195 if (pCell
->GetCellType() != CELLTYPE_NOTE
)
1197 ULONG nFormat
= GetNumberFormat( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1198 ScCellFormat::GetInputString( pCell
, nFormat
, aCellStr
, *(pDocument
->GetFormatTable()) );
1202 GetInputString( static_cast<SCCOL
>(rEntry
.nField
), nRow
, aCellStr
);
1204 BOOL bRealRegExp
= (rParam
.bRegExp
&& ((rEntry
.eOp
== SC_EQUAL
)
1205 || (rEntry
.eOp
== SC_NOT_EQUAL
) || (rEntry
.eOp
== SC_CONTAINS
)
1206 || (rEntry
.eOp
== SC_DOES_NOT_CONTAIN
) || (rEntry
.eOp
== SC_BEGINS_WITH
)
1207 || (rEntry
.eOp
== SC_ENDS_WITH
) || (rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
)
1208 || (rEntry
.eOp
== SC_DOES_NOT_END_WITH
)));
1209 BOOL bTestRegExp
= (pbTestEqualCondition
&& rParam
.bRegExp
1210 && ((rEntry
.eOp
== SC_LESS_EQUAL
)
1211 || (rEntry
.eOp
== SC_GREATER_EQUAL
)));
1212 if ( bRealRegExp
|| bTestRegExp
)
1214 xub_StrLen nStart
= 0;
1215 xub_StrLen nEnd
= aCellStr
.Len();
1217 // from 614 on, nEnd is behind the found text
1218 BOOL bMatch
= FALSE
;
1219 if ( rEntry
.eOp
== SC_ENDS_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
1222 nStart
= aCellStr
.Len();
1223 bMatch
= (BOOL
) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
1224 ->SearchBkwrd( aCellStr
, &nStart
, &nEnd
);
1228 bMatch
= (BOOL
) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
1229 ->SearchFrwrd( aCellStr
, &nStart
, &nEnd
);
1231 if ( bMatch
&& bMatchWholeCell
1232 && (nStart
!= 0 || nEnd
!= aCellStr
.Len()) )
1233 bMatch
= FALSE
; // RegExp must match entire cell string
1242 case SC_DOES_NOT_CONTAIN
:
1245 case SC_BEGINS_WITH
:
1246 bOk
= ( bMatch
&& (nStart
== 0) );
1248 case SC_DOES_NOT_BEGIN_WITH
:
1249 bOk
= !( bMatch
&& (nStart
== 0) );
1252 bOk
= ( bMatch
&& (nEnd
== aCellStr
.Len()) );
1254 case SC_DOES_NOT_END_WITH
:
1255 bOk
= !( bMatch
&& (nEnd
== aCellStr
.Len()) );
1259 // added to avoid warnings
1263 bTestEqual
= bMatch
;
1267 if ( rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
1268 || rEntry
.eOp
== SC_CONTAINS
|| rEntry
.eOp
== SC_DOES_NOT_CONTAIN
1269 || rEntry
.eOp
== SC_BEGINS_WITH
|| rEntry
.eOp
== SC_ENDS_WITH
1270 || rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
1272 if ( !rEntry
.bQueryByString
&& rEntry
.pStr
->Len() == 0 )
1274 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
1275 // the query value is assigned directly, and the string is empty. In that case,
1276 // don't find any string (isEqual would find empty string results in formula cells).
1278 if ( rEntry
.eOp
== SC_NOT_EQUAL
)
1281 else if ( bMatchWholeCell
)
1283 bOk
= pTransliteration
->isEqual( aCellStr
, *rEntry
.pStr
);
1284 if ( rEntry
.eOp
== SC_NOT_EQUAL
)
1289 String
aCell( pTransliteration
->transliterate(
1290 aCellStr
, ScGlobal::eLnge
, 0, aCellStr
.Len(),
1292 String
aQuer( pTransliteration
->transliterate(
1293 *rEntry
.pStr
, ScGlobal::eLnge
, 0, rEntry
.pStr
->Len(),
1295 xub_StrLen nIndex
= (rEntry
.eOp
== SC_ENDS_WITH
1296 || rEntry
.eOp
== SC_DOES_NOT_END_WITH
)? (aCell
.Len()-aQuer
.Len()):0;
1297 xub_StrLen nStrPos
= aCell
.Search( aQuer
, nIndex
);
1302 bOk
= ( nStrPos
!= STRING_NOTFOUND
);
1305 case SC_DOES_NOT_CONTAIN
:
1306 bOk
= ( nStrPos
== STRING_NOTFOUND
);
1308 case SC_BEGINS_WITH
:
1309 bOk
= ( nStrPos
== 0 );
1311 case SC_DOES_NOT_BEGIN_WITH
:
1312 bOk
= ( nStrPos
!= 0 );
1315 bOk
= ( nStrPos
+ aQuer
.Len() == aCell
.Len() );
1317 case SC_DOES_NOT_END_WITH
:
1318 bOk
= ( nStrPos
+ aQuer
.Len() != aCell
.Len() );
1322 // added to avoid warnings
1328 { // use collator here because data was probably sorted
1329 sal_Int32 nCompare
= pCollator
->compareString(
1330 aCellStr
, *rEntry
.pStr
);
1334 bOk
= (nCompare
< 0);
1337 bOk
= (nCompare
> 0);
1339 case SC_LESS_EQUAL
:
1340 bOk
= (nCompare
<= 0);
1341 if ( bOk
&& pbTestEqualCondition
&& !bTestEqual
)
1342 bTestEqual
= (nCompare
== 0);
1344 case SC_GREATER_EQUAL
:
1345 bOk
= (nCompare
>= 0);
1346 if ( bOk
&& pbTestEqualCondition
&& !bTestEqual
)
1347 bTestEqual
= (nCompare
== 0);
1351 // added to avoid warnings
1357 else if (rParam
.bMixedComparison
)
1359 if (rEntry
.bQueryByString
&&
1360 (rEntry
.eOp
== SC_LESS
|| rEntry
.eOp
== SC_LESS_EQUAL
) &&
1361 (pCell
? pCell
->HasValueData() :
1362 HasValueData( static_cast<SCCOL
>(rEntry
.nField
), nRow
)))
1366 else if (!rEntry
.bQueryByString
&&
1367 (rEntry
.eOp
== SC_GREATER
|| rEntry
.eOp
== SC_GREATER_EQUAL
) &&
1368 (pCell
? pCell
->HasStringData() :
1369 HasStringData( static_cast<SCCOL
>(rEntry
.nField
), nRow
)))
1379 pTest
[nPos
] = bTestEqual
;
1383 if (rEntry
.eConnect
== SC_AND
)
1385 pPasst
[nPos
] = pPasst
[nPos
] && bOk
;
1386 pTest
[nPos
] = pTest
[nPos
] && bTestEqual
;
1392 pTest
[nPos
] = bTestEqual
;
1398 for ( long j
=1; j
<= nPos
; j
++ )
1400 pPasst
[0] = pPasst
[0] || pPasst
[j
];
1401 pTest
[0] = pTest
[0] || pTest
[j
];
1404 BOOL bRet
= pPasst
[0];
1405 if ( pPasst
!= &aBool
[0] )
1407 if ( pbTestEqualCondition
)
1408 *pbTestEqualCondition
= pTest
[0];
1409 if ( pTest
!= &aTest
[0] )
1415 void ScTable::TopTenQuery( ScQueryParam
& rParam
)
1417 BOOL bSortCollatorInitialized
= FALSE
;
1418 SCSIZE nEntryCount
= rParam
.GetEntryCount();
1419 SCROW nRow1
= (rParam
.bHasHeader
? rParam
.nRow1
+ 1 : rParam
.nRow1
);
1420 SCSIZE nCount
= static_cast<SCSIZE
>(rParam
.nRow2
- nRow1
+ 1);
1421 for ( SCSIZE i
=0; (i
<nEntryCount
) && (rParam
.GetEntry(i
).bDoQuery
); i
++ )
1423 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
1424 switch ( rEntry
.eOp
)
1431 ScSortParam
aLocalSortParam( rParam
, static_cast<SCCOL
>(rEntry
.nField
) );
1432 aSortParam
= aLocalSortParam
; // used in CreateSortInfoArray, Compare
1433 if ( !bSortCollatorInitialized
)
1435 bSortCollatorInitialized
= TRUE
;
1436 InitSortCollator( aLocalSortParam
);
1438 ScSortInfoArray
* pArray
= CreateSortInfoArray( nRow1
, rParam
.nRow2
);
1439 DecoladeRow( pArray
, nRow1
, rParam
.nRow2
);
1440 QuickSort( pArray
, nRow1
, rParam
.nRow2
);
1441 ScSortInfo
** ppInfo
= pArray
->GetFirstArray();
1442 SCSIZE nValidCount
= nCount
;
1443 // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
1444 while ( nValidCount
> 0 && ( ppInfo
[nValidCount
-1]->pCell
== NULL
||
1445 ppInfo
[nValidCount
-1]->pCell
->GetCellType() == CELLTYPE_NOTE
) )
1447 // keine Strings zaehlen, sind zwischen Value und Leer
1448 while ( nValidCount
> 0
1449 && ppInfo
[nValidCount
-1]->pCell
->HasStringData() )
1451 if ( nValidCount
> 0 )
1453 if ( rEntry
.bQueryByString
)
1455 rEntry
.bQueryByString
= FALSE
;
1456 rEntry
.nVal
= 10; // 10 bzw. 10%
1458 SCSIZE nVal
= (rEntry
.nVal
>= 1 ? static_cast<SCSIZE
>(rEntry
.nVal
) : 1);
1460 switch ( rEntry
.eOp
)
1464 rEntry
.eOp
= SC_GREATER_EQUAL
;
1465 if ( nVal
> nValidCount
)
1467 nOffset
= nValidCount
- nVal
; // 1 <= nVal <= nValidCount
1472 rEntry
.eOp
= SC_LESS_EQUAL
;
1473 if ( nVal
> nValidCount
)
1475 nOffset
= nVal
- 1; // 1 <= nVal <= nValidCount
1480 rEntry
.eOp
= SC_GREATER_EQUAL
;
1483 nOffset
= nValidCount
- (nValidCount
* nVal
/ 100);
1484 if ( nOffset
>= nValidCount
)
1485 nOffset
= nValidCount
- 1;
1490 rEntry
.eOp
= SC_LESS_EQUAL
;
1493 nOffset
= (nValidCount
* nVal
/ 100);
1494 if ( nOffset
>= nValidCount
)
1495 nOffset
= nValidCount
- 1;
1500 // added to avoid warnings
1503 ScBaseCell
* pCell
= ppInfo
[nOffset
]->pCell
;
1504 if ( pCell
->HasValueData() )
1506 if ( pCell
->GetCellType() == CELLTYPE_VALUE
)
1507 rEntry
.nVal
= ((ScValueCell
*)pCell
)->GetValue();
1509 rEntry
.nVal
= ((ScFormulaCell
*)pCell
)->GetValue();
1513 DBG_ERRORFILE( "TopTenQuery: pCell kein ValueData" );
1514 rEntry
.eOp
= SC_GREATER_EQUAL
;
1520 rEntry
.eOp
= SC_GREATER_EQUAL
;
1521 rEntry
.bQueryByString
= FALSE
;
1529 // added to avoid warnings
1533 if ( bSortCollatorInitialized
)
1534 DestroySortCollator();
1537 static void lcl_PrepareQuery( ScDocument
* pDoc
, ScTable
* pTab
, ScQueryParam
& rParam
, BOOL
* pSpecial
)
1539 bool bTopTen
= false;
1540 SCSIZE nEntryCount
= rParam
.GetEntryCount();
1542 for ( SCSIZE i
= 0; i
< nEntryCount
; ++i
)
1544 pSpecial
[i
] = FALSE
;
1545 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
1546 if ( rEntry
.bDoQuery
)
1548 if ( rEntry
.bQueryByString
)
1550 sal_uInt32 nIndex
= 0;
1551 rEntry
.bQueryByString
= !( pDoc
->GetFormatTable()->
1552 IsNumberFormat( *rEntry
.pStr
, nIndex
, rEntry
.nVal
) );
1556 // #58736# call from UNO or second call from autofilter
1557 if ( rEntry
.nVal
== SC_EMPTYFIELDS
|| rEntry
.nVal
== SC_NONEMPTYFIELDS
)
1564 switch ( rEntry
.eOp
)
1584 pTab
->TopTenQuery( rParam
);
1588 SCSIZE
ScTable::Query(ScQueryParam
& rParamOrg
, BOOL bKeepSub
)
1590 ScQueryParam
aParam( rParamOrg
);
1591 ScStrCollection aScStrCollection
;
1592 StrData
* pStrData
= NULL
;
1594 BOOL bStarted
= FALSE
;
1595 BOOL bOldResult
= TRUE
;
1596 SCROW nOldStart
= 0;
1601 SCROW nHeader
= aParam
.bHasHeader
? 1 : 0;
1603 SCSIZE nEntryCount
= aParam
.GetEntryCount();
1604 BOOL
* pSpecial
= new BOOL
[nEntryCount
];
1605 lcl_PrepareQuery( pDocument
, this, aParam
, pSpecial
);
1607 SCROW nEndRow
= aParam
.bUseDynamicRange
? aParam
.nDynamicEndRow
: aParam
.nRow2
;
1608 if (!aParam
.bInplace
)
1610 nOutRow
= aParam
.nDestRow
+ nHeader
;
1612 CopyData( aParam
.nCol1
, aParam
.nRow1
, aParam
.nCol2
, aParam
.nRow1
,
1613 aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
1616 for (SCROW j
=aParam
.nRow1
+ nHeader
; j
<=nEndRow
; j
++)
1618 BOOL bResult
; // Filterergebnis
1619 BOOL bValid
= ValidQuery(j
, aParam
, pSpecial
);
1620 if (!bValid
&& bKeepSub
) // Subtotals stehenlassen
1622 for (SCCOL nCol
=aParam
.nCol1
; nCol
<=aParam
.nCol2
&& !bValid
; nCol
++)
1625 pCell
= GetCell( nCol
, j
);
1627 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
1628 if (((ScFormulaCell
*)pCell
)->IsSubTotal())
1629 if (RefVisible((ScFormulaCell
*)pCell
))
1635 if (aParam
.bDuplicate
)
1640 for (SCCOL k
=aParam
.nCol1
; k
<= aParam
.nCol2
; k
++)
1643 GetString(k
, j
, aCellStr
);
1645 aStr
+= (sal_Unicode
)1;
1647 pStrData
= new StrData(aStr
);
1649 BOOL bIsUnique
= TRUE
;
1651 bIsUnique
= aScStrCollection
.Insert(pStrData
);
1664 if (aParam
.bInplace
)
1666 if (bResult
== bOldResult
&& bStarted
)
1671 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
1672 nOldStart
= nOldEnd
= j
;
1673 bOldResult
= bResult
;
1681 CopyData( aParam
.nCol1
,j
, aParam
.nCol2
,j
, aParam
.nDestCol
,nOutRow
,aParam
.nDestTab
);
1689 if (aParam
.bInplace
&& bStarted
)
1690 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
1697 BOOL
ScTable::CreateExcelQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
1700 SCCOL
* pFields
= new SCCOL
[nCol2
-nCol1
+1];
1703 DBG_ASSERT( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
1704 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
1705 SCROW nDBRow1
= rQueryParam
.nRow1
;
1706 SCCOL nDBCol2
= rQueryParam
.nCol2
;
1707 // Erste Zeile muessen Spaltenkoepfe sein
1708 while (bValid
&& (nCol
<= nCol2
))
1711 GetUpperCellString(nCol
, nRow1
, aQueryStr
);
1712 BOOL bFound
= FALSE
;
1713 SCCOL i
= rQueryParam
.nCol1
;
1714 while (!bFound
&& (i
<= nDBCol2
))
1716 if ( nTab
== nDBTab
)
1717 GetUpperCellString(i
, nDBRow1
, aCellStr
);
1719 pDocument
->GetUpperCellString(i
, nDBRow1
, nDBTab
, aCellStr
);
1720 bFound
= (aCellStr
== aQueryStr
);
1724 pFields
[nCol
- nCol1
] = i
;
1732 for ( nCol
=nCol1
; nCol
<=nCol2
; nCol
++ )
1733 nVisible
+= aCol
[nCol
].VisibleCount( nRow1
+1, nRow2
);
1735 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
1737 DBG_ERROR("zu viele Filterkritierien");
1741 SCSIZE nNewEntries
= nVisible
;
1742 rQueryParam
.Resize( nNewEntries
);
1745 SCROW nRow
= nRow1
+ 1;
1746 while (nRow
<= nRow2
)
1749 while (nCol
<= nCol2
)
1751 GetInputString( nCol
, nRow
, aCellStr
);
1752 ScGlobal::pCharClass
->toUpper( aCellStr
);
1753 if (aCellStr
.Len() > 0)
1755 if (nIndex
< nNewEntries
)
1757 rQueryParam
.GetEntry(nIndex
).nField
= pFields
[nCol
- nCol1
];
1758 rQueryParam
.FillInExcelSyntax(aCellStr
, nIndex
);
1760 if (nIndex
< nNewEntries
)
1761 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_AND
;
1769 if (nIndex
< nNewEntries
)
1770 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_OR
;
1777 BOOL
ScTable::CreateStarQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
1779 // A valid StarQuery must be at least 4 columns wide. To be precise it
1780 // should be exactly 4 columns ...
1781 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
1782 // column Excel style query range immediately left to itself would result
1783 // in a circular reference when the field name or operator or value (first
1784 // to third query range column) is obtained (#i58354#). Furthermore, if the
1785 // range wasn't sufficiently specified data changes wouldn't flag formula
1786 // cells for recalculation.
1787 if (nCol2
- nCol1
< 3)
1795 DBG_ASSERT( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
1796 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
1797 SCROW nDBRow1
= rQueryParam
.nRow1
;
1798 SCCOL nDBCol2
= rQueryParam
.nCol2
;
1800 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRow2
-nRow1
+1);
1801 rQueryParam
.Resize( nNewEntries
);
1805 ScQueryEntry
& rEntry
= rQueryParam
.GetEntry(nIndex
);
1808 // Erste Spalte UND/ODER
1811 GetUpperCellString(nCol1
, nRow
, aCellStr
);
1812 if ( aCellStr
== ScGlobal::GetRscString(STR_TABLE_UND
) )
1814 rEntry
.eConnect
= SC_AND
;
1817 else if ( aCellStr
== ScGlobal::GetRscString(STR_TABLE_ODER
) )
1819 rEntry
.eConnect
= SC_OR
;
1823 // Zweite Spalte FeldName
1824 if ((nIndex
< 1) || bValid
)
1827 GetUpperCellString(nCol1
+ 1, nRow
, aCellStr
);
1828 for (SCCOL i
=rQueryParam
.nCol1
; (i
<= nDBCol2
) && (!bFound
); i
++)
1831 if ( nTab
== nDBTab
)
1832 GetUpperCellString(i
, nDBRow1
, aFieldStr
);
1834 pDocument
->GetUpperCellString(i
, nDBRow1
, nDBTab
, aFieldStr
);
1835 bFound
= (aCellStr
== aFieldStr
);
1845 // Dritte Spalte Operator =<>...
1849 GetUpperCellString(nCol1
+ 2, nRow
, aCellStr
);
1850 if (aCellStr
.GetChar(0) == '<')
1852 if (aCellStr
.GetChar(1) == '>')
1853 rEntry
.eOp
= SC_NOT_EQUAL
;
1854 else if (aCellStr
.GetChar(1) == '=')
1855 rEntry
.eOp
= SC_LESS_EQUAL
;
1857 rEntry
.eOp
= SC_LESS
;
1859 else if (aCellStr
.GetChar(0) == '>')
1861 if (aCellStr
.GetChar(1) == '=')
1862 rEntry
.eOp
= SC_GREATER_EQUAL
;
1864 rEntry
.eOp
= SC_GREATER
;
1866 else if (aCellStr
.GetChar(0) == '=')
1867 rEntry
.eOp
= SC_EQUAL
;
1870 // Vierte Spalte Wert
1873 GetString(nCol1
+ 3, nRow
, *rEntry
.pStr
);
1874 rEntry
.bDoQuery
= TRUE
;
1879 while (bValid
&& (nRow
<= nRow2
) /* && (nIndex < MAXQUERY) */ );
1883 BOOL
ScTable::CreateQueryParam(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
1886 PutInOrder(nCol1
, nCol2
);
1887 PutInOrder(nRow1
, nRow2
);
1889 nCount
= rQueryParam
.GetEntryCount();
1890 for (i
=0; i
< nCount
; i
++)
1891 rQueryParam
.GetEntry(i
).Clear();
1893 // Standard QueryTabelle
1894 BOOL bValid
= CreateStarQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
1895 // Excel QueryTabelle
1897 bValid
= CreateExcelQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
1899 nCount
= rQueryParam
.GetEntryCount();
1902 // bQueryByString muss gesetzt sein
1903 for (i
=0; i
< nCount
; i
++)
1904 rQueryParam
.GetEntry(i
).bQueryByString
= TRUE
;
1909 for (i
=0; i
< nCount
; i
++)
1910 rQueryParam
.GetEntry(i
).Clear();
1915 BOOL
ScTable::HasColHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW
/* nEndRow */ )
1917 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
1919 CellType eType
= GetCellType( nCol
, nStartRow
);
1920 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
1926 BOOL
ScTable::HasRowHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL
/* nEndCol */, SCROW nEndRow
)
1928 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
1930 CellType eType
= GetCellType( nStartCol
, nRow
);
1931 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
1937 void ScTable::GetFilterEntries(SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, TypedScStrCollection
& rStrings
, bool& rHasDates
)
1939 aCol
[nCol
].GetFilterEntries( nRow1
, nRow2
, rStrings
, rHasDates
);
1942 void ScTable::GetFilteredFilterEntries(
1943 SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, const ScQueryParam
& rParam
, TypedScStrCollection
& rStrings
, bool& rHasDates
)
1945 // remove the entry for this column from the query parameter
1946 ScQueryParam
aParam( rParam
);
1947 SCSIZE nEntryCount
= aParam
.GetEntryCount();
1948 for ( SCSIZE i
= 0; i
< nEntryCount
&& aParam
.GetEntry(i
).bDoQuery
; ++i
)
1950 ScQueryEntry
& rEntry
= aParam
.GetEntry(i
);
1951 if ( rEntry
.nField
== nCol
)
1953 aParam
.DeleteQuery(i
);
1957 nEntryCount
= aParam
.GetEntryCount();
1959 BOOL
* pSpecial
= new BOOL
[nEntryCount
];
1960 lcl_PrepareQuery( pDocument
, this, aParam
, pSpecial
);
1961 bool bHasDates
= false;
1962 for ( SCROW j
= nRow1
; j
<= nRow2
; ++j
)
1964 if ( ValidQuery( j
, aParam
, pSpecial
) )
1966 bool bThisHasDates
= false;
1967 aCol
[nCol
].GetFilterEntries( j
, j
, rStrings
, bThisHasDates
);
1968 bHasDates
|= bThisHasDates
;
1972 rHasDates
= bHasDates
;
1976 BOOL
ScTable::GetDataEntries(SCCOL nCol
, SCROW nRow
, TypedScStrCollection
& rStrings
, BOOL bLimit
)
1978 return aCol
[nCol
].GetDataEntries( nRow
, rStrings
, bLimit
);
1981 ULONG
ScTable::GetCellCount() const
1983 ULONG nCellCount
= 0;
1985 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
1986 nCellCount
+= aCol
[nCol
].GetCellCount();
1991 ULONG
ScTable::GetWeightedCount() const
1993 ULONG nCellCount
= 0;
1995 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
1996 if ( aCol
[nCol
].GetCellCount() ) // GetCellCount ist inline
1997 nCellCount
+= aCol
[nCol
].GetWeightedCount();
2002 ULONG
ScTable::GetCodeCount() const
2004 ULONG nCodeCount
= 0;
2006 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
2007 if ( aCol
[nCol
].GetCellCount() ) // GetCellCount ist inline
2008 nCodeCount
+= aCol
[nCol
].GetCodeCount();
2013 sal_Int32
ScTable::GetMaxStringLen( SCCOL nCol
, SCROW nRowStart
,
2014 SCROW nRowEnd
, CharSet eCharSet
) const
2016 if ( ValidCol(nCol
) )
2017 return aCol
[nCol
].GetMaxStringLen( nRowStart
, nRowEnd
, eCharSet
);
2022 xub_StrLen
ScTable::GetMaxNumberStringLen( USHORT
& nPrecision
, SCCOL nCol
,
2023 SCROW nRowStart
, SCROW nRowEnd
) const
2025 if ( ValidCol(nCol
) )
2026 return aCol
[nCol
].GetMaxNumberStringLen( nPrecision
, nRowStart
, nRowEnd
);
2031 void ScTable::UpdateSelectionFunction( ScFunctionData
& rData
,
2032 SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
,
2033 const ScMarkData
& rMark
)
2035 // Cursor neben einer Markierung nicht beruecksichtigen:
2036 //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!!
2037 BOOL bSingle
= ( rMark
.IsMarked() || !rMark
.IsMultiMarked() );
2039 // Mehrfachselektion:
2042 if ( rMark
.IsMultiMarked() )
2043 for (nCol
=0; nCol
<=MAXCOL
&& !rData
.bError
; nCol
++)
2044 if ( !pColFlags
|| !ColHidden(nCol
) )
2045 aCol
[nCol
].UpdateSelectionFunction( rMark
, rData
, *mpHiddenRows
,
2046 bSingle
&& ( nCol
>= nStartCol
&& nCol
<= nEndCol
),
2047 nStartRow
, nEndRow
);
2049 // Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.):
2051 if ( bSingle
&& !rMark
.IsMarkNegative() )
2052 for (nCol
=nStartCol
; nCol
<=nEndCol
&& !rData
.bError
; nCol
++)
2053 if ( !pColFlags
|| !ColHidden(nCol
) )
2054 aCol
[nCol
].UpdateAreaFunction( rData
, *mpHiddenRows
, nStartRow
, nEndRow
);
2057 void ScTable::FindConditionalFormat( ULONG nKey
, ScRangeList
& rList
)
2059 SCROW nStartRow
= 0, nEndRow
= 0;
2060 for (SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++)
2062 ScAttrIterator
* pIter
= aCol
[nCol
].CreateAttrIterator( 0, MAXROW
);
2063 const ScPatternAttr
* pPattern
= pIter
->Next( nStartRow
, nEndRow
);
2066 if (((SfxUInt32Item
&)pPattern
->GetItem(ATTR_CONDITIONAL
)).GetValue() == nKey
)
2067 rList
.Join( ScRange(nCol
,nStartRow
,nTab
, nCol
,nEndRow
,nTab
) );
2068 pPattern
= pIter
->Next( nStartRow
, nEndRow
);