weld FindTextFieldControl

Change-Id: Ib5861855d37c72d2be2b11b77173a141db90444e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88614
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index 9d62ffe..8621a74 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -675,6 +675,7 @@ public:
    virtual void select_entry_region(int nStartPos, int nEndPos) = 0;
    virtual bool get_entry_selection_bounds(int& rStartPos, int& rEndPos) = 0;
    virtual void set_entry_completion(bool bEnable, bool bCaseSensitive = false) = 0;
    virtual void set_entry_placeholder_text(const OUString& rText) = 0;

    virtual bool get_popup_shown() const = 0;

@@ -1336,6 +1337,7 @@ public:
    virtual void set_editable(bool bEditable) = 0;
    virtual bool get_editable() const = 0;
    virtual void set_message_type(EntryMessageType eType) = 0;
    virtual void set_placeholder_text(const OUString& rText) = 0;

    // font size is in points, not pixels, e.g. see Window::[G]etPointFont
    virtual void set_font(const vcl::Font& rFont) = 0;
diff --git a/svx/UIConfig_svx.mk b/svx/UIConfig_svx.mk
index 186fdc8..561bdac 100644
--- a/svx/UIConfig_svx.mk
+++ b/svx/UIConfig_svx.mk
@@ -49,6 +49,7 @@ $(eval $(call gb_UIConfig_add_uifiles,svx,\
	svx/uiconfig/ui/extrustiondepthdialog \
	svx/uiconfig/ui/fillctrlbox \
	svx/uiconfig/ui/filtermenu \
	svx/uiconfig/ui/findbox \
	svx/uiconfig/ui/findreplacedialog \
	svx/uiconfig/ui/floatingareastyle \
	svx/uiconfig/ui/floatingcontour \
diff --git a/svx/inc/pch/precompiled_svx.hxx b/svx/inc/pch/precompiled_svx.hxx
index cf586c0..01e7c0c 100644
--- a/svx/inc/pch/precompiled_svx.hxx
+++ b/svx/inc/pch/precompiled_svx.hxx
@@ -13,7 +13,7 @@
 manual changes will be rewritten by the next run of update_pch.sh (which presumably
 also fixes all possible problems, so it's usually better to use it).

 Generated on 2020-02-08 20:53:08 using:
 Generated on 2020-02-13 15:12:26 using:
 ./bin/update_pch svx svx --cutoff=3 --exclude:system --exclude:module --include:local

 If after updating build fails, use the following command to locate conflicting headers:
@@ -82,7 +82,6 @@
#include <vcl/customweld.hxx>
#include <vcl/dllapi.h>
#include <vcl/dockwin.hxx>
#include <vcl/edit.hxx>
#include <vcl/errcode.hxx>
#include <vcl/errinf.hxx>
#include <vcl/event.hxx>
@@ -270,6 +269,7 @@
#include <o3tl/underlyingenumvalue.hxx>
#include <officecfg/Office/Common.hxx>
#include <salhelper/simplereferenceobject.hxx>
#include <sfx2/InterimItemWindow.hxx>
#include <sfx2/app.hxx>
#include <sfx2/basedlgs.hxx>
#include <sfx2/bindings.hxx>
@@ -357,6 +357,7 @@
#include <unotools/viewoptions.hxx>
#endif // PCH_LEVEL >= 3
#if PCH_LEVEL >= 4
#include <DescriptionGenerator.hxx>
#include <cell.hxx>
#include <celltypes.hxx>
#include <charmapacc.hxx>
@@ -368,7 +369,6 @@
#include <svx/AccessibleShape.hxx>
#include <svx/AccessibleShapeInfo.hxx>
#include <svx/AccessibleTextHelper.hxx>
#include <DescriptionGenerator.hxx>
#include <svx/IAccessibleViewForwarder.hxx>
#include <svx/ShapeTypeHandler.hxx>
#include <svx/SvxShapeTypes.hxx>
diff --git a/svx/inc/pch/precompiled_svxcore.hxx b/svx/inc/pch/precompiled_svxcore.hxx
index b35efb4..78a9fee 100644
--- a/svx/inc/pch/precompiled_svxcore.hxx
+++ b/svx/inc/pch/precompiled_svxcore.hxx
@@ -13,7 +13,7 @@
 manual changes will be rewritten by the next run of update_pch.sh (which presumably
 also fixes all possible problems, so it's usually better to use it).

 Generated on 2020-02-11 09:36:54 using:
 Generated on 2020-02-13 15:10:47 using:
 ./bin/update_pch svx svxcore --cutoff=7 --exclude:system --include:module --exclude:local

 If after updating build fails, use the following command to locate conflicting headers:
@@ -62,7 +62,6 @@
#include <osl/getglobalmutex.hxx>
#include <osl/interlck.h>
#include <osl/mutex.hxx>
#include <osl/thread.h>
#include <osl/time.h>
#include <rtl/alloc.h>
#include <rtl/character.hxx>
@@ -448,10 +447,8 @@
#include <fmservs.hxx>
#include <fmshimp.hxx>
#include <fmundo.hxx>
#include <XPropertyTable.hxx>
#include <svx/dialmgr.hxx>
#include <svx/e3dsceneupdater.hxx>
#include <extrud3d.hxx>
#include <svx/fmglob.hxx>
#include <svx/fmmodel.hxx>
#include <svx/fmpage.hxx>
@@ -472,22 +469,17 @@
#include <svx/sdr/animation/scheduler.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <sdr/contact/objectcontactofobjlistpainter.hxx>
#include <svx/sdr/contact/objectcontactofpageview.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
#include <svx/sdr/contact/viewcontactofsdrobj.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdr/overlay/overlayobject.hxx>
#include <svx/sdr/overlay/overlaypolypolygon.hxx>
#include <sdr/primitive2d/sdrattributecreator.hxx>
#include <sdr/primitive2d/sdrdecompositiontools.hxx>
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
#include <svx/sdrhittesthelper.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <svx/sdtakitm.hxx>
#include <svx/sdtfchim.hxx>
#include <svx/sdynitm.hxx>
#include <svx/selectioncontroller.hxx>
@@ -527,7 +519,6 @@
#include <svx/svx3ditems.hxx>
#include <svx/svxdlg.hxx>
#include <svx/svxdllapi.h>
#include <textchain.hxx>
#include <svx/unoapi.hxx>
#include <svx/unofill.hxx>
#include <svx/unomid.hxx>
@@ -536,7 +527,6 @@
#include <svx/unoshprp.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xdef.hxx>
#include <svx/xenum.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflbmtit.hxx>
#include <svx/xflbstit.hxx>
diff --git a/svx/source/dialog/srchdlg.cxx b/svx/source/dialog/srchdlg.cxx
index 23d21ed..8f9cf9e 100644
--- a/svx/source/dialog/srchdlg.cxx
+++ b/svx/source/dialog/srchdlg.cxx
@@ -69,11 +69,12 @@
#include <svx/svxdlg.hxx>
#include <vcl/toolbox.hxx>
#include <o3tl/typed_flags_set.hxx>
#include <vcl/combobox.hxx>

#include <cstdlib>
#include <memory>

#include <findtextfield.hxx>

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

@@ -2410,13 +2411,12 @@ static void lcl_SetSearchLabelWindow(const OUString& rStr)

        if (pToolBox->GetItemCommand(id) == ".uno:FindText")
        {
            ComboBox* pFindText = dynamic_cast<ComboBox*>(pToolBox->GetItemWindow(id));
            FindTextFieldControl* pFindText = dynamic_cast<FindTextFieldControl*>(pToolBox->GetItemWindow(id));
            assert(pFindText);
            Edit* pEdit = pFindText->GetSubEdit();
            if (bNotFound)
                pEdit->SetControlForeground(COL_LIGHTRED);
                pFindText->set_entry_message_type(weld::EntryMessageType::Error);
            else
                pEdit->SetControlForeground();
                pFindText->set_entry_message_type(weld::EntryMessageType::Normal);
        }
    }
    xLayoutManager->doLayout();
diff --git a/svx/source/inc/findtextfield.hxx b/svx/source/inc/findtextfield.hxx
new file mode 100644
index 0000000..b929773
--- /dev/null
+++ b/svx/source/inc/findtextfield.hxx
@@ -0,0 +1,69 @@
/* -*- 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 .
 */

#pragma once

#include <sfx2/InterimItemWindow.hxx>

namespace svt
{
class AcceleratorExecute;
}

class FindTextFieldControl final : public InterimItemWindow
{
public:
    FindTextFieldControl(vcl::Window* pParent,
                         css::uno::Reference<css::frame::XFrame> const& xFrame,
                         const css::uno::Reference<css::uno::XComponentContext>& xContext);

    virtual void dispose() override;

    virtual ~FindTextFieldControl() override;

    virtual void GetFocus() override;

    void Remember_Impl(const OUString& rStr);
    void SetTextToSelected_Impl();

    void connect_changed(const Link<weld::ComboBox&, void>& rLink);

    int get_count() const;
    OUString get_text(int nIndex) const;
    OUString get_active_text() const;
    void append_text(const OUString& rText);
    void set_entry_message_type(weld::EntryMessageType eType);

private:
    ImplSVEvent* m_nAsyncGetFocusId;
    std::unique_ptr<weld::ComboBox> m_xWidget;
    css::uno::Reference<css::frame::XFrame> m_xFrame;
    css::uno::Reference<css::uno::XComponentContext> m_xContext;
    std::unique_ptr<svt::AcceleratorExecute> m_pAcc;
    Link<weld::ComboBox&, void> m_aChangeHdl;

    DECL_LINK(FocusInHdl, weld::Widget&, void);
    DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
    DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
    DECL_LINK(OnAsyncGetFocus, void*, void);

    void ActivateFind(bool bShift);
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbunosearchcontrollers.cxx b/svx/source/tbxctrls/tbunosearchcontrollers.cxx
index 1043551..3769273 100644
--- a/svx/source/tbxctrls/tbunosearchcontrollers.cxx
+++ b/svx/source/tbxctrls/tbunosearchcontrollers.cxx
@@ -47,6 +47,7 @@
#include <com/sun/star/util/SearchAlgorithms.hpp>
#include <com/sun/star/util/SearchAlgorithms2.hpp>

#include <sfx2/InterimItemWindow.hxx>
#include <svl/ctloptions.hxx>
#include <svl/srchitem.hxx>
#include <svtools/acceleratorexecute.hxx>
@@ -58,11 +59,12 @@
#include <rtl/instance.hxx>
#include <svx/srchdlg.hxx>
#include <vcl/button.hxx>
#include <vcl/combobox.hxx>
#include <vcl/event.hxx>
#include <vcl/fixed.hxx>
#include <vcl/window.hxx>

#include <findtextfield.hxx>

using namespace css;

namespace {
@@ -99,9 +101,9 @@ void impl_executeSearch( const css::uno::Reference< css::uno::XComponentContext 
            OUString sItemCommand = pToolBox->GetItemCommand(id);
            if ( sItemCommand == COMMAND_FINDTEXT )
            {
                vcl::Window* pItemWin = pToolBox->GetItemWindow(id);
                FindTextFieldControl* pItemWin = static_cast<FindTextFieldControl*>(pToolBox->GetItemWindow(id));
                if (pItemWin)
                    sFindText = pItemWin->GetText();
                    sFindText = pItemWin->get_active_text();
            } else if ( sItemCommand == COMMAND_MATCHCASE )
            {
                CheckBox* pItemWin = static_cast<CheckBox*>( pToolBox->GetItemWindow(id) );
@@ -144,52 +146,44 @@ void impl_executeSearch( const css::uno::Reference< css::uno::XComponentContext 
    }
}

class FindTextFieldControl : public ComboBox
{
public:
    FindTextFieldControl( vcl::Window* pParent,
        css::uno::Reference< css::frame::XFrame > const & xFrame,
        const css::uno::Reference< css::uno::XComponentContext >& xContext );

    virtual bool PreNotify( NotifyEvent& rNEvt ) override;

    void Remember_Impl(const OUString& rStr);
    void SetTextToSelected_Impl();

private:

    css::uno::Reference< css::frame::XFrame > m_xFrame;
    css::uno::Reference< css::uno::XComponentContext > m_xContext;
    std::unique_ptr<svt::AcceleratorExecute> m_pAcc;
};
}

FindTextFieldControl::FindTextFieldControl( vcl::Window* pParent,
    css::uno::Reference< css::frame::XFrame > const & xFrame,
    const css::uno::Reference< css::uno::XComponentContext >& xContext) :
    ComboBox(pParent, WB_DROPDOWN | WB_VSCROLL),
    InterimItemWindow(pParent, "svx/ui/findbox.ui", "FindBox"),
    m_nAsyncGetFocusId(nullptr),
    m_xWidget(m_xBuilder->weld_combo_box("find")),
    m_xFrame(xFrame),
    m_xContext(xContext),
    m_pAcc(svt::AcceleratorExecute::createAcceleratorHelper())
{
    SetPlaceholderText(SvxResId(RID_SVXSTR_FINDBAR_FIND));
    EnableAutocomplete(true, true);
    m_xWidget->set_entry_placeholder_text(SvxResId(RID_SVXSTR_FINDBAR_FIND));
    m_xWidget->set_entry_completion(true, true);
    m_pAcc->init(m_xContext, m_xFrame);

    m_xWidget->connect_focus_in(LINK(this, FindTextFieldControl, FocusInHdl));
    m_xWidget->connect_key_press(LINK(this, FindTextFieldControl, KeyInputHdl));
    m_xWidget->connect_entry_activate(LINK(this, FindTextFieldControl, ActivateHdl));

    m_xWidget->set_size_request(250, -1);
    SetSizePixel(m_xWidget->get_preferred_size());
}

void FindTextFieldControl::Remember_Impl(const OUString& rStr)
{
    const sal_Int32 nCount = GetEntryCount();
    const sal_Int32 nCount = m_xWidget->get_count();

    for (sal_Int32 i=0; i<nCount; ++i)
    {
        if ( rStr == GetEntry(i))
        if (rStr == m_xWidget->get_text(i))
            return;
    }

    if (nCount == REMEMBER_SIZE)
        RemoveEntryAt(REMEMBER_SIZE-1);
        m_xWidget->remove(REMEMBER_SIZE-1);

    InsertEntry(rStr, 0);
    m_xWidget->insert_text(0, rStr);
}

void FindTextFieldControl::SetTextToSelected_Impl()
@@ -214,93 +208,159 @@ void FindTextFieldControl::SetTextToSelected_Impl()
    if ( !aString.isEmpty() )
    {
        // If something is selected in the document, prepopulate with this
        SetText( aString );
        GetModifyHdl().Call(*this); // FIXME why SetText doesn't trigger this?
        m_xWidget->set_entry_text(aString);
        m_aChangeHdl.Call(*m_xWidget);
    }
    else if (GetEntryCount() > 0)
    else if (get_count() > 0)
    {
        // Else, prepopulate with last search word (fdo#84256)
        SetText(GetEntry(0));
        m_xWidget->set_entry_text(m_xWidget->get_text(0));
    }
}

bool FindTextFieldControl::PreNotify( NotifyEvent& rNEvt )
IMPL_LINK(FindTextFieldControl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
{
    if (isDisposed())
        return true;
    bool bRet= ComboBox::PreNotify( rNEvt );

    switch ( rNEvt.GetType() )
    bool bRet = false;

    bool bShift = rKeyEvent.GetKeyCode().IsShift();
    bool bMod1 = rKeyEvent.GetKeyCode().IsMod1();
    sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();

    // Close the search bar on Escape
    if ( KEY_ESCAPE == nCode )
    {
        case MouseNotifyEvent::KEYINPUT:
        bRet = true;
        GrabFocusToDocument();

        // hide the findbar
        css::uno::Reference< css::beans::XPropertySet > xPropSet(m_xFrame, css::uno::UNO_QUERY);
        if (xPropSet.is())
        {
            // Clear SearchLabel when altering the search string
            #if HAVE_FEATURE_DESKTOP
            SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
            #endif

            const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
            bool bShift = pKeyEvent->GetKeyCode().IsShift();
            bool bMod1 = pKeyEvent->GetKeyCode().IsMod1();
            sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();

            // Close the search bar on Escape
            if ( KEY_ESCAPE == nCode )
            css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
            css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
            aValue >>= xLayoutManager;
            if (xLayoutManager.is())
            {
                bRet = true;
                GrabFocusToDocument();

                // hide the findbar
                css::uno::Reference< css::beans::XPropertySet > xPropSet(m_xFrame, css::uno::UNO_QUERY);
                if (xPropSet.is())
                {
                    css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
                    css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
                    aValue >>= xLayoutManager;
                    if (xLayoutManager.is())
                    {
                        const OUString sResourceURL( "private:resource/toolbar/findbar" );
                        xLayoutManager->hideElement( sResourceURL );
                        xLayoutManager->destroyElement( sResourceURL );
                    }
                }
                const OUString sResourceURL( "private:resource/toolbar/findbar" );
                xLayoutManager->hideElement( sResourceURL );
                xLayoutManager->destroyElement( sResourceURL );
            }
            // Select text in the search box when Ctrl-F pressed
            else if ( bMod1 && nCode == KEY_F )
                SetSelection( Selection( SELECTION_MIN, SELECTION_MAX ) );

            // Execute the search when Return, Ctrl-G or F3 pressed
            else if ( KEY_RETURN == nCode || (bMod1 && (KEY_G == nCode)) || (KEY_F3 == nCode) )
            {
                Remember_Impl(GetText());

                vcl::Window* pWindow = GetParent();
                ToolBox* pToolBox = static_cast<ToolBox*>(pWindow);

                impl_executeSearch( m_xContext, m_xFrame, pToolBox, bShift);
                bRet = true;
            }
            else
            {
                auto awtKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(pKeyEvent->GetKeyCode());
                const OUString aCommand(m_pAcc->findCommand(awtKey));
                if (aCommand == ".uno:SearchDialog")
                    bRet = m_pAcc->execute(awtKey);
            }
            break;
        }
    }
    // Select text in the search box when Ctrl-F pressed
    else if ( bMod1 && nCode == KEY_F )
        m_xWidget->select_entry_region(0, -1);

        case MouseNotifyEvent::GETFOCUS:
            SetSelection( Selection( SELECTION_MIN, SELECTION_MAX ) );
            break;

        default:
            break;
    // Execute the search when Return, Ctrl-G or F3 pressed
    else if ( KEY_RETURN == nCode || (bMod1 && (KEY_G == nCode)) || (KEY_F3 == nCode) )
    {
        ActivateFind(bShift);
        bRet = true;
    }
    else
    {
        auto awtKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyEvent.GetKeyCode());
        const OUString aCommand(m_pAcc->findCommand(awtKey));
        if (aCommand == ".uno:SearchDialog")
            bRet = m_pAcc->execute(awtKey);
    }

    return bRet;
    return bRet || ChildKeyInput(rKeyEvent);
}

void FindTextFieldControl::ActivateFind(bool bShift)
{
    Remember_Impl(m_xWidget->get_active_text());

    vcl::Window* pWindow = GetParent();
    ToolBox* pToolBox = static_cast<ToolBox*>(pWindow);

    impl_executeSearch(m_xContext, m_xFrame, pToolBox, bShift);
}

IMPL_LINK_NOARG(FindTextFieldControl, ActivateHdl, weld::ComboBox&, bool)
{
    if (isDisposed())
        return true;

    ActivateFind(false);

    return true;
}

IMPL_LINK_NOARG(FindTextFieldControl, OnAsyncGetFocus, void*, void)
{
    m_nAsyncGetFocusId = nullptr;
    m_xWidget->select_entry_region(0, -1);
}

IMPL_LINK_NOARG(FindTextFieldControl, FocusInHdl, weld::Widget&, void)
{
    if (m_nAsyncGetFocusId)
        return;
    // do it async to defeat entry in combobox having its own ideas about the focus
    m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, FindTextFieldControl, OnAsyncGetFocus));
}

void FindTextFieldControl::dispose()
{
    if (m_nAsyncGetFocusId)
    {
        Application::RemoveUserEvent(m_nAsyncGetFocusId);
        m_nAsyncGetFocusId = nullptr;
    }
    m_xWidget.reset();
    InterimItemWindow::dispose();
}

FindTextFieldControl::~FindTextFieldControl()
{
    disposeOnce();
}

void FindTextFieldControl::connect_changed(const Link<weld::ComboBox&, void>& rLink)
{
    m_aChangeHdl = rLink;
    m_xWidget->connect_changed(rLink);
}

int FindTextFieldControl::get_count() const
{
    return m_xWidget->get_count();
}

OUString FindTextFieldControl::get_text(int nIndex) const
{
    return m_xWidget->get_text(nIndex);
}

OUString FindTextFieldControl::get_active_text() const
{
    return m_xWidget->get_active_text();
}

void FindTextFieldControl::append_text(const OUString& rText)
{
    m_xWidget->append_text(rText);
}

void FindTextFieldControl::set_entry_message_type(weld::EntryMessageType eType)
{
    m_xWidget->set_entry_message_type(eType);
}

void FindTextFieldControl::GetFocus()
{
    if (!m_xWidget)
        return;
    m_xWidget->grab_focus();
}

namespace {

class SearchToolbarControllersManager
{
public:
@@ -342,11 +402,11 @@ SearchToolbarControllersManager& SearchToolbarControllersManager::createControll

void SearchToolbarControllersManager::saveSearchHistory(const FindTextFieldControl* pFindTextFieldControl)
{
    const sal_Int32 nECount( pFindTextFieldControl->GetEntryCount() );
    const sal_Int32 nECount( pFindTextFieldControl->get_count() );
    m_aSearchStrings.resize( nECount );
    for( sal_Int32 i=0; i<nECount; ++i )
    {
        m_aSearchStrings[i] = pFindTextFieldControl->GetEntry(i);
        m_aSearchStrings[i] = pFindTextFieldControl->get_text(i);
    }
}

@@ -354,7 +414,7 @@ void SearchToolbarControllersManager::loadSearchHistory(FindTextFieldControl* pF
{
    for( size_t i=0; i<m_aSearchStrings.size(); ++i )
    {
        pFindTextFieldControl->InsertEntry(m_aSearchStrings[i],i);
        pFindTextFieldControl->append_text(m_aSearchStrings[i]);
    }
}

@@ -443,7 +503,7 @@ public:
    // XStatusListener
    virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;

    DECL_LINK(EditModifyHdl, Edit&, void);
    DECL_LINK(EditModifyHdl, weld::ComboBox&, void);

private:

@@ -544,9 +604,7 @@ css::uno::Reference< css::awt::XWindow > SAL_CALL FindTextToolbarController::cre
        ToolBox* pToolbar = static_cast<ToolBox*>(pParent.get());
        m_pFindTextFieldControl = VclPtr<FindTextFieldControl>::Create(pToolbar, m_xFrame, m_xContext);

        Size aSize(250, m_pFindTextFieldControl->GetTextHeight() + 200);
        m_pFindTextFieldControl->SetSizePixel( aSize );
        m_pFindTextFieldControl->SetModifyHdl(LINK(this, FindTextToolbarController, EditModifyHdl));
        m_pFindTextFieldControl->connect_changed(LINK(this, FindTextToolbarController, EditModifyHdl));
        SearchToolbarControllersManager::createControllersManager().loadSearchHistory(m_pFindTextFieldControl);
    }
    xItemWindow = VCLUnoHelper::GetInterface( m_pFindTextFieldControl );
@@ -564,14 +622,19 @@ void SAL_CALL FindTextToolbarController::statusChanged( const css::frame::Featur
    OUString aFeatureURL = rEvent.FeatureURL.Complete;
    if ( aFeatureURL == "AppendSearchHistory" )
    {
        m_pFindTextFieldControl->Remember_Impl(m_pFindTextFieldControl->GetText());
        m_pFindTextFieldControl->Remember_Impl(m_pFindTextFieldControl->get_active_text());
    }
    // enable up/down buttons in case there is already text (from the search history)
    textfieldChanged();
}

IMPL_LINK_NOARG(FindTextToolbarController, EditModifyHdl, Edit&, void)
IMPL_LINK_NOARG(FindTextToolbarController, EditModifyHdl, weld::ComboBox&, void)
{
    // Clear SearchLabel when search string altered
    #if HAVE_FEATURE_DESKTOP
    SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
    #endif

    textfieldChanged();
}

@@ -581,7 +644,7 @@ void FindTextToolbarController::textfieldChanged() {
    ToolBox* pToolBox = static_cast<ToolBox*>(pWindow.get());
    if ( pToolBox && m_pFindTextFieldControl )
    {
        bool enableButtons = !m_pFindTextFieldControl->GetText().isEmpty();
        bool enableButtons = !m_pFindTextFieldControl->get_active_text().isEmpty();
        pToolBox->EnableItem(m_nDownSearchId, enableButtons);
        pToolBox->EnableItem(m_nUpSearchId, enableButtons);
        pToolBox->EnableItem(m_nFindAllId, enableButtons);
diff --git a/svx/uiconfig/ui/findbox.ui b/svx/uiconfig/ui/findbox.ui
new file mode 100644
index 0000000..772f192
--- /dev/null
+++ b/svx/uiconfig/ui/findbox.ui
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface domain="sc">
  <requires lib="gtk+" version="3.18"/>
  <object class="GtkBox" id="FindBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="hexpand">True</property>
    <property name="spacing">6</property>
    <child>
      <object class="GtkComboBoxText" id="find">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="hexpand">True</property>
        <property name="has_entry">True</property>
        <child internal-child="entry">
          <object class="GtkEntry">
            <property name="can_focus">True</property>
          </object>
        </child>
      </object>
      <packing>
        <property name="expand">False</property>
        <property name="fill">True</property>
        <property name="position">0</property>
      </packing>
    </child>
  </object>
</interface>
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 5b1c40c..4eca514 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -3507,6 +3507,11 @@ public:
        weld::Entry::connect_cursor_position(rLink);
    }

    virtual void set_placeholder_text(const OUString& rText) override
    {
        m_xEntry->SetPlaceholderText(rText);
    }

    Edit& getEntry()
    {
        return *m_xEntry;
@@ -6217,6 +6222,11 @@ public:
        assert(false);
    }

    virtual void set_entry_placeholder_text(const OUString&) override
    {
        assert(false);
    }

    virtual ~SalInstanceComboBoxWithoutEdit() override
    {
        m_xComboBox->SetSelectHdl(Link<ListBox&, void>());
@@ -6323,6 +6333,11 @@ public:
        m_xComboBox->EnableAutocomplete(bEnable, bCaseSensitive);
    }

    virtual void set_entry_placeholder_text(const OUString& rText) override
    {
        m_xComboBox->SetPlaceholderText(rText);
    }

    virtual void select_entry_region(int nStartPos, int nEndPos) override
    {
        m_xComboBox->SetSelection(Selection(nStartPos, nEndPos < 0 ? SELECTION_MAX : nEndPos));
@@ -6405,6 +6420,12 @@ public:
        rEntry.SetAutocompleteHdl(Link<Edit&, void>());
    }

    virtual void set_entry_placeholder_text(const OUString& rText) override
    {
        Edit& rEntry = m_pEntry->getEntry();
        rEntry.SetPlaceholderText(rText);
    }

    virtual void grab_focus() override { m_xEntry->grab_focus(); }

    virtual void connect_focus_in(const Link<Widget&, void>& rLink) override
diff --git a/vcl/source/control/combobox.cxx b/vcl/source/control/combobox.cxx
index 03b44f3..6bc591f 100644
--- a/vcl/source/control/combobox.cxx
+++ b/vcl/source/control/combobox.cxx
@@ -1549,6 +1549,8 @@ bool ComboBox::set_property(const OString &rKey, const OUString &rValue)
            nBits |= WB_TABSTOP;
        SetStyle(nBits);
    }
    else if (rKey == "placeholder-text")
        SetPlaceholderText(rValue);
    else
        return Control::set_property(rKey, rValue);
    return true;
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 8ed4f0c..f6414f2 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -8311,6 +8311,11 @@ public:
        gtk_editable_paste_clipboard(GTK_EDITABLE(m_pEntry));
    }

    virtual void set_placeholder_text(const OUString& rText) override
    {
        gtk_entry_set_placeholder_text(m_pEntry, rText.toUtf8().getStr());
    }

    virtual ~GtkInstanceEntry() override
    {
        g_signal_handler_disconnect(m_pEntry, m_nActivateSignalId);
@@ -12563,6 +12568,14 @@ public:
        m_bAutoCompleteCaseSensitive = bCaseSensitive;
    }

    virtual void set_entry_placeholder_text(const OUString& rText) override
    {
        GtkWidget* pChild = gtk_bin_get_child(GTK_BIN(m_pComboBox));
        assert(pChild && GTK_IS_ENTRY(pChild));
        GtkEntry* pEntry = GTK_ENTRY(pChild);
        gtk_entry_set_placeholder_text(pEntry, rText.toUtf8().getStr());
    }

    virtual void disable_notify_events() override
    {
        if (GtkEntry* pEntry = get_entry())
@@ -12853,6 +12866,11 @@ public:
        m_bAutoCompleteCaseSensitive = bCaseSensitive;
    }

    virtual void set_entry_placeholder_text(const OUString& rText) override
    {
        m_xEntry->set_placeholder_text(rText);
    }

    virtual void grab_focus() override { m_xEntry->grab_focus(); }

    virtual void connect_focus_in(const Link<Widget&, void>& rLink) override