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