fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / data / table6.cxx
blob3c24c8fd7c200d84d39c19069987a56a491a1a99
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 <com/sun/star/i18n/TransliterationModules.hpp>
22 #include <unotools/textsearch.hxx>
23 #include <svl/srchitem.hxx>
24 #include <editeng/editobj.hxx>
25 #include <osl/diagnose.h>
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 "detfunc.hxx"
35 #include "postit.hxx"
36 #include "stringutil.hxx"
38 using ::com::sun::star::util::SearchOptions;
40 namespace {
42 bool lcl_GetTextWithBreaks( const EditTextObject& rData, ScDocument* pDoc, OUString& rVal )
44 // true = more than 1 paragraph
46 EditEngine& rEngine = pDoc->GetEditEngine();
47 rEngine.SetText(rData);
48 rVal = rEngine.GetText( LINEEND_LF );
49 return ( rEngine.GetParagraphCount() > 1 );
54 bool ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, SCROW nRow,
55 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
57 if (!ValidColRow( nCol, nRow))
58 return false;
60 bool bFound = false;
61 bool bDoSearch = true;
62 bool bDoBack = rSearchItem.GetBackward();
64 OUString aString;
65 ScRefCellValue aCell;
66 if (rSearchItem.GetSelection())
67 bDoSearch = rMark.IsCellMarked(nCol, nRow);
69 if (!bDoSearch)
70 return false;
72 aCell = aCol[nCol].GetCellValue(nRow);
73 if (aCell.isEmpty())
74 return false;
76 bool bMultiLine = false;
77 CellType eCellType = aCell.meType;
78 switch (rSearchItem.GetCellType())
80 case SvxSearchCellType::FORMULA:
82 if ( eCellType == CELLTYPE_FORMULA )
83 aCell.mpFormula->GetFormula(aString, pDocument->GetGrammar());
84 else if ( eCellType == CELLTYPE_EDIT )
85 bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, pDocument, aString);
86 else
88 aCol[nCol].GetInputString( nRow, aString );
91 break;
92 case SvxSearchCellType::VALUE:
93 if ( eCellType == CELLTYPE_EDIT )
94 bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, pDocument, aString);
95 else
97 aCol[nCol].GetInputString( nRow, aString );
99 break;
100 case SvxSearchCellType::NOTE:
101 break; // don't search this case here
102 default:
103 break;
105 sal_Int32 nStart = 0;
106 sal_Int32 nEnd = aString.getLength();
107 ::com::sun::star::util::SearchResult aSearchResult;
108 if (pSearchText)
110 if ( bDoBack )
112 sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
113 bFound = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
114 // change results to definition before 614:
115 --nEnd;
117 else
119 bFound = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
120 // change results to definition before 614:
121 --nEnd;
124 if (bFound && rSearchItem.GetWordOnly())
125 bFound = (nStart == 0 && nEnd == aString.getLength() - 1);
127 else
129 OSL_FAIL("pSearchText == NULL");
130 return bFound;
133 sal_uInt8 cMatrixFlag = MM_NONE;
134 if ( bFound &&
135 ( (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE)
136 ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) ) &&
137 // Don't split the matrix, only replace Matrix formulas
138 !( (eCellType == CELLTYPE_FORMULA &&
139 ((cMatrixFlag = aCell.mpFormula->GetMatrixFlag()) == MM_REFERENCE))
140 // No UndoDoc => Matrix not restorable => don't replace
141 || (cMatrixFlag != MM_NONE && !pUndoDoc) ) &&
142 IsBlockEditable(nCol, nRow, nCol, nRow)
145 if ( cMatrixFlag == MM_NONE && rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
146 rUndoStr = aString;
147 else if (pUndoDoc)
149 ScAddress aAdr( nCol, nRow, nTab );
150 aCell.commit(*pUndoDoc, aAdr);
152 bool bRepeat = !rSearchItem.GetWordOnly();
155 // don't continue search if the found text is empty,
156 // otherwise it would never stop (#35410#)
157 if ( nEnd < nStart )
158 bRepeat = false;
160 OUString sReplStr = rSearchItem.GetReplaceString();
161 if (rSearchItem.GetRegExp())
163 pSearchText->ReplaceBackReferences( sReplStr, aString, aSearchResult );
164 OUStringBuffer aStrBuffer(aString);
165 aStrBuffer.remove(nStart, nEnd-nStart+1);
166 aStrBuffer.insert(nStart, sReplStr);
167 aString = aStrBuffer.makeStringAndClear();
169 else
171 OUStringBuffer aStrBuffer(aString);
172 aStrBuffer.remove(nStart, nEnd-nStart+1);
173 aStrBuffer.insert(nStart, rSearchItem.GetReplaceString());
174 aString = aStrBuffer.makeStringAndClear();
177 // Adjust index
178 if (bDoBack)
180 nEnd = nStart;
181 nStart = 0;
183 else
185 nStart = nStart + sReplStr.getLength();
186 nEnd = aString.getLength();
189 // continue search ?
190 if (bRepeat)
192 if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL || nStart >= nEnd )
193 bRepeat = false;
194 else if (bDoBack)
196 sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
197 bRepeat = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
198 // change results to definition before 614:
199 --nEnd;
201 else
203 bRepeat = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
204 // change results to definition before 614:
205 --nEnd;
209 while (bRepeat);
211 if ( cMatrixFlag != MM_NONE )
212 { // don't split Matrix
213 if ( aString.getLength() > 2 )
214 { // remove {} here so that "{=" can be replaced by "{=..."
215 if ( aString[ aString.getLength()-1 ] == '}' )
216 aString = aString.copy( 0, aString.getLength()-1 );
217 if ( aString[0] == '{' )
218 aString = aString.copy( 1 );
220 ScAddress aAdr( nCol, nRow, nTab );
221 ScFormulaCell* pFCell = new ScFormulaCell( pDocument, aAdr,
222 aString, pDocument->GetGrammar(), cMatrixFlag );
223 SCCOL nMatCols;
224 SCROW nMatRows;
225 aCell.mpFormula->GetMatColsRows(nMatCols, nMatRows);
226 pFCell->SetMatColsRows( nMatCols, nMatRows );
227 aCol[nCol].SetFormulaCell(nRow, pFCell);
229 else if ( bMultiLine && aString.indexOf('\n') != -1 )
231 ScFieldEditEngine& rEngine = pDocument->GetEditEngine();
232 rEngine.SetText(aString);
233 SetEditText(nCol, nRow, rEngine.CreateTextObject());
235 else
236 aCol[nCol].SetString(nRow, nTab, aString, pDocument->GetAddressConvention());
237 // pCell is invalid now (deleted)
239 return bFound;
242 void ScTable::SkipFilteredRows(SCROW& rRow, SCROW& rLastNonFilteredRow, bool bForward)
244 if (bForward)
246 // forward search
248 if (rRow <= rLastNonFilteredRow)
249 return;
251 SCROW nLastRow = rRow;
252 if (RowFiltered(rRow, NULL, &nLastRow))
253 // move to the first non-filtered row.
254 rRow = nLastRow + 1;
255 else
256 // record the last non-filtered row to avoid checking
257 // the filtered state for each and every row.
258 rLastNonFilteredRow = nLastRow;
260 else
262 // backward search
264 if (rRow >= rLastNonFilteredRow)
265 return;
267 SCROW nFirstRow = rRow;
268 if (RowFiltered(rRow, &nFirstRow, NULL))
269 // move to the first non-filtered row.
270 rRow = nFirstRow - 1;
271 else
272 // record the last non-filtered row to avoid checking
273 // the filtered state for each and every row.
274 rLastNonFilteredRow = nFirstRow;
278 bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
279 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
281 SCCOL nLastCol;
282 SCROW nLastRow;
283 GetLastDataPos(nLastCol, nLastRow);
284 return Search(rSearchItem, rCol, rRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc);
287 bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
288 const SCCOL& nLastCol, const SCROW& nLastRow,
289 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
291 bool bFound = false;
292 bool bAll = (rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL)
293 ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL);
294 SCCOL nCol = rCol;
295 SCROW nRow = rRow;
297 bool bSkipFiltered = !rSearchItem.IsSearchFiltered();
298 if (!bAll && rSearchItem.GetBackward())
300 SCROW nLastNonFilteredRow = MAXROW + 1;
301 nCol = std::min(nCol, (SCCOL)(nLastCol + 1));
302 nRow = std::min(nRow, (SCROW)(nLastRow + 1));
303 if (rSearchItem.GetRowDirection())
305 nCol--;
306 while (!bFound && ((SCsROW)nRow >= 0))
308 if (bSkipFiltered)
309 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
311 while (!bFound && ((SCsCOL)nCol >= 0))
313 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
314 if (!bFound)
316 bool bIsEmpty;
319 nCol--;
320 if ((SCsCOL)nCol >= 0)
321 bIsEmpty = aCol[nCol].IsEmptyData();
322 else
323 bIsEmpty = true;
325 while (((SCsCOL)nCol >= 0) && bIsEmpty);
328 if (!bFound)
330 nCol = nLastCol;
331 nRow--;
335 else
337 nRow--;
338 while (!bFound && ((SCsCOL)nCol >= 0))
340 while (!bFound && ((SCsROW)nRow >= 0))
342 if (bSkipFiltered)
343 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
345 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
346 if (!bFound)
348 if (!aCol[nCol].GetPrevDataPos(nRow))
349 nRow = -1;
352 if (!bFound)
354 // Not found in this column. Move to the next column.
355 bool bIsEmpty;
356 nRow = nLastRow;
357 nLastNonFilteredRow = MAXROW + 1;
360 nCol--;
361 if ((SCsCOL)nCol >= 0)
362 bIsEmpty = aCol[nCol].IsEmptyData();
363 else
364 bIsEmpty = true;
366 while (((SCsCOL)nCol >= 0) && bIsEmpty);
371 else
373 SCROW nLastNonFilteredRow = -1;
374 if (!bAll && rSearchItem.GetRowDirection())
376 nCol++;
377 while (!bFound && (nRow <= nLastRow))
379 if (bSkipFiltered)
380 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
382 while (!bFound && (nCol <= nLastCol))
384 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
385 if (!bFound)
387 nCol++;
388 while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++;
391 if (!bFound)
393 nCol = 0;
394 nRow++;
398 else
400 nRow++;
401 while (!bFound && (nCol <= nLastCol))
403 while (!bFound && (nRow <= nLastRow))
405 if (bSkipFiltered)
406 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
408 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
409 if (!bFound)
411 if (!aCol[nCol].GetNextDataPos(nRow))
412 nRow = MAXROW + 1;
415 if (!bFound)
417 // Not found in this column. Move to the next column.
418 nRow = 0;
419 nLastNonFilteredRow = -1;
420 nCol++;
421 while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++;
426 if (bFound)
428 rCol = nCol;
429 rRow = nRow;
431 return bFound;
434 bool ScTable::SearchAll(const SvxSearchItem& rSearchItem, const ScMarkData& rMark,
435 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
437 bool bFound = true;
438 SCCOL nCol = 0;
439 SCROW nRow = -1;
440 bool bEverFound = false;
442 SCCOL nLastCol;
443 SCROW nLastRow;
444 GetLastDataPos(nLastCol, nLastRow);
448 bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc);
449 if (bFound)
451 bEverFound = true;
452 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
455 while (bFound);
457 return bEverFound;
460 void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow )
462 if (rSearchItem.GetBackward())
464 if (rSearchItem.GetRowDirection())
465 rCol += 1;
466 else
467 rRow += 1;
469 else
471 if (rSearchItem.GetRowDirection())
472 rCol -= 1;
473 else
474 rRow -= 1;
478 bool ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
479 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
481 SCCOL nCol = rCol;
482 SCROW nRow = rRow;
484 UpdateSearchItemAddressForReplace( rSearchItem, nCol, nRow );
485 bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
486 if (bFound)
488 rCol = nCol;
489 rRow = nRow;
491 return bFound;
494 bool ScTable::ReplaceAll(
495 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
496 OUString& rUndoStr, ScDocument* pUndoDoc)
498 SCCOL nCol = 0;
499 SCROW nRow = -1;
501 SCCOL nLastCol;
502 SCROW nLastRow;
503 GetLastDataPos(nLastCol, nLastRow);
505 bool bEverFound = false;
506 while (true)
508 bool bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc);
510 if (bFound)
512 bEverFound = true;
513 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
515 else
516 break;
518 return bEverFound;
521 bool ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
522 const ScMarkData& rMark)
524 const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
525 pDocument->GetStyleSheetPool()->Find(
526 rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA ));
528 SCsCOL nCol = rCol;
529 SCsROW nRow = rRow;
530 bool bFound = false;
532 bool bSelect = rSearchItem.GetSelection();
533 bool bRows = rSearchItem.GetRowDirection();
534 bool bBack = rSearchItem.GetBackward();
535 short nAdd = bBack ? -1 : 1;
537 if (bRows) // by row
539 if (!ValidCol(nCol))
541 SAL_WARN( "sc.core", "SearchStyle: bad column " << nCol);
542 return false;
544 nRow += nAdd;
547 SCsROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark );
548 if (!ValidRow(nNextRow))
550 nRow = bBack ? MAXROW : 0;
551 nCol = sal::static_int_cast<SCsCOL>( nCol + nAdd );
553 else
555 nRow = nNextRow;
556 bFound = true;
559 while (!bFound && ValidCol(nCol));
561 else // by column
563 SCsROW nNextRows[MAXCOLCOUNT];
564 SCsCOL i;
565 for (i=0; i<=MAXCOL; i++)
567 SCsROW nSRow = nRow;
568 if (bBack) { if (i>=nCol) --nSRow; }
569 else { if (i<=nCol) ++nSRow; }
570 nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark );
572 if (bBack) // backwards
574 nRow = -1;
575 for (i=MAXCOL; i>=0; i--)
576 if (nNextRows[i]>nRow)
578 nCol = i;
579 nRow = nNextRows[i];
580 bFound = true;
583 else // forwards
585 nRow = MAXROW+1;
586 for (i=0; i<=MAXCOL; i++)
587 if (nNextRows[i]<nRow)
589 nCol = i;
590 nRow = nNextRows[i];
591 bFound = true;
596 if (bFound)
598 rCol = (SCCOL) nCol;
599 rRow = (SCROW) nRow;
601 return bFound;
604 //TODO: return single Pattern for Undo
606 bool ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
607 const ScMarkData& rMark, bool bIsUndo)
609 bool bRet;
610 if (bIsUndo)
611 bRet = true;
612 else
613 bRet = SearchStyle(rSearchItem, rCol, rRow, rMark);
614 if (bRet)
616 const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
617 pDocument->GetStyleSheetPool()->Find(
618 rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA ));
620 if (pReplaceStyle)
621 ApplyStyle( rCol, rRow, *pReplaceStyle );
622 else
624 OSL_FAIL("pReplaceStyle==0");
628 return bRet;
631 bool ScTable::SearchAllStyle(
632 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges)
634 const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
635 pDocument->GetStyleSheetPool()->Find(
636 rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA ));
637 bool bSelect = rSearchItem.GetSelection();
638 bool bBack = rSearchItem.GetBackward();
639 bool bEverFound = false;
641 for (SCCOL i=0; i<=MAXCOL; i++)
643 bool bFound = true;
644 SCsROW nRow = 0;
645 SCsROW nEndRow;
646 while (bFound && nRow <= MAXROW)
648 bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark );
649 if (bFound)
651 if (nEndRow<nRow)
653 SCsROW nTemp = nRow;
654 nRow = nEndRow;
655 nEndRow = nTemp;
657 rMatchedRanges.Join(ScRange(i, nRow, nTab, i, nEndRow, nTab));
658 nRow = nEndRow + 1;
659 bEverFound = true;
664 return bEverFound;
667 bool ScTable::ReplaceAllStyle(
668 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
669 ScDocument* pUndoDoc)
671 bool bRet = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
672 if (bRet)
674 const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
675 pDocument->GetStyleSheetPool()->Find(
676 rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA ));
678 if (pReplaceStyle)
680 if (pUndoDoc)
681 pDocument->CopyToDocument( 0,0,nTab, MAXCOL,MAXROW,nTab,
682 IDF_ATTRIB, true, pUndoDoc, &rMark );
683 ApplySelectionStyle( *pReplaceStyle, rMark );
685 else
687 OSL_FAIL("pReplaceStyle==0");
691 return bRet;
694 bool ScTable::SearchAndReplace(
695 const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
696 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
698 SvxSearchCmd nCommand = rSearchItem.GetCommand();
699 bool bFound = false;
700 if ( ValidColRow(rCol, rRow) ||
701 ((nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE) &&
702 (((rCol == MAXCOLCOUNT || rCol == -1) && ValidRow(rRow)) ||
703 ((rRow == MAXROWCOUNT || rRow == -1) && ValidCol(rCol))
708 bool bStyles = rSearchItem.GetPattern();
709 if (bStyles)
711 if (nCommand == SvxSearchCmd::FIND)
712 bFound = SearchStyle(rSearchItem, rCol, rRow, rMark);
713 else if (nCommand == SvxSearchCmd::REPLACE)
714 bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, false);
715 else if (nCommand == SvxSearchCmd::FIND_ALL)
716 bFound = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
717 else if (nCommand == SvxSearchCmd::REPLACE_ALL)
718 bFound = ReplaceAllStyle(rSearchItem, rMark, rMatchedRanges, pUndoDoc);
720 else
722 // SearchParam no longer needed - SearchOptions contains all settings
723 com::sun::star::util::SearchOptions aSearchOptions = rSearchItem.GetSearchOptions();
724 aSearchOptions.Locale = *ScGlobal::GetLocale();
726 if (aSearchOptions.searchString.isEmpty())
728 // Search for empty cells.
729 return SearchAndReplaceEmptyCells(rSearchItem, rCol, rRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
732 // reflect UseAsianOptions flag in SearchOptions
733 // (use only ignore case and width if asian options are disabled).
734 // This is also done in SvxSearchDialog CommandHdl, but not in API object.
735 if ( !rSearchItem.IsUseAsianOptions() )
736 aSearchOptions.transliterateFlags &=
737 ( com::sun::star::i18n::TransliterationModules_IGNORE_CASE |
738 com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH );
740 pSearchText = new utl::TextSearch( aSearchOptions );
742 if (nCommand == SvxSearchCmd::FIND)
743 bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
744 else if (nCommand == SvxSearchCmd::FIND_ALL)
745 bFound = SearchAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
746 else if (nCommand == SvxSearchCmd::REPLACE)
747 bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
748 else if (nCommand == SvxSearchCmd::REPLACE_ALL)
749 bFound = ReplaceAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
751 delete pSearchText;
752 pSearchText = NULL;
755 return bFound;
758 bool ScTable::SearchAndReplaceEmptyCells(
759 const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
760 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
762 SCCOL nColStart, nColEnd;
763 SCROW nRowStart, nRowEnd;
764 GetFirstDataPos(nColStart, nRowStart);
765 GetLastDataPos(nColEnd, nRowEnd);
767 ScRangeList aRanges;
768 aRanges.Append(ScRange(nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab));
770 if (rSearchItem.GetSelection())
772 // current selection only.
773 if (!rMark.IsMarked() && !rMark.IsMultiMarked())
774 // There is no selection. Bail out.
775 return false;
777 ScRangeList aMarkedRanges, aNewRanges;
778 rMark.FillRangeListWithMarks(&aMarkedRanges, true);
779 for ( size_t i = 0, n = aMarkedRanges.size(); i < n; ++i )
781 ScRange* p = aMarkedRanges[ i ];
782 if (p->aStart.Col() > nColEnd || p->aStart.Row() > nRowEnd)
783 // This range is outside the data area. Skip it.
784 continue;
786 // Shrink the range into data area only.
787 if (p->aStart.Col() < nColStart)
788 p->aStart.SetCol(rCol);
789 if (p->aStart.Row() < nRowStart)
790 p->aStart.SetRow(rRow);
792 if (p->aEnd.Col() > nColEnd)
793 p->aEnd.SetCol(nColEnd);
794 if (p->aEnd.Row() > nRowEnd)
795 p->aEnd.SetRow(nRowEnd);
797 aNewRanges.Append(*p);
799 aRanges = aNewRanges;
802 SvxSearchCmd nCommand = rSearchItem.GetCommand();
803 if (nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE)
805 if (rSearchItem.GetBackward())
807 for ( size_t i = aRanges.size(); i > 0; --i )
809 ScRange* p = aRanges[ i - 1 ];
810 if (SearchRangeForEmptyCell(*p, rSearchItem, rCol, rRow, rUndoStr))
811 return true;
814 else
816 for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
818 ScRange* p = aRanges[ i ];
819 if (SearchRangeForEmptyCell(*p, rSearchItem, rCol, rRow, rUndoStr))
820 return true;
824 else if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
826 bool bFound = false;
827 for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
829 ScRange* p = aRanges[ i ];
830 bFound |= SearchRangeForAllEmptyCells(*p, rSearchItem, rMatchedRanges, rUndoStr, pUndoDoc);
832 return bFound;
834 return false;
837 namespace {
839 bool lcl_maybeReplaceCellString(
840 ScColumn& rColObj, SCCOL& rCol, SCROW& rRow, OUString& rUndoStr, SCCOL nCol, SCROW nRow, const SvxSearchItem& rSearchItem)
842 ScRefCellValue aCell = rColObj.GetCellValue(nRow);
843 if (aCell.isEmpty())
845 // empty cell found.
846 rCol = nCol;
847 rRow = nRow;
848 if (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE &&
849 !rSearchItem.GetReplaceString().isEmpty())
851 rColObj.SetRawString(nRow, rSearchItem.GetReplaceString());
852 rUndoStr.clear();
854 return true;
856 return false;
861 bool ScTable::SearchRangeForEmptyCell(
862 const ScRange& rRange, const SvxSearchItem& rSearchItem,
863 SCCOL& rCol, SCROW& rRow, OUString& rUndoStr)
865 SvxSearchCmd nCmd = rSearchItem.GetCommand();
866 bool bSkipFiltered = rSearchItem.IsSearchFiltered();
867 if (rSearchItem.GetBackward())
869 // backward search
870 if (rSearchItem.GetRowDirection())
872 // row direction.
873 SCROW nLastNonFilteredRow = MAXROW + 1;
874 SCROW nBeginRow = rRange.aEnd.Row() > rRow ? rRow : rRange.aEnd.Row();
875 for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
877 if (bSkipFiltered)
878 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
879 if (nRow < rRange.aStart.Row())
880 break;
882 SCCOL nBeginCol = rRange.aEnd.Col();
883 if (nRow == rRow && nBeginCol >= rCol)
884 // always start from one cell before the cursor.
885 nBeginCol = rCol - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
887 for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
889 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
890 return true;
894 else
896 // column direction.
897 SCCOL nBeginCol = rRange.aEnd.Col() > rCol ? rCol : rRange.aEnd.Col();
898 for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
900 SCROW nLastNonFilteredRow = MAXROW + 1;
901 SCROW nBeginRow = rRange.aEnd.Row();
902 if (nCol == rCol && nBeginRow >= rRow)
903 // always start from one cell before the cursor.
904 nBeginRow = rRow - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
905 for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
907 if (bSkipFiltered)
908 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
909 if (nRow < rRange.aStart.Row())
910 break;
912 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
913 return true;
918 else
920 // forward search
921 if (rSearchItem.GetRowDirection())
923 // row direction.
924 SCROW nLastNonFilteredRow = -1;
925 SCROW nBeginRow = rRange.aStart.Row() < rRow ? rRow : rRange.aStart.Row();
926 for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
928 if (bSkipFiltered)
929 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
930 if (nRow > rRange.aEnd.Row())
931 break;
933 SCCOL nBeginCol = rRange.aStart.Col();
934 if (nRow == rRow && nBeginCol <= rCol)
935 // always start from one cell past the cursor.
936 nBeginCol = rCol + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
937 for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
939 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
940 return true;
944 else
946 // column direction.
947 SCCOL nBeginCol = rRange.aStart.Col() < rCol ? rCol : rRange.aStart.Col();
948 for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
950 SCROW nLastNonFilteredRow = -1;
951 SCROW nBeginRow = rRange.aStart.Row();
952 if (nCol == rCol && nBeginRow <= rRow)
953 // always start from one cell past the cursor.
954 nBeginRow = rRow + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
955 for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
957 if (bSkipFiltered)
958 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
959 if (nRow > rRange.aEnd.Row())
960 break;
962 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
963 return true;
968 return false;
971 bool ScTable::SearchRangeForAllEmptyCells(
972 const ScRange& rRange, const SvxSearchItem& rSearchItem,
973 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
975 bool bFound = false;
976 bool bReplace = (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) &&
977 !rSearchItem.GetReplaceString().isEmpty();
978 bool bSkipFiltered = rSearchItem.IsSearchFiltered();
980 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
982 SCROW nLastNonFilteredRow = -1;
983 if (aCol[nCol].IsEmptyData())
985 // The entire column is empty.
986 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
988 SCROW nLastRow;
989 if (!RowFiltered(nRow, NULL, &nLastRow))
991 rMatchedRanges.Join(ScRange(nCol, nRow, nTab, nCol, nLastRow, nTab));
992 if (bReplace)
994 const OUString& rNewStr = rSearchItem.GetReplaceString();
995 for (SCROW i = nRow; i <= nLastRow; ++i)
997 aCol[nCol].SetRawString(i, rNewStr);
998 if (pUndoDoc)
1000 // TODO: I'm using a string cell with empty content to
1001 // trigger deletion of cell instance on undo. Maybe I
1002 // should create a new cell type for this?
1003 ScSetStringParam aParam;
1004 aParam.setTextInput();
1005 pUndoDoc->SetString(ScAddress(nCol, i, nTab), EMPTY_OUSTRING);
1008 rUndoStr.clear();
1012 nRow = nLastRow; // move to the last filtered row.
1014 bFound = true;
1015 continue;
1018 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
1020 if (bSkipFiltered)
1021 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
1022 if (nRow > rRange.aEnd.Row())
1023 break;
1025 ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
1026 if (aCell.isEmpty())
1028 // empty cell found
1029 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
1030 bFound = true;
1032 if (bReplace)
1034 aCol[nCol].SetRawString(nRow, rSearchItem.GetReplaceString());
1035 if (pUndoDoc)
1037 // TODO: I'm using a string cell with empty content to
1038 // trigger deletion of cell instance on undo. Maybe I
1039 // should create a new cell type for this?
1040 ScSetStringParam aParam;
1041 aParam.setTextInput();
1042 pUndoDoc->SetString(ScAddress(nCol, nRow, nTab), EMPTY_OUSTRING);
1048 return bFound;
1051 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */