/* -*- 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 <sal/macros.h>
#include <vcl/wrkwin.hxx>
#include <vcl/timer.hxx>
#include <svl/slstitm.hxx>
#include <svl/itemiter.hxx>
#include <svl/style.hxx>
#include <unotools/intlwrapper.hxx>
#include <unotools/moduleoptions.hxx>
#include <unotools/searchopt.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/module.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/basedlgs.hxx>
#include <sfx2/viewfrm.hxx>
#include <svl/cjkoptions.hxx>
#include <svl/ctloptions.hxx>
#include <com/sun/star/awt/XWindow.hpp>
#include <com/sun/star/accessibility/AccessibleRelation.hpp>
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
#include <com/sun/star/accessibility/XAccessibleGetAccFlowTo.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/XLayoutManager.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/configuration/theDefaultProvider.hpp>
#include <com/sun/star/frame/ModuleManager.hpp>
#include <com/sun/star/ui/XUIElement.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/scopeguard.hxx>
#include <svl/itempool.hxx>
#include <svl/intitem.hxx>

#include <sfx2/app.hxx>
#include <toolkit/helper/vclunohelper.hxx>

#include <svx/srchdlg.hxx>
#include <svx/strarray.hxx>

#include <svx/strings.hrc>
#include <svx/svxids.hrc>
#include <svx/svxitems.hrc>

#include <svl/srchitem.hxx>
#include <svx/pageitem.hxx>
#include "srchctrl.hxx"
#include <svx/dialmgr.hxx>
#include <svx/dlgutil.hxx>
#include <editeng/brushitem.hxx>
#include <tools/resary.hxx>
#include <svx/svxdlg.hxx>
#include <vcl/toolbox.hxx>
#include <o3tl/typed_flags_set.hxx>

#include <cstdlib>
#include <memory>

#include <svx/xdef.hxx>
#include <officecfg/Office/Common.hxx>

using namespace com::sun::star::i18n;
using namespace com::sun::star::uno;
using namespace com::sun::star::accessibility;
using namespace com::sun::star;
using namespace comphelper;

enum class ModifyFlags {
    NONE         = 0x000000,
    Search       = 0x000001,
    Replace      = 0x000002,
    Word         = 0x000004,
    Exact        = 0x000008,
    Backwards    = 0x000010,
    Selection    = 0x000020,
    Regexp       = 0x000040,
    Layout       = 0x000080,
    Similarity   = 0x000100,
    Formulas     = 0x000200,
    Values       = 0x000400,
    CalcNotes    = 0x000800,
    Rows         = 0x001000,
    Columns      = 0x002000,
    AllTables    = 0x004000,
    Notes        = 0x008000,
    Wildcard     = 0x010000
};
namespace o3tl {
    template<> struct typed_flags<ModifyFlags> : is_typed_flags<ModifyFlags, 0x01ffff> {};
}

namespace
{
    bool GetCheckBoxValue(const weld::CheckButton& rBox)
    {
        return rBox.get_sensitive() && rBox.get_active();
    }

    bool GetNegatedCheckBoxValue(const weld::CheckButton& rBox)
    {
        return rBox.get_sensitive() && !rBox.get_active();
    }
}

struct SearchDlg_Impl
{
    bool        bSaveToModule  : 1,
                bFocusOnSearch : 1;
    std::unique_ptr<sal_uInt16[]> pRanges;
    Timer       aSelectionTimer;

    uno::Reference< frame::XDispatch > xCommand1Dispatch;
    uno::Reference< frame::XDispatch > xCommand2Dispatch;
    util::URL   aCommand1URL;
    util::URL   aCommand2URL;

    SearchDlg_Impl()
        : bSaveToModule(true)
        , bFocusOnSearch(true)
    {
        aCommand1URL.Complete = aCommand1URL.Main = "vnd.sun.search:SearchViaComponent1";
        aCommand1URL.Protocol = "vnd.sun.search:";
        aCommand1URL.Path = "SearchViaComponent1";
        aCommand2URL.Complete = aCommand2URL.Main = "vnd.sun.search:SearchViaComponent2";
        aCommand2URL.Protocol = "vnd.sun.search:";
        aCommand2URL.Path = "SearchViaComponent2";
    }
};

static void ListToStrArr_Impl( sal_uInt16 nId, std::vector<OUString>& rStrLst, weld::ComboBox& rCBox )
{
    const SfxStringListItem* pSrchItem =
        static_cast<const SfxStringListItem*>(SfxGetpApp()->GetItem( nId ));

    if (pSrchItem)
    {
        std::vector<OUString> aLst = pSrchItem->GetList();

        for (const OUString & s : aLst)
        {
            rStrLst.push_back(s);
            rCBox.append_text(s);
        }
    }
}

static void StrArrToList_Impl( sal_uInt16 nId, const std::vector<OUString>& rStrLst )
{
    DBG_ASSERT( !rStrLst.empty(), "check in advance");
    SfxGetpApp()->PutItem( SfxStringListItem( nId, &rStrLst ) );
}

SearchAttrItemList::SearchAttrItemList( const SearchAttrItemList& rList ) :
    SrchAttrItemList(rList)
{
    for ( size_t i = 0; i < size(); ++i )
        if ( !IsInvalidItem( (*this)[i].pItem ) )
            (*this)[i].pItem = (*this)[i].pItem->Clone();
}

SearchAttrItemList::~SearchAttrItemList()
{
    Clear();
}

void SearchAttrItemList::Put( const SfxItemSet& rSet )
{
    if ( !rSet.Count() )
        return;

    SfxItemPool* pPool = rSet.GetPool();
    SfxItemIter aIter( rSet );
    SearchAttrItem aItem;
    const SfxPoolItem* pItem = aIter.GetCurItem();
    sal_uInt16 nWhich;

    while ( true )
    {
        // only test that it is available?
        if( IsInvalidItem( pItem ) )
        {
            nWhich = rSet.GetWhichByPos( aIter.GetCurPos() );
            aItem.pItem = const_cast<SfxPoolItem*>(pItem);
        }
        else
        {
            nWhich = pItem->Which();
            aItem.pItem = pItem->Clone();
        }

        aItem.nSlot = pPool->GetSlotId( nWhich );
        Insert( aItem );

        if ( aIter.IsAtEnd() )
            break;
        pItem = aIter.NextItem();
    }
}


SfxItemSet& SearchAttrItemList::Get( SfxItemSet& rSet )
{
    SfxItemPool* pPool = rSet.GetPool();

    for ( size_t i = 0; i < size(); ++i )
        if ( IsInvalidItem( (*this)[i].pItem ) )
            rSet.InvalidateItem( pPool->GetWhich( (*this)[i].nSlot ) );
        else
            rSet.Put( *(*this)[i].pItem );
    return rSet;
}


void SearchAttrItemList::Clear()
{
    for ( size_t i = 0; i < size(); ++i )
        if ( !IsInvalidItem( (*this)[i].pItem ) )
            delete (*this)[i].pItem;
    SrchAttrItemList::clear();
}


// Deletes the pointer to the items
void SearchAttrItemList::Remove(size_t nPos)
{
    size_t nLen = 1;
    if ( nPos + nLen > size() )
        nLen = size() - nPos;

    for ( size_t i = nPos; i < nPos + nLen; ++i )
        if ( !IsInvalidItem( (*this)[i].pItem ) )
            delete (*this)[i].pItem;

    SrchAttrItemList::erase( begin() + nPos, begin() + nPos + nLen );
}

SvxSearchDialog::SvxSearchDialog(weld::Window* pParent, SfxChildWindow* pChildWin, SfxBindings& rBind)
    : SfxModelessDialogController(&rBind, pChildWin, pParent,
                                  "svx/ui/findreplacedialog.ui", "FindReplaceDialog")
    , mbSuccess(false)
    , rBindings(rBind)
    , bWriter(false)
    , bSearch(true)
    , bFormat(false)
    , bReplaceBackwards(false)
    , nOptions(SearchOptionFlags::ALL)
    , bSet(false)
    , bConstruct(true)
    , nModifyFlag(ModifyFlags::NONE)
    , pReplaceList(new SearchAttrItemList)
    , nTransliterationFlags(TransliterationFlags::NONE)
    , m_xSearchFrame(m_xBuilder->weld_frame("searchframe"))
    , m_xSearchLB(m_xBuilder->weld_combo_box("searchterm"))
    , m_xSearchTmplLB(m_xBuilder->weld_combo_box("searchlist"))
    , m_xSearchAttrText(m_xBuilder->weld_label("searchdesc"))
    , m_xSearchLabel(m_xBuilder->weld_label("searchlabel"))
    , m_xReplaceFrame(m_xBuilder->weld_frame("replaceframe"))
    , m_xReplaceLB(m_xBuilder->weld_combo_box("replaceterm"))
    , m_xReplaceTmplLB(m_xBuilder->weld_combo_box("replacelist"))
    , m_xReplaceAttrText(m_xBuilder->weld_label("replacedesc"))
    , m_xSearchBtn(m_xBuilder->weld_button("search"))
    , m_xBackSearchBtn(m_xBuilder->weld_button("backsearch"))
    , m_xSearchAllBtn(m_xBuilder->weld_button("searchall"))
    , m_xReplaceBtn(m_xBuilder->weld_button("replace"))
    , m_xReplaceAllBtn(m_xBuilder->weld_button("replaceall"))
    , m_xComponentFrame(m_xBuilder->weld_frame("componentframe"))
    , m_xSearchComponent1PB(m_xBuilder->weld_button("component1"))
    , m_xSearchComponent2PB(m_xBuilder->weld_button("component2"))
    , m_xMatchCaseCB(m_xBuilder->weld_check_button("matchcase"))
    , m_xSearchFormattedCB(m_xBuilder->weld_check_button("searchformatted"))
    , m_xWordBtn(m_xBuilder->weld_check_button("wholewords"))
    , m_xCloseBtn(m_xBuilder->weld_button("close"))
    , m_xIncludeDiacritics(m_xBuilder->weld_check_button("includediacritics"))
    , m_xIncludeKashida(m_xBuilder->weld_check_button("includekashida"))
    , m_xOtherOptionsExpander(m_xBuilder->weld_expander("OptionsExpander"))
    , m_xSelectionBtn(m_xBuilder->weld_check_button("selection"))
    , m_xRegExpBtn(m_xBuilder->weld_check_button("regexp"))
    , m_xWildcardBtn(m_xBuilder->weld_check_button("wildcard"))
    , m_xSimilarityBox(m_xBuilder->weld_check_button("similarity"))
    , m_xSimilarityBtn(m_xBuilder->weld_button("similaritybtn"))
    , m_xLayoutBtn(m_xBuilder->weld_check_button("layout"))
    , m_xNotesBtn(m_xBuilder->weld_check_button("notes"))
    , m_xJapMatchFullHalfWidthCB(m_xBuilder->weld_check_button("matchcharwidth"))
    , m_xJapOptionsCB(m_xBuilder->weld_check_button("soundslike"))
    , m_xReplaceBackwardsCB(m_xBuilder->weld_check_button("replace_backwards"))
    , m_xJapOptionsBtn(m_xBuilder->weld_button("soundslikebtn"))
    , m_xAttributeBtn(m_xBuilder->weld_button("attributes"))
    , m_xFormatBtn(m_xBuilder->weld_button("format"))
    , m_xNoFormatBtn(m_xBuilder->weld_button("noformat"))
    , m_xCalcGrid(m_xBuilder->weld_widget("calcgrid"))
    , m_xCalcSearchInFT(m_xBuilder->weld_label("searchinlabel"))
    , m_xCalcSearchInLB(m_xBuilder->weld_combo_box("calcsearchin"))
    , m_xCalcSearchDirFT(m_xBuilder->weld_label("searchdir"))
    , m_xRowsBtn(m_xBuilder->weld_radio_button("rows"))
    , m_xColumnsBtn(m_xBuilder->weld_radio_button("cols"))
    , m_xAllSheetsCB(m_xBuilder->weld_check_button("allsheets"))
    , m_xCalcStrFT(m_xBuilder->weld_label("entirecells"))
{
    m_xSearchTmplLB->make_sorted();
    m_xSearchAttrText->hide();
    m_xSearchLabel->show();

    m_xReplaceTmplLB->make_sorted();
    m_xReplaceAttrText->hide();

    aCalcStr = m_xCalcStrFT->get_label();

    // m_xSimilarityBtn->set_height_request(m_xSimilarityBox->get_preferred_size().Height());
    // m_xJapOptionsBtn->set_height_request(m_xJapOptionsCB->get_preferred_size().Height());

    //tdf#122322
    nRememberSize = officecfg::Office::Common::Misc::FindReplaceRememberedSearches::get();

    auto nTermWidth = m_xSearchLB->get_approximate_digit_width() * 28;
    m_xSearchLB->set_size_request(nTermWidth, -1);
    m_xSearchTmplLB->set_size_request(nTermWidth, -1);
    m_xReplaceLB->set_size_request(nTermWidth, -1);
    m_xReplaceTmplLB->set_size_request(nTermWidth, -1);

    Construct_Impl();
}

void SvxSearchDialog::ChildWinDispose()
{
    rBindings.EnterRegistrations();
    pSearchController.reset();
    pOptionsController.reset();
    pFamilyController.reset();
    rBindings.LeaveRegistrations();
    SfxModelessDialogController::ChildWinDispose();
}

SvxSearchDialog::~SvxSearchDialog()
{
}

void SvxSearchDialog::Construct_Impl()
{
    pImpl.reset( new SearchDlg_Impl() );
    pImpl->aSelectionTimer.SetTimeout( 500 );
    pImpl->aSelectionTimer.SetInvokeHandler(
        LINK( this, SvxSearchDialog, TimeoutHdl_Impl ) );
    EnableControls_Impl( SearchOptionFlags::NONE );

    // Store old Text from m_xWordBtn
    aCalcStr += "#";
    aCalcStr += m_xWordBtn->get_label();

    aLayoutStr = SvxResId( RID_SVXSTR_SEARCH_STYLES );
    aLayoutWriterStr = SvxResId( RID_SVXSTR_WRITER_STYLES );
    aLayoutCalcStr = SvxResId( RID_SVXSTR_CALC_STYLES );
    aStylesStr = m_xLayoutBtn->get_label();

    // Get stored search-strings from the application
    ListToStrArr_Impl(SID_SEARCHDLG_SEARCHSTRINGS,
                       aSearchStrings, *m_xSearchLB);
    ListToStrArr_Impl(SID_SEARCHDLG_REPLACESTRINGS,
                       aReplaceStrings, *m_xReplaceLB);

    InitControls_Impl();

    // Get attribute sets only once in constructor()
    const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
    const SvxSetItem* pSrchSetItem =
        static_cast<const SvxSetItem*>( rBindings.GetDispatcher()->Execute( FID_SEARCH_SEARCHSET, SfxCallMode::SLOT, ppArgs ) );

    if ( pSrchSetItem )
        InitAttrList_Impl( &pSrchSetItem->GetItemSet(), nullptr );

    const SvxSetItem* pReplSetItem =
        static_cast<const SvxSetItem*>( rBindings.GetDispatcher()->Execute( FID_SEARCH_REPLACESET, SfxCallMode::SLOT, ppArgs ) );

    if ( pReplSetItem )
        InitAttrList_Impl( nullptr, &pReplSetItem->GetItemSet() );

    // Create controller and update at once
    rBindings.EnterRegistrations();
    pSearchController.reset(
        new SvxSearchController( SID_SEARCH_ITEM, rBindings, *this ) );
    pOptionsController.reset(
        new SvxSearchController( SID_SEARCH_OPTIONS, rBindings, *this ) );
    rBindings.LeaveRegistrations();
    rBindings.GetDispatcher()->Execute( FID_SEARCH_ON, SfxCallMode::SLOT, ppArgs );
    pImpl->aSelectionTimer.Start();


    SvtCJKOptions aCJKOptions;
    if(!aCJKOptions.IsJapaneseFindEnabled())
    {
        m_xJapOptionsCB->set_active( false );
        m_xJapOptionsCB->hide();
        m_xJapOptionsBtn->hide();
    }
    if(!aCJKOptions.IsCJKFontEnabled())
    {
        m_xJapMatchFullHalfWidthCB->hide();
    }
    SvtCTLOptions aCTLOptions;
    // Do not disable and hide the m_xIncludeDiacritics button.
    // Include Diacritics == Not Ignore Diacritics => A does not match A-Umlaut (Diaeresis).
    // Confusingly these have negated names (following the UI) but the actual
    // transliteration is to *ignore* diacritics if "included" (sensitive) is
    // _not_ checked.
    if(!aCTLOptions.IsCTLFontEnabled())
    {
        m_xIncludeDiacritics->set_active( true );
        m_xIncludeKashida->set_active( true );
        m_xIncludeKashida->hide();
    }
    //component extension - show component search buttons if the commands
    // vnd.sun.star::SearchViaComponent1 and 2 are supported
    const uno::Reference< frame::XFrame >xFrame = rBindings.GetActiveFrame();
    const uno::Reference< frame::XDispatchProvider > xDispatchProv(xFrame, uno::UNO_QUERY);
    OUString sTarget("_self");

    bool bSearchComponent1 = false;
    bool bSearchComponent2 = false;
    if(xDispatchProv.is() &&
            (pImpl->xCommand1Dispatch = xDispatchProv->queryDispatch(pImpl->aCommand1URL, sTarget, 0)).is())
    {
        bSearchComponent1 = true;
    }
    if(xDispatchProv.is() &&
            (pImpl->xCommand2Dispatch = xDispatchProv->queryDispatch(pImpl->aCommand2URL, sTarget, 0)).is())
    {
        bSearchComponent2 = true;
    }

    if( bSearchComponent1 || bSearchComponent2 )
    {
        try
        {
            uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider =
                    configuration::theDefaultProvider::get( comphelper::getProcessComponentContext() );
            uno::Sequence< uno::Any > aArgs {
                        Any(OUString( "/org.openoffice.Office.Common/SearchOptions/")) };

            uno::Reference< uno::XInterface > xIFace = xConfigurationProvider->createInstanceWithArguments(
                        "com.sun.star.configuration.ConfigurationUpdateAccess",
                        aArgs);
            uno::Reference< container::XNameAccess> xDirectAccess(xIFace, uno::UNO_QUERY);
            if(xDirectAccess.is())
            {
                OUString sTemp;
                uno::Any aRet = xDirectAccess->getByName("ComponentSearchGroupLabel");
                aRet >>= sTemp;
                m_xComponentFrame->set_label(sTemp);
                aRet = xDirectAccess->getByName("ComponentSearchCommandLabel1");
                aRet >>= sTemp;
                m_xSearchComponent1PB->set_label( sTemp );
                aRet = xDirectAccess->getByName("ComponentSearchCommandLabel2");
                aRet >>= sTemp;
                m_xSearchComponent2PB->set_label( sTemp );
            }
        }
        catch(uno::Exception&){}

        if(!m_xSearchComponent1PB->get_label().isEmpty() && bSearchComponent1 )
        {
            m_xComponentFrame->show();
            m_xSearchComponent1PB->show();
        }
        if( !m_xSearchComponent2PB->get_label().isEmpty() )
        {
            m_xComponentFrame->show();
            m_xSearchComponent2PB->show();
        }
    }
}

void SvxSearchDialog::Close()
{
    // remember strings
    if (!aSearchStrings.empty())
        StrArrToList_Impl( SID_SEARCHDLG_SEARCHSTRINGS, aSearchStrings );

    if (!aReplaceStrings.empty())
        StrArrToList_Impl( SID_SEARCHDLG_REPLACESTRINGS, aReplaceStrings );

    // save settings to configuration
    SvtSearchOptions aOpt;
    aOpt.SetWholeWordsOnly          ( m_xWordBtn->get_active() );
    aOpt.SetBackwards               ( m_xReplaceBackwardsCB->get_active() );
    aOpt.SetUseRegularExpression    ( m_xRegExpBtn->get_active() );
    aOpt.SetUseWildcard             ( m_xWildcardBtn->get_active() );
    aOpt.SetSearchForStyles         ( m_xLayoutBtn->get_active() );
    aOpt.SetSimilaritySearch        ( m_xSimilarityBox->get_active() );
    aOpt.SetUseAsianOptions         ( m_xJapOptionsCB->get_active() );
    aOpt.SetNotes                   ( m_xNotesBtn->get_active() );
    aOpt.SetIgnoreDiacritics_CTL    ( !m_xIncludeDiacritics->get_active() );
    aOpt.SetIgnoreKashida_CTL       ( !m_xIncludeKashida->get_active() );
    aOpt.SetSearchFormatted         ( m_xSearchFormattedCB->get_active() );
    aOpt.Commit();

    if (IsClosing())
        return;

    const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
    rBindings.GetDispatcher()->Execute( FID_SEARCH_OFF, SfxCallMode::SLOT, ppArgs );
    rBindings.Invalidate(SID_SEARCH_DLG);

    SfxViewShell* pViewShell = SfxViewShell::Current();
    if (pViewShell)
        pViewShell->GetViewFrame()->ToggleChildWindow(SID_SEARCH_DLG);
}

TransliterationFlags SvxSearchDialog::GetTransliterationFlags() const
{
    if (!m_xMatchCaseCB->get_active())
        nTransliterationFlags |=  TransliterationFlags::IGNORE_CASE;
    else
        nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
    if ( !m_xJapMatchFullHalfWidthCB->get_active())
        nTransliterationFlags |=  TransliterationFlags::IGNORE_WIDTH;
    else
        nTransliterationFlags &= ~TransliterationFlags::IGNORE_WIDTH;
    return nTransliterationFlags;
}

void SvxSearchDialog::SetSaveToModule(bool b)
{
    pImpl->bSaveToModule = b;
}


void SvxSearchDialog::ApplyTransliterationFlags_Impl( TransliterationFlags nSettings )
{
    nTransliterationFlags = nSettings;
    bool bVal(nSettings & TransliterationFlags::IGNORE_CASE);
    m_xMatchCaseCB->set_active( !bVal );
    bVal = bool(nSettings & TransliterationFlags::IGNORE_WIDTH);
    m_xJapMatchFullHalfWidthCB->set_active( !bVal );
}


bool SvxSearchDialog::IsOtherOptionsExpanded()
{
    return m_xReplaceBackwardsCB->get_active() ||
           m_xSelectionBtn->get_active() ||
           m_xRegExpBtn->get_active() ||
           m_xLayoutBtn->get_active() ||
           m_xSimilarityBox->get_active() ||
           m_xJapMatchFullHalfWidthCB->get_active() ||
           m_xJapOptionsCB->get_active() ||
           m_xWildcardBtn->get_active() ||
           m_xNotesBtn->get_active() ||
           m_xIncludeKashida->get_active() ||
           m_xIncludeDiacritics->get_active();
}

void SvxSearchDialog::Activate()
{
    // apply possible transliteration changes of the SvxSearchItem member
    DBG_ASSERT( pSearchItem, "SearchItem missing" );
    if (pSearchItem)
    {
        m_xMatchCaseCB->set_active( pSearchItem->GetExact() );
        m_xJapMatchFullHalfWidthCB->set_active( !pSearchItem->IsMatchFullHalfWidthForms() );
    }

    SfxModelessDialogController::Activate();
}

void SvxSearchDialog::InitControls_Impl()
{
    // CaseSensitives AutoComplete
    m_xSearchLB->set_entry_completion( true, true );
    m_xSearchLB->show();
    m_xReplaceLB->set_entry_completion( true, true );
    m_xReplaceLB->show();

    m_xFormatBtn->set_sensitive(false);
    m_xAttributeBtn->set_sensitive(false);

    m_xSearchLB->connect_changed( LINK( this, SvxSearchDialog, ModifyHdl_Impl ) );
    m_xReplaceLB->connect_changed( LINK( this, SvxSearchDialog, ModifyHdl_Impl ) );

    Link<weld::Widget&,void> aLink = LINK( this, SvxSearchDialog, FocusHdl_Impl );
    m_xSearchLB->connect_focus_in( aLink );
    m_xReplaceLB->connect_focus_in( aLink );

    aLink = LINK( this, SvxSearchDialog, LoseFocusHdl_Impl );
    m_xSearchLB->connect_focus_out( aLink );
    m_xReplaceLB->connect_focus_out( aLink );

    m_xSearchTmplLB->connect_focus_out( aLink );
    m_xReplaceTmplLB->connect_focus_out( aLink );

    Link<weld::Button&,void> aLink2 = LINK( this, SvxSearchDialog, CommandHdl_Impl );
    m_xSearchBtn->connect_clicked( aLink2 );
    m_xBackSearchBtn->connect_clicked( aLink2 );
    m_xSearchAllBtn->connect_clicked( aLink2 );
    m_xReplaceBtn->connect_clicked( aLink2 );
    m_xReplaceAllBtn->connect_clicked( aLink2 );
    m_xCloseBtn->connect_clicked( aLink2 );
    m_xSimilarityBtn->connect_clicked( aLink2 );
    m_xJapOptionsBtn->connect_clicked( aLink2 );
    m_xSearchComponent1PB->connect_clicked( aLink2 );
    m_xSearchComponent2PB->connect_clicked( aLink2 );

    aLink2 = LINK( this, SvxSearchDialog, FlagHdl_Impl );
    m_xReplaceBackwardsCB->connect_clicked( aLink2 );
    m_xWordBtn->connect_clicked( aLink2 );
    m_xSelectionBtn->connect_clicked( aLink2 );
    m_xMatchCaseCB->connect_clicked( aLink2 );
    m_xRegExpBtn->connect_clicked( aLink2 );
    m_xWildcardBtn->connect_clicked( aLink2 );
    m_xNotesBtn->connect_clicked( aLink2 );
    m_xSimilarityBox->connect_clicked( aLink2 );
    m_xJapOptionsCB->connect_clicked( aLink2 );
    m_xJapMatchFullHalfWidthCB->connect_clicked( aLink2 );
    m_xIncludeDiacritics->connect_clicked( aLink2 );
    m_xIncludeKashida->connect_clicked( aLink2 );
    m_xLayoutBtn->connect_clicked( LINK( this, SvxSearchDialog, TemplateHdl_Impl ) );
    m_xFormatBtn->connect_clicked( LINK( this, SvxSearchDialog, FormatHdl_Impl ) );
    m_xNoFormatBtn->connect_clicked(
        LINK( this, SvxSearchDialog, NoFormatHdl_Impl ) );
    m_xAttributeBtn->connect_clicked(
        LINK( this, SvxSearchDialog, AttributeHdl_Impl ) );
}

namespace
{
    SvtModuleOptions::EFactory getModule(SfxBindings const & rBindings)
    {
        SvtModuleOptions::EFactory eFactory(SvtModuleOptions::EFactory::UNKNOWN_FACTORY);
        try
        {
            const uno::Reference< frame::XFrame > xFrame =
                rBindings.GetActiveFrame();
            uno::Reference< frame::XModuleManager2 > xModuleManager(
                frame::ModuleManager::create(::comphelper::getProcessComponentContext()));

            OUString aModuleIdentifier = xModuleManager->identify( xFrame );
            eFactory = SvtModuleOptions::ClassifyFactoryByServiceName(aModuleIdentifier);
        }
        catch (const uno::Exception&)
        {
        }
        return eFactory;
    }
}

void SvxSearchDialog::ShowOptionalControls_Impl()
{
    DBG_ASSERT( pSearchItem, "no search item" );

    SvtCJKOptions aCJKOptions;
    SvtCTLOptions aCTLOptions;
    SvtModuleOptions::EFactory eFactory = getModule(rBindings);
    bool bDrawApp = eFactory == SvtModuleOptions::EFactory::DRAW;
    bool bWriterApp =
        eFactory == SvtModuleOptions::EFactory::WRITER ||
        eFactory == SvtModuleOptions::EFactory::WRITERWEB ||
        eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL;
    bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC;

    m_xLayoutBtn->set_visible(!bDrawApp);
    m_xNotesBtn->set_visible(bWriterApp);
    m_xRegExpBtn->set_visible(!bDrawApp);
    m_xWildcardBtn->set_visible(bCalcApp); /* TODO:WILDCARD enable for other apps if hey handle it */
    m_xReplaceBackwardsCB->show();
    m_xSimilarityBox->show();
    m_xSimilarityBtn->show();
    m_xSelectionBtn->show();
    m_xIncludeDiacritics->show();
    m_xIncludeKashida->set_visible(aCTLOptions.IsCTLFontEnabled());
    m_xJapMatchFullHalfWidthCB->set_visible(aCJKOptions.IsCJKFontEnabled());
    m_xJapOptionsCB->set_visible(aCJKOptions.IsJapaneseFindEnabled());
    m_xJapOptionsBtn->set_visible(aCJKOptions.IsJapaneseFindEnabled());

    if (bWriter)
    {
        m_xAttributeBtn->show();
        m_xFormatBtn->show();
        m_xNoFormatBtn->show();
    }

    if (bCalcApp)
    {
        m_xCalcSearchInFT->show();
        m_xCalcSearchInLB->show();
        m_xCalcSearchDirFT->show();
        m_xRowsBtn->show();
        m_xColumnsBtn->show();
        m_xAllSheetsCB->show();
        m_xSearchFormattedCB->show();
    }
}


namespace {

class ToggleSaveToModule
{
public:
    ToggleSaveToModule(SvxSearchDialog& rDialog, bool bValue) :
        mrDialog(rDialog), mbValue(bValue)
    {
        mrDialog.SetSaveToModule(mbValue);
    }

    ~ToggleSaveToModule()
    {
        mrDialog.SetSaveToModule(!mbValue);
    }
private:
    SvxSearchDialog& mrDialog;
    bool const mbValue;
};

}

void SvxSearchDialog::Init_Impl( bool bSearchPattern )
{
    DBG_ASSERT( pSearchItem, "SearchItem == 0" );

    // We don't want to save any intermediate state to the module while the
    // dialog is being initialized.
    ToggleSaveToModule aNoModuleSave(*this, false);
    SvtSearchOptions aOpt;

    bWriter = ( pSearchItem->GetAppFlag() == SvxSearchApp::WRITER );

    if ( !( nModifyFlag & ModifyFlags::Word ) )
         m_xWordBtn->set_active( pSearchItem->GetWordOnly() );
    if ( !( nModifyFlag & ModifyFlags::Exact ) )
        m_xMatchCaseCB->set_active( pSearchItem->GetExact() );
    if ( !( nModifyFlag & ModifyFlags::Backwards ) )
        m_xReplaceBackwardsCB->set_active( bReplaceBackwards ); //adjustment to replace backwards
    if ( !( nModifyFlag & ModifyFlags::Notes ) )
        m_xNotesBtn->set_active( pSearchItem->GetNotes() );
    if ( !( nModifyFlag & ModifyFlags::Selection ) )
        m_xSelectionBtn->set_active( pSearchItem->GetSelection() );
    if ( !( nModifyFlag & ModifyFlags::Regexp ) )
        m_xRegExpBtn->set_active( pSearchItem->GetRegExp() );
    if ( !( nModifyFlag & ModifyFlags::Wildcard ) )
        m_xWildcardBtn->set_active( pSearchItem->GetWildcard() );
    if ( !( nModifyFlag & ModifyFlags::Layout ) )
        m_xLayoutBtn->set_active( pSearchItem->GetPattern() );
    if (m_xNotesBtn->get_active())
        m_xLayoutBtn->set_sensitive(false);
    m_xSimilarityBox->set_active( pSearchItem->IsLevenshtein() );
    if ( m_xJapOptionsCB->get_visible() )
        m_xJapOptionsCB->set_active( pSearchItem->IsUseAsianOptions() );
    m_xIncludeDiacritics->set_active( !aOpt.IsIgnoreDiacritics_CTL() );
    if ( m_xIncludeKashida->get_visible() )
        m_xIncludeKashida->set_active( !aOpt.IsIgnoreKashida_CTL() );
    if ( SvxSearchDialog::IsOtherOptionsExpanded() )
        m_xOtherOptionsExpander->set_expanded( true );
    ApplyTransliterationFlags_Impl( pSearchItem->GetTransliterationFlags() );

    ShowOptionalControls_Impl();

    if ( pSearchItem->GetAppFlag() == SvxSearchApp::CALC )
    {
        m_xCalcGrid->show();
        m_xSearchFormattedCB->set_active( aOpt.IsSearchFormatted() );
        Link<weld::Button&,void> aLink = LINK( this, SvxSearchDialog, FlagHdl_Impl );
        m_xCalcSearchInLB->connect_changed( LINK( this, SvxSearchDialog, LBSelectHdl_Impl ) );
        m_xRowsBtn->connect_clicked( aLink );
        m_xColumnsBtn->connect_clicked( aLink );
        m_xAllSheetsCB->connect_clicked( aLink );
        m_xSearchFormattedCB->connect_clicked( aLink );

        ModifyFlags nModifyFlagCheck;
        switch ( pSearchItem->GetCellType() )
        {
            case SvxSearchCellType::FORMULA:
                nModifyFlagCheck = ModifyFlags::Formulas;
                break;

            case SvxSearchCellType::VALUE:
                nModifyFlagCheck = ModifyFlags::Values;
                break;

            case SvxSearchCellType::NOTE:
                nModifyFlagCheck = ModifyFlags::CalcNotes;
                break;

            default:
                std::abort(); // cannot happen
        }
        if ( !(nModifyFlag & nModifyFlagCheck) )
            m_xCalcSearchInLB->set_active( static_cast<sal_Int32>(pSearchItem->GetCellType()) );

        m_xWordBtn->set_label( aCalcStr.getToken( 0, '#' ) );

        if ( pSearchItem->GetRowDirection() &&
             !( nModifyFlag & ModifyFlags::Rows ) )
            m_xRowsBtn->set_active(true);
        else if ( !pSearchItem->GetRowDirection() &&
                  !( nModifyFlag & ModifyFlags::Columns ) )
            m_xColumnsBtn->set_active(true);

        if ( !( nModifyFlag & ModifyFlags::AllTables ) )
            m_xAllSheetsCB->set_active( pSearchItem->IsAllTables() );

        // only look for formatting in Writer
        m_xFormatBtn->hide();
        m_xNoFormatBtn->hide();
        m_xAttributeBtn->hide();
    }
    else
    {
        m_xSearchFormattedCB->hide();
        m_xWordBtn->set_label( aCalcStr.getToken( 1, '#' ) );

        if ( pSearchItem->GetAppFlag() == SvxSearchApp::DRAW )
        {
            m_xSearchAllBtn->hide();

            m_xRegExpBtn->hide();
            m_xWildcardBtn->hide();
            m_xLayoutBtn->hide();

            // only look for formatting in Writer
            m_xFormatBtn->hide();
            m_xNoFormatBtn->hide();
            m_xAttributeBtn->hide();
        }
        else
        {
            m_xWildcardBtn->hide(); /* TODO:WILDCARD do not hide for other apps if they handle it */

            if ( !pSearchList )
            {
                // Get attribute sets, if it not has been done already
                const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
                const SvxSetItem* pSrchSetItem =
                    static_cast<const SvxSetItem*>(rBindings.GetDispatcher()->Execute( FID_SEARCH_SEARCHSET, SfxCallMode::SLOT, ppArgs ));

                if ( pSrchSetItem )
                    InitAttrList_Impl( &pSrchSetItem->GetItemSet(), nullptr );

                const SvxSetItem* pReplSetItem =
                    static_cast<const SvxSetItem*>( rBindings.GetDispatcher()->Execute( FID_SEARCH_REPLACESET, SfxCallMode::SLOT, ppArgs ) );

                if ( pReplSetItem )
                    InitAttrList_Impl( nullptr, &pReplSetItem->GetItemSet() );
            }
        }
    }

    // similarity search?
    if ( !( nModifyFlag & ModifyFlags::Similarity ) )
        m_xSimilarityBox->set_active( pSearchItem->IsLevenshtein() );
    bSet = true;

    FlagHdl_Impl(*m_xSimilarityBox);
    FlagHdl_Impl(*m_xJapOptionsCB);

    bool bDisableSearch = false;
    SfxViewShell* pViewShell = SfxViewShell::Current();

    if ( pViewShell )
    {
        bool bText = !bSearchPattern;

        if ( pViewShell->HasSelection( bText ) )
            EnableControl_Impl(*m_xSelectionBtn);
        else
        {
            m_xSelectionBtn->set_active( false );
            m_xSelectionBtn->set_sensitive(false);
        }
    }

    // Pattern Search and there were no AttrSets given
    if ( bSearchPattern )
    {
        SfxObjectShell* pShell = SfxObjectShell::Current();

        if ( pShell && pShell->GetStyleSheetPool() )
        {
            // Templates designed
            m_xSearchTmplLB->clear();
            m_xReplaceTmplLB->clear();
            SfxStyleSheetBasePool* pStylePool = pShell->GetStyleSheetPool();
            pStylePool->SetSearchMask( pSearchItem->GetFamily() );
            SfxStyleSheetBase* pBase = pStylePool->First();

            while ( pBase )
            {
                if ( pBase->IsUsed() )
                    m_xSearchTmplLB->append_text( pBase->GetName() );
                m_xReplaceTmplLB->append_text( pBase->GetName() );
                pBase = pStylePool->Next();
            }
            m_xSearchTmplLB->set_active_text( pSearchItem->GetSearchString() );
            m_xReplaceTmplLB->set_active_text( pSearchItem->GetReplaceString() );

        }
        m_xSearchTmplLB->show();

        if ( bConstruct )
            // Grab focus only after creating
            m_xSearchTmplLB->grab_focus();
        m_xReplaceTmplLB->show();
        m_xSearchLB->hide();
        m_xReplaceLB->hide();

        m_xWordBtn->set_sensitive(false);
        m_xRegExpBtn->set_sensitive(false);
        m_xWildcardBtn->set_sensitive(false);
        m_xMatchCaseCB->set_sensitive(false);

        bDisableSearch = !m_xSearchTmplLB->get_count();
    }
    else
    {
        bool bSetSearch = !( nModifyFlag & ModifyFlags::Search );
        bool bSetReplace = !( nModifyFlag & ModifyFlags::Replace );

        if ( !(pSearchItem->GetSearchString().isEmpty()) && bSetSearch )
            m_xSearchLB->set_entry_text( pSearchItem->GetSearchString() );
        else if (!aSearchStrings.empty())
        {
            bool bAttributes =
                ( ( pSearchList && pSearchList->Count() ) ||
                  ( pReplaceList && pReplaceList->Count() ) );

            if ( bSetSearch && !bAttributes )
                m_xSearchLB->set_entry_text(aSearchStrings[0]);

            OUString aReplaceTxt = pSearchItem->GetReplaceString();

            if (!aReplaceStrings.empty())
                aReplaceTxt = aReplaceStrings[0];

            if ( bSetReplace && !bAttributes )
                m_xReplaceLB->set_entry_text( aReplaceTxt );
        }
        m_xSearchLB->show();

        if ( bConstruct )
            // Grab focus only after creating
            m_xSearchLB->grab_focus();
        m_xReplaceLB->show();
        m_xSearchTmplLB->hide();
        m_xReplaceTmplLB->hide();

        EnableControl_Impl(*m_xRegExpBtn);
        EnableControl_Impl(*m_xWildcardBtn);
        EnableControl_Impl(*m_xMatchCaseCB);

        if ( m_xRegExpBtn->get_active() )
            m_xWordBtn->set_sensitive(false);
        else
            EnableControl_Impl(*m_xWordBtn);

        bDisableSearch = m_xSearchLB->get_active_text().isEmpty() &&
            m_xSearchAttrText->get_label().isEmpty();
    }
    FocusHdl_Impl(*m_xSearchLB);

    if ( bDisableSearch )
    {
        m_xSearchBtn->set_sensitive(false);
        m_xBackSearchBtn->set_sensitive(false);
        m_xSearchAllBtn->set_sensitive(false);
        m_xReplaceBtn->set_sensitive(false);
        m_xReplaceAllBtn->set_sensitive(false);
        m_xComponentFrame->set_sensitive(false);
    }
    else
    {
        EnableControl_Impl(*m_xSearchBtn);
        EnableControl_Impl(*m_xBackSearchBtn);
        EnableControl_Impl(*m_xReplaceBtn);
        if (!bWriter || !m_xNotesBtn->get_active())
        {
            EnableControl_Impl(*m_xSearchAllBtn);
            EnableControl_Impl(*m_xReplaceAllBtn);
        }
        if (bWriter && pSearchItem->GetNotes())
        {
            m_xSearchAllBtn->set_sensitive(false);
            m_xReplaceAllBtn->set_sensitive(false);
        }
    }

    if (!m_xSearchAttrText->get_label().isEmpty())
        EnableControl_Impl(*m_xNoFormatBtn);
    else
        m_xNoFormatBtn->set_sensitive(false);

    if ( !pSearchList )
    {
        m_xAttributeBtn->set_sensitive(false);
        m_xFormatBtn->set_sensitive(false);
    }

    if ( m_xLayoutBtn->get_active() )
    {
        pImpl->bSaveToModule = false;
        TemplateHdl_Impl(*m_xLayoutBtn);
        pImpl->bSaveToModule = true;
    }
}


void SvxSearchDialog::InitAttrList_Impl( const SfxItemSet* pSSet,
                                         const SfxItemSet* pRSet )
{
    if ( !pSSet && !pRSet )
        return;

    if ( !pImpl->pRanges && pSSet )
    {
        const sal_uInt16* pPtr = pSSet->GetRanges();
        const sal_uInt16* pTmp = pPtr;

        while( *pPtr )
        {
            pPtr += 2;
        }
        sal_sSize nCnt = pPtr - pTmp + 1;
        pImpl->pRanges.reset( new sal_uInt16[nCnt] );
        memcpy( pImpl->pRanges.get(), pTmp, sizeof(sal_uInt16) * nCnt );
    }

    bool bSetOptimalLayoutSize = false;

    // See to it that are the texts of the attributes are correct
    OUString aDesc;

    if ( pSSet )
    {
        pSearchList.reset(new SearchAttrItemList);

        if ( pSSet->Count() )
        {
            pSearchList->Put( *pSSet );

            m_xSearchAttrText->set_label( BuildAttrText_Impl( aDesc, true ) );

            if ( !aDesc.isEmpty() )
            {
                if (!m_xSearchAttrText->get_visible())
                {
                    m_xSearchAttrText->show();
                    bSetOptimalLayoutSize = true;
                }
                bFormat |= true;
            }
        }
    }

    if ( pRSet )
    {
        pReplaceList.reset(new SearchAttrItemList);

        if ( pRSet->Count() )
        {
            pReplaceList->Put( *pRSet );

            m_xReplaceAttrText->set_label( BuildAttrText_Impl( aDesc, false ) );

            if ( !aDesc.isEmpty() )
            {
                if (!m_xReplaceAttrText->get_visible())
                {
                    m_xReplaceAttrText->show();
                    bSetOptimalLayoutSize = true;
                }
                bFormat |= true;
            }
        }
    }

    if (bSetOptimalLayoutSize)
        m_xDialog->resize_to_request();
}

IMPL_LINK( SvxSearchDialog, LBSelectHdl_Impl, weld::ComboBox&, rCtrl, void )
{
    ClickHdl_Impl(&rCtrl);
}

IMPL_LINK( SvxSearchDialog, FlagHdl_Impl, weld::Button&, rCtrl, void )
{
    ClickHdl_Impl(&rCtrl);
}

void SvxSearchDialog::ClickHdl_Impl(const weld::Widget* pCtrl)
{
    if ( pCtrl && !bSet )
        SetModifyFlag_Impl(pCtrl);
    else
        bSet = false;

    if (pCtrl == m_xSimilarityBox.get())
    {
        bool bIsChecked = m_xSimilarityBox->get_active();

        if ( bIsChecked )
        {
            m_xSimilarityBtn->set_sensitive(true);
            m_xRegExpBtn->set_active( false );
            m_xRegExpBtn->set_sensitive(false);
            m_xWildcardBtn->set_active( false );
            m_xWildcardBtn->set_sensitive(false);
            EnableControl_Impl(*m_xWordBtn);

            if ( m_xLayoutBtn->get_active() )
            {
                EnableControl_Impl(*m_xMatchCaseCB);
                m_xLayoutBtn->set_active( false );
            }
            m_xRegExpBtn->set_sensitive(false);
            m_xWildcardBtn->set_sensitive(false);
            m_xLayoutBtn->set_sensitive(false);
            m_xFormatBtn->set_sensitive(false);
            m_xNoFormatBtn->set_sensitive(false);
            m_xAttributeBtn->set_sensitive(false);
        }
        else
        {
            EnableControl_Impl(*m_xRegExpBtn);
            EnableControl_Impl(*m_xWildcardBtn);
            if (!m_xNotesBtn->get_active())
                EnableControl_Impl(*m_xLayoutBtn);
            EnableControl_Impl(*m_xFormatBtn);
            EnableControl_Impl(*m_xAttributeBtn);
            m_xSimilarityBtn->set_sensitive(false);
        }
        pSearchItem->SetLevenshtein( bIsChecked );
    }
    else if (pCtrl == m_xNotesBtn.get())
    {
        if (m_xNotesBtn->get_active())
        {
            m_xLayoutBtn->set_sensitive(false);
            m_xSearchAllBtn->set_sensitive(false);
            m_xReplaceAllBtn->set_sensitive(false);
        }
        else
        {
            EnableControl_Impl(*m_xLayoutBtn);
            ModifyHdl_Impl(*m_xSearchLB);
        }
    }
    else
    {
        if ( m_xLayoutBtn->get_active() && !bFormat )
        {
            m_xWordBtn->set_active( false );
            m_xWordBtn->set_sensitive(false);
            m_xRegExpBtn->set_active( false );
            m_xRegExpBtn->set_sensitive(false);
            m_xWildcardBtn->set_active( false );
            m_xWildcardBtn->set_sensitive(false);
            m_xMatchCaseCB->set_active( false );
            m_xMatchCaseCB->set_sensitive(false);
            m_xNotesBtn->set_sensitive(false);

            if ( m_xSearchTmplLB->get_count() )
            {
                EnableControl_Impl(*m_xSearchBtn);
                EnableControl_Impl(*m_xBackSearchBtn);
                EnableControl_Impl(*m_xSearchAllBtn);
                EnableControl_Impl(*m_xReplaceBtn);
                EnableControl_Impl(*m_xReplaceAllBtn);
            }
        }
        else
        {
            EnableControl_Impl(*m_xRegExpBtn);
            EnableControl_Impl(*m_xWildcardBtn);
            EnableControl_Impl(*m_xMatchCaseCB);
            EnableControl_Impl(*m_xNotesBtn);

            if ( m_xRegExpBtn->get_active() )
            {
                m_xWordBtn->set_active( false );
                m_xWordBtn->set_sensitive(false);
                m_xWildcardBtn->set_active( false );
                m_xWildcardBtn->set_sensitive(false);
                m_xSimilarityBox->set_active( false );
                m_xSimilarityBox->set_sensitive(false);
                m_xSimilarityBtn->set_sensitive(false);
            }
            else if ( m_xWildcardBtn->get_active() )
            {
                m_xRegExpBtn->set_active( false );
                m_xRegExpBtn->set_sensitive(false);
                m_xSimilarityBox->set_active( false );
                m_xSimilarityBox->set_sensitive(false);
                m_xSimilarityBtn->set_sensitive(false);
            }
            else
            {
                EnableControl_Impl(*m_xWordBtn);
                EnableControl_Impl(*m_xSimilarityBox);
            }

            // Search-string in place? then enable Buttons
            bSet = true;
            ModifyHdl_Impl(*m_xSearchLB);
        }
    }

    if (pCtrl == m_xAllSheetsCB.get())
    {
        bSet = true;
        ModifyHdl_Impl(*m_xSearchLB);
    }

    if (pCtrl == m_xJapOptionsCB.get())
    {
        bool bEnableJapOpt = m_xJapOptionsCB->get_active();
        m_xMatchCaseCB->set_sensitive(!bEnableJapOpt );
        m_xJapMatchFullHalfWidthCB->set_sensitive(!bEnableJapOpt );
        m_xJapOptionsBtn->set_sensitive( bEnableJapOpt );
    }

    if ( pImpl->bSaveToModule )
        SaveToModule_Impl();
}

IMPL_LINK(SvxSearchDialog, CommandHdl_Impl, weld::Button&, rBtn, void)
{
    bool bInclusive = ( m_xLayoutBtn->get_label() == aLayoutStr );

    if ( ( &rBtn == m_xSearchBtn.get() )   ||
         (&rBtn == m_xBackSearchBtn.get())  ||
         ( &rBtn == m_xSearchAllBtn.get() )||
         ( &rBtn == m_xReplaceBtn.get() )  ||
         ( &rBtn == m_xReplaceAllBtn.get() ) )
    {
        if ( m_xLayoutBtn->get_active() && !bInclusive )
        {
            pSearchItem->SetSearchString ( m_xSearchTmplLB->get_active_text() );
            pSearchItem->SetReplaceString( m_xReplaceTmplLB->get_active_text() );
        }
        else
        {
            pSearchItem->SetSearchString ( m_xSearchLB->get_active_text() );
            pSearchItem->SetReplaceString( m_xReplaceLB->get_active_text() );

            if ( &rBtn == m_xReplaceBtn.get() )
                Remember_Impl( m_xReplaceLB->get_active_text(), false );
            else
            {
                Remember_Impl( m_xSearchLB->get_active_text(), true );

                if ( &rBtn == m_xReplaceAllBtn.get() )
                    Remember_Impl( m_xReplaceLB->get_active_text(), false );
            }
        }

        pSearchItem->SetRegExp( false );
        pSearchItem->SetWildcard( false );
        pSearchItem->SetLevenshtein( false );
        if (GetCheckBoxValue(*m_xRegExpBtn))
            pSearchItem->SetRegExp( true );
        else if (GetCheckBoxValue(*m_xWildcardBtn))
            pSearchItem->SetWildcard( true );
        else if (GetCheckBoxValue(*m_xSimilarityBox))
            pSearchItem->SetLevenshtein( true );

        pSearchItem->SetWordOnly(GetCheckBoxValue(*m_xWordBtn));

        bool bSetBackwards = false;
        if( &rBtn == m_xBackSearchBtn.get())
        {
            bSetBackwards = true;
        }
        else if( &rBtn == m_xReplaceBtn.get())
        {
            bSetBackwards = GetCheckBoxValue(*m_xReplaceBackwardsCB);
            bReplaceBackwards = GetCheckBoxValue(*m_xReplaceBackwardsCB);
        }

        pSearchItem->SetBackward(bSetBackwards);

        pSearchItem->SetNotes(GetCheckBoxValue(*m_xNotesBtn));
        pSearchItem->SetPattern(GetCheckBoxValue(*m_xLayoutBtn));
        pSearchItem->SetSelection(GetCheckBoxValue(*m_xSelectionBtn));
        pSearchItem->SetUseAsianOptions(GetCheckBoxValue(*m_xJapOptionsCB));
        TransliterationFlags nFlags = GetTransliterationFlags();
        if( !pSearchItem->IsUseAsianOptions())
            nFlags &= TransliterationFlags::IGNORE_CASE |
                      TransliterationFlags::IGNORE_WIDTH;
        if (GetNegatedCheckBoxValue(*m_xIncludeDiacritics))
            nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL;
        if (GetNegatedCheckBoxValue(*m_xIncludeKashida))
            nFlags |= TransliterationFlags::IGNORE_KASHIDA_CTL;
        pSearchItem->SetTransliterationFlags( nFlags );

        if ( !bWriter )
        {
            if ( m_xCalcSearchInLB->get_active() != -1)
                pSearchItem->SetCellType( static_cast<SvxSearchCellType>(m_xCalcSearchInLB->get_active()) );

            pSearchItem->SetRowDirection( m_xRowsBtn->get_active() );
            pSearchItem->SetAllTables( m_xAllSheetsCB->get_active() );
            pSearchItem->SetSearchFormatted( m_xSearchFormattedCB->get_active() );
        }

        if ((&rBtn == m_xSearchBtn.get()) ||  (&rBtn == m_xBackSearchBtn.get()))
            pSearchItem->SetCommand( SvxSearchCmd::FIND );
        else if ( &rBtn == m_xSearchAllBtn.get() )
            pSearchItem->SetCommand( SvxSearchCmd::FIND_ALL );
        else if ( &rBtn == m_xReplaceBtn.get() )
            pSearchItem->SetCommand( SvxSearchCmd::REPLACE );
        else if ( &rBtn == m_xReplaceAllBtn.get() )
            pSearchItem->SetCommand( SvxSearchCmd::REPLACE_ALL );

        // when looking for templates, delete format lists
        if ( !bFormat && pSearchItem->GetPattern() )
        {
            if ( pSearchList )
                pSearchList->Clear();

            if ( pReplaceList )
                pReplaceList->Clear();
        }
        nModifyFlag = ModifyFlags::NONE;
        const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
        rBindings.ExecuteSynchron( FID_SEARCH_NOW, ppArgs );
    }
    else if ( &rBtn == m_xCloseBtn.get() )
    {
        if ( !m_xLayoutBtn->get_active() || bInclusive )
        {
            OUString aStr( m_xSearchLB->get_active_text() );

            if ( !aStr.isEmpty() )
                Remember_Impl( aStr, true );
            aStr = m_xReplaceLB->get_active_text();

            if ( !aStr.isEmpty() )
                Remember_Impl( aStr, false );
        }
        SaveToModule_Impl();
        Close();
    }
    else if (&rBtn == m_xSimilarityBtn.get())
    {
        SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
        ScopedVclPtr<AbstractSvxSearchSimilarityDialog> pDlg(pFact->CreateSvxSearchSimilarityDialog(m_xDialog.get(),
                                                                    pSearchItem->IsLEVRelaxed(),
                                                                    pSearchItem->GetLEVOther(),
                                                                    pSearchItem->GetLEVShorter(),
                                                                    pSearchItem->GetLEVLonger() ));
        if ( executeSubDialog(pDlg.get()) == RET_OK )
        {
            pSearchItem->SetLEVRelaxed( pDlg->IsRelaxed() );
            pSearchItem->SetLEVOther( pDlg->GetOther() );
            pSearchItem->SetLEVShorter( pDlg->GetShorter() );
            pSearchItem->SetLEVLonger( pDlg->GetLonger() );
            SaveToModule_Impl();
        }
    }
    else if (&rBtn == m_xJapOptionsBtn.get())
    {
        SfxItemSet aSet( SfxGetpApp()->GetPool() );
        pSearchItem->SetTransliterationFlags( GetTransliterationFlags() );
        SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
        ScopedVclPtr<AbstractSvxJSearchOptionsDialog> aDlg(pFact->CreateSvxJSearchOptionsDialog(m_xDialog.get(), aSet,
                pSearchItem->GetTransliterationFlags() ));
        int nRet = executeSubDialog(aDlg.get());
        if (RET_OK == nRet) //! true only if FillItemSet of SvxJSearchOptionsPage returns true
        {
            TransliterationFlags nFlags = aDlg->GetTransliterationFlags();
            pSearchItem->SetTransliterationFlags( nFlags );
            ApplyTransliterationFlags_Impl( nFlags );
        }
    }
    else if (&rBtn == m_xSearchComponent1PB.get() || &rBtn == m_xSearchComponent2PB.get())
    {
        uno::Sequence < beans::PropertyValue > aArgs(2);
        beans::PropertyValue* pArgs = aArgs.getArray();
        pArgs[0].Name = "SearchString";
        pArgs[0].Value <<= m_xSearchLB->get_active_text();
        pArgs[1].Name = "ParentWindow";
        pArgs[1].Value <<= m_xDialog->GetXWindow();
        if (&rBtn == m_xSearchComponent1PB.get())
        {
            if ( pImpl->xCommand1Dispatch.is() )
                pImpl->xCommand1Dispatch->dispatch(pImpl->aCommand1URL, aArgs);
        }
        else
        {
            if ( pImpl->xCommand2Dispatch.is() )
                pImpl->xCommand2Dispatch->dispatch(pImpl->aCommand2URL, aArgs);
        }
    }
}


IMPL_LINK( SvxSearchDialog, ModifyHdl_Impl, weld::ComboBox&, rEd, void )
{
    if ( !bSet )
        SetModifyFlag_Impl( &rEd );
    else
        bSet = false;

    // Calc allows searching for empty cells.
    bool bAllowEmptySearch = (pSearchItem->GetAppFlag() == SvxSearchApp::CALC);

    if (&rEd == m_xSearchLB.get() || &rEd == m_xReplaceLB.get())
    {
        sal_Int32 nSrchTxtLen = m_xSearchLB->get_active_text().getLength();
        sal_Int32 nReplTxtLen = 0;
        if (bAllowEmptySearch)
            nReplTxtLen = m_xReplaceLB->get_active_text().getLength();
        sal_Int32 nAttrTxtLen = m_xSearchAttrText->get_label().getLength();

        if (nSrchTxtLen || nReplTxtLen || nAttrTxtLen)
        {
            EnableControl_Impl(*m_xSearchBtn);
            EnableControl_Impl(*m_xBackSearchBtn);
            EnableControl_Impl(*m_xReplaceBtn);
            if (!bWriter || !m_xNotesBtn->get_active())
            {
                EnableControl_Impl(*m_xSearchAllBtn);
                EnableControl_Impl(*m_xReplaceAllBtn);
            }
        }
        else
        {
            m_xComponentFrame->set_sensitive(false);
            m_xSearchBtn->set_sensitive(false);
            m_xBackSearchBtn->set_sensitive(false);
            m_xSearchAllBtn->set_sensitive(false);
            m_xReplaceBtn->set_sensitive(false);
            m_xReplaceAllBtn->set_sensitive(false);
        }
    }
}

IMPL_LINK_NOARG(SvxSearchDialog, TemplateHdl_Impl, weld::Button&, void)
{
    if ( pImpl->bSaveToModule )
        SaveToModule_Impl();

    if ( bFormat )
        return;
    OUString sDesc;

    if ( m_xLayoutBtn->get_active() )
    {
        if ( !pFamilyController )
        {
            sal_uInt16 nId = 0;

            // Enable templates controller
            switch ( pSearchItem->GetFamily() )
            {
                case SfxStyleFamily::Char:
                    nId = SID_STYLE_FAMILY1; break;

                case SfxStyleFamily::Para:
                    nId = SID_STYLE_FAMILY2; break;

                case SfxStyleFamily::Frame:
                    nId = SID_STYLE_FAMILY3; break;

                case SfxStyleFamily::Page:
                    nId = SID_STYLE_FAMILY4; break;

                case SfxStyleFamily::All:
                    break;

                default:
                    OSL_FAIL( "StyleSheetFamily was changed?" );
            }

            rBindings.EnterRegistrations();
            pFamilyController.reset(
                new SvxSearchController( nId, rBindings, *this ) );
            rBindings.LeaveRegistrations();
            m_xSearchTmplLB->clear();
            m_xReplaceTmplLB->clear();

            m_xSearchTmplLB->show();
            m_xReplaceTmplLB->show();
            m_xSearchLB->hide();
            m_xReplaceLB->hide();

            m_xSearchAttrText->set_label( sDesc );
            m_xReplaceAttrText->set_label( sDesc );

            if(!sDesc.isEmpty())
            {
                if (!m_xReplaceAttrText->get_visible() || !m_xReplaceAttrText->get_visible())
                {
                    m_xSearchAttrText->show();
                    m_xReplaceAttrText->show();
                    m_xDialog->resize_to_request();
                }
            }
        }
        m_xFormatBtn->set_sensitive(false);
        m_xNoFormatBtn->set_sensitive(false);
        m_xAttributeBtn->set_sensitive(false);
        m_xSimilarityBox->set_sensitive(false);
        m_xSimilarityBtn->set_sensitive(false);
    }
    else
    {
        // Disable templates controller
        rBindings.EnterRegistrations();
        pFamilyController.reset();
        rBindings.LeaveRegistrations();

        m_xSearchLB->show();
        m_xReplaceLB->show();
        m_xSearchTmplLB->hide();
        m_xReplaceTmplLB->hide();

        m_xSearchAttrText->set_label( BuildAttrText_Impl( sDesc, true ) );
        m_xReplaceAttrText->set_label( BuildAttrText_Impl( sDesc, false ) );

        if(!sDesc.isEmpty())
        {
            if (!m_xReplaceAttrText->get_visible() || !m_xReplaceAttrText->get_visible())
            {
                m_xSearchAttrText->show();
                m_xReplaceAttrText->show();
                m_xDialog->resize_to_request();
            }
        }

        EnableControl_Impl(*m_xFormatBtn);
        EnableControl_Impl(*m_xAttributeBtn);
        EnableControl_Impl(*m_xSimilarityBox);

        FocusHdl_Impl( bSearch ? *m_xSearchLB : *m_xReplaceLB );
    }
    bSet = true;
    pImpl->bSaveToModule = false;
    FlagHdl_Impl(*m_xLayoutBtn);
    pImpl->bSaveToModule = true;
}

void SvxSearchDialog::Remember_Impl( const OUString &rStr, bool _bSearch )
{
    if ( rStr.isEmpty() )
        return;

    std::vector<OUString>* pArr = _bSearch ? &aSearchStrings : &aReplaceStrings;
    weld::ComboBox* pListBox = _bSearch ? m_xSearchLB.get() : m_xReplaceLB.get();

    // ignore identical strings
    if (std::find(pArr->begin(), pArr->end(), rStr) != pArr->end())
        return;

    // delete oldest entry at maximum occupancy (ListBox and Array)
    if(nRememberSize < pArr->size())
    {
        pListBox->remove(static_cast<sal_uInt16>(nRememberSize - 1));
        (*pArr)[nRememberSize - 1] = rStr;
        pArr->erase(pArr->begin() + nRememberSize - 1);
    }

    pArr->insert(pArr->begin(), rStr);
    pListBox->insert_text(0, rStr);
}

void SvxSearchDialog::TemplatesChanged_Impl( SfxStyleSheetBasePool& rPool )
{
    OUString aOldSrch( m_xSearchTmplLB->get_active_text() );
    OUString aOldRepl( m_xReplaceTmplLB->get_active_text() );
    m_xSearchTmplLB->clear();
    m_xReplaceTmplLB->clear();
    rPool.SetSearchMask( pSearchItem->GetFamily() );
    m_xSearchTmplLB->freeze();
    m_xReplaceTmplLB->freeze();
    SfxStyleSheetBase* pBase = rPool.First();

    while ( pBase )
    {
        if ( pBase->IsUsed() )
            m_xSearchTmplLB->append_text( pBase->GetName() );
        m_xReplaceTmplLB->append_text( pBase->GetName() );
        pBase = rPool.Next();
    }
    m_xSearchTmplLB->thaw();
    m_xReplaceTmplLB->thaw();
    m_xSearchTmplLB->set_active(0);

    if ( !aOldSrch.isEmpty() )
        m_xSearchTmplLB->set_active_text( aOldSrch );
    m_xReplaceTmplLB->set_active(0);

    if ( !aOldRepl.isEmpty() )
        m_xReplaceTmplLB->set_active_text( aOldRepl );

    if ( m_xSearchTmplLB->get_count() )
    {
        EnableControl_Impl(*m_xSearchBtn);
        EnableControl_Impl(*m_xBackSearchBtn);
        EnableControl_Impl(*m_xSearchAllBtn);
        EnableControl_Impl(*m_xReplaceBtn);
        EnableControl_Impl(*m_xReplaceAllBtn);
    }
}


void SvxSearchDialog::EnableControls_Impl( const SearchOptionFlags nFlags )
{
    if ( nFlags == nOptions )
        return;
    else
        nOptions = nFlags;

    bool bNoSearch = true;

    bool bEnableSearch = bool( SearchOptionFlags::SEARCH & nOptions );
    m_xSearchBtn->set_sensitive(bEnableSearch);
    m_xBackSearchBtn->set_sensitive(bEnableSearch);

    if( bEnableSearch )
        bNoSearch = false;


    if ( SearchOptionFlags::SEARCHALL & nOptions )
    {
        m_xSearchAllBtn->set_sensitive(true);
        bNoSearch = false;
    }
    else
        m_xSearchAllBtn->set_sensitive(false);
    if ( SearchOptionFlags::REPLACE & nOptions )
    {
        m_xReplaceBtn->set_sensitive(true);
        m_xReplaceFrame->set_sensitive(true);
        m_xReplaceLB->set_sensitive(true);
        m_xReplaceTmplLB->set_sensitive(true);
        bNoSearch = false;
    }
    else
    {
        m_xReplaceBtn->set_sensitive(false);
        m_xReplaceFrame->set_sensitive(false);
        m_xReplaceLB->set_sensitive(false);
        m_xReplaceTmplLB->set_sensitive(false);
    }
    if ( SearchOptionFlags::REPLACE_ALL & nOptions )
    {
        m_xReplaceAllBtn->set_sensitive(true);
        bNoSearch = false;
    }
    else
        m_xReplaceAllBtn->set_sensitive(false);
    m_xComponentFrame->set_sensitive(!bNoSearch);
    m_xSearchBtn->set_sensitive( !bNoSearch );
    m_xBackSearchBtn->set_sensitive( !bNoSearch );
    m_xSearchFrame->set_sensitive( !bNoSearch );
    m_xSearchLB->set_sensitive( !bNoSearch );
    m_xNotesBtn->set_sensitive(true);

    if ( SearchOptionFlags::WHOLE_WORDS & nOptions )
        m_xWordBtn->set_sensitive(true);
    else
        m_xWordBtn->set_sensitive(false);
    if ( SearchOptionFlags::BACKWARDS & nOptions )
    {
        m_xBackSearchBtn->set_sensitive(true);
        m_xReplaceBackwardsCB->set_sensitive(true);
    }
    else
    {
        m_xBackSearchBtn->set_sensitive(false);
        m_xReplaceBackwardsCB->set_sensitive(false);
    }
    if ( SearchOptionFlags::REG_EXP & nOptions )
        m_xRegExpBtn->set_sensitive(true);
    else
        m_xRegExpBtn->set_sensitive(false);
    if ( SearchOptionFlags::WILDCARD & nOptions )
        m_xWildcardBtn->set_sensitive(true);
    else
        m_xWildcardBtn->set_sensitive(false);
    if ( SearchOptionFlags::EXACT & nOptions )
        m_xMatchCaseCB->set_sensitive(true);
    else
        m_xMatchCaseCB->set_sensitive(false);
    if ( SearchOptionFlags::SELECTION & nOptions )
        m_xSelectionBtn->set_sensitive(true);
    else
        m_xSelectionBtn->set_sensitive(false);
    if ( SearchOptionFlags::FAMILIES & nOptions )
        m_xLayoutBtn->set_sensitive(true);
    else
        m_xLayoutBtn->set_sensitive(false);
    if ( SearchOptionFlags::FORMAT & nOptions )
    {
        m_xAttributeBtn->set_sensitive(true);
        m_xFormatBtn->set_sensitive(true);
        m_xNoFormatBtn->set_sensitive(true);
    }
    else
    {
        m_xAttributeBtn->set_sensitive(false);
        m_xFormatBtn->set_sensitive(false);
        m_xNoFormatBtn->set_sensitive(false);
    }

    if ( SearchOptionFlags::SIMILARITY & nOptions )
    {
        m_xSimilarityBox->set_sensitive(true);
        m_xSimilarityBtn->set_sensitive(true);
    }
    else
    {
        m_xSimilarityBox->set_sensitive(false);
        m_xSimilarityBtn->set_sensitive(false);
    }

    if ( pSearchItem )
        Init_Impl( pSearchItem->GetPattern() &&
                   ( !pSearchList || !pSearchList->Count() ) );
}

void SvxSearchDialog::EnableControl_Impl(const weld::Widget& rCtrl)
{
    if (m_xSearchBtn.get() == &rCtrl && ( SearchOptionFlags::SEARCH & nOptions ) )
    {
        m_xComponentFrame->set_sensitive(true);
        m_xSearchBtn->set_sensitive(true);
        return;
    }
    if ( m_xSearchAllBtn.get() == &rCtrl &&
         ( SearchOptionFlags::SEARCHALL & nOptions )  )
    {
        m_xSearchAllBtn->set_sensitive(true);
        return;
    }
    if ( m_xReplaceBtn.get() == &rCtrl && ( SearchOptionFlags::REPLACE & nOptions )  )
    {
        m_xReplaceBtn->set_sensitive(true);
        return;
    }
    if ( m_xReplaceAllBtn.get() == &rCtrl &&
         ( SearchOptionFlags::REPLACE_ALL & nOptions ) )
    {
        m_xReplaceAllBtn->set_sensitive(true);
        return;
    }
    if ( m_xWordBtn.get() == &rCtrl && ( SearchOptionFlags::WHOLE_WORDS & nOptions ) )
    {
        m_xWordBtn->set_sensitive(true);
        return;
    }
    if ( SearchOptionFlags::BACKWARDS & nOptions )
    {
        if( m_xBackSearchBtn.get() == &rCtrl )
        {
            m_xBackSearchBtn->set_sensitive(true);
            return;
        }
        else if ( m_xReplaceBackwardsCB.get() == &rCtrl )
        {
            m_xReplaceBackwardsCB->set_sensitive(true);
            return;
        }
    }
    if (m_xNotesBtn.get() == &rCtrl)
    {
        m_xNotesBtn->set_sensitive(true);
        return;
    }
    if ( m_xRegExpBtn.get() == &rCtrl && ( SearchOptionFlags::REG_EXP & nOptions )
        && !m_xSimilarityBox->get_active() && !m_xWildcardBtn->get_active())
    {
        m_xRegExpBtn->set_sensitive(true);
        return;
    }
    if ( m_xWildcardBtn.get() == &rCtrl && ( SearchOptionFlags::WILDCARD & nOptions )
        && !m_xSimilarityBox->get_active() && !m_xRegExpBtn->get_active())
    {
        m_xWildcardBtn->set_sensitive(true);
        return;
    }
    if ( m_xMatchCaseCB.get() == &rCtrl && ( SearchOptionFlags::EXACT & nOptions ) )
    {
        if (!m_xJapOptionsCB->get_active())
            m_xMatchCaseCB->set_sensitive(true);
        return;
    }
    if ( m_xSelectionBtn.get() == &rCtrl && ( SearchOptionFlags::SELECTION & nOptions ) )
    {
        m_xSelectionBtn->set_sensitive(true);
        return;
    }
    if ( m_xLayoutBtn.get() == &rCtrl && ( SearchOptionFlags::FAMILIES & nOptions ) )
    {
        m_xLayoutBtn->set_sensitive(true);
        return;
    }
    if ( m_xAttributeBtn.get() == &rCtrl
         && ( SearchOptionFlags::FORMAT & nOptions )
         && pSearchList )
    {
        m_xAttributeBtn->set_sensitive( pImpl->bFocusOnSearch );
    }
    if ( m_xFormatBtn.get() == &rCtrl && ( SearchOptionFlags::FORMAT & nOptions ) )
    {
        m_xFormatBtn->set_sensitive(true);
        return;
    }
    if ( m_xNoFormatBtn.get() == &rCtrl && ( SearchOptionFlags::FORMAT & nOptions ) )
    {
        m_xNoFormatBtn->set_sensitive(true);
        return;
    }
    if ( m_xSimilarityBox.get() == &rCtrl && ( SearchOptionFlags::SIMILARITY & nOptions )
        && !m_xRegExpBtn->get_active() && !m_xWildcardBtn->get_active())
    {
        m_xSimilarityBox->set_sensitive(true);

        if ( m_xSimilarityBox->get_active() )
            m_xSimilarityBtn->set_sensitive(true);
    }
}

void SvxSearchDialog::SetItem_Impl( const SvxSearchItem* pItem )
{
    //TODO: save pItem and process later if m_executingSubDialog?
    if ( pItem && !m_executingSubDialog )
    {
        pSearchItem.reset(static_cast<SvxSearchItem*>(pItem->Clone()));
        Init_Impl( pSearchItem->GetPattern() &&
                   ( !pSearchList || !pSearchList->Count() ) );
    }
}

IMPL_LINK(SvxSearchDialog, FocusHdl_Impl, weld::Widget&, rControl, void)
{
    sal_Int32 nTxtLen = m_xSearchAttrText->get_label().getLength();
    weld::Widget* pCtrl = &rControl;
    if (pCtrl == m_xSearchLB.get())
    {
        if (pCtrl->has_focus())
            pImpl->bFocusOnSearch = true;
        pCtrl = m_xSearchLB.get();
        bSearch = true;

        if( nTxtLen )
            EnableControl_Impl(*m_xNoFormatBtn);
        else
            m_xNoFormatBtn->set_sensitive(false);
        EnableControl_Impl(*m_xAttributeBtn);
    }
    else
    {
        pImpl->bFocusOnSearch = false;
        pCtrl = m_xReplaceLB.get();
        bSearch = false;

        if (!m_xReplaceAttrText->get_label().isEmpty())
            EnableControl_Impl(*m_xNoFormatBtn);
        else
            m_xNoFormatBtn->set_sensitive(false);
        m_xAttributeBtn->set_sensitive(false);
    }
    bSet = true;

    weld::ComboBox &rComboBox = dynamic_cast<weld::ComboBox&>(*pCtrl);
    rComboBox.select_entry_region(0, -1);
    ModifyHdl_Impl(rComboBox);

    if (bFormat && nTxtLen)
        m_xLayoutBtn->set_label(aLayoutStr);
    else
    {
        SvtModuleOptions::EFactory eFactory = getModule(rBindings);
        bool bWriterApp =
            eFactory == SvtModuleOptions::EFactory::WRITER ||
            eFactory == SvtModuleOptions::EFactory::WRITERWEB ||
            eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL;
        bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC;

        if (bWriterApp)
            m_xLayoutBtn->set_label(aLayoutWriterStr);
        else
        {
            if (bCalcApp)
                m_xLayoutBtn->set_label(aLayoutCalcStr);
            else
                m_xLayoutBtn->set_label(aStylesStr);
        }
    }
}

IMPL_LINK_NOARG(SvxSearchDialog, LoseFocusHdl_Impl, weld::Widget&, void)
{
    SaveToModule_Impl();
}

IMPL_LINK_NOARG(SvxSearchDialog, FormatHdl_Impl, weld::Button&, void)
{
    SfxObjectShell* pSh = SfxObjectShell::Current();

    DBG_ASSERT( pSh, "no DocShell" );

    if ( !pSh || !pImpl->pRanges )
        return;

    SfxItemPool& rPool = pSh->GetPool();
    SfxItemSet aSet(rPool, pImpl->pRanges.get());

    aSet.MergeRange(SID_ATTR_PARA_MODEL, SID_ATTR_PARA_MODEL);

    sal_uInt16 nBrushWhich = pSh->GetPool().GetWhich(SID_ATTR_BRUSH);
    aSet.MergeRange(nBrushWhich, nBrushWhich);

    aSet.MergeRange(XATTR_FILL_FIRST, XATTR_FILL_LAST);

    OUString aTxt;

    aSet.InvalidateAllItems();
    aSet.Put(SvxBrushItem(nBrushWhich));

    if ( bSearch )
    {
        aTxt = SvxResId( RID_SVXSTR_SEARCH );
        pSearchList->Get( aSet );
    }
    else
    {
        aTxt = SvxResId( RID_SVXSTR_REPLACE );
        pReplaceList->Get( aSet );
    }
    aSet.DisableItem(SID_ATTR_PARA_MODEL);
    aSet.DisableItem(rPool.GetWhich(SID_ATTR_PARA_PAGEBREAK));
    aSet.DisableItem(rPool.GetWhich(SID_ATTR_PARA_KEEP));


    SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
    ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTabItemDialog(m_xDialog.get(), aSet));
    pDlg->SetText( aTxt );

    if ( executeSubDialog(pDlg.get()) == RET_OK )
    {
        DBG_ASSERT( pDlg->GetOutputItemSet(), "invalid Output-Set" );
        SfxItemSet aOutSet( *pDlg->GetOutputItemSet() );

        SearchAttrItemList* pList = bSearch ? pSearchList.get() : pReplaceList.get();

        const SfxPoolItem* pItem;
        for( sal_uInt16 n = 0; n < pList->Count(); ++n )
        {
            SearchAttrItem* pAItem;
            if( !IsInvalidItem( (pAItem = &pList->GetObject(n))->pItem ) &&
                SfxItemState::SET == aOutSet.GetItemState(
                    pAItem->pItem->Which(), false, &pItem ) )
            {
                delete pAItem->pItem;
                pAItem->pItem = pItem->Clone();
                aOutSet.ClearItem( pAItem->pItem->Which() );
            }
        }

        if( aOutSet.Count() )
            pList->Put( aOutSet );

        PaintAttrText_Impl(); // Set AttributText in GroupBox
    }
}

IMPL_LINK_NOARG(SvxSearchDialog, NoFormatHdl_Impl, weld::Button&, void)
{
    SvtModuleOptions::EFactory eFactory = getModule(rBindings);
    bool bWriterApp =
        eFactory == SvtModuleOptions::EFactory::WRITER ||
        eFactory == SvtModuleOptions::EFactory::WRITERWEB ||
        eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL;
    bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC;

    if (bCalcApp)
        m_xLayoutBtn->set_label( aLayoutCalcStr );
    else
    {
        if (bWriterApp)
            m_xLayoutBtn->set_label( aLayoutWriterStr);
        else
            m_xLayoutBtn->set_label( aStylesStr );
    }

    bFormat = false;
    m_xLayoutBtn->set_active( false );

    bool bSetOptimalLayoutSize = false;

    if ( bSearch )
    {
        pSearchList->Clear();
        m_xSearchAttrText->set_label( "" );
        if (m_xSearchAttrText->get_visible())
        {
            m_xSearchAttrText->hide();
            bSetOptimalLayoutSize = true;
        }
    }
    else
    {
        pReplaceList->Clear();
        m_xReplaceAttrText->set_label( "" );
        if (m_xReplaceAttrText->get_visible())
        {
            m_xReplaceAttrText->hide();
            bSetOptimalLayoutSize = true;
        }
    }

    if (bSetOptimalLayoutSize)
        m_xDialog->resize_to_request();

    pImpl->bSaveToModule = false;
    TemplateHdl_Impl(*m_xLayoutBtn);
    pImpl->bSaveToModule = true;
    m_xNoFormatBtn->set_sensitive(false);
}

IMPL_LINK_NOARG(SvxSearchDialog, AttributeHdl_Impl, weld::Button&, void)
{
    if ( !pSearchList || !pImpl->pRanges )
        return;

    SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
    ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSvxSearchAttributeDialog(m_xDialog.get(), *pSearchList, pImpl->pRanges.get()));
    executeSubDialog(pDlg.get());
    PaintAttrText_Impl();
}

IMPL_LINK( SvxSearchDialog, TimeoutHdl_Impl, Timer *, pTimer, void )
{
    SfxViewShell* pViewShell = SfxViewShell::Current();

    if ( pViewShell )
    {
        if ( pViewShell->HasSelection( m_xSearchLB->get_visible() ) )
            EnableControl_Impl(*m_xSelectionBtn);
        else
        {
            m_xSelectionBtn->set_active( false );
            m_xSelectionBtn->set_sensitive(false);
        }
    }

    pTimer->Start();
}

OUString& SvxSearchDialog::BuildAttrText_Impl( OUString& rStr,
                                             bool bSrchFlag ) const
{
    rStr.clear();

    SfxObjectShell* pSh = SfxObjectShell::Current();
    DBG_ASSERT( pSh, "no DocShell" );

    if ( !pSh )
        return rStr;

    SfxItemPool& rPool = pSh->GetPool();
    SearchAttrItemList* pList = bSrchFlag ? pSearchList.get() : pReplaceList.get();

    if ( !pList )
        return rStr;

    // Metric query
    MapUnit eMapUnit = MapUnit::MapCM;
    FieldUnit eFieldUnit = pSh->GetModule()->GetFieldUnit();
    switch ( eFieldUnit )
    {
        case FieldUnit::MM:          eMapUnit = MapUnit::MapMM; break;
        case FieldUnit::CM:
        case FieldUnit::M:
        case FieldUnit::KM:          eMapUnit = MapUnit::MapCM; break;
        case FieldUnit::TWIP:        eMapUnit = MapUnit::MapTwip; break;
        case FieldUnit::POINT:
        case FieldUnit::PICA:        eMapUnit = MapUnit::MapPoint; break;
        case FieldUnit::INCH:
        case FieldUnit::FOOT:
        case FieldUnit::MILE:        eMapUnit = MapUnit::MapInch; break;
        case FieldUnit::MM_100TH:    eMapUnit = MapUnit::Map100thMM; break;
        default: ;//prevent warning
    }

    IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
    for ( sal_uInt16 i = 0; i < pList->Count(); ++i )
    {
        const SearchAttrItem& rItem = pList->GetObject(i);

        if ( !rStr.isEmpty() )
            rStr += ", ";

        if ( !IsInvalidItem( rItem.pItem ) )
        {
            OUString aStr;
            rPool.GetPresentation(*rItem.pItem, eMapUnit, aStr, aIntlWrapper);
            rStr += aStr;
        }
        else if ( rItem.nSlot == SID_ATTR_BRUSH_CHAR )
        {
            // Special treatment for text background
            rStr += SvxResId( RID_SVXITEMS_BRUSH_CHAR );
        }
        else
        {
            sal_uInt32 nId = SvxAttrNameTable::FindIndex(rItem.nSlot);
            if ( RESARRAY_INDEX_NOTFOUND != nId )
                rStr += SvxAttrNameTable::GetString(nId);
        }
    }
    return rStr;
}


void SvxSearchDialog::PaintAttrText_Impl()
{
    OUString aDesc;
    BuildAttrText_Impl( aDesc, bSearch );

    if ( !bFormat && !aDesc.isEmpty() )
        bFormat = true;

    bool bSetOptimalLayoutSize = false;

    if ( bSearch )
    {
        m_xSearchAttrText->set_label( aDesc );
        if (!aDesc.isEmpty() && !m_xSearchAttrText->get_visible())
        {
            m_xSearchAttrText->show();
            bSetOptimalLayoutSize = true;
        }

        FocusHdl_Impl(*m_xSearchLB);
    }
    else
    {
        m_xReplaceAttrText->set_label( aDesc );
        if (!aDesc.isEmpty() && !m_xReplaceAttrText->get_visible())
        {
            m_xReplaceAttrText->show();
            bSetOptimalLayoutSize = true;
        }

        FocusHdl_Impl(*m_xReplaceLB);
    }

    if (bSetOptimalLayoutSize)
        m_xDialog->resize_to_request();
}

void SvxSearchDialog::SetModifyFlag_Impl( const weld::Widget* pCtrl )
{
    if (m_xSearchLB.get() == pCtrl)
        nModifyFlag |= ModifyFlags::Search;
    else if ( m_xReplaceLB.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Replace;
    else if ( m_xWordBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Word;
    else if ( m_xMatchCaseCB.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Exact;
    else if ( m_xReplaceBackwardsCB.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Backwards;
    else if ( m_xNotesBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Notes;
    else if ( m_xSelectionBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Selection;
    else if ( m_xRegExpBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Regexp;
    else if ( m_xWildcardBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Wildcard;
    else if ( m_xLayoutBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Layout;
    else if ( m_xSimilarityBox.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Similarity;
    else if ( m_xCalcSearchInLB.get() == pCtrl )
    {
        nModifyFlag |= ModifyFlags::Formulas;
        nModifyFlag |= ModifyFlags::Values;
        nModifyFlag |= ModifyFlags::CalcNotes;
    }
    else if ( m_xRowsBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Rows;
    else if ( m_xColumnsBtn.get() == pCtrl )
        nModifyFlag |= ModifyFlags::Columns;
    else if ( m_xAllSheetsCB.get() == pCtrl )
        nModifyFlag |= ModifyFlags::AllTables;
}

void SvxSearchDialog::SaveToModule_Impl()
{
    if ( !pSearchItem )
        return;

    if ( m_xLayoutBtn->get_active() )
    {
        pSearchItem->SetSearchString ( m_xSearchTmplLB->get_active_text() );
        pSearchItem->SetReplaceString( m_xReplaceTmplLB->get_active_text() );
    }
    else
    {
        pSearchItem->SetSearchString ( m_xSearchLB->get_active_text() );
        pSearchItem->SetReplaceString( m_xReplaceLB->get_active_text() );
        Remember_Impl( m_xSearchLB->get_active_text(), true );
    }

    pSearchItem->SetRegExp( false );
    pSearchItem->SetWildcard( false );
    pSearchItem->SetLevenshtein( false );
    if (GetCheckBoxValue(*m_xRegExpBtn))
        pSearchItem->SetRegExp( true );
    else if (GetCheckBoxValue(*m_xWildcardBtn))
        pSearchItem->SetWildcard( true );
    else if (GetCheckBoxValue(*m_xSimilarityBox))
        pSearchItem->SetLevenshtein( true );

    pSearchItem->SetWordOnly(GetCheckBoxValue(*m_xWordBtn));
    pSearchItem->SetBackward(GetCheckBoxValue(*m_xReplaceBackwardsCB));
    pSearchItem->SetNotes(GetCheckBoxValue(*m_xNotesBtn));
    pSearchItem->SetPattern(GetCheckBoxValue(*m_xLayoutBtn));
    pSearchItem->SetSelection(GetCheckBoxValue(*m_xSelectionBtn));
    pSearchItem->SetUseAsianOptions(GetCheckBoxValue(*m_xJapOptionsCB));

    SvtSearchOptions aOpt;
    aOpt.SetIgnoreDiacritics_CTL(GetNegatedCheckBoxValue(*m_xIncludeDiacritics));
    aOpt.SetIgnoreKashida_CTL(GetNegatedCheckBoxValue(*m_xIncludeKashida));
    aOpt.Commit();

    TransliterationFlags nFlags = GetTransliterationFlags();
    if( !pSearchItem->IsUseAsianOptions())
        nFlags &= TransliterationFlags::IGNORE_CASE |
                   TransliterationFlags::IGNORE_WIDTH;
    if (GetNegatedCheckBoxValue(*m_xIncludeDiacritics))
        nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL;
    if (GetNegatedCheckBoxValue(*m_xIncludeKashida))
        nFlags |= TransliterationFlags::IGNORE_KASHIDA_CTL;
    pSearchItem->SetTransliterationFlags( nFlags );

    if ( !bWriter )
    {
        if (m_xCalcSearchInLB->get_active() != -1)
            pSearchItem->SetCellType( static_cast<SvxSearchCellType>(m_xCalcSearchInLB->get_active()) );

        pSearchItem->SetRowDirection( m_xRowsBtn->get_active() );
        pSearchItem->SetAllTables( m_xAllSheetsCB->get_active() );
        pSearchItem->SetSearchFormatted( m_xSearchFormattedCB->get_active() );
    }

    pSearchItem->SetCommand( SvxSearchCmd::FIND );
    nModifyFlag = ModifyFlags::NONE;
    const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
    rBindings.GetDispatcher()->Execute( SID_SEARCH_ITEM, SfxCallMode::SLOT, ppArgs );
}

void SvxSearchDialog::SetDocWin(vcl::Window* pDocWin)
{
    m_xDialog->clear_extra_accessible_relations();

    if (!pDocWin)
        return;

    Reference<css::accessibility::XAccessible> xDocAcc = pDocWin->GetAccessible();
    if (!xDocAcc.is())
    {
        return;
    }
    Reference<css::accessibility::XAccessibleGetAccFlowTo> xGetAccFlowTo(xDocAcc, UNO_QUERY);
    if (!xGetAccFlowTo.is())
    {
        return;
    }

    const sal_Int32 FORFINDREPLACEFLOWTO = 2;
    uno::Sequence<uno::Any> aAnySeq = xGetAccFlowTo->getAccFlowTo(Any(GetSrchFlag()),  FORFINDREPLACEFLOWTO);

    sal_Int32 nLen = aAnySeq.getLength();
    if (nLen)
    {
        uno::Sequence<uno::Reference<uno::XInterface>> aSequence(nLen);
        for (sal_Int32 i = 0; i < nLen; ++i)
        {
            uno::Reference < css::accessibility::XAccessible > xAcc;
            aAnySeq[i] >>= xAcc;
            aSequence[i] = xAcc;
        }
        m_xDialog->add_extra_accessible_relation(css::accessibility::AccessibleRelation(css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence));
    }
}

short SvxSearchDialog::executeSubDialog(VclAbstractDialog * dialog) {
    assert(!m_executingSubDialog);
    comphelper::ScopeGuard g([this] { m_executingSubDialog = false; });
    m_executingSubDialog = true;
    return dialog->Execute();
}

SFX_IMPL_CHILDWINDOW_WITHID(SvxSearchDialogWrapper, SID_SEARCH_DLG);


SvxSearchDialogWrapper::SvxSearchDialogWrapper( vcl::Window* _pParent, sal_uInt16 nId,
                                                SfxBindings* pBindings,
                                                SfxChildWinInfo const * pInfo )
    : SfxChildWindow( _pParent, nId )
    , dialog(new SvxSearchDialog(_pParent->GetFrameWeld(), this, *pBindings))
{
    SetController(dialog);
    dialog->Initialize( pInfo );

    pBindings->Update( SID_SEARCH_ITEM );
    pBindings->Update( SID_SEARCH_OPTIONS );
    pBindings->Update( SID_SEARCH_SEARCHSET );
    pBindings->Update( SID_SEARCH_REPLACESET );
    dialog->bConstruct = false;
}

SvxSearchDialogWrapper::~SvxSearchDialogWrapper ()
{
}


SfxChildWinInfo SvxSearchDialogWrapper::GetInfo() const
{
    SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
    aInfo.bVisible = false;
    return aInfo;
}

static void lcl_SetSearchLabelWindow(const OUString& rStr)
{
    SfxViewFrame* pViewFrame = SfxViewFrame::Current();
    if (!pViewFrame)
        return;

    css::uno::Reference< css::beans::XPropertySet > xPropSet(
            pViewFrame->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY_THROW);
    css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
    xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
    css::uno::Reference< css::ui::XUIElement > xUIElement =
        xLayoutManager->getElement("private:resource/toolbar/findbar");
    if (!xUIElement.is())
        return;
    css::uno::Reference< css::awt::XWindow > xWindow(
            xUIElement->getRealInterface(), css::uno::UNO_QUERY_THROW);
    VclPtr< ToolBox > pToolBox = static_cast<ToolBox*>( VCLUnoHelper::GetWindow(xWindow).get() );
    for (ToolBox::ImplToolItems::size_type i = 0; pToolBox && i < pToolBox->GetItemCount(); ++i)
    {
        sal_uInt16 id = pToolBox->GetItemId(i);
        if (pToolBox->GetItemCommand(id) == ".uno:SearchLabel")
        {
            vcl::Window* pSearchLabel = pToolBox->GetItemWindow(id);
            assert(pSearchLabel);
            pSearchLabel->SetText(rStr);
            if (rStr.isEmpty())
                pSearchLabel->SetSizePixel(Size(16, pSearchLabel->get_preferred_size().Height()));
            else
                pSearchLabel->SetSizePixel(pSearchLabel->get_preferred_size());
        }
    }
    xLayoutManager->doLayout();
    pToolBox->Resize();
}

OUString SvxSearchDialogWrapper::GetSearchLabel()
{
    SfxViewFrame* pViewFrame = SfxViewFrame::Current();
    if (!pViewFrame)
        return OUString();

    css::uno::Reference< css::beans::XPropertySet > xPropSet(
            pViewFrame->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY_THROW);
    css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
    xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
    css::uno::Reference< css::ui::XUIElement > xUIElement =
        xLayoutManager->getElement("private:resource/toolbar/findbar");
    if (!xUIElement.is())
        return OUString();
    css::uno::Reference< css::awt::XWindow > xWindow(
            xUIElement->getRealInterface(), css::uno::UNO_QUERY_THROW);
    VclPtr< ToolBox > pToolBox = static_cast<ToolBox*>( VCLUnoHelper::GetWindow(xWindow).get() );
    for (ToolBox::ImplToolItems::size_type i = 0; pToolBox && i < pToolBox->GetItemCount(); ++i)
    {
        sal_uInt16 id = pToolBox->GetItemId(i);
        if (pToolBox->GetItemCommand(id) == ".uno:SearchLabel")
        {
            vcl::Window* pSearchLabel = pToolBox->GetItemWindow(id);
            return pSearchLabel ? pSearchLabel->GetText() : OUString();
        }
    }
    return OUString();
}

void SvxSearchDialogWrapper::SetSearchLabel(const SearchLabel& rSL)
{
    OUString sStr;
    if (rSL == SearchLabel::End)
        sStr = SvxResId(RID_SVXSTR_SEARCH_END);
    else if (rSL == SearchLabel::Start)
        sStr = SvxResId(RID_SVXSTR_SEARCH_START);
    else if (rSL == SearchLabel::EndWrapped)
        sStr = SvxResId(RID_SVXSTR_SEARCH_END_WRAPPED);
    else if (rSL == SearchLabel::StartWrapped)
        sStr = SvxResId(RID_SVXSTR_SEARCH_START_WRAPPED);
    else if (rSL == SearchLabel::EndSheet)
        sStr = SvxResId(RID_SVXSTR_SEARCH_END_SHEET);
    else if (rSL == SearchLabel::NotFound)
        sStr = SvxResId(RID_SVXSTR_SEARCH_NOT_FOUND);
    else if (rSL == SearchLabel::NavElementNotFound)
        sStr = SvxResId(RID_SVXSTR_SEARCH_NAV_ELEMENT_NOT_FOUND);

    lcl_SetSearchLabelWindow(sStr);
    if (SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( SfxViewFrame::Current()->
            GetChildWindow( SvxSearchDialogWrapper::GetChildWindowId() )))
        pWrp->getDialog()->SetSearchLabel(sStr);
}

void SvxSearchDialogWrapper::SetSearchLabel(const OUString& sStr)
{

    lcl_SetSearchLabelWindow(sStr);
    if (SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( SfxViewFrame::Current()->
            GetChildWindow( SvxSearchDialogWrapper::GetChildWindowId() )))
        pWrp->getDialog()->SetSearchLabel(sStr);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
