summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/table6.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/table6.cxx')
-rw-r--r--sc/source/core/data/table6.cxx1155
1 files changed, 1155 insertions, 0 deletions
diff --git a/sc/source/core/data/table6.cxx b/sc/source/core/data/table6.cxx
new file mode 100644
index 000000000..966b4491c
--- /dev/null
+++ b/sc/source/core/data/table6.cxx
@@ -0,0 +1,1155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unotools/textsearch.hxx>
+#include <com/sun/star/util/SearchResult.hpp>
+#include <svl/srchitem.hxx>
+#include <editeng/editobj.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <table.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <markdata.hxx>
+#include <editutil.hxx>
+#include <postit.hxx>
+
+namespace {
+
+bool lcl_GetTextWithBreaks( const EditTextObject& rData, ScDocument* pDoc, OUString& rVal )
+{
+ // true = more than 1 paragraph
+
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(rData);
+ rVal = rEngine.GetText();
+ return ( rEngine.GetParagraphCount() > 1 );
+}
+
+}
+
+bool ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ if ( !IsColRowValid( nCol, nRow ) )
+ return false;
+
+ bool bFound = false;
+ bool bDoSearch = true;
+ bool bDoBack = rSearchItem.GetBackward();
+ bool bSearchFormatted = rSearchItem.IsSearchFormatted();
+
+ OUString aString;
+ ScRefCellValue aCell;
+ if (rSearchItem.GetSelection())
+ bDoSearch = rMark.IsCellMarked(nCol, nRow);
+
+ if (!bDoSearch)
+ return false;
+
+ ScPostIt* pNote;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ {
+ pNote = aCol[nCol].GetCellNote(rBlockPos, nRow);
+ if (!pNote)
+ return false;
+ }
+ else
+ {
+ aCell = aCol[nCol].GetCellValue(rBlockPos, nRow);
+ if (aCell.isEmpty())
+ return false;
+ pNote = nullptr;
+ }
+
+ bool bMultiLine = false;
+ CellType eCellType = aCell.meType;
+ switch (rSearchItem.GetCellType())
+ {
+ case SvxSearchCellType::FORMULA:
+ {
+ if ( eCellType == CELLTYPE_FORMULA )
+ aString = aCell.mpFormula->GetFormula(rDocument.GetGrammar());
+ else if ( eCellType == CELLTYPE_EDIT )
+ bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, &rDocument, aString);
+ else
+ {
+ if( !bSearchFormatted )
+ aString = aCol[nCol].GetInputString( rBlockPos, nRow );
+ else
+ aString = aCol[nCol].GetString( rBlockPos, nRow );
+ }
+ break;
+ }
+ case SvxSearchCellType::VALUE:
+ if ( eCellType == CELLTYPE_EDIT )
+ bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, &rDocument, aString);
+ else
+ {
+ if( !bSearchFormatted )
+ aString = aCol[nCol].GetInputString( rBlockPos, nRow );
+ else
+ aString = aCol[nCol].GetString( rBlockPos, nRow );
+ }
+ break;
+ case SvxSearchCellType::NOTE:
+ {
+ if (pNote)
+ {
+ aString = pNote->GetText();
+ bMultiLine = pNote->HasMultiLineText();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = aString.getLength();
+ css::util::SearchResult aSearchResult;
+ if (pSearchText)
+ {
+ if ( bDoBack )
+ {
+ sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
+ bFound = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+ else
+ {
+ bFound = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+
+ if (bFound && rSearchItem.GetWordOnly())
+ bFound = (nStart == 0 && nEnd == aString.getLength() - 1);
+ }
+ else
+ {
+ OSL_FAIL("pSearchText == NULL");
+ return bFound;
+ }
+
+ if (!bFound)
+ return false;
+ if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE
+ && rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL )
+ return bFound;
+
+ if (!IsBlockEditable(nCol, nRow, nCol, nRow))
+ return bFound;
+
+ ScMatrixMode cMatrixFlag = ScMatrixMode::NONE;
+
+ // Don't split the matrix, only replace Matrix formulas
+ if (eCellType == CELLTYPE_FORMULA)
+ {
+ cMatrixFlag = aCell.mpFormula->GetMatrixFlag();
+ if(cMatrixFlag == ScMatrixMode::Reference)
+ return bFound;
+ }
+ // No UndoDoc => Matrix not restorable => don't replace
+ if (cMatrixFlag != ScMatrixMode::NONE && !pUndoDoc)
+ return bFound;
+
+ if ( cMatrixFlag == ScMatrixMode::NONE && rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
+ rUndoStr = aString;
+ else if (pUndoDoc)
+ {
+ ScAddress aAdr( nCol, nRow, nTab );
+ aCell.commit(*pUndoDoc, aAdr);
+ }
+
+ bool bRepeat = !rSearchItem.GetWordOnly();
+ do
+ {
+ // don't continue search if the found text is empty,
+ // otherwise it would never stop (#35410#)
+ if ( nEnd < nStart )
+ bRepeat = false;
+
+ OUString sReplStr = rSearchItem.GetReplaceString();
+ if (rSearchItem.GetRegExp())
+ {
+ pSearchText->ReplaceBackReferences( sReplStr, aString, aSearchResult );
+ OUStringBuffer aStrBuffer(aString);
+ aStrBuffer.remove(nStart, nEnd-nStart+1);
+ aStrBuffer.insert(nStart, sReplStr);
+ aString = aStrBuffer.makeStringAndClear();
+ }
+ else
+ {
+ OUStringBuffer aStrBuffer(aString);
+ aStrBuffer.remove(nStart, nEnd-nStart+1);
+ aStrBuffer.insert(nStart, rSearchItem.GetReplaceString());
+ aString = aStrBuffer.makeStringAndClear();
+ }
+
+ // Adjust index
+ if (bDoBack)
+ {
+ nEnd = nStart;
+ nStart = 0;
+ }
+ else
+ {
+ nStart = nStart + sReplStr.getLength();
+ nEnd = aString.getLength();
+ }
+
+ // continue search ?
+ if (bRepeat)
+ {
+ if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL || nStart >= nEnd )
+ bRepeat = false;
+ else if (bDoBack)
+ {
+ sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
+ bRepeat = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+ else
+ {
+ bRepeat = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+ }
+ }
+ while (bRepeat);
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ {
+ // NB: rich text format is lost.
+ // This is also true of Cells.
+ if (pNote)
+ pNote->SetText( ScAddress( nCol, nRow, nTab ), aString );
+ }
+ else if ( cMatrixFlag != ScMatrixMode::NONE )
+ { // don't split Matrix
+ if ( aString.getLength() > 2 )
+ { // remove {} here so that "{=" can be replaced by "{=..."
+ if ( aString[ aString.getLength()-1 ] == '}' )
+ aString = aString.copy( 0, aString.getLength()-1 );
+ if ( aString[0] == '{' )
+ aString = aString.copy( 1 );
+ }
+ ScAddress aAdr( nCol, nRow, nTab );
+ ScFormulaCell* pFCell = new ScFormulaCell( rDocument, aAdr,
+ aString, rDocument.GetGrammar(), cMatrixFlag );
+ SCCOL nMatCols;
+ SCROW nMatRows;
+ aCell.mpFormula->GetMatColsRows(nMatCols, nMatRows);
+ pFCell->SetMatColsRows( nMatCols, nMatRows );
+ aCol[nCol].SetFormulaCell(nRow, pFCell);
+ }
+ else if ( bMultiLine && aString.indexOf('\n') != -1 )
+ {
+ ScFieldEditEngine& rEngine = rDocument.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aString);
+ SetEditText(nCol, nRow, rEngine.CreateTextObject());
+ }
+ else
+ aCol[nCol].SetString(nRow, nTab, aString, rDocument.GetAddressConvention());
+ // pCell is invalid now (deleted)
+ aCol[nCol].InitBlockPosition( rBlockPos ); // invalidate also the cached position
+
+ return bFound;
+}
+
+void ScTable::SkipFilteredRows(SCROW& rRow, SCROW& rLastNonFilteredRow, bool bForward)
+{
+ if (bForward)
+ {
+ // forward search
+
+ if (rRow <= rLastNonFilteredRow)
+ return;
+
+ SCROW nLastRow = rRow;
+ if (RowFiltered(rRow, nullptr, &nLastRow))
+ // move to the first non-filtered row.
+ rRow = nLastRow + 1;
+ else
+ // record the last non-filtered row to avoid checking
+ // the filtered state for each and every row.
+ rLastNonFilteredRow = nLastRow;
+ }
+ else
+ {
+ // backward search
+
+ if (rRow >= rLastNonFilteredRow)
+ return;
+
+ SCROW nFirstRow = rRow;
+ if (RowFiltered(rRow, &nFirstRow))
+ // move to the first non-filtered row.
+ rRow = nFirstRow - 1;
+ else
+ // record the last non-filtered row to avoid checking
+ // the filtered state for each and every row.
+ rLastNonFilteredRow = nFirstRow;
+ }
+}
+
+bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ GetCellArea( nLastCol, nLastRow);
+ else
+ GetLastDataPos(nLastCol, nLastRow);
+ std::vector< sc::ColumnBlockConstPosition > blockPos;
+ return Search(rSearchItem, rCol, rRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
+}
+
+bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ SCCOL nLastCol, SCROW nLastRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc,
+ std::vector< sc::ColumnBlockConstPosition >& blockPos)
+{
+ bool bFound = false;
+ bool bAll = (rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL)
+ ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL);
+ SCCOL nCol = rCol;
+ SCROW nRow = rRow;
+
+ bool bSkipFiltered = !rSearchItem.IsSearchFiltered();
+ bool bSearchNotes = (rSearchItem.GetCellType() == SvxSearchCellType::NOTE);
+ // We need to cache sc::ColumnBlockConstPosition per each column.
+ if (static_cast<SCCOL>(blockPos.size()) != nLastCol + 1)
+ {
+ blockPos.resize( nLastCol + 1 );
+ for( SCCOL i = 0; i <= nLastCol; ++i )
+ aCol[ i ].InitBlockPosition( blockPos[ i ] );
+ }
+ if (!bAll && rSearchItem.GetBackward())
+ {
+ SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ if (rSearchItem.GetRowDirection())
+ {
+ nCol--;
+ nCol = std::min(nCol, nLastCol);
+ nRow = std::min(nRow, nLastRow);
+ while (!bFound && (nRow >= 0))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+
+ while (!bFound && (nCol >= 0))
+ {
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ], nRow,
+ rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ bool bIsEmpty;
+ do
+ {
+ nCol--;
+ if (nCol >= 0)
+ {
+ if (bSearchNotes)
+ bIsEmpty = !aCol[nCol].HasCellNotes();
+ else
+ bIsEmpty = aCol[nCol].IsEmptyData();
+ }
+ else
+ bIsEmpty = true;
+ }
+ while ((nCol >= 0) && bIsEmpty);
+ }
+ }
+ if (!bFound)
+ {
+ nCol = nLastCol;
+ nRow--;
+ }
+ }
+ }
+ else
+ {
+ nRow--;
+ nCol = std::min(nCol, nLastCol);
+ nRow = std::min(nRow, nLastRow);
+ while (!bFound && (nCol >= 0))
+ {
+ while (!bFound && (nRow >= 0))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
+ nRow, rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ if (bSearchNotes)
+ {
+ /* TODO: can we look for the previous cell note instead? */
+ --nRow;
+ }
+ else
+ {
+ if (!aCol[nCol].GetPrevDataPos(nRow))
+ nRow = -1;
+ }
+ }
+ }
+ if (!bFound)
+ {
+ // Not found in this column. Move to the next column.
+ bool bIsEmpty;
+ nRow = nLastRow;
+ nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ do
+ {
+ nCol--;
+ if (nCol >= 0)
+ {
+ if (bSearchNotes)
+ bIsEmpty = !aCol[nCol].HasCellNotes();
+ else
+ bIsEmpty = aCol[nCol].IsEmptyData();
+ }
+ else
+ bIsEmpty = true;
+ }
+ while ((nCol >= 0) && bIsEmpty);
+ }
+ }
+ }
+ }
+ else
+ {
+ SCROW nLastNonFilteredRow = -1;
+ if (rSearchItem.GetRowDirection())
+ {
+ nCol++;
+ while (!bFound && (nRow <= nLastRow))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+
+ while (!bFound && (nCol <= nLastCol))
+ {
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
+ nRow, rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ nCol++;
+ while ((nCol <= nLastCol) &&
+ (bSearchNotes ? !aCol[nCol].HasCellNotes() : aCol[nCol].IsEmptyData()))
+ nCol++;
+ }
+ }
+ if (!bFound)
+ {
+ nCol = 0;
+ nRow++;
+ }
+ }
+ }
+ else
+ {
+ nRow++;
+ while (!bFound && (nCol <= nLastCol))
+ {
+ while (!bFound && (nRow <= nLastRow))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
+ nRow, rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ if (bSearchNotes)
+ {
+ /* TODO: can we look for the next cell note instead? */
+ ++nRow;
+ }
+ else
+ {
+ if (!aCol[nCol].GetNextDataPos(nRow))
+ nRow = rDocument.MaxRow() + 1;
+ }
+ }
+ }
+ if (!bFound)
+ {
+ // Not found in this column. Move to the next column.
+ nRow = 0;
+ nLastNonFilteredRow = -1;
+ nCol++;
+ while ((nCol <= nLastCol) &&
+ (bSearchNotes ? !aCol[nCol].HasCellNotes() : aCol[nCol].IsEmptyData()))
+ nCol++;
+ }
+ }
+ }
+ }
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return bFound;
+}
+
+bool ScTable::SearchAll(const SvxSearchItem& rSearchItem, const ScMarkData& rMark,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ bool bFound = true;
+ SCCOL nCol = 0;
+ SCROW nRow = -1;
+ bool bEverFound = false;
+
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ GetCellArea( nLastCol, nLastRow);
+ else
+ GetLastDataPos(nLastCol, nLastRow);
+
+ std::vector< sc::ColumnBlockConstPosition > blockPos;
+ do
+ {
+ bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
+ if (bFound)
+ {
+ bEverFound = true;
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
+ }
+ }
+ while (bFound);
+
+ return bEverFound;
+}
+
+void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow )
+{
+ if (rSearchItem.GetBackward())
+ {
+ if (rSearchItem.GetRowDirection())
+ rCol += 1;
+ else
+ rRow += 1;
+ }
+ else
+ {
+ if (rSearchItem.GetRowDirection())
+ rCol -= 1;
+ else
+ rRow -= 1;
+ }
+}
+
+bool ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nCol = rCol;
+ SCROW nRow = rRow;
+
+ UpdateSearchItemAddressForReplace( rSearchItem, nCol, nRow );
+ bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return bFound;
+}
+
+bool ScTable::ReplaceAll(
+ const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
+ OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nCol = 0;
+ SCROW nRow = -1;
+
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ GetCellArea( nLastCol, nLastRow);
+ else
+ GetLastDataPos(nLastCol, nLastRow);
+
+ // tdf#92160 - columnar replace is faster, and more memory efficient.
+ SvxSearchItem aCopyItem(rSearchItem);
+ aCopyItem.SetRowDirection(false);
+
+ std::vector< sc::ColumnBlockConstPosition > blockPos;
+ bool bEverFound = false;
+ while (true)
+ {
+ bool bFound = Search(aCopyItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
+
+ if (bFound)
+ {
+ bEverFound = true;
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
+ }
+ else
+ break;
+ }
+ return bEverFound;
+}
+
+bool ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark)
+{
+ const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetSearchString(), SfxStyleFamily::Para ));
+
+ SCCOL nCol = rCol;
+ SCROW nRow = rRow;
+ bool bFound = false;
+
+ bool bSelect = rSearchItem.GetSelection();
+ bool bRows = rSearchItem.GetRowDirection();
+ bool bBack = rSearchItem.GetBackward();
+ short nAdd = bBack ? -1 : 1;
+
+ if (bRows) // by row
+ {
+ if ( !IsColValid( nCol ) )
+ {
+ SAL_WARN( "sc.core", "SearchStyle: bad column " << nCol);
+ return false;
+ }
+ nRow += nAdd;
+ do
+ {
+ SCROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark );
+ if (!ValidRow(nNextRow))
+ {
+ nRow = bBack ? rDocument.MaxRow() : 0;
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAdd );
+ }
+ else
+ {
+ nRow = nNextRow;
+ bFound = true;
+ }
+ }
+ while ( !bFound && IsColValid( nCol ) );
+ }
+ else // by column
+ {
+ SCCOL aColSize = aCol.size();
+ std::vector< SCROW > nNextRows ( aColSize );
+ SCCOL i;
+ for (i=0; i < aColSize; ++i)
+ {
+ SCROW nSRow = nRow;
+ if (bBack)
+ {
+ if (i>=nCol) --nSRow;
+ }
+ else
+ {
+ if (i<=nCol) ++nSRow;
+ }
+ nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark );
+ }
+ if (bBack) // backwards
+ {
+ nRow = -1;
+ for (i = aColSize - 1; i>=0; --i)
+ if (nNextRows[i]>nRow)
+ {
+ nCol = i;
+ nRow = nNextRows[i];
+ bFound = true;
+ }
+ }
+ else // forwards
+ {
+ nRow = rDocument.MaxRow()+1;
+ for (i=0; i < aColSize; ++i)
+ if (nNextRows[i]<nRow)
+ {
+ nCol = i;
+ nRow = nNextRows[i];
+ bFound = true;
+ }
+ }
+ }
+
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return bFound;
+}
+
+//TODO: return single Pattern for Undo
+
+bool ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark, bool bIsUndo)
+{
+ bool bRet;
+ if (bIsUndo)
+ bRet = true;
+ else
+ bRet = SearchStyle(rSearchItem, rCol, rRow, rMark);
+ if (bRet)
+ {
+ const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetReplaceString(), SfxStyleFamily::Para ));
+
+ if (pReplaceStyle)
+ ApplyStyle( rCol, rRow, pReplaceStyle );
+ else
+ {
+ OSL_FAIL("pReplaceStyle==0");
+ }
+ }
+
+ return bRet;
+}
+
+bool ScTable::SearchAllStyle(
+ const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges)
+{
+ const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetSearchString(), SfxStyleFamily::Para ));
+ bool bSelect = rSearchItem.GetSelection();
+ bool bBack = rSearchItem.GetBackward();
+ bool bEverFound = false;
+
+ for (SCCOL i=0; i < aCol.size(); ++i)
+ {
+ bool bFound = true;
+ SCROW nRow = 0;
+ SCROW nEndRow;
+ while (bFound && nRow <= rDocument.MaxRow())
+ {
+ bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark );
+ if (bFound)
+ {
+ if (nEndRow<nRow)
+ {
+ SCROW nTemp = nRow;
+ nRow = nEndRow;
+ nEndRow = nTemp;
+ }
+ rMatchedRanges.Join(ScRange(i, nRow, nTab, i, nEndRow, nTab));
+ nRow = nEndRow + 1;
+ bEverFound = true;
+ }
+ }
+ }
+
+ return bEverFound;
+}
+
+bool ScTable::ReplaceAllStyle(
+ const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
+ ScDocument* pUndoDoc)
+{
+ bool bRet = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
+ if (bRet)
+ {
+ const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetReplaceString(), SfxStyleFamily::Para ));
+
+ if (pReplaceStyle)
+ {
+ if (pUndoDoc)
+ rDocument.CopyToDocument(0, 0 ,nTab, rDocument.MaxCol(),rDocument.MaxRow(),nTab,
+ InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark);
+ ApplySelectionStyle( *pReplaceStyle, rMark );
+ }
+ else
+ {
+ OSL_FAIL("pReplaceStyle==0");
+ }
+ }
+
+ return bRet;
+}
+
+bool ScTable::SearchAndReplace(
+ const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ bool bFound = false;
+ if ( ValidColRow(rCol, rRow) ||
+ ((nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE) &&
+ (((rCol == GetDoc().GetMaxColCount() || rCol == -1) && ValidRow(rRow)) ||
+ ((rRow == GetDoc().GetMaxRowCount() || rRow == -1) && ValidCol(rCol))
+ )
+ )
+ )
+ {
+ bool bStyles = rSearchItem.GetPattern();
+ if (bStyles)
+ {
+ if (nCommand == SvxSearchCmd::FIND)
+ bFound = SearchStyle(rSearchItem, rCol, rRow, rMark);
+ else if (nCommand == SvxSearchCmd::REPLACE)
+ bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, false);
+ else if (nCommand == SvxSearchCmd::FIND_ALL)
+ bFound = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
+ else if (nCommand == SvxSearchCmd::REPLACE_ALL)
+ bFound = ReplaceAllStyle(rSearchItem, rMark, rMatchedRanges, pUndoDoc);
+ }
+ else if (ScDocument::IsEmptyCellSearch( rSearchItem))
+ {
+ // Search for empty cells.
+ bFound = SearchAndReplaceEmptyCells(rSearchItem, rCol, rRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ }
+ else
+ {
+ // SearchParam no longer needed - SearchOptions contains all settings
+ i18nutil::SearchOptions2 aSearchOptions = rSearchItem.GetSearchOptions();
+ aSearchOptions.Locale = ScGlobal::GetLocale();
+
+ // reflect UseAsianOptions flag in SearchOptions
+ // (use only ignore case and width if asian options are disabled).
+ // This is also done in SvxSearchDialog CommandHdl, but not in API object.
+ if ( !rSearchItem.IsUseAsianOptions() )
+ aSearchOptions.transliterateFlags &=
+ TransliterationFlags::IGNORE_CASE |
+ TransliterationFlags::IGNORE_WIDTH;
+
+ pSearchText.reset( new utl::TextSearch( aSearchOptions ) );
+
+ if (nCommand == SvxSearchCmd::FIND)
+ bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
+ else if (nCommand == SvxSearchCmd::FIND_ALL)
+ bFound = SearchAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ else if (nCommand == SvxSearchCmd::REPLACE)
+ bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
+ else if (nCommand == SvxSearchCmd::REPLACE_ALL)
+ bFound = ReplaceAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+
+ pSearchText.reset();
+ }
+ }
+ return bFound;
+}
+
+bool ScTable::SearchAndReplaceEmptyCells(
+ const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nColStart, nColEnd;
+ SCROW nRowStart, nRowEnd;
+ GetFirstDataPos(nColStart, nRowStart);
+ GetLastDataPos(nColEnd, nRowEnd);
+
+ ScRangeList aRanges(ScRange(nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab));
+
+ if (rSearchItem.GetSelection())
+ {
+ // current selection only.
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ // There is no selection. Bail out.
+ return false;
+
+ ScRangeList aMarkedRanges, aNewRanges;
+ rMark.FillRangeListWithMarks(&aMarkedRanges, true);
+ for ( size_t i = 0, n = aMarkedRanges.size(); i < n; ++i )
+ {
+ ScRange & rRange = aMarkedRanges[ i ];
+ if (rRange.aStart.Col() > nColEnd || rRange.aStart.Row() > nRowEnd || rRange.aEnd.Col() < nColStart || rRange.aEnd.Row() < nRowStart)
+ // This range is outside the data area. Skip it.
+ continue;
+
+ // Shrink the range into data area only.
+ if (rRange.aStart.Col() < nColStart)
+ rRange.aStart.SetCol(nColStart);
+ if (rRange.aStart.Row() < nRowStart)
+ rRange.aStart.SetRow(nRowStart);
+
+ if (rRange.aEnd.Col() > nColEnd)
+ rRange.aEnd.SetCol(nColEnd);
+ if (rRange.aEnd.Row() > nRowEnd)
+ rRange.aEnd.SetRow(nRowEnd);
+
+ aNewRanges.push_back(rRange);
+ }
+ aRanges = aNewRanges;
+ }
+
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ if (nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE)
+ {
+ if (rSearchItem.GetBackward())
+ {
+ for ( size_t i = aRanges.size(); i > 0; --i )
+ {
+ const ScRange & rRange = aRanges[ i - 1 ];
+ if (SearchRangeForEmptyCell(rRange, rSearchItem, rCol, rRow, rUndoStr))
+ return true;
+ }
+ }
+ else
+ {
+ for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
+ {
+ const ScRange & rRange = aRanges[ i ];
+ if (SearchRangeForEmptyCell(rRange, rSearchItem, rCol, rRow, rUndoStr))
+ return true;
+ }
+ }
+ }
+ else if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
+ {
+ bool bFound = false;
+ for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
+ {
+ ScRange const & rRange = aRanges[ i ];
+ bFound |= SearchRangeForAllEmptyCells(rRange, rSearchItem, rMatchedRanges, rUndoStr, pUndoDoc);
+ }
+ return bFound;
+ }
+ return false;
+}
+
+namespace {
+
+bool lcl_maybeReplaceCellString(
+ ScColumn& rColObj, SCCOL& rCol, SCROW& rRow, OUString& rUndoStr, SCCOL nCol, SCROW nRow, const SvxSearchItem& rSearchItem)
+{
+ ScRefCellValue aCell = rColObj.GetCellValue(nRow);
+ if (aCell.isEmpty())
+ {
+ // empty cell found.
+ rCol = nCol;
+ rRow = nRow;
+ if (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE &&
+ !rSearchItem.GetReplaceString().isEmpty())
+ {
+ rColObj.SetRawString(nRow, rSearchItem.GetReplaceString());
+ rUndoStr.clear();
+ }
+ return true;
+ }
+ return false;
+}
+
+}
+
+bool ScTable::SearchRangeForEmptyCell(
+ const ScRange& rRange, const SvxSearchItem& rSearchItem,
+ SCCOL& rCol, SCROW& rRow, OUString& rUndoStr)
+{
+ SvxSearchCmd nCmd = rSearchItem.GetCommand();
+ bool bSkipFiltered = rSearchItem.IsSearchFiltered();
+ if (rSearchItem.GetBackward())
+ {
+ // backward search
+ if (rSearchItem.GetRowDirection())
+ {
+ // row direction.
+ SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ SCROW nBeginRow = std::min(rRange.aEnd.Row(), rRow);
+ for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+ if (nRow < rRange.aStart.Row())
+ break;
+
+ SCCOL nBeginCol = rRange.aEnd.Col();
+ if (nRow == rRow && nBeginCol >= rCol)
+ // always start from one cell before the cursor.
+ nBeginCol = rCol - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+
+ for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
+ {
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // column direction.
+ SCCOL nBeginCol = std::min(rRange.aEnd.Col(), rCol);
+ for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
+ {
+ SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ SCROW nBeginRow = rRange.aEnd.Row();
+ if (nCol == rCol && nBeginRow >= rRow)
+ // always start from one cell before the cursor.
+ nBeginRow = rRow - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+ for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+ if (nRow < rRange.aStart.Row())
+ break;
+
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // forward search
+ if (rSearchItem.GetRowDirection())
+ {
+ // row direction.
+ SCROW nLastNonFilteredRow = -1;
+ SCROW nBeginRow = rRange.aStart.Row() < rRow ? rRow : rRange.aStart.Row();
+ for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+ if (nRow > rRange.aEnd.Row())
+ break;
+
+ SCCOL nBeginCol = rRange.aStart.Col();
+ if (nRow == rRow && nBeginCol <= rCol)
+ // always start from one cell past the cursor.
+ nBeginCol = rCol + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+ for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // column direction.
+ SCCOL nBeginCol = rRange.aStart.Col() < rCol ? rCol : rRange.aStart.Col();
+ for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ SCROW nLastNonFilteredRow = -1;
+ SCROW nBeginRow = rRange.aStart.Row();
+ if (nCol == rCol && nBeginRow <= rRow)
+ // always start from one cell past the cursor.
+ nBeginRow = rRow + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+ for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+ if (nRow > rRange.aEnd.Row())
+ break;
+
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool ScTable::SearchRangeForAllEmptyCells(
+ const ScRange& rRange, const SvxSearchItem& rSearchItem,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ bool bFound = false;
+ bool bReplace = (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) &&
+ !rSearchItem.GetReplaceString().isEmpty();
+ bool bSkipFiltered = rSearchItem.IsSearchFiltered();
+
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ SCROW nLastNonFilteredRow = -1;
+ if (aCol[nCol].IsEmptyData())
+ {
+ // The entire column is empty.
+ const SCROW nEndRow = rRange.aEnd.Row();
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; ++nRow)
+ {
+ SCROW nLastRow;
+ const bool bFiltered = RowFiltered(nRow, nullptr, &nLastRow);
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+ if (!bFiltered)
+ {
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab, nCol, nLastRow, nTab));
+ if (bReplace)
+ {
+ const OUString& rNewStr = rSearchItem.GetReplaceString();
+ for (SCROW i = nRow; i <= nLastRow; ++i)
+ {
+ aCol[nCol].SetRawString(i, rNewStr);
+ if (pUndoDoc)
+ {
+ // TODO: I'm using a string cell with empty content to
+ // trigger deletion of cell instance on undo. Maybe I
+ // should create a new cell type for this?
+ pUndoDoc->SetString(ScAddress(nCol, i, nTab), OUString());
+ }
+ }
+ rUndoStr.clear();
+ }
+ }
+
+ nRow = nLastRow; // move to the last filtered row.
+ }
+ bFound = true;
+ continue;
+ }
+
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+ if (nRow > rRange.aEnd.Row())
+ break;
+
+ ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
+ if (aCell.isEmpty())
+ {
+ // empty cell found
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
+ bFound = true;
+
+ if (bReplace)
+ {
+ aCol[nCol].SetRawString(nRow, rSearchItem.GetReplaceString());
+ if (pUndoDoc)
+ {
+ // TODO: I'm using a string cell with empty content to
+ // trigger deletion of cell instance on undo. Maybe I
+ // should create a new cell type for this?
+ pUndoDoc->SetString(ScAddress(nCol, nRow, nTab), OUString());
+ }
+ }
+ }
+ }
+ }
+ return bFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */