1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
28 #include <formulacell.hxx>
29 #include <document.hxx>
30 #include <stlpool.hxx>
31 #include <stlsheet.hxx>
32 #include <markdata.hxx>
33 #include <editutil.hxx>
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
) )
54 bool bDoSearch
= true;
55 bool bDoBack
= rSearchItem
.GetBackward();
56 bool bSearchFormatted
= rSearchItem
.IsSearchFormatted();
60 if (rSearchItem
.GetSelection())
61 bDoSearch
= rMark
.IsCellMarked(nCol
, nRow
);
67 if (rSearchItem
.GetCellType() == SvxSearchCellType::NOTE
)
69 pNote
= aCol
[nCol
].GetCellNote(rBlockPos
, nRow
);
75 aCell
= aCol
[nCol
].GetCellValue(rBlockPos
, nRow
);
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
);
92 if( !bSearchFormatted
)
93 aString
= aCol
[nCol
].GetInputString( rBlockPos
, nRow
);
95 aString
= aCol
[nCol
].GetString( rBlockPos
, nRow
);
99 case SvxSearchCellType::VALUE
:
100 if ( eCellType
== CELLTYPE_EDIT
)
101 lcl_GetTextWithBreaks(*aCell
.getEditText(), &rDocument
, aString
);
104 if( !bSearchFormatted
)
105 aString
= aCol
[nCol
].GetInputString( rBlockPos
, nRow
);
107 aString
= aCol
[nCol
].GetString( rBlockPos
, nRow
);
110 case SvxSearchCellType::NOTE
:
113 aString
= pNote
->GetText();
119 sal_Int32 nStart
= 0;
120 sal_Int32 nEnd
= aString
.getLength();
121 css::util::SearchResult aSearchResult
;
126 sal_Int32 nTemp
=nStart
; nStart
=nEnd
; nEnd
=nTemp
;
127 bFound
= pSearchText
->SearchBackward(aString
, &nStart
, &nEnd
, &aSearchResult
);
128 // change results to definition before 614:
133 bFound
= pSearchText
->SearchForward(aString
, &nStart
, &nEnd
, &aSearchResult
);
134 // change results to definition before 614:
138 if (bFound
&& rSearchItem
.GetWordOnly())
139 bFound
= (nStart
== 0 && nEnd
== aString
.getLength() - 1);
143 OSL_FAIL("pSearchText == NULL");
149 if ( rSearchItem
.GetCommand() != SvxSearchCmd::REPLACE
150 && rSearchItem
.GetCommand() != SvxSearchCmd::REPLACE_ALL
)
153 if (!IsBlockEditable(nCol
, nRow
, nCol
, nRow
))
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
)
165 // No UndoDoc => Matrix not restorable => don't replace
166 if (cMatrixFlag
!= ScMatrixMode::NONE
&& !pUndoDoc
)
169 if ( cMatrixFlag
== ScMatrixMode::NONE
&& rSearchItem
.GetCommand() == SvxSearchCmd::REPLACE
)
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#)
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();
196 OUStringBuffer
aStrBuffer(aString
);
197 aStrBuffer
.remove(nStart
, nEnd
-nStart
+1);
198 aStrBuffer
.insert(nStart
, rSearchItem
.GetReplaceString());
199 aString
= aStrBuffer
.makeStringAndClear();
210 nStart
= nStart
+ sReplStr
.getLength();
211 nEnd
= aString
.getLength();
217 if ( rSearchItem
.GetCommand() != SvxSearchCmd::REPLACE_ALL
|| nStart
>= nEnd
)
221 sal_Int32 nTemp
=nStart
; nStart
=nEnd
; nEnd
=nTemp
;
222 bRepeat
= pSearchText
->SearchBackward(aString
, &nStart
, &nEnd
, &aSearchResult
);
223 // change results to definition before 614:
228 bRepeat
= pSearchText
->SearchForward(aString
, &nStart
, &nEnd
, &aSearchResult
);
229 // change results to definition before 614:
235 if (rSearchItem
.GetCellType() == SvxSearchCellType::NOTE
)
237 // NB: rich text format is lost.
238 // This is also true of Cells.
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
);
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());
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
274 void ScTable::SkipFilteredRows(SCROW
& rRow
, SCROW
& rLastNonFilteredRow
, bool bForward
)
280 if (rRow
<= rLastNonFilteredRow
)
283 SCROW nLastRow
= rRow
;
284 if (RowFiltered(rRow
, nullptr, &nLastRow
))
285 // move to the first non-filtered row.
288 // record the last non-filtered row to avoid checking
289 // the filtered state for each and every row.
290 rLastNonFilteredRow
= nLastRow
;
296 if (rRow
>= rLastNonFilteredRow
)
299 SCROW nFirstRow
= rRow
;
300 if (RowFiltered(rRow
, &nFirstRow
))
301 // move to the first non-filtered row.
302 rRow
= nFirstRow
- 1;
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
)
315 if (rSearchItem
.GetCellType() == SvxSearchCellType::NOTE
)
316 GetCellArea( nLastCol
, nLastRow
);
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
)
329 bool bAll
= (rSearchItem
.GetCommand() == SvxSearchCmd::FIND_ALL
)
330 ||(rSearchItem
.GetCommand() == SvxSearchCmd::REPLACE_ALL
);
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())
349 nCol
= std::min(nCol
, nLastCol
);
350 nRow
= std::min(nRow
, nLastRow
);
351 while (!bFound
&& (nRow
>= 0))
354 SkipFilteredRows(nRow
, nLastNonFilteredRow
, false);
356 while (!bFound
&& (nCol
>= 0))
358 bFound
= SearchCell(rSearchItem
, nCol
, blockPos
[ nCol
], nRow
,
359 rMark
, rUndoStr
, pUndoDoc
);
369 bIsEmpty
= !aCol
[nCol
].HasCellNotes();
371 bIsEmpty
= aCol
[nCol
].IsEmptyData();
376 while ((nCol
>= 0) && bIsEmpty
);
389 nCol
= std::min(nCol
, nLastCol
);
390 nRow
= std::min(nRow
, nLastRow
);
391 while (!bFound
&& (nCol
>= 0))
393 while (!bFound
&& (nRow
>= 0))
396 SkipFilteredRows(nRow
, nLastNonFilteredRow
, false);
398 bFound
= SearchCell(rSearchItem
, nCol
, blockPos
[ nCol
],
399 nRow
, rMark
, rUndoStr
, pUndoDoc
);
404 /* TODO: can we look for the previous cell note instead? */
409 if (!aCol
[nCol
].GetPrevDataPos(nRow
))
416 // Not found in this column. Move to the next column.
419 nLastNonFilteredRow
= rDocument
.MaxRow() + 1;
426 bIsEmpty
= !aCol
[nCol
].HasCellNotes();
428 bIsEmpty
= aCol
[nCol
].IsEmptyData();
433 while ((nCol
>= 0) && bIsEmpty
);
440 SCROW nLastNonFilteredRow
= -1;
441 if (rSearchItem
.GetRowDirection())
444 while (!bFound
&& (nRow
<= nLastRow
))
447 SkipFilteredRows(nRow
, nLastNonFilteredRow
, true);
449 while (!bFound
&& (nCol
<= nLastCol
))
451 bFound
= SearchCell(rSearchItem
, nCol
, blockPos
[ nCol
],
452 nRow
, rMark
, rUndoStr
, pUndoDoc
);
456 while ((nCol
<= nLastCol
) &&
457 (bSearchNotes
? !aCol
[nCol
].HasCellNotes() : aCol
[nCol
].IsEmptyData()))
471 while (!bFound
&& (nCol
<= nLastCol
))
473 while (!bFound
&& (nRow
<= nLastRow
))
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
);
489 /* TODO: can we look for the next cell note instead? */
494 if (!aCol
[nCol
].GetNextDataPos(nRow
))
495 nRow
= rDocument
.MaxRow() + 1;
501 // Not found in this column. Move to the next column.
503 nLastNonFilteredRow
= -1;
505 while ((nCol
<= nLastCol
) &&
506 (bSearchNotes
? !aCol
[nCol
].HasCellNotes() : aCol
[nCol
].IsEmptyData()))
520 bool ScTable::SearchAll(const SvxSearchItem
& rSearchItem
, const ScMarkData
& rMark
,
521 ScRangeList
& rMatchedRanges
, OUString
& rUndoStr
, ScDocument
* pUndoDoc
)
526 bool bEverFound
= false;
530 if (rSearchItem
.GetCellType() == SvxSearchCellType::NOTE
)
531 GetCellArea( nLastCol
, nLastRow
);
533 GetLastDataPos(nLastCol
, nLastRow
);
535 std::vector
< sc::ColumnBlockConstPosition
> blockPos
;
538 bFound
= Search(rSearchItem
, nCol
, nRow
, nLastCol
, nLastRow
, rMark
, rUndoStr
, pUndoDoc
, blockPos
);
542 rMatchedRanges
.Join(ScRange(nCol
, nRow
, nTab
));
550 void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem
& rSearchItem
, SCCOL
& rCol
, SCROW
& rRow
)
552 if (rSearchItem
.GetBackward())
554 if (rSearchItem
.GetRowDirection())
561 if (rSearchItem
.GetRowDirection())
568 bool ScTable::Replace(const SvxSearchItem
& rSearchItem
, SCCOL
& rCol
, SCROW
& rRow
,
569 const ScMarkData
& rMark
, OUString
& rUndoStr
, ScDocument
* pUndoDoc
)
574 UpdateSearchItemAddressForReplace( rSearchItem
, nCol
, nRow
);
575 bool bFound
= Search(rSearchItem
, nCol
, nRow
, rMark
, rUndoStr
, pUndoDoc
);
584 bool ScTable::ReplaceAll(
585 const SvxSearchItem
& rSearchItem
, const ScMarkData
& rMark
, ScRangeList
& rMatchedRanges
,
586 OUString
& rUndoStr
, ScDocument
* pUndoDoc
, bool& bMatchedRangesWereClamped
)
593 if (rSearchItem
.GetCellType() == SvxSearchCellType::NOTE
)
594 GetCellArea( nLastCol
, nLastRow
);
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;
606 bool bFound
= Search(aCopyItem
, nCol
, nRow
, nLastCol
, nLastRow
, rMark
, rUndoStr
, pUndoDoc
, blockPos
);
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
));
616 bMatchedRangesWereClamped
= true;
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
));
635 bool bSelect
= rSearchItem
.GetSelection();
636 bool bRows
= rSearchItem
.GetRowDirection();
637 bool bBack
= rSearchItem
.GetBackward();
638 short nAdd
= bBack
? -1 : 1;
642 if ( !IsColValid( nCol
) )
644 SAL_WARN( "sc.core", "SearchStyle: bad column " << nCol
);
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
);
662 while ( !bFound
&& IsColValid( nCol
) );
666 SCCOL aColSize
= aCol
.size();
667 std::vector
< SCROW
> nNextRows ( aColSize
);
669 for (i
=0; i
< aColSize
; ++i
)
674 if (i
>=nCol
) --nSRow
;
678 if (i
<=nCol
) ++nSRow
;
680 nNextRows
[i
] = aCol
[i
].SearchStyle( nSRow
, pSearchStyle
, bBack
, bSelect
, rMark
);
682 if (bBack
) // backwards
685 for (i
= aColSize
- 1; i
>=0; --i
)
686 if (nNextRows
[i
]>nRow
)
695 nRow
= rDocument
.MaxRow()+1;
696 for (i
=0; i
< aColSize
; ++i
)
697 if (nNextRows
[i
]<nRow
)
714 //TODO: return single Pattern for Undo
716 bool ScTable::ReplaceStyle(const SvxSearchItem
& rSearchItem
, SCCOL
& rCol
, SCROW
& rRow
,
717 const ScMarkData
& rMark
, bool bIsUndo
)
723 bRet
= SearchStyle(rSearchItem
, rCol
, rRow
, rMark
);
726 const ScStyleSheet
* pReplaceStyle
= static_cast<const ScStyleSheet
*>(
727 rDocument
.GetStyleSheetPool()->Find(
728 rSearchItem
.GetReplaceString(), SfxStyleFamily::Para
));
731 ApplyStyle( rCol
, rRow
, pReplaceStyle
);
734 OSL_FAIL("pReplaceStyle==0");
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
)
756 while (bFound
&& nRow
<= rDocument
.MaxRow())
758 bFound
= aCol
[i
].SearchStyleRange( nRow
, nEndRow
, pSearchStyle
, bBack
, bSelect
, rMark
);
762 std::swap( nRow
, nEndRow
);
763 rMatchedRanges
.Join(ScRange(i
, nRow
, nTab
, i
, nEndRow
, nTab
));
773 bool ScTable::ReplaceAllStyle(
774 const SvxSearchItem
& rSearchItem
, const ScMarkData
& rMark
, ScRangeList
& rMatchedRanges
,
775 ScDocument
* pUndoDoc
)
777 bool bRet
= SearchAllStyle(rSearchItem
, rMark
, rMatchedRanges
);
780 const ScStyleSheet
* pReplaceStyle
= static_cast<const ScStyleSheet
*>(
781 rDocument
.GetStyleSheetPool()->Find(
782 rSearchItem
.GetReplaceString(), SfxStyleFamily::Para
));
787 rDocument
.CopyToDocument(0, 0 ,nTab
, rDocument
.MaxCol(),rDocument
.MaxRow(),nTab
,
788 InsertDeleteFlags::ATTRIB
, true, *pUndoDoc
, &rMark
);
789 ApplySelectionStyle( *pReplaceStyle
, rMark
);
793 OSL_FAIL("pReplaceStyle==0");
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();
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();
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
);
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
);
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.
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.
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
))
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
))
927 else if (nCommand
== SvxSearchCmd::FIND_ALL
|| nCommand
== SvxSearchCmd::REPLACE_ALL
)
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
);
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
);
951 if (rSearchItem
.GetCommand() == SvxSearchCmd::REPLACE
&&
952 !rSearchItem
.GetReplaceString().isEmpty())
954 rColObj
.SetRawString(nRow
, rSearchItem
.GetReplaceString());
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())
973 if (rSearchItem
.GetRowDirection())
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
)
981 SkipFilteredRows(nRow
, nLastNonFilteredRow
, false);
982 if (nRow
< rRange
.aStart
.Row())
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
))
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
)
1011 SkipFilteredRows(nRow
, nLastNonFilteredRow
, false);
1012 if (nRow
< rRange
.aStart
.Row())
1015 if (lcl_maybeReplaceCellString(aCol
[nCol
], rCol
, rRow
, rUndoStr
, nCol
, nRow
, rSearchItem
))
1024 if (rSearchItem
.GetRowDirection())
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
)
1032 SkipFilteredRows(nRow
, nLastNonFilteredRow
, true);
1033 if (nRow
> rRange
.aEnd
.Row())
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
))
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
)
1061 SkipFilteredRows(nRow
, nLastNonFilteredRow
, true);
1062 if (nRow
> rRange
.aEnd
.Row())
1065 if (lcl_maybeReplaceCellString(aCol
[nCol
], rCol
, rRow
, rUndoStr
, nCol
, nRow
, rSearchItem
))
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
)
1093 const bool bFiltered
= RowFiltered(nRow
, nullptr, &nLastRow
);
1094 if (nLastRow
> nEndRow
)
1098 rMatchedRanges
.Join(ScRange(nCol
, nRow
, nTab
, nCol
, nLastRow
, nTab
));
1101 const OUString
& rNewStr
= rSearchItem
.GetReplaceString();
1102 for (SCROW i
= nRow
; i
<= nLastRow
; ++i
)
1104 aCol
[nCol
].SetRawString(i
, rNewStr
);
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());
1117 nRow
= nLastRow
; // move to the last filtered row.
1123 for (SCROW nRow
= rRange
.aStart
.Row(); nRow
<= rRange
.aEnd
.Row(); ++nRow
)
1126 SkipFilteredRows(nRow
, nLastNonFilteredRow
, true);
1127 if (nRow
> rRange
.aEnd
.Row())
1130 ScRefCellValue aCell
= aCol
[nCol
].GetCellValue(nRow
);
1131 if (aCell
.isEmpty())
1134 rMatchedRanges
.Join(ScRange(nCol
, nRow
, nTab
));
1139 aCol
[nCol
].SetRawString(nRow
, rSearchItem
.GetReplaceString());
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());
1154 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */