Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <rtl/math.hxx>
22 : #include <unotools/textsearch.hxx>
23 : #include <svl/zforlist.hxx>
24 : #include <svl/zformat.hxx>
25 : #include <unotools/charclass.hxx>
26 : #include <unotools/collatorwrapper.hxx>
27 : #include <com/sun/star/i18n/CollatorOptions.hpp>
28 : #include <stdlib.h>
29 : #include <unotools/transliterationwrapper.hxx>
30 :
31 : #include "table.hxx"
32 : #include "scitems.hxx"
33 : #include "attrib.hxx"
34 : #include "formulacell.hxx"
35 : #include "document.hxx"
36 : #include "globstr.hrc"
37 : #include "global.hxx"
38 : #include "stlpool.hxx"
39 : #include "compiler.hxx"
40 : #include "patattr.hxx"
41 : #include "subtotal.hxx"
42 : #include "docoptio.hxx"
43 : #include "markdata.hxx"
44 : #include "rangelst.hxx"
45 : #include "attarray.hxx"
46 : #include "userlist.hxx"
47 : #include "progress.hxx"
48 : #include "cellform.hxx"
49 : #include "postit.hxx"
50 : #include "queryparam.hxx"
51 : #include "queryentry.hxx"
52 : #include "segmenttree.hxx"
53 : #include "subtotalparam.hxx"
54 : #include "docpool.hxx"
55 : #include "cellvalue.hxx"
56 : #include "tokenarray.hxx"
57 : #include "mtvcellfunc.hxx"
58 : #include "columnspanset.hxx"
59 :
60 : #include "svl/sharedstringpool.hxx"
61 :
62 : #include <vector>
63 : #include <boost/scoped_array.hpp>
64 : #include <boost/unordered_set.hpp>
65 :
66 : using namespace ::com::sun::star;
67 :
68 : namespace naturalsort {
69 :
70 : using namespace ::com::sun::star::i18n;
71 :
72 : /** Splits a given string into three parts: the prefix, number string, and
73 : the suffix.
74 :
75 : @param sWhole
76 : Original string to be split into pieces
77 :
78 : @param sPrefix
79 : Prefix string that consists of the part before the first number token
80 :
81 : @param sSuffix
82 : String after the last number token. This may still contain number strings.
83 :
84 : @param fNum
85 : Number converted from the middle number string
86 :
87 : @return Returns TRUE if a numeral element is found in a given string, or
88 : FALSE if no numeral element is found.
89 : */
90 0 : bool SplitString( const OUString &sWhole,
91 : OUString &sPrefix, OUString &sSuffix, double &fNum )
92 : {
93 0 : i18n::LocaleDataItem aLocaleItem = ScGlobal::pLocaleData->getLocaleItem();
94 :
95 : // Get prefix element
96 0 : OUString sEmpty, sUser = "-";
97 : ParseResult aPRPre = ScGlobal::pCharClass->parsePredefinedToken(
98 : KParseType::IDENTNAME, sWhole, 0,
99 0 : KParseTokens::ANY_LETTER, sUser, KParseTokens::ANY_LETTER, sUser );
100 0 : sPrefix = sWhole.copy( 0, aPRPre.EndPos );
101 :
102 : // Return FALSE if no numeral element is found
103 0 : if ( aPRPre.EndPos == sWhole.getLength() )
104 0 : return false;
105 :
106 : // Get numeral element
107 0 : sUser = aLocaleItem.decimalSeparator;
108 : ParseResult aPRNum = ScGlobal::pCharClass->parsePredefinedToken(
109 : KParseType::ANY_NUMBER, sWhole, aPRPre.EndPos,
110 0 : KParseTokens::ANY_NUMBER, sEmpty, KParseTokens::ANY_NUMBER, sUser );
111 :
112 0 : if ( aPRNum.EndPos == aPRPre.EndPos )
113 0 : return false;
114 :
115 0 : fNum = aPRNum.Value;
116 0 : sSuffix = sWhole.copy( aPRNum.EndPos );
117 :
118 0 : return true;
119 : }
120 :
121 : /** Naturally compares two given strings.
122 :
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.
125 :
126 : @param sInput1
127 : Input string 1
128 :
129 : @param sInput2
130 : Input string 2
131 :
132 : @param bCaseSens
133 : Boolean value for case sensitivity
134 :
135 : @param pData
136 : Pointer to user defined sort list
137 :
138 : @param pCW
139 : Pointer to collator wrapper for normal string comparison
140 :
141 : @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
142 : sInput2 is greater.
143 : */
144 0 : short Compare( const OUString &sInput1, const OUString &sInput2,
145 : const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
146 : {
147 0 : OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
148 :
149 : do
150 : {
151 : double nNum1, nNum2;
152 0 : bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
153 0 : bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
154 :
155 : short nPreRes; // Prefix comparison result
156 0 : if ( pData )
157 : {
158 0 : if ( bCaseSens )
159 : {
160 0 : if ( !bNumFound1 || !bNumFound2 )
161 0 : return static_cast<short>(pData->Compare( sStr1, sStr2 ));
162 : else
163 0 : nPreRes = pData->Compare( sPre1, sPre2 );
164 : }
165 : else
166 : {
167 0 : if ( !bNumFound1 || !bNumFound2 )
168 0 : return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
169 : else
170 0 : nPreRes = pData->ICompare( sPre1, sPre2 );
171 : }
172 : }
173 : else
174 : {
175 0 : if ( !bNumFound1 || !bNumFound2 )
176 0 : return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
177 : else
178 0 : nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
179 : }
180 :
181 : // Prefix strings differ. Return immediately.
182 0 : if ( nPreRes != 0 ) return nPreRes;
183 :
184 0 : if ( nNum1 != nNum2 )
185 : {
186 0 : if ( nNum1 < nNum2 ) return -1;
187 0 : return static_cast<short>( nNum1 > nNum2 );
188 : }
189 :
190 : // The prefix and the first numerical elements are equal, but the suffix
191 : // strings may still differ. Stay in the loop.
192 :
193 0 : sStr1 = sSuf1;
194 0 : sStr2 = sSuf2;
195 :
196 : } while (true);
197 :
198 0 : return 0;
199 : }
200 :
201 : }
202 :
203 : // STATIC DATA -----------------------------------------------------------
204 :
205 0 : struct ScSortInfo
206 : {
207 : ScRefCellValue maCell;
208 : SCCOLROW nOrg;
209 0 : DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo );
210 : };
211 0 : IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo )
212 :
213 : // END OF STATIC DATA -----------------------------------------------------
214 :
215 :
216 : class ScSortInfoArray
217 : {
218 : private:
219 : ScSortInfo*** pppInfo;
220 : SCSIZE nCount;
221 : SCCOLROW nStart;
222 : sal_uInt16 nUsedSorts;
223 :
224 : public:
225 0 : ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
226 0 : pppInfo( new ScSortInfo**[nSorts]),
227 0 : nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
228 0 : nUsedSorts( nSorts )
229 : {
230 0 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
231 : {
232 0 : ScSortInfo** ppInfo = new ScSortInfo* [nCount];
233 0 : for ( SCSIZE j = 0; j < nCount; j++ )
234 0 : ppInfo[j] = new ScSortInfo;
235 0 : pppInfo[nSort] = ppInfo;
236 : }
237 0 : }
238 0 : ~ScSortInfoArray()
239 : {
240 0 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
241 : {
242 0 : ScSortInfo** ppInfo = pppInfo[nSort];
243 0 : for ( SCSIZE j = 0; j < nCount; j++ )
244 0 : delete ppInfo[j];
245 0 : delete [] ppInfo;
246 : }
247 0 : delete[] pppInfo;
248 0 : }
249 0 : ScSortInfo* Get( sal_uInt16 nSort, SCCOLROW nInd )
250 0 : { return (pppInfo[nSort])[ nInd - nStart ]; }
251 0 : void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
252 : {
253 0 : SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
254 0 : SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
255 0 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
256 : {
257 0 : ScSortInfo** ppInfo = pppInfo[nSort];
258 0 : ScSortInfo* pTmp = ppInfo[n1];
259 0 : ppInfo[n1] = ppInfo[n2];
260 0 : ppInfo[n2] = pTmp;
261 : }
262 0 : }
263 0 : sal_uInt16 GetUsedSorts() const { return nUsedSorts; }
264 0 : ScSortInfo** GetFirstArray() const { return pppInfo[0]; }
265 0 : SCCOLROW GetStart() const { return nStart; }
266 0 : SCSIZE GetCount() const { return nCount; }
267 : };
268 :
269 0 : ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 )
270 : {
271 0 : sal_uInt16 nUsedSorts = 1;
272 0 : while ( nUsedSorts < aSortParam.GetSortKeyCount() && aSortParam.maKeyState[nUsedSorts].bDoSort )
273 0 : nUsedSorts++;
274 0 : ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 );
275 0 : if ( aSortParam.bByRow )
276 : {
277 0 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
278 : {
279 0 : SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
280 0 : ScColumn* pCol = &aCol[nCol];
281 0 : for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
282 : {
283 0 : ScSortInfo* pInfo = pArray->Get( nSort, nRow );
284 0 : pInfo->maCell = pCol->GetCellValue(nRow);
285 0 : pInfo->nOrg = nRow;
286 : }
287 : }
288 : }
289 : else
290 : {
291 0 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
292 : {
293 0 : SCROW nRow = aSortParam.maKeyState[nSort].nField;
294 0 : for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
295 0 : nCol <= static_cast<SCCOL>(nInd2); nCol++ )
296 : {
297 0 : ScSortInfo* pInfo = pArray->Get( nSort, nCol );
298 0 : pInfo->maCell = GetCellValue(nCol, nRow);
299 0 : pInfo->nOrg = nCol;
300 : }
301 : }
302 : }
303 0 : return pArray;
304 : }
305 :
306 :
307 0 : bool ScTable::IsSortCollatorGlobal() const
308 : {
309 0 : return pSortCollator == ScGlobal::GetCollator() ||
310 0 : pSortCollator == ScGlobal::GetCaseCollator();
311 : }
312 :
313 :
314 0 : void ScTable::InitSortCollator( const ScSortParam& rPar )
315 : {
316 0 : if ( !rPar.aCollatorLocale.Language.isEmpty() )
317 : {
318 0 : if ( !pSortCollator || IsSortCollatorGlobal() )
319 0 : pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
320 : pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
321 0 : rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
322 : }
323 : else
324 : { // SYSTEM
325 0 : DestroySortCollator();
326 : pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() :
327 0 : ScGlobal::GetCollator());
328 : }
329 0 : }
330 :
331 :
332 0 : void ScTable::DestroySortCollator()
333 : {
334 0 : if ( pSortCollator )
335 : {
336 0 : if ( !IsSortCollatorGlobal() )
337 0 : delete pSortCollator;
338 0 : pSortCollator = NULL;
339 : }
340 0 : }
341 :
342 :
343 0 : void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
344 : {
345 0 : bool bByRow = aSortParam.bByRow;
346 0 : SCSIZE nCount = pArray->GetCount();
347 0 : SCCOLROW nStart = pArray->GetStart();
348 0 : ScSortInfo** ppInfo = pArray->GetFirstArray();
349 0 : ::std::vector<ScSortInfo*> aTable(nCount);
350 : SCSIZE nPos;
351 0 : for ( nPos = 0; nPos < nCount; nPos++ )
352 0 : aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
353 :
354 0 : SCCOLROW nDest = nStart;
355 0 : for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
356 : {
357 0 : SCCOLROW nOrg = ppInfo[nPos]->nOrg;
358 0 : if ( nDest != nOrg )
359 : {
360 0 : if ( bByRow )
361 0 : SwapRow( nDest, nOrg );
362 : else
363 0 : SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
364 : // neue Position des weggeswapten eintragen
365 0 : ScSortInfo* p = ppInfo[nPos];
366 0 : p->nOrg = nDest;
367 0 : ::std::swap(p, aTable[nDest-nStart]);
368 0 : p->nOrg = nOrg;
369 0 : ::std::swap(p, aTable[nOrg-nStart]);
370 : OSL_ENSURE( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" );
371 : }
372 0 : if(pProgress)
373 0 : pProgress->SetStateOnPercent( nPos );
374 0 : }
375 0 : }
376 :
377 0 : short ScTable::CompareCell(
378 : sal_uInt16 nSort,
379 : ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
380 : ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
381 : {
382 0 : short nRes = 0;
383 :
384 0 : CellType eType1 = rCell1.meType, eType2 = rCell2.meType;
385 :
386 0 : if (!rCell1.isEmpty())
387 : {
388 0 : if (!rCell2.isEmpty())
389 : {
390 0 : bool bStr1 = ( eType1 != CELLTYPE_VALUE );
391 0 : if (eType1 == CELLTYPE_FORMULA && rCell1.mpFormula->IsValue())
392 0 : bStr1 = false;
393 0 : bool bStr2 = ( eType2 != CELLTYPE_VALUE );
394 0 : if (eType2 == CELLTYPE_FORMULA && rCell2.mpFormula->IsValue())
395 0 : bStr2 = false;
396 :
397 0 : if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen!
398 : {
399 0 : OUString aStr1;
400 0 : OUString aStr2;
401 0 : if (eType1 == CELLTYPE_STRING)
402 0 : aStr1 = rCell1.mpString->getString();
403 : else
404 0 : GetString(nCell1Col, nCell1Row, aStr1);
405 0 : if (eType2 == CELLTYPE_STRING)
406 0 : aStr2 = rCell2.mpString->getString();
407 : else
408 0 : GetString(nCell2Col, nCell2Row, aStr2);
409 :
410 0 : bool bUserDef = aSortParam.bUserDef; // custom sort order
411 0 : bool bNaturalSort = aSortParam.bNaturalSort; // natural sort
412 0 : bool bCaseSens = aSortParam.bCaseSens; // case sensitivity
413 :
414 0 : if (bUserDef)
415 : {
416 0 : ScUserList* pList = ScGlobal::GetUserList();
417 0 : const ScUserListData* pData = (*pList)[aSortParam.nUserIndex];
418 :
419 0 : if (pData)
420 : {
421 0 : if ( bNaturalSort )
422 0 : nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, pData, pSortCollator );
423 : else
424 : {
425 0 : if ( bCaseSens )
426 0 : nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) );
427 : else
428 0 : nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) );
429 : }
430 : }
431 : else
432 0 : bUserDef = false;
433 :
434 : }
435 0 : if (!bUserDef)
436 : {
437 0 : if ( bNaturalSort )
438 0 : nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, NULL, pSortCollator );
439 : else
440 0 : nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
441 0 : }
442 : }
443 0 : else if ( bStr1 ) // String <-> Zahl
444 0 : nRes = 1; // Zahl vorne
445 0 : else if ( bStr2 ) // Zahl <-> String
446 0 : nRes = -1; // Zahl vorne
447 : else // Zahlen untereinander
448 : {
449 0 : double nVal1 = rCell1.getValue();
450 0 : double nVal2 = rCell2.getValue();
451 0 : if (nVal1 < nVal2)
452 0 : nRes = -1;
453 0 : else if (nVal1 > nVal2)
454 0 : nRes = 1;
455 : }
456 0 : if ( !aSortParam.maKeyState[nSort].bAscending )
457 0 : nRes = -nRes;
458 : }
459 : else
460 0 : nRes = -1;
461 : }
462 : else
463 : {
464 0 : if (!rCell2.isEmpty())
465 0 : nRes = 1;
466 : else
467 0 : nRes = 0; // beide leer
468 : }
469 0 : return nRes;
470 : }
471 :
472 0 : short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
473 : {
474 : short nRes;
475 0 : sal_uInt16 nSort = 0;
476 0 : do
477 : {
478 0 : ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 );
479 0 : ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 );
480 0 : if ( aSortParam.bByRow )
481 : nRes = CompareCell( nSort,
482 0 : pInfo1->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo1->nOrg,
483 0 : pInfo2->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo2->nOrg );
484 : else
485 : nRes = CompareCell( nSort,
486 0 : pInfo1->maCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.maKeyState[nSort].nField,
487 0 : pInfo2->maCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.maKeyState[nSort].nField );
488 0 : } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
489 0 : if( nRes == 0 )
490 : {
491 0 : ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 );
492 0 : ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 );
493 0 : if( pInfo1->nOrg < pInfo2->nOrg )
494 0 : nRes = -1;
495 0 : else if( pInfo1->nOrg > pInfo2->nOrg )
496 0 : nRes = 1;
497 : }
498 0 : return nRes;
499 : }
500 :
501 0 : void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi )
502 : {
503 0 : if ((nHi - nLo) == 1)
504 : {
505 0 : if (Compare(pArray, nLo, nHi) > 0)
506 0 : pArray->Swap( nLo, nHi );
507 : }
508 : else
509 : {
510 0 : SCsCOLROW ni = nLo;
511 0 : SCsCOLROW nj = nHi;
512 0 : do
513 : {
514 0 : while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
515 0 : ni++;
516 0 : while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
517 0 : nj--;
518 0 : if (ni <= nj)
519 : {
520 0 : if (ni != nj)
521 0 : pArray->Swap( ni, nj );
522 0 : ni++;
523 0 : nj--;
524 : }
525 : } while (ni < nj);
526 0 : if ((nj - nLo) < (nHi - ni))
527 : {
528 0 : if (nLo < nj)
529 0 : QuickSort(pArray, nLo, nj);
530 0 : if (ni < nHi)
531 0 : QuickSort(pArray, ni, nHi);
532 : }
533 : else
534 : {
535 0 : if (ni < nHi)
536 0 : QuickSort(pArray, ni, nHi);
537 0 : if (nLo < nj)
538 0 : QuickSort(pArray, nLo, nj);
539 : }
540 : }
541 0 : }
542 :
543 0 : void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2)
544 : {
545 0 : SCROW nRowStart = aSortParam.nRow1;
546 0 : SCROW nRowEnd = aSortParam.nRow2;
547 0 : for (SCROW nRow = nRowStart; nRow <= nRowEnd; nRow++)
548 : {
549 0 : aCol[nCol1].SwapCell(nRow, aCol[nCol2]);
550 0 : if (aSortParam.bIncludePattern)
551 : {
552 0 : const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow);
553 0 : const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow);
554 0 : if (pPat1 != pPat2)
555 : {
556 0 : pDocument->GetPool()->Put(*pPat1);
557 0 : SetPattern(nCol1, nRow, *pPat2, true);
558 0 : SetPattern(nCol2, nRow, *pPat1, true);
559 0 : pDocument->GetPool()->Remove(*pPat1);
560 : }
561 : }
562 : }
563 0 : }
564 :
565 0 : void ScTable::SwapRow(SCROW nRow1, SCROW nRow2)
566 : {
567 0 : SCCOL nColStart = aSortParam.nCol1;
568 0 : SCCOL nColEnd = aSortParam.nCol2;
569 0 : for (SCCOL nCol = nColStart; nCol <= nColEnd; nCol++)
570 : {
571 0 : aCol[nCol].SwapRow(nRow1, nRow2);
572 0 : if (aSortParam.bIncludePattern)
573 : {
574 0 : const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1);
575 0 : const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2);
576 0 : if (pPat1 != pPat2)
577 : {
578 0 : pDocument->GetPool()->Put(*pPat1);
579 0 : SetPattern(nCol, nRow1, *pPat2, true);
580 0 : SetPattern(nCol, nRow2, *pPat1, true);
581 0 : pDocument->GetPool()->Remove(*pPat1);
582 : }
583 : }
584 : }
585 0 : if (bGlobalKeepQuery)
586 : {
587 0 : bool bRow1Hidden = RowHidden(nRow1);
588 0 : bool bRow2Hidden = RowHidden(nRow2);
589 0 : SetRowHidden(nRow1, nRow1, bRow2Hidden);
590 0 : SetRowHidden(nRow2, nRow2, bRow1Hidden);
591 :
592 0 : bool bRow1Filtered = RowFiltered(nRow1);
593 0 : bool bRow2Filtered = RowFiltered(nRow2);
594 0 : SetRowFiltered(nRow1, nRow1, bRow2Filtered);
595 0 : SetRowFiltered(nRow2, nRow2, bRow1Filtered);
596 : }
597 0 : }
598 :
599 0 : short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
600 : {
601 : short nRes;
602 0 : sal_uInt16 nSort = 0;
603 0 : const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
604 0 : if (aSortParam.bByRow)
605 : {
606 0 : do
607 : {
608 0 : SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
609 0 : ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
610 0 : ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
611 0 : nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
612 0 : } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
613 : }
614 : else
615 : {
616 0 : do
617 : {
618 0 : SCROW nRow = aSortParam.maKeyState[nSort].nField;
619 0 : ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow);
620 0 : ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow);
621 : nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
622 0 : nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
623 0 : } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
624 : }
625 0 : return nRes;
626 : }
627 :
628 0 : bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // ueber aSortParam
629 : {
630 0 : for (SCCOLROW i=nStart; i<nEnd; i++)
631 : {
632 0 : if (Compare( i, i+1 ) > 0)
633 0 : return false;
634 : }
635 0 : return true;
636 : }
637 :
638 0 : void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
639 : {
640 : SCROW nRow;
641 0 : SCROW nMax = nRow2 - nRow1;
642 0 : for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
643 : {
644 0 : nRow = rand() % nMax;
645 0 : pArray->Swap(i, nRow1 + nRow);
646 : }
647 0 : }
648 :
649 0 : void ScTable::Sort(const ScSortParam& rSortParam, bool bKeepQuery, ScProgress* pProgress)
650 : {
651 0 : aSortParam = rSortParam;
652 0 : InitSortCollator( rSortParam );
653 0 : bGlobalKeepQuery = bKeepQuery;
654 0 : if (rSortParam.bByRow)
655 : {
656 0 : SCROW nLastRow = 0;
657 0 : for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++)
658 0 : nLastRow = std::max(nLastRow, aCol[nCol].GetLastDataPos());
659 0 : nLastRow = std::min(nLastRow, aSortParam.nRow2);
660 : SCROW nRow1 = (rSortParam.bHasHeader ?
661 0 : aSortParam.nRow1 + 1 : aSortParam.nRow1);
662 0 : if (!IsSorted(nRow1, nLastRow))
663 : {
664 0 : if(pProgress)
665 0 : pProgress->SetState( 0, nLastRow-nRow1 );
666 0 : ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, nLastRow );
667 0 : if ( nLastRow - nRow1 > 255 )
668 0 : DecoladeRow( pArray, nRow1, nLastRow );
669 0 : QuickSort( pArray, nRow1, nLastRow );
670 0 : SortReorder( pArray, pProgress );
671 0 : delete pArray;
672 : // #i59745# update position of caption objects of cell notes --> reported at (SortReorder) ScColumn::SwapCellNotes level
673 : }
674 : }
675 : else
676 : {
677 : SCCOL nLastCol;
678 0 : for (nLastCol = aSortParam.nCol2;
679 0 : (nLastCol > aSortParam.nCol1) && aCol[nLastCol].IsEmptyBlock(aSortParam.nRow1, aSortParam.nRow2); nLastCol--)
680 : {
681 : }
682 : SCCOL nCol1 = (rSortParam.bHasHeader ?
683 0 : aSortParam.nCol1 + 1 : aSortParam.nCol1);
684 0 : if (!IsSorted(nCol1, nLastCol))
685 : {
686 0 : if(pProgress)
687 0 : pProgress->SetState( 0, nLastCol-nCol1 );
688 0 : ScSortInfoArray* pArray = CreateSortInfoArray( nCol1, nLastCol );
689 0 : QuickSort( pArray, nCol1, nLastCol );
690 0 : SortReorder( pArray, pProgress );
691 0 : delete pArray;
692 : // #i59745# update position of caption objects of cell notes --> reported at (SortReorder) ScColumn::SwapCellNotes level
693 : }
694 : }
695 0 : DestroySortCollator();
696 0 : }
697 :
698 : namespace {
699 :
700 : class SubTotalRowFinder
701 : {
702 : const ScTable& mrTab;
703 : const ScSubTotalParam& mrParam;
704 :
705 : public:
706 0 : SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
707 0 : mrTab(rTab), mrParam(rParam) {}
708 :
709 0 : bool operator() (size_t nRow, const ScFormulaCell* pCell)
710 : {
711 0 : if (!pCell->IsSubTotal())
712 0 : return false;
713 :
714 0 : SCCOL nStartCol = mrParam.nCol1;
715 0 : SCCOL nEndCol = mrParam.nCol2;
716 :
717 0 : for (SCCOL i = 0; i <= MAXCOL; ++i)
718 : {
719 0 : if (nStartCol <= i && i <= nEndCol)
720 0 : continue;
721 :
722 0 : if (mrTab.HasData(i, nRow))
723 0 : return true;
724 : }
725 :
726 0 : return false;
727 : }
728 : };
729 :
730 : }
731 :
732 0 : bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
733 : {
734 0 : SCCOL nStartCol = rParam.nCol1;
735 0 : SCROW nStartRow = rParam.nRow1 + 1; // Header
736 0 : SCCOL nEndCol = rParam.nCol2;
737 0 : SCROW nEndRow = rParam.nRow2;
738 :
739 0 : for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
740 : {
741 0 : const sc::CellStoreType& rCells = aCol[nCol].maCells;
742 0 : SubTotalRowFinder aFunc(*this, rParam);
743 : std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
744 0 : sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
745 0 : if (aPos.first != rCells.end())
746 0 : return true;
747 : }
748 0 : return false;
749 : }
750 :
751 : namespace {
752 :
753 0 : class RemoveSubTotalsHandler
754 : {
755 : std::vector<SCROW> maRemoved;
756 : public:
757 :
758 0 : void operator() (size_t nRow, const ScFormulaCell* p)
759 : {
760 0 : if (p->IsSubTotal())
761 0 : maRemoved.push_back(nRow);
762 0 : }
763 :
764 0 : void getRows(std::vector<SCROW>& rRows)
765 : {
766 : // Sort and remove duplicates.
767 0 : std::sort(maRemoved.begin(), maRemoved.end());
768 0 : std::vector<SCROW>::iterator it = std::unique(maRemoved.begin(), maRemoved.end());
769 0 : maRemoved.erase(it, maRemoved.end());
770 :
771 0 : maRemoved.swap(rRows);
772 0 : }
773 : };
774 :
775 : }
776 :
777 0 : void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
778 : {
779 0 : SCCOL nStartCol = rParam.nCol1;
780 0 : SCROW nStartRow = rParam.nRow1 + 1; // Header
781 0 : SCCOL nEndCol = rParam.nCol2;
782 0 : SCROW nEndRow = rParam.nRow2; // wird veraendert
783 :
784 0 : RemoveSubTotalsHandler aFunc;
785 0 : for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
786 : {
787 0 : const sc::CellStoreType& rCells = aCol[nCol].maCells;
788 0 : sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
789 : }
790 :
791 0 : std::vector<SCROW> aRows;
792 0 : aFunc.getRows(aRows);
793 :
794 0 : std::vector<SCROW>::reverse_iterator it = aRows.rbegin(), itEnd = aRows.rend();
795 0 : for (; it != itEnd; ++it)
796 : {
797 0 : SCROW nRow = *it;
798 0 : RemoveRowBreak(nRow+1, false, true);
799 0 : pDocument->DeleteRow(0, nTab, MAXCOL, nTab, nRow, 1);
800 : }
801 :
802 0 : rParam.nRow2 -= aRows.size();
803 0 : }
804 :
805 : // harte Zahlenformate loeschen (fuer Ergebnisformeln)
806 :
807 0 : static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
808 : {
809 0 : const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
810 0 : if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
811 : == SFX_ITEM_SET )
812 : {
813 0 : ScPatternAttr aNewPattern( *pPattern );
814 0 : SfxItemSet& rSet = aNewPattern.GetItemSet();
815 0 : rSet.ClearItem( ATTR_VALUE_FORMAT );
816 0 : rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
817 0 : pTab->SetPattern( nCol, nRow, aNewPattern, true );
818 : }
819 0 : }
820 :
821 :
822 : // at least MSC needs this at linkage level to be able to use it in a template
823 : typedef struct lcl_ScTable_DoSubTotals_RowEntry
824 : {
825 : sal_uInt16 nGroupNo;
826 : SCROW nSubStartRow;
827 : SCROW nDestRow;
828 : SCROW nFuncStart;
829 : SCROW nFuncEnd;
830 : } RowEntry;
831 :
832 : // neue Zwischenergebnisse
833 : // rParam.nRow2 wird veraendert !
834 :
835 0 : bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
836 : {
837 0 : SCCOL nStartCol = rParam.nCol1;
838 0 : SCROW nStartRow = rParam.nRow1 + 1; // Header
839 0 : SCCOL nEndCol = rParam.nCol2;
840 0 : SCROW nEndRow = rParam.nRow2; // wird veraendert
841 : sal_uInt16 i;
842 :
843 : // Leerzeilen am Ende weglassen,
844 : // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#)
845 : // Wenn sortiert wurde, sind alle Leerzeilen am Ende.
846 0 : SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
847 0 : nEndRow -= nEmpty;
848 :
849 0 : sal_uInt16 nLevelCount = 0; // Anzahl Gruppierungen
850 0 : bool bDoThis = true;
851 0 : for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
852 0 : if (rParam.bGroupActive[i])
853 0 : nLevelCount = i+1;
854 : else
855 0 : bDoThis = false;
856 :
857 0 : if (nLevelCount==0) // nichts tun
858 0 : return true;
859 :
860 0 : SCCOL* nGroupCol = rParam.nField; // Spalten nach denen
861 : // gruppiert wird
862 :
863 : // Durch (leer) als eigene Kategorie muss immer auf
864 : // Teilergebniszeilen aus den anderen Spalten getestet werden
865 : // (frueher nur, wenn eine Spalte mehrfach vorkam)
866 0 : bool bTestPrevSub = ( nLevelCount > 1 );
867 :
868 0 : OUString aSubString;
869 0 : OUString aOutString;
870 :
871 0 : bool bIgnoreCase = !rParam.bCaseSens;
872 :
873 : OUString *pCompString[MAXSUBTOTAL]; // Pointer wegen Compiler-Problemen
874 0 : for (i=0; i<MAXSUBTOTAL; i++)
875 0 : pCompString[i] = new OUString;
876 :
877 : //! sortieren?
878 :
879 0 : ScStyleSheet* pStyle = (ScStyleSheet*) pDocument->GetStyleSheetPool()->Find(
880 0 : ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA );
881 :
882 0 : bool bSpaceLeft = true; // Erfolg beim Einfuegen?
883 :
884 : // For performance reasons collect formula entries so their
885 : // references don't have to be tested for updates each time a new row is
886 : // inserted
887 : RowEntry aRowEntry;
888 0 : ::std::vector< RowEntry > aRowVector;
889 :
890 0 : for (sal_uInt16 nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++) // incl. Gesamtergebnis
891 : {
892 0 : bool bTotal = ( nLevel == nLevelCount );
893 0 : aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1);
894 :
895 : // how many results per level
896 0 : SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
897 : // result functions
898 0 : ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo];
899 :
900 0 : if (nResCount > 0) // sonst nur sortieren
901 : {
902 0 : for (i=0; i<=aRowEntry.nGroupNo; i++)
903 : {
904 0 : GetString( nGroupCol[i], nStartRow, aSubString );
905 0 : if ( bIgnoreCase )
906 0 : *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
907 : else
908 0 : *pCompString[i] = aSubString;
909 : } // aSubString bleibt auf dem letzten stehen
910 :
911 0 : bool bBlockVis = false; // Gruppe eingeblendet?
912 0 : aRowEntry.nSubStartRow = nStartRow;
913 0 : for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
914 : {
915 : bool bChanged;
916 0 : if (nRow>nEndRow)
917 0 : bChanged = true;
918 : else
919 : {
920 0 : bChanged = false;
921 0 : if (!bTotal)
922 : {
923 0 : OUString aString;
924 0 : for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
925 : {
926 0 : GetString( nGroupCol[i], nRow, aString );
927 0 : if (bIgnoreCase)
928 0 : aString = ScGlobal::pCharClass->uppercase(aString);
929 : // wenn sortiert, ist "leer" eine eigene Gruppe
930 : // sonst sind leere Zellen unten erlaubt
931 0 : bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
932 0 : aString != *pCompString[i] );
933 : }
934 0 : if ( bChanged && bTestPrevSub )
935 : {
936 : // No group change on rows that will contain subtotal formulas
937 0 : for ( ::std::vector< RowEntry >::const_iterator
938 0 : iEntry( aRowVector.begin());
939 0 : iEntry != aRowVector.end(); ++iEntry)
940 : {
941 0 : if ( iEntry->nDestRow == nRow )
942 : {
943 0 : bChanged = false;
944 0 : break;
945 : }
946 : }
947 0 : }
948 : }
949 : }
950 0 : if ( bChanged )
951 : {
952 0 : aRowEntry.nDestRow = nRow;
953 0 : aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
954 0 : aRowEntry.nFuncEnd = nRow-1;
955 :
956 : bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab,
957 0 : aRowEntry.nDestRow, 1 );
958 0 : DBShowRow( aRowEntry.nDestRow, bBlockVis );
959 0 : bBlockVis = false;
960 0 : if ( rParam.bPagebreak && nRow < MAXROW &&
961 0 : aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
962 0 : SetRowBreak(aRowEntry.nSubStartRow, false, true);
963 :
964 0 : if (bSpaceLeft)
965 : {
966 0 : for ( ::std::vector< RowEntry >::iterator iMove(
967 0 : aRowVector.begin() );
968 0 : iMove != aRowVector.end(); ++iMove)
969 : {
970 0 : if ( aRowEntry.nDestRow <= iMove->nSubStartRow )
971 0 : ++iMove->nSubStartRow;
972 0 : if ( aRowEntry.nDestRow <= iMove->nDestRow )
973 0 : ++iMove->nDestRow;
974 0 : if ( aRowEntry.nDestRow <= iMove->nFuncStart )
975 0 : ++iMove->nFuncStart;
976 0 : if ( aRowEntry.nDestRow <= iMove->nFuncEnd )
977 0 : ++iMove->nFuncEnd;
978 : }
979 : // collect formula positions
980 0 : aRowVector.push_back( aRowEntry );
981 :
982 0 : if (bTotal) // "Gesamtergebnis"
983 0 : aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS );
984 : else
985 : { // " Ergebnis"
986 0 : aOutString = aSubString;
987 0 : if (aOutString.isEmpty())
988 0 : aOutString = ScGlobal::GetRscString( STR_EMPTYDATA );
989 0 : aOutString += " ";
990 0 : sal_uInt16 nStrId = STR_TABLE_ERGEBNIS;
991 0 : if ( nResCount == 1 )
992 0 : switch ( eResFunc[0] )
993 : {
994 0 : case SUBTOTAL_FUNC_AVE: nStrId = STR_FUN_TEXT_AVG; break;
995 : case SUBTOTAL_FUNC_CNT:
996 0 : case SUBTOTAL_FUNC_CNT2: nStrId = STR_FUN_TEXT_COUNT; break;
997 0 : case SUBTOTAL_FUNC_MAX: nStrId = STR_FUN_TEXT_MAX; break;
998 0 : case SUBTOTAL_FUNC_MIN: nStrId = STR_FUN_TEXT_MIN; break;
999 0 : case SUBTOTAL_FUNC_PROD: nStrId = STR_FUN_TEXT_PRODUCT; break;
1000 : case SUBTOTAL_FUNC_STD:
1001 0 : case SUBTOTAL_FUNC_STDP: nStrId = STR_FUN_TEXT_STDDEV; break;
1002 0 : case SUBTOTAL_FUNC_SUM: nStrId = STR_FUN_TEXT_SUM; break;
1003 : case SUBTOTAL_FUNC_VAR:
1004 0 : case SUBTOTAL_FUNC_VARP: nStrId = STR_FUN_TEXT_VAR; break;
1005 : default:
1006 : {
1007 : // added to avoid warnings
1008 : }
1009 : }
1010 0 : aOutString += ScGlobal::GetRscString( nStrId );
1011 : }
1012 0 : SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
1013 0 : ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle );
1014 :
1015 0 : ++nRow;
1016 0 : ++nEndRow;
1017 0 : aRowEntry.nSubStartRow = nRow;
1018 0 : for (i=0; i<=aRowEntry.nGroupNo; i++)
1019 : {
1020 0 : GetString( nGroupCol[i], nRow, aSubString );
1021 0 : if ( bIgnoreCase )
1022 0 : *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
1023 : else
1024 0 : *pCompString[i] = aSubString;
1025 : }
1026 : }
1027 : }
1028 0 : bBlockVis = !RowFiltered(nRow);
1029 : }
1030 : }
1031 : }
1032 :
1033 : // now insert the formulas
1034 : ScComplexRefData aRef;
1035 0 : aRef.InitFlags();
1036 0 : aRef.Ref1.SetAbsTab(nTab);
1037 0 : aRef.Ref2.SetAbsTab(nTab);
1038 0 : for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin());
1039 0 : iEntry != aRowVector.end(); ++iEntry)
1040 : {
1041 0 : SCCOL nResCount = rParam.nSubTotals[iEntry->nGroupNo];
1042 0 : SCCOL* nResCols = rParam.pSubTotals[iEntry->nGroupNo];
1043 0 : ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo];
1044 0 : for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
1045 : {
1046 0 : aRef.Ref1.SetAbsCol(nResCols[nResult]);
1047 0 : aRef.Ref1.SetAbsRow(iEntry->nFuncStart);
1048 0 : aRef.Ref2.SetAbsCol(nResCols[nResult]);
1049 0 : aRef.Ref2.SetAbsRow(iEntry->nFuncEnd);
1050 :
1051 0 : ScTokenArray aArr;
1052 0 : aArr.AddOpCode( ocSubTotal );
1053 0 : aArr.AddOpCode( ocOpen );
1054 0 : aArr.AddDouble( (double) eResFunc[nResult] );
1055 0 : aArr.AddOpCode( ocSep );
1056 0 : aArr.AddDoubleReference( aRef );
1057 0 : aArr.AddOpCode( ocClose );
1058 0 : aArr.AddOpCode( ocStop );
1059 : ScFormulaCell* pCell = new ScFormulaCell(
1060 0 : pDocument, ScAddress(nResCols[nResult], iEntry->nDestRow, nTab), aArr);
1061 :
1062 0 : SetFormulaCell(nResCols[nResult], iEntry->nDestRow, pCell);
1063 :
1064 0 : if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] )
1065 : {
1066 0 : ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle );
1067 :
1068 : // Zahlformat loeschen
1069 0 : lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow );
1070 : }
1071 0 : }
1072 :
1073 : }
1074 :
1075 : //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ?
1076 :
1077 : //! Outlines direkt erzeugen?
1078 :
1079 0 : if (bSpaceLeft)
1080 0 : DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
1081 :
1082 0 : for (i=0; i<MAXSUBTOTAL; i++)
1083 0 : delete pCompString[i];
1084 :
1085 0 : rParam.nRow2 = nEndRow; // neues Ende
1086 0 : return bSpaceLeft;
1087 : }
1088 :
1089 0 : void ScTable::MarkSubTotalCells(
1090 : sc::ColumnSpanSet& rSet, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal ) const
1091 : {
1092 0 : if (!ValidCol(nCol1) || !ValidCol(nCol2))
1093 0 : return;
1094 :
1095 : // Pick up all subtotal formula cells.
1096 0 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1097 0 : aCol[nCol].MarkSubTotalCells(rSet, nRow1, nRow2, bVal);
1098 :
1099 : // Pick up all filtered rows.
1100 : ScFlatBoolRowSegments::RangeData aFilteredSpan;
1101 0 : SCROW nRow = nRow1;
1102 0 : while (nRow <= nRow2)
1103 : {
1104 0 : if (!mpFilteredRows->getRangeData(nRow, aFilteredSpan))
1105 : // Failed for whatever reason.
1106 0 : return;
1107 :
1108 0 : if (aFilteredSpan.mbValue)
1109 : {
1110 : // Filtered span found.
1111 0 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1112 0 : rSet.set(nTab, nCol, nRow, aFilteredSpan.mnRow2, bVal);
1113 : }
1114 :
1115 0 : nRow = aFilteredSpan.mnRow2 + 1;
1116 : }
1117 : }
1118 :
1119 : namespace {
1120 :
1121 : class QueryEvaluator
1122 : {
1123 : ScDocument& mrDoc;
1124 : svl::SharedStringPool& mrStrPool;
1125 : const ScTable& mrTab;
1126 : const ScQueryParam& mrParam;
1127 : const bool* mpTestEqualCondition;
1128 : utl::TransliterationWrapper* mpTransliteration;
1129 : CollatorWrapper* mpCollator;
1130 : const bool mbMatchWholeCell;
1131 :
1132 0 : bool isPartialTextMatchOp(const ScQueryEntry& rEntry) const
1133 : {
1134 0 : switch (rEntry.eOp)
1135 : {
1136 : // these operators can only be used with textural comparisons.
1137 : case SC_CONTAINS:
1138 : case SC_DOES_NOT_CONTAIN:
1139 : case SC_BEGINS_WITH:
1140 : case SC_ENDS_WITH:
1141 : case SC_DOES_NOT_BEGIN_WITH:
1142 : case SC_DOES_NOT_END_WITH:
1143 0 : return true;
1144 : default:
1145 : ;
1146 : }
1147 0 : return false;
1148 : }
1149 :
1150 0 : bool isTextMatchOp(const ScQueryEntry& rEntry) const
1151 : {
1152 0 : if (isPartialTextMatchOp(rEntry))
1153 0 : return true;
1154 :
1155 0 : switch (rEntry.eOp)
1156 : {
1157 : // these operators can be used for either textural or value comparison.
1158 : case SC_EQUAL:
1159 : case SC_NOT_EQUAL:
1160 0 : return true;
1161 : default:
1162 : ;
1163 : }
1164 0 : return false;
1165 : }
1166 :
1167 0 : bool isRealRegExp(const ScQueryEntry& rEntry) const
1168 : {
1169 0 : if (!mrParam.bRegExp)
1170 0 : return false;
1171 :
1172 0 : return isTextMatchOp(rEntry);
1173 : }
1174 :
1175 0 : bool isTestRegExp(const ScQueryEntry& rEntry) const
1176 : {
1177 0 : if (!mpTestEqualCondition)
1178 0 : return false;
1179 :
1180 0 : if (!mrParam.bRegExp)
1181 0 : return false;
1182 :
1183 0 : return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
1184 : }
1185 :
1186 : public:
1187 0 : QueryEvaluator(ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
1188 : const bool* pTestEqualCondition) :
1189 : mrDoc(rDoc),
1190 0 : mrStrPool(rDoc.GetSharedStringPool()),
1191 : mrTab(rTab),
1192 : mrParam(rParam),
1193 : mpTestEqualCondition(pTestEqualCondition),
1194 0 : mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell())
1195 : {
1196 0 : if (rParam.bCaseSens)
1197 : {
1198 0 : mpTransliteration = ScGlobal::GetCaseTransliteration();
1199 0 : mpCollator = ScGlobal::GetCaseCollator();
1200 : }
1201 : else
1202 : {
1203 0 : mpTransliteration = ScGlobal::GetpTransliteration();
1204 0 : mpCollator = ScGlobal::GetCollator();
1205 : }
1206 0 : }
1207 :
1208 0 : bool isQueryByValue(
1209 : const ScQueryEntry::Item& rItem, SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
1210 : {
1211 0 : if (rItem.meType == ScQueryEntry::ByString)
1212 0 : return false;
1213 :
1214 0 : if (!rCell.isEmpty())
1215 : {
1216 0 : if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
1217 : // Error values are compared as string.
1218 0 : return false;
1219 :
1220 0 : return rCell.hasNumeric();
1221 : }
1222 :
1223 0 : return mrTab.HasValueData(nCol, nRow);
1224 : }
1225 :
1226 0 : bool isQueryByString(
1227 : const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
1228 : SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
1229 : {
1230 0 : if (isTextMatchOp(rEntry))
1231 0 : return true;
1232 :
1233 0 : if (rItem.meType != ScQueryEntry::ByString)
1234 0 : return false;
1235 :
1236 0 : if (!rCell.isEmpty())
1237 0 : return rCell.hasString();
1238 :
1239 0 : return mrTab.HasStringData(nCol, nRow);
1240 : }
1241 :
1242 0 : std::pair<bool,bool> compareByValue(
1243 : const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
1244 : const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
1245 : {
1246 0 : bool bOk = false;
1247 0 : bool bTestEqual = false;
1248 : double nCellVal;
1249 0 : if (!rCell.isEmpty())
1250 : {
1251 0 : switch (rCell.meType)
1252 : {
1253 : case CELLTYPE_VALUE :
1254 0 : nCellVal = rCell.mfValue;
1255 0 : break;
1256 : case CELLTYPE_FORMULA :
1257 0 : nCellVal = rCell.mpFormula->GetValue();
1258 0 : break;
1259 : default:
1260 0 : nCellVal = 0.0;
1261 : }
1262 :
1263 : }
1264 : else
1265 0 : nCellVal = mrTab.GetValue(nCol, nRow);
1266 :
1267 : /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
1268 : * date+time format was queried rEntry.bQueryByDate is not set. In
1269 : * case other queries wanted to use this mechanism they should do
1270 : * the same, in other words only if rEntry.nVal is an integer value
1271 : * rEntry.bQueryByDate should be true and the time fraction be
1272 : * stripped here. */
1273 0 : if (rItem.meType == ScQueryEntry::ByDate)
1274 : {
1275 0 : sal_uInt32 nNumFmt = mrTab.GetNumberFormat(nCol, nRow);
1276 0 : const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nNumFmt);
1277 0 : if (pEntry)
1278 : {
1279 0 : short nNumFmtType = pEntry->GetType();
1280 : /* NOTE: Omitting the check for absence of
1281 : * NUMBERFORMAT_TIME would include also date+time formatted
1282 : * values of the same day. That may be desired in some
1283 : * cases, querying all time values of a day, but confusing
1284 : * in other cases. A user can always setup a standard
1285 : * filter query for x >= date AND x < date+1 */
1286 0 : if ((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME))
1287 : {
1288 : // The format is of date type. Strip off the time
1289 : // element.
1290 0 : nCellVal = ::rtl::math::approxFloor(nCellVal);
1291 : }
1292 : }
1293 : }
1294 :
1295 0 : switch (rEntry.eOp)
1296 : {
1297 : case SC_EQUAL :
1298 0 : bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1299 0 : break;
1300 : case SC_LESS :
1301 0 : bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1302 0 : break;
1303 : case SC_GREATER :
1304 0 : bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1305 0 : break;
1306 : case SC_LESS_EQUAL :
1307 0 : bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1308 0 : if ( bOk && mpTestEqualCondition )
1309 0 : bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1310 0 : break;
1311 : case SC_GREATER_EQUAL :
1312 0 : bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual( nCellVal, rItem.mfVal);
1313 0 : if ( bOk && mpTestEqualCondition )
1314 0 : bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1315 0 : break;
1316 : case SC_NOT_EQUAL :
1317 0 : bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
1318 0 : break;
1319 : default:
1320 : {
1321 : // added to avoid warnings
1322 : }
1323 : }
1324 :
1325 0 : return std::pair<bool,bool>(bOk, bTestEqual);
1326 : }
1327 :
1328 0 : std::pair<bool,bool> compareByString(
1329 : ScRefCellValue& rCell, SCROW nRow, const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
1330 : {
1331 0 : bool bOk = false;
1332 0 : bool bTestEqual = false;
1333 0 : bool bMatchWholeCell = mbMatchWholeCell;
1334 0 : svl::SharedString aCellStr;
1335 0 : if (isPartialTextMatchOp(rEntry))
1336 : // may have to do partial textural comparison.
1337 0 : bMatchWholeCell = false;
1338 :
1339 0 : if (!rCell.isEmpty())
1340 : {
1341 0 : if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
1342 : {
1343 : // Error cell is evaluated as string (for now).
1344 0 : aCellStr = mrStrPool.intern(ScGlobal::GetErrorString(rCell.mpFormula->GetErrCode()));
1345 : }
1346 0 : else if (rCell.meType == CELLTYPE_STRING)
1347 0 : aCellStr = *rCell.mpString;
1348 : else
1349 : {
1350 0 : sal_uLong nFormat = mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
1351 0 : OUString aStr;
1352 0 : ScCellFormat::GetInputString(rCell, nFormat, aStr, *mrDoc.GetFormatTable(), &mrDoc);
1353 0 : aCellStr = mrStrPool.intern(aStr);
1354 : }
1355 : }
1356 : else
1357 : {
1358 0 : OUString aStr;
1359 0 : mrTab.GetInputString(static_cast<SCCOL>(rEntry.nField), nRow, aStr);
1360 0 : aCellStr = mrStrPool.intern(aStr);
1361 : }
1362 :
1363 0 : bool bRealRegExp = isRealRegExp(rEntry);
1364 0 : bool bTestRegExp = isTestRegExp(rEntry);
1365 :
1366 0 : if ( bRealRegExp || bTestRegExp )
1367 : {
1368 0 : sal_Int32 nStart = 0;
1369 0 : sal_Int32 nEnd = aCellStr.getLength();
1370 :
1371 : // from 614 on, nEnd is behind the found text
1372 0 : bool bMatch = false;
1373 0 : if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
1374 : {
1375 0 : nEnd = 0;
1376 0 : nStart = aCellStr.getLength();
1377 : bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
1378 0 : ->SearchBackward(aCellStr.getString(), &nStart, &nEnd);
1379 : }
1380 : else
1381 : {
1382 : bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
1383 0 : ->SearchForward(aCellStr.getString(), &nStart, &nEnd);
1384 : }
1385 0 : if ( bMatch && bMatchWholeCell
1386 0 : && (nStart != 0 || nEnd != aCellStr.getLength()) )
1387 0 : bMatch = false; // RegExp must match entire cell string
1388 0 : if ( bRealRegExp )
1389 0 : switch (rEntry.eOp)
1390 : {
1391 : case SC_EQUAL:
1392 : case SC_CONTAINS:
1393 0 : bOk = bMatch;
1394 0 : break;
1395 : case SC_NOT_EQUAL:
1396 : case SC_DOES_NOT_CONTAIN:
1397 0 : bOk = !bMatch;
1398 0 : break;
1399 : case SC_BEGINS_WITH:
1400 0 : bOk = ( bMatch && (nStart == 0) );
1401 0 : break;
1402 : case SC_DOES_NOT_BEGIN_WITH:
1403 0 : bOk = !( bMatch && (nStart == 0) );
1404 0 : break;
1405 : case SC_ENDS_WITH:
1406 0 : bOk = ( bMatch && (nEnd == aCellStr.getLength()) );
1407 0 : break;
1408 : case SC_DOES_NOT_END_WITH:
1409 0 : bOk = !( bMatch && (nEnd == aCellStr.getLength()) );
1410 0 : break;
1411 : default:
1412 : {
1413 : // added to avoid warnings
1414 : }
1415 : }
1416 : else
1417 0 : bTestEqual = bMatch;
1418 : }
1419 0 : if ( !bRealRegExp )
1420 : {
1421 : // Simple string matching i.e. no regexp match.
1422 0 : if (isTextMatchOp(rEntry))
1423 : {
1424 0 : if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
1425 : {
1426 : // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
1427 : // the query value is assigned directly, and the string is empty. In that case,
1428 : // don't find any string (isEqual would find empty string results in formula cells).
1429 0 : bOk = false;
1430 0 : if ( rEntry.eOp == SC_NOT_EQUAL )
1431 0 : bOk = !bOk;
1432 : }
1433 0 : else if ( bMatchWholeCell )
1434 : {
1435 : // Fast string equality check by comparing string identifiers.
1436 0 : if (mrParam.bCaseSens)
1437 0 : bOk = aCellStr.getData() == rItem.maString.getData();
1438 : else
1439 0 : bOk = aCellStr.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
1440 :
1441 0 : if ( rEntry.eOp == SC_NOT_EQUAL )
1442 0 : bOk = !bOk;
1443 : }
1444 : else
1445 : {
1446 0 : OUString aQueryStr = rItem.maString.getString();
1447 : OUString aCell( mpTransliteration->transliterate(
1448 : aCellStr.getString(), ScGlobal::eLnge, 0, aCellStr.getLength(),
1449 0 : NULL ) );
1450 : OUString aQuer( mpTransliteration->transliterate(
1451 : aQueryStr, ScGlobal::eLnge, 0, aQueryStr.getLength(),
1452 0 : NULL ) );
1453 0 : sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH
1454 0 : || rEntry.eOp == SC_DOES_NOT_END_WITH) ? (aCell.getLength()-aQuer.getLength()) : 0;
1455 0 : sal_Int32 nStrPos = aCell.indexOf( aQuer, nIndex );
1456 0 : switch (rEntry.eOp)
1457 : {
1458 : case SC_EQUAL:
1459 : case SC_CONTAINS:
1460 0 : bOk = ( nStrPos != -1 );
1461 0 : break;
1462 : case SC_NOT_EQUAL:
1463 : case SC_DOES_NOT_CONTAIN:
1464 0 : bOk = ( nStrPos == -1 );
1465 0 : break;
1466 : case SC_BEGINS_WITH:
1467 0 : bOk = ( nStrPos == 0 );
1468 0 : break;
1469 : case SC_DOES_NOT_BEGIN_WITH:
1470 0 : bOk = ( nStrPos != 0 );
1471 0 : break;
1472 : case SC_ENDS_WITH:
1473 0 : bOk = ( nStrPos + aQuer.getLength() == aCell.getLength() );
1474 0 : break;
1475 : case SC_DOES_NOT_END_WITH:
1476 0 : bOk = ( nStrPos + aQuer.getLength() != aCell.getLength() );
1477 0 : break;
1478 : default:
1479 : {
1480 : // added to avoid warnings
1481 : }
1482 0 : }
1483 : }
1484 : }
1485 : else
1486 : { // use collator here because data was probably sorted
1487 : sal_Int32 nCompare = mpCollator->compareString(
1488 0 : aCellStr.getString(), rItem.maString.getString());
1489 0 : switch (rEntry.eOp)
1490 : {
1491 : case SC_LESS :
1492 0 : bOk = (nCompare < 0);
1493 0 : break;
1494 : case SC_GREATER :
1495 0 : bOk = (nCompare > 0);
1496 0 : break;
1497 : case SC_LESS_EQUAL :
1498 0 : bOk = (nCompare <= 0);
1499 0 : if ( bOk && mpTestEqualCondition && !bTestEqual )
1500 0 : bTestEqual = (nCompare == 0);
1501 0 : break;
1502 : case SC_GREATER_EQUAL :
1503 0 : bOk = (nCompare >= 0);
1504 0 : if ( bOk && mpTestEqualCondition && !bTestEqual )
1505 0 : bTestEqual = (nCompare == 0);
1506 0 : break;
1507 : default:
1508 : {
1509 : // added to avoid warnings
1510 : }
1511 : }
1512 : }
1513 : }
1514 :
1515 0 : return std::pair<bool,bool>(bOk, bTestEqual);
1516 : }
1517 :
1518 : // To be called only if both isQueryByValue() and isQueryByString()
1519 : // returned false and range lookup is wanted! In range lookup comparison
1520 : // numbers are less than strings. Nothing else is compared.
1521 0 : std::pair<bool,bool> compareByRangeLookup(
1522 : const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
1523 : const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
1524 : {
1525 0 : bool bTestEqual = false;
1526 :
1527 0 : if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS && rEntry.eOp != SC_LESS_EQUAL)
1528 0 : return std::pair<bool,bool>(false, bTestEqual);
1529 :
1530 0 : if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER && rEntry.eOp != SC_GREATER_EQUAL)
1531 0 : return std::pair<bool,bool>(false, bTestEqual);
1532 :
1533 0 : if (!rCell.isEmpty())
1534 : {
1535 0 : if (rItem.meType == ScQueryEntry::ByString)
1536 : {
1537 0 : if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
1538 : // Error values are compared as string.
1539 0 : return std::pair<bool,bool>(false, bTestEqual);
1540 :
1541 0 : return std::pair<bool,bool>(rCell.hasNumeric(), bTestEqual);
1542 : }
1543 :
1544 0 : return std::pair<bool,bool>(!rCell.hasNumeric(), bTestEqual);
1545 : }
1546 :
1547 0 : if (rItem.meType == ScQueryEntry::ByString)
1548 0 : return std::pair<bool,bool>(mrTab.HasValueData(nCol, nRow), bTestEqual);
1549 :
1550 0 : return std::pair<bool,bool>(!mrTab.HasValueData(nCol, nRow), bTestEqual);
1551 : }
1552 : };
1553 :
1554 : }
1555 :
1556 0 : bool ScTable::ValidQuery(
1557 : SCROW nRow, const ScQueryParam& rParam, ScRefCellValue* pCell, bool* pbTestEqualCondition)
1558 : {
1559 0 : if (!rParam.GetEntry(0).bDoQuery)
1560 0 : return true;
1561 :
1562 0 : SCSIZE nEntryCount = rParam.GetEntryCount();
1563 :
1564 : typedef std::pair<bool,bool> ResultType;
1565 0 : static std::vector<ResultType> aResults;
1566 0 : if (aResults.size() < nEntryCount)
1567 0 : aResults.resize(nEntryCount);
1568 :
1569 0 : long nPos = -1;
1570 0 : QueryEvaluator aEval(*pDocument, *this, rParam, pbTestEqualCondition);
1571 0 : ScQueryParam::const_iterator it, itBeg = rParam.begin(), itEnd = rParam.end();
1572 0 : for (it = itBeg; it != itEnd && it->bDoQuery; ++it)
1573 : {
1574 0 : const ScQueryEntry& rEntry = *it;
1575 0 : SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
1576 :
1577 : // we can only handle one single direct query
1578 0 : ScRefCellValue aCell;
1579 0 : if (pCell && it == itBeg)
1580 0 : aCell = *pCell;
1581 : else
1582 0 : aCell = GetCellValue(nCol, nRow);
1583 :
1584 0 : std::pair<bool,bool> aRes(false, false);
1585 :
1586 0 : const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
1587 0 : if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
1588 : {
1589 0 : if (rEntry.IsQueryByEmpty())
1590 0 : aRes.first = !aCol[rEntry.nField].HasDataAt(nRow);
1591 : else
1592 : {
1593 : OSL_ASSERT(rEntry.IsQueryByNonEmpty());
1594 0 : aRes.first = aCol[rEntry.nField].HasDataAt(nRow);
1595 : }
1596 : }
1597 : else
1598 : {
1599 0 : ScQueryEntry::QueryItemsType::const_iterator itr = rItems.begin(), itrEnd = rItems.end();
1600 :
1601 0 : for (; itr != itrEnd; ++itr)
1602 : {
1603 0 : if (aEval.isQueryByValue(*itr, nCol, nRow, aCell))
1604 : {
1605 : std::pair<bool,bool> aThisRes =
1606 0 : aEval.compareByValue(aCell, nCol, nRow, rEntry, *itr);
1607 0 : aRes.first |= aThisRes.first;
1608 0 : aRes.second |= aThisRes.second;
1609 : }
1610 0 : else if (aEval.isQueryByString(rEntry, *itr, nCol, nRow, aCell))
1611 : {
1612 : std::pair<bool,bool> aThisRes =
1613 0 : aEval.compareByString(aCell, nRow, rEntry, *itr);
1614 0 : aRes.first |= aThisRes.first;
1615 0 : aRes.second |= aThisRes.second;
1616 : }
1617 0 : else if (rParam.mbRangeLookup)
1618 : {
1619 : std::pair<bool,bool> aThisRes =
1620 0 : aEval.compareByRangeLookup(aCell, nCol, nRow, rEntry, *itr);
1621 0 : aRes.first |= aThisRes.first;
1622 0 : aRes.second |= aThisRes.second;
1623 : }
1624 :
1625 0 : if (aRes.first && aRes.second)
1626 0 : break;
1627 : }
1628 : }
1629 :
1630 0 : if (nPos == -1)
1631 : {
1632 0 : nPos++;
1633 0 : aResults[nPos] = aRes;
1634 : }
1635 : else
1636 : {
1637 0 : if (rEntry.eConnect == SC_AND)
1638 : {
1639 0 : aResults[nPos].first = aResults[nPos].first && aRes.first;
1640 0 : aResults[nPos].second = aResults[nPos].second && aRes.second;
1641 : }
1642 : else
1643 : {
1644 0 : nPos++;
1645 0 : aResults[nPos] = aRes;
1646 : }
1647 : }
1648 0 : }
1649 :
1650 0 : for ( long j=1; j <= nPos; j++ )
1651 : {
1652 0 : aResults[0].first = aResults[0].first || aResults[j].first;
1653 0 : aResults[0].second = aResults[0].second || aResults[j].second;
1654 : }
1655 :
1656 0 : bool bRet = aResults[0].first;
1657 0 : if ( pbTestEqualCondition )
1658 0 : *pbTestEqualCondition = aResults[0].second;
1659 :
1660 0 : return bRet;
1661 : }
1662 :
1663 0 : void ScTable::TopTenQuery( ScQueryParam& rParam )
1664 : {
1665 0 : bool bSortCollatorInitialized = false;
1666 0 : SCSIZE nEntryCount = rParam.GetEntryCount();
1667 0 : SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
1668 0 : SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
1669 0 : for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
1670 : {
1671 0 : ScQueryEntry& rEntry = rParam.GetEntry(i);
1672 0 : ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
1673 :
1674 0 : switch ( rEntry.eOp )
1675 : {
1676 : case SC_TOPVAL:
1677 : case SC_BOTVAL:
1678 : case SC_TOPPERC:
1679 : case SC_BOTPERC:
1680 : {
1681 0 : ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) );
1682 0 : aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
1683 0 : if ( !bSortCollatorInitialized )
1684 : {
1685 0 : bSortCollatorInitialized = true;
1686 0 : InitSortCollator( aLocalSortParam );
1687 : }
1688 0 : ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 );
1689 0 : DecoladeRow( pArray, nRow1, rParam.nRow2 );
1690 0 : QuickSort( pArray, nRow1, rParam.nRow2 );
1691 0 : ScSortInfo** ppInfo = pArray->GetFirstArray();
1692 0 : SCSIZE nValidCount = nCount;
1693 : // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert
1694 0 : while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.isEmpty())
1695 0 : nValidCount--;
1696 : // keine Strings zaehlen, sind zwischen Value und Leer
1697 0 : while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.hasString())
1698 0 : nValidCount--;
1699 0 : if ( nValidCount > 0 )
1700 : {
1701 0 : if ( rItem.meType == ScQueryEntry::ByString )
1702 : { // dat wird nix
1703 0 : rItem.meType = ScQueryEntry::ByValue;
1704 0 : rItem.mfVal = 10; // 10 bzw. 10%
1705 : }
1706 0 : SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
1707 0 : SCSIZE nOffset = 0;
1708 0 : switch ( rEntry.eOp )
1709 : {
1710 : case SC_TOPVAL:
1711 : {
1712 0 : rEntry.eOp = SC_GREATER_EQUAL;
1713 0 : if ( nVal > nValidCount )
1714 0 : nVal = nValidCount;
1715 0 : nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
1716 : }
1717 0 : break;
1718 : case SC_BOTVAL:
1719 : {
1720 0 : rEntry.eOp = SC_LESS_EQUAL;
1721 0 : if ( nVal > nValidCount )
1722 0 : nVal = nValidCount;
1723 0 : nOffset = nVal - 1; // 1 <= nVal <= nValidCount
1724 : }
1725 0 : break;
1726 : case SC_TOPPERC:
1727 : {
1728 0 : rEntry.eOp = SC_GREATER_EQUAL;
1729 0 : if ( nVal > 100 )
1730 0 : nVal = 100;
1731 0 : nOffset = nValidCount - (nValidCount * nVal / 100);
1732 0 : if ( nOffset >= nValidCount )
1733 0 : nOffset = nValidCount - 1;
1734 : }
1735 0 : break;
1736 : case SC_BOTPERC:
1737 : {
1738 0 : rEntry.eOp = SC_LESS_EQUAL;
1739 0 : if ( nVal > 100 )
1740 0 : nVal = 100;
1741 0 : nOffset = (nValidCount * nVal / 100);
1742 0 : if ( nOffset >= nValidCount )
1743 0 : nOffset = nValidCount - 1;
1744 : }
1745 0 : break;
1746 : default:
1747 : {
1748 : // added to avoid warnings
1749 : }
1750 : }
1751 0 : ScRefCellValue aCell = ppInfo[nOffset]->maCell;
1752 0 : if (aCell.hasNumeric())
1753 0 : rItem.mfVal = aCell.getValue();
1754 : else
1755 : {
1756 : OSL_FAIL( "TopTenQuery: pCell no ValueData" );
1757 0 : rEntry.eOp = SC_GREATER_EQUAL;
1758 0 : rItem.mfVal = 0;
1759 0 : }
1760 : }
1761 : else
1762 : {
1763 0 : rEntry.eOp = SC_GREATER_EQUAL;
1764 0 : rItem.meType = ScQueryEntry::ByValue;
1765 0 : rItem.mfVal = 0;
1766 : }
1767 0 : delete pArray;
1768 : }
1769 0 : break;
1770 : default:
1771 : {
1772 : // added to avoid warnings
1773 : }
1774 : }
1775 : }
1776 0 : if ( bSortCollatorInitialized )
1777 0 : DestroySortCollator();
1778 0 : }
1779 :
1780 : namespace {
1781 :
1782 : class PrepareQueryItem : public std::unary_function<ScQueryEntry::Item, void>
1783 : {
1784 : const ScDocument& mrDoc;
1785 : public:
1786 0 : PrepareQueryItem(const ScDocument& rDoc) : mrDoc(rDoc) {}
1787 :
1788 0 : void operator() (ScQueryEntry::Item& rItem)
1789 : {
1790 0 : if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
1791 0 : return;
1792 :
1793 0 : sal_uInt32 nIndex = 0;
1794 : bool bNumber = mrDoc.GetFormatTable()->
1795 0 : IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
1796 :
1797 : // Advanced Filter creates only ByString queries that need to be
1798 : // converted to ByValue if appropriate. rItem.mfVal now holds the value
1799 : // if bNumber==true.
1800 :
1801 0 : if (rItem.meType == ScQueryEntry::ByString)
1802 : {
1803 0 : if (bNumber)
1804 0 : rItem.meType = ScQueryEntry::ByValue;
1805 0 : return;
1806 : }
1807 :
1808 : // Double-check if the query by date is really appropriate.
1809 :
1810 0 : if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
1811 : {
1812 0 : const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
1813 0 : if (pEntry)
1814 : {
1815 0 : short nNumFmtType = pEntry->GetType();
1816 0 : if (!((nNumFmtType & NUMBERFORMAT_DATE) && !(nNumFmtType & NUMBERFORMAT_TIME)))
1817 0 : rItem.meType = ScQueryEntry::ByValue; // not a date only
1818 : }
1819 : else
1820 0 : rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
1821 : }
1822 : else
1823 0 : rItem.meType = ScQueryEntry::ByValue; // not a date
1824 : }
1825 : };
1826 :
1827 0 : void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam )
1828 : {
1829 0 : bool bTopTen = false;
1830 0 : SCSIZE nEntryCount = rParam.GetEntryCount();
1831 :
1832 0 : for ( SCSIZE i = 0; i < nEntryCount; ++i )
1833 : {
1834 0 : ScQueryEntry& rEntry = rParam.GetEntry(i);
1835 0 : if (!rEntry.bDoQuery)
1836 0 : continue;
1837 :
1838 0 : ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
1839 0 : std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc));
1840 :
1841 0 : if ( !bTopTen )
1842 : {
1843 0 : switch ( rEntry.eOp )
1844 : {
1845 : case SC_TOPVAL:
1846 : case SC_BOTVAL:
1847 : case SC_TOPPERC:
1848 : case SC_BOTPERC:
1849 : {
1850 0 : bTopTen = true;
1851 : }
1852 0 : break;
1853 : default:
1854 : {
1855 : }
1856 : }
1857 : }
1858 : }
1859 :
1860 0 : if ( bTopTen )
1861 : {
1862 0 : pTab->TopTenQuery( rParam );
1863 : }
1864 0 : }
1865 :
1866 : }
1867 :
1868 0 : SCSIZE ScTable::Query(ScQueryParam& rParamOrg, bool bKeepSub)
1869 : {
1870 0 : ScQueryParam aParam( rParamOrg );
1871 : typedef boost::unordered_set<OUString, OUStringHash> StrSetType;
1872 0 : StrSetType aStrSet;
1873 :
1874 0 : bool bStarted = false;
1875 0 : bool bOldResult = true;
1876 0 : SCROW nOldStart = 0;
1877 0 : SCROW nOldEnd = 0;
1878 :
1879 0 : SCSIZE nCount = 0;
1880 0 : SCROW nOutRow = 0;
1881 0 : SCROW nHeader = aParam.bHasHeader ? 1 : 0;
1882 :
1883 0 : lcl_PrepareQuery(pDocument, this, aParam);
1884 :
1885 0 : if (!aParam.bInplace)
1886 : {
1887 0 : nOutRow = aParam.nDestRow + nHeader;
1888 0 : if (nHeader > 0)
1889 : CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
1890 0 : aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
1891 : }
1892 :
1893 0 : SCROW nRealRow2 = aParam.nRow2;
1894 0 : for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
1895 : {
1896 : bool bResult; // Filterergebnis
1897 0 : bool bValid = ValidQuery(j, aParam);
1898 0 : if (!bValid && bKeepSub) // Subtotals stehenlassen
1899 : {
1900 0 : for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
1901 : {
1902 0 : ScRefCellValue aCell = GetCellValue(nCol, j);
1903 0 : if (aCell.meType != CELLTYPE_FORMULA)
1904 0 : continue;
1905 :
1906 0 : if (!aCell.mpFormula->IsSubTotal())
1907 0 : continue;
1908 :
1909 0 : if (RefVisible(aCell.mpFormula))
1910 0 : bValid = true;
1911 0 : }
1912 : }
1913 0 : if (bValid)
1914 : {
1915 0 : if (aParam.bDuplicate)
1916 0 : bResult = true;
1917 : else
1918 : {
1919 0 : OUString aStr;
1920 0 : for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
1921 : {
1922 0 : OUString aCellStr;
1923 0 : GetString(k, j, aCellStr);
1924 0 : OUStringBuffer aBuf(aStr);
1925 0 : aBuf.append(aCellStr);
1926 0 : aBuf.append(static_cast<sal_Unicode>(1));
1927 0 : aStr = aBuf.makeStringAndClear();
1928 0 : }
1929 :
1930 0 : std::pair<StrSetType::iterator, bool> r = aStrSet.insert(aStr);
1931 0 : bool bIsUnique = r.second; // unique if inserted.
1932 0 : bResult = bIsUnique;
1933 : }
1934 : }
1935 : else
1936 0 : bResult = false;
1937 :
1938 0 : if (aParam.bInplace)
1939 : {
1940 0 : if (bResult == bOldResult && bStarted)
1941 0 : nOldEnd = j;
1942 : else
1943 : {
1944 0 : if (bStarted)
1945 0 : DBShowRows(nOldStart,nOldEnd, bOldResult);
1946 0 : nOldStart = nOldEnd = j;
1947 0 : bOldResult = bResult;
1948 : }
1949 0 : bStarted = true;
1950 : }
1951 : else
1952 : {
1953 0 : if (bResult)
1954 : {
1955 0 : CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
1956 0 : ++nOutRow;
1957 : }
1958 : }
1959 0 : if (bResult)
1960 0 : ++nCount;
1961 : }
1962 :
1963 0 : if (aParam.bInplace && bStarted)
1964 0 : DBShowRows(nOldStart,nOldEnd, bOldResult);
1965 :
1966 0 : if (aParam.bInplace)
1967 0 : SetDrawPageSize();
1968 :
1969 0 : return nCount;
1970 : }
1971 :
1972 0 : bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
1973 : {
1974 0 : bool bValid = true;
1975 0 : boost::scoped_array<SCCOL> pFields(new SCCOL[nCol2-nCol1+1]);
1976 0 : OUString aCellStr;
1977 0 : SCCOL nCol = nCol1;
1978 : OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
1979 0 : SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
1980 0 : SCROW nDBRow1 = rQueryParam.nRow1;
1981 0 : SCCOL nDBCol2 = rQueryParam.nCol2;
1982 : // Erste Zeile muessen Spaltenkoepfe sein
1983 0 : while (bValid && (nCol <= nCol2))
1984 : {
1985 0 : OUString aQueryStr;
1986 0 : GetUpperCellString(nCol, nRow1, aQueryStr);
1987 0 : bool bFound = false;
1988 0 : SCCOL i = rQueryParam.nCol1;
1989 0 : while (!bFound && (i <= nDBCol2))
1990 : {
1991 0 : if ( nTab == nDBTab )
1992 0 : GetUpperCellString(i, nDBRow1, aCellStr);
1993 : else
1994 0 : pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
1995 0 : bFound = (aCellStr == aQueryStr);
1996 0 : if (!bFound) i++;
1997 : }
1998 0 : if (bFound)
1999 0 : pFields[nCol - nCol1] = i;
2000 : else
2001 0 : bValid = false;
2002 0 : nCol++;
2003 0 : }
2004 0 : if (bValid)
2005 : {
2006 0 : sal_uLong nVisible = 0;
2007 0 : for ( nCol=nCol1; nCol<=nCol2; nCol++ )
2008 0 : nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
2009 :
2010 0 : if ( nVisible > SCSIZE_MAX / sizeof(void*) )
2011 : {
2012 : OSL_FAIL("too many filter criteria");
2013 0 : nVisible = 0;
2014 : }
2015 :
2016 0 : SCSIZE nNewEntries = nVisible;
2017 0 : rQueryParam.Resize( nNewEntries );
2018 :
2019 0 : SCSIZE nIndex = 0;
2020 0 : SCROW nRow = nRow1 + 1;
2021 0 : svl::SharedStringPool& rPool = pDocument->GetSharedStringPool();
2022 0 : while (nRow <= nRow2)
2023 : {
2024 0 : nCol = nCol1;
2025 0 : while (nCol <= nCol2)
2026 : {
2027 0 : GetInputString( nCol, nRow, aCellStr );
2028 0 : if (!aCellStr.isEmpty())
2029 : {
2030 0 : if (nIndex < nNewEntries)
2031 : {
2032 0 : rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
2033 0 : rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex);
2034 0 : nIndex++;
2035 0 : if (nIndex < nNewEntries)
2036 0 : rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
2037 : }
2038 : else
2039 0 : bValid = false;
2040 : }
2041 0 : nCol++;
2042 : }
2043 0 : nRow++;
2044 0 : if (nIndex < nNewEntries)
2045 0 : rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
2046 : }
2047 : }
2048 0 : return bValid;
2049 : }
2050 :
2051 0 : bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2052 : {
2053 : // A valid StarQuery must be at least 4 columns wide. To be precise it
2054 : // should be exactly 4 columns ...
2055 : // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
2056 : // column Excel style query range immediately left to itself would result
2057 : // in a circular reference when the field name or operator or value (first
2058 : // to third query range column) is obtained (#i58354#). Furthermore, if the
2059 : // range wasn't sufficiently specified data changes wouldn't flag formula
2060 : // cells for recalculation.
2061 0 : if (nCol2 - nCol1 < 3)
2062 0 : return false;
2063 :
2064 : bool bValid;
2065 : bool bFound;
2066 0 : OUString aCellStr;
2067 0 : SCSIZE nIndex = 0;
2068 0 : SCROW nRow = nRow1;
2069 : OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
2070 0 : SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
2071 0 : SCROW nDBRow1 = rQueryParam.nRow1;
2072 0 : SCCOL nDBCol2 = rQueryParam.nCol2;
2073 :
2074 0 : SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
2075 0 : rQueryParam.Resize( nNewEntries );
2076 0 : svl::SharedStringPool& rPool = pDocument->GetSharedStringPool();
2077 :
2078 0 : do
2079 : {
2080 0 : ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
2081 :
2082 0 : bValid = false;
2083 : // Erste Spalte UND/ODER
2084 0 : if (nIndex > 0)
2085 : {
2086 0 : GetUpperCellString(nCol1, nRow, aCellStr);
2087 0 : if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) )
2088 : {
2089 0 : rEntry.eConnect = SC_AND;
2090 0 : bValid = true;
2091 : }
2092 0 : else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) )
2093 : {
2094 0 : rEntry.eConnect = SC_OR;
2095 0 : bValid = true;
2096 : }
2097 : }
2098 : // Zweite Spalte FeldName
2099 0 : if ((nIndex < 1) || bValid)
2100 : {
2101 0 : bFound = false;
2102 0 : GetUpperCellString(nCol1 + 1, nRow, aCellStr);
2103 0 : for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
2104 : {
2105 0 : OUString aFieldStr;
2106 0 : if ( nTab == nDBTab )
2107 0 : GetUpperCellString(i, nDBRow1, aFieldStr);
2108 : else
2109 0 : pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
2110 0 : bFound = (aCellStr == aFieldStr);
2111 0 : if (bFound)
2112 : {
2113 0 : rEntry.nField = i;
2114 0 : bValid = true;
2115 : }
2116 : else
2117 0 : bValid = false;
2118 0 : }
2119 : }
2120 : // Dritte Spalte Operator =<>...
2121 0 : if (bValid)
2122 : {
2123 0 : bFound = false;
2124 0 : GetUpperCellString(nCol1 + 2, nRow, aCellStr);
2125 0 : if (aCellStr.startsWith("<"))
2126 : {
2127 0 : if (aCellStr[1] == '>')
2128 0 : rEntry.eOp = SC_NOT_EQUAL;
2129 0 : else if (aCellStr[1] == '=')
2130 0 : rEntry.eOp = SC_LESS_EQUAL;
2131 : else
2132 0 : rEntry.eOp = SC_LESS;
2133 : }
2134 0 : else if (aCellStr.startsWith(">"))
2135 : {
2136 0 : if (aCellStr[1] == '=')
2137 0 : rEntry.eOp = SC_GREATER_EQUAL;
2138 : else
2139 0 : rEntry.eOp = SC_GREATER;
2140 : }
2141 0 : else if (aCellStr.startsWith("="))
2142 0 : rEntry.eOp = SC_EQUAL;
2143 :
2144 : }
2145 : // Vierte Spalte Wert
2146 0 : if (bValid)
2147 : {
2148 0 : OUString aStr;
2149 0 : GetString(nCol1 + 3, nRow, aStr);
2150 0 : rEntry.GetQueryItem().maString = rPool.intern(aStr);
2151 0 : rEntry.bDoQuery = true;
2152 : }
2153 0 : nIndex++;
2154 0 : nRow++;
2155 : }
2156 0 : while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
2157 0 : return bValid;
2158 : }
2159 :
2160 0 : bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2161 : {
2162 : SCSIZE i, nCount;
2163 0 : PutInOrder(nCol1, nCol2);
2164 0 : PutInOrder(nRow1, nRow2);
2165 :
2166 0 : nCount = rQueryParam.GetEntryCount();
2167 0 : for (i=0; i < nCount; i++)
2168 0 : rQueryParam.GetEntry(i).Clear();
2169 :
2170 : // Standard QueryTabelle
2171 0 : bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
2172 : // Excel QueryTabelle
2173 0 : if (!bValid)
2174 0 : bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
2175 :
2176 0 : nCount = rQueryParam.GetEntryCount();
2177 0 : if (bValid)
2178 : {
2179 : // bQueryByString muss gesetzt sein
2180 0 : for (i=0; i < nCount; i++)
2181 0 : rQueryParam.GetEntry(i).GetQueryItem().meType = ScQueryEntry::ByString;
2182 : }
2183 : else
2184 : {
2185 : // nix
2186 0 : for (i=0; i < nCount; i++)
2187 0 : rQueryParam.GetEntry(i).Clear();
2188 : }
2189 0 : return bValid;
2190 : }
2191 :
2192 0 : bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ ) const
2193 : {
2194 0 : for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
2195 : {
2196 0 : CellType eType = GetCellType( nCol, nStartRow );
2197 0 : if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2198 0 : return false;
2199 : }
2200 0 : return true;
2201 : }
2202 :
2203 0 : bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow ) const
2204 : {
2205 0 : for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
2206 : {
2207 0 : CellType eType = GetCellType( nStartCol, nRow );
2208 0 : if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2209 0 : return false;
2210 : }
2211 0 : return true;
2212 : }
2213 :
2214 0 : void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScTypedStrData>& rStrings, bool& rHasDates)
2215 : {
2216 0 : aCol[nCol].GetFilterEntries( nRow1, nRow2, rStrings, rHasDates );
2217 0 : }
2218 :
2219 0 : void ScTable::GetFilteredFilterEntries(
2220 : SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, std::vector<ScTypedStrData>& rStrings, bool& rHasDates)
2221 : {
2222 : // remove the entry for this column from the query parameter
2223 0 : ScQueryParam aParam( rParam );
2224 0 : aParam.RemoveEntryByField(nCol);
2225 :
2226 0 : lcl_PrepareQuery(pDocument, this, aParam);
2227 0 : bool bHasDates = false;
2228 0 : for ( SCROW j = nRow1; j <= nRow2; ++j )
2229 : {
2230 0 : if (ValidQuery(j, aParam))
2231 : {
2232 0 : bool bThisHasDates = false;
2233 0 : aCol[nCol].GetFilterEntries( j, j, rStrings, bThisHasDates );
2234 0 : bHasDates |= bThisHasDates;
2235 : }
2236 : }
2237 :
2238 0 : rHasDates = bHasDates;
2239 0 : }
2240 :
2241 0 : bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings, bool bLimit)
2242 : {
2243 0 : return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit );
2244 : }
2245 :
2246 0 : ScDocument& ScTable::GetDoc()
2247 : {
2248 0 : return *pDocument;
2249 : }
2250 :
2251 0 : const ScDocument& ScTable::GetDoc() const
2252 : {
2253 0 : return *pDocument;
2254 : }
2255 :
2256 0 : sal_uLong ScTable::GetCellCount() const
2257 : {
2258 0 : sal_uLong nCellCount = 0;
2259 :
2260 0 : for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
2261 0 : nCellCount += aCol[nCol].GetCellCount();
2262 :
2263 0 : return nCellCount;
2264 : }
2265 :
2266 0 : sal_uLong ScTable::GetWeightedCount() const
2267 : {
2268 0 : sal_uLong nCellCount = 0;
2269 :
2270 0 : for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
2271 0 : if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
2272 0 : nCellCount += aCol[nCol].GetWeightedCount();
2273 :
2274 0 : return nCellCount;
2275 : }
2276 :
2277 0 : sal_uLong ScTable::GetCodeCount() const
2278 : {
2279 0 : sal_uLong nCodeCount = 0;
2280 :
2281 0 : for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
2282 0 : if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
2283 0 : nCodeCount += aCol[nCol].GetCodeCount();
2284 :
2285 0 : return nCodeCount;
2286 : }
2287 :
2288 0 : sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
2289 : SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
2290 : {
2291 0 : if ( ValidCol(nCol) )
2292 0 : return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
2293 : else
2294 0 : return 0;
2295 : }
2296 :
2297 0 : sal_Int32 ScTable::GetMaxNumberStringLen(
2298 : sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
2299 : {
2300 0 : if ( ValidCol(nCol) )
2301 0 : return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
2302 : else
2303 0 : return 0;
2304 : }
2305 :
2306 0 : void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
2307 : {
2308 0 : for (SCCOL nCol = 0; nCol <= MAXCOL && !rData.bError; ++nCol)
2309 : {
2310 0 : if (pColFlags && ColHidden(nCol))
2311 0 : continue;
2312 :
2313 0 : aCol[nCol].UpdateSelectionFunction(rMark, rData, *mpHiddenRows);
2314 : }
2315 0 : }
2316 :
2317 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|