Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/uiview/viewsrch.cxx
Line
Count
Source
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 <string>
21
22
#include <memory>
23
#include <boost/property_tree/json_parser.hpp>
24
25
#include <hintids.hxx>
26
27
#include <sal/log.hxx>
28
#include <svl/cjkoptions.hxx>
29
#include <svl/ctloptions.hxx>
30
#include <svx/pageitem.hxx>
31
#include <svl/whiter.hxx>
32
#include <sfx2/viewfrm.hxx>
33
#include <svl/eitem.hxx>
34
#include <svl/srchitem.hxx>
35
#include <sfx2/bindings.hxx>
36
#include <sfx2/request.hxx>
37
#include <sfx2/lokhelper.hxx>
38
#include <svx/srchdlg.hxx>
39
#include <pamtyp.hxx>
40
#include <swmodule.hxx>
41
#include <swwait.hxx>
42
#include <workctrl.hxx>
43
#include <view.hxx>
44
#include <wrtsh.hxx>
45
#include <swundo.hxx>
46
#include <uitool.hxx>
47
#include <wview.hxx>
48
#include <cmdid.h>
49
#include <docsh.hxx>
50
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
51
#include <comphelper/lok.hxx>
52
#include <comphelper/string.hxx>
53
54
#include <strings.hrc>
55
#include <SwRewriter.hxx>
56
57
#include <PostItMgr.hxx>
58
59
using namespace com::sun::star;
60
using namespace ::com::sun::star::lang;
61
62
//Search Parameter
63
64
struct SwSearchOptions
65
{
66
    SwDocPositions eStart, eEnd;
67
    bool bDontWrap;
68
69
    SwSearchOptions( SwWrtShell const * pSh, bool bBackward );
70
};
71
72
/// Adds rMatches using rKey as a key to the rTree tree.
73
static void lcl_addContainerToJson(boost::property_tree::ptree& rTree, const OString& rKey, const std::vector<OString>& rMatches)
74
0
{
75
0
    boost::property_tree::ptree aChildren;
76
77
0
    for (const OString& rMatch : rMatches)
78
0
    {
79
0
        boost::property_tree::ptree aChild;
80
0
        aChild.put("part", "0");
81
0
        aChild.put("rectangles", rMatch.getStr());
82
0
        aChildren.push_back(std::make_pair("", aChild));
83
0
    }
84
85
0
    rTree.add_child(rKey.getStr(), aChildren);
86
0
}
87
88
/// Emits LOK callbacks (count, selection) for search results.
89
static void lcl_emitSearchResultCallbacks(SvxSearchItem const * pSearchItem, SwWrtShell const * pWrtShell, bool bHighlightAll)
90
0
{
91
    // Emit a callback also about the selection rectangles, grouped by matches.
92
0
    SwPaM* pPaM = pWrtShell->GetCursor();
93
0
    if (!pPaM)
94
0
        return;
95
96
0
    std::vector<OString> aMatches;
97
0
    for (SwPaM& rPaM : pPaM->GetRingContainer())
98
0
    {
99
0
        if (SwShellCursor* pShellCursor = dynamic_cast<SwShellCursor*>(&rPaM))
100
0
        {
101
0
            std::vector<OString> aSelectionRectangles;
102
0
            pShellCursor->SwSelPaintRects::Show(&aSelectionRectangles);
103
0
            std::vector<OString> aRect;
104
0
            for (const OString & rSelectionRectangle : aSelectionRectangles)
105
0
            {
106
0
                if (rSelectionRectangle.isEmpty())
107
0
                    continue;
108
0
                aRect.push_back(rSelectionRectangle);
109
0
            }
110
0
            OString sRect = comphelper::string::join("; ", aRect);
111
0
            aMatches.push_back(sRect);
112
0
        }
113
0
    }
114
0
    boost::property_tree::ptree aTree;
115
0
    aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr());
116
0
    aTree.put("highlightAll", bHighlightAll);
117
0
    lcl_addContainerToJson(aTree, "searchResultSelection"_ostr, aMatches);
118
119
0
    std::stringstream aStream;
120
0
    boost::property_tree::write_json(aStream, aTree);
121
0
    OString aPayload( aStream.str() );
122
123
0
    SfxViewShell* pNotifySh = pWrtShell->GetSfxViewShell();
124
0
    if (pNotifySh)
125
0
        pNotifySh->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload);
126
127
0
    if(bHighlightAll)
128
0
    {   // FindAll disables this during find, do it once when done.
129
0
        SfxLokHelper::notifyUpdate(pNotifySh,LOK_CALLBACK_TEXT_SELECTION);
130
0
        SfxLokHelper::notifyOtherViewsUpdatePerViewId(pNotifySh, LOK_CALLBACK_TEXT_VIEW_SELECTION);
131
0
    }
132
0
}
133
134
static bool lcl_IsNotTextSearch(const SvxSearchItem* pSearchItem)
135
0
{
136
0
    if (!pSearchItem || pSearchItem->GetSearchString().isEmpty())
137
0
        return true;
138
139
0
    return pSearchItem->GetPattern(); // paragraph style search
140
0
}
141
142
void SwView::ExecSearch(SfxRequest& rReq)
143
0
{
144
0
    GetWrtShell().addCurrentPosition();
145
146
0
    const SfxItemSet* pArgs = rReq.GetArgs();
147
0
    const SfxPoolItem* pItem = nullptr;
148
0
    bool bQuiet = false;
149
0
    if(pArgs && SfxItemState::SET == pArgs->GetItemState(SID_SEARCH_QUIET, false, &pItem))
150
0
        bQuiet = static_cast<const SfxBoolItem*>( pItem)->GetValue();
151
152
0
    sal_uInt16 nSlot = rReq.GetSlot();
153
0
    if (nSlot == FN_REPEAT_SEARCH && !s_pSrchItem)
154
0
    {
155
0
        if(bQuiet)
156
0
        {
157
0
            rReq.SetReturnValue(SfxBoolItem(nSlot, false));
158
0
            nSlot = 0;
159
0
        }
160
0
    }
161
0
    if( m_pWrtShell->IsBlockMode() )
162
0
        m_pWrtShell->LeaveBlockMode();
163
0
    switch (nSlot)
164
0
    {
165
    // for now do nothing
166
0
    case SID_SEARCH_ITEM:
167
0
    {
168
0
        OSL_ENSURE(pArgs, "Args missing");
169
0
        if (pArgs)
170
0
        {
171
0
            delete s_pSrchItem;
172
0
            s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone();
173
0
        }
174
0
    }
175
0
    break;
176
177
0
    case FID_SEARCH_ON:
178
0
    {
179
0
        s_bJustOpened = true;
180
0
        GetViewFrame().GetBindings().Invalidate(SID_SEARCH_ITEM);
181
182
        // ensure SvxSearchDialog's Format dialog uses the preferred measurement unit
183
0
        const bool bIsHtmlMode = dynamic_cast<SwWebView*>(this);
184
0
        auto nMetric = static_cast<sal_uInt16>(GetDfltMetric(bIsHtmlMode));
185
0
        SwModule::get()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, nMetric));
186
0
    }
187
0
    break;
188
189
0
    case FID_SEARCH_OFF:
190
0
        if(pArgs)
191
0
        {
192
            // Unregister dialog
193
0
            delete s_pSrchItem;
194
0
            s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone();
195
196
0
            s_xSearchList.reset();
197
0
            s_xReplaceList.reset();
198
199
0
            SvxSearchDialog *const pSrchDlg(GetSearchDialog());
200
0
            if (pSrchDlg)
201
0
            {
202
                // We will remember the search-/replace items.
203
0
                const SearchAttrItemList* pList = pSrchDlg->GetSearchItemList();
204
0
                if( nullptr != pList && pList->Count() )
205
0
                    s_xSearchList.reset(new SearchAttrItemList( *pList ));
206
207
0
                pList = pSrchDlg->GetReplaceItemList();
208
0
                if (nullptr != pList && pList->Count())
209
0
                    s_xReplaceList.reset(new SearchAttrItemList( *pList ));
210
0
            }
211
0
        }
212
0
        break;
213
214
0
        case FN_REPEAT_SEARCH:
215
0
        case FID_SEARCH_NOW:
216
0
        {
217
0
            sal_uInt16 nMoveType = SwView::GetMoveType();
218
0
            {
219
0
                if(FID_SEARCH_NOW == nSlot && !rReq.IsAPI())
220
0
                    SwView::SetMoveType(NID_SRCH_REP);
221
0
            }
222
223
0
            SvxSearchDialog * pSrchDlg(GetSearchDialog());
224
0
            if (pSrchDlg)
225
0
            {
226
0
                s_xSearchList.reset();
227
0
                s_xReplaceList.reset();
228
229
0
                const SearchAttrItemList* pList = pSrchDlg->GetSearchItemList();
230
0
                if( nullptr != pList && pList->Count() )
231
0
                    s_xSearchList.reset(new SearchAttrItemList( *pList ));
232
233
0
                pList = pSrchDlg->GetReplaceItemList();
234
0
                if (nullptr != pList && pList->Count())
235
0
                    s_xReplaceList.reset(new SearchAttrItemList( *pList ));
236
0
            }
237
238
0
            if (nSlot == FN_REPEAT_SEARCH)
239
0
            {
240
0
                OSL_ENSURE(s_pSrchItem, "SearchItem missing");
241
0
                if( !s_pSrchItem )
242
0
                    s_pSrchItem = new SvxSearchItem(SID_SEARCH_ITEM);
243
0
            }
244
0
            else
245
0
            {
246
                // Get SearchItem from request
247
0
                OSL_ENSURE(pArgs, "Args missing");
248
0
                if ( pArgs )
249
0
                {
250
0
                    delete s_pSrchItem;
251
0
                    s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone();
252
0
                }
253
0
            }
254
255
            // tdf#115665 Format in the CTRL+H search affects the CTRL+F search
256
0
            if (pArgs)
257
0
            {
258
0
                if (const SfxBoolItem* pUseAttrItemListItem
259
0
                    = pArgs->GetItemIfSet(SID_SEARCH_USE_ATTR_ITEM_LIST, false))
260
0
                {
261
0
                    if (!pUseAttrItemListItem->GetValue())
262
0
                        s_xSearchList.reset();
263
0
                }
264
0
            }
265
266
0
            SvxSearchCmd eCommand = s_pSrchItem->GetCommand();
267
0
            switch (eCommand)
268
0
            {
269
0
            case SvxSearchCmd::FIND:
270
0
            {
271
0
                bool bRet = SearchAndWrap(bQuiet);
272
0
                if( bRet )
273
0
                {
274
0
                    Scroll(m_pWrtShell->GetCharRect().SVRect());
275
0
                    if (comphelper::LibreOfficeKit::isActive())
276
0
                        lcl_emitSearchResultCallbacks(s_pSrchItem, m_pWrtShell.get(), /* bHighlightAll = */ false);
277
0
                }
278
0
                rReq.SetReturnValue(SfxBoolItem(nSlot, bRet));
279
280
0
                GetDocShell()->Broadcast(SfxHint(SfxHintId::SwNavigatorUpdateTracking));
281
0
            }
282
0
            break;
283
0
            case SvxSearchCmd::FIND_ALL:
284
0
            {
285
                // Disable LOK selection notifications during search.
286
0
                m_pWrtShell->GetSfxViewShell()->setTiledSearching(true);
287
0
                const auto nFound = SearchAll();
288
0
                m_pWrtShell->GetSfxViewShell()->setTiledSearching(false);
289
290
0
                GetDocShell()->Broadcast(
291
0
                            SfxHint(SfxHintId::SwNavigatorUpdateTracking));
292
0
                GetDocShell()->Broadcast(
293
0
                            SfxHint(SfxHintId::SwNavigatorSelectOutlinesWithSelections));
294
295
0
                if (nFound == 0)
296
0
                {
297
#if HAVE_FEATURE_DESKTOP
298
                    if( !bQuiet )
299
                    {
300
                        if (SfxViewShell* pNotifySh = m_pWrtShell->GetSfxViewShell())
301
                            pNotifySh->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
302
                        SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
303
                    }
304
#endif
305
0
                    s_bFound = false;
306
0
                }
307
0
                else
308
0
                {
309
0
                    if (comphelper::LibreOfficeKit::isActive())
310
0
                        lcl_emitSearchResultCallbacks(s_pSrchItem, m_pWrtShell.get(), /* bHighlightAll = */ true);
311
0
                    if (!bQuiet)
312
0
                    {
313
0
                        OUString sText(SwResId(STR_SEARCH_KEY_FOUND_TIMES, nFound));
314
0
                        sText = sText.replaceFirst("%1", OUString::number(nFound));
315
0
                        SvxSearchDialogWrapper::SetSearchLabel(sText);
316
0
                    }
317
0
                }
318
0
                rReq.SetReturnValue(SfxBoolItem(nSlot, nFound != 0));
319
0
            }
320
0
            break;
321
0
            case SvxSearchCmd::REPLACE:
322
0
                {
323
324
                    // 1) Replace selection
325
326
                    // Prevent, that the replaced string will be found again
327
                    // if the replacement string is containing the search string.
328
0
                    bool bBack = s_pSrchItem->GetBackward();
329
0
                    if (bBack)
330
0
                        m_pWrtShell->Push();
331
0
                    OUString aReplace( s_pSrchItem->GetReplaceString() );
332
0
                    std::optional<OUString> xBackRef = sw::ReplaceBackReferences(
333
0
                        s_pSrchItem->GetSearchOptions(), m_pWrtShell->GetCursor(),
334
0
                        m_pWrtShell->GetLayout());
335
0
                    if( xBackRef )
336
0
                        s_pSrchItem->SetReplaceString( *xBackRef );
337
0
                    Replace();
338
0
                    if( xBackRef )
339
0
                    {
340
0
                        s_pSrchItem->SetReplaceString( aReplace );
341
0
                    }
342
0
                    if (bBack)
343
0
                    {
344
0
                        m_pWrtShell->Pop();
345
0
                        m_pWrtShell->SwapPam();
346
0
                    }
347
348
                    // 2) Search further (without replacing!)
349
350
0
                    SvxSearchCmd nOldCmd = s_pSrchItem->GetCommand();
351
0
                    s_pSrchItem->SetCommand( SvxSearchCmd::FIND );
352
0
                    bool bRet = SearchAndWrap(bQuiet);
353
0
                    if( bRet )
354
0
                        Scroll( m_pWrtShell->GetCharRect().SVRect());
355
0
                    s_pSrchItem->SetCommand( nOldCmd );
356
0
                    rReq.SetReturnValue(SfxBoolItem(nSlot, bRet));
357
0
                }
358
0
                break;
359
360
0
            case SvxSearchCmd::REPLACE_ALL:
361
0
                {
362
0
                    SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
363
0
                    aOpts.eStart
364
0
                        = s_pSrchItem->GetBackward() ? SwDocPositions::End : SwDocPositions::Start;
365
0
                    s_bExtra = false;
366
0
                    sal_Int32 nFound;
367
368
0
                    {   //Scope for SwWait-Object
369
0
                        SwWait aWait( *GetDocShell(), true );
370
0
                        m_pWrtShell->StartAllAction();
371
372
                        // i#8288 "replace all" should not change cursor
373
                        // position, so save current cursor
374
0
                        m_pWrtShell->Push();
375
376
0
                        if (!s_pSrchItem->GetSelection())
377
0
                        {
378
                            // if we don't want to search in the selection...
379
0
                            m_pWrtShell->KillSelection(nullptr, false);
380
0
                            if (SwDocPositions::Start == aOpts.eEnd)
381
0
                            {
382
0
                                m_pWrtShell->EndOfSection();
383
0
                            }
384
0
                            else
385
0
                            {
386
0
                                m_pWrtShell->StartOfSection();
387
0
                            }
388
0
                        }
389
0
                        nFound = FUNC_Search( aOpts );
390
                        // create it just to overwrite it with stack cursor
391
0
                        m_pWrtShell->CreateCursor();
392
                        // i#8288 restore the original cursor position
393
0
                        m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
394
0
                        m_pWrtShell->EndAllAction();
395
0
                    }
396
397
0
                    rReq.SetReturnValue(SfxBoolItem(nSlot, nFound != 0 && SAL_MAX_INT32 != nFound));
398
0
                    if( !nFound )
399
0
                    {
400
#if HAVE_FEATURE_DESKTOP
401
                        if( !bQuiet )
402
                        {
403
                            if (SfxViewShell* pNotifySh = m_pWrtShell->GetSfxViewShell())
404
                                pNotifySh->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
405
                            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
406
                        }
407
#endif
408
0
                        s_bFound = false;
409
0
                        SwView::SetMoveType(nMoveType);
410
0
                        return;
411
0
                    }
412
413
0
                    if (!bQuiet && SAL_MAX_INT32 != nFound)
414
0
                    {
415
0
                        OUString sText( SwResId( STR_NB_REPLACED ) );
416
0
                        sText = sText.replaceFirst("XX", OUString::number( nFound ));
417
0
                        SvxSearchDialogWrapper::SetSearchLabel(sText);
418
0
                    }
419
0
                }
420
0
                break;
421
0
            }
422
423
0
            uno::Reference< frame::XDispatchRecorder > xRecorder =
424
0
                    GetViewFrame().GetBindings().GetRecorder();
425
            //prevent additional dialogs in recorded macros
426
0
            if ( xRecorder.is() )
427
0
                rReq.AppendItem(SfxBoolItem(SID_SEARCH_QUIET, true));
428
429
0
            rReq.Done();
430
0
            m_eLastSearchCommand = s_pSrchItem->GetCommand();
431
0
            SwView::SetMoveType(nMoveType);
432
0
        }
433
0
        break;
434
0
        case FID_SEARCH_SEARCHSET:
435
0
        case FID_SEARCH_REPLACESET:
436
0
        {
437
0
            static const WhichRangesContainer aNormalAttr(svl::Items<
438
0
                RES_CHRATR_CASEMAP,     RES_CHRATR_CASEMAP,
439
0
                RES_CHRATR_COLOR,       RES_CHRATR_POSTURE,
440
0
                RES_CHRATR_SHADOWED,    RES_CHRATR_WORDLINEMODE,
441
0
                RES_CHRATR_BLINK,       RES_CHRATR_BLINK,
442
0
                RES_CHRATR_BACKGROUND,  RES_CHRATR_BACKGROUND,
443
0
                RES_CHRATR_ROTATE,      RES_CHRATR_ROTATE,
444
0
                RES_CHRATR_SCALEW,      RES_CHRATR_OVERLINE,
445
0
                RES_PARATR_LINESPACING, RES_PARATR_HYPHENZONE,
446
0
                RES_PARATR_REGISTER,    RES_PARATR_REGISTER,
447
0
                RES_PARATR_VERTALIGN,   RES_PARATR_VERTALIGN,
448
0
                RES_MARGIN_FIRSTLINE,   RES_MARGIN_RIGHT,
449
0
                RES_UL_SPACE,           RES_UL_SPACE,
450
0
                SID_ATTR_PARA_PAGEBREAK, SID_ATTR_PARA_PAGEBREAK,
451
0
                SID_ATTR_PARA_MODEL,    SID_ATTR_PARA_MODEL,
452
0
                SID_ATTR_PARA_KEEP,     SID_ATTR_PARA_KEEP
453
0
            >);
454
455
0
            SfxItemSet aSet(m_pWrtShell->GetAttrPool(), aNormalAttr);
456
457
0
            if( SvtCTLOptions::IsCTLFontEnabled() )
458
0
            {
459
0
                aSet.MergeRange(RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_WEIGHT);
460
0
            }
461
0
            if( SvtCJKOptions::IsAnyEnabled() )
462
0
            {
463
0
                aSet.MergeRange(RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_WEIGHT);
464
0
                aSet.MergeRange(RES_CHRATR_EMPHASIS_MARK, RES_CHRATR_TWO_LINES);
465
0
                aSet.MergeRange(RES_PARATR_SCRIPTSPACE, RES_PARATR_FORBIDDEN_RULES);
466
0
            }
467
468
0
            TypedWhichId<SvxSetItem> nWhich = SID_SEARCH_SEARCHSET;
469
470
0
            if ( FID_SEARCH_REPLACESET == nSlot )
471
0
            {
472
0
                nWhich = SID_SEARCH_REPLACESET;
473
474
0
                if ( s_xReplaceList )
475
0
                {
476
0
                    s_xReplaceList->Get( aSet );
477
0
                    s_xReplaceList.reset();
478
0
                }
479
0
            }
480
0
            else if ( s_xSearchList )
481
0
            {
482
0
                s_xSearchList->Get( aSet );
483
0
                s_xSearchList.reset();
484
0
            }
485
0
            rReq.SetReturnValue( SvxSetItem( nWhich, aSet ) );
486
0
        }
487
0
        break;
488
0
        default:
489
0
            SAL_WARN_IF( nSlot, "sw", "nSlot: " << nSlot << " wrong Dispatcher (viewsrch.cxx)" );
490
0
            return;
491
0
    }
492
0
}
493
494
bool SwView::SearchAndWrap(bool bApi)
495
0
{
496
    // tdf#124442 moved m_pWrtShell->GetCursor()->Normalize to before SwSearchOptions aOpts is
497
    // constructed to fix search key not found when a forward search (Find Next) results in a
498
    // unique find selection to the end of the document followed by a backward search (Find
499
    // Previous) or when a backward search results in a unique find selection to the start of the
500
    // document followed by a forward search. Unique find selection meaning the document
501
    // contains only one match of the search key.
502
503
    // fdo#65014 : Ensure that the point of the cursor is at the extremity of the
504
    // selection closest to the end being searched to as to exclude the selected
505
    // region from the search. (This doesn't work in the case of multiple
506
    // selected regions as the cursor doesn't mark the selection in that case.)
507
0
    m_pWrtShell->GetCursor()->Normalize( s_pSrchItem->GetBackward() );
508
509
0
    SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
510
511
        // Remember starting position of the search for wraparound
512
        // Start- / EndAction perhaps because existing selections of 'search all'
513
0
    m_pWrtShell->StartAllAction();
514
0
    m_pWrtShell->Push();
515
516
    // After a search all action we place the cursor at the beginning of
517
    // the document so that the single search selects the first matching
518
    // occurrence in the document instead of the second.
519
0
    if( m_eLastSearchCommand == SvxSearchCmd::FIND_ALL )
520
0
    {
521
0
        if( SwDocPositions::Start == aOpts.eEnd )
522
0
            m_pWrtShell->EndOfSection();
523
0
        else
524
0
            m_pWrtShell->StartOfSection();
525
0
    }
526
527
0
    if (!s_pSrchItem->GetSelection())
528
0
    {
529
        // If you want to search in selected areas, they must not be unselected.
530
0
        m_pWrtShell->KillSelection(nullptr, false);
531
532
0
        if (s_pSrchItem->HasStartPoint())
533
0
        {
534
            // No selection -> but we have a start point (top left corner of the
535
            // current view), start searching from there, not from the current
536
            // cursor position.
537
0
            SwEditShell& rShell = GetWrtShell();
538
0
            Point aPosition(s_pSrchItem->GetStartPointX(), s_pSrchItem->GetStartPointY());
539
0
            rShell.SetCursor(aPosition);
540
0
        }
541
0
        else if (m_pWrtShell->GetCursor()->GetPointContentNode()
542
0
                 && lcl_IsNotTextSearch(s_pSrchItem))
543
0
        {
544
            // if already at the end then start at the next paragraph
545
0
            SwCursor* pCursor = m_pWrtShell->GetCursor();
546
0
            const sal_Int32 nIndex = pCursor->GetPoint()->GetContentIndex();
547
0
            const bool bToNextPara
548
0
                = s_pSrchItem->GetBackward()
549
0
                    ? !nIndex
550
0
                    : nIndex == pCursor->GetPointContentNode()->Len();
551
0
            if (bToNextPara)
552
0
            {
553
0
                SwMoveFnCollection const & fnMove
554
0
                    = s_pSrchItem->GetBackward() ? fnMoveBackward : fnMoveForward;
555
0
                if (!(*fnMove.fnPos)(pCursor->GetPoint(), false))
556
0
                {
557
0
                    if (s_pSrchItem->GetBackward())
558
0
                        m_pWrtShell->EndOfSection();
559
0
                    else
560
0
                        m_pWrtShell->StartOfSection();
561
0
                }
562
0
                else
563
0
                    pCursor->GetPoint()->SetContent(
564
0
                        s_pSrchItem->GetBackward() ? pCursor->GetPointContentNode()->Len() : 0);
565
0
            }
566
0
        }
567
0
    }
568
569
0
    std::optional<SwWait> oWait( std::in_place, *GetDocShell(), true );
570
0
    if( FUNC_Search( aOpts ) )
571
0
    {
572
0
        s_bFound = true;
573
0
        if(m_pWrtShell->IsSelFrameMode())
574
0
        {
575
0
            m_pWrtShell->Push(); // push the 'found' cursor to the stack
576
0
            m_pWrtShell->UnSelectFrame();
577
0
            m_pWrtShell->LeaveSelFrameMode();
578
0
            m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // restore stack cursor
579
0
        }
580
0
        m_pWrtShell->Pop();
581
0
        m_pWrtShell->EndAllAction();
582
0
        return true;
583
0
    }
584
0
    oWait.reset();
585
586
        // Search in the specialized areas when no search is present in selections.
587
        // When searching selections they will already be searched in these special areas.
588
0
    bool bHasSrchInOther = s_bExtra;
589
0
    if (!s_pSrchItem->GetSelection() && !s_bExtra )
590
0
    {
591
0
        s_bExtra = true;
592
0
        if( FUNC_Search( aOpts ) )
593
0
        {
594
0
            s_bFound = true;
595
0
            m_pWrtShell->Pop();
596
0
            m_pWrtShell->EndAllAction();
597
0
            return true;
598
0
        }
599
0
        s_bExtra = false;
600
0
    }
601
0
    else
602
0
        s_bExtra = !s_bExtra;
603
604
        // If starting position is at the end or beginning of the document.
605
0
    if (aOpts.bDontWrap)
606
0
    {
607
0
        m_pWrtShell->EndAllAction();
608
0
        if( !bApi )
609
0
        {
610
#if HAVE_FEATURE_DESKTOP
611
            if (SfxViewShell* pNotifySh = m_pWrtShell->GetSfxViewShell())
612
                pNotifySh->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
613
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
614
#endif
615
0
        }
616
0
        s_bFound = false;
617
0
        m_pWrtShell->Pop();
618
0
        return false;
619
0
    }
620
0
    m_pWrtShell->EndAllAction();
621
        // Try again with WrapAround?
622
623
0
    m_pWrtShell->StartAllAction();
624
0
    m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
625
0
    oWait.emplace( *GetDocShell(), true );
626
627
0
    bool bSrchBkwrd = SwDocPositions::Start == aOpts.eEnd;
628
629
0
    aOpts.eEnd =  bSrchBkwrd ? SwDocPositions::Start : SwDocPositions::End;
630
0
    aOpts.eStart = bSrchBkwrd ? SwDocPositions::End : SwDocPositions::Start;
631
632
0
    if (bHasSrchInOther)
633
0
    {
634
0
        m_pWrtShell->ClearMark();
635
        // Select the start or the end of the entire document
636
0
        if (bSrchBkwrd)
637
0
            m_pWrtShell->SttEndDoc(false);
638
0
        else
639
0
            m_pWrtShell->SttEndDoc(true);
640
0
    }
641
642
0
    s_bFound = bool(FUNC_Search( aOpts ));
643
644
    // If WrapAround found no matches in the body text, search in the special
645
    // sections, too.
646
0
    if (!s_bFound && !s_pSrchItem->GetSelection() && !s_bExtra)
647
0
    {
648
0
        s_bExtra = true;
649
0
        if (FUNC_Search(aOpts))
650
0
            s_bFound = true;
651
0
        else
652
0
            s_bExtra = false;
653
0
    }
654
655
0
    m_pWrtShell->EndAllAction();
656
0
    oWait.reset();
657
#if HAVE_FEATURE_DESKTOP
658
    if (s_bFound)
659
    {
660
        if (!bSrchBkwrd)
661
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
662
        else
663
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start);
664
    }
665
    else if(!bApi)
666
    {
667
        if (SfxViewShell* pNotifySh = m_pWrtShell->GetSfxViewShell())
668
            pNotifySh->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
669
        SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
670
    }
671
#endif
672
0
    return s_bFound;
673
0
}
674
675
sal_Int32 SwView::SearchAll()
676
0
{
677
0
    SwWait aWait( *GetDocShell(), true );
678
0
    m_pWrtShell->StartAllAction();
679
680
0
    SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
681
0
    aOpts.eStart = s_pSrchItem->GetBackward() ? SwDocPositions::End : SwDocPositions::Start;
682
683
0
    if (!s_pSrchItem->GetSelection())
684
0
    {
685
        // Cancel existing selections, if should not be sought in selected areas.
686
0
        m_pWrtShell->KillSelection(nullptr, false);
687
688
0
        if( SwDocPositions::Start == aOpts.eEnd )
689
0
            m_pWrtShell->EndOfSection();
690
0
        else
691
0
            m_pWrtShell->StartOfSection();
692
0
    }
693
0
    s_bExtra = false;
694
0
    auto nFound = FUNC_Search(aOpts);
695
0
    s_bFound = 0 != nFound;
696
697
0
    m_pWrtShell->EndAllAction();
698
0
    return nFound;
699
0
}
700
701
void SwView::Replace()
702
0
{
703
0
    SwWait aWait( *GetDocShell(), true );
704
705
0
    m_pWrtShell->StartAllAction();
706
0
    const bool bReplaceParagraphStyle = s_pSrchItem->GetPattern();
707
708
0
    if (!bReplaceParagraphStyle && GetPostItMgr()->HasActiveSidebarWin())
709
0
        GetPostItMgr()->Replace(s_pSrchItem);
710
711
0
    bool bReqReplace = true;
712
713
0
    if (m_pWrtShell->HasSelection())
714
0
    {
715
        /* check that the selection match the search string */
716
        //save state
717
0
        SwPosition aStartPos = *m_pWrtShell->GetCursor()->Start();
718
0
        SwPosition aEndPos = *m_pWrtShell->GetCursor()->End();
719
0
        const bool bHasSelection = s_pSrchItem->GetSelection();
720
0
        const SvxSearchCmd nOldCmd = s_pSrchItem->GetCommand();
721
722
        //set state for checking if current selection has a match
723
0
        s_pSrchItem->SetCommand(SvxSearchCmd::FIND);
724
0
        s_pSrchItem->SetSelection(true);
725
726
        //check if it matches
727
0
        SwSearchOptions aOpts(m_pWrtShell.get(), s_pSrchItem->GetBackward());
728
0
        if (!FUNC_Search(aOpts))
729
0
        {
730
            // no matching therefore should not replace selection
731
            // => remove selection
732
733
0
            if (!s_pSrchItem->GetBackward())
734
0
            {
735
0
                (*m_pWrtShell->GetCursor()->Start()) = std::move(aStartPos);
736
0
                (*m_pWrtShell->GetCursor()->End()) = std::move(aEndPos);
737
0
            }
738
0
            else
739
0
            {
740
0
                (*m_pWrtShell->GetCursor()->Start()) = std::move(aEndPos);
741
0
                (*m_pWrtShell->GetCursor()->End()) = std::move(aStartPos);
742
0
            }
743
0
            bReqReplace = false;
744
0
        }
745
746
        // set back old search state
747
0
        s_pSrchItem->SetCommand(nOldCmd);
748
0
        s_pSrchItem->SetSelection(bHasSelection);
749
0
    }
750
    /*
751
        * remove current selection
752
        * otherwise it is always replaced
753
        * no matter if the search string exists or not in the selection
754
        * Now the selection is removed and the next matching string is selected
755
        */
756
757
0
    if (bReqReplace)
758
0
    {
759
0
        if (bReplaceParagraphStyle)
760
0
        {
761
0
            SwRewriter aRewriter;
762
0
            aRewriter.AddRule(UndoArg1, s_pSrchItem->GetSearchString());
763
0
            aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
764
0
            aRewriter.AddRule(UndoArg3, s_pSrchItem->GetReplaceString());
765
766
0
            m_pWrtShell->StartUndo(SwUndoId::UI_REPLACE_STYLE, &aRewriter);
767
768
0
            m_pWrtShell->SetTextFormatColl(m_pWrtShell->GetParaStyle(
769
0
                UIName(s_pSrchItem->GetReplaceString()), SwWrtShell::GETSTYLE_CREATESOME));
770
771
0
            m_pWrtShell->EndUndo();
772
0
        }
773
0
        else
774
0
        {
775
0
            bool bReplaced = true;
776
            // Replace selection (Not if only attributes should be replaced)
777
0
            if (!s_pSrchItem->GetReplaceString().isEmpty() || !s_xReplaceList)
778
0
            {
779
0
                bReplaced = m_pWrtShell->SwEditShell::Replace( s_pSrchItem->GetReplaceString(),
780
0
                                                               s_pSrchItem->GetRegExp() );
781
0
            }
782
            // Replace attributes
783
0
            if( bReplaced && s_xReplaceList && s_xReplaceList->Count() && m_pWrtShell->HasSelection() )
784
0
            {
785
0
                SfxItemSet aReplSet( m_pWrtShell->GetAttrPool(),
786
0
                                     aTextFormatCollSetRange );
787
0
                if( s_xReplaceList->Get( aReplSet ).Count() )
788
0
                    m_pWrtShell->SwEditShell::SetAttrSet( aReplSet );
789
0
            }
790
0
        }
791
0
    }
792
793
0
    m_pWrtShell->EndAllAction();
794
0
}
795
796
SwSearchOptions::SwSearchOptions( SwWrtShell const * pSh, bool bBackward )
797
0
   : eStart(SwDocPositions::Curr)
798
0
{
799
0
    if( bBackward )
800
0
    {
801
0
        eEnd = SwDocPositions::Start;
802
0
        bDontWrap = pSh->IsEndOfDoc();
803
0
    }
804
0
    else
805
0
    {
806
0
        eEnd = SwDocPositions::End;
807
0
        bDontWrap = pSh->IsStartOfDoc();
808
0
    }
809
0
}
810
811
sal_Int32 SwView::FUNC_Search(const SwSearchOptions& rOptions)
812
0
{
813
#if HAVE_FEATURE_DESKTOP
814
    SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
815
#endif
816
0
    bool bDoReplace = s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE ||
817
0
                      s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL;
818
819
0
    FindRanges eRanges = s_pSrchItem->GetSelection()
820
0
                        ? FindRanges::InSel
821
0
                        : s_bExtra
822
0
                          ? FindRanges::InOther : FindRanges::InBody;
823
0
    if (s_pSrchItem->GetCommand() == SvxSearchCmd::FIND_ALL    ||
824
0
        s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL)
825
0
        eRanges |= FindRanges::InSelAll;
826
827
0
    m_pWrtShell->SttSelect();
828
829
0
    static const WhichRangesContainer aSearchAttrRange(svl::Items<
830
0
        RES_CHRATR_BEGIN, RES_CHRATR_END-1,
831
0
        RES_PARATR_BEGIN, RES_PARATR_END-1,
832
0
        RES_FRMATR_BEGIN, RES_FRMATR_END-1,
833
0
        SID_ATTR_PARA_PAGEBREAK, SID_ATTR_PARA_PAGEBREAK,
834
0
        SID_ATTR_PARA_MODEL, SID_ATTR_PARA_MODEL, // page style
835
0
        SID_ATTR_PARA_KEEP, SID_ATTR_PARA_KEEP
836
0
        >);
837
838
0
    SfxItemSet aSrchSet( m_pWrtShell->GetAttrPool(), aSearchAttrRange);
839
0
    if( s_xSearchList && s_xSearchList->Count() )
840
0
    {
841
0
        s_xSearchList->Get( aSrchSet );
842
843
        // create Sw Page Style from Sfx Page Style
844
0
        ::SfxToSwPageDescAttr(*m_pWrtShell, aSrchSet);
845
0
    }
846
847
0
    std::optional<SfxItemSet> xReplSet;
848
0
    if (bDoReplace)
849
0
    {
850
        // tdf#99672 - create empty replace set to check for replacements in SwCursor::FindAttrs
851
0
        xReplSet.emplace(m_pWrtShell->GetAttrPool(), aSearchAttrRange);
852
0
        if (s_xReplaceList && s_xReplaceList->Count())
853
0
        {
854
0
            s_xReplaceList->Get( *xReplSet );
855
856
0
            if( !xReplSet->Count() )        // too bad, we don't know
857
0
                xReplSet.reset();        // the attributes
858
0
        }
859
0
    }
860
861
    // build SearchOptions to be used
862
863
0
    i18nutil::SearchOptions2 aSearchOpt( s_pSrchItem->GetSearchOptions() );
864
0
    aSearchOpt.Locale = GetAppLanguageTag().getLocale();
865
0
    if( !bDoReplace )
866
0
        aSearchOpt.replaceString.clear();
867
868
0
    sal_Int32 nFound;
869
0
    if( aSrchSet.Count() || ( xReplSet && xReplSet->Count() ))
870
0
    {
871
0
        nFound = m_pWrtShell->SearchAttr(
872
0
            aSrchSet,
873
0
            !s_pSrchItem->GetPattern(),
874
0
            rOptions.eStart,
875
0
            rOptions.eEnd,
876
0
            eRanges,
877
0
            !s_pSrchItem->GetSearchString().isEmpty() ? &aSearchOpt : nullptr,
878
0
            xReplSet ? &*xReplSet : nullptr );
879
0
    }
880
0
    else if( s_pSrchItem->GetPattern() )
881
0
    {
882
        // Searching (and replacing) templates
883
0
        const UIName sRplStr( s_pSrchItem->GetReplaceString() );
884
0
        nFound = m_pWrtShell->SearchTempl( UIName(s_pSrchItem->GetSearchString()),
885
0
            rOptions.eStart,
886
0
            rOptions.eEnd,
887
0
            eRanges,
888
0
            bDoReplace ? &sRplStr : nullptr );
889
0
    }
890
0
    else
891
0
    {
892
        // Normal search
893
0
        nFound = m_pWrtShell->SearchPattern(aSearchOpt, s_pSrchItem->GetNotes(),
894
0
                                          rOptions.eStart,
895
0
                                          rOptions.eEnd,
896
0
                                          eRanges,
897
0
                                          bDoReplace );
898
0
    }
899
0
    m_pWrtShell->EndSelect();
900
0
    return nFound;
901
0
}
902
903
SvxSearchDialog* SwView::GetSearchDialog()
904
0
{
905
#if HAVE_FEATURE_DESKTOP
906
    const sal_uInt16 nId = SvxSearchDialogWrapper::GetChildWindowId();
907
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
908
    if (!pViewFrm)
909
        return nullptr;
910
    SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>(pViewFrm->GetChildWindow(nId));
911
    if (!pWrp)
912
        return nullptr;
913
    return pWrp->getDialog();
914
#else
915
0
    return nullptr;
916
0
#endif
917
0
}
918
919
void SwView::StateSearch(SfxItemSet &rSet)
920
0
{
921
0
    SfxWhichIter aIter(rSet);
922
0
    sal_uInt16 nWhich = aIter.FirstWhich();
923
924
0
    while(nWhich)
925
0
    {
926
0
        switch(nWhich)
927
0
        {
928
0
            case SID_SEARCH_OPTIONS:
929
0
            {
930
0
                SearchOptionFlags nOpt = SearchOptionFlags::ALL;
931
0
                if (GetDocShell()->IsReadOnly() || IsCurrentLokViewReadOnly())
932
0
                    nOpt &= ~SearchOptionFlags( SearchOptionFlags::REPLACE |
933
0
                               SearchOptionFlags::REPLACE_ALL );
934
0
                rSet.Put( SfxUInt16Item( SID_SEARCH_OPTIONS, static_cast<sal_uInt16>(nOpt) ));
935
0
            }
936
0
            break;
937
0
            case SID_SEARCH_ITEM:
938
0
            {
939
0
                if ( !s_pSrchItem )
940
0
                {
941
0
                    s_pSrchItem = new SvxSearchItem( SID_SEARCH_ITEM );
942
0
                    s_pSrchItem->SetFamily(SfxStyleFamily::Para);
943
0
                    s_pSrchItem->SetSearchString( m_pWrtShell->GetSelText() );
944
0
                }
945
946
0
                if( s_bJustOpened && m_pWrtShell->IsSelection() )
947
0
                {
948
0
                    OUString aText;
949
0
                    if( 1 == m_pWrtShell->GetCursorCnt() &&
950
0
                        !( aText = m_pWrtShell->SwCursorShell::GetSelText() ).isEmpty() )
951
0
                    {
952
0
                        s_pSrchItem->SetSearchString( aText );
953
0
                        s_pSrchItem->SetSelection( false );
954
0
                        s_pSrchItem->SetReplaceString("");
955
0
                    }
956
0
                    else
957
0
                        s_pSrchItem->SetSelection( true );
958
0
                }
959
960
0
                s_bJustOpened = false;
961
0
                rSet.Put( *s_pSrchItem );
962
0
            }
963
0
            break;
964
0
        }
965
0
        nWhich = aIter.NextWhich();
966
0
    }
967
0
}
968
969
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */