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 : #include <rtl/math.hxx>
21 : #include <comphelper/random.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 : #include <fstalgorithm.hxx>
60 : #include <listenercontext.hxx>
61 : #include <sharedformula.hxx>
62 : #include <stlsheet.hxx>
63 : #include <refhint.hxx>
64 : #include <listenerquery.hxx>
65 : #include <bcaslot.hxx>
66 : #include <reordermap.hxx>
67 :
68 : #include <svl/sharedstringpool.hxx>
69 :
70 : #include <unordered_set>
71 : #include <vector>
72 : #include <boost/checked_delete.hpp>
73 : #include <boost/scoped_ptr.hpp>
74 : #include <boost/scoped_array.hpp>
75 : #include <boost/noncopyable.hpp>
76 : #include <boost/ptr_container/ptr_vector.hpp>
77 : #include <mdds/flat_segment_tree.hpp>
78 :
79 : using namespace ::com::sun::star;
80 :
81 : namespace naturalsort {
82 :
83 : using namespace ::com::sun::star::i18n;
84 :
85 : /** Splits a given string into three parts: the prefix, number string, and
86 : the suffix.
87 :
88 : @param sWhole
89 : Original string to be split into pieces
90 :
91 : @param sPrefix
92 : Prefix string that consists of the part before the first number token
93 :
94 : @param sSuffix
95 : String after the last number token. This may still contain number strings.
96 :
97 : @param fNum
98 : Number converted from the middle number string
99 :
100 : @return Returns TRUE if a numeral element is found in a given string, or
101 : FALSE if no numeral element is found.
102 : */
103 0 : bool SplitString( const OUString &sWhole,
104 : OUString &sPrefix, OUString &sSuffix, double &fNum )
105 : {
106 0 : i18n::LocaleDataItem aLocaleItem = ScGlobal::pLocaleData->getLocaleItem();
107 :
108 : // Get prefix element
109 0 : OUString sEmpty, sUser = "-";
110 : ParseResult aPRPre = ScGlobal::pCharClass->parsePredefinedToken(
111 : KParseType::IDENTNAME, sWhole, 0,
112 0 : KParseTokens::ANY_LETTER, sUser, KParseTokens::ANY_LETTER, sUser );
113 0 : sPrefix = sWhole.copy( 0, aPRPre.EndPos );
114 :
115 : // Return FALSE if no numeral element is found
116 0 : if ( aPRPre.EndPos == sWhole.getLength() )
117 0 : return false;
118 :
119 : // Get numeral element
120 0 : sUser = aLocaleItem.decimalSeparator;
121 : ParseResult aPRNum = ScGlobal::pCharClass->parsePredefinedToken(
122 : KParseType::ANY_NUMBER, sWhole, aPRPre.EndPos,
123 0 : KParseTokens::ANY_NUMBER, sEmpty, KParseTokens::ANY_NUMBER, sUser );
124 :
125 0 : if ( aPRNum.EndPos == aPRPre.EndPos )
126 0 : return false;
127 :
128 0 : fNum = aPRNum.Value;
129 0 : sSuffix = sWhole.copy( aPRNum.EndPos );
130 :
131 0 : return true;
132 : }
133 :
134 : /** Naturally compares two given strings.
135 :
136 : This is the main function that should be called externally. It returns
137 : either 1, 0, or -1 depending on the comparison result of given two strings.
138 :
139 : @param sInput1
140 : Input string 1
141 :
142 : @param sInput2
143 : Input string 2
144 :
145 : @param bCaseSens
146 : Boolean value for case sensitivity
147 :
148 : @param pData
149 : Pointer to user defined sort list
150 :
151 : @param pCW
152 : Pointer to collator wrapper for normal string comparison
153 :
154 : @return Returnes 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
155 : sInput2 is greater.
156 : */
157 0 : short Compare( const OUString &sInput1, const OUString &sInput2,
158 : const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
159 : {
160 0 : OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
161 :
162 : do
163 : {
164 : double nNum1, nNum2;
165 0 : bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
166 0 : bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
167 :
168 : short nPreRes; // Prefix comparison result
169 0 : if ( pData )
170 : {
171 0 : if ( bCaseSens )
172 : {
173 0 : if ( !bNumFound1 || !bNumFound2 )
174 0 : return static_cast<short>(pData->Compare( sStr1, sStr2 ));
175 : else
176 0 : nPreRes = pData->Compare( sPre1, sPre2 );
177 : }
178 : else
179 : {
180 0 : if ( !bNumFound1 || !bNumFound2 )
181 0 : return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
182 : else
183 0 : nPreRes = pData->ICompare( sPre1, sPre2 );
184 : }
185 : }
186 : else
187 : {
188 0 : if ( !bNumFound1 || !bNumFound2 )
189 0 : return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
190 : else
191 0 : nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
192 : }
193 :
194 : // Prefix strings differ. Return immediately.
195 0 : if ( nPreRes != 0 ) return nPreRes;
196 :
197 0 : if ( nNum1 != nNum2 )
198 : {
199 0 : if ( nNum1 < nNum2 ) return -1;
200 0 : return (nNum1 > nNum2) ? 1 : 0;
201 : }
202 :
203 : // The prefix and the first numerical elements are equal, but the suffix
204 : // strings may still differ. Stay in the loop.
205 :
206 0 : sStr1 = sSuf1;
207 0 : sStr2 = sSuf2;
208 :
209 : } while (true);
210 :
211 0 : return 0;
212 : }
213 :
214 : }
215 :
216 : // STATIC DATA -----------------------------------------------------------
217 :
218 332 : struct ScSortInfo
219 : {
220 : ScRefCellValue maCell;
221 : SCCOLROW nOrg;
222 332 : DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo );
223 : };
224 52 : IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo )
225 :
226 : // END OF STATIC DATA -----------------------------------------------------
227 :
228 : class ScSortInfoArray : boost::noncopyable
229 : {
230 : public:
231 :
232 1442 : struct Cell
233 : {
234 : ScRefCellValue maCell;
235 : const sc::CellTextAttr* mpAttr;
236 : const SvtBroadcaster* mpBroadcaster;
237 : const ScPostIt* mpNote;
238 : const ScPatternAttr* mpPattern;
239 :
240 244 : Cell() : mpAttr(NULL), mpBroadcaster(NULL), mpNote(NULL), mpPattern(NULL) {}
241 : };
242 :
243 244 : struct Row
244 : {
245 : std::vector<Cell> maCells;
246 :
247 : bool mbHidden:1;
248 : bool mbFiltered:1;
249 :
250 244 : Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
251 : };
252 :
253 : typedef std::vector<Row*> RowsType;
254 :
255 : private:
256 : boost::scoped_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
257 :
258 : ScSortInfo*** pppInfo;
259 : SCSIZE nCount;
260 : SCCOLROW nStart;
261 : SCCOLROW mnLastIndex; /// index of last non-empty cell position.
262 : sal_uInt16 nUsedSorts;
263 :
264 : std::vector<SCCOLROW> maOrderIndices;
265 : bool mbKeepQuery;
266 : bool mbUpdateRefs;
267 :
268 : public:
269 55 : ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
270 : pppInfo(NULL),
271 55 : nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ),
272 : mnLastIndex(nInd2),
273 : nUsedSorts(nSorts),
274 : mbKeepQuery(false),
275 110 : mbUpdateRefs(false)
276 : {
277 55 : if (nUsedSorts)
278 : {
279 33 : pppInfo = new ScSortInfo**[nUsedSorts];
280 66 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
281 : {
282 33 : ScSortInfo** ppInfo = new ScSortInfo* [nCount];
283 199 : for ( SCSIZE j = 0; j < nCount; j++ )
284 166 : ppInfo[j] = new ScSortInfo;
285 33 : pppInfo[nSort] = ppInfo;
286 : }
287 : }
288 :
289 337 : for (size_t i = 0; i < nCount; ++i)
290 282 : maOrderIndices.push_back(i+nStart);
291 55 : }
292 :
293 55 : ~ScSortInfoArray()
294 55 : {
295 55 : if (pppInfo)
296 : {
297 66 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
298 : {
299 33 : ScSortInfo** ppInfo = pppInfo[nSort];
300 199 : for ( SCSIZE j = 0; j < nCount; j++ )
301 166 : delete ppInfo[j];
302 33 : delete [] ppInfo;
303 : }
304 33 : delete[] pppInfo;
305 : }
306 :
307 55 : if (mpRows)
308 47 : std::for_each(mpRows->begin(), mpRows->end(), boost::checked_deleter<Row>());
309 55 : }
310 :
311 55 : void SetKeepQuery( bool b ) { mbKeepQuery = b; }
312 :
313 291 : bool IsKeepQuery() const { return mbKeepQuery; }
314 :
315 55 : void SetUpdateRefs( bool b ) { mbUpdateRefs = b; }
316 :
317 1005 : bool IsUpdateRefs() const { return mbUpdateRefs; }
318 :
319 : /**
320 : * Call this only during normal sorting, not from reordering.
321 : */
322 0 : ScSortInfo** GetFirstArray() const
323 : {
324 : OSL_ASSERT(pppInfo);
325 0 : return pppInfo[0];
326 : }
327 :
328 : /**
329 : * Call this only during normal sorting, not from reordering.
330 : */
331 1554 : ScSortInfo* Get( sal_uInt16 nSort, SCCOLROW nInd )
332 : {
333 : OSL_ASSERT(pppInfo);
334 1554 : return (pppInfo[nSort])[ nInd - nStart ];
335 : }
336 :
337 : /**
338 : * Call this only during normal sorting, not from reordering.
339 : */
340 98 : void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
341 : {
342 : OSL_ASSERT(pppInfo);
343 98 : SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
344 98 : SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
345 196 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
346 : {
347 98 : ScSortInfo** ppInfo = pppInfo[nSort];
348 98 : ScSortInfo* pTmp = ppInfo[n1];
349 98 : ppInfo[n1] = ppInfo[n2];
350 98 : ppInfo[n2] = pTmp;
351 : }
352 :
353 98 : std::swap(maOrderIndices[n1], maOrderIndices[n2]);
354 :
355 98 : if (mpRows)
356 : {
357 : // Swap rows in data table.
358 90 : RowsType& rRows = *mpRows;
359 90 : std::swap(rRows[n1], rRows[n2]);
360 : }
361 98 : }
362 :
363 4 : void SetOrderIndices( const std::vector<SCCOLROW>& rIndices )
364 : {
365 4 : maOrderIndices = rIndices;
366 4 : }
367 :
368 : /**
369 : * @param rIndices indices are actual row positions on the sheet, not an
370 : * offset from the top row.
371 : */
372 18 : void ReorderByRow( const std::vector<SCCOLROW>& rIndices )
373 : {
374 18 : if (!mpRows)
375 18 : return;
376 :
377 18 : RowsType& rRows = *mpRows;
378 :
379 18 : std::vector<SCCOLROW> aOrderIndices2;
380 18 : aOrderIndices2.reserve(rIndices.size());
381 :
382 36 : RowsType aRows2;
383 18 : aRows2.reserve(rRows.size());
384 :
385 18 : std::vector<SCCOLROW>::const_iterator it = rIndices.begin(), itEnd = rIndices.end();
386 112 : for (; it != itEnd; ++it)
387 : {
388 94 : size_t nPos = *it - nStart; // switch to an offset to top row.
389 94 : aRows2.push_back(rRows[nPos]);
390 94 : aOrderIndices2.push_back(maOrderIndices[nPos]);
391 : }
392 :
393 18 : rRows.swap(aRows2);
394 36 : maOrderIndices.swap(aOrderIndices2);
395 : }
396 :
397 144 : sal_uInt16 GetUsedSorts() const { return nUsedSorts; }
398 :
399 102 : SCCOLROW GetStart() const { return nStart; }
400 55 : SCCOLROW GetLast() const { return mnLastIndex; }
401 :
402 64 : const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; }
403 :
404 47 : RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
405 : {
406 47 : mpRows.reset(new RowsType);
407 47 : mpRows->reserve(nRowSize);
408 291 : for (size_t i = 0; i < nRowSize; ++i)
409 244 : mpRows->push_back(new Row(nColSize));
410 :
411 47 : return *mpRows;
412 : }
413 :
414 47 : RowsType* GetDataRows()
415 : {
416 47 : return mpRows.get();
417 : }
418 : };
419 :
420 : namespace {
421 :
422 47 : void initDataRows(
423 : ScSortInfoArray& rArray, ScTable& rTab, ScColumn* pCols,
424 : SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
425 : bool bPattern, bool bHiddenFiltered )
426 : {
427 : // Fill row-wise data table.
428 47 : ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
429 :
430 160 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
431 : {
432 113 : ScColumn& rCol = pCols[nCol];
433 :
434 : // Skip reordering of cell formats if the whole span is on the same pattern entry.
435 113 : bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
436 :
437 113 : sc::ColumnBlockConstPosition aBlockPos;
438 113 : rCol.InitBlockPosition(aBlockPos);
439 712 : for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
440 : {
441 599 : ScSortInfoArray::Row& rRow = *rRows[nRow-nRow1];
442 599 : ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
443 :
444 599 : rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
445 599 : rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
446 599 : rCell.mpBroadcaster = rCol.GetBroadcaster(aBlockPos, nRow);
447 599 : rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
448 :
449 599 : if (!bUniformPattern && bPattern)
450 3 : rCell.mpPattern = rCol.GetPattern(nRow);
451 : }
452 : }
453 :
454 47 : if (bHiddenFiltered)
455 : {
456 0 : for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
457 : {
458 0 : ScSortInfoArray::Row& rRow = *rRows[nRow-nRow1];
459 0 : rRow.mbHidden = rTab.RowHidden(nRow);
460 0 : rRow.mbFiltered = rTab.RowFiltered(nRow);
461 : }
462 : }
463 47 : }
464 :
465 : }
466 :
467 22 : ScSortInfoArray* ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
468 : {
469 22 : ScSortInfoArray* pArray = NULL;
470 :
471 22 : if (rParam.mbByRow)
472 : {
473 : // Create a sort info array with just the data table.
474 18 : SCROW nRow1 = rParam.maSortRange.aStart.Row();
475 18 : SCROW nRow2 = rParam.maSortRange.aEnd.Row();
476 18 : SCCOL nCol1 = rParam.maSortRange.aStart.Col();
477 18 : SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
478 :
479 18 : pArray = new ScSortInfoArray(0, nRow1, nRow2);
480 18 : pArray->SetKeepQuery(rParam.mbHiddenFiltered);
481 18 : pArray->SetUpdateRefs(rParam.mbUpdateRefs);
482 :
483 : initDataRows(
484 : *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2,
485 18 : rParam.mbPattern, rParam.mbHiddenFiltered);
486 : }
487 : else
488 : {
489 4 : SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
490 4 : SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
491 :
492 4 : pArray = new ScSortInfoArray(0, nCol1, nCol2);
493 4 : pArray->SetKeepQuery(rParam.mbHiddenFiltered);
494 4 : pArray->SetUpdateRefs(rParam.mbUpdateRefs);
495 : }
496 :
497 22 : return pArray;
498 : }
499 :
500 33 : ScSortInfoArray* ScTable::CreateSortInfoArray(
501 : const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
502 : bool bKeepQuery, bool bUpdateRefs )
503 : {
504 33 : sal_uInt16 nUsedSorts = 1;
505 66 : while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
506 0 : nUsedSorts++;
507 33 : ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 );
508 33 : pArray->SetKeepQuery(bKeepQuery);
509 33 : pArray->SetUpdateRefs(bUpdateRefs);
510 :
511 33 : if ( rSortParam.bByRow )
512 : {
513 58 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
514 : {
515 29 : SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
516 29 : ScColumn* pCol = &aCol[nCol];
517 29 : sc::ColumnBlockConstPosition aBlockPos;
518 29 : pCol->InitBlockPosition(aBlockPos);
519 179 : for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
520 : {
521 150 : ScSortInfo* pInfo = pArray->Get( nSort, nRow );
522 150 : pInfo->maCell = pCol->GetCellValue(aBlockPos, nRow);
523 150 : pInfo->nOrg = nRow;
524 : }
525 : }
526 :
527 : initDataRows(
528 : *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2,
529 29 : rSortParam.bIncludePattern, bKeepQuery);
530 : }
531 : else
532 : {
533 8 : for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
534 : {
535 4 : SCROW nRow = rSortParam.maKeyState[nSort].nField;
536 40 : for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
537 20 : nCol <= static_cast<SCCOL>(nInd2); nCol++ )
538 : {
539 16 : ScSortInfo* pInfo = pArray->Get( nSort, nCol );
540 16 : pInfo->maCell = GetCellValue(nCol, nRow);
541 16 : pInfo->nOrg = nCol;
542 : }
543 : }
544 : }
545 33 : return pArray;
546 : }
547 :
548 : namespace {
549 :
550 113 : struct SortedColumn : boost::noncopyable
551 : {
552 : typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
553 :
554 : sc::CellStoreType maCells;
555 : sc::CellTextAttrStoreType maCellTextAttrs;
556 : sc::BroadcasterStoreType maBroadcasters;
557 : sc::CellNoteStoreType maCellNotes;
558 :
559 : PatRangeType maPatterns;
560 : PatRangeType::const_iterator miPatternPos;
561 :
562 113 : SortedColumn( size_t nTopEmptyRows ) :
563 : maCells(nTopEmptyRows),
564 : maCellTextAttrs(nTopEmptyRows),
565 : maBroadcasters(nTopEmptyRows),
566 : maCellNotes(nTopEmptyRows),
567 : maPatterns(0, MAXROWCOUNT, NULL),
568 113 : miPatternPos(maPatterns.begin()) {}
569 :
570 3 : void setPattern( SCROW nRow, const ScPatternAttr* pPat )
571 : {
572 3 : miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
573 3 : }
574 : };
575 :
576 94 : struct SortedRowFlags
577 : {
578 : typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
579 :
580 : FlagsType maRowsHidden;
581 : FlagsType maRowsFiltered;
582 : FlagsType::const_iterator miPosHidden;
583 : FlagsType::const_iterator miPosFiltered;
584 :
585 94 : SortedRowFlags() :
586 : maRowsHidden(0, MAXROWCOUNT, false),
587 : maRowsFiltered(0, MAXROWCOUNT, false),
588 : miPosHidden(maRowsHidden.begin()),
589 94 : miPosFiltered(maRowsFiltered.begin()) {}
590 :
591 0 : void setRowHidden( SCROW nRow, bool b )
592 : {
593 0 : miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
594 0 : }
595 :
596 0 : void setRowFiltered( SCROW nRow, bool b )
597 : {
598 0 : miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
599 0 : }
600 :
601 47 : void swap( SortedRowFlags& r )
602 : {
603 47 : maRowsHidden.swap(r.maRowsHidden);
604 47 : maRowsFiltered.swap(r.maRowsFiltered);
605 :
606 : // Just reset the position hints.
607 47 : miPosHidden = maRowsHidden.begin();
608 47 : miPosFiltered = maRowsFiltered.begin();
609 47 : }
610 : };
611 :
612 : struct PatternSpan
613 : {
614 : SCROW mnRow1;
615 : SCROW mnRow2;
616 : const ScPatternAttr* mpPattern;
617 :
618 3 : PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
619 3 : mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
620 : };
621 :
622 : }
623 :
624 35 : bool ScTable::IsSortCollatorGlobal() const
625 : {
626 35 : return pSortCollator == ScGlobal::GetCollator() ||
627 35 : pSortCollator == ScGlobal::GetCaseCollator();
628 : }
629 :
630 35 : void ScTable::InitSortCollator( const ScSortParam& rPar )
631 : {
632 35 : if ( !rPar.aCollatorLocale.Language.isEmpty() )
633 : {
634 0 : if ( !pSortCollator || IsSortCollatorGlobal() )
635 0 : pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
636 : pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
637 0 : rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
638 : }
639 : else
640 : { // SYSTEM
641 35 : DestroySortCollator();
642 : pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() :
643 35 : ScGlobal::GetCollator());
644 : }
645 35 : }
646 :
647 2450 : void ScTable::DestroySortCollator()
648 : {
649 2450 : if ( pSortCollator )
650 : {
651 35 : if ( !IsSortCollatorGlobal() )
652 0 : delete pSortCollator;
653 35 : pSortCollator = NULL;
654 : }
655 2450 : }
656 :
657 : namespace {
658 :
659 : template<typename _Hint, typename _ReorderMap, typename _Index>
660 145 : class ReorderNotifier : std::unary_function<SvtListener*, void>
661 : {
662 : _Hint maHint;
663 : public:
664 29 : ReorderNotifier( const _ReorderMap& rMap, SCTAB nTab, _Index nPos1, _Index nPos2 ) :
665 29 : maHint(rMap, nTab, nPos1, nPos2) {}
666 :
667 108 : void operator() ( SvtListener* p )
668 : {
669 108 : p->Notify(maHint);
670 108 : }
671 : };
672 :
673 : typedef ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> ColReorderNotifier;
674 : typedef ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> RowReorderNotifier;
675 :
676 130 : class StartListeningNotifier : std::unary_function<SvtListener*, void>
677 : {
678 : sc::RefStartListeningHint maHint;
679 : public:
680 26 : StartListeningNotifier() {}
681 :
682 17 : void operator() ( SvtListener* p )
683 : {
684 17 : p->Notify(maHint);
685 17 : }
686 : };
687 :
688 130 : class StopListeningNotifier : std::unary_function<SvtListener*, void>
689 : {
690 : sc::RefStopListeningHint maHint;
691 : public:
692 26 : StopListeningNotifier() {}
693 :
694 17 : void operator() ( SvtListener* p )
695 : {
696 17 : p->Notify(maHint);
697 17 : }
698 : };
699 :
700 : class FormulaGroupPosCollector : std::unary_function<SvtListener*, void>
701 : {
702 : sc::RefQueryFormulaGroup& mrQuery;
703 :
704 : public:
705 28 : FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
706 :
707 105 : void operator() ( SvtListener* p )
708 : {
709 105 : p->Query(mrQuery);
710 105 : }
711 : };
712 :
713 47 : void fillSortedColumnArray(
714 : boost::ptr_vector<SortedColumn>& rSortedCols,
715 : SortedRowFlags& rRowFlags,
716 : ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
717 : {
718 47 : SCROW nRow1 = pArray->GetStart();
719 47 : ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
720 :
721 47 : size_t nColCount = nCol2 - nCol1 + 1;
722 47 : boost::ptr_vector<SortedColumn> aSortedCols; // storage for copied cells.
723 94 : SortedRowFlags aRowFlags;
724 47 : aSortedCols.reserve(nColCount);
725 160 : for (size_t i = 0; i < nColCount; ++i)
726 : {
727 : // In the sorted column container, element positions and row
728 : // positions must match, else formula cells may mis-behave during
729 : // grouping.
730 113 : aSortedCols.push_back(new SortedColumn(nRow1));
731 : }
732 :
733 291 : for (size_t i = 0; i < pRows->size(); ++i)
734 : {
735 244 : ScSortInfoArray::Row* pRow = (*pRows)[i];
736 843 : for (size_t j = 0; j < pRow->maCells.size(); ++j)
737 : {
738 599 : ScAddress aCellPos(nCol1 + j, nRow1 + i, nTab);
739 :
740 599 : ScSortInfoArray::Cell& rCell = pRow->maCells[j];
741 :
742 599 : sc::CellStoreType& rCellStore = aSortedCols.at(j).maCells;
743 599 : switch (rCell.maCell.meType)
744 : {
745 : case CELLTYPE_STRING:
746 : assert(rCell.mpAttr);
747 34 : rCellStore.push_back(*rCell.maCell.mpString);
748 34 : break;
749 : case CELLTYPE_VALUE:
750 : assert(rCell.mpAttr);
751 222 : rCellStore.push_back(rCell.maCell.mfValue);
752 222 : break;
753 : case CELLTYPE_EDIT:
754 : assert(rCell.mpAttr);
755 2 : rCellStore.push_back(rCell.maCell.mpEditText->Clone());
756 2 : break;
757 : case CELLTYPE_FORMULA:
758 : {
759 : assert(rCell.mpAttr);
760 335 : ScAddress aOldPos = rCell.maCell.mpFormula->aPos;
761 :
762 335 : ScFormulaCell* pNew = rCell.maCell.mpFormula->Clone( aCellPos, SC_CLONECELL_DEFAULT);
763 335 : if (pArray->IsUpdateRefs())
764 : {
765 186 : pNew->CopyAllBroadcasters(*rCell.maCell.mpFormula);
766 186 : pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
767 : }
768 : else
769 : {
770 149 : pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
771 : }
772 :
773 335 : rCellStore.push_back(pNew);
774 : }
775 335 : break;
776 : default:
777 : assert(!rCell.mpAttr);
778 6 : rCellStore.push_back_empty();
779 : }
780 :
781 599 : sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j).maCellTextAttrs;
782 599 : if (rCell.mpAttr)
783 593 : rAttrStore.push_back(*rCell.mpAttr);
784 : else
785 6 : rAttrStore.push_back_empty();
786 :
787 599 : if (pArray->IsUpdateRefs())
788 : {
789 : // At this point each broadcaster instance is managed by 2
790 : // containers. We will release those in the original storage
791 : // below before transferring them to the document.
792 315 : sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j).maBroadcasters;
793 315 : if (rCell.mpBroadcaster)
794 : // A const pointer would be implicitly converted to a bool type.
795 82 : rBCStore.push_back(const_cast<SvtBroadcaster*>(rCell.mpBroadcaster));
796 : else
797 233 : rBCStore.push_back_empty();
798 : }
799 :
800 : // The same with cell note instances ...
801 599 : sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j).maCellNotes;
802 599 : if (rCell.mpNote)
803 1 : rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
804 : else
805 598 : rNoteStore.push_back_empty();
806 :
807 599 : if (rCell.mpPattern)
808 3 : aSortedCols.at(j).setPattern(aCellPos.Row(), rCell.mpPattern);
809 : }
810 :
811 244 : if (pArray->IsKeepQuery())
812 : {
813 : // Hidden and filtered flags are first converted to segments.
814 0 : SCROW nRow = nRow1 + i;
815 0 : aRowFlags.setRowHidden(nRow, pRow->mbHidden);
816 0 : aRowFlags.setRowFiltered(nRow, pRow->mbFiltered);
817 : }
818 :
819 244 : if (pProgress)
820 119 : pProgress->SetStateOnPercent(i);
821 : }
822 :
823 47 : rSortedCols.swap(aSortedCols);
824 94 : rRowFlags.swap(aRowFlags);
825 47 : }
826 :
827 4 : void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
828 : {
829 4 : if (nTop < rRange.aStart.Row())
830 0 : rRange.aStart.SetRow(nTop);
831 :
832 4 : if (rRange.aEnd.Row() < nBottom)
833 0 : rRange.aEnd.SetRow(nBottom);
834 4 : }
835 :
836 28 : class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
837 : {
838 : std::vector<ScFormulaCell*>& mrCells;
839 : ScColumn* mpCol;
840 :
841 : public:
842 28 : FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
843 28 : mrCells(rCells), mpCol(NULL) {}
844 :
845 4 : virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
846 : {
847 4 : mpCol = pCol;
848 4 : }
849 :
850 12 : virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
851 : {
852 : assert(mpCol);
853 :
854 12 : if (!bVal)
855 20 : return;
856 :
857 4 : mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
858 : }
859 : };
860 :
861 28 : class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
862 : {
863 : ScColumn* mpCol;
864 :
865 : boost::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
866 : sc::StartListeningContext maStartCxt;
867 : sc::EndListeningContext maEndCxt;
868 :
869 : public:
870 28 : ListenerStartAction( ScDocument& rDoc ) :
871 : mpCol(0),
872 28 : mpPosSet(new sc::ColumnBlockPositionSet(rDoc)),
873 : maStartCxt(rDoc, mpPosSet),
874 56 : maEndCxt(rDoc, mpPosSet) {}
875 :
876 4 : virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
877 : {
878 4 : mpCol = pCol;
879 4 : }
880 :
881 12 : virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
882 : {
883 : assert(mpCol);
884 :
885 12 : if (!bVal)
886 20 : return;
887 :
888 4 : mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
889 : }
890 : };
891 :
892 : }
893 :
894 8 : void ScTable::SortReorderByColumn(
895 : ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
896 : {
897 8 : SCCOLROW nStart = pArray->GetStart();
898 8 : SCCOLROW nLast = pArray->GetLast();
899 :
900 8 : std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
901 8 : size_t nCount = aIndices.size();
902 :
903 : // Cut formula grouping at row and reference boundaries before the reordering.
904 8 : ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
905 46 : for (SCCOL nCol = nStart; nCol <= (SCCOL)nLast; ++nCol)
906 38 : aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
907 :
908 : // Collect all listeners of cell broadcasters of sorted range.
909 16 : std::vector<SvtListener*> aCellListeners;
910 :
911 8 : if (!pArray->IsUpdateRefs())
912 : {
913 : // Collect listeners of cell broadcasters.
914 42 : for (SCCOL nCol = nStart; nCol <= (SCCOL)nLast; ++nCol)
915 35 : aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
916 :
917 : // Remove any duplicate listener entries. We must ensure that we
918 : // notify each unique listener only once.
919 7 : std::sort(aCellListeners.begin(), aCellListeners.end());
920 7 : aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
921 :
922 : // Notify the cells' listeners to stop listening.
923 : /* TODO: for performance this could be enhanced to stop and later
924 : * restart only listening to within the reordered range and keep
925 : * listening to everything outside untouched. */
926 7 : StopListeningNotifier aFunc;
927 7 : std::for_each(aCellListeners.begin(), aCellListeners.end(), aFunc);
928 : }
929 :
930 : // table to keep track of column index to position in the index table.
931 16 : std::vector<SCCOLROW> aPosTable(nCount);
932 46 : for (size_t i = 0; i < nCount; ++i)
933 38 : aPosTable[aIndices[i]-nStart] = i;
934 :
935 8 : SCCOLROW nDest = nStart;
936 46 : for (size_t i = 0; i < nCount; ++i, ++nDest)
937 : {
938 38 : SCCOLROW nSrc = aIndices[i];
939 38 : if (nDest != nSrc)
940 : {
941 18 : aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
942 :
943 : // Update the position of the index that was originally equal to nDest.
944 18 : size_t nPos = aPosTable[nDest-nStart];
945 18 : aIndices[nPos] = nSrc;
946 18 : aPosTable[nSrc-nStart] = nPos;
947 : }
948 :
949 38 : if (pProgress)
950 16 : pProgress->SetStateOnPercent(i);
951 : }
952 :
953 : // Reset formula cell positions which became out-of-sync after column reordering.
954 8 : bool bUpdateRefs = pArray->IsUpdateRefs();
955 46 : for (SCCOL nCol = nStart; nCol <= (SCCOL)nLast; ++nCol)
956 38 : aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
957 :
958 8 : if (pArray->IsUpdateRefs())
959 : {
960 : // Set up column reorder map (for later broadcasting of reference updates).
961 1 : sc::ColRowReorderMapType aColMap;
962 1 : const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
963 4 : for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
964 : {
965 3 : SCCOL nNew = i + nStart;
966 3 : SCCOL nOld = rOldIndices[i];
967 3 : aColMap.insert(sc::ColRowReorderMapType::value_type(nOld, nNew));
968 : }
969 :
970 : // Collect all listeners within sorted range ahead of time.
971 2 : std::vector<SvtListener*> aListeners;
972 :
973 4 : for (SCCOL nCol = nStart; nCol <= (SCCOL)nLast; ++nCol)
974 3 : aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
975 :
976 : // Get all area listeners that listen on one column within the range
977 : // and end their listening.
978 1 : ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
979 : std::vector<sc::AreaListener> aAreaListeners = pDocument->GetBASM()->GetAllListeners(
980 2 : aMoveRange, sc::OneColumnInsideArea);
981 : {
982 1 : std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
983 1 : for (; it != itEnd; ++it)
984 : {
985 0 : pDocument->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener);
986 0 : aListeners.push_back( it->mpListener);
987 : }
988 : }
989 :
990 : // Remove any duplicate listener entries and notify all listeners
991 : // afterward. We must ensure that we notify each unique listener only
992 : // once.
993 1 : std::sort(aListeners.begin(), aListeners.end());
994 1 : aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
995 2 : ColReorderNotifier aFunc(aColMap, nTab, nRow1, nRow2);
996 1 : std::for_each(aListeners.begin(), aListeners.end(), aFunc);
997 :
998 : // Re-start area listeners on the reordered columns.
999 : {
1000 1 : std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
1001 1 : for (; it != itEnd; ++it)
1002 : {
1003 0 : ScRange aNewRange = it->maArea;
1004 0 : sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
1005 0 : if (itCol != aColMap.end())
1006 : {
1007 0 : aNewRange.aStart.SetCol( itCol->second);
1008 0 : aNewRange.aEnd.SetCol( itCol->second);
1009 : }
1010 0 : pDocument->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener);
1011 : }
1012 1 : }
1013 : }
1014 : else // !(pArray->IsUpdateRefs())
1015 : {
1016 : // Notify the cells' listeners to (re-)start listening.
1017 7 : StartListeningNotifier aFunc;
1018 7 : std::for_each(aCellListeners.begin(), aCellListeners.end(), aFunc);
1019 : }
1020 :
1021 : // Re-join formulas at row boundaries now that all the references have
1022 : // been adjusted for column reordering.
1023 46 : for (SCCOL nCol = nStart; nCol <= (SCCOL)nLast; ++nCol)
1024 : {
1025 38 : sc::CellStoreType& rCells = aCol[nCol].maCells;
1026 38 : sc::CellStoreType::position_type aPos = rCells.position(nRow1);
1027 38 : sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1028 38 : if (nRow2 < MAXROW)
1029 : {
1030 38 : aPos = rCells.position(aPos.first, nRow2+1);
1031 38 : sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1032 : }
1033 8 : }
1034 8 : }
1035 :
1036 19 : void ScTable::SortReorderByRow(
1037 : ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1038 : {
1039 : assert(!pArray->IsUpdateRefs());
1040 :
1041 19 : if (nCol2 < nCol1)
1042 19 : return;
1043 :
1044 19 : SCROW nRow1 = pArray->GetStart();
1045 19 : SCROW nRow2 = pArray->GetLast();
1046 :
1047 : // Collect all listeners of cell broadcasters of sorted range.
1048 19 : std::vector<SvtListener*> aCellListeners;
1049 :
1050 : // When the update ref mode is disabled, we need to detach all formula
1051 : // cells in the sorted range before reordering, and re-start them
1052 : // afterward.
1053 : {
1054 19 : sc::EndListeningContext aCxt(*pDocument);
1055 19 : DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1056 : }
1057 :
1058 : // Collect listeners of cell broadcasters.
1059 70 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1060 51 : aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
1061 :
1062 : // Remove any duplicate listener entries. We must ensure that we notify
1063 : // each unique listener only once.
1064 19 : std::sort(aCellListeners.begin(), aCellListeners.end());
1065 19 : aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
1066 :
1067 : // Notify the cells' listeners to stop listening.
1068 : /* TODO: for performance this could be enhanced to stop and later
1069 : * restart only listening to within the reordered range and keep
1070 : * listening to everything outside untouched. */
1071 : {
1072 19 : StopListeningNotifier aFunc;
1073 19 : std::for_each(aCellListeners.begin(), aCellListeners.end(), aFunc);
1074 : }
1075 :
1076 : // Split formula groups at the sort range boundaries (if applicable).
1077 38 : std::vector<SCROW> aRowBounds;
1078 19 : aRowBounds.reserve(2);
1079 19 : aRowBounds.push_back(nRow1);
1080 19 : aRowBounds.push_back(nRow2+1);
1081 70 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1082 51 : SplitFormulaGroups(nCol, aRowBounds);
1083 :
1084 : // Cells in the data rows only reference values in the document. Make
1085 : // a copy before updating the document.
1086 38 : boost::ptr_vector<SortedColumn> aSortedCols; // storage for copied cells.
1087 38 : SortedRowFlags aRowFlags;
1088 19 : fillSortedColumnArray(aSortedCols, aRowFlags, pArray, nTab, nCol1, nCol2, pProgress);
1089 :
1090 70 : for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1091 : {
1092 51 : SCCOL nThisCol = i + nCol1;
1093 :
1094 : {
1095 51 : sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1096 51 : sc::CellStoreType& rSrc = aSortedCols[i].maCells;
1097 51 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1098 : }
1099 :
1100 : {
1101 51 : sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1102 51 : sc::CellTextAttrStoreType& rSrc = aSortedCols[i].maCellTextAttrs;
1103 51 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1104 : }
1105 :
1106 : {
1107 51 : sc::CellNoteStoreType& rSrc = aSortedCols[i].maCellNotes;
1108 51 : sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1109 :
1110 : // Do the same as broadcaster storage transfer (to prevent double deletion).
1111 51 : rDest.release_range(nRow1, nRow2);
1112 51 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1113 51 : aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1114 : }
1115 :
1116 : {
1117 : // Get all row spans where the pattern is not NULL.
1118 : std::vector<PatternSpan> aSpans =
1119 : sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1120 51 : aSortedCols[i].maPatterns);
1121 :
1122 51 : std::vector<PatternSpan>::iterator it = aSpans.begin(), itEnd = aSpans.end();
1123 54 : for (; it != itEnd; ++it)
1124 : {
1125 : assert(it->mpPattern); // should never be NULL.
1126 3 : pDocument->GetPool()->Put(*it->mpPattern);
1127 : }
1128 :
1129 54 : for (it = aSpans.begin(); it != itEnd; ++it)
1130 : {
1131 3 : aCol[nThisCol].SetPatternArea(it->mnRow1, it->mnRow2, *it->mpPattern, true);
1132 3 : pDocument->GetPool()->Remove(*it->mpPattern);
1133 51 : }
1134 : }
1135 :
1136 51 : aCol[nThisCol].CellStorageModified();
1137 : }
1138 :
1139 19 : if (pArray->IsKeepQuery())
1140 : {
1141 0 : aRowFlags.maRowsHidden.build_tree();
1142 0 : aRowFlags.maRowsFiltered.build_tree();
1143 :
1144 : // Remove all flags in the range first.
1145 0 : SetRowHidden(nRow1, nRow2, false);
1146 0 : SetRowFiltered(nRow1, nRow2, false);
1147 :
1148 : std::vector<sc::RowSpan> aSpans =
1149 0 : sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1150 :
1151 0 : std::vector<sc::RowSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
1152 0 : for (; it != itEnd; ++it)
1153 0 : SetRowHidden(it->mnRow1, it->mnRow2, true);
1154 :
1155 0 : aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1156 :
1157 0 : it = aSpans.begin(), itEnd = aSpans.end();
1158 0 : for (; it != itEnd; ++it)
1159 0 : SetRowFiltered(it->mnRow1, it->mnRow2, true);
1160 : }
1161 :
1162 : // Notify the cells' listeners to (re-)start listening.
1163 : {
1164 19 : StartListeningNotifier aFunc;
1165 19 : std::for_each(aCellListeners.begin(), aCellListeners.end(), aFunc);
1166 : }
1167 :
1168 : // Re-group columns in the sorted range too.
1169 70 : for (SCCOL i = nCol1; i <= nCol2; ++i)
1170 51 : aCol[i].RegroupFormulaCells();
1171 :
1172 : {
1173 19 : sc::StartListeningContext aCxt(*pDocument);
1174 19 : AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1175 19 : }
1176 : }
1177 :
1178 28 : void ScTable::SortReorderByRowRefUpdate(
1179 : ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1180 : {
1181 : assert(pArray->IsUpdateRefs());
1182 :
1183 28 : if (nCol2 < nCol1)
1184 28 : return;
1185 :
1186 28 : SCROW nRow1 = pArray->GetStart();
1187 28 : SCROW nRow2 = pArray->GetLast();
1188 :
1189 28 : ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1190 28 : sc::ColumnSpanSet aGrpListenerRanges(false);
1191 :
1192 : {
1193 : // Get the range of formula group listeners within sorted range (if any).
1194 28 : sc::QueryRange aQuery;
1195 :
1196 28 : ScBroadcastAreaSlotMachine* pBASM = pDocument->GetBASM();
1197 : std::vector<sc::AreaListener> aGrpListeners =
1198 : pBASM->GetAllListeners(
1199 56 : aMoveRange, sc::AreaInsideOrOverlap, sc::ListenerGroup);
1200 :
1201 : {
1202 28 : std::vector<sc::AreaListener>::iterator it = aGrpListeners.begin(), itEnd = aGrpListeners.end();
1203 32 : for (; it != itEnd; ++it)
1204 : {
1205 : assert(it->mbGroupListening);
1206 4 : SvtListener* pGrpLis = it->mpListener;
1207 4 : pGrpLis->Query(aQuery);
1208 4 : pDocument->EndListeningArea(it->maArea, it->mbGroupListening, pGrpLis);
1209 : }
1210 : }
1211 :
1212 56 : ScRangeList aTmp;
1213 28 : aQuery.swapRanges(aTmp);
1214 :
1215 : // If the range is within the sorted range, we need to expand its rows
1216 : // to the top and bottom of the sorted range, since the formula cells
1217 : // could be anywhere in the sorted range after reordering.
1218 32 : for (size_t i = 0, n = aTmp.size(); i < n; ++i)
1219 : {
1220 4 : ScRange aRange = *aTmp[i];
1221 4 : if (!aMoveRange.Intersects(aRange))
1222 : {
1223 : // Doesn't overlap with the sorted range at all.
1224 0 : aGrpListenerRanges.set(aRange, true);
1225 4 : continue;
1226 : }
1227 :
1228 4 : if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
1229 : {
1230 : // Its column range is within the column range of the sorted range.
1231 4 : expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1232 4 : aGrpListenerRanges.set(aRange, true);
1233 4 : continue;
1234 : }
1235 :
1236 : // It intersects with the sorted range, but its column range is
1237 : // not within the column range of the sorted range. Split it into
1238 : // 2 ranges.
1239 0 : ScRange aR1 = aRange;
1240 0 : ScRange aR2 = aRange;
1241 0 : if (aRange.aStart.Col() < aMoveRange.aStart.Col())
1242 : {
1243 : // Left half is outside the sorted range while the right half is inside.
1244 0 : aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
1245 0 : aR2.aStart.SetCol(aMoveRange.aStart.Col());
1246 0 : expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1247 : }
1248 : else
1249 : {
1250 : // Left half is inside the sorted range while the right half is outside.
1251 0 : aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
1252 0 : aR2.aStart.SetCol(aMoveRange.aEnd.Col());
1253 0 : expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1254 : }
1255 :
1256 0 : aGrpListenerRanges.set(aR1, true);
1257 0 : aGrpListenerRanges.set(aR2, true);
1258 28 : }
1259 : }
1260 :
1261 : // Split formula groups at the sort range boundaries (if applicable).
1262 56 : std::vector<SCROW> aRowBounds;
1263 28 : aRowBounds.reserve(2);
1264 28 : aRowBounds.push_back(nRow1);
1265 28 : aRowBounds.push_back(nRow2+1);
1266 90 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1267 62 : SplitFormulaGroups(nCol, aRowBounds);
1268 :
1269 : // Cells in the data rows only reference values in the document. Make
1270 : // a copy before updating the document.
1271 56 : boost::ptr_vector<SortedColumn> aSortedCols; // storage for copied cells.
1272 56 : SortedRowFlags aRowFlags;
1273 28 : fillSortedColumnArray(aSortedCols, aRowFlags, pArray, nTab, nCol1, nCol2, pProgress);
1274 :
1275 90 : for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1276 : {
1277 62 : SCCOL nThisCol = i + nCol1;
1278 :
1279 : {
1280 62 : sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1281 62 : sc::CellStoreType& rSrc = aSortedCols[i].maCells;
1282 62 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1283 : }
1284 :
1285 : {
1286 62 : sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1287 62 : sc::CellTextAttrStoreType& rSrc = aSortedCols[i].maCellTextAttrs;
1288 62 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1289 : }
1290 :
1291 : {
1292 62 : sc::BroadcasterStoreType& rSrc = aSortedCols[i].maBroadcasters;
1293 62 : sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
1294 :
1295 : // Release current broadcasters first, to prevent them from getting deleted.
1296 62 : rDest.release_range(nRow1, nRow2);
1297 :
1298 : // Transfer sorted broadcaster segment to the document.
1299 62 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1300 : }
1301 :
1302 : {
1303 62 : sc::CellNoteStoreType& rSrc = aSortedCols[i].maCellNotes;
1304 62 : sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1305 :
1306 : // Do the same as broadcaster storage transfer (to prevent double deletion).
1307 62 : rDest.release_range(nRow1, nRow2);
1308 62 : rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1309 62 : aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1310 : }
1311 :
1312 : {
1313 : // Get all row spans where the pattern is not NULL.
1314 : std::vector<PatternSpan> aSpans =
1315 : sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1316 62 : aSortedCols[i].maPatterns);
1317 :
1318 62 : std::vector<PatternSpan>::iterator it = aSpans.begin(), itEnd = aSpans.end();
1319 62 : for (; it != itEnd; ++it)
1320 : {
1321 : assert(it->mpPattern); // should never be NULL.
1322 0 : pDocument->GetPool()->Put(*it->mpPattern);
1323 : }
1324 :
1325 62 : for (it = aSpans.begin(); it != itEnd; ++it)
1326 : {
1327 0 : aCol[nThisCol].SetPatternArea(it->mnRow1, it->mnRow2, *it->mpPattern, true);
1328 0 : pDocument->GetPool()->Remove(*it->mpPattern);
1329 62 : }
1330 : }
1331 :
1332 62 : aCol[nThisCol].CellStorageModified();
1333 : }
1334 :
1335 28 : if (pArray->IsKeepQuery())
1336 : {
1337 0 : aRowFlags.maRowsHidden.build_tree();
1338 0 : aRowFlags.maRowsFiltered.build_tree();
1339 :
1340 : // Remove all flags in the range first.
1341 0 : SetRowHidden(nRow1, nRow2, false);
1342 0 : SetRowFiltered(nRow1, nRow2, false);
1343 :
1344 : std::vector<sc::RowSpan> aSpans =
1345 0 : sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1346 :
1347 0 : std::vector<sc::RowSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
1348 0 : for (; it != itEnd; ++it)
1349 0 : SetRowHidden(it->mnRow1, it->mnRow2, true);
1350 :
1351 0 : aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1352 :
1353 0 : it = aSpans.begin(), itEnd = aSpans.end();
1354 0 : for (; it != itEnd; ++it)
1355 0 : SetRowFiltered(it->mnRow1, it->mnRow2, true);
1356 : }
1357 :
1358 : // Set up row reorder map (for later broadcasting of reference updates).
1359 56 : sc::ColRowReorderMapType aRowMap;
1360 28 : const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1361 177 : for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1362 : {
1363 149 : SCROW nNew = i + nRow1;
1364 149 : SCROW nOld = rOldIndices[i];
1365 149 : aRowMap.insert(sc::ColRowReorderMapType::value_type(nOld, nNew));
1366 : }
1367 :
1368 : // Collect all listeners within sorted range ahead of time.
1369 56 : std::vector<SvtListener*> aListeners;
1370 :
1371 : // Collect listeners of cell broadcasters.
1372 90 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1373 62 : aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1374 :
1375 : // Get all area listeners that listen on one row within the range and end
1376 : // their listening.
1377 : std::vector<sc::AreaListener> aAreaListeners = pDocument->GetBASM()->GetAllListeners(
1378 56 : aMoveRange, sc::OneRowInsideArea);
1379 : {
1380 28 : std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
1381 28 : for (; it != itEnd; ++it)
1382 : {
1383 0 : pDocument->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener);
1384 0 : aListeners.push_back( it->mpListener);
1385 : }
1386 : }
1387 :
1388 : {
1389 : // Get all formula cells from the former group area listener ranges.
1390 :
1391 28 : std::vector<ScFormulaCell*> aFCells;
1392 56 : FormulaCellCollectAction aAction(aFCells);
1393 28 : aGrpListenerRanges.executeColumnAction(*pDocument, aAction);
1394 :
1395 56 : std::copy(aFCells.begin(), aFCells.end(), std::back_inserter(aListeners));
1396 : }
1397 :
1398 : // Remove any duplicate listener entries. We must ensure that we notify
1399 : // each unique listener only once.
1400 28 : std::sort(aListeners.begin(), aListeners.end());
1401 28 : aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1402 :
1403 : // Collect positions of all shared formula cells outside the sorted range,
1404 : // and make them unshared before notifying them.
1405 56 : sc::RefQueryFormulaGroup aFormulaGroupPos;
1406 28 : aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1407 :
1408 28 : std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
1409 28 : const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
1410 28 : sc::RefQueryFormulaGroup::TabsType::const_iterator itGroupTab = rGroupTabs.begin(), itGroupTabEnd = rGroupTabs.end();
1411 33 : for (; itGroupTab != itGroupTabEnd; ++itGroupTab)
1412 : {
1413 5 : const sc::RefQueryFormulaGroup::ColsType& rCols = itGroupTab->second;
1414 5 : sc::RefQueryFormulaGroup::ColsType::const_iterator itCol = rCols.begin(), itColEnd = rCols.end();
1415 10 : for (; itCol != itColEnd; ++itCol)
1416 : {
1417 5 : const sc::RefQueryFormulaGroup::ColType& rCol = itCol->second;
1418 5 : std::vector<SCROW> aBounds(rCol);
1419 5 : pDocument->UnshareFormulaCells(itGroupTab->first, itCol->first, aBounds);
1420 5 : }
1421 : }
1422 :
1423 : // Notify the listeners to update their references.
1424 56 : RowReorderNotifier aFunc(aRowMap, nTab, nCol1, nCol2);
1425 28 : std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1426 :
1427 : // Re-group formulas in affected columns.
1428 33 : for (itGroupTab = rGroupTabs.begin(); itGroupTab != itGroupTabEnd; ++itGroupTab)
1429 : {
1430 5 : const sc::RefQueryFormulaGroup::ColsType& rCols = itGroupTab->second;
1431 5 : sc::RefQueryFormulaGroup::ColsType::const_iterator itCol = rCols.begin(), itColEnd = rCols.end();
1432 10 : for (; itCol != itColEnd; ++itCol)
1433 5 : pDocument->RegroupFormulaCells(itGroupTab->first, itCol->first);
1434 : }
1435 :
1436 : // Re-start area listeners on the reordered rows.
1437 : {
1438 28 : std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
1439 28 : for (; it != itEnd; ++it)
1440 : {
1441 0 : ScRange aNewRange = it->maArea;
1442 0 : sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
1443 0 : if (itRow != aRowMap.end())
1444 : {
1445 0 : aNewRange.aStart.SetRow( itRow->second);
1446 0 : aNewRange.aEnd.SetRow( itRow->second);
1447 : }
1448 0 : pDocument->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener);
1449 : }
1450 : }
1451 :
1452 : // Re-group columns in the sorted range too.
1453 90 : for (SCCOL i = nCol1; i <= nCol2; ++i)
1454 62 : aCol[i].RegroupFormulaCells();
1455 :
1456 : {
1457 : // Re-start area listeners on the old group listener ranges.
1458 28 : ListenerStartAction aAction(*pDocument);
1459 28 : aGrpListenerRanges.executeColumnAction(*pDocument, aAction);
1460 28 : }
1461 : }
1462 :
1463 594 : short ScTable::CompareCell(
1464 : sal_uInt16 nSort,
1465 : ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1466 : ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1467 : {
1468 594 : short nRes = 0;
1469 :
1470 594 : CellType eType1 = rCell1.meType, eType2 = rCell2.meType;
1471 :
1472 594 : if (!rCell1.isEmpty())
1473 : {
1474 583 : if (!rCell2.isEmpty())
1475 : {
1476 569 : bool bStr1 = ( eType1 != CELLTYPE_VALUE );
1477 569 : if (eType1 == CELLTYPE_FORMULA && rCell1.mpFormula->IsValue())
1478 123 : bStr1 = false;
1479 569 : bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1480 569 : if (eType2 == CELLTYPE_FORMULA && rCell2.mpFormula->IsValue())
1481 122 : bStr2 = false;
1482 :
1483 569 : if ( bStr1 && bStr2 ) // only compare strings as strings!
1484 : {
1485 69 : OUString aStr1;
1486 138 : OUString aStr2;
1487 69 : if (eType1 == CELLTYPE_STRING)
1488 35 : aStr1 = rCell1.mpString->getString();
1489 : else
1490 34 : GetString(nCell1Col, nCell1Row, aStr1);
1491 69 : if (eType2 == CELLTYPE_STRING)
1492 35 : aStr2 = rCell2.mpString->getString();
1493 : else
1494 34 : GetString(nCell2Col, nCell2Row, aStr2);
1495 :
1496 69 : bool bUserDef = aSortParam.bUserDef; // custom sort order
1497 69 : bool bNaturalSort = aSortParam.bNaturalSort; // natural sort
1498 69 : bool bCaseSens = aSortParam.bCaseSens; // case sensitivity
1499 :
1500 69 : if (bUserDef)
1501 : {
1502 0 : ScUserList* pList = ScGlobal::GetUserList();
1503 0 : const ScUserListData* pData = (*pList)[aSortParam.nUserIndex];
1504 :
1505 0 : if (pData)
1506 : {
1507 0 : if ( bNaturalSort )
1508 0 : nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, pData, pSortCollator );
1509 : else
1510 : {
1511 0 : if ( bCaseSens )
1512 0 : nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) );
1513 : else
1514 0 : nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) );
1515 : }
1516 : }
1517 : else
1518 0 : bUserDef = false;
1519 :
1520 : }
1521 69 : if (!bUserDef)
1522 : {
1523 69 : if ( bNaturalSort )
1524 0 : nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, NULL, pSortCollator );
1525 : else
1526 69 : nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
1527 69 : }
1528 : }
1529 500 : else if ( bStr1 ) // String <-> Number
1530 11 : nRes = 1; // Number in front
1531 489 : else if ( bStr2 ) // Number <-> String
1532 15 : nRes = -1; // Number in front
1533 : else // Mixed numbers
1534 : {
1535 474 : double nVal1 = rCell1.getValue();
1536 474 : double nVal2 = rCell2.getValue();
1537 474 : if (nVal1 < nVal2)
1538 203 : nRes = -1;
1539 271 : else if (nVal1 > nVal2)
1540 159 : nRes = 1;
1541 : }
1542 569 : if ( !aSortParam.maKeyState[nSort].bAscending )
1543 156 : nRes = -nRes;
1544 : }
1545 : else
1546 14 : nRes = -1;
1547 : }
1548 : else
1549 : {
1550 11 : if (!rCell2.isEmpty())
1551 3 : nRes = 1;
1552 : else
1553 8 : nRes = 0; // both empty
1554 : }
1555 594 : return nRes;
1556 : }
1557 :
1558 550 : short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1559 : {
1560 : short nRes;
1561 550 : sal_uInt16 nSort = 0;
1562 550 : do
1563 : {
1564 550 : ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 );
1565 550 : ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 );
1566 550 : if ( aSortParam.bByRow )
1567 : nRes = CompareCell( nSort,
1568 507 : pInfo1->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo1->nOrg,
1569 1014 : pInfo2->maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), pInfo2->nOrg );
1570 : else
1571 : nRes = CompareCell( nSort,
1572 43 : pInfo1->maCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.maKeyState[nSort].nField,
1573 86 : pInfo2->maCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.maKeyState[nSort].nField );
1574 550 : } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
1575 550 : if( nRes == 0 )
1576 : {
1577 144 : ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 );
1578 144 : ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 );
1579 144 : if( pInfo1->nOrg < pInfo2->nOrg )
1580 3 : nRes = -1;
1581 141 : else if( pInfo1->nOrg > pInfo2->nOrg )
1582 1 : nRes = 1;
1583 : }
1584 550 : return nRes;
1585 : }
1586 :
1587 155 : void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi )
1588 : {
1589 155 : if ((nHi - nLo) == 1)
1590 : {
1591 65 : if (Compare(pArray, nLo, nHi) > 0)
1592 28 : pArray->Swap( nLo, nHi );
1593 : }
1594 : else
1595 : {
1596 90 : SCsCOLROW ni = nLo;
1597 90 : SCsCOLROW nj = nHi;
1598 129 : do
1599 : {
1600 268 : while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1601 10 : ni++;
1602 475 : while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1603 217 : nj--;
1604 129 : if (ni <= nj)
1605 : {
1606 102 : if (ni != nj)
1607 70 : pArray->Swap( ni, nj );
1608 102 : ni++;
1609 102 : nj--;
1610 : }
1611 : } while (ni < nj);
1612 90 : if ((nj - nLo) < (nHi - ni))
1613 : {
1614 74 : if (nLo < nj)
1615 16 : QuickSort(pArray, nLo, nj);
1616 74 : if (ni < nHi)
1617 74 : QuickSort(pArray, ni, nHi);
1618 : }
1619 : else
1620 : {
1621 16 : if (ni < nHi)
1622 16 : QuickSort(pArray, ni, nHi);
1623 16 : if (nLo < nj)
1624 16 : QuickSort(pArray, nLo, nj);
1625 : }
1626 : }
1627 155 : }
1628 :
1629 44 : short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1630 : {
1631 : short nRes;
1632 44 : sal_uInt16 nSort = 0;
1633 44 : const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
1634 44 : if (aSortParam.bByRow)
1635 : {
1636 38 : do
1637 : {
1638 38 : SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
1639 38 : ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
1640 76 : ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
1641 76 : nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
1642 38 : } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1643 : }
1644 : else
1645 : {
1646 6 : do
1647 : {
1648 6 : SCROW nRow = aSortParam.maKeyState[nSort].nField;
1649 6 : ScRefCellValue aCell1 = aCol[nIndex1].GetCellValue(nRow);
1650 12 : ScRefCellValue aCell2 = aCol[nIndex2].GetCellValue(nRow);
1651 : nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
1652 12 : nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
1653 6 : } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1654 : }
1655 44 : return nRes;
1656 : }
1657 :
1658 34 : bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // over aSortParam
1659 : {
1660 45 : for (SCCOLROW i=nStart; i<nEnd; i++)
1661 : {
1662 44 : if (Compare( i, i+1 ) > 0)
1663 33 : return false;
1664 : }
1665 1 : return true;
1666 : }
1667 :
1668 0 : void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1669 : {
1670 : SCROW nRow;
1671 0 : int nMax = nRow2 - nRow1;
1672 0 : for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
1673 : {
1674 0 : nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
1675 0 : pArray->Swap(i, nRow1 + nRow);
1676 : }
1677 0 : }
1678 :
1679 35 : void ScTable::Sort(
1680 : const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1681 : ScProgress* pProgress, sc::ReorderParam* pUndo )
1682 : {
1683 35 : InitSortCollator( rSortParam );
1684 35 : bGlobalKeepQuery = bKeepQuery;
1685 :
1686 35 : if (pUndo)
1687 : {
1688 : // Copy over the basic sort parameters.
1689 29 : pUndo->mbByRow = rSortParam.bByRow;
1690 29 : pUndo->mbPattern = rSortParam.bIncludePattern;
1691 29 : pUndo->mbHiddenFiltered = bKeepQuery;
1692 29 : pUndo->mbUpdateRefs = bUpdateRefs;
1693 29 : pUndo->mbHasHeaders = rSortParam.bHasHeader;
1694 : }
1695 :
1696 : // It is assumed that the data area has already been trimmed as necessary.
1697 :
1698 35 : aSortParam = rSortParam; // must be assigned before calling IsSorted()
1699 35 : if (rSortParam.bByRow)
1700 : {
1701 31 : SCROW nLastRow = rSortParam.nRow2;
1702 31 : SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
1703 31 : if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
1704 : {
1705 29 : if(pProgress)
1706 23 : pProgress->SetState( 0, nLastRow-nRow1 );
1707 :
1708 29 : boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
1709 :
1710 29 : if ( nLastRow - nRow1 > 255 )
1711 0 : DecoladeRow(pArray.get(), nRow1, nLastRow);
1712 :
1713 29 : QuickSort(pArray.get(), nRow1, nLastRow);
1714 29 : if (pArray->IsUpdateRefs())
1715 15 : SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1716 : else
1717 14 : SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1718 :
1719 29 : if (pUndo)
1720 : {
1721 23 : pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1722 23 : pUndo->maOrderIndices = pArray->GetOrderIndices();
1723 29 : }
1724 : }
1725 : }
1726 : else
1727 : {
1728 4 : SCCOL nLastCol = rSortParam.nCol2;
1729 4 : SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1730 4 : if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1731 : {
1732 4 : if(pProgress)
1733 4 : pProgress->SetState( 0, nLastCol-nCol1 );
1734 :
1735 4 : boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
1736 :
1737 4 : QuickSort(pArray.get(), nCol1, nLastCol);
1738 4 : SortReorderByColumn(pArray.get(), aSortParam.nRow1, aSortParam.nRow2, aSortParam.bIncludePattern, pProgress);
1739 :
1740 4 : if (pUndo)
1741 : {
1742 4 : pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
1743 4 : pUndo->maOrderIndices = pArray->GetOrderIndices();
1744 4 : }
1745 : }
1746 : }
1747 35 : DestroySortCollator();
1748 35 : }
1749 :
1750 22 : void ScTable::Reorder( const sc::ReorderParam& rParam, ScProgress* pProgress )
1751 : {
1752 22 : if (rParam.maOrderIndices.empty())
1753 0 : return;
1754 :
1755 22 : boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1756 22 : if (!pArray)
1757 0 : return;
1758 :
1759 22 : if (rParam.mbByRow)
1760 : {
1761 : // Re-play sorting from the known sort indices.
1762 18 : pArray->ReorderByRow(rParam.maOrderIndices);
1763 18 : if (pArray->IsUpdateRefs())
1764 : SortReorderByRowRefUpdate(
1765 13 : pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), pProgress);
1766 : else
1767 : SortReorderByRow(
1768 5 : pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), pProgress);
1769 : }
1770 : else
1771 : {
1772 : // Ordering by column is much simpler. Just set the order indices and we are done.
1773 4 : pArray->SetOrderIndices(rParam.maOrderIndices);
1774 : SortReorderByColumn(
1775 : pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1776 4 : rParam.mbPattern, pProgress);
1777 22 : }
1778 : }
1779 :
1780 : namespace {
1781 :
1782 : class SubTotalRowFinder
1783 : {
1784 : const ScTable& mrTab;
1785 : const ScSubTotalParam& mrParam;
1786 :
1787 : public:
1788 8 : SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
1789 8 : mrTab(rTab), mrParam(rParam) {}
1790 :
1791 2 : bool operator() (size_t nRow, const ScFormulaCell* pCell)
1792 : {
1793 2 : if (!pCell->IsSubTotal())
1794 0 : return false;
1795 :
1796 2 : SCCOL nStartCol = mrParam.nCol1;
1797 2 : SCCOL nEndCol = mrParam.nCol2;
1798 :
1799 2050 : for (SCCOL i = 0; i <= MAXCOL; ++i)
1800 : {
1801 2048 : if (nStartCol <= i && i <= nEndCol)
1802 8 : continue;
1803 :
1804 2040 : if (mrTab.HasData(i, nRow))
1805 0 : return true;
1806 : }
1807 :
1808 2 : return false;
1809 : }
1810 : };
1811 :
1812 : }
1813 :
1814 2 : bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
1815 : {
1816 2 : SCCOL nStartCol = rParam.nCol1;
1817 2 : SCROW nStartRow = rParam.nRow1 + 1; // Header
1818 2 : SCCOL nEndCol = rParam.nCol2;
1819 2 : SCROW nEndRow = rParam.nRow2;
1820 :
1821 10 : for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1822 : {
1823 8 : const sc::CellStoreType& rCells = aCol[nCol].maCells;
1824 8 : SubTotalRowFinder aFunc(*this, rParam);
1825 : std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
1826 8 : sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
1827 8 : if (aPos.first != rCells.end())
1828 0 : return true;
1829 : }
1830 2 : return false;
1831 : }
1832 :
1833 : namespace {
1834 :
1835 4 : class RemoveSubTotalsHandler
1836 : {
1837 : std::vector<SCROW> maRemoved;
1838 : public:
1839 :
1840 2 : void operator() (size_t nRow, const ScFormulaCell* p)
1841 : {
1842 2 : if (p->IsSubTotal())
1843 2 : maRemoved.push_back(nRow);
1844 2 : }
1845 :
1846 2 : void getRows(std::vector<SCROW>& rRows)
1847 : {
1848 : // Sort and remove duplicates.
1849 2 : std::sort(maRemoved.begin(), maRemoved.end());
1850 2 : std::vector<SCROW>::iterator it = std::unique(maRemoved.begin(), maRemoved.end());
1851 2 : maRemoved.erase(it, maRemoved.end());
1852 :
1853 2 : maRemoved.swap(rRows);
1854 2 : }
1855 : };
1856 :
1857 : }
1858 :
1859 2 : void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
1860 : {
1861 2 : SCCOL nStartCol = rParam.nCol1;
1862 2 : SCROW nStartRow = rParam.nRow1 + 1; // Header
1863 2 : SCCOL nEndCol = rParam.nCol2;
1864 2 : SCROW nEndRow = rParam.nRow2; // will change
1865 :
1866 2 : RemoveSubTotalsHandler aFunc;
1867 10 : for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1868 : {
1869 8 : const sc::CellStoreType& rCells = aCol[nCol].maCells;
1870 8 : sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
1871 : }
1872 :
1873 4 : std::vector<SCROW> aRows;
1874 2 : aFunc.getRows(aRows);
1875 :
1876 2 : std::vector<SCROW>::reverse_iterator it = aRows.rbegin(), itEnd = aRows.rend();
1877 4 : for (; it != itEnd; ++it)
1878 : {
1879 2 : SCROW nRow = *it;
1880 2 : RemoveRowBreak(nRow+1, false, true);
1881 2 : pDocument->DeleteRow(0, nTab, MAXCOL, nTab, nRow, 1);
1882 : }
1883 :
1884 4 : rParam.nRow2 -= aRows.size();
1885 2 : }
1886 :
1887 : // Delete hard number formats (for result formulas)
1888 :
1889 0 : static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
1890 : {
1891 0 : const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
1892 0 : if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
1893 : == SfxItemState::SET )
1894 : {
1895 0 : ScPatternAttr aNewPattern( *pPattern );
1896 0 : SfxItemSet& rSet = aNewPattern.GetItemSet();
1897 0 : rSet.ClearItem( ATTR_VALUE_FORMAT );
1898 0 : rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
1899 0 : pTab->SetPattern( nCol, nRow, aNewPattern, true );
1900 : }
1901 0 : }
1902 :
1903 : // at least MSC needs this at linkage level to be able to use it in a template
1904 : typedef struct lcl_ScTable_DoSubTotals_RowEntry
1905 : {
1906 : sal_uInt16 nGroupNo;
1907 : SCROW nSubStartRow;
1908 : SCROW nDestRow;
1909 : SCROW nFuncStart;
1910 : SCROW nFuncEnd;
1911 : } RowEntry;
1912 :
1913 : // new intermediate results
1914 : // rParam.nRow2 is changed !
1915 :
1916 1 : bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
1917 : {
1918 1 : SCCOL nStartCol = rParam.nCol1;
1919 1 : SCROW nStartRow = rParam.nRow1 + 1; // Header
1920 1 : SCCOL nEndCol = rParam.nCol2;
1921 1 : SCROW nEndRow = rParam.nRow2; // will change
1922 : sal_uInt16 i;
1923 :
1924 : // Remove empty rows at the end
1925 : // so that all exceeding (MAXROW) can be found by InsertRow (#35180#)
1926 : // If sorted, all empty rows are at the end.
1927 1 : SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
1928 1 : nEndRow -= nEmpty;
1929 :
1930 1 : sal_uInt16 nLevelCount = 0; // Number of levels
1931 1 : bool bDoThis = true;
1932 3 : for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
1933 2 : if (rParam.bGroupActive[i])
1934 1 : nLevelCount = i+1;
1935 : else
1936 1 : bDoThis = false;
1937 :
1938 1 : if (nLevelCount==0) // do nothing
1939 0 : return true;
1940 :
1941 1 : SCCOL* nGroupCol = rParam.nField; // columns which will be used when grouping
1942 :
1943 : // With (blank) as a separate category, subtotal rows from
1944 : // the other columns must always be tested
1945 : // (previously only when a column occured more than once)
1946 1 : bool bTestPrevSub = ( nLevelCount > 1 );
1947 :
1948 1 : OUString aSubString;
1949 2 : OUString aOutString;
1950 :
1951 1 : bool bIgnoreCase = !rParam.bCaseSens;
1952 :
1953 : OUString *pCompString[MAXSUBTOTAL]; // Pointer due to compiler problemens
1954 4 : for (i=0; i<MAXSUBTOTAL; i++)
1955 3 : pCompString[i] = new OUString;
1956 :
1957 : //TODO: sort?
1958 :
1959 1 : ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(pDocument->GetStyleSheetPool()->Find(
1960 1 : ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA ));
1961 :
1962 1 : bool bSpaceLeft = true; // Succsess when inserting?
1963 :
1964 : // For performance reasons collect formula entries so their
1965 : // references don't have to be tested for updates each time a new row is
1966 : // inserted
1967 : RowEntry aRowEntry;
1968 2 : ::std::vector< RowEntry > aRowVector;
1969 :
1970 3 : for (sal_uInt16 nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++) // including grand total
1971 : {
1972 2 : bool bTotal = ( nLevel == nLevelCount );
1973 2 : aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1);
1974 :
1975 : // how many results per level
1976 2 : SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
1977 : // result functions
1978 2 : ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo];
1979 :
1980 2 : if (nResCount > 0) // otherwise only sort
1981 : {
1982 4 : for (i=0; i<=aRowEntry.nGroupNo; i++)
1983 : {
1984 2 : GetString( nGroupCol[i], nStartRow, aSubString );
1985 2 : if ( bIgnoreCase )
1986 2 : *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
1987 : else
1988 0 : *pCompString[i] = aSubString;
1989 : } // aSubString stays on the last
1990 :
1991 2 : bool bBlockVis = false; // group visible?
1992 2 : aRowEntry.nSubStartRow = nStartRow;
1993 9 : for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
1994 : {
1995 : bool bChanged;
1996 7 : if (nRow>nEndRow)
1997 2 : bChanged = true;
1998 : else
1999 : {
2000 5 : bChanged = false;
2001 5 : if (!bTotal)
2002 : {
2003 2 : OUString aString;
2004 4 : for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
2005 : {
2006 2 : GetString( nGroupCol[i], nRow, aString );
2007 2 : if (bIgnoreCase)
2008 2 : aString = ScGlobal::pCharClass->uppercase(aString);
2009 : // when sorting, blanks are seperate group
2010 : // otherwise blak cells are allowed below
2011 4 : bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
2012 4 : aString != *pCompString[i] );
2013 : }
2014 2 : if ( bChanged && bTestPrevSub )
2015 : {
2016 : // No group change on rows that will contain subtotal formulas
2017 0 : for ( ::std::vector< RowEntry >::const_iterator
2018 0 : iEntry( aRowVector.begin());
2019 0 : iEntry != aRowVector.end(); ++iEntry)
2020 : {
2021 0 : if ( iEntry->nDestRow == nRow )
2022 : {
2023 0 : bChanged = false;
2024 0 : break;
2025 : }
2026 : }
2027 2 : }
2028 : }
2029 : }
2030 7 : if ( bChanged )
2031 : {
2032 2 : aRowEntry.nDestRow = nRow;
2033 2 : aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
2034 2 : aRowEntry.nFuncEnd = nRow-1;
2035 :
2036 : bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab,
2037 2 : aRowEntry.nDestRow, 1 );
2038 2 : DBShowRow( aRowEntry.nDestRow, bBlockVis );
2039 2 : bBlockVis = false;
2040 2 : if ( rParam.bPagebreak && nRow < MAXROW &&
2041 0 : aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2042 0 : SetRowBreak(aRowEntry.nSubStartRow, false, true);
2043 :
2044 2 : if (bSpaceLeft)
2045 : {
2046 9 : for ( ::std::vector< RowEntry >::iterator iMove(
2047 2 : aRowVector.begin() );
2048 6 : iMove != aRowVector.end(); ++iMove)
2049 : {
2050 1 : if ( aRowEntry.nDestRow <= iMove->nSubStartRow )
2051 0 : ++iMove->nSubStartRow;
2052 1 : if ( aRowEntry.nDestRow <= iMove->nDestRow )
2053 0 : ++iMove->nDestRow;
2054 1 : if ( aRowEntry.nDestRow <= iMove->nFuncStart )
2055 0 : ++iMove->nFuncStart;
2056 1 : if ( aRowEntry.nDestRow <= iMove->nFuncEnd )
2057 0 : ++iMove->nFuncEnd;
2058 : }
2059 : // collect formula positions
2060 2 : aRowVector.push_back( aRowEntry );
2061 :
2062 2 : if (bTotal) // "Grand total"
2063 1 : aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS );
2064 : else
2065 : { // "Result"
2066 1 : aOutString = aSubString;
2067 1 : if (aOutString.isEmpty())
2068 0 : aOutString = ScGlobal::GetRscString( STR_EMPTYDATA );
2069 1 : aOutString += " ";
2070 1 : sal_uInt16 nStrId = STR_TABLE_ERGEBNIS;
2071 1 : if ( nResCount == 1 )
2072 1 : switch ( eResFunc[0] )
2073 : {
2074 0 : case SUBTOTAL_FUNC_AVE: nStrId = STR_FUN_TEXT_AVG; break;
2075 : case SUBTOTAL_FUNC_CNT:
2076 0 : case SUBTOTAL_FUNC_CNT2: nStrId = STR_FUN_TEXT_COUNT; break;
2077 0 : case SUBTOTAL_FUNC_MAX: nStrId = STR_FUN_TEXT_MAX; break;
2078 0 : case SUBTOTAL_FUNC_MIN: nStrId = STR_FUN_TEXT_MIN; break;
2079 0 : case SUBTOTAL_FUNC_PROD: nStrId = STR_FUN_TEXT_PRODUCT; break;
2080 : case SUBTOTAL_FUNC_STD:
2081 0 : case SUBTOTAL_FUNC_STDP: nStrId = STR_FUN_TEXT_STDDEV; break;
2082 1 : case SUBTOTAL_FUNC_SUM: nStrId = STR_FUN_TEXT_SUM; break;
2083 : case SUBTOTAL_FUNC_VAR:
2084 0 : case SUBTOTAL_FUNC_VARP: nStrId = STR_FUN_TEXT_VAR; break;
2085 : default:
2086 : {
2087 : // added to avoid warnings
2088 : }
2089 : }
2090 1 : aOutString += ScGlobal::GetRscString( nStrId );
2091 : }
2092 2 : SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
2093 2 : ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle );
2094 :
2095 2 : ++nRow;
2096 2 : ++nEndRow;
2097 2 : aRowEntry.nSubStartRow = nRow;
2098 4 : for (i=0; i<=aRowEntry.nGroupNo; i++)
2099 : {
2100 2 : GetString( nGroupCol[i], nRow, aSubString );
2101 2 : if ( bIgnoreCase )
2102 2 : *pCompString[i] = ScGlobal::pCharClass->uppercase( aSubString );
2103 : else
2104 0 : *pCompString[i] = aSubString;
2105 : }
2106 : }
2107 : }
2108 7 : bBlockVis = !RowFiltered(nRow);
2109 : }
2110 : }
2111 : }
2112 :
2113 : // now insert the formulas
2114 : ScComplexRefData aRef;
2115 1 : aRef.InitFlags();
2116 1 : aRef.Ref1.SetAbsTab(nTab);
2117 1 : aRef.Ref2.SetAbsTab(nTab);
2118 9 : for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin());
2119 6 : iEntry != aRowVector.end(); ++iEntry)
2120 : {
2121 2 : SCCOL nResCount = rParam.nSubTotals[iEntry->nGroupNo];
2122 2 : SCCOL* nResCols = rParam.pSubTotals[iEntry->nGroupNo];
2123 2 : ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo];
2124 4 : for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
2125 : {
2126 2 : aRef.Ref1.SetAbsCol(nResCols[nResult]);
2127 2 : aRef.Ref1.SetAbsRow(iEntry->nFuncStart);
2128 2 : aRef.Ref2.SetAbsCol(nResCols[nResult]);
2129 2 : aRef.Ref2.SetAbsRow(iEntry->nFuncEnd);
2130 :
2131 2 : ScTokenArray aArr;
2132 2 : aArr.AddOpCode( ocSubTotal );
2133 2 : aArr.AddOpCode( ocOpen );
2134 2 : aArr.AddDouble( (double) eResFunc[nResult] );
2135 2 : aArr.AddOpCode( ocSep );
2136 2 : aArr.AddDoubleReference( aRef );
2137 2 : aArr.AddOpCode( ocClose );
2138 2 : aArr.AddOpCode( ocStop );
2139 : ScFormulaCell* pCell = new ScFormulaCell(
2140 2 : pDocument, ScAddress(nResCols[nResult], iEntry->nDestRow, nTab), aArr);
2141 :
2142 2 : SetFormulaCell(nResCols[nResult], iEntry->nDestRow, pCell);
2143 :
2144 2 : if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] )
2145 : {
2146 0 : ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle );
2147 :
2148 0 : lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow );
2149 : }
2150 2 : }
2151 :
2152 : }
2153 :
2154 : //TODO: according to setting, shift intermediate-sum rows up ?
2155 :
2156 : //TODO: create Outlines directly?
2157 :
2158 1 : if (bSpaceLeft)
2159 1 : DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2160 :
2161 4 : for (i=0; i<MAXSUBTOTAL; i++)
2162 3 : delete pCompString[i];
2163 :
2164 1 : rParam.nRow2 = nEndRow; // new end
2165 2 : return bSpaceLeft;
2166 : }
2167 :
2168 : namespace {
2169 :
2170 : class QueryEvaluator
2171 : {
2172 : ScDocument& mrDoc;
2173 : svl::SharedStringPool& mrStrPool;
2174 : const ScTable& mrTab;
2175 : const ScQueryParam& mrParam;
2176 : const bool* mpTestEqualCondition;
2177 : utl::TransliterationWrapper* mpTransliteration;
2178 : CollatorWrapper* mpCollator;
2179 : const bool mbMatchWholeCell;
2180 :
2181 830 : static bool isPartialTextMatchOp(const ScQueryEntry& rEntry)
2182 : {
2183 830 : switch (rEntry.eOp)
2184 : {
2185 : // these operators can only be used with textural comparisons.
2186 : case SC_CONTAINS:
2187 : case SC_DOES_NOT_CONTAIN:
2188 : case SC_BEGINS_WITH:
2189 : case SC_ENDS_WITH:
2190 : case SC_DOES_NOT_BEGIN_WITH:
2191 : case SC_DOES_NOT_END_WITH:
2192 0 : return true;
2193 : default:
2194 : ;
2195 : }
2196 830 : return false;
2197 : }
2198 :
2199 569 : static bool isTextMatchOp(const ScQueryEntry& rEntry)
2200 : {
2201 569 : if (isPartialTextMatchOp(rEntry))
2202 0 : return true;
2203 :
2204 569 : switch (rEntry.eOp)
2205 : {
2206 : // these operators can be used for either textural or value comparison.
2207 : case SC_EQUAL:
2208 : case SC_NOT_EQUAL:
2209 376 : return true;
2210 : default:
2211 : ;
2212 : }
2213 193 : return false;
2214 : }
2215 :
2216 261 : bool isRealRegExp(const ScQueryEntry& rEntry) const
2217 : {
2218 261 : if (!mrParam.bRegExp)
2219 237 : return false;
2220 :
2221 24 : return isTextMatchOp(rEntry);
2222 : }
2223 :
2224 261 : bool isTestRegExp(const ScQueryEntry& rEntry) const
2225 : {
2226 261 : if (!mpTestEqualCondition)
2227 191 : return false;
2228 :
2229 70 : if (!mrParam.bRegExp)
2230 70 : return false;
2231 :
2232 0 : return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
2233 : }
2234 :
2235 : public:
2236 1049 : QueryEvaluator(ScDocument& rDoc, const ScTable& rTab, const ScQueryParam& rParam,
2237 : const bool* pTestEqualCondition) :
2238 : mrDoc(rDoc),
2239 1049 : mrStrPool(rDoc.GetSharedStringPool()),
2240 : mrTab(rTab),
2241 : mrParam(rParam),
2242 : mpTestEqualCondition(pTestEqualCondition),
2243 2098 : mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell())
2244 : {
2245 1049 : if (rParam.bCaseSens)
2246 : {
2247 0 : mpTransliteration = ScGlobal::GetCaseTransliteration();
2248 0 : mpCollator = ScGlobal::GetCaseCollator();
2249 : }
2250 : else
2251 : {
2252 1049 : mpTransliteration = ScGlobal::GetpTransliteration();
2253 1049 : mpCollator = ScGlobal::GetCollator();
2254 : }
2255 1049 : }
2256 :
2257 1070 : bool isQueryByValue(
2258 : const ScQueryEntry::Item& rItem, SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
2259 : {
2260 1070 : if (rItem.meType == ScQueryEntry::ByString)
2261 294 : return false;
2262 :
2263 776 : if (!rCell.isEmpty())
2264 : {
2265 774 : if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
2266 : // Error values are compared as string.
2267 0 : return false;
2268 :
2269 774 : return rCell.hasNumeric();
2270 : }
2271 :
2272 2 : return mrTab.HasValueData(nCol, nRow);
2273 : }
2274 :
2275 308 : bool isQueryByString(
2276 : const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem,
2277 : SCCOL nCol, SCROW nRow, ScRefCellValue& rCell)
2278 : {
2279 308 : if (isTextMatchOp(rEntry))
2280 188 : return true;
2281 :
2282 120 : if (rItem.meType != ScQueryEntry::ByString)
2283 7 : return false;
2284 :
2285 113 : if (!rCell.isEmpty())
2286 111 : return rCell.hasString();
2287 :
2288 2 : return mrTab.HasStringData(nCol, nRow);
2289 : }
2290 :
2291 762 : std::pair<bool,bool> compareByValue(
2292 : const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2293 : const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
2294 : {
2295 762 : bool bOk = false;
2296 762 : bool bTestEqual = false;
2297 : double nCellVal;
2298 762 : if (!rCell.isEmpty())
2299 : {
2300 762 : switch (rCell.meType)
2301 : {
2302 : case CELLTYPE_VALUE :
2303 420 : nCellVal = rCell.mfValue;
2304 420 : break;
2305 : case CELLTYPE_FORMULA :
2306 342 : nCellVal = rCell.mpFormula->GetValue();
2307 342 : break;
2308 : default:
2309 0 : nCellVal = 0.0;
2310 : }
2311 :
2312 : }
2313 : else
2314 0 : nCellVal = mrTab.GetValue(nCol, nRow);
2315 :
2316 : /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
2317 : * date+time format was queried rEntry.bQueryByDate is not set. In
2318 : * case other queries wanted to use this mechanism they should do
2319 : * the same, in other words only if rEntry.nVal is an integer value
2320 : * rEntry.bQueryByDate should be true and the time fraction be
2321 : * stripped here. */
2322 762 : if (rItem.meType == ScQueryEntry::ByDate)
2323 : {
2324 0 : sal_uInt32 nNumFmt = mrTab.GetNumberFormat(nCol, nRow);
2325 0 : const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nNumFmt);
2326 0 : if (pEntry)
2327 : {
2328 0 : short nNumFmtType = pEntry->GetType();
2329 : /* NOTE: Omitting the check for absence of
2330 : * css::util::NumberFormat::TIME would include also date+time formatted
2331 : * values of the same day. That may be desired in some
2332 : * cases, querying all time values of a day, but confusing
2333 : * in other cases. A user can always setup a standard
2334 : * filter query for x >= date AND x < date+1 */
2335 0 : if ((nNumFmtType & css::util::NumberFormat::DATE) && !(nNumFmtType & css::util::NumberFormat::TIME))
2336 : {
2337 : // The format is of date type. Strip off the time
2338 : // element.
2339 0 : nCellVal = ::rtl::math::approxFloor(nCellVal);
2340 : }
2341 : }
2342 : }
2343 :
2344 762 : switch (rEntry.eOp)
2345 : {
2346 : case SC_EQUAL :
2347 296 : bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2348 296 : break;
2349 : case SC_LESS :
2350 39 : bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2351 39 : break;
2352 : case SC_GREATER :
2353 40 : bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2354 40 : break;
2355 : case SC_LESS_EQUAL :
2356 174 : bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2357 174 : if ( bOk && mpTestEqualCondition )
2358 103 : bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2359 174 : break;
2360 : case SC_GREATER_EQUAL :
2361 213 : bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual( nCellVal, rItem.mfVal);
2362 213 : if ( bOk && mpTestEqualCondition )
2363 85 : bTestEqual = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2364 213 : break;
2365 : case SC_NOT_EQUAL :
2366 0 : bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
2367 0 : break;
2368 : default:
2369 : {
2370 : // added to avoid warnings
2371 : }
2372 : }
2373 :
2374 762 : return std::pair<bool,bool>(bOk, bTestEqual);
2375 : }
2376 :
2377 261 : std::pair<bool,bool> compareByString(
2378 : ScRefCellValue& rCell, SCROW nRow, const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
2379 : {
2380 261 : bool bOk = false;
2381 261 : bool bTestEqual = false;
2382 261 : bool bMatchWholeCell = mbMatchWholeCell;
2383 261 : svl::SharedString aCellStr;
2384 261 : if (isPartialTextMatchOp(rEntry))
2385 : // may have to do partial textural comparison.
2386 0 : bMatchWholeCell = false;
2387 :
2388 261 : if (!rCell.isEmpty())
2389 : {
2390 260 : if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
2391 : {
2392 : // Error cell is evaluated as string (for now).
2393 0 : aCellStr = mrStrPool.intern(ScGlobal::GetErrorString(rCell.mpFormula->GetErrCode()));
2394 : }
2395 260 : else if (rCell.meType == CELLTYPE_STRING)
2396 227 : aCellStr = *rCell.mpString;
2397 : else
2398 : {
2399 33 : sal_uLong nFormat = mrTab.GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow );
2400 33 : OUString aStr;
2401 33 : ScCellFormat::GetInputString(rCell, nFormat, aStr, *mrDoc.GetFormatTable(), &mrDoc);
2402 33 : aCellStr = mrStrPool.intern(aStr);
2403 : }
2404 : }
2405 : else
2406 : {
2407 1 : OUString aStr;
2408 1 : mrTab.GetInputString(static_cast<SCCOL>(rEntry.nField), nRow, aStr);
2409 1 : aCellStr = mrStrPool.intern(aStr);
2410 : }
2411 :
2412 261 : bool bRealRegExp = isRealRegExp(rEntry);
2413 261 : bool bTestRegExp = isTestRegExp(rEntry);
2414 :
2415 261 : if ( bRealRegExp || bTestRegExp )
2416 : {
2417 24 : sal_Int32 nStart = 0;
2418 24 : sal_Int32 nEnd = aCellStr.getLength();
2419 :
2420 : // from 614 on, nEnd is behind the found text
2421 24 : bool bMatch = false;
2422 24 : if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH )
2423 : {
2424 0 : nEnd = 0;
2425 0 : nStart = aCellStr.getLength();
2426 : bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
2427 0 : ->SearchBackward(aCellStr.getString(), &nStart, &nEnd);
2428 : }
2429 : else
2430 : {
2431 : bMatch = rEntry.GetSearchTextPtr( mrParam.bCaseSens )
2432 24 : ->SearchForward(aCellStr.getString(), &nStart, &nEnd);
2433 : }
2434 31 : if ( bMatch && bMatchWholeCell
2435 31 : && (nStart != 0 || nEnd != aCellStr.getLength()) )
2436 0 : bMatch = false; // RegExp must match entire cell string
2437 24 : if ( bRealRegExp )
2438 24 : switch (rEntry.eOp)
2439 : {
2440 : case SC_EQUAL:
2441 : case SC_CONTAINS:
2442 20 : bOk = bMatch;
2443 20 : break;
2444 : case SC_NOT_EQUAL:
2445 : case SC_DOES_NOT_CONTAIN:
2446 4 : bOk = !bMatch;
2447 4 : break;
2448 : case SC_BEGINS_WITH:
2449 0 : bOk = ( bMatch && (nStart == 0) );
2450 0 : break;
2451 : case SC_DOES_NOT_BEGIN_WITH:
2452 0 : bOk = !( bMatch && (nStart == 0) );
2453 0 : break;
2454 : case SC_ENDS_WITH:
2455 0 : bOk = ( bMatch && (nEnd == aCellStr.getLength()) );
2456 0 : break;
2457 : case SC_DOES_NOT_END_WITH:
2458 0 : bOk = !( bMatch && (nEnd == aCellStr.getLength()) );
2459 0 : break;
2460 : default:
2461 : {
2462 : // added to avoid warnings
2463 : }
2464 : }
2465 : else
2466 0 : bTestEqual = bMatch;
2467 : }
2468 261 : if ( !bRealRegExp )
2469 : {
2470 : // Simple string matching i.e. no regexp match.
2471 237 : if (isTextMatchOp(rEntry))
2472 : {
2473 164 : if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
2474 : {
2475 : // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
2476 : // the query value is assigned directly, and the string is empty. In that case,
2477 : // don't find any string (isEqual would find empty string results in formula cells).
2478 7 : bOk = false;
2479 7 : if ( rEntry.eOp == SC_NOT_EQUAL )
2480 0 : bOk = !bOk;
2481 : }
2482 157 : else if ( bMatchWholeCell )
2483 : {
2484 : // Fast string equality check by comparing string identifiers.
2485 157 : if (mrParam.bCaseSens)
2486 0 : bOk = aCellStr.getData() == rItem.maString.getData();
2487 : else
2488 157 : bOk = aCellStr.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
2489 :
2490 157 : if ( rEntry.eOp == SC_NOT_EQUAL )
2491 0 : bOk = !bOk;
2492 : }
2493 : else
2494 : {
2495 0 : OUString aQueryStr = rItem.maString.getString();
2496 : OUString aCell( mpTransliteration->transliterate(
2497 : aCellStr.getString(), ScGlobal::eLnge, 0, aCellStr.getLength(),
2498 0 : NULL ) );
2499 : OUString aQuer( mpTransliteration->transliterate(
2500 : aQueryStr, ScGlobal::eLnge, 0, aQueryStr.getLength(),
2501 0 : NULL ) );
2502 0 : sal_Int32 nIndex = (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) ?
2503 0 : (aCell.getLength() - aQuer.getLength()) : 0;
2504 0 : sal_Int32 nStrPos = ((nIndex < 0) ? -1 : aCell.indexOf( aQuer, nIndex ));
2505 0 : switch (rEntry.eOp)
2506 : {
2507 : case SC_EQUAL:
2508 : case SC_CONTAINS:
2509 0 : bOk = ( nStrPos != -1 );
2510 0 : break;
2511 : case SC_NOT_EQUAL:
2512 : case SC_DOES_NOT_CONTAIN:
2513 0 : bOk = ( nStrPos == -1 );
2514 0 : break;
2515 : case SC_BEGINS_WITH:
2516 0 : bOk = ( nStrPos == 0 );
2517 0 : break;
2518 : case SC_DOES_NOT_BEGIN_WITH:
2519 0 : bOk = ( nStrPos != 0 );
2520 0 : break;
2521 : case SC_ENDS_WITH:
2522 0 : bOk = (nStrPos >= 0 && nStrPos + aQuer.getLength() == aCell.getLength() );
2523 0 : break;
2524 : case SC_DOES_NOT_END_WITH:
2525 0 : bOk = (nStrPos < 0 || nStrPos + aQuer.getLength() != aCell.getLength() );
2526 0 : break;
2527 : default:
2528 : {
2529 : // added to avoid warnings
2530 : }
2531 0 : }
2532 : }
2533 : }
2534 : else
2535 : { // use collator here because data was probably sorted
2536 : sal_Int32 nCompare = mpCollator->compareString(
2537 73 : aCellStr.getString(), rItem.maString.getString());
2538 73 : switch (rEntry.eOp)
2539 : {
2540 : case SC_LESS :
2541 3 : bOk = (nCompare < 0);
2542 3 : break;
2543 : case SC_GREATER :
2544 0 : bOk = (nCompare > 0);
2545 0 : break;
2546 : case SC_LESS_EQUAL :
2547 51 : bOk = (nCompare <= 0);
2548 51 : if ( bOk && mpTestEqualCondition && !bTestEqual )
2549 44 : bTestEqual = (nCompare == 0);
2550 51 : break;
2551 : case SC_GREATER_EQUAL :
2552 19 : bOk = (nCompare >= 0);
2553 19 : if ( bOk && mpTestEqualCondition && !bTestEqual )
2554 13 : bTestEqual = (nCompare == 0);
2555 19 : break;
2556 : default:
2557 : {
2558 : // added to avoid warnings
2559 : }
2560 : }
2561 : }
2562 : }
2563 :
2564 261 : return std::pair<bool,bool>(bOk, bTestEqual);
2565 : }
2566 :
2567 : // To be called only if both isQueryByValue() and isQueryByString()
2568 : // returned false and range lookup is wanted! In range lookup comparison
2569 : // numbers are less than strings. Nothing else is compared.
2570 34 : std::pair<bool,bool> compareByRangeLookup(
2571 : const ScRefCellValue& rCell, SCCOL nCol, SCROW nRow,
2572 : const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem)
2573 : {
2574 34 : bool bTestEqual = false;
2575 :
2576 34 : if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS && rEntry.eOp != SC_LESS_EQUAL)
2577 1 : return std::pair<bool,bool>(false, bTestEqual);
2578 :
2579 33 : if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER && rEntry.eOp != SC_GREATER_EQUAL)
2580 0 : return std::pair<bool,bool>(false, bTestEqual);
2581 :
2582 33 : if (!rCell.isEmpty())
2583 : {
2584 33 : if (rItem.meType == ScQueryEntry::ByString)
2585 : {
2586 33 : if (rCell.meType == CELLTYPE_FORMULA && rCell.mpFormula->GetErrCode())
2587 : // Error values are compared as string.
2588 0 : return std::pair<bool,bool>(false, bTestEqual);
2589 :
2590 33 : return std::pair<bool,bool>(rCell.hasNumeric(), bTestEqual);
2591 : }
2592 :
2593 0 : return std::pair<bool,bool>(!rCell.hasNumeric(), bTestEqual);
2594 : }
2595 :
2596 0 : if (rItem.meType == ScQueryEntry::ByString)
2597 0 : return std::pair<bool,bool>(mrTab.HasValueData(nCol, nRow), bTestEqual);
2598 :
2599 0 : return std::pair<bool,bool>(!mrTab.HasValueData(nCol, nRow), bTestEqual);
2600 : }
2601 : };
2602 :
2603 : }
2604 :
2605 1068 : bool ScTable::ValidQuery(
2606 : SCROW nRow, const ScQueryParam& rParam, ScRefCellValue* pCell, bool* pbTestEqualCondition)
2607 : {
2608 1068 : if (!rParam.GetEntry(0).bDoQuery)
2609 19 : return true;
2610 :
2611 1049 : SCSIZE nEntryCount = rParam.GetEntryCount();
2612 :
2613 : typedef std::pair<bool,bool> ResultType;
2614 1049 : static std::vector<ResultType> aResults;
2615 1049 : if (aResults.size() < nEntryCount)
2616 5 : aResults.resize(nEntryCount);
2617 :
2618 1049 : long nPos = -1;
2619 1049 : QueryEvaluator aEval(*pDocument, *this, rParam, pbTestEqualCondition);
2620 1049 : ScQueryParam::const_iterator it, itBeg = rParam.begin(), itEnd = rParam.end();
2621 2131 : for (it = itBeg; it != itEnd && it->bDoQuery; ++it)
2622 : {
2623 1082 : const ScQueryEntry& rEntry = *it;
2624 1082 : SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
2625 :
2626 : // we can only handle one single direct query
2627 1082 : ScRefCellValue aCell;
2628 1082 : if (pCell && it == itBeg)
2629 975 : aCell = *pCell;
2630 : else
2631 107 : aCell = GetCellValue(nCol, nRow);
2632 :
2633 1082 : std::pair<bool,bool> aRes(false, false);
2634 :
2635 1082 : const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2636 1082 : if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
2637 : {
2638 22 : if (rEntry.IsQueryByEmpty())
2639 11 : aRes.first = !aCol[rEntry.nField].HasDataAt(nRow);
2640 : else
2641 : {
2642 : OSL_ASSERT(rEntry.IsQueryByNonEmpty());
2643 11 : aRes.first = aCol[rEntry.nField].HasDataAt(nRow);
2644 : }
2645 : }
2646 : else
2647 : {
2648 1060 : ScQueryEntry::QueryItemsType::const_iterator itr = rItems.begin(), itrEnd = rItems.end();
2649 :
2650 2100 : for (; itr != itrEnd; ++itr)
2651 : {
2652 1070 : if (aEval.isQueryByValue(*itr, nCol, nRow, aCell))
2653 : {
2654 : std::pair<bool,bool> aThisRes =
2655 762 : aEval.compareByValue(aCell, nCol, nRow, rEntry, *itr);
2656 762 : aRes.first |= aThisRes.first;
2657 762 : aRes.second |= aThisRes.second;
2658 : }
2659 308 : else if (aEval.isQueryByString(rEntry, *itr, nCol, nRow, aCell))
2660 : {
2661 : std::pair<bool,bool> aThisRes =
2662 261 : aEval.compareByString(aCell, nRow, rEntry, *itr);
2663 261 : aRes.first |= aThisRes.first;
2664 261 : aRes.second |= aThisRes.second;
2665 : }
2666 47 : else if (rParam.mbRangeLookup)
2667 : {
2668 : std::pair<bool,bool> aThisRes =
2669 34 : aEval.compareByRangeLookup(aCell, nCol, nRow, rEntry, *itr);
2670 34 : aRes.first |= aThisRes.first;
2671 34 : aRes.second |= aThisRes.second;
2672 : }
2673 :
2674 1070 : if (aRes.first && aRes.second)
2675 30 : break;
2676 : }
2677 : }
2678 :
2679 1082 : if (nPos == -1)
2680 : {
2681 1049 : nPos++;
2682 1049 : aResults[nPos] = aRes;
2683 : }
2684 : else
2685 : {
2686 33 : if (rEntry.eConnect == SC_AND)
2687 : {
2688 29 : aResults[nPos].first = aResults[nPos].first && aRes.first;
2689 29 : aResults[nPos].second = aResults[nPos].second && aRes.second;
2690 : }
2691 : else
2692 : {
2693 4 : nPos++;
2694 4 : aResults[nPos] = aRes;
2695 : }
2696 : }
2697 1082 : }
2698 :
2699 1053 : for ( long j=1; j <= nPos; j++ )
2700 : {
2701 4 : aResults[0].first = aResults[0].first || aResults[j].first;
2702 4 : aResults[0].second = aResults[0].second || aResults[j].second;
2703 : }
2704 :
2705 1049 : bool bRet = aResults[0].first;
2706 1049 : if ( pbTestEqualCondition )
2707 339 : *pbTestEqualCondition = aResults[0].second;
2708 :
2709 1049 : return bRet;
2710 : }
2711 :
2712 0 : void ScTable::TopTenQuery( ScQueryParam& rParam )
2713 : {
2714 0 : bool bSortCollatorInitialized = false;
2715 0 : SCSIZE nEntryCount = rParam.GetEntryCount();
2716 0 : SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
2717 0 : SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
2718 0 : for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
2719 : {
2720 0 : ScQueryEntry& rEntry = rParam.GetEntry(i);
2721 0 : ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
2722 :
2723 0 : switch ( rEntry.eOp )
2724 : {
2725 : case SC_TOPVAL:
2726 : case SC_BOTVAL:
2727 : case SC_TOPPERC:
2728 : case SC_BOTPERC:
2729 : {
2730 0 : ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) );
2731 0 : aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
2732 0 : if ( !bSortCollatorInitialized )
2733 : {
2734 0 : bSortCollatorInitialized = true;
2735 0 : InitSortCollator( aLocalSortParam );
2736 : }
2737 0 : boost::scoped_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
2738 0 : DecoladeRow( pArray.get(), nRow1, rParam.nRow2 );
2739 0 : QuickSort( pArray.get(), nRow1, rParam.nRow2 );
2740 0 : ScSortInfo** ppInfo = pArray->GetFirstArray();
2741 0 : SCSIZE nValidCount = nCount;
2742 : // Don't count note or blank cells, they are sorted to the end
2743 0 : while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.isEmpty())
2744 0 : nValidCount--;
2745 : // Don't count Strings, they are between Value and blank
2746 0 : while (nValidCount > 0 && ppInfo[nValidCount-1]->maCell.hasString())
2747 0 : nValidCount--;
2748 0 : if ( nValidCount > 0 )
2749 : {
2750 0 : if ( rItem.meType == ScQueryEntry::ByString )
2751 : { // by string ain't going to work
2752 0 : rItem.meType = ScQueryEntry::ByValue;
2753 0 : rItem.mfVal = 10; // 10 and 10% respectively
2754 : }
2755 0 : SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
2756 0 : SCSIZE nOffset = 0;
2757 0 : switch ( rEntry.eOp )
2758 : {
2759 : case SC_TOPVAL:
2760 : {
2761 0 : rEntry.eOp = SC_GREATER_EQUAL;
2762 0 : if ( nVal > nValidCount )
2763 0 : nVal = nValidCount;
2764 0 : nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
2765 : }
2766 0 : break;
2767 : case SC_BOTVAL:
2768 : {
2769 0 : rEntry.eOp = SC_LESS_EQUAL;
2770 0 : if ( nVal > nValidCount )
2771 0 : nVal = nValidCount;
2772 0 : nOffset = nVal - 1; // 1 <= nVal <= nValidCount
2773 : }
2774 0 : break;
2775 : case SC_TOPPERC:
2776 : {
2777 0 : rEntry.eOp = SC_GREATER_EQUAL;
2778 0 : if ( nVal > 100 )
2779 0 : nVal = 100;
2780 0 : nOffset = nValidCount - (nValidCount * nVal / 100);
2781 0 : if ( nOffset >= nValidCount )
2782 0 : nOffset = nValidCount - 1;
2783 : }
2784 0 : break;
2785 : case SC_BOTPERC:
2786 : {
2787 0 : rEntry.eOp = SC_LESS_EQUAL;
2788 0 : if ( nVal > 100 )
2789 0 : nVal = 100;
2790 0 : nOffset = (nValidCount * nVal / 100);
2791 0 : if ( nOffset >= nValidCount )
2792 0 : nOffset = nValidCount - 1;
2793 : }
2794 0 : break;
2795 : default:
2796 : {
2797 : // added to avoid warnings
2798 : }
2799 : }
2800 0 : ScRefCellValue aCell = ppInfo[nOffset]->maCell;
2801 0 : if (aCell.hasNumeric())
2802 0 : rItem.mfVal = aCell.getValue();
2803 : else
2804 : {
2805 : OSL_FAIL( "TopTenQuery: pCell no ValueData" );
2806 0 : rEntry.eOp = SC_GREATER_EQUAL;
2807 0 : rItem.mfVal = 0;
2808 0 : }
2809 : }
2810 : else
2811 : {
2812 0 : rEntry.eOp = SC_GREATER_EQUAL;
2813 0 : rItem.meType = ScQueryEntry::ByValue;
2814 0 : rItem.mfVal = 0;
2815 0 : }
2816 : }
2817 0 : break;
2818 : default:
2819 : {
2820 : // added to avoid warnings
2821 : }
2822 : }
2823 : }
2824 0 : if ( bSortCollatorInitialized )
2825 0 : DestroySortCollator();
2826 0 : }
2827 :
2828 : namespace {
2829 :
2830 : class PrepareQueryItem : public std::unary_function<ScQueryEntry::Item, void>
2831 : {
2832 : const ScDocument& mrDoc;
2833 : public:
2834 16 : PrepareQueryItem(const ScDocument& rDoc) : mrDoc(rDoc) {}
2835 :
2836 18 : void operator() (ScQueryEntry::Item& rItem)
2837 : {
2838 18 : if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
2839 27 : return;
2840 :
2841 9 : sal_uInt32 nIndex = 0;
2842 : bool bNumber = mrDoc.GetFormatTable()->
2843 9 : IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
2844 :
2845 : // Advanced Filter creates only ByString queries that need to be
2846 : // converted to ByValue if appropriate. rItem.mfVal now holds the value
2847 : // if bNumber==true.
2848 :
2849 9 : if (rItem.meType == ScQueryEntry::ByString)
2850 : {
2851 9 : if (bNumber)
2852 3 : rItem.meType = ScQueryEntry::ByValue;
2853 9 : return;
2854 : }
2855 :
2856 : // Double-check if the query by date is really appropriate.
2857 :
2858 0 : if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
2859 : {
2860 0 : const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
2861 0 : if (pEntry)
2862 : {
2863 0 : short nNumFmtType = pEntry->GetType();
2864 0 : if (!((nNumFmtType & css::util::NumberFormat::DATE) && !(nNumFmtType & css::util::NumberFormat::TIME)))
2865 0 : rItem.meType = ScQueryEntry::ByValue; // not a date only
2866 : }
2867 : else
2868 0 : rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
2869 : }
2870 : else
2871 0 : rItem.meType = ScQueryEntry::ByValue; // not a date
2872 : }
2873 : };
2874 :
2875 18 : void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam )
2876 : {
2877 18 : bool bTopTen = false;
2878 18 : SCSIZE nEntryCount = rParam.GetEntryCount();
2879 :
2880 162 : for ( SCSIZE i = 0; i < nEntryCount; ++i )
2881 : {
2882 144 : ScQueryEntry& rEntry = rParam.GetEntry(i);
2883 144 : if (!rEntry.bDoQuery)
2884 128 : continue;
2885 :
2886 16 : ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2887 16 : std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc));
2888 :
2889 16 : if ( !bTopTen )
2890 : {
2891 16 : switch ( rEntry.eOp )
2892 : {
2893 : case SC_TOPVAL:
2894 : case SC_BOTVAL:
2895 : case SC_TOPPERC:
2896 : case SC_BOTPERC:
2897 : {
2898 0 : bTopTen = true;
2899 : }
2900 0 : break;
2901 : default:
2902 : {
2903 : }
2904 : }
2905 : }
2906 : }
2907 :
2908 18 : if ( bTopTen )
2909 : {
2910 0 : pTab->TopTenQuery( rParam );
2911 : }
2912 18 : }
2913 :
2914 : }
2915 :
2916 18 : SCSIZE ScTable::Query(ScQueryParam& rParamOrg, bool bKeepSub)
2917 : {
2918 18 : ScQueryParam aParam( rParamOrg );
2919 : typedef std::unordered_set<OUString, OUStringHash> StrSetType;
2920 36 : StrSetType aStrSet;
2921 :
2922 18 : bool bStarted = false;
2923 18 : bool bOldResult = true;
2924 18 : SCROW nOldStart = 0;
2925 18 : SCROW nOldEnd = 0;
2926 :
2927 18 : SCSIZE nCount = 0;
2928 18 : SCROW nOutRow = 0;
2929 18 : SCROW nHeader = aParam.bHasHeader ? 1 : 0;
2930 :
2931 18 : lcl_PrepareQuery(pDocument, this, aParam);
2932 :
2933 18 : if (!aParam.bInplace)
2934 : {
2935 0 : nOutRow = aParam.nDestRow + nHeader;
2936 0 : if (nHeader > 0)
2937 : CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
2938 0 : aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
2939 : }
2940 :
2941 18 : SCROW nRealRow2 = aParam.nRow2;
2942 104 : for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
2943 : {
2944 : bool bResult; // Filter result
2945 86 : bool bValid = ValidQuery(j, aParam);
2946 86 : if (!bValid && bKeepSub) // Keep subtotals
2947 : {
2948 24 : for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
2949 : {
2950 18 : ScRefCellValue aCell = GetCellValue(nCol, j);
2951 18 : if (aCell.meType != CELLTYPE_FORMULA)
2952 18 : continue;
2953 :
2954 0 : if (!aCell.mpFormula->IsSubTotal())
2955 0 : continue;
2956 :
2957 0 : if (RefVisible(aCell.mpFormula))
2958 0 : bValid = true;
2959 0 : }
2960 : }
2961 86 : if (bValid)
2962 : {
2963 52 : if (aParam.bDuplicate)
2964 52 : bResult = true;
2965 : else
2966 : {
2967 0 : OUString aStr;
2968 0 : for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
2969 : {
2970 0 : OUString aCellStr;
2971 0 : GetString(k, j, aCellStr);
2972 0 : OUStringBuffer aBuf(aStr);
2973 0 : aBuf.append(aCellStr);
2974 0 : aBuf.append(static_cast<sal_Unicode>(1));
2975 0 : aStr = aBuf.makeStringAndClear();
2976 0 : }
2977 :
2978 0 : std::pair<StrSetType::iterator, bool> r = aStrSet.insert(aStr);
2979 0 : bool bIsUnique = r.second; // unique if inserted.
2980 0 : bResult = bIsUnique;
2981 : }
2982 : }
2983 : else
2984 34 : bResult = false;
2985 :
2986 86 : if (aParam.bInplace)
2987 : {
2988 86 : if (bResult == bOldResult && bStarted)
2989 40 : nOldEnd = j;
2990 : else
2991 : {
2992 46 : if (bStarted)
2993 28 : DBShowRows(nOldStart,nOldEnd, bOldResult);
2994 46 : nOldStart = nOldEnd = j;
2995 46 : bOldResult = bResult;
2996 : }
2997 86 : bStarted = true;
2998 : }
2999 : else
3000 : {
3001 0 : if (bResult)
3002 : {
3003 0 : CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
3004 0 : ++nOutRow;
3005 : }
3006 : }
3007 86 : if (bResult)
3008 52 : ++nCount;
3009 : }
3010 :
3011 18 : if (aParam.bInplace && bStarted)
3012 18 : DBShowRows(nOldStart,nOldEnd, bOldResult);
3013 :
3014 18 : if (aParam.bInplace)
3015 18 : SetDrawPageSize();
3016 :
3017 36 : return nCount;
3018 : }
3019 :
3020 1 : bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3021 : {
3022 1 : bool bValid = true;
3023 1 : boost::scoped_array<SCCOL> pFields(new SCCOL[nCol2-nCol1+1]);
3024 2 : OUString aCellStr;
3025 1 : SCCOL nCol = nCol1;
3026 : OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3027 1 : SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3028 1 : SCROW nDBRow1 = rQueryParam.nRow1;
3029 1 : SCCOL nDBCol2 = rQueryParam.nCol2;
3030 : // First row must be column headers
3031 6 : while (bValid && (nCol <= nCol2))
3032 : {
3033 4 : OUString aQueryStr;
3034 4 : GetUpperCellString(nCol, nRow1, aQueryStr);
3035 4 : bool bFound = false;
3036 4 : SCCOL i = rQueryParam.nCol1;
3037 12 : while (!bFound && (i <= nDBCol2))
3038 : {
3039 4 : if ( nTab == nDBTab )
3040 4 : GetUpperCellString(i, nDBRow1, aCellStr);
3041 : else
3042 0 : pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr);
3043 4 : bFound = (aCellStr == aQueryStr);
3044 4 : if (!bFound) i++;
3045 : }
3046 4 : if (bFound)
3047 4 : pFields[nCol - nCol1] = i;
3048 : else
3049 0 : bValid = false;
3050 4 : nCol++;
3051 4 : }
3052 1 : if (bValid)
3053 : {
3054 1 : sal_uLong nVisible = 0;
3055 5 : for ( nCol=nCol1; nCol<=nCol2; nCol++ )
3056 4 : nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
3057 :
3058 1 : if ( nVisible > SCSIZE_MAX / sizeof(void*) )
3059 : {
3060 : OSL_FAIL("too many filter criteria");
3061 0 : nVisible = 0;
3062 : }
3063 :
3064 1 : SCSIZE nNewEntries = nVisible;
3065 1 : rQueryParam.Resize( nNewEntries );
3066 :
3067 1 : SCSIZE nIndex = 0;
3068 1 : SCROW nRow = nRow1 + 1;
3069 1 : svl::SharedStringPool& rPool = pDocument->GetSharedStringPool();
3070 6 : while (nRow <= nRow2)
3071 : {
3072 4 : nCol = nCol1;
3073 24 : while (nCol <= nCol2)
3074 : {
3075 16 : GetInputString( nCol, nRow, aCellStr );
3076 16 : if (!aCellStr.isEmpty())
3077 : {
3078 0 : if (nIndex < nNewEntries)
3079 : {
3080 0 : rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
3081 0 : rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, NULL);
3082 0 : nIndex++;
3083 0 : if (nIndex < nNewEntries)
3084 0 : rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
3085 : }
3086 : else
3087 0 : bValid = false;
3088 : }
3089 16 : nCol++;
3090 : }
3091 4 : nRow++;
3092 4 : if (nIndex < nNewEntries)
3093 0 : rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
3094 : }
3095 : }
3096 2 : return bValid;
3097 : }
3098 :
3099 1 : bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3100 : {
3101 : // A valid StarQuery must be at least 4 columns wide. To be precise it
3102 : // should be exactly 4 columns ...
3103 : // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
3104 : // column Excel style query range immediately left to itself would result
3105 : // in a circular reference when the field name or operator or value (first
3106 : // to third query range column) is obtained (#i58354#). Furthermore, if the
3107 : // range wasn't sufficiently specified data changes wouldn't flag formula
3108 : // cells for recalculation.
3109 1 : if (nCol2 - nCol1 < 3)
3110 0 : return false;
3111 :
3112 : bool bValid;
3113 : bool bFound;
3114 1 : OUString aCellStr;
3115 1 : SCSIZE nIndex = 0;
3116 1 : SCROW nRow = nRow1;
3117 : OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
3118 1 : SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
3119 1 : SCROW nDBRow1 = rQueryParam.nRow1;
3120 1 : SCCOL nDBCol2 = rQueryParam.nCol2;
3121 :
3122 1 : SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
3123 1 : rQueryParam.Resize( nNewEntries );
3124 1 : svl::SharedStringPool& rPool = pDocument->GetSharedStringPool();
3125 :
3126 2 : do
3127 : {
3128 2 : ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
3129 :
3130 2 : bValid = false;
3131 : // First column AND/OR
3132 2 : if (nIndex > 0)
3133 : {
3134 1 : GetUpperCellString(nCol1, nRow, aCellStr);
3135 1 : if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) )
3136 : {
3137 0 : rEntry.eConnect = SC_AND;
3138 0 : bValid = true;
3139 : }
3140 1 : else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) )
3141 : {
3142 0 : rEntry.eConnect = SC_OR;
3143 0 : bValid = true;
3144 : }
3145 : }
3146 : // Second column field name
3147 2 : if ((nIndex < 1) || bValid)
3148 : {
3149 1 : bFound = false;
3150 1 : GetUpperCellString(nCol1 + 1, nRow, aCellStr);
3151 2 : for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
3152 : {
3153 1 : OUString aFieldStr;
3154 1 : if ( nTab == nDBTab )
3155 1 : GetUpperCellString(i, nDBRow1, aFieldStr);
3156 : else
3157 0 : pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr);
3158 1 : bFound = (aCellStr == aFieldStr);
3159 1 : if (bFound)
3160 : {
3161 1 : rEntry.nField = i;
3162 1 : bValid = true;
3163 : }
3164 : else
3165 0 : bValid = false;
3166 1 : }
3167 : }
3168 : // Third column operator =<>...
3169 2 : if (bValid)
3170 : {
3171 1 : bFound = false;
3172 1 : GetUpperCellString(nCol1 + 2, nRow, aCellStr);
3173 1 : if (aCellStr.startsWith("<"))
3174 : {
3175 0 : if (aCellStr[1] == '>')
3176 0 : rEntry.eOp = SC_NOT_EQUAL;
3177 0 : else if (aCellStr[1] == '=')
3178 0 : rEntry.eOp = SC_LESS_EQUAL;
3179 : else
3180 0 : rEntry.eOp = SC_LESS;
3181 : }
3182 1 : else if (aCellStr.startsWith(">"))
3183 : {
3184 0 : if (aCellStr[1] == '=')
3185 0 : rEntry.eOp = SC_GREATER_EQUAL;
3186 : else
3187 0 : rEntry.eOp = SC_GREATER;
3188 : }
3189 1 : else if (aCellStr.startsWith("="))
3190 0 : rEntry.eOp = SC_EQUAL;
3191 :
3192 : }
3193 : // Fourth column values
3194 2 : if (bValid)
3195 : {
3196 1 : OUString aStr;
3197 1 : GetString(nCol1 + 3, nRow, aStr);
3198 1 : rEntry.GetQueryItem().maString = rPool.intern(aStr);
3199 1 : rEntry.bDoQuery = true;
3200 : }
3201 2 : nIndex++;
3202 2 : nRow++;
3203 : }
3204 1 : while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
3205 1 : return bValid;
3206 : }
3207 :
3208 1 : bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
3209 : {
3210 : SCSIZE i, nCount;
3211 1 : PutInOrder(nCol1, nCol2);
3212 1 : PutInOrder(nRow1, nRow2);
3213 :
3214 1 : nCount = rQueryParam.GetEntryCount();
3215 9 : for (i=0; i < nCount; i++)
3216 8 : rQueryParam.GetEntry(i).Clear();
3217 :
3218 : // Standard query table
3219 1 : bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3220 : // Excel Query table
3221 1 : if (!bValid)
3222 1 : bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
3223 :
3224 1 : nCount = rQueryParam.GetEntryCount();
3225 1 : if (bValid)
3226 : {
3227 : // bQueryByString must be set
3228 9 : for (i=0; i < nCount; i++)
3229 8 : rQueryParam.GetEntry(i).GetQueryItem().meType = ScQueryEntry::ByString;
3230 : }
3231 : else
3232 : {
3233 : // nix
3234 0 : for (i=0; i < nCount; i++)
3235 0 : rQueryParam.GetEntry(i).Clear();
3236 : }
3237 1 : return bValid;
3238 : }
3239 :
3240 9 : bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ ) const
3241 : {
3242 18 : for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
3243 : {
3244 16 : CellType eType = GetCellType( nCol, nStartRow );
3245 16 : if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3246 7 : return false;
3247 : }
3248 2 : return true;
3249 : }
3250 :
3251 0 : bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow ) const
3252 : {
3253 0 : for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
3254 : {
3255 0 : CellType eType = GetCellType( nStartCol, nRow );
3256 0 : if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
3257 0 : return false;
3258 : }
3259 0 : return true;
3260 : }
3261 :
3262 0 : void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScTypedStrData>& rStrings, bool& rHasDates)
3263 : {
3264 0 : sc::ColumnBlockConstPosition aBlockPos;
3265 0 : aCol[nCol].InitBlockPosition(aBlockPos);
3266 0 : aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rStrings, rHasDates);
3267 0 : }
3268 :
3269 0 : void ScTable::GetFilteredFilterEntries(
3270 : SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, std::vector<ScTypedStrData>& rStrings, bool& rHasDates)
3271 : {
3272 0 : sc::ColumnBlockConstPosition aBlockPos;
3273 0 : aCol[nCol].InitBlockPosition(aBlockPos);
3274 :
3275 : // remove the entry for this column from the query parameter
3276 0 : ScQueryParam aParam( rParam );
3277 0 : aParam.RemoveEntryByField(nCol);
3278 :
3279 0 : lcl_PrepareQuery(pDocument, this, aParam);
3280 0 : bool bHasDates = false;
3281 0 : for ( SCROW j = nRow1; j <= nRow2; ++j )
3282 : {
3283 0 : if (ValidQuery(j, aParam))
3284 : {
3285 0 : bool bThisHasDates = false;
3286 0 : aCol[nCol].GetFilterEntries(aBlockPos, j, j, rStrings, bThisHasDates);
3287 0 : bHasDates |= bThisHasDates;
3288 : }
3289 : }
3290 :
3291 0 : rHasDates = bHasDates;
3292 0 : }
3293 :
3294 2 : bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings, bool bLimit)
3295 : {
3296 2 : return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit );
3297 : }
3298 :
3299 35 : sal_uLong ScTable::GetCellCount() const
3300 : {
3301 35 : sal_uLong nCellCount = 0;
3302 :
3303 35875 : for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
3304 35840 : nCellCount += aCol[nCol].GetCellCount();
3305 :
3306 35 : return nCellCount;
3307 : }
3308 :
3309 861 : sal_uLong ScTable::GetWeightedCount() const
3310 : {
3311 861 : sal_uLong nCellCount = 0;
3312 :
3313 882525 : for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
3314 881664 : if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
3315 4478 : nCellCount += aCol[nCol].GetWeightedCount();
3316 :
3317 861 : return nCellCount;
3318 : }
3319 :
3320 0 : sal_uLong ScTable::GetCodeCount() const
3321 : {
3322 0 : sal_uLong nCodeCount = 0;
3323 :
3324 0 : for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ )
3325 0 : if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline
3326 0 : nCodeCount += aCol[nCol].GetCodeCount();
3327 :
3328 0 : return nCodeCount;
3329 : }
3330 :
3331 0 : sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
3332 : SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
3333 : {
3334 0 : if ( ValidCol(nCol) )
3335 0 : return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
3336 : else
3337 0 : return 0;
3338 : }
3339 :
3340 0 : sal_Int32 ScTable::GetMaxNumberStringLen(
3341 : sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
3342 : {
3343 0 : if ( ValidCol(nCol) )
3344 0 : return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
3345 : else
3346 0 : return 0;
3347 : }
3348 :
3349 212 : void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
3350 : {
3351 212 : ScRangeList aRanges = rMark.GetMarkedRanges();
3352 217300 : for (SCCOL nCol = 0; nCol <= MAXCOL && !rData.bError; ++nCol)
3353 : {
3354 217088 : if (pColFlags && ColHidden(nCol))
3355 0 : continue;
3356 :
3357 217088 : aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3358 212 : }
3359 368 : }
3360 :
3361 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|