tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / table6.cxx
blobe6919485f0f42a458ee8a15e1e434d666de92db5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <unotools/textsearch.hxx>
21 #include <com/sun/star/util/SearchResult.hpp>
22 #include <svl/srchitem.hxx>
23 #include <editeng/editobj.hxx>
24 #include <osl/diagnose.h>
25 #include <sal/log.hxx>
27 #include <table.hxx>
28 #include <formulacell.hxx>
29 #include <document.hxx>
30 #include <stlpool.hxx>
31 #include <stlsheet.hxx>
32 #include <markdata.hxx>
33 #include <editutil.hxx>
34 #include <postit.hxx>
36 namespace {
38 void lcl_GetTextWithBreaks( const EditTextObject& rData, ScDocument* pDoc, OUString& rVal )
40 EditEngine& rEngine = pDoc->GetEditEngine();
41 rEngine.SetText(rData);
42 rVal = rEngine.GetText();
47 bool ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow,
48 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
50 if ( !IsColRowValid( nCol, nRow ) )
51 return false;
53 bool bFound = false;
54 bool bDoSearch = true;
55 bool bDoBack = rSearchItem.GetBackward();
56 bool bSearchFormatted = rSearchItem.IsSearchFormatted();
58 OUString aString;
59 ScRefCellValue aCell;
60 if (rSearchItem.GetSelection())
61 bDoSearch = rMark.IsCellMarked(nCol, nRow);
63 if (!bDoSearch)
64 return false;
66 ScPostIt* pNote;
67 if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
69 pNote = aCol[nCol].GetCellNote(rBlockPos, nRow);
70 if (!pNote)
71 return false;
73 else
75 aCell = aCol[nCol].GetCellValue(rBlockPos, nRow);
76 if (aCell.isEmpty())
77 return false;
78 pNote = nullptr;
81 CellType eCellType = aCell.getType();
82 switch (rSearchItem.GetCellType())
84 case SvxSearchCellType::FORMULA:
86 if ( eCellType == CELLTYPE_FORMULA )
87 aString = aCell.getFormula()->GetFormula(rDocument.GetGrammar());
88 else if ( eCellType == CELLTYPE_EDIT )
89 lcl_GetTextWithBreaks(*aCell.getEditText(), &rDocument, aString);
90 else
92 if( !bSearchFormatted )
93 aString = aCol[nCol].GetInputString( rBlockPos, nRow );
94 else
95 aString = aCol[nCol].GetString( rBlockPos, nRow );
97 break;
99 case SvxSearchCellType::VALUE:
100 if ( eCellType == CELLTYPE_EDIT )
101 lcl_GetTextWithBreaks(*aCell.getEditText(), &rDocument, aString);
102 else
104 if( !bSearchFormatted )
105 aString = aCol[nCol].GetInputString( rBlockPos, nRow );
106 else
107 aString = aCol[nCol].GetString( rBlockPos, nRow );
109 break;
110 case SvxSearchCellType::NOTE:
112 if (pNote)
113 aString = pNote->GetText();
114 break;
116 default:
117 break;
119 sal_Int32 nStart = 0;
120 sal_Int32 nEnd = aString.getLength();
121 css::util::SearchResult aSearchResult;
122 if (pSearchText)
124 if ( bDoBack )
126 sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
127 bFound = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
128 // change results to definition before 614:
129 --nEnd;
131 else
133 bFound = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
134 // change results to definition before 614:
135 --nEnd;
138 if (bFound && rSearchItem.GetWordOnly())
139 bFound = (nStart == 0 && nEnd == aString.getLength() - 1);
141 else
143 OSL_FAIL("pSearchText == NULL");
144 return bFound;
147 if (!bFound)
148 return false;
149 if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE
150 && rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL )
151 return bFound;
153 if (!IsBlockEditable(nCol, nRow, nCol, nRow))
154 return bFound;
156 ScMatrixMode cMatrixFlag = ScMatrixMode::NONE;
158 // Don't split the matrix, only replace Matrix formulas
159 if (eCellType == CELLTYPE_FORMULA)
161 cMatrixFlag = aCell.getFormula()->GetMatrixFlag();
162 if(cMatrixFlag == ScMatrixMode::Reference)
163 return bFound;
165 // No UndoDoc => Matrix not restorable => don't replace
166 if (cMatrixFlag != ScMatrixMode::NONE && !pUndoDoc)
167 return bFound;
169 if ( cMatrixFlag == ScMatrixMode::NONE && rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
170 rUndoStr = aString;
171 else if (pUndoDoc)
173 ScAddress aAdr( nCol, nRow, nTab );
174 aCell.commit(*pUndoDoc, aAdr);
177 bool bRepeat = !rSearchItem.GetWordOnly();
180 // don't continue search if the found text is empty,
181 // otherwise it would never stop (#35410#)
182 if ( nEnd < nStart )
183 bRepeat = false;
185 OUString sReplStr = rSearchItem.GetReplaceString();
186 if (rSearchItem.GetRegExp())
188 utl::TextSearch::ReplaceBackReferences( sReplStr, aString, aSearchResult );
189 OUStringBuffer aStrBuffer(aString);
190 aStrBuffer.remove(nStart, nEnd-nStart+1);
191 aStrBuffer.insert(nStart, sReplStr);
192 aString = aStrBuffer.makeStringAndClear();
194 else
196 OUStringBuffer aStrBuffer(aString);
197 aStrBuffer.remove(nStart, nEnd-nStart+1);
198 aStrBuffer.insert(nStart, rSearchItem.GetReplaceString());
199 aString = aStrBuffer.makeStringAndClear();
202 // Adjust index
203 if (bDoBack)
205 nEnd = nStart;
206 nStart = 0;
208 else
210 nStart = nStart + sReplStr.getLength();
211 nEnd = aString.getLength();
214 // continue search ?
215 if (bRepeat)
217 if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL || nStart >= nEnd )
218 bRepeat = false;
219 else if (bDoBack)
221 sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
222 bRepeat = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
223 // change results to definition before 614:
224 --nEnd;
226 else
228 bRepeat = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
229 // change results to definition before 614:
230 --nEnd;
234 while (bRepeat);
235 if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
237 // NB: rich text format is lost.
238 // This is also true of Cells.
239 if (pNote)
240 pNote->SetText( ScAddress( nCol, nRow, nTab ), aString );
242 else if ( cMatrixFlag != ScMatrixMode::NONE )
243 { // don't split Matrix
244 if ( aString.getLength() > 2 )
245 { // remove {} here so that "{=" can be replaced by "{=..."
246 if ( aString[ aString.getLength()-1 ] == '}' )
247 aString = aString.copy( 0, aString.getLength()-1 );
248 if ( aString[0] == '{' )
249 aString = aString.copy( 1 );
251 ScAddress aAdr( nCol, nRow, nTab );
252 ScFormulaCell* pFCell = new ScFormulaCell( rDocument, aAdr,
253 aString, rDocument.GetGrammar(), cMatrixFlag );
254 SCCOL nMatCols;
255 SCROW nMatRows;
256 aCell.getFormula()->GetMatColsRows(nMatCols, nMatRows);
257 pFCell->SetMatColsRows( nMatCols, nMatRows );
258 aCol[nCol].SetFormulaCell(nRow, pFCell);
260 else if (eCellType != CELLTYPE_FORMULA && aString.indexOf('\n') != -1)
262 ScFieldEditEngine& rEngine = rDocument.GetEditEngine();
263 rEngine.SetTextCurrentDefaults(aString);
264 SetEditText(nCol, nRow, rEngine.CreateTextObject());
266 else
267 aCol[nCol].SetString(nRow, nTab, aString, rDocument.GetAddressConvention());
268 // pCell is invalid now (deleted)
269 aCol[nCol].InitBlockPosition( rBlockPos ); // invalidate also the cached position
271 return bFound;
274 void ScTable::SkipFilteredRows(SCROW& rRow, SCROW& rLastNonFilteredRow, bool bForward)
276 if (bForward)
278 // forward search
280 if (rRow <= rLastNonFilteredRow)
281 return;
283 SCROW nLastRow = rRow;
284 if (RowFiltered(rRow, nullptr, &nLastRow))
285 // move to the first non-filtered row.
286 rRow = nLastRow + 1;
287 else
288 // record the last non-filtered row to avoid checking
289 // the filtered state for each and every row.
290 rLastNonFilteredRow = nLastRow;
292 else
294 // backward search
296 if (rRow >= rLastNonFilteredRow)
297 return;
299 SCROW nFirstRow = rRow;
300 if (RowFiltered(rRow, &nFirstRow))
301 // move to the first non-filtered row.
302 rRow = nFirstRow - 1;
303 else
304 // record the last non-filtered row to avoid checking
305 // the filtered state for each and every row.
306 rLastNonFilteredRow = nFirstRow;
310 bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
311 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
313 SCCOL nLastCol;
314 SCROW nLastRow;
315 if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
316 GetCellArea( nLastCol, nLastRow);
317 else
318 GetLastDataPos(nLastCol, nLastRow);
319 std::vector< sc::ColumnBlockConstPosition > blockPos;
320 return Search(rSearchItem, rCol, rRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
323 bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
324 SCCOL nLastCol, SCROW nLastRow,
325 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc,
326 std::vector< sc::ColumnBlockConstPosition >& blockPos)
328 bool bFound = false;
329 bool bAll = (rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL)
330 ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL);
331 SCCOL nCol = rCol;
332 SCROW nRow = rRow;
334 bool bSkipFiltered = !rSearchItem.IsSearchFiltered();
335 bool bSearchNotes = (rSearchItem.GetCellType() == SvxSearchCellType::NOTE);
336 // We need to cache sc::ColumnBlockConstPosition per each column.
337 if (static_cast<SCCOL>(blockPos.size()) != nLastCol + 1)
339 blockPos.resize( nLastCol + 1 );
340 for( SCCOL i = 0; i <= nLastCol; ++i )
341 aCol[ i ].InitBlockPosition( blockPos[ i ] );
343 if (!bAll && rSearchItem.GetBackward())
345 SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
346 if (rSearchItem.GetRowDirection())
348 nCol--;
349 nCol = std::min(nCol, nLastCol);
350 nRow = std::min(nRow, nLastRow);
351 while (!bFound && (nRow >= 0))
353 if (bSkipFiltered)
354 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
356 while (!bFound && (nCol >= 0))
358 bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ], nRow,
359 rMark, rUndoStr, pUndoDoc);
360 if (!bFound)
362 bool bIsEmpty;
365 nCol--;
366 if (nCol >= 0)
368 if (bSearchNotes)
369 bIsEmpty = !aCol[nCol].HasCellNotes();
370 else
371 bIsEmpty = aCol[nCol].IsEmptyData();
373 else
374 bIsEmpty = true;
376 while ((nCol >= 0) && bIsEmpty);
379 if (!bFound)
381 nCol = nLastCol;
382 nRow--;
386 else
388 nRow--;
389 nCol = std::min(nCol, nLastCol);
390 nRow = std::min(nRow, nLastRow);
391 while (!bFound && (nCol >= 0))
393 while (!bFound && (nRow >= 0))
395 if (bSkipFiltered)
396 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
398 bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
399 nRow, rMark, rUndoStr, pUndoDoc);
400 if (!bFound)
402 if (bSearchNotes)
404 /* TODO: can we look for the previous cell note instead? */
405 --nRow;
407 else
409 if (!aCol[nCol].GetPrevDataPos(nRow))
410 nRow = -1;
414 if (!bFound)
416 // Not found in this column. Move to the next column.
417 bool bIsEmpty;
418 nRow = nLastRow;
419 nLastNonFilteredRow = rDocument.MaxRow() + 1;
422 nCol--;
423 if (nCol >= 0)
425 if (bSearchNotes)
426 bIsEmpty = !aCol[nCol].HasCellNotes();
427 else
428 bIsEmpty = aCol[nCol].IsEmptyData();
430 else
431 bIsEmpty = true;
433 while ((nCol >= 0) && bIsEmpty);
438 else
440 SCROW nLastNonFilteredRow = -1;
441 if (rSearchItem.GetRowDirection())
443 nCol++;
444 while (!bFound && (nRow <= nLastRow))
446 if (bSkipFiltered)
447 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
449 while (!bFound && (nCol <= nLastCol))
451 bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
452 nRow, rMark, rUndoStr, pUndoDoc);
453 if (!bFound)
455 nCol++;
456 while ((nCol <= nLastCol) &&
457 (bSearchNotes ? !aCol[nCol].HasCellNotes() : aCol[nCol].IsEmptyData()))
458 nCol++;
461 if (!bFound)
463 nCol = 0;
464 nRow++;
468 else
470 nRow++;
471 while (!bFound && (nCol <= nLastCol))
473 while (!bFound && (nRow <= nLastRow))
475 if (bSkipFiltered)
476 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
478 // GetSearchAndReplaceStart sets a nCol of -1 for
479 // ColDirection() only if rSearchItem.GetPattern() is true,
480 // so a negative column shouldn't be possible here.
481 assert(nCol >= 0 && "negative nCol for ColDirection");
483 bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
484 nRow, rMark, rUndoStr, pUndoDoc);
485 if (!bFound)
487 if (bSearchNotes)
489 /* TODO: can we look for the next cell note instead? */
490 ++nRow;
492 else
494 if (!aCol[nCol].GetNextDataPos(nRow))
495 nRow = rDocument.MaxRow() + 1;
499 if (!bFound)
501 // Not found in this column. Move to the next column.
502 nRow = 0;
503 nLastNonFilteredRow = -1;
504 nCol++;
505 while ((nCol <= nLastCol) &&
506 (bSearchNotes ? !aCol[nCol].HasCellNotes() : aCol[nCol].IsEmptyData()))
507 nCol++;
512 if (bFound)
514 rCol = nCol;
515 rRow = nRow;
517 return bFound;
520 bool ScTable::SearchAll(const SvxSearchItem& rSearchItem, const ScMarkData& rMark,
521 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
523 bool bFound = true;
524 SCCOL nCol = 0;
525 SCROW nRow = -1;
526 bool bEverFound = false;
528 SCCOL nLastCol;
529 SCROW nLastRow;
530 if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
531 GetCellArea( nLastCol, nLastRow);
532 else
533 GetLastDataPos(nLastCol, nLastRow);
535 std::vector< sc::ColumnBlockConstPosition > blockPos;
538 bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
539 if (bFound)
541 bEverFound = true;
542 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
545 while (bFound);
547 return bEverFound;
550 void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow )
552 if (rSearchItem.GetBackward())
554 if (rSearchItem.GetRowDirection())
555 rCol += 1;
556 else
557 rRow += 1;
559 else
561 if (rSearchItem.GetRowDirection())
562 rCol -= 1;
563 else
564 rRow -= 1;
568 bool ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
569 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
571 SCCOL nCol = rCol;
572 SCROW nRow = rRow;
574 UpdateSearchItemAddressForReplace( rSearchItem, nCol, nRow );
575 bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
576 if (bFound)
578 rCol = nCol;
579 rRow = nRow;
581 return bFound;
584 bool ScTable::ReplaceAll(
585 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
586 OUString& rUndoStr, ScDocument* pUndoDoc, bool& bMatchedRangesWereClamped)
588 SCCOL nCol = 0;
589 SCROW nRow = -1;
591 SCCOL nLastCol;
592 SCROW nLastRow;
593 if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
594 GetCellArea( nLastCol, nLastRow);
595 else
596 GetLastDataPos(nLastCol, nLastRow);
598 // tdf#92160 - columnar replace is faster, and more memory efficient.
599 SvxSearchItem aCopyItem(rSearchItem);
600 aCopyItem.SetRowDirection(false);
602 std::vector< sc::ColumnBlockConstPosition > blockPos;
603 bool bEverFound = false;
604 while (true)
606 bool bFound = Search(aCopyItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
608 if (bFound)
610 bEverFound = true;
611 // The combination of this loop and the Join() algorithm is O(n^2),
612 // so just give up if the list gets too big.
613 if (rMatchedRanges.size() < 1000)
614 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
615 else
616 bMatchedRangesWereClamped = true;
618 else
619 break;
621 return bEverFound;
624 bool ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
625 const ScMarkData& rMark)
627 const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
628 rDocument.GetStyleSheetPool()->Find(
629 rSearchItem.GetSearchString(), SfxStyleFamily::Para ));
631 SCCOL nCol = rCol;
632 SCROW nRow = rRow;
633 bool bFound = false;
635 bool bSelect = rSearchItem.GetSelection();
636 bool bRows = rSearchItem.GetRowDirection();
637 bool bBack = rSearchItem.GetBackward();
638 short nAdd = bBack ? -1 : 1;
640 if (bRows) // by row
642 if ( !IsColValid( nCol ) )
644 SAL_WARN( "sc.core", "SearchStyle: bad column " << nCol);
645 return false;
647 nRow += nAdd;
650 SCROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark );
651 if (!ValidRow(nNextRow))
653 nRow = bBack ? rDocument.MaxRow() : 0;
654 nCol = sal::static_int_cast<SCCOL>( nCol + nAdd );
656 else
658 nRow = nNextRow;
659 bFound = true;
662 while ( !bFound && IsColValid( nCol ) );
664 else // by column
666 SCCOL aColSize = aCol.size();
667 std::vector< SCROW > nNextRows ( aColSize );
668 SCCOL i;
669 for (i=0; i < aColSize; ++i)
671 SCROW nSRow = nRow;
672 if (bBack)
674 if (i>=nCol) --nSRow;
676 else
678 if (i<=nCol) ++nSRow;
680 nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark );
682 if (bBack) // backwards
684 nRow = -1;
685 for (i = aColSize - 1; i>=0; --i)
686 if (nNextRows[i]>nRow)
688 nCol = i;
689 nRow = nNextRows[i];
690 bFound = true;
693 else // forwards
695 nRow = rDocument.MaxRow()+1;
696 for (i=0; i < aColSize; ++i)
697 if (nNextRows[i]<nRow)
699 nCol = i;
700 nRow = nNextRows[i];
701 bFound = true;
706 if (bFound)
708 rCol = nCol;
709 rRow = nRow;
711 return bFound;
714 //TODO: return single Pattern for Undo
716 bool ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
717 const ScMarkData& rMark, bool bIsUndo)
719 bool bRet;
720 if (bIsUndo)
721 bRet = true;
722 else
723 bRet = SearchStyle(rSearchItem, rCol, rRow, rMark);
724 if (bRet)
726 const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
727 rDocument.GetStyleSheetPool()->Find(
728 rSearchItem.GetReplaceString(), SfxStyleFamily::Para ));
730 if (pReplaceStyle)
731 ApplyStyle( rCol, rRow, pReplaceStyle );
732 else
734 OSL_FAIL("pReplaceStyle==0");
738 return bRet;
741 bool ScTable::SearchAllStyle(
742 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges)
744 const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
745 rDocument.GetStyleSheetPool()->Find(
746 rSearchItem.GetSearchString(), SfxStyleFamily::Para ));
747 bool bSelect = rSearchItem.GetSelection();
748 bool bBack = rSearchItem.GetBackward();
749 bool bEverFound = false;
751 for (SCCOL i=0; i < aCol.size(); ++i)
753 bool bFound = true;
754 SCROW nRow = 0;
755 SCROW nEndRow;
756 while (bFound && nRow <= rDocument.MaxRow())
758 bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark );
759 if (bFound)
761 if (nEndRow<nRow)
762 std::swap( nRow, nEndRow );
763 rMatchedRanges.Join(ScRange(i, nRow, nTab, i, nEndRow, nTab));
764 nRow = nEndRow + 1;
765 bEverFound = true;
770 return bEverFound;
773 bool ScTable::ReplaceAllStyle(
774 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
775 ScDocument* pUndoDoc)
777 bool bRet = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
778 if (bRet)
780 const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
781 rDocument.GetStyleSheetPool()->Find(
782 rSearchItem.GetReplaceString(), SfxStyleFamily::Para ));
784 if (pReplaceStyle)
786 if (pUndoDoc)
787 rDocument.CopyToDocument(0, 0 ,nTab, rDocument.MaxCol(),rDocument.MaxRow(),nTab,
788 InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark);
789 ApplySelectionStyle( *pReplaceStyle, rMark );
791 else
793 OSL_FAIL("pReplaceStyle==0");
797 return bRet;
800 bool ScTable::SearchAndReplace(
801 const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
802 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc, bool& bMatchedRangesWereClamped)
804 SvxSearchCmd nCommand = rSearchItem.GetCommand();
805 bool bFound = false;
806 if ( ValidColRow(rCol, rRow) ||
807 ((nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE) &&
808 (((rCol == GetDoc().GetMaxColCount() || rCol == -1) && ValidRow(rRow)) ||
809 ((rRow == GetDoc().GetMaxRowCount() || rRow == -1) && ValidCol(rCol))
814 bool bStyles = rSearchItem.GetPattern();
815 if (bStyles)
817 if (nCommand == SvxSearchCmd::FIND)
818 bFound = SearchStyle(rSearchItem, rCol, rRow, rMark);
819 else if (nCommand == SvxSearchCmd::REPLACE)
820 bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, false);
821 else if (nCommand == SvxSearchCmd::FIND_ALL)
822 bFound = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
823 else if (nCommand == SvxSearchCmd::REPLACE_ALL)
824 bFound = ReplaceAllStyle(rSearchItem, rMark, rMatchedRanges, pUndoDoc);
826 else if (ScDocument::IsEmptyCellSearch( rSearchItem))
828 // Search for empty cells.
829 bFound = SearchAndReplaceEmptyCells(rSearchItem, rCol, rRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
831 else
833 // SearchParam no longer needed - SearchOptions contains all settings
834 i18nutil::SearchOptions2 aSearchOptions = rSearchItem.GetSearchOptions();
835 aSearchOptions.Locale = ScGlobal::GetLocale();
837 // reflect UseAsianOptions flag in SearchOptions
838 // (use only ignore case and width if asian options are disabled).
839 // This is also done in SvxSearchDialog CommandHdl, but not in API object.
840 if ( !rSearchItem.IsUseAsianOptions() )
841 aSearchOptions.transliterateFlags &=
842 TransliterationFlags::IGNORE_CASE |
843 TransliterationFlags::IGNORE_WIDTH;
845 pSearchText.reset( new utl::TextSearch( aSearchOptions ) );
847 if (nCommand == SvxSearchCmd::FIND)
848 bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
849 else if (nCommand == SvxSearchCmd::FIND_ALL)
850 bFound = SearchAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
851 else if (nCommand == SvxSearchCmd::REPLACE)
852 bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
853 else if (nCommand == SvxSearchCmd::REPLACE_ALL)
854 bFound = ReplaceAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc, bMatchedRangesWereClamped);
856 pSearchText.reset();
859 return bFound;
862 bool ScTable::SearchAndReplaceEmptyCells(
863 const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
864 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
866 SCCOL nColStart, nColEnd;
867 SCROW nRowStart, nRowEnd;
868 GetFirstDataPos(nColStart, nRowStart);
869 GetLastDataPos(nColEnd, nRowEnd);
871 ScRangeList aRanges(ScRange(nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab));
873 if (rSearchItem.GetSelection())
875 // current selection only.
876 if (!rMark.IsMarked() && !rMark.IsMultiMarked())
877 // There is no selection. Bail out.
878 return false;
880 ScRangeList aMarkedRanges, aNewRanges;
881 rMark.FillRangeListWithMarks(&aMarkedRanges, true);
882 for ( size_t i = 0, n = aMarkedRanges.size(); i < n; ++i )
884 ScRange & rRange = aMarkedRanges[ i ];
885 if (rRange.aStart.Col() > nColEnd || rRange.aStart.Row() > nRowEnd || rRange.aEnd.Col() < nColStart || rRange.aEnd.Row() < nRowStart)
886 // This range is outside the data area. Skip it.
887 continue;
889 // Shrink the range into data area only.
890 if (rRange.aStart.Col() < nColStart)
891 rRange.aStart.SetCol(nColStart);
892 if (rRange.aStart.Row() < nRowStart)
893 rRange.aStart.SetRow(nRowStart);
895 if (rRange.aEnd.Col() > nColEnd)
896 rRange.aEnd.SetCol(nColEnd);
897 if (rRange.aEnd.Row() > nRowEnd)
898 rRange.aEnd.SetRow(nRowEnd);
900 aNewRanges.push_back(rRange);
902 aRanges = std::move(aNewRanges);
905 SvxSearchCmd nCommand = rSearchItem.GetCommand();
906 if (nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE)
908 if (rSearchItem.GetBackward())
910 for ( size_t i = aRanges.size(); i > 0; --i )
912 const ScRange & rRange = aRanges[ i - 1 ];
913 if (SearchRangeForEmptyCell(rRange, rSearchItem, rCol, rRow, rUndoStr))
914 return true;
917 else
919 for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
921 const ScRange & rRange = aRanges[ i ];
922 if (SearchRangeForEmptyCell(rRange, rSearchItem, rCol, rRow, rUndoStr))
923 return true;
927 else if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
929 bool bFound = false;
930 for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
932 ScRange const & rRange = aRanges[ i ];
933 bFound |= SearchRangeForAllEmptyCells(rRange, rSearchItem, rMatchedRanges, rUndoStr, pUndoDoc);
935 return bFound;
937 return false;
940 namespace {
942 bool lcl_maybeReplaceCellString(
943 ScColumn& rColObj, SCCOL& rCol, SCROW& rRow, OUString& rUndoStr, SCCOL nCol, SCROW nRow, const SvxSearchItem& rSearchItem)
945 ScRefCellValue aCell = rColObj.GetCellValue(nRow);
946 if (aCell.isEmpty())
948 // empty cell found.
949 rCol = nCol;
950 rRow = nRow;
951 if (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE &&
952 !rSearchItem.GetReplaceString().isEmpty())
954 rColObj.SetRawString(nRow, rSearchItem.GetReplaceString());
955 rUndoStr.clear();
957 return true;
959 return false;
964 bool ScTable::SearchRangeForEmptyCell(
965 const ScRange& rRange, const SvxSearchItem& rSearchItem,
966 SCCOL& rCol, SCROW& rRow, OUString& rUndoStr)
968 SvxSearchCmd nCmd = rSearchItem.GetCommand();
969 bool bSkipFiltered = rSearchItem.IsSearchFiltered();
970 if (rSearchItem.GetBackward())
972 // backward search
973 if (rSearchItem.GetRowDirection())
975 // row direction.
976 SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
977 SCROW nBeginRow = std::min(rRange.aEnd.Row(), rRow);
978 for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
980 if (bSkipFiltered)
981 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
982 if (nRow < rRange.aStart.Row())
983 break;
985 SCCOL nBeginCol = rRange.aEnd.Col();
986 if (nRow == rRow && nBeginCol >= rCol)
987 // always start from one cell before the cursor.
988 nBeginCol = rCol - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
990 for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
992 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
993 return true;
997 else
999 // column direction.
1000 SCCOL nBeginCol = std::min(rRange.aEnd.Col(), rCol);
1001 for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
1003 SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
1004 SCROW nBeginRow = rRange.aEnd.Row();
1005 if (nCol == rCol && nBeginRow >= rRow)
1006 // always start from one cell before the cursor.
1007 nBeginRow = rRow - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
1008 for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
1010 if (bSkipFiltered)
1011 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
1012 if (nRow < rRange.aStart.Row())
1013 break;
1015 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
1016 return true;
1021 else
1023 // forward search
1024 if (rSearchItem.GetRowDirection())
1026 // row direction.
1027 SCROW nLastNonFilteredRow = -1;
1028 SCROW nBeginRow = rRange.aStart.Row() < rRow ? rRow : rRange.aStart.Row();
1029 for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
1031 if (bSkipFiltered)
1032 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
1033 if (nRow > rRange.aEnd.Row())
1034 break;
1036 SCCOL nBeginCol = rRange.aStart.Col();
1037 if (nRow == rRow && nBeginCol <= rCol)
1038 // always start from one cell past the cursor.
1039 nBeginCol = rCol + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
1040 for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
1042 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
1043 return true;
1047 else
1049 // column direction.
1050 SCCOL nBeginCol = rRange.aStart.Col() < rCol ? rCol : rRange.aStart.Col();
1051 for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
1053 SCROW nLastNonFilteredRow = -1;
1054 SCROW nBeginRow = rRange.aStart.Row();
1055 if (nCol == rCol && nBeginRow <= rRow)
1056 // always start from one cell past the cursor.
1057 nBeginRow = rRow + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
1058 for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
1060 if (bSkipFiltered)
1061 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
1062 if (nRow > rRange.aEnd.Row())
1063 break;
1065 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
1066 return true;
1071 return false;
1074 bool ScTable::SearchRangeForAllEmptyCells(
1075 const ScRange& rRange, const SvxSearchItem& rSearchItem,
1076 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
1078 bool bFound = false;
1079 bool bReplace = (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) &&
1080 !rSearchItem.GetReplaceString().isEmpty();
1081 bool bSkipFiltered = rSearchItem.IsSearchFiltered();
1083 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
1085 SCROW nLastNonFilteredRow = -1;
1086 if (aCol[nCol].IsEmptyData())
1088 // The entire column is empty.
1089 const SCROW nEndRow = rRange.aEnd.Row();
1090 for (SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; ++nRow)
1092 SCROW nLastRow;
1093 const bool bFiltered = RowFiltered(nRow, nullptr, &nLastRow);
1094 if (nLastRow > nEndRow)
1095 nLastRow = nEndRow;
1096 if (!bFiltered)
1098 rMatchedRanges.Join(ScRange(nCol, nRow, nTab, nCol, nLastRow, nTab));
1099 if (bReplace)
1101 const OUString& rNewStr = rSearchItem.GetReplaceString();
1102 for (SCROW i = nRow; i <= nLastRow; ++i)
1104 aCol[nCol].SetRawString(i, rNewStr);
1105 if (pUndoDoc)
1107 // TODO: I'm using a string cell with empty content to
1108 // trigger deletion of cell instance on undo. Maybe I
1109 // should create a new cell type for this?
1110 pUndoDoc->SetString(ScAddress(nCol, i, nTab), OUString());
1113 rUndoStr.clear();
1117 nRow = nLastRow; // move to the last filtered row.
1119 bFound = true;
1120 continue;
1123 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
1125 if (bSkipFiltered)
1126 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
1127 if (nRow > rRange.aEnd.Row())
1128 break;
1130 ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
1131 if (aCell.isEmpty())
1133 // empty cell found
1134 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
1135 bFound = true;
1137 if (bReplace)
1139 aCol[nCol].SetRawString(nRow, rSearchItem.GetReplaceString());
1140 if (pUndoDoc)
1142 // TODO: I'm using a string cell with empty content to
1143 // trigger deletion of cell instance on undo. Maybe I
1144 // should create a new cell type for this?
1145 pUndoDoc->SetString(ScAddress(nCol, nRow, nTab), OUString());
1151 return bFound;
1154 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */