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