/* -*- 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 <string>
 
#include <memory>
#include <boost/property_tree/json_parser.hpp>
 
#include <hintids.hxx>
 
#include <sal/log.hxx>
#include <svl/cjkoptions.hxx>
#include <svl/ctloptions.hxx>
#include <svx/pageitem.hxx>
#include <svl/whiter.hxx>
#include <sfx2/viewfrm.hxx>
#include <svl/eitem.hxx>
#include <svl/srchitem.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/request.hxx>
#include <sfx2/lokhelper.hxx>
#include <svx/srchdlg.hxx>
#include <swmodule.hxx>
#include <swwait.hxx>
#include <workctrl.hxx>
#include <view.hxx>
#include <wrtsh.hxx>
#include <swundo.hxx>
#include <uitool.hxx>
#include <cmdid.h>
#include <docsh.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
 
#include <strings.hrc>
#include <SwRewriter.hxx>
 
#include <PostItMgr.hxx>
 
using namespace com::sun::star;
using namespace ::com::sun::star::lang;
 
//Search Parameter
 
struct SwSearchOptions
{
    SwDocPositions eStart, eEnd;
    bool bDontWrap;
 
    SwSearchOptions( SwWrtShell const * pSh, bool bBackward );
};
 
/// Adds rMatches using rKey as a key to the rTree tree.
static void lcl_addContainerToJson(boost::property_tree::ptree& rTree, const OString& rKey, const std::vector<OString>& rMatches)
{
    boost::property_tree::ptree aChildren;
 
    for (const OString& rMatch : rMatches)
    {
        boost::property_tree::ptree aChild;
        aChild.put("part", "0");
        aChild.put("rectangles", rMatch.getStr());
        aChildren.push_back(std::make_pair("", aChild));
    }
 
    rTree.add_child(rKey.getStr(), aChildren);
}
 
/// Emits LOK callbacks (count, selection) for search results.
static void lcl_emitSearchResultCallbacks(SvxSearchItem const * pSearchItem, SwWrtShell const * pWrtShell, bool bHighlightAll)
{
    // Emit a callback also about the selection rectangles, grouped by matches.
    SwPaM* pPaM = pWrtShell->GetCursor();
    if (!pPaM)
        return;
 
    std::vector<OString> aMatches;
    for (SwPaM& rPaM : pPaM->GetRingContainer())
    {
        if (SwShellCursor* pShellCursor = dynamic_cast<SwShellCursor*>(&rPaM))
        {
            std::vector<OString> aSelectionRectangles;
            pShellCursor->SwSelPaintRects::Show(&aSelectionRectangles);
            std::vector<OString> aRect;
            for (const OString & rSelectionRectangle : aSelectionRectangles)
            {
                if (rSelectionRectangle.isEmpty())
                    continue;
                aRect.push_back(rSelectionRectangle);
            }
            OString sRect = comphelper::string::join("; ", aRect);
            aMatches.push_back(sRect);
        }
    }
    boost::property_tree::ptree aTree;
    aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr());
    aTree.put("highlightAll", bHighlightAll);
    lcl_addContainerToJson(aTree, "searchResultSelection"_ostr, aMatches);
 
    std::stringstream aStream;
    boost::property_tree::write_json(aStream, aTree);
    OString aPayload( aStream.str() );
 
    pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload);
 
    if(bHighlightAll)
    {   // FindAll disables this during find, do it once when done.
        SfxLokHelper::notifyUpdate(pWrtShell->GetSfxViewShell(),LOK_CALLBACK_TEXT_SELECTION);
        SfxLokHelper::notifyOtherViewsUpdatePerViewId(pWrtShell->GetSfxViewShell(), LOK_CALLBACK_TEXT_VIEW_SELECTION);
    }
}
 
void SwView::ExecSearch(SfxRequest& rReq)
{
    GetWrtShell().addCurrentPosition();
 
    const SfxItemSet* pArgs = rReq.GetArgs();
    const SfxPoolItem* pItem = nullptr;
    bool bQuiet = false;
    if(pArgs && SfxItemState::SET == pArgs->GetItemState(SID_SEARCH_QUIET, false, &pItem))
        bQuiet = static_cast<const SfxBoolItem*>( pItem)->GetValue();
 
    sal_uInt16 nSlot = rReq.GetSlot();
    if (nSlot == FN_REPEAT_SEARCH && !s_pSrchItem)
    {
        if(bQuiet)
        {
            rReq.SetReturnValue(SfxBoolItem(nSlot, false));
            nSlot = 0;
        }
    }
    if( m_pWrtShell->IsBlockMode() )
        m_pWrtShell->LeaveBlockMode();
    switch (nSlot)
    {
    // for now do nothing
    case SID_SEARCH_ITEM:
    {
        OSL_ENSURE(pArgs, "Args missing");
        if (pArgs)
        {
            delete s_pSrchItem;
            s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone();
        }
    }
    break;
 
    case FID_SEARCH_ON:
        s_bJustOpened = true;
        GetViewFrame().GetBindings().Invalidate(SID_SEARCH_ITEM);
        break;
 
    case FID_SEARCH_OFF:
        if(pArgs)
        {
            // Unregister dialog
            delete s_pSrchItem;
            s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone();
 
            s_xSearchList.reset();
            s_xReplaceList.reset();
 
            SvxSearchDialog *const pSrchDlg(GetSearchDialog());
            if (pSrchDlg)
            {
                // We will remember the search-/replace items.
                const SearchAttrItemList* pList = pSrchDlg->GetSearchItemList();
                if( nullptr != pList && pList->Count() )
                    s_xSearchList.reset(new SearchAttrItemList( *pList ));
 
                pList = pSrchDlg->GetReplaceItemList();
                if (nullptr != pList && pList->Count())
                    s_xReplaceList.reset(new SearchAttrItemList( *pList ));
            }
        }
        break;
 
        case FN_REPEAT_SEARCH:
        case FID_SEARCH_NOW:
        {
            sal_uInt16 nMoveType = SwView::GetMoveType();
            {
                if(FID_SEARCH_NOW == nSlot && !rReq.IsAPI())
                    SwView::SetMoveType(NID_SRCH_REP);
            }
 
            SvxSearchDialog * pSrchDlg(GetSearchDialog());
            if (pSrchDlg)
            {
                s_xSearchList.reset();
                s_xReplaceList.reset();
 
                const SearchAttrItemList* pList = pSrchDlg->GetSearchItemList();
                if( nullptr != pList && pList->Count() )
                    s_xSearchList.reset(new SearchAttrItemList( *pList ));
 
                pList = pSrchDlg->GetReplaceItemList();
                if (nullptr != pList && pList->Count())
                    s_xReplaceList.reset(new SearchAttrItemList( *pList ));
            }
 
            if (nSlot == FN_REPEAT_SEARCH)
            {
                OSL_ENSURE(s_pSrchItem, "SearchItem missing");
                if( !s_pSrchItem )
                    s_pSrchItem = new SvxSearchItem(SID_SEARCH_ITEM);
            }
            else
            {
                // Get SearchItem from request
                OSL_ENSURE(pArgs, "Args missing");
                if ( pArgs )
                {
                    delete s_pSrchItem;
                    s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone();
                }
            }
 
            // tdf#115665 Format in the CTRL+H search affects the CTRL+F search
            if (pArgs)
            {
                if (const SfxBoolItem* pUseAttrItemListItem
                    = pArgs->GetItemIfSet(SID_SEARCH_USE_ATTR_ITEM_LIST, false))
                {
                    if (!pUseAttrItemListItem->GetValue())
                        s_xSearchList.reset();
                }
            }
 
            SvxSearchCmd eCommand = s_pSrchItem->GetCommand();
            switch (eCommand)
            {
            case SvxSearchCmd::FIND:
            {
                bool bRet = SearchAndWrap(bQuiet);
                if( bRet )
                {
                    Scroll(m_pWrtShell->GetCharRect().SVRect());
                    if (comphelper::LibreOfficeKit::isActive())
                        lcl_emitSearchResultCallbacks(s_pSrchItem, m_pWrtShell.get(), /* bHighlightAll = */ false);
                }
                rReq.SetReturnValue(SfxBoolItem(nSlot, bRet));
 
                GetDocShell()->Broadcast(SfxHint(SfxHintId::SwNavigatorUpdateTracking));
            }
            break;
            case SvxSearchCmd::FIND_ALL:
            {
                // Disable LOK selection notifications during search.
                m_pWrtShell->GetSfxViewShell()->setTiledSearching(true);
                const auto nFound = SearchAll();
                m_pWrtShell->GetSfxViewShell()->setTiledSearching(false);
 
                GetDocShell()->Broadcast(
                            SfxHint(SfxHintId::SwNavigatorUpdateTracking));
                GetDocShell()->Broadcast(
                            SfxHint(SfxHintId::SwNavigatorSelectOutlinesWithSelections));
 
                if (nFound == 0)
                {
#if HAVE_FEATURE_DESKTOP
                    if( !bQuiet )
                    {
                        m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
                        SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
                    }
#endif
                    s_bFound = false;
                }
                else
                {
                    if (comphelper::LibreOfficeKit::isActive())
                        lcl_emitSearchResultCallbacks(s_pSrchItem, m_pWrtShell.get(), /* bHighlightAll = */ true);
                    if (!bQuiet)
                    {
                        OUString sText(SwResId(STR_SEARCH_KEY_FOUND_TIMES, nFound));
                        sText = sText.replaceFirst("%1", OUString::number(nFound));
                        SvxSearchDialogWrapper::SetSearchLabel(sText);
                    }
                }
                rReq.SetReturnValue(SfxBoolItem(nSlot, nFound != 0));
            }
            break;
            case SvxSearchCmd::REPLACE:
                {
 
                    // 1) Replace selection (Not if only attributes should be replaced)
//JP 27.04.95: Why?
//      what if you only want to assign attributes to the found??
 
                    SvxSearchCmd nCmd = SvxSearchCmd::FIND;
                    if( !s_pSrchItem->GetReplaceString().isEmpty() ||
                        !s_xReplaceList )
                    {
                        // Prevent, that the replaced string will be found again
                        // if the replacement string is containing the search string.
                        bool bBack = s_pSrchItem->GetBackward();
                        if (bBack)
                            m_pWrtShell->Push();
                        OUString aReplace( s_pSrchItem->GetReplaceString() );
                        i18nutil::SearchOptions2 aTmp( s_pSrchItem->GetSearchOptions() );
                        std::optional<OUString> xBackRef = sw::ReplaceBackReferences(aTmp,
                            m_pWrtShell->GetCursor(), m_pWrtShell->GetLayout());
                        if( xBackRef )
                            s_pSrchItem->SetReplaceString( *xBackRef );
                        Replace();
                        if( xBackRef )
                        {
                            s_pSrchItem->SetReplaceString( aReplace );
                        }
                        if (bBack)
                        {
                            m_pWrtShell->Pop();
                            m_pWrtShell->SwapPam();
                        }
                    }
                    else if( s_xReplaceList )
                        nCmd = SvxSearchCmd::REPLACE;
 
                    // 2) Search further (without replacing!)
 
                    SvxSearchCmd nOldCmd = s_pSrchItem->GetCommand();
                    s_pSrchItem->SetCommand( nCmd );
                    bool bRet = SearchAndWrap(bQuiet);
                    if( bRet )
                        Scroll( m_pWrtShell->GetCharRect().SVRect());
                    s_pSrchItem->SetCommand( nOldCmd );
                    rReq.SetReturnValue(SfxBoolItem(nSlot, bRet));
                }
                break;
 
            case SvxSearchCmd::REPLACE_ALL:
                {
                    SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
                    s_bExtra = false;
                    sal_uLong nFound;
 
                    {   //Scope for SwWait-Object
                        SwWait aWait( *GetDocShell(), true );
                        m_pWrtShell->StartAllAction();
 
                        // i#8288 "replace all" should not change cursor
                        // position, so save current cursor
                        m_pWrtShell->Push();
 
                        if (!s_pSrchItem->GetSelection())
                        {
                            // if we don't want to search in the selection...
                            m_pWrtShell->KillSelection(nullptr, false);
                            if (SwDocPositions::Start == aOpts.eEnd)
                            {
                                m_pWrtShell->EndOfSection();
                            }
                            else
                            {
                                m_pWrtShell->StartOfSection();
                            }
                        }
                        nFound = FUNC_Search( aOpts );
                        // create it just to overwrite it with stack cursor
                        m_pWrtShell->CreateCursor();
                        // i#8288 restore the original cursor position
                        m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
                        m_pWrtShell->EndAllAction();
                    }
 
                    rReq.SetReturnValue(SfxBoolItem(nSlot, nFound != 0 && ULONG_MAX != nFound));
                    if( !nFound )
                    {
#if HAVE_FEATURE_DESKTOP
                        if( !bQuiet )
                        {
                            m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
                            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
                        }
#endif
                        s_bFound = false;
                        SwView::SetMoveType(nMoveType);
                        return;
                    }
 
                    if( !bQuiet && ULONG_MAX != nFound)
                    {
                        OUString sText( SwResId( STR_NB_REPLACED ) );
                        sText = sText.replaceFirst("XX", OUString::number( nFound ));
                        SvxSearchDialogWrapper::SetSearchLabel(sText);
                    }
                }
                break;
            }
 
            uno::Reference< frame::XDispatchRecorder > xRecorder =
                    GetViewFrame().GetBindings().GetRecorder();
            //prevent additional dialogs in recorded macros
            if ( xRecorder.is() )
                rReq.AppendItem(SfxBoolItem(SID_SEARCH_QUIET, true));
 
            rReq.Done();
            m_eLastSearchCommand = s_pSrchItem->GetCommand();
            SwView::SetMoveType(nMoveType);
        }
        break;
        case FID_SEARCH_SEARCHSET:
        case FID_SEARCH_REPLACESET:
        {
            static const WhichRangesContainer aNormalAttr(svl::Items<
/* 0 */         RES_CHRATR_CASEMAP,     RES_CHRATR_CASEMAP,
/* 2 */         RES_CHRATR_COLOR,       RES_CHRATR_POSTURE,
/* 4 */         RES_CHRATR_SHADOWED,    RES_CHRATR_WORDLINEMODE,
/* 6 */         RES_CHRATR_BLINK,       RES_CHRATR_BLINK,
/* 8 */         RES_CHRATR_BACKGROUND,  RES_CHRATR_BACKGROUND,
/*10 */         RES_CHRATR_ROTATE,      RES_CHRATR_ROTATE,
/*12 */         RES_CHRATR_SCALEW,      RES_CHRATR_RELIEF,
/*14 */         RES_CHRATR_OVERLINE,    RES_CHRATR_OVERLINE,
/*16 */         RES_PARATR_LINESPACING, RES_PARATR_HYPHENZONE,
/*18 */         RES_PARATR_REGISTER,    RES_PARATR_REGISTER,
/*20 */         RES_PARATR_VERTALIGN,   RES_PARATR_VERTALIGN,
                RES_MARGIN_FIRSTLINE,   RES_MARGIN_RIGHT,
                RES_UL_SPACE,           RES_UL_SPACE,
/*24 */         SID_ATTR_PARA_MODEL,    SID_ATTR_PARA_KEEP
            >);
 
            SfxItemSet aSet(m_pWrtShell->GetAttrPool(), aNormalAttr);
 
            if( SvtCTLOptions::IsCTLFontEnabled() )
            {
                aSet.MergeRange(RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_WEIGHT);
            }
            if( SvtCJKOptions::IsAnyEnabled() )
            {
                aSet.MergeRange(RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_WEIGHT);
                aSet.MergeRange(RES_CHRATR_EMPHASIS_MARK, RES_CHRATR_TWO_LINES);
                aSet.MergeRange(RES_PARATR_SCRIPTSPACE, RES_PARATR_FORBIDDEN_RULES);
            }
 
            TypedWhichId<SvxSetItem> nWhich = SID_SEARCH_SEARCHSET;
 
            if ( FID_SEARCH_REPLACESET == nSlot )
            {
                nWhich = SID_SEARCH_REPLACESET;
 
                if ( s_xReplaceList )
                {
                    s_xReplaceList->Get( aSet );
                    s_xReplaceList.reset();
                }
            }
            else if ( s_xSearchList )
            {
                s_xSearchList->Get( aSet );
                s_xSearchList.reset();
            }
            rReq.SetReturnValue( SvxSetItem( nWhich, aSet ) );
        }
        break;
        default:
            SAL_WARN_IF( nSlot, "sw", "nSlot: " << nSlot << " wrong Dispatcher (viewsrch.cxx)" );
            return;
    }
}
 
bool SwView::SearchAndWrap(bool bApi)
{
    SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
 
        // Remember starting position of the search for wraparound
        // Start- / EndAction perhaps because existing selections of 'search all'
    m_pWrtShell->StartAllAction();
    m_pWrtShell->Push();
 
    // After a search all action we place the cursor at the beginning of
    // the document so that the single search selects the first matching
    // occurrence in the document instead of the second.
    if( m_eLastSearchCommand == SvxSearchCmd::FIND_ALL )
    {
        if( SwDocPositions::Start == aOpts.eEnd )
            m_pWrtShell->EndOfSection();
        else
            m_pWrtShell->StartOfSection();
    }
 
    // fdo#65014 : Ensure that the point of the cursor is at the extremity of the
    // selection closest to the end being searched to as to exclude the selected
    // region from the search. (This doesn't work in the case of multiple
    // selected regions as the cursor doesn't mark the selection in that case.)
    m_pWrtShell->GetCursor()->Normalize( s_pSrchItem->GetBackward() );
 
    if (!m_pWrtShell->HasSelection() && (s_pSrchItem->HasStartPoint()))
    {
        // No selection -> but we have a start point (top left corner of the
        // current view), start searching from there, not from the current
        // cursor position.
        SwEditShell& rShell = GetWrtShell();
        Point aPosition(s_pSrchItem->GetStartPointX(), s_pSrchItem->GetStartPointY());
        rShell.SetCursor(aPosition);
    }
 
        // If you want to search in selected areas, they must not be unselected.
    if (!s_pSrchItem->GetSelection())
        m_pWrtShell->KillSelection(nullptr, false);
 
    std::optional<SwWait> oWait( std::in_place, *GetDocShell(), true );
    if( FUNC_Search( aOpts ) )
    {
        s_bFound = true;
        if(m_pWrtShell->IsSelFrameMode())
        {
            m_pWrtShell->UnSelectFrame();
            m_pWrtShell->LeaveSelFrameMode();
        }
        m_pWrtShell->Pop();
        m_pWrtShell->EndAllAction();
        return true;
    }
    oWait.reset();
 
        // Search in the specialized areas when no search is present in selections.
        // When searching selections will already searched in these special areas.
    bool bHasSrchInOther = s_bExtra;
    if (!s_pSrchItem->GetSelection() && !s_bExtra )
    {
        s_bExtra = true;
        if( FUNC_Search( aOpts ) )
        {
            s_bFound = true;
            m_pWrtShell->Pop();
            m_pWrtShell->EndAllAction();
            return true;
        }
        s_bExtra = false;
    }
    else
        s_bExtra = !s_bExtra;
 
        // If starting position is at the end or beginning of the document.
    if (aOpts.bDontWrap)
    {
        m_pWrtShell->EndAllAction();
        if( !bApi )
        {
#if HAVE_FEATURE_DESKTOP
            m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
#endif
        }
        s_bFound = false;
        m_pWrtShell->Pop();
        return false;
    }
    m_pWrtShell->EndAllAction();
        // Try again with WrapAround?
 
    m_pWrtShell->StartAllAction();
    m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
    oWait.emplace( *GetDocShell(), true );
 
    bool bSrchBkwrd = SwDocPositions::Start == aOpts.eEnd;
 
    aOpts.eEnd =  bSrchBkwrd ? SwDocPositions::Start : SwDocPositions::End;
    aOpts.eStart = bSrchBkwrd ? SwDocPositions::End : SwDocPositions::Start;
 
    if (bHasSrchInOther)
    {
        m_pWrtShell->ClearMark();
        // Select the start or the end of the entire document
        if (bSrchBkwrd)
            m_pWrtShell->SttEndDoc(false);
        else
            m_pWrtShell->SttEndDoc(true);
    }
 
    s_bFound = bool(FUNC_Search( aOpts ));
 
    // If WrapAround found no matches in the body text, search in the special
    // sections, too.
    if (!s_bFound && !s_pSrchItem->GetSelection() && !s_bExtra)
    {
        s_bExtra = true;
        if (FUNC_Search(aOpts))
            s_bFound = true;
        else
            s_bExtra = false;
    }
 
    m_pWrtShell->EndAllAction();
    oWait.reset();
#if HAVE_FEATURE_DESKTOP
    if (s_bFound)
    {
        if (!bSrchBkwrd)
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
        else
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start);
    }
    else if(!bApi)
    {
        m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8());
        SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
    }
#endif
    return s_bFound;
}
 
sal_uInt16 SwView::SearchAll()
{
    SwWait aWait( *GetDocShell(), true );
    m_pWrtShell->StartAllAction();
 
    SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
 
    if (!s_pSrchItem->GetSelection())
    {
        // Cancel existing selections, if should not be sought in selected areas.
        m_pWrtShell->KillSelection(nullptr, false);
 
        if( SwDocPositions::Start == aOpts.eEnd )
            m_pWrtShell->EndOfSection();
        else
            m_pWrtShell->StartOfSection();
    }
    s_bExtra = false;
    sal_uInt16 nFound = o3tl::narrowing<sal_uInt16>(FUNC_Search( aOpts ));
    s_bFound = 0 != nFound;
 
    m_pWrtShell->EndAllAction();
    return nFound;
}
 
void SwView::Replace()
{
    SwWait aWait( *GetDocShell(), true );
 
    m_pWrtShell->StartAllAction();
 
    if( s_pSrchItem->GetPattern() ) // Templates?
    {
        SwRewriter aRewriter;
        aRewriter.AddRule(UndoArg1, s_pSrchItem->GetSearchString());
        aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
        aRewriter.AddRule(UndoArg3, s_pSrchItem->GetReplaceString());
 
        m_pWrtShell->StartUndo(SwUndoId::UI_REPLACE_STYLE, &aRewriter);
 
        m_pWrtShell->SetTextFormatColl( m_pWrtShell->GetParaStyle(
                            s_pSrchItem->GetReplaceString(),
                            SwWrtShell::GETSTYLE_CREATESOME ));
 
        m_pWrtShell->EndUndo();
    }
    else
    {
        if (GetPostItMgr()->HasActiveSidebarWin())
            GetPostItMgr()->Replace(s_pSrchItem);
 
        bool bReqReplace = true;
 
        if(m_pWrtShell->HasSelection())
        {
            /* check that the selection match the search string*/
            //save state
            SwPosition aStartPos = * m_pWrtShell->GetCursor()->Start();
            SwPosition aEndPos = * m_pWrtShell->GetCursor()->End();
            bool   bHasSelection = s_pSrchItem->GetSelection();
            SvxSearchCmd nOldCmd = s_pSrchItem->GetCommand();
 
            //set state for checking if current selection has a match
            s_pSrchItem->SetCommand( SvxSearchCmd::FIND );
            s_pSrchItem->SetSelection(true);
 
            //check if it matches
            SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() );
            if( ! FUNC_Search(aOpts) )
            {
 
                //no matching therefore should not replace selection
                // => remove selection
 
                if(! s_pSrchItem->GetBackward() )
                {
                    (* m_pWrtShell->GetCursor()->Start()) = std::move(aStartPos);
                    (* m_pWrtShell->GetCursor()->End()) = std::move(aEndPos);
                }
                else
                {
                    (* m_pWrtShell->GetCursor()->Start()) = std::move(aEndPos);
                    (* m_pWrtShell->GetCursor()->End()) = std::move(aStartPos);
                }
                bReqReplace = false;
            }
 
            //set back old search state
            s_pSrchItem->SetCommand( nOldCmd );
            s_pSrchItem->SetSelection(bHasSelection);
        }
        /*
         * remove current selection
         * otherwise it is always replaced
         * no matter if the search string exists or not in the selection
         * Now the selection is removed and the next matching string is selected
         */
 
        if( bReqReplace )
        {
 
            bool bReplaced = m_pWrtShell->SwEditShell::Replace( s_pSrchItem->GetReplaceString(),
                                                                  s_pSrchItem->GetRegExp());
            if( bReplaced && s_xReplaceList && s_xReplaceList->Count() && m_pWrtShell->HasSelection() )
            {
                SfxItemSet aReplSet( m_pWrtShell->GetAttrPool(),
                                     aTextFormatCollSetRange );
                if( s_xReplaceList->Get( aReplSet ).Count() )
                {
                    ::SfxToSwPageDescAttr( *m_pWrtShell, aReplSet );
                    m_pWrtShell->SwEditShell::SetAttrSet( aReplSet );
                }
            }
        }
    }
 
    m_pWrtShell->EndAllAction();
}
 
SwSearchOptions::SwSearchOptions( SwWrtShell const * pSh, bool bBackward )
   : eStart(SwDocPositions::Curr)
{
    if( bBackward )
    {
        eEnd = SwDocPositions::Start;
        bDontWrap = pSh->IsEndOfDoc();
    }
    else
    {
        eEnd = SwDocPositions::End;
        bDontWrap = pSh->IsStartOfDoc();
    }
}
 
sal_uLong SwView::FUNC_Search( const SwSearchOptions& rOptions )
{
#if HAVE_FEATURE_DESKTOP
    SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
#endif
    bool bDoReplace = s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE ||
                      s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL;
 
    FindRanges eRanges = s_pSrchItem->GetSelection()
                        ? FindRanges::InSel
                        : s_bExtra
                          ? FindRanges::InOther : FindRanges::InBody;
    if (s_pSrchItem->GetCommand() == SvxSearchCmd::FIND_ALL    ||
        s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL)
        eRanges |= FindRanges::InSelAll;
 
    m_pWrtShell->SttSelect();
 
    static const WhichRangesContainer aSearchAttrRange(svl::Items<
        RES_CHRATR_BEGIN, RES_CHRATR_END-1,
        RES_PARATR_BEGIN, RES_PARATR_END-1,
        RES_FRMATR_BEGIN, RES_FRMATR_END-1,
        SID_ATTR_PARA_MODEL, SID_ATTR_PARA_KEEP
        >);
 
    SfxItemSet aSrchSet( m_pWrtShell->GetAttrPool(), aSearchAttrRange);
    if( s_xSearchList && s_xSearchList->Count() )
    {
        s_xSearchList->Get( aSrchSet );
 
        // -- Page break with page template
        ::SfxToSwPageDescAttr( *m_pWrtShell, aSrchSet );
    }
 
    std::optional<SfxItemSet> xReplSet;
    if( bDoReplace && s_xReplaceList && s_xReplaceList->Count() )
    {
        xReplSet.emplace( m_pWrtShell->GetAttrPool(), aSearchAttrRange );
        s_xReplaceList->Get( *xReplSet );
 
        // -- Page break with page template
        ::SfxToSwPageDescAttr( *m_pWrtShell, *xReplSet );
 
        if( !xReplSet->Count() )        // too bad, we don't know
            xReplSet.reset();        // the attributes
    }
 
    // build SearchOptions to be used
 
    i18nutil::SearchOptions2 aSearchOpt( s_pSrchItem->GetSearchOptions() );
    aSearchOpt.Locale = GetAppLanguageTag().getLocale();
    if( !bDoReplace )
        aSearchOpt.replaceString.clear();
 
    sal_Int32 nFound;
    if( aSrchSet.Count() || ( xReplSet && xReplSet->Count() ))
    {
        nFound = m_pWrtShell->SearchAttr(
            aSrchSet,
            !s_pSrchItem->GetPattern(),
            rOptions.eStart,
            rOptions.eEnd,
            eRanges,
            !s_pSrchItem->GetSearchString().isEmpty() ? &aSearchOpt : nullptr,
            xReplSet ? &*xReplSet : nullptr );
    }
    else if( s_pSrchItem->GetPattern() )
    {
        // Searching (and replacing) templates
        const OUString& sRplStr( s_pSrchItem->GetReplaceString() );
        nFound = m_pWrtShell->SearchTempl( s_pSrchItem->GetSearchString(),
            rOptions.eStart,
            rOptions.eEnd,
            eRanges,
            bDoReplace ? &sRplStr : nullptr );
    }
    else
    {
        // Normal search
        nFound = m_pWrtShell->SearchPattern(aSearchOpt, s_pSrchItem->GetNotes(),
                                          rOptions.eStart,
                                          rOptions.eEnd,
                                          eRanges,
                                          bDoReplace );
    }
    m_pWrtShell->EndSelect();
    return nFound;
}
 
SvxSearchDialog* SwView::GetSearchDialog()
{
#if HAVE_FEATURE_DESKTOP
    const sal_uInt16 nId = SvxSearchDialogWrapper::GetChildWindowId();
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
    if (!pViewFrm)
        return nullptr;
    SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>(pViewFrm->GetChildWindow(nId));
    if (!pWrp)
        return nullptr;
    return pWrp->getDialog();
#else
    return nullptr;
#endif
}
 
void SwView::StateSearch(SfxItemSet &rSet)
{
    SfxWhichIter aIter(rSet);
    sal_uInt16 nWhich = aIter.FirstWhich();
 
    while(nWhich)
    {
        switch(nWhich)
        {
            case SID_SEARCH_OPTIONS:
            {
                SearchOptionFlags nOpt = SearchOptionFlags::ALL;
                if( GetDocShell()->IsReadOnly() )
                    nOpt &= ~SearchOptionFlags( SearchOptionFlags::REPLACE |
                               SearchOptionFlags::REPLACE_ALL );
                rSet.Put( SfxUInt16Item( SID_SEARCH_OPTIONS, static_cast<sal_uInt16>(nOpt) ));
            }
            break;
            case SID_SEARCH_ITEM:
            {
                if ( !s_pSrchItem )
                {
                    s_pSrchItem = new SvxSearchItem( SID_SEARCH_ITEM );
                    s_pSrchItem->SetFamily(SfxStyleFamily::Para);
                    s_pSrchItem->SetSearchString( m_pWrtShell->GetSelText() );
                }
 
                if( s_bJustOpened && m_pWrtShell->IsSelection() )
                {
                    OUString aText;
                    if( 1 == m_pWrtShell->GetCursorCnt() &&
                        !( aText = m_pWrtShell->SwCursorShell::GetSelText() ).isEmpty() )
                    {
                        s_pSrchItem->SetSearchString( aText );
                        s_pSrchItem->SetSelection( false );
                    }
                    else
                        s_pSrchItem->SetSelection( true );
                }
 
                s_bJustOpened = false;
                rSet.Put( *s_pSrchItem );
            }
            break;
        }
        nWhich = aIter.NextWhich();
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'Get' is required to be utilized.

V530 The return value of function 'Get' is required to be utilized.

V530 The return value of function 'Get' is required to be utilized.

V530 The return value of function 'Get' is required to be utilized.