tdf#95405 Sidebar: Quick Find for writer

Implemented the quick find deck on the sidebar of Writer with a corresponding panel in it.

Change-Id: Ic4ce6823e11b27b3386e820a657bc5d973e47007
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160500
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
diff --git a/officecfg/registry/data/org/openoffice/Office/UI/Sidebar.xcu b/officecfg/registry/data/org/openoffice/Office/UI/Sidebar.xcu
index e038472..291f4dd 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/Sidebar.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/Sidebar.xcu
@@ -335,7 +335,28 @@
        </prop>
      </node>

      <node oor:name="FindDeck" oor:op="replace">
        <prop oor:name="Title" oor:type="xs:string">
          <value xml:lang="en-US">Find</value>
        </prop>
        <prop oor:name="Id" oor:type="xs:string">
          <value>FindDeck</value>
        </prop>
        <prop oor:name="IconURL" oor:type="xs:string">
          <value>private:graphicrepository/cmd/lc_recsearch.png</value>
        </prop>
        <prop oor:name="ContextList">
          <value oor:separator=";">
             WriterVariants, any, visible ;
          </value>
        </prop>
        <prop oor:name="OrderIndex" oor:type="xs:int">
          <value>1000</value>
        </prop>
      </node>

    </node>

    <node oor:name="PanelList">

      <node oor:name="StylesPropertyPanel" oor:op="replace">
@@ -2020,6 +2041,37 @@
        </prop>
      </node>

      <node oor:name="QuickFindPanel" oor:op="replace">
        <prop oor:name="Title" oor:type="xs:string">
          <value xml:lang="en-US">QuickFind</value>
        </prop>
        <prop oor:name="TitleBarIsOptional" oor:type="xs:boolean">
          <value>true</value>
        </prop>
        <prop oor:name="Id" oor:type="xs:string">
          <value>QuickFindPanel</value>
        </prop>
        <prop oor:name="DeckId" oor:type="xs:string">
          <value>FindDeck</value>
        </prop>
        <prop oor:name="ContextList">
          <value oor:separator=";">
            Writer, any, visible;
          </value>
        </prop>
        <prop oor:name="ImplementationURL" oor:type="xs:string">
          <value>private:resource/toolpanel/SwPanelFactory/QuickFindPanel</value>
        </prop>
        <prop oor:name="OrderIndex" oor:type="xs:int">
          <value>300</value>
        </prop>
        <prop oor:name="WantsAWT" oor:type="xs:boolean">
          <value>false</value>
        </prop>
      </node>



    </node>
  </node>
</oor:component-data>
diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index c8cc2e9..872ae88 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -736,6 +736,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
    sw/source/uibase/sidebar/SwPanelFactory \
    sw/source/uibase/sidebar/WriterInspectorTextPanel \
    sw/source/uibase/sidebar/A11yCheckIssuesPanel \
    sw/source/uibase/sidebar/QuickFindPanel \
    sw/source/uibase/table/chartins \
    sw/source/uibase/table/swtablerep \
    sw/source/uibase/table/tablemgr \
diff --git a/sw/UIConfig_swriter.mk b/sw/UIConfig_swriter.mk
index 309eb86..27b7248 100644
--- a/sw/UIConfig_swriter.mk
+++ b/sw/UIConfig_swriter.mk
@@ -291,6 +291,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/swriter,\
	sw/uiconfig/swriter/ui/sidebarstylepresets \
	sw/uiconfig/swriter/ui/sidebartableedit \
	sw/uiconfig/swriter/ui/sidebartheme \
	sw/uiconfig/swriter/ui/sidebarquickfind \
	sw/uiconfig/swriter/ui/sortdialog \
	sw/uiconfig/swriter/ui/spellmenu \
	sw/uiconfig/swriter/ui/splittable \
diff --git a/sw/source/uibase/sidebar/QuickFindPanel.cxx b/sw/source/uibase/sidebar/QuickFindPanel.cxx
new file mode 100644
index 0000000..90f428b
--- /dev/null
+++ b/sw/source/uibase/sidebar/QuickFindPanel.cxx
@@ -0,0 +1,190 @@
/* -*- 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/.
 *
 */

#include "QuickFindPanel.hxx"
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <svl/srchitem.hxx>
#include <view.hxx>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/propertysequence.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <swmodule.hxx>
#include <pam.hxx>
#include <contentindex.hxx>
#include <node.hxx>
#include <ndtxt.hxx>
#include <edtwin.hxx>
#include <com/sun/star/uno/Any.h>
using namespace css;
using namespace std;

const int MinimumPanelWidth = 250;

namespace sw::sidebar
{
std::unique_ptr<PanelLayout> QuickFindPanel::Create(weld::Widget* pParent)
{
    if (pParent == nullptr)
        throw css::lang::IllegalArgumentException(
            "no parent Window given to QuickFindPanel::Create", nullptr, 0);
    return std::make_unique<QuickFindPanel>(pParent);
}

QuickFindPanel::QuickFindPanel(weld::Widget* pParent)
    : PanelLayout(pParent, "QuickFindPanel", "modules/swriter/ui/sidebarquickfind.ui")
    , m_xSearchFindEntry(m_xBuilder->weld_entry("Find"))
    , m_xSearchFindsList(m_xBuilder->weld_tree_view("searchfinds"))
    , m_nRowHeight(m_xSearchFindsList->get_height_rows(4))
    , m_pWrtShell(::GetActiveWrtShell())

{
    m_xContainer->set_size_request(MinimumPanelWidth, -1);
    m_xSearchFindsList->set_size_request(1, m_nRowHeight);
    m_xSearchFindEntry->connect_activate(
        LINK(this, QuickFindPanel, SearchFindEntryActivateHandler));
    m_xSearchFindEntry->connect_changed(LINK(this, QuickFindPanel, SearchFindEntryChangedHandler));
    m_xSearchFindsList->connect_custom_get_size(
        LINK(this, QuickFindPanel, SearchFindsListCustomGetSizeHandler));
    m_xSearchFindsList->connect_custom_render(LINK(this, QuickFindPanel, SearchFindsListRender));
    m_xSearchFindsList->set_column_custom_renderer(1, true);
    m_xSearchFindsList->connect_changed(
        LINK(this, QuickFindPanel, SearchFindsListSelectionChangedHandler));
    m_xSearchFindsList->connect_row_activated(
        LINK(this, QuickFindPanel, SearchFindsListRowActivatedHandler));
}

QuickFindPanel::~QuickFindPanel()
{
    m_xSearchFindEntry.reset();
    m_xSearchFindsList.reset();
}

IMPL_LINK_NOARG(QuickFindPanel, SearchFindEntryActivateHandler, weld::Entry&, bool)
{
    FillSearchFindsList();
    return true;
}

IMPL_LINK_NOARG(QuickFindPanel, SearchFindsListCustomGetSizeHandler, weld::TreeView::get_size_args,
                Size)
{
    return Size(1, m_nRowHeight);
}

IMPL_LINK(QuickFindPanel, SearchFindsListRender, weld::TreeView::render_args, aPayload, void)
{
    vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
    const ::tools::Rectangle& rRect = std::get<1>(aPayload);
    const OUString& rId = std::get<3>(aPayload);
    int nIndex = m_xSearchFindsList->find_id(rId);
    OUString aEntry(m_xSearchFindsList->get_text(nIndex));
    DrawTextFlags const nTextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter
                                     | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
    tools::Rectangle aRect(
        rRect.TopLeft(),
        Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight()));
    rRenderContext.DrawText(aRect, aEntry, nTextStyle);
}

IMPL_LINK_NOARG(QuickFindPanel, SearchFindsListSelectionChangedHandler, weld::TreeView&, void)
{
    std::unique_ptr<SwPaM>& rxPaM = m_vPaMs[m_xSearchFindsList->get_cursor_index()];

    m_pWrtShell->StartAction();
    bool bFound = false;
    for (SwPaM& rPaM : m_pWrtShell->GetCursor()->GetRingContainer())
    {
        if (*rxPaM->GetPoint() == *rPaM.GetPoint() && *rxPaM->GetMark() == *rPaM.GetMark())
        {
            bFound = true;
            break;
        }
        m_pWrtShell->GoNextCursor();
    }
    if (!bFound)
    {
        m_pWrtShell->AssureStdMode();
        m_pWrtShell->SetSelection(*rxPaM);
    }
    m_pWrtShell->EndAction();

    SwShellCursor* pShellCursor = m_pWrtShell->GetCursor_();
    std::vector<basegfx::B2DRange> vRanges;
    for (const SwRect& rRect : *pShellCursor)
    {
        tools::Rectangle aRect = rRect.SVRect();
        vRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom());
    }
    m_pWrtShell->GetView().BringToAttention(std::move(vRanges));
}

IMPL_LINK_NOARG(QuickFindPanel, SearchFindsListRowActivatedHandler, weld::TreeView&, bool)
{
    m_pWrtShell->GetView().GetEditWin().GrabFocus();
    return true;
}

IMPL_LINK_NOARG(QuickFindPanel, SearchFindEntryChangedHandler, weld::Entry&, void)
{
    m_xSearchFindsList->clear();
}

void QuickFindPanel::FillSearchFindsList()
{
    m_vPaMs.clear();
    m_xSearchFindsList->clear();
    const OUString& sText = m_xSearchFindEntry->get_text();
    css::uno::Sequence<css::beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
        { "SearchItem.SearchString", css::uno::Any(sText) },
        { "SearchItem.Backward", css::uno::Any(false) },
        { "SearchItem.Command", css::uno::Any(sal_uInt16(SvxSearchCmd::FIND_ALL)) },
    }));

    comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);

    if (m_pWrtShell->HasMark())
    {
        for (SwPaM& rPaM : m_pWrtShell->GetCursor()->GetRingContainer())
        {
            SwPosition* pMarkPosition = rPaM.GetMark();
            const SwContentIndex aContentIndex = pMarkPosition->nContent;
            const SwContentNode* pContentNode = aContentIndex.GetContentNode();
            const SwTextNode* pTextNode = pContentNode->GetTextNode();
            const OUString& sNodeText = pTextNode->GetText();
            auto nMarkIndex = rPaM.GetMark()->nContent.GetIndex();
            auto nPointIndex = rPaM.GetPoint()->nContent.GetIndex();

            auto nStartIndex = nMarkIndex - 50;
            if (nStartIndex < 0)
                nStartIndex = 0;
            auto nEndIndex = nPointIndex + 50;
            if (nEndIndex > sNodeText.getLength())
            {
                nEndIndex = sNodeText.getLength();
            }
            auto nCount = nMarkIndex - nStartIndex;
            OUString sTextBeforeFind = OUString::Concat(sNodeText.subView(nStartIndex, nCount));
            auto nCount1 = nPointIndex - nMarkIndex;
            OUString sFind = OUString::Concat(sNodeText.subView(nMarkIndex, nCount1));
            auto nCount2 = nEndIndex - nPointIndex;
            OUString sTextAfterFind = OUString::Concat(sNodeText.subView(nPointIndex, nCount2));
            OUString sStr = sTextBeforeFind + "[" + sFind + "]" + sTextAfterFind;

            std::unique_ptr<SwPaM> xPaM(std::make_unique<SwPaM>(*rPaM.GetMark(), *rPaM.GetPoint()));
            m_vPaMs.push_back(std::move(xPaM));
            OUString sId = OUString::number(m_xSearchFindsList->n_children());
            m_xSearchFindsList->append(sId, sStr);
        }
    }
}
}

// end of namespace ::sw::sidebar
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/sidebar/QuickFindPanel.hxx b/sw/source/uibase/sidebar/QuickFindPanel.hxx
new file mode 100644
index 0000000..af95bd1
--- /dev/null
+++ b/sw/source/uibase/sidebar/QuickFindPanel.hxx
@@ -0,0 +1,42 @@
/* -*- 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/.
 *
 */

#pragma once
#include <sfx2/sidebar/PanelLayout.hxx>
#include <wrtsh.hxx>

namespace sw::sidebar
{
class QuickFindPanel : public PanelLayout
{
public:
    static std::unique_ptr<PanelLayout> Create(weld::Widget* pParent);

    QuickFindPanel(weld::Widget* pParent);
    virtual ~QuickFindPanel() override;

private:
    std::unique_ptr<weld::Entry> m_xSearchFindEntry;
    std::unique_ptr<weld::TreeView> m_xSearchFindsList;
    std::vector<std::unique_ptr<SwPaM>> m_vPaMs;
    int m_nRowHeight;

    SwWrtShell* m_pWrtShell;

    DECL_LINK(SearchFindEntryActivateHandler, weld::Entry&, bool);
    DECL_LINK(SearchFindsListCustomGetSizeHandler, weld::TreeView::get_size_args, Size);
    DECL_LINK(SearchFindsListRender, weld::TreeView::render_args, void);
    DECL_LINK(SearchFindsListSelectionChangedHandler, weld::TreeView&, void);
    DECL_LINK(SearchFindEntryChangedHandler, weld::Entry&, void);
    DECL_LINK(SearchFindsListRowActivatedHandler, weld::TreeView&, bool);
    void FillSearchFindsList();
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/sidebar/SwPanelFactory.cxx b/sw/source/uibase/sidebar/SwPanelFactory.cxx
index 5ae3a78..75b7e2f 100644
--- a/sw/source/uibase/sidebar/SwPanelFactory.cxx
+++ b/sw/source/uibase/sidebar/SwPanelFactory.cxx
@@ -26,6 +26,7 @@
#include "PageFormatPanel.hxx"
#include "PageHeaderPanel.hxx"
#include "PageFooterPanel.hxx"
#include "QuickFindPanel.hxx"
#include "WrapPropertyPanel.hxx"
#include "WriterInspectorTextPanel.hxx"
#include "TableEditPanel.hxx"
@@ -203,6 +204,12 @@ Reference<ui::XUIElement> SAL_CALL SwPanelFactory::createUIElement (
        xElement = sfx2::sidebar::SidebarPanelBase::Create(
                        rsResourceURL, xFrame, std::move(xPanel), ui::LayoutSize(-1,-1,-1));
    }
    else if (rsResourceURL.endsWith("/QuickFindPanel"))
    {
        std::unique_ptr<PanelLayout> xPanel = sw::sidebar::QuickFindPanel::Create(pParent);
        xElement = sfx2::sidebar::SidebarPanelBase::Create(rsResourceURL, xFrame, std::move(xPanel),
                                                           ui::LayoutSize(-1, -1, -1));
    }

    return xElement;
}
diff --git a/sw/uiconfig/swriter/ui/sidebarquickfind.ui b/sw/uiconfig/swriter/ui/sidebarquickfind.ui
new file mode 100644
index 0000000..92e3e5b
--- /dev/null
+++ b/sw/uiconfig/swriter/ui/sidebarquickfind.ui
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface domain="sw">
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkTreeStore" id="liststore1">
    <columns>
      <!-- column-name text1 -->
      <column type="gchararray"/>
      <!-- column-name text2 -->
      <column type="gchararray"/>
      <!-- column-name id -->
      <column type="gchararray"/>
    </columns>
  </object>
  <!-- n-columns=1 n-rows=1 -->
  <object class="GtkGrid" id="QuickFindPanel">
    <property name="visible">True</property>
    <property name="can-focus">False</property>
    <property name="hexpand">True</property>
    <property name="vexpand">True</property>
    <property name="row-spacing">6</property>
    <property name="column-spacing">6</property>
    <property name="row-homogeneous">True</property>
    <property name="column-homogeneous">True</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can-focus">False</property>
        <property name="border-width">6</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child>
          <object class="GtkEntry" id="Find">
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="placeholder-text">Find</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow">
            <property name="visible">True</property>
            <property name="can-focus">True</property>
            <property name="hexpand">True</property>
            <property name="vexpand">True</property>
            <property name="shadow-type">in</property>
            <child>
              <object class="GtkTreeView" id="searchfinds">
                <property name="width-request">-1</property>
                <property name="visible">True</property>
                <property name="can-focus">True</property>
                <property name="hexpand">True</property>
                <property name="vexpand">True</property>
                <property name="border-width">0</property>
                <property name="model">liststore1</property>
                <property name="headers-visible">False</property>
                <property name="enable-search">False</property>
                <property name="search-column">1</property>
                <property name="show-expanders">False</property>
                <property name="activate-on-single-click">False</property>
                <child internal-child="selection">
                  <object class="GtkTreeSelection"/>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="treeviewcolumn0"/>
                </child>
                <child>
                  <object class="GtkTreeViewColumn" id="treeviewcolumn1">
                    <child>
                      <object class="GtkCellRendererText" id="cellrenderertext"/>
                      <attributes>
                        <attribute name="text">0</attribute>
                      </attributes>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
      <packing>
        <property name="left-attach">0</property>
        <property name="top-attach">0</property>
      </packing>
    </child>
  </object>
</interface>