Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <com/sun/star/i18n/TransliterationModules.hpp>
21 :
22 : #include <unotools/textsearch.hxx>
23 : #include <svl/srchitem.hxx>
24 : #include <editeng/editobj.hxx>
25 : #include <osl/diagnose.h>
26 :
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"
37 :
38 : using ::com::sun::star::util::SearchOptions;
39 :
40 : namespace {
41 :
42 0 : bool lcl_GetTextWithBreaks( const EditTextObject& rData, ScDocument* pDoc, OUString& rVal )
43 : {
44 : // true = more than 1 paragraph
45 :
46 0 : EditEngine& rEngine = pDoc->GetEditEngine();
47 0 : rEngine.SetText(rData);
48 0 : rVal = rEngine.GetText( LINEEND_LF );
49 0 : return ( rEngine.GetParagraphCount() > 1 );
50 : }
51 :
52 : }
53 :
54 4622 : bool ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, SCROW nRow,
55 : const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
56 : {
57 4622 : if (!ValidColRow( nCol, nRow))
58 0 : return false;
59 :
60 4622 : bool bFound = false;
61 4622 : bool bDoSearch = true;
62 4622 : bool bDoBack = rSearchItem.GetBackward();
63 :
64 4622 : OUString aString;
65 9244 : ScRefCellValue aCell;
66 4622 : if (rSearchItem.GetSelection())
67 4505 : bDoSearch = rMark.IsCellMarked(nCol, nRow);
68 :
69 4622 : if (!bDoSearch)
70 3528 : return false;
71 :
72 1094 : aCell = aCol[nCol].GetCellValue(nRow);
73 1094 : if (aCell.isEmpty())
74 71 : return false;
75 :
76 1023 : bool bMultiLine = false;
77 1023 : CellType eCellType = aCell.meType;
78 1023 : switch (rSearchItem.GetCellType())
79 : {
80 : case SvxSearchCellType::FORMULA:
81 : {
82 1019 : if ( eCellType == CELLTYPE_FORMULA )
83 18 : aCell.mpFormula->GetFormula(aString, pDocument->GetGrammar());
84 1001 : else if ( eCellType == CELLTYPE_EDIT )
85 0 : bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, pDocument, aString);
86 : else
87 : {
88 1001 : aCol[nCol].GetInputString( nRow, aString );
89 : }
90 : }
91 1019 : break;
92 : case SvxSearchCellType::VALUE:
93 4 : if ( eCellType == CELLTYPE_EDIT )
94 0 : bMultiLine = lcl_GetTextWithBreaks(*aCell.mpEditText, pDocument, aString);
95 : else
96 : {
97 4 : aCol[nCol].GetInputString( nRow, aString );
98 : }
99 4 : break;
100 : case SvxSearchCellType::NOTE:
101 0 : break; // don't search this case here
102 : default:
103 0 : break;
104 : }
105 1023 : sal_Int32 nStart = 0;
106 1023 : sal_Int32 nEnd = aString.getLength();
107 1023 : ::com::sun::star::util::SearchResult aSearchResult;
108 1023 : if (pSearchText)
109 : {
110 1023 : if ( bDoBack )
111 : {
112 4 : sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
113 4 : bFound = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
114 : // change results to definition before 614:
115 4 : --nEnd;
116 : }
117 : else
118 : {
119 1019 : bFound = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
120 : // change results to definition before 614:
121 1019 : --nEnd;
122 : }
123 :
124 1023 : if (bFound && rSearchItem.GetWordOnly())
125 106 : bFound = (nStart == 0 && nEnd == aString.getLength() - 1);
126 : }
127 : else
128 : {
129 : OSL_FAIL("pSearchText == NULL");
130 0 : return bFound;
131 : }
132 :
133 1023 : sal_uInt8 cMatrixFlag = MM_NONE;
134 1607 : if ( bFound &&
135 584 : ( (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE)
136 584 : ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) ) &&
137 : // Don't split the matrix, only replace Matrix formulas
138 3 : !( (eCellType == CELLTYPE_FORMULA &&
139 3 : ((cMatrixFlag = aCell.mpFormula->GetMatrixFlag()) == MM_REFERENCE))
140 : // No UndoDoc => Matrix not restorable => don't replace
141 1593 : || (cMatrixFlag != MM_NONE && !pUndoDoc) ) &&
142 285 : IsBlockEditable(nCol, nRow, nCol, nRow)
143 : )
144 : {
145 285 : if ( cMatrixFlag == MM_NONE && rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
146 0 : rUndoStr = aString;
147 285 : else if (pUndoDoc)
148 : {
149 285 : ScAddress aAdr( nCol, nRow, nTab );
150 285 : aCell.commit(*pUndoDoc, aAdr);
151 : }
152 285 : bool bRepeat = !rSearchItem.GetWordOnly();
153 285 : do
154 : {
155 : // don't continue search if the found text is empty,
156 : // otherwise it would never stop (#35410#)
157 285 : if ( nEnd < nStart )
158 0 : bRepeat = false;
159 :
160 285 : OUString sReplStr = rSearchItem.GetReplaceString();
161 285 : if (rSearchItem.GetRegExp())
162 : {
163 269 : pSearchText->ReplaceBackReferences( sReplStr, aString, aSearchResult );
164 269 : OUStringBuffer aStrBuffer(aString);
165 269 : aStrBuffer.remove(nStart, nEnd-nStart+1);
166 269 : aStrBuffer.insert(nStart, sReplStr);
167 269 : aString = aStrBuffer.makeStringAndClear();
168 : }
169 : else
170 : {
171 16 : OUStringBuffer aStrBuffer(aString);
172 16 : aStrBuffer.remove(nStart, nEnd-nStart+1);
173 16 : aStrBuffer.insert(nStart, rSearchItem.GetReplaceString());
174 16 : aString = aStrBuffer.makeStringAndClear();
175 : }
176 :
177 : // Adjust index
178 285 : if (bDoBack)
179 : {
180 0 : nEnd = nStart;
181 0 : nStart = 0;
182 : }
183 : else
184 : {
185 285 : nStart = nStart + sReplStr.getLength();
186 285 : nEnd = aString.getLength();
187 : }
188 :
189 : // continue search ?
190 285 : if (bRepeat)
191 : {
192 285 : if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL || nStart >= nEnd )
193 281 : bRepeat = false;
194 4 : else if (bDoBack)
195 : {
196 0 : sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
197 0 : bRepeat = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
198 : // change results to definition before 614:
199 0 : --nEnd;
200 : }
201 : else
202 : {
203 4 : bRepeat = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
204 : // change results to definition before 614:
205 4 : --nEnd;
206 : }
207 285 : }
208 : }
209 : while (bRepeat);
210 :
211 285 : if ( cMatrixFlag != MM_NONE )
212 : { // don't split Matrix
213 0 : if ( aString.getLength() > 2 )
214 : { // remove {} here so that "{=" can be replaced by "{=..."
215 0 : if ( aString[ aString.getLength()-1 ] == '}' )
216 0 : aString = aString.copy( 0, aString.getLength()-1 );
217 0 : if ( aString[0] == '{' )
218 0 : aString = aString.copy( 1 );
219 : }
220 0 : ScAddress aAdr( nCol, nRow, nTab );
221 : ScFormulaCell* pFCell = new ScFormulaCell( pDocument, aAdr,
222 0 : aString, pDocument->GetGrammar(), cMatrixFlag );
223 : SCCOL nMatCols;
224 : SCROW nMatRows;
225 0 : aCell.mpFormula->GetMatColsRows(nMatCols, nMatRows);
226 0 : pFCell->SetMatColsRows( nMatCols, nMatRows );
227 0 : aCol[nCol].SetFormulaCell(nRow, pFCell);
228 : }
229 285 : else if ( bMultiLine && aString.indexOf('\n') != -1 )
230 : {
231 0 : ScFieldEditEngine& rEngine = pDocument->GetEditEngine();
232 0 : rEngine.SetText(aString);
233 0 : SetEditText(nCol, nRow, rEngine.CreateTextObject());
234 : }
235 : else
236 285 : aCol[nCol].SetString(nRow, nTab, aString, pDocument->GetAddressConvention());
237 : // pCell is invalid now (deleted)
238 : }
239 5645 : return bFound;
240 : }
241 :
242 4610 : void ScTable::SkipFilteredRows(SCROW& rRow, SCROW& rLastNonFilteredRow, bool bForward)
243 : {
244 4610 : if (bForward)
245 : {
246 : // forward search
247 :
248 4594 : if (rRow <= rLastNonFilteredRow)
249 3436 : return;
250 :
251 1158 : SCROW nLastRow = rRow;
252 1158 : if (RowFiltered(rRow, NULL, &nLastRow))
253 : // move to the first non-filtered row.
254 0 : 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 1158 : rLastNonFilteredRow = nLastRow;
259 : }
260 : else
261 : {
262 : // backward search
263 :
264 16 : if (rRow >= rLastNonFilteredRow)
265 8 : return;
266 :
267 8 : SCROW nFirstRow = rRow;
268 8 : if (RowFiltered(rRow, &nFirstRow, NULL))
269 : // move to the first non-filtered row.
270 0 : 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 8 : rLastNonFilteredRow = nFirstRow;
275 : }
276 : }
277 :
278 31 : bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
279 : const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
280 : {
281 : SCCOL nLastCol;
282 : SCROW nLastRow;
283 31 : GetLastDataPos(nLastCol, nLastRow);
284 31 : return Search(rSearchItem, rCol, rRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc);
285 : }
286 :
287 645 : 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)
290 : {
291 645 : bool bFound = false;
292 645 : bool bAll = (rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL)
293 645 : ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL);
294 645 : SCCOL nCol = rCol;
295 645 : SCROW nRow = rRow;
296 :
297 645 : bool bSkipFiltered = !rSearchItem.IsSearchFiltered();
298 645 : if (!bAll && rSearchItem.GetBackward())
299 : {
300 8 : SCROW nLastNonFilteredRow = MAXROW + 1;
301 8 : nCol = std::min(nCol, (SCCOL)(nLastCol + 1));
302 8 : nRow = std::min(nRow, (SCROW)(nLastRow + 1));
303 8 : if (rSearchItem.GetRowDirection())
304 : {
305 8 : nCol--;
306 32 : while (!bFound && ((SCsROW)nRow >= 0))
307 : {
308 16 : if (bSkipFiltered)
309 16 : SkipFilteredRows(nRow, nLastNonFilteredRow, false);
310 :
311 60 : while (!bFound && ((SCsCOL)nCol >= 0))
312 : {
313 28 : bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
314 28 : if (!bFound)
315 : {
316 : bool bIsEmpty;
317 32 : do
318 : {
319 32 : nCol--;
320 32 : if ((SCsCOL)nCol >= 0)
321 24 : bIsEmpty = aCol[nCol].IsEmptyData();
322 : else
323 8 : bIsEmpty = true;
324 : }
325 56 : while (((SCsCOL)nCol >= 0) && bIsEmpty);
326 : }
327 : }
328 16 : if (!bFound)
329 : {
330 12 : nCol = nLastCol;
331 12 : nRow--;
332 : }
333 : }
334 : }
335 : else
336 : {
337 0 : nRow--;
338 0 : while (!bFound && ((SCsCOL)nCol >= 0))
339 : {
340 0 : while (!bFound && ((SCsROW)nRow >= 0))
341 : {
342 0 : if (bSkipFiltered)
343 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, false);
344 :
345 0 : bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
346 0 : if (!bFound)
347 : {
348 0 : if (!aCol[nCol].GetPrevDataPos(nRow))
349 0 : nRow = -1;
350 : }
351 : }
352 0 : if (!bFound)
353 : {
354 : // Not found in this column. Move to the next column.
355 : bool bIsEmpty;
356 0 : nRow = nLastRow;
357 0 : nLastNonFilteredRow = MAXROW + 1;
358 0 : do
359 : {
360 0 : nCol--;
361 0 : if ((SCsCOL)nCol >= 0)
362 0 : bIsEmpty = aCol[nCol].IsEmptyData();
363 : else
364 0 : bIsEmpty = true;
365 : }
366 0 : while (((SCsCOL)nCol >= 0) && bIsEmpty);
367 : }
368 : }
369 : }
370 : }
371 : else
372 : {
373 637 : SCROW nLastNonFilteredRow = -1;
374 637 : if (!bAll && rSearchItem.GetRowDirection())
375 : {
376 0 : nCol++;
377 0 : while (!bFound && (nRow <= nLastRow))
378 : {
379 0 : if (bSkipFiltered)
380 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, true);
381 :
382 0 : while (!bFound && (nCol <= nLastCol))
383 : {
384 0 : bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
385 0 : if (!bFound)
386 : {
387 0 : nCol++;
388 0 : while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++;
389 : }
390 : }
391 0 : if (!bFound)
392 : {
393 0 : nCol = 0;
394 0 : nRow++;
395 : }
396 : }
397 : }
398 : else
399 : {
400 637 : nRow++;
401 2441 : while (!bFound && (nCol <= nLastCol))
402 : {
403 6928 : while (!bFound && (nRow <= nLastRow))
404 : {
405 4594 : if (bSkipFiltered)
406 4594 : SkipFilteredRows(nRow, nLastNonFilteredRow, true);
407 :
408 4594 : bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
409 4594 : if (!bFound)
410 : {
411 4014 : if (!aCol[nCol].GetNextDataPos(nRow))
412 578 : nRow = MAXROW + 1;
413 : }
414 : }
415 1167 : if (!bFound)
416 : {
417 : // Not found in this column. Move to the next column.
418 587 : nRow = 0;
419 587 : nLastNonFilteredRow = -1;
420 587 : nCol++;
421 587 : while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++;
422 : }
423 : }
424 : }
425 : }
426 645 : if (bFound)
427 : {
428 584 : rCol = nCol;
429 584 : rRow = nRow;
430 : }
431 645 : return bFound;
432 : }
433 :
434 23 : bool ScTable::SearchAll(const SvxSearchItem& rSearchItem, const ScMarkData& rMark,
435 : ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
436 : {
437 23 : bool bFound = true;
438 23 : SCCOL nCol = 0;
439 23 : SCROW nRow = -1;
440 23 : bool bEverFound = false;
441 :
442 : SCCOL nLastCol;
443 : SCROW nLastRow;
444 23 : GetLastDataPos(nLastCol, nLastRow);
445 :
446 303 : do
447 : {
448 303 : bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc);
449 303 : if (bFound)
450 : {
451 280 : bEverFound = true;
452 280 : rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
453 : }
454 : }
455 : while (bFound);
456 :
457 23 : return bEverFound;
458 : }
459 :
460 0 : void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow )
461 : {
462 0 : if (rSearchItem.GetBackward())
463 : {
464 0 : if (rSearchItem.GetRowDirection())
465 0 : rCol += 1;
466 : else
467 0 : rRow += 1;
468 : }
469 : else
470 : {
471 0 : if (rSearchItem.GetRowDirection())
472 0 : rCol -= 1;
473 : else
474 0 : rRow -= 1;
475 : }
476 0 : }
477 :
478 0 : bool ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
479 : const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
480 : {
481 0 : SCCOL nCol = rCol;
482 0 : SCROW nRow = rRow;
483 :
484 0 : UpdateSearchItemAddressForReplace( rSearchItem, nCol, nRow );
485 0 : bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
486 0 : if (bFound)
487 : {
488 0 : rCol = nCol;
489 0 : rRow = nRow;
490 : }
491 0 : return bFound;
492 : }
493 :
494 26 : bool ScTable::ReplaceAll(
495 : const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
496 : OUString& rUndoStr, ScDocument* pUndoDoc)
497 : {
498 26 : SCCOL nCol = 0;
499 26 : SCROW nRow = -1;
500 :
501 : SCCOL nLastCol;
502 : SCROW nLastRow;
503 26 : GetLastDataPos(nLastCol, nLastRow);
504 :
505 26 : bool bEverFound = false;
506 : while (true)
507 : {
508 311 : bool bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc);
509 :
510 311 : if (bFound)
511 : {
512 285 : bEverFound = true;
513 285 : rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
514 : }
515 : else
516 26 : break;
517 : }
518 285 : return bEverFound;
519 : }
520 :
521 0 : bool ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
522 : const ScMarkData& rMark)
523 : {
524 : const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
525 0 : pDocument->GetStyleSheetPool()->Find(
526 0 : rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA ));
527 :
528 0 : SCsCOL nCol = rCol;
529 0 : SCsROW nRow = rRow;
530 0 : bool bFound = false;
531 :
532 0 : bool bSelect = rSearchItem.GetSelection();
533 0 : bool bRows = rSearchItem.GetRowDirection();
534 0 : bool bBack = rSearchItem.GetBackward();
535 0 : short nAdd = bBack ? -1 : 1;
536 :
537 0 : if (bRows) // by row
538 : {
539 0 : if (!ValidCol(nCol))
540 : {
541 : SAL_WARN( "sc.core", "SearchStyle: bad column " << nCol);
542 0 : return false;
543 : }
544 0 : nRow += nAdd;
545 0 : do
546 : {
547 0 : SCsROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark );
548 0 : if (!ValidRow(nNextRow))
549 : {
550 0 : nRow = bBack ? MAXROW : 0;
551 0 : nCol = sal::static_int_cast<SCsCOL>( nCol + nAdd );
552 : }
553 : else
554 : {
555 0 : nRow = nNextRow;
556 0 : bFound = true;
557 : }
558 : }
559 0 : while (!bFound && ValidCol(nCol));
560 : }
561 : else // by column
562 : {
563 : SCsROW nNextRows[MAXCOLCOUNT];
564 : SCsCOL i;
565 0 : for (i=0; i<=MAXCOL; i++)
566 : {
567 0 : SCsROW nSRow = nRow;
568 0 : if (bBack) { if (i>=nCol) --nSRow; }
569 0 : else { if (i<=nCol) ++nSRow; }
570 0 : nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark );
571 : }
572 0 : if (bBack) // backwards
573 : {
574 0 : nRow = -1;
575 0 : for (i=MAXCOL; i>=0; i--)
576 0 : if (nNextRows[i]>nRow)
577 : {
578 0 : nCol = i;
579 0 : nRow = nNextRows[i];
580 0 : bFound = true;
581 : }
582 : }
583 : else // forwards
584 : {
585 0 : nRow = MAXROW+1;
586 0 : for (i=0; i<=MAXCOL; i++)
587 0 : if (nNextRows[i]<nRow)
588 : {
589 0 : nCol = i;
590 0 : nRow = nNextRows[i];
591 0 : bFound = true;
592 : }
593 : }
594 : }
595 :
596 0 : if (bFound)
597 : {
598 0 : rCol = (SCCOL) nCol;
599 0 : rRow = (SCROW) nRow;
600 : }
601 0 : return bFound;
602 : }
603 :
604 : //TODO: return single Pattern for Undo
605 :
606 0 : bool ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
607 : const ScMarkData& rMark, bool bIsUndo)
608 : {
609 : bool bRet;
610 0 : if (bIsUndo)
611 0 : bRet = true;
612 : else
613 0 : bRet = SearchStyle(rSearchItem, rCol, rRow, rMark);
614 0 : if (bRet)
615 : {
616 : const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
617 0 : pDocument->GetStyleSheetPool()->Find(
618 0 : rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA ));
619 :
620 0 : if (pReplaceStyle)
621 0 : ApplyStyle( rCol, rRow, *pReplaceStyle );
622 : else
623 : {
624 : OSL_FAIL("pReplaceStyle==0");
625 : }
626 : }
627 :
628 0 : return bRet;
629 : }
630 :
631 0 : bool ScTable::SearchAllStyle(
632 : const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges)
633 : {
634 : const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
635 0 : pDocument->GetStyleSheetPool()->Find(
636 0 : rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA ));
637 0 : bool bSelect = rSearchItem.GetSelection();
638 0 : bool bBack = rSearchItem.GetBackward();
639 0 : bool bEverFound = false;
640 :
641 0 : for (SCCOL i=0; i<=MAXCOL; i++)
642 : {
643 0 : bool bFound = true;
644 0 : SCsROW nRow = 0;
645 : SCsROW nEndRow;
646 0 : while (bFound && nRow <= MAXROW)
647 : {
648 0 : bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark );
649 0 : if (bFound)
650 : {
651 0 : if (nEndRow<nRow)
652 : {
653 0 : SCsROW nTemp = nRow;
654 0 : nRow = nEndRow;
655 0 : nEndRow = nTemp;
656 : }
657 0 : rMatchedRanges.Join(ScRange(i, nRow, nTab, i, nEndRow, nTab));
658 0 : nRow = nEndRow + 1;
659 0 : bEverFound = true;
660 : }
661 : }
662 : }
663 :
664 0 : return bEverFound;
665 : }
666 :
667 0 : bool ScTable::ReplaceAllStyle(
668 : const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
669 : ScDocument* pUndoDoc)
670 : {
671 0 : bool bRet = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
672 0 : if (bRet)
673 : {
674 : const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
675 0 : pDocument->GetStyleSheetPool()->Find(
676 0 : rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA ));
677 :
678 0 : if (pReplaceStyle)
679 : {
680 0 : if (pUndoDoc)
681 : pDocument->CopyToDocument( 0,0,nTab, MAXCOL,MAXROW,nTab,
682 0 : IDF_ATTRIB, true, pUndoDoc, &rMark );
683 0 : ApplySelectionStyle( *pReplaceStyle, rMark );
684 : }
685 : else
686 : {
687 : OSL_FAIL("pReplaceStyle==0");
688 : }
689 : }
690 :
691 0 : return bRet;
692 : }
693 :
694 80 : bool ScTable::SearchAndReplace(
695 : const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
696 : ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
697 : {
698 80 : SvxSearchCmd nCommand = rSearchItem.GetCommand();
699 80 : bool bFound = false;
700 160 : if ( ValidColRow(rCol, rRow) ||
701 25 : ((nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE) &&
702 67 : (((rCol == MAXCOLCOUNT || rCol == -1) && ValidRow(rRow)) ||
703 42 : ((rRow == MAXROWCOUNT || rRow == -1) && ValidCol(rCol))
704 : )
705 : )
706 : )
707 : {
708 80 : bool bStyles = rSearchItem.GetPattern();
709 80 : if (bStyles)
710 : {
711 0 : if (nCommand == SvxSearchCmd::FIND)
712 0 : bFound = SearchStyle(rSearchItem, rCol, rRow, rMark);
713 0 : else if (nCommand == SvxSearchCmd::REPLACE)
714 0 : bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, false);
715 0 : else if (nCommand == SvxSearchCmd::FIND_ALL)
716 0 : bFound = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
717 0 : else if (nCommand == SvxSearchCmd::REPLACE_ALL)
718 0 : bFound = ReplaceAllStyle(rSearchItem, rMark, rMatchedRanges, pUndoDoc);
719 : }
720 : else
721 : {
722 : // SearchParam no longer needed - SearchOptions contains all settings
723 80 : com::sun::star::util::SearchOptions aSearchOptions = rSearchItem.GetSearchOptions();
724 80 : aSearchOptions.Locale = *ScGlobal::GetLocale();
725 :
726 80 : if (aSearchOptions.searchString.isEmpty())
727 : {
728 : // Search for empty cells.
729 0 : return SearchAndReplaceEmptyCells(rSearchItem, rCol, rRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
730 : }
731 :
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 80 : if ( !rSearchItem.IsUseAsianOptions() )
736 : aSearchOptions.transliterateFlags &=
737 : ( com::sun::star::i18n::TransliterationModules_IGNORE_CASE |
738 80 : com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH );
739 :
740 80 : pSearchText = new utl::TextSearch( aSearchOptions );
741 :
742 80 : if (nCommand == SvxSearchCmd::FIND)
743 31 : bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
744 49 : else if (nCommand == SvxSearchCmd::FIND_ALL)
745 23 : bFound = SearchAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
746 26 : else if (nCommand == SvxSearchCmd::REPLACE)
747 0 : bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
748 26 : else if (nCommand == SvxSearchCmd::REPLACE_ALL)
749 26 : bFound = ReplaceAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
750 :
751 80 : delete pSearchText;
752 80 : pSearchText = NULL;
753 : }
754 : }
755 80 : return bFound;
756 : }
757 :
758 0 : bool ScTable::SearchAndReplaceEmptyCells(
759 : const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
760 : ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
761 : {
762 : SCCOL nColStart, nColEnd;
763 : SCROW nRowStart, nRowEnd;
764 0 : GetFirstDataPos(nColStart, nRowStart);
765 0 : GetLastDataPos(nColEnd, nRowEnd);
766 :
767 0 : ScRangeList aRanges;
768 0 : aRanges.Append(ScRange(nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab));
769 :
770 0 : if (rSearchItem.GetSelection())
771 : {
772 : // current selection only.
773 0 : if (!rMark.IsMarked() && !rMark.IsMultiMarked())
774 : // There is no selection. Bail out.
775 0 : return false;
776 :
777 0 : ScRangeList aMarkedRanges, aNewRanges;
778 0 : rMark.FillRangeListWithMarks(&aMarkedRanges, true);
779 0 : for ( size_t i = 0, n = aMarkedRanges.size(); i < n; ++i )
780 : {
781 0 : ScRange* p = aMarkedRanges[ i ];
782 0 : if (p->aStart.Col() > nColEnd || p->aStart.Row() > nRowEnd)
783 : // This range is outside the data area. Skip it.
784 0 : continue;
785 :
786 : // Shrink the range into data area only.
787 0 : if (p->aStart.Col() < nColStart)
788 0 : p->aStart.SetCol(rCol);
789 0 : if (p->aStart.Row() < nRowStart)
790 0 : p->aStart.SetRow(rRow);
791 :
792 0 : if (p->aEnd.Col() > nColEnd)
793 0 : p->aEnd.SetCol(nColEnd);
794 0 : if (p->aEnd.Row() > nRowEnd)
795 0 : p->aEnd.SetRow(nRowEnd);
796 :
797 0 : aNewRanges.Append(*p);
798 : }
799 0 : aRanges = aNewRanges;
800 : }
801 :
802 0 : SvxSearchCmd nCommand = rSearchItem.GetCommand();
803 0 : if (nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE)
804 : {
805 0 : if (rSearchItem.GetBackward())
806 : {
807 0 : for ( size_t i = aRanges.size(); i > 0; --i )
808 : {
809 0 : ScRange* p = aRanges[ i - 1 ];
810 0 : if (SearchRangeForEmptyCell(*p, rSearchItem, rCol, rRow, rUndoStr))
811 0 : return true;
812 : }
813 : }
814 : else
815 : {
816 0 : for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
817 : {
818 0 : ScRange* p = aRanges[ i ];
819 0 : if (SearchRangeForEmptyCell(*p, rSearchItem, rCol, rRow, rUndoStr))
820 0 : return true;
821 : }
822 0 : }
823 : }
824 0 : else if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
825 : {
826 0 : bool bFound = false;
827 0 : for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
828 : {
829 0 : ScRange* p = aRanges[ i ];
830 0 : bFound |= SearchRangeForAllEmptyCells(*p, rSearchItem, rMatchedRanges, rUndoStr, pUndoDoc);
831 : }
832 0 : return bFound;
833 : }
834 0 : return false;
835 : }
836 :
837 : namespace {
838 :
839 0 : bool lcl_maybeReplaceCellString(
840 : ScColumn& rColObj, SCCOL& rCol, SCROW& rRow, OUString& rUndoStr, SCCOL nCol, SCROW nRow, const SvxSearchItem& rSearchItem)
841 : {
842 0 : ScRefCellValue aCell = rColObj.GetCellValue(nRow);
843 0 : if (aCell.isEmpty())
844 : {
845 : // empty cell found.
846 0 : rCol = nCol;
847 0 : rRow = nRow;
848 0 : if (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE &&
849 0 : !rSearchItem.GetReplaceString().isEmpty())
850 : {
851 0 : rColObj.SetRawString(nRow, rSearchItem.GetReplaceString());
852 0 : rUndoStr.clear();
853 : }
854 0 : return true;
855 : }
856 0 : return false;
857 : }
858 :
859 : }
860 :
861 0 : bool ScTable::SearchRangeForEmptyCell(
862 : const ScRange& rRange, const SvxSearchItem& rSearchItem,
863 : SCCOL& rCol, SCROW& rRow, OUString& rUndoStr)
864 : {
865 0 : SvxSearchCmd nCmd = rSearchItem.GetCommand();
866 0 : bool bSkipFiltered = rSearchItem.IsSearchFiltered();
867 0 : if (rSearchItem.GetBackward())
868 : {
869 : // backward search
870 0 : if (rSearchItem.GetRowDirection())
871 : {
872 : // row direction.
873 0 : SCROW nLastNonFilteredRow = MAXROW + 1;
874 0 : SCROW nBeginRow = rRange.aEnd.Row() > rRow ? rRow : rRange.aEnd.Row();
875 0 : for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
876 : {
877 0 : if (bSkipFiltered)
878 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, false);
879 0 : if (nRow < rRange.aStart.Row())
880 0 : break;
881 :
882 0 : SCCOL nBeginCol = rRange.aEnd.Col();
883 0 : if (nRow == rRow && nBeginCol >= rCol)
884 : // always start from one cell before the cursor.
885 0 : nBeginCol = rCol - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
886 :
887 0 : for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
888 : {
889 0 : if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
890 0 : return true;
891 : }
892 : }
893 : }
894 : else
895 : {
896 : // column direction.
897 0 : SCCOL nBeginCol = rRange.aEnd.Col() > rCol ? rCol : rRange.aEnd.Col();
898 0 : for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
899 : {
900 0 : SCROW nLastNonFilteredRow = MAXROW + 1;
901 0 : SCROW nBeginRow = rRange.aEnd.Row();
902 0 : if (nCol == rCol && nBeginRow >= rRow)
903 : // always start from one cell before the cursor.
904 0 : nBeginRow = rRow - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
905 0 : for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
906 : {
907 0 : if (bSkipFiltered)
908 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, false);
909 0 : if (nRow < rRange.aStart.Row())
910 0 : break;
911 :
912 0 : if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
913 0 : return true;
914 : }
915 : }
916 : }
917 : }
918 : else
919 : {
920 : // forward search
921 0 : if (rSearchItem.GetRowDirection())
922 : {
923 : // row direction.
924 0 : SCROW nLastNonFilteredRow = -1;
925 0 : SCROW nBeginRow = rRange.aStart.Row() < rRow ? rRow : rRange.aStart.Row();
926 0 : for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
927 : {
928 0 : if (bSkipFiltered)
929 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, true);
930 0 : if (nRow > rRange.aEnd.Row())
931 0 : break;
932 :
933 0 : SCCOL nBeginCol = rRange.aStart.Col();
934 0 : if (nRow == rRow && nBeginCol <= rCol)
935 : // always start from one cell past the cursor.
936 0 : nBeginCol = rCol + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
937 0 : for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
938 : {
939 0 : if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
940 0 : return true;
941 : }
942 : }
943 : }
944 : else
945 : {
946 : // column direction.
947 0 : SCCOL nBeginCol = rRange.aStart.Col() < rCol ? rCol : rRange.aStart.Col();
948 0 : for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
949 : {
950 0 : SCROW nLastNonFilteredRow = -1;
951 0 : SCROW nBeginRow = rRange.aStart.Row();
952 0 : if (nCol == rCol && nBeginRow <= rRow)
953 : // always start from one cell past the cursor.
954 0 : nBeginRow = rRow + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
955 0 : for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
956 : {
957 0 : if (bSkipFiltered)
958 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, true);
959 0 : if (nRow > rRange.aEnd.Row())
960 0 : break;
961 :
962 0 : if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
963 0 : return true;
964 : }
965 : }
966 : }
967 : }
968 0 : return false;
969 : }
970 :
971 0 : bool ScTable::SearchRangeForAllEmptyCells(
972 : const ScRange& rRange, const SvxSearchItem& rSearchItem,
973 : ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
974 : {
975 0 : bool bFound = false;
976 0 : bool bReplace = (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) &&
977 0 : !rSearchItem.GetReplaceString().isEmpty();
978 0 : bool bSkipFiltered = rSearchItem.IsSearchFiltered();
979 :
980 0 : for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
981 : {
982 0 : SCROW nLastNonFilteredRow = -1;
983 0 : if (aCol[nCol].IsEmptyData())
984 : {
985 : // The entire column is empty.
986 0 : for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
987 : {
988 : SCROW nLastRow;
989 0 : if (!RowFiltered(nRow, NULL, &nLastRow))
990 : {
991 0 : rMatchedRanges.Join(ScRange(nCol, nRow, nTab, nCol, nLastRow, nTab));
992 0 : if (bReplace)
993 : {
994 0 : const OUString& rNewStr = rSearchItem.GetReplaceString();
995 0 : for (SCROW i = nRow; i <= nLastRow; ++i)
996 : {
997 0 : aCol[nCol].SetRawString(i, rNewStr);
998 0 : if (pUndoDoc)
999 : {
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 0 : ScSetStringParam aParam;
1004 0 : aParam.setTextInput();
1005 0 : pUndoDoc->SetString(ScAddress(nCol, i, nTab), EMPTY_OUSTRING);
1006 : }
1007 : }
1008 0 : rUndoStr.clear();
1009 : }
1010 : }
1011 :
1012 0 : nRow = nLastRow; // move to the last filtered row.
1013 : }
1014 0 : bFound = true;
1015 0 : continue;
1016 : }
1017 :
1018 0 : for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
1019 : {
1020 0 : if (bSkipFiltered)
1021 0 : SkipFilteredRows(nRow, nLastNonFilteredRow, true);
1022 0 : if (nRow > rRange.aEnd.Row())
1023 0 : break;
1024 :
1025 0 : ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
1026 0 : if (aCell.isEmpty())
1027 : {
1028 : // empty cell found
1029 0 : rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
1030 0 : bFound = true;
1031 :
1032 0 : if (bReplace)
1033 : {
1034 0 : aCol[nCol].SetRawString(nRow, rSearchItem.GetReplaceString());
1035 0 : if (pUndoDoc)
1036 : {
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 0 : ScSetStringParam aParam;
1041 0 : aParam.setTextInput();
1042 0 : pUndoDoc->SetString(ScAddress(nCol, nRow, nTab), EMPTY_OUSTRING);
1043 : }
1044 : }
1045 : }
1046 0 : }
1047 : }
1048 0 : return bFound;
1049 156 : }
1050 :
1051 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|