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"
66 using namespace ::com::sun::star
;
68 namespace naturalsort
{
70 using namespace ::com::sun::star::i18n
;
72 /** Splits a given string into three parts: the prefix, number string, and
76 Original string to be split into pieces
79 Prefix string that consists of the part before the first number token
82 String after the last number token. This may still contain number strings.
85 Number converted from the middle number string
87 @return Returns TRUE if a numeral element is found in a given string, or
88 FALSE if no numeral element is found.
90 BOOL
SplitString( const rtl::OUString
&sWhole
,
91 rtl::OUString
&sPrefix
, rtl::OUString
&sSuffix
, double &fNum
)
93 i18n::LocaleDataItem aLocaleItem
= ScGlobal::pLocaleData
->getLocaleItem();
96 rtl::OUString sEmpty
, sUser
= rtl::OUString::createFromAscii( "-" );
97 ParseResult aPRPre
= ScGlobal::pCharClass
->parsePredefinedToken(
98 KParseType::IDENTNAME
, sWhole
, 0,
99 KParseTokens::ANY_LETTER
, sUser
, KParseTokens::ANY_LETTER
, sUser
);
100 sPrefix
= sWhole
.copy( 0, aPRPre
.EndPos
);
102 // Return FALSE if no numeral element is found
103 if ( aPRPre
.EndPos
== sWhole
.getLength() )
106 // Get numeral element
107 sUser
= aLocaleItem
.decimalSeparator
;
108 ParseResult aPRNum
= ScGlobal::pCharClass
->parsePredefinedToken(
109 KParseType::ANY_NUMBER
, sWhole
, aPRPre
.EndPos
,
110 KParseTokens::ANY_NUMBER
, sEmpty
, KParseTokens::ANY_NUMBER
, sUser
);
112 if ( aPRNum
.EndPos
== aPRPre
.EndPos
)
116 sSuffix
= sWhole
.copy( aPRNum
.EndPos
);
121 /** Naturally compares two given strings.
123 This is the main function that should be called externally. It returns
124 either 1, 0, or -1 depending on the comparison result of given two strings.
133 Boolean value for case sensitivity
136 Pointer to user defined sort list
139 Pointer to collator wrapper for normal string comparison
141 @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
144 short Compare( const String
&sInput1
, const String
&sInput2
,
145 const BOOL bCaseSens
, const ScUserListData
* pData
, const CollatorWrapper
*pCW
)
147 rtl::OUString
sStr1( sInput1
), sStr2( sInput2
), sPre1
, sSuf1
, sPre2
, sSuf2
;
152 BOOL bNumFound1
= SplitString( sStr1
, sPre1
, sSuf1
, nNum1
);
153 BOOL bNumFound2
= SplitString( sStr2
, sPre2
, sSuf2
, nNum2
);
155 short nPreRes
; // Prefix comparison result
160 if ( !bNumFound1
|| !bNumFound2
)
161 return static_cast<short>(pData
->Compare( sStr1
, sStr2
));
163 nPreRes
= pData
->Compare( sPre1
, sPre2
);
167 if ( !bNumFound1
|| !bNumFound2
)
168 return static_cast<short>(pData
->ICompare( sStr1
, sStr2
));
170 nPreRes
= pData
->ICompare( sPre1
, sPre2
);
175 if ( !bNumFound1
|| !bNumFound2
)
176 return static_cast<short>(pCW
->compareString( sStr1
, sStr2
));
178 nPreRes
= static_cast<short>(pCW
->compareString( sPre1
, sPre2
));
181 // Prefix strings differ. Return immediately.
182 if ( nPreRes
!= 0 ) return nPreRes
;
184 if ( nNum1
!= nNum2
)
186 if ( nNum1
< nNum2
) return -1;
187 return static_cast<short>( nNum1
> nNum2
);
190 // The prefix and the first numerical elements are equal, but the suffix
191 // strings may still differ. Stay in the loop.
203 // STATIC DATA -----------------------------------------------------------
205 const USHORT nMaxSorts
= 3; // maximale Anzahl Sortierkriterien in aSortParam
211 DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo
);
213 const USHORT nMemPoolSortInfo
= (0x8000 - 64) / sizeof(ScSortInfo
);
214 IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo
, nMemPoolSortInfo
, nMemPoolSortInfo
)
216 // END OF STATIC DATA -----------------------------------------------------
219 class ScSortInfoArray
222 ScSortInfo
** pppInfo
[nMaxSorts
];
228 ScSortInfoArray( USHORT nSorts
, SCCOLROW nInd1
, SCCOLROW nInd2
) :
229 nCount( nInd2
- nInd1
+ 1 ), nStart( nInd1
),
230 nUsedSorts( Min( nSorts
, nMaxSorts
) )
232 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
234 ScSortInfo
** ppInfo
= new ScSortInfo
* [nCount
];
235 for ( SCSIZE j
= 0; j
< nCount
; j
++ )
236 ppInfo
[j
] = new ScSortInfo
;
237 pppInfo
[nSort
] = ppInfo
;
242 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
244 ScSortInfo
** ppInfo
= pppInfo
[nSort
];
245 for ( SCSIZE j
= 0; j
< nCount
; j
++ )
250 ScSortInfo
* Get( USHORT nSort
, SCCOLROW nInd
)
251 { return (pppInfo
[nSort
])[ nInd
- nStart
]; }
252 void Swap( SCCOLROW nInd1
, SCCOLROW nInd2
)
254 SCSIZE n1
= static_cast<SCSIZE
>(nInd1
- nStart
);
255 SCSIZE n2
= static_cast<SCSIZE
>(nInd2
- nStart
);
256 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
258 ScSortInfo
** ppInfo
= pppInfo
[nSort
];
259 ScSortInfo
* pTmp
= ppInfo
[n1
];
260 ppInfo
[n1
] = ppInfo
[n2
];
264 USHORT
GetUsedSorts() { return nUsedSorts
; }
265 ScSortInfo
** GetFirstArray() { return pppInfo
[0]; }
266 SCCOLROW
GetStart() { return nStart
; }
267 SCSIZE
GetCount() { return nCount
; }
270 ScSortInfoArray
* ScTable::CreateSortInfoArray( SCCOLROW nInd1
, SCCOLROW nInd2
)
272 USHORT nUsedSorts
= 1;
273 while ( nUsedSorts
< nMaxSorts
&& aSortParam
.bDoSort
[nUsedSorts
] )
275 ScSortInfoArray
* pArray
= new ScSortInfoArray( nUsedSorts
, nInd1
, nInd2
);
276 if ( aSortParam
.bByRow
)
278 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
280 SCCOL nCol
= static_cast<SCCOL
>(aSortParam
.nField
[nSort
]);
281 ScColumn
* pCol
= &aCol
[nCol
];
282 for ( SCROW nRow
= nInd1
; nRow
<= nInd2
; nRow
++ )
284 //2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell
285 ScSortInfo
* pInfo
= pArray
->Get( nSort
, nRow
);
286 pInfo
->pCell
= pCol
->GetCell( nRow
);
293 for ( USHORT nSort
= 0; nSort
< nUsedSorts
; nSort
++ )
295 SCROW nRow
= aSortParam
.nField
[nSort
];
296 for ( SCCOL nCol
= static_cast<SCCOL
>(nInd1
);
297 nCol
<= static_cast<SCCOL
>(nInd2
); nCol
++ )
299 ScSortInfo
* pInfo
= pArray
->Get( nSort
, nCol
);
300 pInfo
->pCell
= GetCell( nCol
, nRow
);
309 BOOL
ScTable::IsSortCollatorGlobal() const
311 return pSortCollator
== ScGlobal::pCollator
||
312 pSortCollator
== ScGlobal::pCaseCollator
;
316 void ScTable::InitSortCollator( const ScSortParam
& rPar
)
318 if ( rPar
.aCollatorLocale
.Language
.getLength() )
320 if ( !pSortCollator
|| IsSortCollatorGlobal() )
321 pSortCollator
= new CollatorWrapper( pDocument
->GetServiceManager() );
322 pSortCollator
->loadCollatorAlgorithm( rPar
.aCollatorAlgorithm
,
323 rPar
.aCollatorLocale
, (rPar
.bCaseSens
? 0 : SC_COLLATOR_IGNORES
) );
327 DestroySortCollator();
328 pSortCollator
= (rPar
.bCaseSens
? ScGlobal::pCaseCollator
:
329 ScGlobal::pCollator
);
334 void ScTable::DestroySortCollator()
338 if ( !IsSortCollatorGlobal() )
339 delete pSortCollator
;
340 pSortCollator
= NULL
;
345 void ScTable::SortReorder( ScSortInfoArray
* pArray
, ScProgress
& rProgress
)
347 BOOL bByRow
= aSortParam
.bByRow
;
348 SCSIZE nCount
= pArray
->GetCount();
349 SCCOLROW nStart
= pArray
->GetStart();
350 ScSortInfo
** ppInfo
= pArray
->GetFirstArray();
351 ::std::vector
<ScSortInfo
*> aTable(nCount
);
353 for ( nPos
= 0; nPos
< nCount
; nPos
++ )
354 aTable
[ppInfo
[nPos
]->nOrg
- nStart
] = ppInfo
[nPos
];
356 SCCOLROW nDest
= nStart
;
357 for ( nPos
= 0; nPos
< nCount
; nPos
++, nDest
++ )
359 SCCOLROW nOrg
= ppInfo
[nPos
]->nOrg
;
363 SwapRow( nDest
, nOrg
);
365 SwapCol( static_cast<SCCOL
>(nDest
), static_cast<SCCOL
>(nOrg
) );
366 // neue Position des weggeswapten eintragen
367 ScSortInfo
* p
= ppInfo
[nPos
];
369 ::std::swap(p
, aTable
[nDest
-nStart
]);
371 ::std::swap(p
, aTable
[nOrg
-nStart
]);
372 DBG_ASSERT( p
== ppInfo
[nPos
], "SortReorder: nOrg MisMatch" );
374 rProgress
.SetStateOnPercent( nPos
);
378 short ScTable::CompareCell( USHORT nSort
,
379 ScBaseCell
* pCell1
, SCCOL nCell1Col
, SCROW nCell1Row
,
380 ScBaseCell
* pCell2
, SCCOL nCell2Col
, SCROW nCell2Row
)
384 CellType eType1
= CELLTYPE_NONE
, eType2
= CELLTYPE_NONE
;
387 eType1
= pCell1
->GetCellType();
388 if (eType1
== CELLTYPE_NOTE
)
393 eType2
= pCell2
->GetCellType();
394 if (eType2
== CELLTYPE_NOTE
)
402 BOOL bStr1
= ( eType1
!= CELLTYPE_VALUE
);
403 if ( eType1
== CELLTYPE_FORMULA
&& ((ScFormulaCell
*)pCell1
)->IsValue() )
405 BOOL bStr2
= ( eType2
!= CELLTYPE_VALUE
);
406 if ( eType2
== CELLTYPE_FORMULA
&& ((ScFormulaCell
*)pCell2
)->IsValue() )
409 if ( bStr1
&& bStr2
) // nur Strings untereinander als String vergleichen!
413 if (eType1
== CELLTYPE_STRING
)
414 ((ScStringCell
*)pCell1
)->GetString(aStr1
);
416 GetString(nCell1Col
, nCell1Row
, aStr1
);
417 if (eType2
== CELLTYPE_STRING
)
418 ((ScStringCell
*)pCell2
)->GetString(aStr2
);
420 GetString(nCell2Col
, nCell2Row
, aStr2
);
422 BOOL bUserDef
= aSortParam
.bUserDef
; // custom sort order
423 BOOL bNaturalSort
= aSortParam
.bNaturalSort
; // natural sort
424 BOOL bCaseSens
= aSortParam
.bCaseSens
; // case sensitivity
428 ScUserListData
* pData
=
429 static_cast<ScUserListData
*>( (ScGlobal::GetUserList()->At(
430 aSortParam
.nUserIndex
)) );
435 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, pData
, pSortCollator
);
439 nRes
= sal::static_int_cast
<short>( pData
->Compare(aStr1
, aStr2
) );
441 nRes
= sal::static_int_cast
<short>( pData
->ICompare(aStr1
, aStr2
) );
451 nRes
= naturalsort::Compare( aStr1
, aStr2
, bCaseSens
, NULL
, pSortCollator
);
453 nRes
= static_cast<short>( pSortCollator
->compareString( aStr1
, aStr2
) );
456 else if ( bStr1
) // String <-> Zahl
457 nRes
= 1; // Zahl vorne
458 else if ( bStr2
) // Zahl <-> String
459 nRes
= -1; // Zahl vorne
460 else // Zahlen untereinander
464 if (eType1
== CELLTYPE_VALUE
)
465 nVal1
= ((ScValueCell
*)pCell1
)->GetValue();
466 else if (eType1
== CELLTYPE_FORMULA
)
467 nVal1
= ((ScFormulaCell
*)pCell1
)->GetValue();
470 if (eType2
== CELLTYPE_VALUE
)
471 nVal2
= ((ScValueCell
*)pCell2
)->GetValue();
472 else if (eType2
== CELLTYPE_FORMULA
)
473 nVal2
= ((ScFormulaCell
*)pCell2
)->GetValue();
478 else if (nVal1
> nVal2
)
481 if ( !aSortParam
.bAscending
[nSort
] )
492 nRes
= 0; // beide leer
497 short ScTable::Compare( ScSortInfoArray
* pArray
, SCCOLROW nIndex1
, SCCOLROW nIndex2
)
503 ScSortInfo
* pInfo1
= pArray
->Get( nSort
, nIndex1
);
504 ScSortInfo
* pInfo2
= pArray
->Get( nSort
, nIndex2
);
505 if ( aSortParam
.bByRow
)
506 nRes
= CompareCell( nSort
,
507 pInfo1
->pCell
, static_cast<SCCOL
>(aSortParam
.nField
[nSort
]), pInfo1
->nOrg
,
508 pInfo2
->pCell
, static_cast<SCCOL
>(aSortParam
.nField
[nSort
]), pInfo2
->nOrg
);
510 nRes
= CompareCell( nSort
,
511 pInfo1
->pCell
, static_cast<SCCOL
>(pInfo1
->nOrg
), aSortParam
.nField
[nSort
],
512 pInfo2
->pCell
, static_cast<SCCOL
>(pInfo2
->nOrg
), aSortParam
.nField
[nSort
] );
513 } while ( nRes
== 0 && ++nSort
< pArray
->GetUsedSorts() );
516 ScSortInfo
* pInfo1
= pArray
->Get( 0, nIndex1
);
517 ScSortInfo
* pInfo2
= pArray
->Get( 0, nIndex2
);
518 if( pInfo1
->nOrg
< pInfo2
->nOrg
)
520 else if( pInfo1
->nOrg
> pInfo2
->nOrg
)
526 void ScTable::QuickSort( ScSortInfoArray
* pArray
, SCsCOLROW nLo
, SCsCOLROW nHi
)
528 if ((nHi
- nLo
) == 1)
530 if (Compare(pArray
, nLo
, nHi
) > 0)
531 pArray
->Swap( nLo
, nHi
);
539 while ((ni
<= nHi
) && (Compare(pArray
, ni
, nLo
)) < 0)
541 while ((nj
>= nLo
) && (Compare(pArray
, nLo
, nj
)) < 0)
546 pArray
->Swap( ni
, nj
);
551 if ((nj
- nLo
) < (nHi
- ni
))
554 QuickSort(pArray
, nLo
, nj
);
556 QuickSort(pArray
, ni
, nHi
);
561 QuickSort(pArray
, ni
, nHi
);
563 QuickSort(pArray
, nLo
, nj
);
568 void ScTable::SwapCol(SCCOL nCol1
, SCCOL nCol2
)
570 for (SCROW nRow
= aSortParam
.nRow1
; nRow
<= aSortParam
.nRow2
; nRow
++)
572 aCol
[nCol1
].SwapCell(nRow
, aCol
[nCol2
]);
573 if (aSortParam
.bIncludePattern
)
575 const ScPatternAttr
* pPat1
= GetPattern(nCol1
, nRow
);
576 const ScPatternAttr
* pPat2
= GetPattern(nCol2
, nRow
);
579 SetPattern(nCol1
, nRow
, *pPat2
, TRUE
);
580 SetPattern(nCol2
, nRow
, *pPat1
, TRUE
);
586 void ScTable::SwapRow(SCROW nRow1
, SCROW nRow2
)
588 for (SCCOL nCol
= aSortParam
.nCol1
; nCol
<= aSortParam
.nCol2
; nCol
++)
590 aCol
[nCol
].SwapRow(nRow1
, nRow2
);
591 if (aSortParam
.bIncludePattern
)
593 const ScPatternAttr
* pPat1
= GetPattern(nCol
, nRow1
);
594 const ScPatternAttr
* pPat2
= GetPattern(nCol
, nRow2
);
597 SetPattern(nCol
, nRow1
, *pPat2
, TRUE
);
598 SetPattern(nCol
, nRow2
, *pPat1
, TRUE
);
602 if (bGlobalKeepQuery
&& pRowFlags
)
604 BYTE nRow1Flags
= pRowFlags
->GetValue(nRow1
);
605 BYTE nRow2Flags
= pRowFlags
->GetValue(nRow2
);
606 BYTE nFlags1
= nRow1Flags
& ( CR_HIDDEN
| CR_FILTERED
);
607 BYTE nFlags2
= nRow2Flags
& ( CR_HIDDEN
| CR_FILTERED
);
608 pRowFlags
->SetValue( nRow1
, (nRow1Flags
& ~( CR_HIDDEN
| CR_FILTERED
)) | nFlags2
);
609 pRowFlags
->SetValue( nRow2
, (nRow2Flags
& ~( CR_HIDDEN
| CR_FILTERED
)) | nFlags1
);
613 short ScTable::Compare(SCCOLROW nIndex1
, SCCOLROW nIndex2
)
617 if (aSortParam
.bByRow
)
621 SCCOL nCol
= static_cast<SCCOL
>(aSortParam
.nField
[nSort
]);
622 ScBaseCell
* pCell1
= aCol
[nCol
].GetCell( nIndex1
);
623 ScBaseCell
* pCell2
= aCol
[nCol
].GetCell( nIndex2
);
624 nRes
= CompareCell( nSort
, pCell1
, nCol
, nIndex1
, pCell2
, nCol
, nIndex2
);
625 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.bDoSort
[nSort
] );
631 SCROW nRow
= aSortParam
.nField
[nSort
];
632 ScBaseCell
* pCell1
= aCol
[nIndex1
].GetCell( nRow
);
633 ScBaseCell
* pCell2
= aCol
[nIndex2
].GetCell( nRow
);
634 nRes
= CompareCell( nSort
, pCell1
, static_cast<SCCOL
>(nIndex1
),
635 nRow
, pCell2
, static_cast<SCCOL
>(nIndex2
), nRow
);
636 } while ( nRes
== 0 && ++nSort
< nMaxSorts
&& aSortParam
.bDoSort
[nSort
] );
641 BOOL
ScTable::IsSorted( SCCOLROW nStart
, SCCOLROW nEnd
) // ueber aSortParam
643 for (SCCOLROW i
=nStart
; i
<nEnd
; i
++)
645 if (Compare( i
, i
+1 ) > 0)
651 void ScTable::DecoladeRow( ScSortInfoArray
* pArray
, SCROW nRow1
, SCROW nRow2
)
654 SCROW nMax
= nRow2
- nRow1
;
655 for (SCROW i
= nRow1
; (i
+ 4) <= nRow2
; i
+= 4)
657 nRow
= rand() % nMax
;
658 pArray
->Swap(i
, nRow1
+ nRow
);
662 void ScTable::Sort(const ScSortParam
& rSortParam
, BOOL bKeepQuery
)
664 aSortParam
= rSortParam
;
665 InitSortCollator( rSortParam
);
666 bGlobalKeepQuery
= bKeepQuery
;
667 if (rSortParam
.bByRow
)
670 for (SCCOL nCol
= aSortParam
.nCol1
; nCol
<= aSortParam
.nCol2
; nCol
++)
671 nLastRow
= Max(nLastRow
, aCol
[nCol
].GetLastDataPos());
672 nLastRow
= Min(nLastRow
, aSortParam
.nRow2
);
673 SCROW nRow1
= (rSortParam
.bHasHeader
?
674 aSortParam
.nRow1
+ 1 : aSortParam
.nRow1
);
675 if (!IsSorted(nRow1
, nLastRow
))
677 ScProgress
aProgress( pDocument
->GetDocumentShell(),
678 ScGlobal::GetRscString(STR_PROGRESS_SORTING
), nLastRow
- nRow1
);
679 ScSortInfoArray
* pArray
= CreateSortInfoArray( nRow1
, nLastRow
);
680 if ( nLastRow
- nRow1
> 255 )
681 DecoladeRow( pArray
, nRow1
, nLastRow
);
682 QuickSort( pArray
, nRow1
, nLastRow
);
683 SortReorder( pArray
, aProgress
);
685 // #158377# #i59745# update position of caption objects of cell notes
686 ScNoteUtil::UpdateCaptionPositions( *pDocument
, ScRange( aSortParam
.nCol1
, nRow1
, nTab
, aSortParam
.nCol2
, nLastRow
, nTab
) );
692 for (nLastCol
= aSortParam
.nCol2
;
693 (nLastCol
> aSortParam
.nCol1
) && aCol
[nLastCol
].IsEmptyBlock(aSortParam
.nRow1
, aSortParam
.nRow2
); nLastCol
--)
696 SCCOL nCol1
= (rSortParam
.bHasHeader
?
697 aSortParam
.nCol1
+ 1 : aSortParam
.nCol1
);
698 if (!IsSorted(nCol1
, nLastCol
))
700 ScProgress
aProgress( pDocument
->GetDocumentShell(),
701 ScGlobal::GetRscString(STR_PROGRESS_SORTING
), nLastCol
- nCol1
);
702 ScSortInfoArray
* pArray
= CreateSortInfoArray( nCol1
, nLastCol
);
703 QuickSort( pArray
, nCol1
, nLastCol
);
704 SortReorder( pArray
, aProgress
);
706 // #158377# #i59745# update position of caption objects of cell notes
707 ScNoteUtil::UpdateCaptionPositions( *pDocument
, ScRange( nCol1
, aSortParam
.nRow1
, nTab
, nLastCol
, aSortParam
.nRow2
, nTab
) );
710 DestroySortCollator();
714 // Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden
715 // (fuer Hinweis-Box)
717 BOOL
ScTable::TestRemoveSubTotals( const ScSubTotalParam
& rParam
)
719 SCCOL nStartCol
= rParam
.nCol1
;
720 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
721 SCCOL nEndCol
= rParam
.nCol2
;
722 SCROW nEndRow
= rParam
.nRow2
;
728 BOOL bWillDelete
= FALSE
;
729 for ( nCol
=nStartCol
; nCol
<=nEndCol
&& !bWillDelete
; nCol
++ )
731 ScColumnIterator
aIter( &aCol
[nCol
],nStartRow
,nEndRow
);
732 while ( aIter
.Next( nRow
, pCell
) && !bWillDelete
)
734 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
735 if (((ScFormulaCell
*)pCell
)->IsSubTotal())
737 for (SCCOL nTestCol
=0; nTestCol
<=MAXCOL
; nTestCol
++)
738 if (nTestCol
<nStartCol
|| nTestCol
>nEndCol
)
739 if (aCol
[nTestCol
].HasDataAt(nRow
))
747 // alte Ergebnisse loeschen
748 // rParam.nRow2 wird veraendert !
750 void ScTable::RemoveSubTotals( ScSubTotalParam
& rParam
)
752 SCCOL nStartCol
= rParam
.nCol1
;
753 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
754 SCCOL nEndCol
= rParam
.nCol2
;
755 SCROW nEndRow
= rParam
.nRow2
; // wird veraendert
761 for ( nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++ )
763 ScColumnIterator
aIter( &aCol
[nCol
],nStartRow
,nEndRow
);
764 while ( aIter
.Next( nRow
, pCell
) )
766 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
767 if (((ScFormulaCell
*)pCell
)->IsSubTotal())
769 SetRowFlags(nRow
+1,GetRowFlags(nRow
+1)&(~CR_MANUALBREAK
));
770 pDocument
->DeleteRow( 0,nTab
, MAXCOL
,nTab
, nRow
, 1 );
772 aIter
= ScColumnIterator( &aCol
[nCol
],nRow
,nEndRow
);
777 rParam
.nRow2
= nEndRow
; // neues Ende
780 // harte Zahlenformate loeschen (fuer Ergebnisformeln)
782 void lcl_RemoveNumberFormat( ScTable
* pTab
, SCCOL nCol
, SCROW nRow
)
784 const ScPatternAttr
* pPattern
= pTab
->GetPattern( nCol
, nRow
);
785 if ( pPattern
->GetItemSet().GetItemState( ATTR_VALUE_FORMAT
, FALSE
)
788 ScPatternAttr
aNewPattern( *pPattern
);
789 SfxItemSet
& rSet
= aNewPattern
.GetItemSet();
790 rSet
.ClearItem( ATTR_VALUE_FORMAT
);
791 rSet
.ClearItem( ATTR_LANGUAGE_FORMAT
);
792 pTab
->SetPattern( nCol
, nRow
, aNewPattern
, TRUE
);
797 // at least MSC needs this at linkage level to be able to use it in a template
798 typedef struct lcl_ScTable_DoSubTotals_RowEntry
807 // neue Zwischenergebnisse
808 // rParam.nRow2 wird veraendert !
810 BOOL
ScTable::DoSubTotals( ScSubTotalParam
& rParam
)
812 SCCOL nStartCol
= rParam
.nCol1
;
813 SCROW nStartRow
= rParam
.nRow1
+ 1; // Header
814 SCCOL nEndCol
= rParam
.nCol2
;
815 SCROW nEndRow
= rParam
.nRow2
; // wird veraendert
818 // Leerzeilen am Ende weglassen,
819 // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#)
820 // Wenn sortiert wurde, sind alle Leerzeilen am Ende.
821 SCSIZE nEmpty
= GetEmptyLinesInBlock( nStartCol
, nStartRow
, nEndCol
, nEndRow
, DIR_BOTTOM
);
824 USHORT nLevelCount
= 0; // Anzahl Gruppierungen
826 for (i
=0; i
<MAXSUBTOTAL
&& bDoThis
; i
++)
827 if (rParam
.bGroupActive
[i
])
832 if (nLevelCount
==0) // nichts tun
835 SCCOL
* nGroupCol
= rParam
.nField
; // Spalten nach denen
838 // #44444# Durch (leer) als eigene Kategorie muss immer auf
839 // Teilergebniszeilen aus den anderen Spalten getestet werden
840 // (frueher nur, wenn eine Spalte mehrfach vorkam)
841 BOOL bTestPrevSub
= ( nLevelCount
> 1 );
846 BOOL bIgnoreCase
= !rParam
.bCaseSens
;
848 String
*pCompString
[MAXSUBTOTAL
]; // Pointer wegen Compiler-Problemen
849 for (i
=0; i
<MAXSUBTOTAL
; i
++)
850 pCompString
[i
] = new String
;
854 ScStyleSheet
* pStyle
= (ScStyleSheet
*) pDocument
->GetStyleSheetPool()->Find(
855 ScGlobal::GetRscString(STR_STYLENAME_RESULT
), SFX_STYLE_FAMILY_PARA
);
857 BOOL bSpaceLeft
= TRUE
; // Erfolg beim Einfuegen?
859 // #90279# For performance reasons collect formula entries so their
860 // references don't have to be tested for updates each time a new row is
863 ::std::vector
< RowEntry
> aRowVector
;
865 for (USHORT nLevel
=0; nLevel
<=nLevelCount
&& bSpaceLeft
; nLevel
++) // incl. Gesamtergebnis
867 BOOL bTotal
= ( nLevel
== nLevelCount
);
868 aRowEntry
.nGroupNo
= bTotal
? 0 : (nLevelCount
-nLevel
-1);
870 // how many results per level
871 SCCOL nResCount
= rParam
.nSubTotals
[aRowEntry
.nGroupNo
];
873 ScSubTotalFunc
* eResFunc
= rParam
.pFunctions
[aRowEntry
.nGroupNo
];
875 if (nResCount
> 0) // sonst nur sortieren
877 for (i
=0; i
<=aRowEntry
.nGroupNo
; i
++)
879 GetString( nGroupCol
[i
], nStartRow
, aSubString
);
881 *pCompString
[i
] = ScGlobal::pCharClass
->upper( aSubString
);
883 *pCompString
[i
] = aSubString
;
884 } // aSubString bleibt auf dem letzten stehen
886 BOOL bBlockVis
= FALSE
; // Gruppe eingeblendet?
887 aRowEntry
.nSubStartRow
= nStartRow
;
888 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
+1 && bSpaceLeft
; nRow
++)
899 for (i
=0; i
<=aRowEntry
.nGroupNo
&& !bChanged
; i
++)
901 GetString( nGroupCol
[i
], nRow
, aString
);
903 ScGlobal::pCharClass
->toUpper( aString
);
904 // #41427# wenn sortiert, ist "leer" eine eigene Gruppe
905 // sonst sind leere Zellen unten erlaubt
906 bChanged
= ( ( aString
.Len() || rParam
.bDoSort
) &&
907 aString
!= *pCompString
[i
] );
909 if ( bChanged
&& bTestPrevSub
)
911 // No group change on rows that will contain subtotal formulas
912 for ( ::std::vector
< RowEntry
>::const_iterator
913 iEntry( aRowVector
.begin());
914 iEntry
!= aRowVector
.end(); ++iEntry
)
916 if ( iEntry
->nDestRow
== nRow
)
927 aRowEntry
.nDestRow
= nRow
;
928 aRowEntry
.nFuncStart
= aRowEntry
.nSubStartRow
;
929 aRowEntry
.nFuncEnd
= nRow
-1;
931 bSpaceLeft
= pDocument
->InsertRow( 0, nTab
, MAXCOL
, nTab
,
932 aRowEntry
.nDestRow
, 1 );
933 DBShowRow( aRowEntry
.nDestRow
, bBlockVis
);
935 if ( rParam
.bPagebreak
&& nRow
< MAXROW
&&
936 aRowEntry
.nSubStartRow
!= nStartRow
&& nLevel
== 0)
937 SetRowFlags( aRowEntry
.nSubStartRow
,
938 GetRowFlags(aRowEntry
.nSubStartRow
) |
943 for ( ::std::vector
< RowEntry
>::iterator
iMove(
944 aRowVector
.begin() );
945 iMove
!= aRowVector
.end(); ++iMove
)
947 if ( aRowEntry
.nDestRow
<= iMove
->nSubStartRow
)
948 ++iMove
->nSubStartRow
;
949 if ( aRowEntry
.nDestRow
<= iMove
->nDestRow
)
951 if ( aRowEntry
.nDestRow
<= iMove
->nFuncStart
)
953 if ( aRowEntry
.nDestRow
<= iMove
->nFuncEnd
)
956 // collect formula positions
957 aRowVector
.push_back( aRowEntry
);
959 if (bTotal
) // "Gesamtergebnis"
960 aOutString
= ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS
);
963 aOutString
= aSubString
;
964 if (!aOutString
.Len())
965 aOutString
= ScGlobal::GetRscString( STR_EMPTYDATA
);
967 USHORT nStrId
= STR_TABLE_ERGEBNIS
;
968 if ( nResCount
== 1 )
969 switch ( eResFunc
[0] )
971 case SUBTOTAL_FUNC_AVE
: nStrId
= STR_FUN_TEXT_AVG
; break;
972 case SUBTOTAL_FUNC_CNT
:
973 case SUBTOTAL_FUNC_CNT2
: nStrId
= STR_FUN_TEXT_COUNT
; break;
974 case SUBTOTAL_FUNC_MAX
: nStrId
= STR_FUN_TEXT_MAX
; break;
975 case SUBTOTAL_FUNC_MIN
: nStrId
= STR_FUN_TEXT_MIN
; break;
976 case SUBTOTAL_FUNC_PROD
: nStrId
= STR_FUN_TEXT_PRODUCT
; break;
977 case SUBTOTAL_FUNC_STD
:
978 case SUBTOTAL_FUNC_STDP
: nStrId
= STR_FUN_TEXT_STDDEV
; break;
979 case SUBTOTAL_FUNC_SUM
: nStrId
= STR_FUN_TEXT_SUM
; break;
980 case SUBTOTAL_FUNC_VAR
:
981 case SUBTOTAL_FUNC_VARP
: nStrId
= STR_FUN_TEXT_VAR
; break;
984 // added to avoid warnings
987 aOutString
+= ScGlobal::GetRscString( nStrId
);
989 SetString( nGroupCol
[aRowEntry
.nGroupNo
], aRowEntry
.nDestRow
, nTab
, aOutString
);
990 ApplyStyle( nGroupCol
[aRowEntry
.nGroupNo
], aRowEntry
.nDestRow
, *pStyle
);
992 /* if (rParam.bPagebreak && nRow < MAXROW)
994 BYTE nFlags = GetRowFlags( nRow+1 );
995 nFlags |= CR_MANUALBREAK;
996 SetRowFlags( nRow+1, nFlags );
1001 aRowEntry
.nSubStartRow
= nRow
;
1002 for (i
=0; i
<=aRowEntry
.nGroupNo
; i
++)
1004 GetString( nGroupCol
[i
], nRow
, aSubString
);
1006 *pCompString
[i
] = ScGlobal::pCharClass
->upper( aSubString
);
1008 *pCompString
[i
] = aSubString
;
1015 if ( (pRowFlags
->GetValue(nRow
) & CR_FILTERED
) == 0 )
1021 // DBG_ERROR( "nSubTotals==0 bei DoSubTotals" );
1025 // now insert the formulas
1026 ScComplexRefData aRef
;
1028 aRef
.Ref1
.nTab
= nTab
;
1029 aRef
.Ref2
.nTab
= nTab
;
1030 for ( ::std::vector
< RowEntry
>::const_iterator
iEntry( aRowVector
.begin());
1031 iEntry
!= aRowVector
.end(); ++iEntry
)
1033 SCCOL nResCount
= rParam
.nSubTotals
[iEntry
->nGroupNo
];
1034 SCCOL
* nResCols
= rParam
.pSubTotals
[iEntry
->nGroupNo
];
1035 ScSubTotalFunc
* eResFunc
= rParam
.pFunctions
[iEntry
->nGroupNo
];
1036 for ( SCCOL nResult
=0; nResult
< nResCount
; ++nResult
)
1038 aRef
.Ref1
.nCol
= nResCols
[nResult
];
1039 aRef
.Ref1
.nRow
= iEntry
->nFuncStart
;
1040 aRef
.Ref2
.nCol
= nResCols
[nResult
];
1041 aRef
.Ref2
.nRow
= iEntry
->nFuncEnd
;
1044 aArr
.AddOpCode( ocSubTotal
);
1045 aArr
.AddOpCode( ocOpen
);
1046 aArr
.AddDouble( (double) eResFunc
[nResult
] );
1047 aArr
.AddOpCode( ocSep
);
1048 aArr
.AddDoubleReference( aRef
);
1049 aArr
.AddOpCode( ocClose
);
1050 aArr
.AddOpCode( ocStop
);
1051 ScBaseCell
* pCell
= new ScFormulaCell( pDocument
, ScAddress(
1052 nResCols
[nResult
], iEntry
->nDestRow
, nTab
), &aArr
);
1053 PutCell( nResCols
[nResult
], iEntry
->nDestRow
, pCell
);
1055 if ( nResCols
[nResult
] != nGroupCol
[iEntry
->nGroupNo
] )
1057 ApplyStyle( nResCols
[nResult
], iEntry
->nDestRow
, *pStyle
);
1059 // Zahlformat loeschen
1060 lcl_RemoveNumberFormat( this, nResCols
[nResult
], iEntry
->nDestRow
);
1066 //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ?
1068 //! Outlines direkt erzeugen?
1071 DoAutoOutline( nStartCol
, nStartRow
, nEndCol
, nEndRow
);
1073 for (i
=0; i
<MAXSUBTOTAL
; i
++)
1074 delete pCompString
[i
];
1076 rParam
.nRow2
= nEndRow
; // neues Ende
1081 BOOL
ScTable::ValidQuery(SCROW nRow
, const ScQueryParam
& rParam
,
1082 BOOL
* pSpecial
/* =NULL */ , ScBaseCell
* pCell
/* =NULL */ ,
1083 BOOL
* pbTestEqualCondition
/* = NULL */ )
1085 if (!rParam
.GetEntry(0).bDoQuery
)
1088 //---------------------------------------------------------------
1090 const SCSIZE nFixedBools
= 32;
1091 BOOL aBool
[nFixedBools
];
1092 BOOL aTest
[nFixedBools
];
1093 SCSIZE nEntryCount
= rParam
.GetEntryCount();
1094 BOOL
* pPasst
= ( nEntryCount
<= nFixedBools
? &aBool
[0] : new BOOL
[nEntryCount
] );
1095 BOOL
* pTest
= ( nEntryCount
<= nFixedBools
? &aTest
[0] : new BOOL
[nEntryCount
] );
1099 BOOL bMatchWholeCell
= pDocument
->GetDocOptions().IsMatchWholeCell();
1100 CollatorWrapper
* pCollator
= (rParam
.bCaseSens
? ScGlobal::pCaseCollator
:
1101 ScGlobal::pCollator
);
1102 ::utl::TransliterationWrapper
* pTransliteration
= (rParam
.bCaseSens
?
1103 ScGlobal::pCaseTransliteration
: ScGlobal::pTransliteration
);
1105 while ( (i
< nEntryCount
) && rParam
.GetEntry(i
).bDoQuery
)
1107 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
1108 // we can only handle one single direct query
1109 if ( !pCell
|| i
> 0 )
1110 pCell
= GetCell( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1113 BOOL bTestEqual
= FALSE
;
1115 if ( pSpecial
&& pSpecial
[i
] )
1117 if (rEntry
.nVal
== SC_EMPTYFIELDS
)
1118 bOk
= !( aCol
[rEntry
.nField
].HasDataAt( nRow
) );
1119 else // if (rEntry.nVal == SC_NONEMPTYFIELDS)
1120 bOk
= aCol
[rEntry
.nField
].HasDataAt( nRow
);
1122 else if ( !rEntry
.bQueryByString
&& (pCell
? pCell
->HasValueData() :
1123 HasValueData( static_cast<SCCOL
>(rEntry
.nField
), nRow
)))
1128 switch ( pCell
->GetCellType() )
1130 case CELLTYPE_VALUE
:
1131 nCellVal
= ((ScValueCell
*)pCell
)->GetValue();
1133 case CELLTYPE_FORMULA
:
1134 nCellVal
= ((ScFormulaCell
*)pCell
)->GetValue();
1142 nCellVal
= GetValue( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1146 bOk
= ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1149 bOk
= (nCellVal
< rEntry
.nVal
) && !::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1152 bOk
= (nCellVal
> rEntry
.nVal
) && !::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1154 case SC_LESS_EQUAL
:
1155 bOk
= (nCellVal
< rEntry
.nVal
) || ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1156 if ( bOk
&& pbTestEqualCondition
)
1157 bTestEqual
= ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1159 case SC_GREATER_EQUAL
:
1160 bOk
= (nCellVal
> rEntry
.nVal
) || ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1161 if ( bOk
&& pbTestEqualCondition
)
1162 bTestEqual
= ::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1165 bOk
= !::rtl::math::approxEqual( nCellVal
, rEntry
.nVal
);
1169 // added to avoid warnings
1173 else if ( (rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
) ||
1174 (rEntry
.eOp
== SC_CONTAINS
|| rEntry
.eOp
== SC_DOES_NOT_CONTAIN
||
1175 rEntry
.eOp
== SC_BEGINS_WITH
|| rEntry
.eOp
== SC_ENDS_WITH
||
1176 rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
) ||
1177 (rEntry
.bQueryByString
&& (pCell
? pCell
->HasStringData() :
1179 static_cast<SCCOL
>(rEntry
.nField
),
1183 if( rEntry
.eOp
== SC_CONTAINS
|| rEntry
.eOp
== SC_DOES_NOT_CONTAIN
1184 || rEntry
.eOp
== SC_BEGINS_WITH
|| rEntry
.eOp
== SC_ENDS_WITH
1185 || rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
1186 bMatchWholeCell
= FALSE
;
1189 if (pCell
->GetCellType() != CELLTYPE_NOTE
)
1191 ULONG nFormat
= GetNumberFormat( static_cast<SCCOL
>(rEntry
.nField
), nRow
);
1192 ScCellFormat::GetInputString( pCell
, nFormat
, aCellStr
, *(pDocument
->GetFormatTable()) );
1196 GetInputString( static_cast<SCCOL
>(rEntry
.nField
), nRow
, aCellStr
);
1198 BOOL bRealRegExp
= (rParam
.bRegExp
&& ((rEntry
.eOp
== SC_EQUAL
)
1199 || (rEntry
.eOp
== SC_NOT_EQUAL
) || (rEntry
.eOp
== SC_CONTAINS
)
1200 || (rEntry
.eOp
== SC_DOES_NOT_CONTAIN
) || (rEntry
.eOp
== SC_BEGINS_WITH
)
1201 || (rEntry
.eOp
== SC_ENDS_WITH
) || (rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
)
1202 || (rEntry
.eOp
== SC_DOES_NOT_END_WITH
)));
1203 BOOL bTestRegExp
= (pbTestEqualCondition
&& rParam
.bRegExp
1204 && ((rEntry
.eOp
== SC_LESS_EQUAL
)
1205 || (rEntry
.eOp
== SC_GREATER_EQUAL
)));
1206 if ( bRealRegExp
|| bTestRegExp
)
1208 xub_StrLen nStart
= 0;
1209 xub_StrLen nEnd
= aCellStr
.Len();
1211 // from 614 on, nEnd is behind the found text
1212 BOOL bMatch
= FALSE
;
1213 if ( rEntry
.eOp
== SC_ENDS_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
1216 nStart
= aCellStr
.Len();
1217 bMatch
= (BOOL
) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
1218 ->SearchBkwrd( aCellStr
, &nStart
, &nEnd
);
1222 bMatch
= (BOOL
) rEntry
.GetSearchTextPtr( rParam
.bCaseSens
)
1223 ->SearchFrwrd( aCellStr
, &nStart
, &nEnd
);
1225 if ( bMatch
&& bMatchWholeCell
1226 && (nStart
!= 0 || nEnd
!= aCellStr
.Len()) )
1227 bMatch
= FALSE
; // RegExp must match entire cell string
1236 case SC_DOES_NOT_CONTAIN
:
1239 case SC_BEGINS_WITH
:
1240 bOk
= ( bMatch
&& (nStart
== 0) );
1242 case SC_DOES_NOT_BEGIN_WITH
:
1243 bOk
= !( bMatch
&& (nStart
== 0) );
1246 bOk
= ( bMatch
&& (nEnd
== aCellStr
.Len()) );
1248 case SC_DOES_NOT_END_WITH
:
1249 bOk
= !( bMatch
&& (nEnd
== aCellStr
.Len()) );
1253 // added to avoid warnings
1257 bTestEqual
= bMatch
;
1261 if ( rEntry
.eOp
== SC_EQUAL
|| rEntry
.eOp
== SC_NOT_EQUAL
1262 || rEntry
.eOp
== SC_CONTAINS
|| rEntry
.eOp
== SC_DOES_NOT_CONTAIN
1263 || rEntry
.eOp
== SC_BEGINS_WITH
|| rEntry
.eOp
== SC_ENDS_WITH
1264 || rEntry
.eOp
== SC_DOES_NOT_BEGIN_WITH
|| rEntry
.eOp
== SC_DOES_NOT_END_WITH
)
1266 if ( !rEntry
.bQueryByString
&& rEntry
.pStr
->Len() == 0 )
1268 // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
1269 // the query value is assigned directly, and the string is empty. In that case,
1270 // don't find any string (isEqual would find empty string results in formula cells).
1272 if ( rEntry
.eOp
== SC_NOT_EQUAL
)
1275 else if ( bMatchWholeCell
)
1277 bOk
= pTransliteration
->isEqual( aCellStr
, *rEntry
.pStr
);
1278 if ( rEntry
.eOp
== SC_NOT_EQUAL
)
1283 String
aCell( pTransliteration
->transliterate(
1284 aCellStr
, ScGlobal::eLnge
, 0, aCellStr
.Len(),
1286 String
aQuer( pTransliteration
->transliterate(
1287 *rEntry
.pStr
, ScGlobal::eLnge
, 0, rEntry
.pStr
->Len(),
1289 xub_StrLen nIndex
= (rEntry
.eOp
== SC_ENDS_WITH
1290 || rEntry
.eOp
== SC_DOES_NOT_END_WITH
)? (aCell
.Len()-aQuer
.Len()):0;
1291 xub_StrLen nStrPos
= aCell
.Search( aQuer
, nIndex
);
1296 bOk
= ( nStrPos
!= STRING_NOTFOUND
);
1299 case SC_DOES_NOT_CONTAIN
:
1300 bOk
= ( nStrPos
== STRING_NOTFOUND
);
1302 case SC_BEGINS_WITH
:
1303 bOk
= ( nStrPos
== 0 );
1305 case SC_DOES_NOT_BEGIN_WITH
:
1306 bOk
= ( nStrPos
!= 0 );
1309 bOk
= ( nStrPos
+ aQuer
.Len() == aCell
.Len() );
1311 case SC_DOES_NOT_END_WITH
:
1312 bOk
= ( nStrPos
+ aQuer
.Len() != aCell
.Len() );
1316 // added to avoid warnings
1322 { // use collator here because data was probably sorted
1323 sal_Int32 nCompare
= pCollator
->compareString(
1324 aCellStr
, *rEntry
.pStr
);
1328 bOk
= (nCompare
< 0);
1331 bOk
= (nCompare
> 0);
1333 case SC_LESS_EQUAL
:
1334 bOk
= (nCompare
<= 0);
1335 if ( bOk
&& pbTestEqualCondition
&& !bTestEqual
)
1336 bTestEqual
= (nCompare
== 0);
1338 case SC_GREATER_EQUAL
:
1339 bOk
= (nCompare
>= 0);
1340 if ( bOk
&& pbTestEqualCondition
&& !bTestEqual
)
1341 bTestEqual
= (nCompare
== 0);
1345 // added to avoid warnings
1351 else if (rParam
.bMixedComparison
)
1353 if (rEntry
.bQueryByString
&&
1354 (rEntry
.eOp
== SC_LESS
|| rEntry
.eOp
== SC_LESS_EQUAL
) &&
1355 (pCell
? pCell
->HasValueData() :
1356 HasValueData( static_cast<SCCOL
>(rEntry
.nField
), nRow
)))
1360 else if (!rEntry
.bQueryByString
&&
1361 (rEntry
.eOp
== SC_GREATER
|| rEntry
.eOp
== SC_GREATER_EQUAL
) &&
1362 (pCell
? pCell
->HasStringData() :
1363 HasStringData( static_cast<SCCOL
>(rEntry
.nField
), nRow
)))
1373 pTest
[nPos
] = bTestEqual
;
1377 if (rEntry
.eConnect
== SC_AND
)
1379 pPasst
[nPos
] = pPasst
[nPos
] && bOk
;
1380 pTest
[nPos
] = pTest
[nPos
] && bTestEqual
;
1386 pTest
[nPos
] = bTestEqual
;
1392 for ( long j
=1; j
<= nPos
; j
++ )
1394 pPasst
[0] = pPasst
[0] || pPasst
[j
];
1395 pTest
[0] = pTest
[0] || pTest
[j
];
1398 BOOL bRet
= pPasst
[0];
1399 if ( pPasst
!= &aBool
[0] )
1401 if ( pbTestEqualCondition
)
1402 *pbTestEqualCondition
= pTest
[0];
1403 if ( pTest
!= &aTest
[0] )
1409 void ScTable::TopTenQuery( ScQueryParam
& rParam
)
1411 BOOL bSortCollatorInitialized
= FALSE
;
1412 SCSIZE nEntryCount
= rParam
.GetEntryCount();
1413 SCROW nRow1
= (rParam
.bHasHeader
? rParam
.nRow1
+ 1 : rParam
.nRow1
);
1414 SCSIZE nCount
= static_cast<SCSIZE
>(rParam
.nRow2
- nRow1
+ 1);
1415 for ( SCSIZE i
=0; (i
<nEntryCount
) && (rParam
.GetEntry(i
).bDoQuery
); i
++ )
1417 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
1418 switch ( rEntry
.eOp
)
1425 ScSortParam
aLocalSortParam( rParam
, static_cast<SCCOL
>(rEntry
.nField
) );
1426 aSortParam
= aLocalSortParam
; // used in CreateSortInfoArray, Compare
1427 if ( !bSortCollatorInitialized
)
1429 bSortCollatorInitialized
= TRUE
;
1430 InitSortCollator( aLocalSortParam
);
1432 ScSortInfoArray
* pArray
= CreateSortInfoArray( nRow1
, rParam
.nRow2
);
1433 DecoladeRow( pArray
, nRow1
, rParam
.nRow2
);
1434 QuickSort( pArray
, nRow1
, rParam
.nRow2
);
1435 ScSortInfo
** ppInfo
= pArray
->GetFirstArray();
1436 SCSIZE nValidCount
= nCount
;
1437 // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
1438 while ( nValidCount
> 0 && ( ppInfo
[nValidCount
-1]->pCell
== NULL
||
1439 ppInfo
[nValidCount
-1]->pCell
->GetCellType() == CELLTYPE_NOTE
) )
1441 // keine Strings zaehlen, sind zwischen Value und Leer
1442 while ( nValidCount
> 0
1443 && ppInfo
[nValidCount
-1]->pCell
->HasStringData() )
1445 if ( nValidCount
> 0 )
1447 if ( rEntry
.bQueryByString
)
1449 rEntry
.bQueryByString
= FALSE
;
1450 rEntry
.nVal
= 10; // 10 bzw. 10%
1452 SCSIZE nVal
= (rEntry
.nVal
>= 1 ? static_cast<SCSIZE
>(rEntry
.nVal
) : 1);
1454 switch ( rEntry
.eOp
)
1458 rEntry
.eOp
= SC_GREATER_EQUAL
;
1459 if ( nVal
> nValidCount
)
1461 nOffset
= nValidCount
- nVal
; // 1 <= nVal <= nValidCount
1466 rEntry
.eOp
= SC_LESS_EQUAL
;
1467 if ( nVal
> nValidCount
)
1469 nOffset
= nVal
- 1; // 1 <= nVal <= nValidCount
1474 rEntry
.eOp
= SC_GREATER_EQUAL
;
1477 nOffset
= nValidCount
- (nValidCount
* nVal
/ 100);
1478 if ( nOffset
>= nValidCount
)
1479 nOffset
= nValidCount
- 1;
1484 rEntry
.eOp
= SC_LESS_EQUAL
;
1487 nOffset
= (nValidCount
* nVal
/ 100);
1488 if ( nOffset
>= nValidCount
)
1489 nOffset
= nValidCount
- 1;
1494 // added to avoid warnings
1497 ScBaseCell
* pCell
= ppInfo
[nOffset
]->pCell
;
1498 if ( pCell
->HasValueData() )
1500 if ( pCell
->GetCellType() == CELLTYPE_VALUE
)
1501 rEntry
.nVal
= ((ScValueCell
*)pCell
)->GetValue();
1503 rEntry
.nVal
= ((ScFormulaCell
*)pCell
)->GetValue();
1507 DBG_ERRORFILE( "TopTenQuery: pCell kein ValueData" );
1508 rEntry
.eOp
= SC_GREATER_EQUAL
;
1514 rEntry
.eOp
= SC_GREATER_EQUAL
;
1515 rEntry
.bQueryByString
= FALSE
;
1523 // added to avoid warnings
1527 if ( bSortCollatorInitialized
)
1528 DestroySortCollator();
1531 static void lcl_PrepareQuery( ScDocument
* pDoc
, ScTable
* pTab
, ScQueryParam
& rParam
, BOOL
* pSpecial
)
1533 bool bTopTen
= false;
1534 SCSIZE nEntryCount
= rParam
.GetEntryCount();
1536 for ( SCSIZE i
= 0; i
< nEntryCount
; ++i
)
1538 pSpecial
[i
] = FALSE
;
1539 ScQueryEntry
& rEntry
= rParam
.GetEntry(i
);
1540 if ( rEntry
.bDoQuery
)
1542 if ( rEntry
.bQueryByString
)
1544 sal_uInt32 nIndex
= 0;
1545 rEntry
.bQueryByString
= !( pDoc
->GetFormatTable()->
1546 IsNumberFormat( *rEntry
.pStr
, nIndex
, rEntry
.nVal
) );
1550 // #58736# call from UNO or second call from autofilter
1551 if ( rEntry
.nVal
== SC_EMPTYFIELDS
|| rEntry
.nVal
== SC_NONEMPTYFIELDS
)
1558 switch ( rEntry
.eOp
)
1578 pTab
->TopTenQuery( rParam
);
1582 SCSIZE
ScTable::Query(ScQueryParam
& rParamOrg
, BOOL bKeepSub
)
1584 ScQueryParam
aParam( rParamOrg
);
1585 ScStrCollection aScStrCollection
;
1586 StrData
* pStrData
= NULL
;
1588 BOOL bStarted
= FALSE
;
1589 BOOL bOldResult
= TRUE
;
1590 SCROW nOldStart
= 0;
1595 SCROW nHeader
= aParam
.bHasHeader
? 1 : 0;
1597 SCSIZE nEntryCount
= aParam
.GetEntryCount();
1598 BOOL
* pSpecial
= new BOOL
[nEntryCount
];
1599 lcl_PrepareQuery( pDocument
, this, aParam
, pSpecial
);
1601 SCROW nEndRow
= aParam
.bUseDynamicRange
? aParam
.nDynamicEndRow
: aParam
.nRow2
;
1602 if (!aParam
.bInplace
)
1604 nOutRow
= aParam
.nDestRow
+ nHeader
;
1606 CopyData( aParam
.nCol1
, aParam
.nRow1
, aParam
.nCol2
, aParam
.nRow1
,
1607 aParam
.nDestCol
, aParam
.nDestRow
, aParam
.nDestTab
);
1610 for (SCROW j
=aParam
.nRow1
+ nHeader
; j
<=nEndRow
; j
++)
1612 BOOL bResult
; // Filterergebnis
1613 BOOL bValid
= ValidQuery(j
, aParam
, pSpecial
);
1614 if (!bValid
&& bKeepSub
) // Subtotals stehenlassen
1616 for (SCCOL nCol
=aParam
.nCol1
; nCol
<=aParam
.nCol2
&& !bValid
; nCol
++)
1619 pCell
= GetCell( nCol
, j
);
1621 if ( pCell
->GetCellType() == CELLTYPE_FORMULA
)
1622 if (((ScFormulaCell
*)pCell
)->IsSubTotal())
1623 if (RefVisible((ScFormulaCell
*)pCell
))
1629 if (aParam
.bDuplicate
)
1634 for (SCCOL k
=aParam
.nCol1
; k
<= aParam
.nCol2
; k
++)
1637 GetString(k
, j
, aCellStr
);
1639 aStr
+= (sal_Unicode
)1;
1641 pStrData
= new StrData(aStr
);
1643 BOOL bIsUnique
= TRUE
;
1645 bIsUnique
= aScStrCollection
.Insert(pStrData
);
1658 if (aParam
.bInplace
)
1660 if (bResult
== bOldResult
&& bStarted
)
1665 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
1666 nOldStart
= nOldEnd
= j
;
1667 bOldResult
= bResult
;
1675 CopyData( aParam
.nCol1
,j
, aParam
.nCol2
,j
, aParam
.nDestCol
,nOutRow
,aParam
.nDestTab
);
1683 if (aParam
.bInplace
&& bStarted
)
1684 DBShowRows(nOldStart
,nOldEnd
, bOldResult
);
1691 BOOL
ScTable::CreateExcelQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
1694 SCCOL
* pFields
= new SCCOL
[nCol2
-nCol1
+1];
1697 DBG_ASSERT( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
1698 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
1699 SCROW nDBRow1
= rQueryParam
.nRow1
;
1700 SCCOL nDBCol2
= rQueryParam
.nCol2
;
1701 // Erste Zeile muessen Spaltenkoepfe sein
1702 while (bValid
&& (nCol
<= nCol2
))
1705 GetUpperCellString(nCol
, nRow1
, aQueryStr
);
1706 BOOL bFound
= FALSE
;
1707 SCCOL i
= rQueryParam
.nCol1
;
1708 while (!bFound
&& (i
<= nDBCol2
))
1710 if ( nTab
== nDBTab
)
1711 GetUpperCellString(i
, nDBRow1
, aCellStr
);
1713 pDocument
->GetUpperCellString(i
, nDBRow1
, nDBTab
, aCellStr
);
1714 bFound
= (aCellStr
== aQueryStr
);
1718 pFields
[nCol
- nCol1
] = i
;
1726 for ( nCol
=nCol1
; nCol
<=nCol2
; nCol
++ )
1727 nVisible
+= aCol
[nCol
].VisibleCount( nRow1
+1, nRow2
);
1729 if ( nVisible
> SCSIZE_MAX
/ sizeof(void*) )
1731 DBG_ERROR("zu viele Filterkritierien");
1735 SCSIZE nNewEntries
= nVisible
;
1736 rQueryParam
.Resize( nNewEntries
);
1739 SCROW nRow
= nRow1
+ 1;
1740 while (nRow
<= nRow2
)
1743 while (nCol
<= nCol2
)
1745 GetInputString( nCol
, nRow
, aCellStr
);
1746 ScGlobal::pCharClass
->toUpper( aCellStr
);
1747 if (aCellStr
.Len() > 0)
1749 if (nIndex
< nNewEntries
)
1751 rQueryParam
.GetEntry(nIndex
).nField
= pFields
[nCol
- nCol1
];
1752 rQueryParam
.FillInExcelSyntax(aCellStr
, nIndex
);
1754 if (nIndex
< nNewEntries
)
1755 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_AND
;
1763 if (nIndex
< nNewEntries
)
1764 rQueryParam
.GetEntry(nIndex
).eConnect
= SC_OR
;
1771 BOOL
ScTable::CreateStarQuery(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
1773 // A valid StarQuery must be at least 4 columns wide. To be precise it
1774 // should be exactly 4 columns ...
1775 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
1776 // column Excel style query range immediately left to itself would result
1777 // in a circular reference when the field name or operator or value (first
1778 // to third query range column) is obtained (#i58354#). Furthermore, if the
1779 // range wasn't sufficiently specified data changes wouldn't flag formula
1780 // cells for recalculation.
1781 if (nCol2
- nCol1
< 3)
1789 DBG_ASSERT( rQueryParam
.nTab
!= SCTAB_MAX
, "rQueryParam.nTab no value, not bad but no good" );
1790 SCTAB nDBTab
= (rQueryParam
.nTab
== SCTAB_MAX
? nTab
: rQueryParam
.nTab
);
1791 SCROW nDBRow1
= rQueryParam
.nRow1
;
1792 SCCOL nDBCol2
= rQueryParam
.nCol2
;
1794 SCSIZE nNewEntries
= static_cast<SCSIZE
>(nRow2
-nRow1
+1);
1795 rQueryParam
.Resize( nNewEntries
);
1799 ScQueryEntry
& rEntry
= rQueryParam
.GetEntry(nIndex
);
1802 // Erste Spalte UND/ODER
1805 GetUpperCellString(nCol1
, nRow
, aCellStr
);
1806 if ( aCellStr
== ScGlobal::GetRscString(STR_TABLE_UND
) )
1808 rEntry
.eConnect
= SC_AND
;
1811 else if ( aCellStr
== ScGlobal::GetRscString(STR_TABLE_ODER
) )
1813 rEntry
.eConnect
= SC_OR
;
1817 // Zweite Spalte FeldName
1818 if ((nIndex
< 1) || bValid
)
1821 GetUpperCellString(nCol1
+ 1, nRow
, aCellStr
);
1822 for (SCCOL i
=rQueryParam
.nCol1
; (i
<= nDBCol2
) && (!bFound
); i
++)
1825 if ( nTab
== nDBTab
)
1826 GetUpperCellString(i
, nDBRow1
, aFieldStr
);
1828 pDocument
->GetUpperCellString(i
, nDBRow1
, nDBTab
, aFieldStr
);
1829 bFound
= (aCellStr
== aFieldStr
);
1839 // Dritte Spalte Operator =<>...
1843 GetUpperCellString(nCol1
+ 2, nRow
, aCellStr
);
1844 if (aCellStr
.GetChar(0) == '<')
1846 if (aCellStr
.GetChar(1) == '>')
1847 rEntry
.eOp
= SC_NOT_EQUAL
;
1848 else if (aCellStr
.GetChar(1) == '=')
1849 rEntry
.eOp
= SC_LESS_EQUAL
;
1851 rEntry
.eOp
= SC_LESS
;
1853 else if (aCellStr
.GetChar(0) == '>')
1855 if (aCellStr
.GetChar(1) == '=')
1856 rEntry
.eOp
= SC_GREATER_EQUAL
;
1858 rEntry
.eOp
= SC_GREATER
;
1860 else if (aCellStr
.GetChar(0) == '=')
1861 rEntry
.eOp
= SC_EQUAL
;
1864 // Vierte Spalte Wert
1867 GetString(nCol1
+ 3, nRow
, *rEntry
.pStr
);
1868 rEntry
.bDoQuery
= TRUE
;
1873 while (bValid
&& (nRow
<= nRow2
) /* && (nIndex < MAXQUERY) */ );
1877 BOOL
ScTable::CreateQueryParam(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
, ScQueryParam
& rQueryParam
)
1880 PutInOrder(nCol1
, nCol2
);
1881 PutInOrder(nRow1
, nRow2
);
1883 nCount
= rQueryParam
.GetEntryCount();
1884 for (i
=0; i
< nCount
; i
++)
1885 rQueryParam
.GetEntry(i
).Clear();
1887 // Standard QueryTabelle
1888 BOOL bValid
= CreateStarQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
1889 // Excel QueryTabelle
1891 bValid
= CreateExcelQuery(nCol1
, nRow1
, nCol2
, nRow2
, rQueryParam
);
1893 nCount
= rQueryParam
.GetEntryCount();
1896 // bQueryByString muss gesetzt sein
1897 for (i
=0; i
< nCount
; i
++)
1898 rQueryParam
.GetEntry(i
).bQueryByString
= TRUE
;
1903 for (i
=0; i
< nCount
; i
++)
1904 rQueryParam
.GetEntry(i
).Clear();
1909 BOOL
ScTable::HasColHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW
/* nEndRow */ )
1911 for (SCCOL nCol
=nStartCol
; nCol
<=nEndCol
; nCol
++)
1913 CellType eType
= GetCellType( nCol
, nStartRow
);
1914 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
1920 BOOL
ScTable::HasRowHeader( SCCOL nStartCol
, SCROW nStartRow
, SCCOL
/* nEndCol */, SCROW nEndRow
)
1922 for (SCROW nRow
=nStartRow
; nRow
<=nEndRow
; nRow
++)
1924 CellType eType
= GetCellType( nStartCol
, nRow
);
1925 if (eType
!= CELLTYPE_STRING
&& eType
!= CELLTYPE_EDIT
)
1931 void ScTable::GetFilterEntries(SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, TypedScStrCollection
& rStrings
)
1933 aCol
[nCol
].GetFilterEntries( nRow1
, nRow2
, rStrings
);
1936 void ScTable::GetFilteredFilterEntries( SCCOL nCol
, SCROW nRow1
, SCROW nRow2
, const ScQueryParam
& rParam
, TypedScStrCollection
& rStrings
)
1938 // remove the entry for this column from the query parameter
1939 ScQueryParam
aParam( rParam
);
1940 SCSIZE nEntryCount
= aParam
.GetEntryCount();
1941 for ( SCSIZE i
= 0; i
< nEntryCount
&& aParam
.GetEntry(i
).bDoQuery
; ++i
)
1943 ScQueryEntry
& rEntry
= aParam
.GetEntry(i
);
1944 if ( rEntry
.nField
== nCol
)
1946 aParam
.DeleteQuery(i
);
1950 nEntryCount
= aParam
.GetEntryCount();
1952 BOOL
* pSpecial
= new BOOL
[nEntryCount
];
1953 lcl_PrepareQuery( pDocument
, this, aParam
, pSpecial
);
1955 for ( SCROW j
= nRow1
; j
<= nRow2
; ++j
)
1957 if ( ValidQuery( j
, aParam
, pSpecial
) )
1959 aCol
[nCol
].GetFilterEntries( j
, j
, rStrings
);
1966 BOOL
ScTable::GetDataEntries(SCCOL nCol
, SCROW nRow
, TypedScStrCollection
& rStrings
, BOOL bLimit
)
1968 return aCol
[nCol
].GetDataEntries( nRow
, rStrings
, bLimit
);
1971 ULONG
ScTable::GetCellCount() const
1973 ULONG nCellCount
= 0;
1975 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
1976 nCellCount
+= aCol
[nCol
].GetCellCount();
1981 ULONG
ScTable::GetWeightedCount() const
1983 ULONG nCellCount
= 0;
1985 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
1986 if ( aCol
[nCol
].GetCellCount() ) // GetCellCount ist inline
1987 nCellCount
+= aCol
[nCol
].GetWeightedCount();
1992 ULONG
ScTable::GetCodeCount() const
1994 ULONG nCodeCount
= 0;
1996 for ( SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++ )
1997 if ( aCol
[nCol
].GetCellCount() ) // GetCellCount ist inline
1998 nCodeCount
+= aCol
[nCol
].GetCodeCount();
2003 sal_Int32
ScTable::GetMaxStringLen( SCCOL nCol
, SCROW nRowStart
,
2004 SCROW nRowEnd
, CharSet eCharSet
) const
2006 if ( ValidCol(nCol
) )
2007 return aCol
[nCol
].GetMaxStringLen( nRowStart
, nRowEnd
, eCharSet
);
2012 xub_StrLen
ScTable::GetMaxNumberStringLen( USHORT
& nPrecision
, SCCOL nCol
,
2013 SCROW nRowStart
, SCROW nRowEnd
) const
2015 if ( ValidCol(nCol
) )
2016 return aCol
[nCol
].GetMaxNumberStringLen( nPrecision
, nRowStart
, nRowEnd
);
2021 void ScTable::UpdateSelectionFunction( ScFunctionData
& rData
,
2022 SCCOL nStartCol
, SCROW nStartRow
, SCCOL nEndCol
, SCROW nEndRow
,
2023 const ScMarkData
& rMark
)
2025 // Cursor neben einer Markierung nicht beruecksichtigen:
2026 //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!!
2027 BOOL bSingle
= ( rMark
.IsMarked() || !rMark
.IsMultiMarked() );
2029 // Mehrfachselektion:
2032 if ( rMark
.IsMultiMarked() )
2033 for (nCol
=0; nCol
<=MAXCOL
&& !rData
.bError
; nCol
++)
2034 if ( !pColFlags
|| !( pColFlags
[nCol
] & CR_HIDDEN
) )
2035 aCol
[nCol
].UpdateSelectionFunction( rMark
, rData
, pRowFlags
,
2036 bSingle
&& ( nCol
>= nStartCol
&& nCol
<= nEndCol
),
2037 nStartRow
, nEndRow
);
2039 // Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.):
2041 if ( bSingle
&& !rMark
.IsMarkNegative() )
2042 for (nCol
=nStartCol
; nCol
<=nEndCol
&& !rData
.bError
; nCol
++)
2043 if ( !pColFlags
|| !( pColFlags
[nCol
] & CR_HIDDEN
) )
2044 aCol
[nCol
].UpdateAreaFunction( rData
, pRowFlags
, nStartRow
, nEndRow
);
2047 void ScTable::FindConditionalFormat( ULONG nKey
, ScRangeList
& rList
)
2049 SCROW nStartRow
= 0, nEndRow
= 0;
2050 for (SCCOL nCol
=0; nCol
<=MAXCOL
; nCol
++)
2052 ScAttrIterator
* pIter
= aCol
[nCol
].CreateAttrIterator( 0, MAXROW
);
2053 const ScPatternAttr
* pPattern
= pIter
->Next( nStartRow
, nEndRow
);
2056 if (((SfxUInt32Item
&)pPattern
->GetItem(ATTR_CONDITIONAL
)).GetValue() == nKey
)
2057 rList
.Join( ScRange(nCol
,nStartRow
,nTab
, nCol
,nEndRow
,nTab
) );
2058 pPattern
= pIter
->Next( nStartRow
, nEndRow
);