/* -*- 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 <Navigator.hxx>
 
#include <strings.hxx>
#include <bitmaps.hlst>
#include <ReportController.hxx>
#include <UITools.hxx>
#include <reportformula.hxx>
#include <com/sun/star/report/XFixedText.hpp>
#include <com/sun/star/report/XFixedLine.hpp>
#include <com/sun/star/report/XFormattedField.hpp>
#include <com/sun/star/report/XImageControl.hpp>
#include <com/sun/star/report/XShape.hpp>
#include <helpids.h>
#include <strings.hrc>
#include <rptui_slotid.hrc>
#include <comphelper/propmultiplex.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/containermultiplexer.hxx>
#include <cppuhelper/basemutex.hxx>
#include <comphelper/SelectionMultiplex.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/commandevent.hxx>
#include <ReportVisitor.hxx>
#include <core_resource.hxx>
#include <rtl/ref.hxx>
#include <svx/svxids.hrc>
 
#include <memory>
#include <string_view>
 
namespace rptui
{
using namespace ::com::sun::star;
using namespace ::comphelper;
 
static OUString lcl_getImageId(const uno::Reference< report::XReportComponent>& _xElement)
{
    OUString sId;
    uno::Reference< report::XFixedLine> xFixedLine(_xElement,uno::UNO_QUERY);
    if ( uno::Reference< report::XFixedText>(_xElement,uno::UNO_QUERY).is() )
        sId = RID_SVXBMP_FM_FIXEDTEXT;
    else if ( xFixedLine.is() )
        sId = xFixedLine->getOrientation() ? RID_SVXBMP_INSERT_VFIXEDLINE : RID_SVXBMP_INSERT_HFIXEDLINE;
    else if ( uno::Reference< report::XFormattedField>(_xElement,uno::UNO_QUERY).is() )
        sId = RID_SVXBMP_FM_EDIT;
    else if ( uno::Reference< report::XImageControl>(_xElement,uno::UNO_QUERY).is() )
        sId = RID_SVXBMP_FM_IMAGECONTROL;
    else if ( uno::Reference< report::XShape>(_xElement,uno::UNO_QUERY).is() )
        sId = RID_SVXBMP_DRAWTBX_CS_BASIC;
    return sId;
}
 
static OUString lcl_getName(const uno::Reference< beans::XPropertySet>& _xElement)
{
    OSL_ENSURE(_xElement.is(),"Found report element which is NULL!");
    OUString sTempName;
    _xElement->getPropertyValue(PROPERTY_NAME) >>= sTempName;
    OUStringBuffer sName(sTempName);
    uno::Reference< report::XFixedText> xFixedText(_xElement,uno::UNO_QUERY);
    uno::Reference< report::XReportControlModel> xReportModel(_xElement,uno::UNO_QUERY);
    if ( xFixedText.is() )
    {
        sName.append(" : " + xFixedText->getLabel());
    }
    else if ( xReportModel.is() && _xElement->getPropertySetInfo()->hasPropertyByName(PROPERTY_DATAFIELD) )
    {
        ReportFormula aFormula( xReportModel->getDataField() );
        if ( aFormula.isValid() )
        {
            sName.append(" : " + aFormula.getUndecoratedContent() );
        }
    }
    return sName.makeStringAndClear();
}
 
class NavigatorTree : public ::cppu::BaseMutex
                    , public reportdesign::ITraverseReport
                    , public comphelper::OSelectionChangeListener
                    , public ::comphelper::OPropertyChangeListener
{
    class UserData;
    friend class UserData;
    class UserData : public ::cppu::BaseMutex
                    ,public ::comphelper::OPropertyChangeListener
                    ,public ::comphelper::OContainerListener
    {
        uno::Reference< uno::XInterface >                           m_xContent;
        ::rtl::Reference< comphelper::OPropertyChangeMultiplexer>   m_pListener;
        ::rtl::Reference< comphelper::OContainerListenerAdapter>    m_pContainerListener;
        NavigatorTree*                                              m_pTree;
    public:
        UserData(NavigatorTree* pTree, uno::Reference<uno::XInterface> xContent);
        virtual ~UserData() override;
 
        const uno::Reference< uno::XInterface >& getContent() const { return m_xContent; }
        void setContent(const uno::Reference< uno::XInterface >& _xContent) { m_xContent = _xContent; }
 
    protected:
        // OPropertyChangeListener
        virtual void _propertyChanged(const beans::PropertyChangeEvent& _rEvent) override;
 
        // OContainerListener
        virtual void _elementInserted( const container::ContainerEvent& _rEvent ) override;
        virtual void _elementRemoved( const container::ContainerEvent& Event ) override;
        virtual void _elementReplaced( const container::ContainerEvent& _rEvent ) override;
        virtual void _disposing(const lang::EventObject& _rSource) override;
    };
 
    std::unique_ptr<weld::TreeView>                                             m_xTreeView;
    OReportController&                                                          m_rController;
    std::unique_ptr<weld::TreeIter>                                             m_xMasterReport;
    ::rtl::Reference< comphelper::OPropertyChangeMultiplexer>                   m_pReportListener;
    ::rtl::Reference< comphelper::OSelectionChangeMultiplexer>                  m_pSelectionListener;
 
    void insertEntry(const OUString& rName, const weld::TreeIter* pParent, const OUString& rImageId, int nPosition, const UserData* pData, weld::TreeIter& rRet);
 
    void traverseSection(const uno::Reference<report::XSection>& xSection, const weld::TreeIter* pParent, const OUString& rImageId, int nPosition = -1);
    void traverseFunctions(const uno::Reference< report::XFunctions>& xFunctions, const weld::TreeIter* pParent);
 
protected:
    // OSelectionChangeListener
    virtual void _disposing(const lang::EventObject& _rSource) override;
 
    // OPropertyChangeListener
    virtual void _propertyChanged(const beans::PropertyChangeEvent& _rEvent) override;
 
    // OContainerListener Helper
    void _elementInserted( const container::ContainerEvent& _rEvent );
    void _elementRemoved( const container::ContainerEvent& Event );
    void _elementReplaced( const container::ContainerEvent& _rEvent );
 
public:
    NavigatorTree(std::unique_ptr<weld::TreeView>, OReportController& rController);
    virtual ~NavigatorTree() override;
 
    DECL_LINK(OnEntrySelDesel, weld::TreeView&, void);
    DECL_LINK(CommandHdl, const CommandEvent&, bool);
 
    virtual void _selectionChanged( const lang::EventObject& aEvent ) override;
 
    // ITraverseReport
    virtual void traverseReport(const uno::Reference< report::XReportDefinition>& xReport) override;
    virtual void traverseReportFunctions(const uno::Reference< report::XFunctions>& xFunctions) override;
    virtual void traverseReportHeader(const uno::Reference< report::XSection>& xSection) override;
    virtual void traverseReportFooter(const uno::Reference< report::XSection>& xSection) override;
    virtual void traversePageHeader(const uno::Reference< report::XSection>& xSection) override;
    virtual void traversePageFooter(const uno::Reference< report::XSection>& xSection) override;
 
    virtual void traverseGroups(const uno::Reference< report::XGroups>& xGroups) override;
    virtual void traverseGroup(const uno::Reference< report::XGroup>& xGroup) override;
    virtual void traverseGroupFunctions(const uno::Reference< report::XFunctions>& xFunctions) override;
    virtual void traverseGroupHeader(const uno::Reference< report::XSection>& xSection) override;
    virtual void traverseGroupFooter(const uno::Reference< report::XSection>& xSection) override;
 
    virtual void traverseDetail(const uno::Reference< report::XSection>& xSection) override;
 
    bool find(const uno::Reference<uno::XInterface>& xContent, weld::TreeIter& rIter);
    void removeEntry(const weld::TreeIter& rEntry, bool bRemove = true);
 
    void grab_focus() { m_xTreeView->grab_focus(); }
 
    void set_text(const weld::TreeIter& rIter, const OUString& rStr)
    {
        m_xTreeView->set_text(rIter, rStr);
    }
 
    void expand_row(const weld::TreeIter& rIter)
    {
        m_xTreeView->expand_row(rIter);
    }
 
    std::unique_ptr<weld::TreeIter> make_iterator() const
    {
        return m_xTreeView->make_iterator();
    }
 
    int iter_n_children(const weld::TreeIter& rIter) const
    {
        return m_xTreeView->iter_n_children(rIter);
    }
};
 
NavigatorTree::NavigatorTree(std::unique_ptr<weld::TreeView> xTreeView, OReportController& rController)
    : OPropertyChangeListener()
    , m_xTreeView(std::move(xTreeView))
    , m_rController(rController)
{
    m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 25, m_xTreeView->get_height_rows(18));
 
    m_pReportListener = new OPropertyChangeMultiplexer(this,m_rController.getReportDefinition());
    m_pReportListener->addProperty(PROPERTY_PAGEHEADERON);
    m_pReportListener->addProperty(PROPERTY_PAGEFOOTERON);
    m_pReportListener->addProperty(PROPERTY_REPORTHEADERON);
    m_pReportListener->addProperty(PROPERTY_REPORTFOOTERON);
 
    m_pSelectionListener = new OSelectionChangeMultiplexer(this,&m_rController);
 
    m_xTreeView->set_help_id(HID_REPORT_NAVIGATOR_TREE);
 
    m_xTreeView->set_selection_mode(SelectionMode::Multiple);
 
    m_xTreeView->connect_changed(LINK(this, NavigatorTree, OnEntrySelDesel));
    m_xTreeView->connect_popup_menu(LINK(this, NavigatorTree, CommandHdl));
}
 
NavigatorTree::~NavigatorTree()
{
    m_xTreeView->all_foreach([this](weld::TreeIter& rIter) {
        UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_id(rIter));
        delete pData;
        return false;
    });
    m_pSelectionListener->dispose();
    m_pReportListener->dispose();
}
 
namespace
{
    sal_uInt16 mapIdent(std::u16string_view rIdent)
    {
        if (rIdent == u"sorting")
            return SID_SORTINGANDGROUPING;
        else if (rIdent == u"page")
            return SID_PAGEHEADERFOOTER;
        else if (rIdent == u"report")
            return SID_REPORTHEADERFOOTER;
        else if (rIdent == u"function")
            return SID_RPT_NEW_FUNCTION;
        else if (rIdent == u"properties")
            return SID_SHOW_PROPERTYBROWSER;
        else if (rIdent == u"delete")
            return SID_DELETE;
        return 0;
    }
}
 
IMPL_LINK(NavigatorTree, CommandHdl, const CommandEvent&, rEvt, bool)
{
    bool bHandled = false;
    switch( rEvt.GetCommand())
    {
    case CommandEventId::ContextMenu:
        {
            UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_selected_id());
            if (!pData)
                break;
 
            uno::Reference< report::XFunctionsSupplier> xSupplier(pData->getContent(),uno::UNO_QUERY);
            uno::Reference< report::XFunctions> xFunctions(pData->getContent(),uno::UNO_QUERY);
            uno::Reference< report::XGroup> xGroup(pData->getContent(),uno::UNO_QUERY);
            bool bDeleteAllowed = m_rController.isEditable() && (xGroup.is() ||
                                      uno::Reference< report::XFunction>(pData->getContent(),uno::UNO_QUERY).is());
 
            std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), u"modules/dbreport/ui/navigatormenu.ui"_ustr));
            std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
 
            const OUString aIds[] = { u"sorting"_ustr, u"page"_ustr, u"report"_ustr, u"function"_ustr, u"properties"_ustr, u"delete"_ustr };
            for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i)
            {
                sal_uInt16 nSId = mapIdent(aIds[i]);
 
                if (aIds[i] == "page" || aIds[i] == "report" || aIds[i] == "properties")
                    xContextMenu->set_active(aIds[i], m_rController.isCommandChecked(nSId));
                bool bEnabled = m_rController.isCommandEnabled(nSId);
                if (nSId == SID_RPT_NEW_FUNCTION)
                    xContextMenu->set_sensitive(aIds[i], m_rController.isEditable() && (xSupplier.is() || xFunctions.is()));
                // special condition, check for function and group
                else if (nSId == SID_DELETE)
                    xContextMenu->set_sensitive(aIds[i], bDeleteAllowed);
                else
                    xContextMenu->set_sensitive(aIds[i], bEnabled);
            }
 
            // the point that was clicked on
            ::Point aWhere(rEvt.GetMousePosPixel());
            OUString sCurItemIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(aWhere, Size(1,1)));
            if (!sCurItemIdent.isEmpty())
            {
                sal_uInt16 nId = mapIdent(sCurItemIdent);
                uno::Sequence< beans::PropertyValue> aArgs;
                if ( nId == SID_RPT_NEW_FUNCTION )
                {
                    aArgs.realloc(1);
                    aArgs.getArray()[0].Value <<= (xFunctions.is() ? xFunctions : xSupplier->getFunctions());
                }
                else if ( nId == SID_DELETE )
                {
                    if ( xGroup.is() )
                        nId = SID_GROUP_REMOVE;
                    aArgs = { comphelper::makePropertyValue(PROPERTY_GROUP, pData->getContent()) };
                }
                m_rController.executeUnChecked(nId,aArgs);
            }
 
            bHandled = true;
        }
        break;
        default: break;
    }
 
    return bHandled;
}
 
IMPL_LINK_NOARG(NavigatorTree, OnEntrySelDesel, weld::TreeView&, void)
{
    if ( !m_pSelectionListener->locked() )
    {
        m_pSelectionListener->lock();
        std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
        bool bEntry = m_xTreeView->get_cursor(xEntry.get());
        uno::Any aSelection;
        if (bEntry && m_xTreeView->is_selected(*xEntry))
            aSelection <<= weld::fromId<UserData*>(m_xTreeView->get_id(*xEntry))->getContent();
        m_rController.select(aSelection);
        m_pSelectionListener->unlock();
    }
}
 
void NavigatorTree::_selectionChanged( const lang::EventObject& aEvent )
{
    m_pSelectionListener->lock();
    uno::Reference< view::XSelectionSupplier> xSelectionSupplier(aEvent.Source,uno::UNO_QUERY);
    uno::Any aSec = xSelectionSupplier->getSelection();
    uno::Sequence< uno::Reference< report::XReportComponent > > aSelection;
    aSec >>= aSelection;
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
    if ( !aSelection.hasElements() )
    {
        uno::Reference< uno::XInterface> xSelection(aSec,uno::UNO_QUERY);
        bool bEntry = find(xSelection, *xEntry);
        if (bEntry && !m_xTreeView->is_selected(*xEntry))
        {
            m_xTreeView->select(*xEntry);
            m_xTreeView->set_cursor(*xEntry);
        }
        else if (!bEntry)
            m_xTreeView->unselect_all();
    }
    else
    {
        for (const uno::Reference<report::XReportComponent>& rElem : aSelection)
        {
            bool bEntry = find(rElem, *xEntry);
            if (bEntry && !m_xTreeView->is_selected(*xEntry))
            {
                m_xTreeView->select(*xEntry);
                m_xTreeView->set_cursor(*xEntry);
            }
        }
    }
    m_pSelectionListener->unlock();
}
 
void NavigatorTree::insertEntry(const OUString& rName, const weld::TreeIter* pParent, const OUString& rImageId,
                                int nPosition, const UserData* pData, weld::TreeIter& rRet)
{
    OUString sId = pData ? weld::toId(pData) : OUString();
    m_xTreeView->insert(pParent, nPosition, &rName, &sId, nullptr, nullptr, false, &rRet);
    if (!rImageId.isEmpty())
        m_xTreeView->set_image(rRet, rImageId);
}
 
void NavigatorTree::traverseSection(const uno::Reference<report::XSection>& xSection, const weld::TreeIter* pParent, const OUString& rImageId, int nPosition)
{
    std::unique_ptr<weld::TreeIter> xSectionIter = m_xTreeView->make_iterator();
    std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator();
    insertEntry(xSection->getName(), pParent, rImageId, nPosition, new UserData(this, xSection), *xSectionIter);
    const sal_Int32 nCount = xSection->getCount();
    for (sal_Int32 i = 0; i < nCount; ++i)
    {
        uno::Reference< report::XReportComponent> xElement(xSection->getByIndex(i), uno::UNO_QUERY_THROW);
        insertEntry(lcl_getName(xElement), xSectionIter.get(), lcl_getImageId(xElement), -1, new UserData(this, xElement), *xScratch);
        uno::Reference< report::XReportDefinition> xSubReport(xElement,uno::UNO_QUERY);
        if ( xSubReport.is() )
        {
            bool bMasterReport = find(xSection->getReportDefinition(), *xScratch);
            if (!bMasterReport)
                m_xMasterReport.reset();
            else
                m_xMasterReport = m_xTreeView->make_iterator(xScratch.get());
            reportdesign::OReportVisitor aSubVisitor(this);
            aSubVisitor.start(xSubReport);
        }
    }
}
 
void NavigatorTree::traverseFunctions(const uno::Reference< report::XFunctions>& xFunctions, const weld::TreeIter* pParent)
{
    std::unique_ptr<weld::TreeIter> xFunctionIter = m_xTreeView->make_iterator();
    std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator();
    insertEntry(RptResId(RID_STR_FUNCTIONS), pParent, RID_SVXBMP_RPT_NEW_FUNCTION, -1, new UserData(this, xFunctions), *xFunctionIter);
    const sal_Int32 nCount = xFunctions->getCount();
    for (sal_Int32 i = 0; i< nCount; ++i)
    {
        uno::Reference< report::XFunction> xElement(xFunctions->getByIndex(i),uno::UNO_QUERY);
        insertEntry(xElement->getName(), xFunctionIter.get(), RID_SVXBMP_RPT_NEW_FUNCTION, -1, new UserData(this,xElement), *xScratch);
    }
}
 
bool NavigatorTree::find(const uno::Reference<uno::XInterface>& xContent, weld::TreeIter& rRet)
{
    bool bRet = false;
    if (xContent.is())
    {
        m_xTreeView->all_foreach([this, &xContent, &bRet, &rRet](weld::TreeIter& rIter) {
            UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_id(rIter));
            if (pData->getContent() == xContent)
            {
                m_xTreeView->copy_iterator(rIter, rRet);
                bRet = true;
                return true;
            }
            return false;
        });
    }
    return bRet;
}
 
// ITraverseReport
 
void NavigatorTree::traverseReport(const uno::Reference< report::XReportDefinition>& xReport)
{
    std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator();
    insertEntry(xReport->getName(), m_xMasterReport.get(), RID_SVXBMP_SELECT_REPORT,-1, new UserData(this, xReport), *xScratch);
}
 
void NavigatorTree::traverseReportFunctions(const uno::Reference< report::XFunctions>& xFunctions)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xFunctions->getParent(), *xReport);
    if (!bReport)
        xReport.reset();
    traverseFunctions(xFunctions, xReport.get());
}
 
void NavigatorTree::traverseReportHeader(const uno::Reference< report::XSection>& xSection)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xSection->getReportDefinition(), *xReport);
    if (!bReport)
        xReport.reset();
    traverseSection(xSection, xReport.get(), RID_SVXBMP_REPORTHEADERFOOTER);
}
 
void NavigatorTree::traverseReportFooter(const uno::Reference< report::XSection>& xSection)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xSection->getReportDefinition(), *xReport);
    if (!bReport)
        xReport.reset();
    traverseSection(xSection, xReport.get(), RID_SVXBMP_REPORTHEADERFOOTER);
}
 
void NavigatorTree::traversePageHeader(const uno::Reference< report::XSection>& xSection)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xSection->getReportDefinition(), *xReport);
    if (!bReport)
        xReport.reset();
    traverseSection(xSection, xReport.get(), RID_SVXBMP_PAGEHEADERFOOTER);
}
 
void NavigatorTree::traversePageFooter(const uno::Reference< report::XSection>& xSection)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xSection->getReportDefinition(), *xReport);
    if (!bReport)
        xReport.reset();
    traverseSection(xSection, xReport.get(), RID_SVXBMP_PAGEHEADERFOOTER);
}
 
void NavigatorTree::traverseGroups(const uno::Reference< report::XGroups>& xGroups)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xGroups->getReportDefinition(), *xReport);
    if (!bReport)
        xReport.reset();
    std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator();
    insertEntry(RptResId(RID_STR_GROUPS), xReport.get(), RID_SVXBMP_SORTINGANDGROUPING, -1, new UserData(this, xGroups), *xScratch);
}
 
void NavigatorTree::traverseGroup(const uno::Reference< report::XGroup>& xGroup)
{
    uno::Reference< report::XGroups> xGroups(xGroup->getParent(),uno::UNO_QUERY);
    std::unique_ptr<weld::TreeIter> xGroupsIter = m_xTreeView->make_iterator();
    bool bGroups = find(xGroups, *xGroupsIter);
    OSL_ENSURE(bGroups, "No Groups inserted so far. Why!");
    if (!bGroups)
        xGroupsIter.reset();
    std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator();
    insertEntry(xGroup->getExpression(), xGroupsIter.get(), RID_SVXBMP_GROUP, rptui::getPositionInIndexAccess(xGroups,xGroup), new UserData(this,xGroup), *xScratch);
}
 
void NavigatorTree::traverseGroupFunctions(const uno::Reference< report::XFunctions>& xFunctions)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xFunctions->getParent(), *xReport);
    if (!bReport)
        xReport.reset();
    traverseFunctions(xFunctions, xReport.get());
}
 
void NavigatorTree::traverseGroupHeader(const uno::Reference< report::XSection>& xSection)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xSection->getGroup(), *xReport);
    OSL_ENSURE(bReport, "No group found");
    if (!bReport)
        xReport.reset();
    traverseSection(xSection, xReport.get(), RID_SVXBMP_GROUPHEADER, 1);
}
 
void NavigatorTree::traverseGroupFooter(const uno::Reference< report::XSection>& xSection)
{
    std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator();
    bool bReport = find(xSection->getGroup(), *xReport);
    OSL_ENSURE(bReport, "No group found");
    if (!bReport)
        xReport.reset();
    traverseSection(xSection, xReport.get(), RID_SVXBMP_GROUPFOOTER);
}
 
void NavigatorTree::traverseDetail(const uno::Reference< report::XSection>& xSection)
{
    uno::Reference< report::XReportDefinition> xReport = xSection->getReportDefinition();
    std::unique_ptr<weld::TreeIter> xParent = m_xTreeView->make_iterator();
    bool bParent = find(xReport, *xParent);
    if (!bParent)
        xParent.reset();
    traverseSection(xSection, xParent.get(), RID_SVXBMP_ICON_DETAIL);
}
 
void NavigatorTree::_propertyChanged(const beans::PropertyChangeEvent& _rEvent)
{
    uno::Reference< report::XReportDefinition> xReport(_rEvent.Source,uno::UNO_QUERY);
    if ( !xReport.is() )
        return;
 
    bool bEnabled = false;
    _rEvent.NewValue >>= bEnabled;
    if ( !bEnabled )
        return;
 
    std::unique_ptr<weld::TreeIter> xParent = m_xTreeView->make_iterator();
    bool bParent = find(xReport, *xParent);
    if (!bParent)
        xParent.reset();
    if ( _rEvent.PropertyName == PROPERTY_REPORTHEADERON )
    {
        int nPos = xReport->getReportHeaderOn() ? 2 : 1;
        traverseSection(xReport->getReportHeader(),xParent.get(),RID_SVXBMP_REPORTHEADERFOOTER,nPos);
    }
    else if ( _rEvent.PropertyName == PROPERTY_PAGEHEADERON )
    {
        traverseSection(xReport->getPageHeader(),xParent.get(), RID_SVXBMP_PAGEHEADERFOOTER,1);
    }
    else if ( _rEvent.PropertyName == PROPERTY_PAGEFOOTERON )
        traverseSection(xReport->getPageFooter(),xParent.get(), RID_SVXBMP_PAGEHEADERFOOTER);
    else if ( _rEvent.PropertyName == PROPERTY_REPORTFOOTERON )
    {
        int nPos = -1;
        if (xReport->getPageFooterOn() && xParent)
            nPos = m_xTreeView->iter_n_children(*xParent) - 1;
        traverseSection(xReport->getReportFooter(),xParent.get(),RID_SVXBMP_REPORTHEADERFOOTER,nPos);
    }
}
 
void NavigatorTree::_elementInserted( const container::ContainerEvent& _rEvent )
{
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
    bool bEntry = find(_rEvent.Source, *xEntry);
    if (!bEntry)
        xEntry.reset();
    uno::Reference<beans::XPropertySet> xProp(_rEvent.Element,uno::UNO_QUERY_THROW);
    OUString sName;
    uno::Reference< beans::XPropertySetInfo> xInfo = xProp->getPropertySetInfo();
    if ( xInfo.is() )
    {
        if ( xInfo->hasPropertyByName(PROPERTY_NAME) )
            xProp->getPropertyValue(PROPERTY_NAME) >>= sName;
        else if ( xInfo->hasPropertyByName(PROPERTY_EXPRESSION) )
            xProp->getPropertyValue(PROPERTY_EXPRESSION) >>= sName;
    }
    uno::Reference< report::XGroup> xGroup(xProp,uno::UNO_QUERY);
    if ( xGroup.is() )
    {
        reportdesign::OReportVisitor aSubVisitor(this);
        aSubVisitor.start(xGroup);
    }
    else
    {
        uno::Reference< report::XReportComponent> xElement(xProp,uno::UNO_QUERY);
        if ( xProp.is() )
            sName = lcl_getName(xProp);
        std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator();
        insertEntry(sName, xEntry.get(), (!xElement.is() ? RID_SVXBMP_RPT_NEW_FUNCTION : lcl_getImageId(xElement)),
                    -1, new UserData(this,xProp), *xScratch);
    }
    if (bEntry && !m_xTreeView->get_row_expanded(*xEntry))
        m_xTreeView->expand_row(*xEntry);
}
 
void NavigatorTree::_elementRemoved( const container::ContainerEvent& _rEvent )
{
    uno::Reference<beans::XPropertySet> xProp(_rEvent.Element,uno::UNO_QUERY);
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
    bool bEntry = find(xProp, *xEntry);
    OSL_ENSURE(bEntry,"NavigatorTree::_elementRemoved: No Entry found!");
 
    if (bEntry)
    {
        removeEntry(*xEntry);
    }
}
 
void NavigatorTree::_elementReplaced( const container::ContainerEvent& _rEvent )
{
    uno::Reference<beans::XPropertySet> xProp(_rEvent.ReplacedElement,uno::UNO_QUERY);
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
    bool bEntry = find(xProp, *xEntry);
    if (bEntry)
    {
        UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_id(*xEntry));
        xProp.set(_rEvent.Element,uno::UNO_QUERY);
        pData->setContent(xProp);
        OUString sName;
        xProp->getPropertyValue(PROPERTY_NAME) >>= sName;
        m_xTreeView->set_text(*xEntry, sName);
    }
}
 
void NavigatorTree::_disposing(const lang::EventObject& _rSource)
{
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
    if (find(_rSource.Source, *xEntry))
        removeEntry(*xEntry);
}
 
void NavigatorTree::removeEntry(const weld::TreeIter& rEntry, bool bRemove)
{
    std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rEntry);
    bool bChild = m_xTreeView->iter_children(*xChild);
    while (bChild)
    {
        removeEntry(*xChild, false);
        bChild = m_xTreeView->iter_next_sibling(*xChild);
    }
    delete weld::fromId<UserData*>(m_xTreeView->get_id(rEntry));
    if (bRemove)
        m_xTreeView->remove(rEntry);
}
 
NavigatorTree::UserData::UserData(NavigatorTree* pTree,uno::Reference<uno::XInterface> xContent)
    : OPropertyChangeListener()
    , OContainerListener(m_aMutex)
    , m_xContent(std::move(xContent))
    , m_pTree(pTree)
{
    uno::Reference<beans::XPropertySet> xProp(m_xContent,uno::UNO_QUERY);
    if ( xProp.is() )
    {
        uno::Reference< beans::XPropertySetInfo> xInfo = xProp->getPropertySetInfo();
        if ( xInfo.is() )
        {
            m_pListener = new ::comphelper::OPropertyChangeMultiplexer(this,xProp);
            if ( xInfo->hasPropertyByName(PROPERTY_NAME) )
                m_pListener->addProperty(PROPERTY_NAME);
            else if ( xInfo->hasPropertyByName(PROPERTY_EXPRESSION) )
                m_pListener->addProperty(PROPERTY_EXPRESSION);
            if ( xInfo->hasPropertyByName(PROPERTY_DATAFIELD) )
                m_pListener->addProperty(PROPERTY_DATAFIELD);
            if ( xInfo->hasPropertyByName(PROPERTY_LABEL) )
                m_pListener->addProperty(PROPERTY_LABEL);
            if ( xInfo->hasPropertyByName(PROPERTY_HEADERON) )
                m_pListener->addProperty(PROPERTY_HEADERON);
            if ( xInfo->hasPropertyByName(PROPERTY_FOOTERON) )
                m_pListener->addProperty(PROPERTY_FOOTERON);
        }
    }
    uno::Reference< container::XContainer> xContainer(m_xContent,uno::UNO_QUERY);
    if ( xContainer.is() )
    {
        m_pContainerListener = new ::comphelper::OContainerListenerAdapter(this,xContainer);
    }
}
 
NavigatorTree::UserData::~UserData()
{
    if ( m_pContainerListener.is() )
        m_pContainerListener->dispose();
    if ( m_pListener.is() )
        m_pListener->dispose();
}
 
// OPropertyChangeListener
void NavigatorTree::UserData::_propertyChanged(const beans::PropertyChangeEvent& _rEvent)
{
    std::unique_ptr<weld::TreeIter> xEntry = m_pTree->make_iterator();
    bool bEntry = m_pTree->find(_rEvent.Source, *xEntry);
    OSL_ENSURE(bEntry,"No entry could be found! Why not!");
    if (!bEntry)
        return;
    const bool bFooterOn = (PROPERTY_FOOTERON == _rEvent.PropertyName);
    try
    {
        if ( bFooterOn || PROPERTY_HEADERON == _rEvent.PropertyName )
        {
            sal_Int32 nPos = 1;
            uno::Reference< report::XGroup> xGroup(_rEvent.Source,uno::UNO_QUERY);
            ::std::function<bool(OGroupHelper *)> pIsOn = ::std::mem_fn(&OGroupHelper::getHeaderOn);
            ::std::function<uno::Reference<report::XSection>(OGroupHelper *)> pMemFunSection = ::std::mem_fn(&OGroupHelper::getHeader);
            if ( bFooterOn )
            {
                pIsOn = ::std::mem_fn(&OGroupHelper::getFooterOn);
                pMemFunSection = ::std::mem_fn(&OGroupHelper::getFooter);
                nPos = m_pTree->iter_n_children(*xEntry) - 1;
            }
 
            OGroupHelper aGroupHelper(xGroup);
            if ( pIsOn(&aGroupHelper) )
            {
                if ( bFooterOn )
                    ++nPos;
                m_pTree->traverseSection(pMemFunSection(&aGroupHelper),xEntry.get(),bFooterOn ? RID_SVXBMP_GROUPFOOTER : RID_SVXBMP_GROUPHEADER,nPos);
            }
        }
        else if ( PROPERTY_EXPRESSION == _rEvent.PropertyName)
        {
            OUString sNewName;
            _rEvent.NewValue >>= sNewName;
            m_pTree->set_text(*xEntry, sNewName);
        }
        else if ( PROPERTY_DATAFIELD == _rEvent.PropertyName || PROPERTY_LABEL == _rEvent.PropertyName || PROPERTY_NAME == _rEvent.PropertyName )
        {
            uno::Reference<beans::XPropertySet> xProp(_rEvent.Source,uno::UNO_QUERY);
            m_pTree->set_text(*xEntry, lcl_getName(xProp));
        }
    }
    catch(const uno::Exception &)
    {}
}
 
void NavigatorTree::UserData::_elementInserted( const container::ContainerEvent& _rEvent )
{
    m_pTree->_elementInserted( _rEvent );
}
 
void NavigatorTree::UserData::_elementRemoved( const container::ContainerEvent& _rEvent )
{
    m_pTree->_elementRemoved( _rEvent );
}
 
void NavigatorTree::UserData::_elementReplaced( const container::ContainerEvent& _rEvent )
{
    m_pTree->_elementReplaced( _rEvent );
}
 
void NavigatorTree::UserData::_disposing(const lang::EventObject& _rSource)
{
    m_pTree->_disposing( _rSource );
}
 
ONavigator::ONavigator(weld::Window* pParent, OReportController& rController)
    : GenericDialogController(pParent, u"modules/dbreport/ui/floatingnavigator.ui"_ustr, u"FloatingNavigator"_ustr)
    , m_xReport(rController.getReportDefinition())
    , m_xNavigatorTree(std::make_unique<NavigatorTree>(m_xBuilder->weld_tree_view(u"treeview"_ustr), rController))
{
    reportdesign::OReportVisitor aVisitor(m_xNavigatorTree.get());
    aVisitor.start(m_xReport);
    std::unique_ptr<weld::TreeIter> xScratch = m_xNavigatorTree->make_iterator();
    if (m_xNavigatorTree->find(m_xReport, *xScratch))
        m_xNavigatorTree->expand_row(*xScratch);
    lang::EventObject aEvent(rController);
    m_xNavigatorTree->_selectionChanged(aEvent);
    m_xNavigatorTree->grab_focus();
 
    m_xDialog->connect_container_focus_changed(LINK(this, ONavigator, FocusChangeHdl));
}
 
ONavigator::~ONavigator()
{
}
 
IMPL_LINK_NOARG(ONavigator, FocusChangeHdl, weld::Container&, void)
{
    if (m_xDialog->has_toplevel_focus())
        m_xNavigatorTree->grab_focus();
}
 
} // rptui
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression '!bEnabled' is always true.