Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / data / table6.cxx
blobe1a650d1220ad53e398f0478fa0ce7b607a0436b
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>
26 #include "table.hxx"
27 #include "formulacell.hxx"
28 #include "document.hxx"
29 #include "stlpool.hxx"
30 #include "markdata.hxx"
31 #include "editutil.hxx"
32 #include "detfunc.hxx"
33 #include "postit.hxx"
34 #include "stringutil.hxx"
36 //--------------------------------------------------------------------------
39 using ::com::sun::star::util::SearchOptions;
41 namespace {
43 bool lcl_GetTextWithBreaks( const EditTextObject& rData, ScDocument* pDoc, OUString& rVal )
45 // true = more than 1 paragraph
47 EditEngine& rEngine = pDoc->GetEditEngine();
48 rEngine.SetText(rData);
49 rVal = rEngine.GetText( LINEEND_LF );
50 return ( rEngine.GetParagraphCount() > 1 );
55 bool ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, SCROW nRow,
56 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
58 bool bFound = false;
59 bool bDoSearch = true;
60 bool bDoBack = rSearchItem.GetBackward();
62 OUString aString;
63 ScRefCellValue aCell;
64 if (rSearchItem.GetSelection())
65 bDoSearch = rMark.IsCellMarked(nCol, nRow);
67 if (!bDoSearch)
68 return false;
70 aCell = aCol[nCol].GetCellValue(nRow);
71 if (aCell.isEmpty())
72 return false;
74 bool bMultiLine = false;
75 CellType eCellType = aCell.meType;
76 switch (rSearchItem.GetCellType())
78 case SVX_SEARCHIN_FORMULA:
80 if ( eCellType == CELLTYPE_FORMULA )
81 aCell.mpFormula->GetFormula(aString, pDocument->GetGrammar());
82 else if ( eCellType == CELLTYPE_EDIT )
83 bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, pDocument, aString);
84 else
86 aCol[nCol].GetInputString( nRow, aString );
89 break;
90 case SVX_SEARCHIN_VALUE:
91 if ( eCellType == CELLTYPE_EDIT )
92 bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, pDocument, aString);
93 else
95 aCol[nCol].GetInputString( nRow, aString );
97 break;
98 case SVX_SEARCHIN_NOTE:
99 break; // don't search this case here
100 default:
101 break;
103 sal_Int32 nStart = 0;
104 sal_Int32 nEnd = aString.getLength();
105 ::com::sun::star::util::SearchResult aSearchResult;
106 if (pSearchText)
108 if ( bDoBack )
110 sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
111 bFound = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
112 // change results to definition before 614:
113 --nEnd;
115 else
117 bFound = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
118 // change results to definition before 614:
119 --nEnd;
122 if (bFound && rSearchItem.GetWordOnly())
123 bFound = (nStart == 0 && nEnd == aString.getLength() - 1);
125 else
127 OSL_FAIL("pSearchText == NULL");
128 return bFound;
131 sal_uInt8 cMatrixFlag = MM_NONE;
132 if ( bFound &&
133 ( (rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE)
134 ||(rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL) ) &&
135 // Matrix nicht zerreissen, nur Matrixformel ersetzen
136 !( (eCellType == CELLTYPE_FORMULA &&
137 ((cMatrixFlag = aCell.mpFormula->GetMatrixFlag()) == MM_REFERENCE))
138 // kein UndoDoc => Matrix nicht wiederherstellbar => nicht ersetzen
139 || (cMatrixFlag != MM_NONE && !pUndoDoc) ) &&
140 IsBlockEditable(nCol, nRow, nCol, nRow)
143 if ( cMatrixFlag == MM_NONE && rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE )
144 rUndoStr = aString;
145 else if (pUndoDoc)
147 ScAddress aAdr( nCol, nRow, nTab );
148 aCell.commit(*pUndoDoc, aAdr);
150 bool bRepeat = !rSearchItem.GetWordOnly();
153 // wenn der gefundene Text leer ist, nicht weitersuchen,
154 // sonst wuerde man nie mehr aufhoeren (#35410#)
155 if ( nEnd < nStart )
156 bRepeat = false;
158 OUString sReplStr = rSearchItem.GetReplaceString();
159 if (rSearchItem.GetRegExp())
161 pSearchText->ReplaceBackReferences( sReplStr, aString, aSearchResult );
162 OUStringBuffer aStrBuffer(aString);
163 aStrBuffer.remove(nStart, nEnd-nStart+1);
164 aStrBuffer.insert(nStart, sReplStr);
165 aString = aStrBuffer.makeStringAndClear();
167 else
169 OUStringBuffer aStrBuffer(aString);
170 aStrBuffer.remove(nStart, nEnd-nStart+1);
171 aStrBuffer.insert(nStart, rSearchItem.GetReplaceString());
172 aString = aStrBuffer.makeStringAndClear();
175 // Indizes anpassen
176 if (bDoBack)
178 nEnd = nStart;
179 nStart = 0;
181 else
183 nStart = nStart + sReplStr.getLength();
184 nEnd = aString.getLength();
187 // weitersuchen ?
188 if (bRepeat)
190 if ( rSearchItem.GetCommand() != SVX_SEARCHCMD_REPLACE_ALL || nStart >= nEnd )
191 bRepeat = false;
192 else if (bDoBack)
194 sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
195 bRepeat = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
196 // change results to definition before 614:
197 --nEnd;
199 else
201 bRepeat = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
202 // change results to definition before 614:
203 --nEnd;
207 while (bRepeat);
209 if ( cMatrixFlag != MM_NONE )
210 { // Matrix nicht zerreissen
211 if ( aString.getLength() > 2 )
212 { // {} raus, erst hier damit auch "{=" durch "{=..." ersetzt werden kann
213 if ( aString[ aString.getLength()-1 ] == '}' )
214 aString = aString.copy( 0, aString.getLength()-1 );
215 if ( aString[0] == '{' )
216 aString = aString.copy( 1 );
218 ScAddress aAdr( nCol, nRow, nTab );
219 ScFormulaCell* pFCell = new ScFormulaCell( pDocument, aAdr,
220 aString, pDocument->GetGrammar(), cMatrixFlag );
221 SCCOL nMatCols;
222 SCROW nMatRows;
223 aCell.mpFormula->GetMatColsRows(nMatCols, nMatRows);
224 pFCell->SetMatColsRows( nMatCols, nMatRows );
225 aCol[nCol].SetFormulaCell(nRow, pFCell);
227 else if ( bMultiLine && aString.indexOf('\n') != -1 )
229 ScFieldEditEngine& rEngine = pDocument->GetEditEngine();
230 rEngine.SetText(aString);
231 SetEditText(nCol, nRow, rEngine.CreateTextObject());
233 else
234 aCol[nCol].SetString(nRow, nTab, aString, pDocument->GetAddressConvention());
235 // pCell is invalid now (deleted)
237 return bFound;
240 void ScTable::SkipFilteredRows(SCROW& rRow, SCROW& rLastNonFilteredRow, bool bForward)
242 if (bForward)
244 // forward search
246 if (rRow <= rLastNonFilteredRow)
247 return;
249 SCROW nLastRow = rRow;
250 if (RowFiltered(rRow, NULL, &nLastRow))
251 // move to the first non-filtered row.
252 rRow = nLastRow + 1;
253 else
254 // record the last non-filtered row to avoid checking
255 // the filtered state for each and every row.
256 rLastNonFilteredRow = nLastRow;
258 else
260 // backward search
262 if (rRow >= rLastNonFilteredRow)
263 return;
265 SCROW nFirstRow = rRow;
266 if (RowFiltered(rRow, &nFirstRow, NULL))
267 // move to the first non-filtered row.
268 rRow = nFirstRow - 1;
269 else
270 // record the last non-filtered row to avoid checking
271 // the filtered state for each and every row.
272 rLastNonFilteredRow = nFirstRow;
276 bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
277 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
279 bool bFound = false;
280 bool bAll = (rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND_ALL)
281 ||(rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL);
282 SCCOL nCol = rCol;
283 SCROW nRow = rRow;
284 SCCOL nLastCol;
285 SCROW nLastRow;
286 GetLastDataPos(nLastCol, nLastRow);
287 bool bSkipFiltered = !rSearchItem.IsSearchFiltered();
288 if (!bAll && rSearchItem.GetBackward())
290 SCROW nLastNonFilteredRow = MAXROW + 1;
291 nCol = std::min(nCol, (SCCOL)(nLastCol + 1));
292 nRow = std::min(nRow, (SCROW)(nLastRow + 1));
293 if (rSearchItem.GetRowDirection())
295 nCol--;
296 while (!bFound && ((SCsROW)nRow >= 0))
298 if (bSkipFiltered)
299 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
301 while (!bFound && ((SCsCOL)nCol >= 0))
303 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
304 if (!bFound)
306 bool bIsEmpty;
309 nCol--;
310 if ((SCsCOL)nCol >= 0)
311 bIsEmpty = aCol[nCol].IsEmptyData();
312 else
313 bIsEmpty = true;
315 while (((SCsCOL)nCol >= 0) && bIsEmpty);
318 if (!bFound)
320 nCol = nLastCol;
321 nRow--;
325 else
327 nRow--;
328 while (!bFound && ((SCsCOL)nCol >= 0))
330 while (!bFound && ((SCsROW)nRow >= 0))
332 if (bSkipFiltered)
333 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
335 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
336 if (!bFound)
338 if (!aCol[nCol].GetPrevDataPos(nRow))
339 nRow = -1;
342 if (!bFound)
344 // Not found in this column. Move to the next column.
345 bool bIsEmpty;
346 nRow = nLastRow;
347 nLastNonFilteredRow = MAXROW + 1;
350 nCol--;
351 if ((SCsCOL)nCol >= 0)
352 bIsEmpty = aCol[nCol].IsEmptyData();
353 else
354 bIsEmpty = true;
356 while (((SCsCOL)nCol >= 0) && bIsEmpty);
361 else
363 SCROW nLastNonFilteredRow = -1;
364 if (!bAll && rSearchItem.GetRowDirection())
366 nCol++;
367 while (!bFound && (nRow <= nLastRow))
369 if (bSkipFiltered)
370 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
372 while (!bFound && (nCol <= nLastCol))
374 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
375 if (!bFound)
377 nCol++;
378 while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++;
381 if (!bFound)
383 nCol = 0;
384 nRow++;
388 else
390 nRow++;
391 while (!bFound && (nCol <= nLastCol))
393 while (!bFound && (nRow <= nLastRow))
395 if (bSkipFiltered)
396 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
398 bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
399 if (!bFound)
401 if (!aCol[nCol].GetNextDataPos(nRow))
402 nRow = MAXROW + 1;
405 if (!bFound)
407 // Not found in this column. Move to the next column.
408 nRow = 0;
409 nLastNonFilteredRow = -1;
410 nCol++;
411 while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++;
416 if (bFound)
418 rCol = nCol;
419 rRow = nRow;
421 return bFound;
424 bool ScTable::SearchAll(const SvxSearchItem& rSearchItem, const ScMarkData& rMark,
425 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
427 bool bFound = true;
428 SCCOL nCol = 0;
429 SCROW nRow = -1;
430 bool bEverFound = false;
434 bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
435 if (bFound)
437 bEverFound = true;
438 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
441 while (bFound);
443 return bEverFound;
446 void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow )
448 if (rSearchItem.GetBackward())
450 if (rSearchItem.GetRowDirection())
451 rCol += 1;
452 else
453 rRow += 1;
455 else
457 if (rSearchItem.GetRowDirection())
458 rCol -= 1;
459 else
460 rRow -= 1;
464 bool ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
465 const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
467 SCCOL nCol = rCol;
468 SCROW nRow = rRow;
470 UpdateSearchItemAddressForReplace( rSearchItem, nCol, nRow );
471 bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
472 if (bFound)
474 rCol = nCol;
475 rRow = nRow;
477 return bFound;
480 bool ScTable::ReplaceAll(
481 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
482 OUString& rUndoStr, ScDocument* pUndoDoc)
484 SCCOL nCol = 0;
485 SCROW nRow = -1;
487 bool bEverFound = false;
488 while (true)
490 bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
492 if (bFound)
494 bEverFound = true;
495 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
497 else
498 break;
500 return bEverFound;
503 bool ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
504 const ScMarkData& rMark)
506 const ScStyleSheet* pSearchStyle = (const ScStyleSheet*)
507 pDocument->GetStyleSheetPool()->Find(
508 rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA );
510 SCsCOL nCol = rCol;
511 SCsROW nRow = rRow;
512 bool bFound = false;
514 bool bSelect = rSearchItem.GetSelection();
515 bool bRows = rSearchItem.GetRowDirection();
516 bool bBack = rSearchItem.GetBackward();
517 short nAdd = bBack ? -1 : 1;
519 if (bRows) // zeilenweise
521 nRow += nAdd;
524 SCsROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark );
525 if (!ValidRow(nNextRow))
527 nRow = bBack ? MAXROW : 0;
528 nCol = sal::static_int_cast<SCsCOL>( nCol + nAdd );
530 else
532 nRow = nNextRow;
533 bFound = true;
536 while (!bFound && ValidCol(nCol));
538 else // spaltenweise
540 SCsROW nNextRows[MAXCOLCOUNT];
541 SCsCOL i;
542 for (i=0; i<=MAXCOL; i++)
544 SCsROW nSRow = nRow;
545 if (bBack) { if (i>=nCol) --nSRow; }
546 else { if (i<=nCol) ++nSRow; }
547 nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark );
549 if (bBack) // rueckwaerts
551 nRow = -1;
552 for (i=MAXCOL; i>=0; i--)
553 if (nNextRows[i]>nRow)
555 nCol = i;
556 nRow = nNextRows[i];
557 bFound = true;
560 else // vorwaerts
562 nRow = MAXROW+1;
563 for (i=0; i<=MAXCOL; i++)
564 if (nNextRows[i]<nRow)
566 nCol = i;
567 nRow = nNextRows[i];
568 bFound = true;
573 if (bFound)
575 rCol = (SCCOL) nCol;
576 rRow = (SCROW) nRow;
578 return bFound;
581 //! einzelnes Pattern fuer Undo zurueckgeben
583 bool ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
584 const ScMarkData& rMark, bool bIsUndo)
586 bool bRet;
587 if (bIsUndo)
588 bRet = true;
589 else
590 bRet = SearchStyle(rSearchItem, rCol, rRow, rMark);
591 if (bRet)
593 const ScStyleSheet* pReplaceStyle = (const ScStyleSheet*)
594 pDocument->GetStyleSheetPool()->Find(
595 rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA );
597 if (pReplaceStyle)
598 ApplyStyle( rCol, rRow, *pReplaceStyle );
599 else
601 OSL_FAIL("pReplaceStyle==0");
605 return bRet;
608 bool ScTable::SearchAllStyle(
609 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges)
611 const ScStyleSheet* pSearchStyle = (const ScStyleSheet*)
612 pDocument->GetStyleSheetPool()->Find(
613 rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA );
614 bool bSelect = rSearchItem.GetSelection();
615 bool bBack = rSearchItem.GetBackward();
616 bool bEverFound = false;
618 for (SCCOL i=0; i<=MAXCOL; i++)
620 bool bFound = true;
621 SCsROW nRow = 0;
622 SCsROW nEndRow;
623 while (bFound && nRow <= MAXROW)
625 bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark );
626 if (bFound)
628 if (nEndRow<nRow)
630 SCsROW nTemp = nRow;
631 nRow = nEndRow;
632 nEndRow = nTemp;
634 rMatchedRanges.Join(ScRange(i, nRow, nTab, i, nEndRow, nTab));
635 nRow = nEndRow + 1;
636 bEverFound = true;
641 return bEverFound;
644 bool ScTable::ReplaceAllStyle(
645 const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
646 ScDocument* pUndoDoc)
648 bool bRet = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
649 if (bRet)
651 const ScStyleSheet* pReplaceStyle = (const ScStyleSheet*)
652 pDocument->GetStyleSheetPool()->Find(
653 rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA );
655 if (pReplaceStyle)
657 if (pUndoDoc)
658 pDocument->CopyToDocument( 0,0,nTab, MAXCOL,MAXROW,nTab,
659 IDF_ATTRIB, true, pUndoDoc, &rMark );
660 ApplySelectionStyle( *pReplaceStyle, rMark );
662 else
664 OSL_FAIL("pReplaceStyle==0");
668 return bRet;
671 bool ScTable::SearchAndReplace(
672 const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
673 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
675 sal_uInt16 nCommand = rSearchItem.GetCommand();
676 bool bFound = false;
677 if ( ValidColRow(rCol, rRow) ||
678 ((nCommand == SVX_SEARCHCMD_FIND || nCommand == SVX_SEARCHCMD_REPLACE) &&
679 (((rCol == MAXCOLCOUNT || rCol == -1) && ValidRow(rRow)) ||
680 ((rRow == MAXROWCOUNT || rRow == -1) && ValidCol(rCol))
685 bool bStyles = rSearchItem.GetPattern();
686 if (bStyles)
688 if (nCommand == SVX_SEARCHCMD_FIND)
689 bFound = SearchStyle(rSearchItem, rCol, rRow, rMark);
690 else if (nCommand == SVX_SEARCHCMD_REPLACE)
691 bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, false);
692 else if (nCommand == SVX_SEARCHCMD_FIND_ALL)
693 bFound = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
694 else if (nCommand == SVX_SEARCHCMD_REPLACE_ALL)
695 bFound = ReplaceAllStyle(rSearchItem, rMark, rMatchedRanges, pUndoDoc);
697 else
699 // SearchParam no longer needed - SearchOptions contains all settings
700 com::sun::star::util::SearchOptions aSearchOptions = rSearchItem.GetSearchOptions();
701 aSearchOptions.Locale = *ScGlobal::GetLocale();
703 if (aSearchOptions.searchString.isEmpty())
705 // Search for empty cells.
706 return SearchAndReplaceEmptyCells(rSearchItem, rCol, rRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
709 // reflect UseAsianOptions flag in SearchOptions
710 // (use only ignore case and width if asian options are disabled).
711 // This is also done in SvxSearchDialog CommandHdl, but not in API object.
712 if ( !rSearchItem.IsUseAsianOptions() )
713 aSearchOptions.transliterateFlags &=
714 ( com::sun::star::i18n::TransliterationModules_IGNORE_CASE |
715 com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH );
717 pSearchText = new utl::TextSearch( aSearchOptions );
719 if (nCommand == SVX_SEARCHCMD_FIND)
720 bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
721 else if (nCommand == SVX_SEARCHCMD_FIND_ALL)
722 bFound = SearchAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
723 else if (nCommand == SVX_SEARCHCMD_REPLACE)
724 bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
725 else if (nCommand == SVX_SEARCHCMD_REPLACE_ALL)
726 bFound = ReplaceAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
728 delete pSearchText;
729 pSearchText = NULL;
732 return bFound;
735 bool ScTable::SearchAndReplaceEmptyCells(
736 const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
737 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
739 SCCOL nColStart, nColEnd;
740 SCROW nRowStart, nRowEnd;
741 GetFirstDataPos(nColStart, nRowStart);
742 GetLastDataPos(nColEnd, nRowEnd);
744 ScRangeList aRanges;
745 aRanges.Append(ScRange(nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab));
747 if (rSearchItem.GetSelection())
749 // current selection only.
750 if (!rMark.IsMarked() && !rMark.IsMultiMarked())
751 // There is no selection. Bail out.
752 return false;
754 ScRangeList aMarkedRanges, aNewRanges;
755 rMark.FillRangeListWithMarks(&aMarkedRanges, true);
756 for ( size_t i = 0, n = aMarkedRanges.size(); i < n; ++i )
758 ScRange* p = aMarkedRanges[ i ];
759 if (p->aStart.Col() > nColEnd || p->aStart.Row() > nRowEnd)
760 // This range is outside the data area. Skip it.
761 continue;
763 // Shrink the range into data area only.
764 if (p->aStart.Col() < nColStart)
765 p->aStart.SetCol(rCol);
766 if (p->aStart.Row() < nRowStart)
767 p->aStart.SetRow(rRow);
769 if (p->aEnd.Col() > nColEnd)
770 p->aEnd.SetCol(nColEnd);
771 if (p->aEnd.Row() > nRowEnd)
772 p->aEnd.SetRow(nRowEnd);
774 aNewRanges.Append(*p);
776 aRanges = aNewRanges;
779 sal_uInt16 nCommand = rSearchItem.GetCommand();
780 if (nCommand == SVX_SEARCHCMD_FIND || nCommand == SVX_SEARCHCMD_REPLACE)
782 if (rSearchItem.GetBackward())
784 for ( size_t i = aRanges.size(); i > 0; --i )
786 ScRange* p = aRanges[ i - 1 ];
787 if (SearchRangeForEmptyCell(*p, rSearchItem, rCol, rRow, rUndoStr))
788 return true;
791 else
793 for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
795 ScRange* p = aRanges[ i ];
796 if (SearchRangeForEmptyCell(*p, rSearchItem, rCol, rRow, rUndoStr))
797 return true;
801 else if (nCommand == SVX_SEARCHCMD_FIND_ALL || nCommand == SVX_SEARCHCMD_REPLACE_ALL)
803 bool bFound = false;
804 for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
806 ScRange* p = aRanges[ i ];
807 bFound |= SearchRangeForAllEmptyCells(*p, rSearchItem, rMatchedRanges, rUndoStr, pUndoDoc);
809 return bFound;
811 return false;
814 namespace {
816 bool lcl_maybeReplaceCellString(
817 ScColumn& rColObj, SCCOL& rCol, SCROW& rRow, OUString& rUndoStr, SCCOL nCol, SCROW nRow, const SvxSearchItem& rSearchItem)
819 ScRefCellValue aCell = rColObj.GetCellValue(nRow);
820 if (aCell.isEmpty())
822 // empty cell found.
823 rCol = nCol;
824 rRow = nRow;
825 if (rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE &&
826 !rSearchItem.GetReplaceString().isEmpty())
828 rColObj.SetRawString(nRow, rSearchItem.GetReplaceString());
829 rUndoStr = OUString();
831 return true;
833 return false;
838 bool ScTable::SearchRangeForEmptyCell(
839 const ScRange& rRange, const SvxSearchItem& rSearchItem,
840 SCCOL& rCol, SCROW& rRow, OUString& rUndoStr)
842 sal_uInt16 nCmd = rSearchItem.GetCommand();
843 bool bSkipFiltered = rSearchItem.IsSearchFiltered();
844 if (rSearchItem.GetBackward())
846 // backward search
847 if (rSearchItem.GetRowDirection())
849 // row direction.
850 SCROW nLastNonFilteredRow = MAXROW + 1;
851 SCROW nBeginRow = rRange.aEnd.Row() > rRow ? rRow : rRange.aEnd.Row();
852 for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
854 if (bSkipFiltered)
855 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
856 if (nRow < rRange.aStart.Row())
857 break;
859 SCCOL nBeginCol = rRange.aEnd.Col();
860 if (nRow == rRow && nBeginCol >= rCol)
861 // always start from one cell before the cursor.
862 nBeginCol = rCol - (nCmd == SVX_SEARCHCMD_FIND ? 1 : 0);
864 for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
866 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
867 return true;
871 else
873 // column direction.
874 SCCOL nBeginCol = rRange.aEnd.Col() > rCol ? rCol : rRange.aEnd.Col();
875 for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
877 SCROW nLastNonFilteredRow = MAXROW + 1;
878 SCROW nBeginRow = rRange.aEnd.Row();
879 if (nCol == rCol && nBeginRow >= rRow)
880 // always start from one cell before the cursor.
881 nBeginRow = rRow - (nCmd == SVX_SEARCHCMD_FIND ? 1 : 0);
882 for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
884 if (bSkipFiltered)
885 SkipFilteredRows(nRow, nLastNonFilteredRow, false);
886 if (nRow < rRange.aStart.Row())
887 break;
889 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
890 return true;
895 else
897 // forward search
898 if (rSearchItem.GetRowDirection())
900 // row direction.
901 SCROW nLastNonFilteredRow = -1;
902 SCROW nBeginRow = rRange.aStart.Row() < rRow ? rRow : rRange.aStart.Row();
903 for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
905 if (bSkipFiltered)
906 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
907 if (nRow > rRange.aEnd.Row())
908 break;
910 SCCOL nBeginCol = rRange.aStart.Col();
911 if (nRow == rRow && nBeginCol <= rCol)
912 // always start from one cell past the cursor.
913 nBeginCol = rCol + (nCmd == SVX_SEARCHCMD_FIND ? 1 : 0);
914 for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
916 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
917 return true;
921 else
923 // column direction.
924 SCCOL nBeginCol = rRange.aStart.Col() < rCol ? rCol : rRange.aStart.Col();
925 for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
927 SCROW nLastNonFilteredRow = -1;
928 SCROW nBeginRow = rRange.aStart.Row();
929 if (nCol == rCol && nBeginRow <= rRow)
930 // always start from one cell past the cursor.
931 nBeginRow = rRow + (nCmd == SVX_SEARCHCMD_FIND ? 1 : 0);
932 for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
934 if (bSkipFiltered)
935 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
936 if (nRow > rRange.aEnd.Row())
937 break;
939 if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
940 return true;
945 return false;
948 bool ScTable::SearchRangeForAllEmptyCells(
949 const ScRange& rRange, const SvxSearchItem& rSearchItem,
950 ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
952 bool bFound = false;
953 bool bReplace = (rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL) &&
954 !rSearchItem.GetReplaceString().isEmpty();
955 bool bSkipFiltered = rSearchItem.IsSearchFiltered();
957 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
959 SCROW nLastNonFilteredRow = -1;
960 if (aCol[nCol].IsEmptyData())
962 // The entire column is empty.
963 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
965 SCROW nLastRow;
966 if (!RowFiltered(nRow, NULL, &nLastRow))
968 rMatchedRanges.Join(ScRange(nCol, nRow, nTab, nCol, nLastRow, nTab));
969 if (bReplace)
971 const OUString& rNewStr = rSearchItem.GetReplaceString();
972 for (SCROW i = nRow; i <= nLastRow; ++i)
974 aCol[nCol].SetRawString(i, rNewStr);
975 if (pUndoDoc)
977 // TODO: I'm using a string cell with empty content to
978 // trigger deletion of cell instance on undo. Maybe I
979 // should create a new cell type for this?
980 ScSetStringParam aParam;
981 aParam.setTextInput();
982 pUndoDoc->SetString(ScAddress(nCol, i, nTab), EMPTY_OUSTRING);
985 rUndoStr = OUString();
989 nRow = nLastRow; // move to the last filtered row.
991 bFound = true;
992 continue;
995 for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
997 if (bSkipFiltered)
998 SkipFilteredRows(nRow, nLastNonFilteredRow, true);
999 if (nRow > rRange.aEnd.Row())
1000 break;
1002 ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
1003 if (aCell.isEmpty())
1005 // empty cell found
1006 rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
1007 bFound = true;
1009 if (bReplace)
1011 aCol[nCol].SetRawString(nRow, rSearchItem.GetReplaceString());
1012 if (pUndoDoc)
1014 // TODO: I'm using a string cell with empty content to
1015 // trigger deletion of cell instance on undo. Maybe I
1016 // should create a new cell type for this?
1017 ScSetStringParam aParam;
1018 aParam.setTextInput();
1019 pUndoDoc->SetString(ScAddress(nCol, nRow, nTab), EMPTY_OUSTRING);
1025 return bFound;
1028 void ScTable::RebuildFormulaGroups()
1030 for (SCCOL i=0; i<=MAXCOL; i++)
1031 aCol[i].RegroupFormulaCells();
1034 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */