/* -*- 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 "newhelp.hxx"
#include <sfx2/sfxuno.hxx>
#include <sfx2/sfxresid.hxx>
#include "helpinterceptor.hxx"
#include <helper.hxx>
#include <sfx2/msgpool.hxx>
#include <sfx2/app.hxx>
#include <sfxtypes.hxx>
#include "panelist.hxx"
#include <srchdlg.hxx>
#include <sfx2/sfxhelp.hxx>
#include <svtools/treelistentry.hxx>
#include <sal/log.hxx>
 
#include <sfx2/strings.hrc>
#include <helpids.h>
#include <bitmaps.hlst>
 
#include <rtl/ustrbuf.hxx>
#include <comphelper/configurationhelper.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <com/sun/star/awt/PosSize.hpp>
#include <com/sun/star/awt/XWindow.hpp>
#include <com/sun/star/beans/Property.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/frame/XComponentLoader.hpp>
#include <com/sun/star/frame/XTitle.hpp>
#include <com/sun/star/frame/XLayoutManager.hpp>
#include <com/sun/star/frame/XController.hpp>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/frame/Frame.hpp>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/style/XStyle.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextCursor.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/text/XTextViewCursor.hpp>
#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
#include <com/sun/star/ucb/CommandAbortedException.hpp>
#include <com/sun/star/util/URL.hpp>
#include <com/sun/star/util/XSearchable.hpp>
#include <com/sun/star/util/XSearchDescriptor.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/util/XModifiable.hpp>
#include <com/sun/star/util/XCloseable.hpp>
#include <com/sun/star/util/CloseVetoException.hpp>
#include <com/sun/star/ui/XDockingAreaAcceptor.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/view/XViewSettingsSupplier.hpp>
#include <svtools/helpopt.hxx>
#include <unotools/historyoptions.hxx>
#include <svtools/menuoptions.hxx>
#include <unotools/pathoptions.hxx>
#include <unotools/viewoptions.hxx>
#include <tools/urlobj.hxx>
#include <unotools/streamhelper.hxx>
#include <svtools/imagemgr.hxx>
#include <svtools/miscopt.hxx>
#include <svtools/imgdef.hxx>
#include <vcl/builderfactory.hxx>
#include <vcl/unohelp.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/layout.hxx>
#include <vcl/settings.hxx>
#include <vcl/weld.hxx>
 
#include <ucbhelper/content.hxx>
#include <vcl/waitobj.hxx>
#include <unotools/ucbhelper.hxx>
 
#include <sfx2/viewfrm.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/docfac.hxx>
 
#include <unordered_map>
#include <vector>
 
using namespace ::ucbhelper;
using namespace ::com::sun::star::ucb;
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::view;
using namespace ::com::sun::star::ui;
 
using namespace ::comphelper;
 
// defines ---------------------------------------------------------------
 
#define SPLITSET_ID         0
#define COLSET_ID           1
#define INDEXWIN_ID         2
#define TEXTWIN_ID          3
 
#define TOOLBOX_OFFSET      3
 
#define TBI_INDEX           1001
#define TBI_BACKWARD        1002
#define TBI_FORWARD         1003
#define TBI_START           1004
#define TBI_PRINT           1005
#define TBI_COPY            1006
#define TBI_BOOKMARKS       1007
#define TBI_SEARCHDIALOG    1008
#define TBI_SOURCEVIEW      1009
#define TBI_SELECTIONMODE   1010
 
#define CONFIGNAME_HELPWIN      "OfficeHelp"
#define CONFIGNAME_INDEXWIN     "OfficeHelpIndex"
#define CONFIGNAME_SEARCHPAGE   "OfficeHelpSearch"
#define IMAGE_URL               "private:factory/"
 
#define PROPERTY_KEYWORDLIST    "KeywordList"
#define PROPERTY_KEYWORDREF     "KeywordRef"
#define PROPERTY_ANCHORREF      "KeywordAnchorForRef"
#define PROPERTY_TITLEREF       "KeywordTitleForRef"
#define PROPERTY_TITLE          "Title"
#define HELP_URL                "vnd.sun.star.help://"
#define HELP_SEARCH_TAG         "/?Query="
#define USERITEM_NAME           "UserItem"
 
#define PACKAGE_SETUP           "/org.openoffice.Setup"
#define PATH_OFFICE_FACTORIES   "Office/Factories/"
#define KEY_HELP_ON_OPEN        "ooSetupFactoryHelpOnOpen"
#define KEY_UI_NAME             "ooSetupFactoryUIName"
 
namespace sfx2
{
 
 
    void HandleTaskPaneList( vcl::Window* pWindow, bool bAddToList )
    {
        vcl::Window* pParent = pWindow->GetParent();
        DBG_ASSERT( pParent, "HandleTaskPaneList(): every window here should have a parent" );
 
        SystemWindow* pSysWin = pParent->GetSystemWindow();
        if( pSysWin )
        {
            TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList();
            if( pTaskPaneList )
            {
                if( bAddToList )
                    pTaskPaneList->AddWindow( pWindow );
                else
                    pTaskPaneList->RemoveWindow( pWindow );
            }
        }
    }
 
    /** Prepare a search string for searching or selecting.
        For searching every search word needs the postfix '*' and the delimiter ' ' if necessary.
        For selecting the delimiter '|' is required to search with regular expressions.
        Samples:
        search string | output for searching | output for selecting
        -----------------------------------------------------------
        "text"        | "text*"              | "text"
        "text*"       | "text*"              | "text"
        "text menu"   | "text* menu*"        | "text|menu"
    */
    OUString PrepareSearchString( const OUString& rSearchString,
                                const Reference< XBreakIterator >& xBreak, bool bForSearch )
    {
        OUStringBuffer sSearchStr;
        sal_Int32 nStartPos = 0;
        const lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale();
        Boundary aBoundary = xBreak->getWordBoundary(
            rSearchString, nStartPos, aLocale, WordType::ANYWORD_IGNOREWHITESPACES, true );
 
        while ( aBoundary.startPos != aBoundary.endPos )
        {
            nStartPos = aBoundary.endPos;
            OUString sSearchToken( rSearchString.copy(
                static_cast<sal_uInt16>(aBoundary.startPos), static_cast<sal_uInt16>(aBoundary.endPos) - static_cast<sal_uInt16>(aBoundary.startPos) ) );
            if ( !sSearchToken.isEmpty() && ( sSearchToken.getLength() > 1 || sSearchToken[0] != '.' ) )
            {
                if ( bForSearch && sSearchToken[ sSearchToken.getLength() - 1 ] != '*' )
                    sSearchToken += "*";
 
                if ( sSearchToken.getLength() > 1 ||
                     ( sSearchToken.getLength() > 0 && sSearchToken[ 0 ] != '*' ) )
                {
                    if ( !sSearchStr.isEmpty() )
                    {
                        if ( bForSearch )
                            sSearchStr.append(" ");
                        else
                            sSearchStr.append("|");
                    }
                    sSearchStr.append(sSearchToken);
                }
            }
            aBoundary = xBreak->nextWord( rSearchString, nStartPos,
                                          aLocale, WordType::ANYWORD_IGNOREWHITESPACES );
        }
 
        return sSearchStr.makeStringAndClear();
    }
 
// namespace sfx2
}
 
 
// struct IndexEntry_Impl ------------------------------------------------
 
struct IndexEntry_Impl
{
    bool        m_bSubEntry;
    OUString        m_aURL;
 
    IndexEntry_Impl( const OUString& rURL, bool bSubEntry ) :
        m_bSubEntry( bSubEntry ), m_aURL( rURL ) {}
};
 
// struct ContentEntry_Impl ----------------------------------------------
 
struct ContentEntry_Impl
{
    OUString    aURL;
    bool    bIsFolder;
 
    ContentEntry_Impl( const OUString& rURL, bool bFolder ) :
        aURL( rURL ), bIsFolder( bFolder ) {}
};
 
// ContentListBox_Impl ---------------------------------------------------
 
ContentListBox_Impl::ContentListBox_Impl(vcl::Window* pParent, WinBits nStyle)
    : SvTreeListBox(pParent, nStyle)
    , aOpenBookImage(BitmapEx(BMP_HELP_CONTENT_BOOK_OPEN))
    , aClosedBookImage(BitmapEx(BMP_HELP_CONTENT_BOOK_CLOSED))
    , aDocumentImage(BitmapEx(BMP_HELP_CONTENT_DOC))
 
{
    SetStyle( GetStyle() | WB_HIDESELECTION | WB_HSCROLL );
 
    SetEntryHeight( 16 );
    SetSelectionMode( SelectionMode::Single );
    SetSpaceBetweenEntries( 2 );
    SetNodeBitmaps( aClosedBookImage, aOpenBookImage );
 
    SetSublistOpenWithReturn();
    SetSublistOpenWithLeftRight();
 
    InitRoot();
}
 
extern "C" SAL_DLLPUBLIC_EXPORT void makeContentListBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
    WinBits nWinStyle = WB_TABSTOP;
    OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
    if (!sBorder.isEmpty())
        nWinStyle |= WB_BORDER;
    rRet = VclPtr<ContentListBox_Impl>::Create(pParent, nWinStyle);
}
 
ContentListBox_Impl::~ContentListBox_Impl()
{
    disposeOnce();
}
 
void ContentListBox_Impl::dispose()
{
    sal_uInt16 nPos = 0;
    SvTreeListEntry* pEntry = GetEntry( nPos++ );
    while ( pEntry )
    {
        ClearChildren( pEntry );
        delete static_cast<ContentEntry_Impl*>(pEntry->GetUserData());
        pEntry = GetEntry( nPos++ );
    }
    SvTreeListBox::dispose();
}
 
void ContentListBox_Impl::InitRoot()
{
    OUString const aHelpTreeviewURL( "vnd.sun.star.hier://com.sun.star.help.TreeView/" );
    std::vector< OUString > aList =
        SfxContentHelper::GetHelpTreeViewContents( aHelpTreeviewURL );
 
    for(const OUString & aRow : aList)
    {
        sal_Int32 nIdx = 0;
        OUString aTitle = aRow.getToken( 0, '\t', nIdx );
        OUString aURL = aRow.getToken( 0, '\t', nIdx );
        sal_Unicode cFolder = aRow.getToken( 0, '\t', nIdx )[0];
        bool bIsFolder = ( '1' == cFolder );
        SvTreeListEntry* pEntry = InsertEntry( aTitle, aOpenBookImage, aClosedBookImage, nullptr, true );
        if ( bIsFolder )
            pEntry->SetUserData( new ContentEntry_Impl( aURL, true ) );
    }
}
 
 
void ContentListBox_Impl::ClearChildren( SvTreeListEntry* pParent )
{
    SvTreeListEntry* pEntry = FirstChild( pParent );
    while ( pEntry )
    {
        ClearChildren( pEntry );
        delete static_cast<ContentEntry_Impl*>(pEntry->GetUserData());
        pEntry = pEntry->NextSibling();
    }
}
 
 
void ContentListBox_Impl::RequestingChildren( SvTreeListEntry* pParent )
{
    try
    {
        if ( !pParent->HasChildren() )
        {
            if ( pParent->GetUserData() )
            {
                OUString aTmpURL( static_cast<ContentEntry_Impl*>(pParent->GetUserData())->aURL );
                std::vector<OUString > aList =
                    SfxContentHelper::GetHelpTreeViewContents( aTmpURL );
 
                for (const OUString & aRow : aList)
                {
                    sal_Int32 nIdx = 0;
                    OUString aTitle = aRow.getToken( 0, '\t', nIdx );
                    OUString aURL = aRow.getToken( 0, '\t', nIdx );
                    sal_Unicode cFolder = aRow.getToken( 0, '\t', nIdx )[0];
                    bool bIsFolder = ( '1' == cFolder );
                    SvTreeListEntry* pEntry = nullptr;
                    if ( bIsFolder )
                    {
                        pEntry = InsertEntry( aTitle, aOpenBookImage, aClosedBookImage, pParent, true );
                        pEntry->SetUserData( new ContentEntry_Impl( aURL, true ) );
                    }
                    else
                    {
                        pEntry = InsertEntry( aTitle, aDocumentImage, aDocumentImage, pParent );
                        Any aAny( ::utl::UCBContentHelper::GetProperty( aURL, "TargetURL" ) );
                        OUString aTargetURL;
                        if ( aAny >>= aTargetURL )
                            pEntry->SetUserData( new ContentEntry_Impl( aTargetURL, false ) );
                    }
                }
            }
        }
    }
    catch( Exception& )
    {
        OSL_FAIL( "ContentListBox_Impl::RequestingChildren(): unexpected exception" );
    }
}
 
 
bool ContentListBox_Impl::EventNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;
    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT &&
         KEY_RETURN == rNEvt.GetKeyEvent()->GetKeyCode().GetCode() )
    {
        GetDoubleClickHdl().Call( nullptr );
        bHandled = true;
    }
 
    return bHandled || SvTreeListBox::EventNotify( rNEvt );
}
 
 
OUString ContentListBox_Impl::GetSelectedEntry() const
{
    OUString aRet;
    SvTreeListEntry* pEntry = FirstSelected();
    if ( pEntry && !static_cast<ContentEntry_Impl*>(pEntry->GetUserData())->bIsFolder )
        aRet = static_cast<ContentEntry_Impl*>(pEntry->GetUserData())->aURL;
    return aRet;
}
 
// class HelpTabPage_Impl ------------------------------------------------
HelpTabPage_Impl::HelpTabPage_Impl(vcl::Window* pParent, SfxHelpIndexWindow_Impl* _pIdxWin,
    const OString& rID, const OUString& rUIXMLDescription)
    : TabPage( pParent, rID, rUIXMLDescription)
    , m_pIdxWin( _pIdxWin )
{
}
 
HelpTabPage_Impl::~HelpTabPage_Impl()
{
    disposeOnce();
}
 
void HelpTabPage_Impl::dispose()
{
    m_pIdxWin.clear();
    TabPage::dispose();
}
 
// class ContentTabPage_Impl ---------------------------------------------
ContentTabPage_Impl::ContentTabPage_Impl(vcl::Window* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
    : HelpTabPage_Impl(pParent, _pIdxWin, "HelpContentPage",
        "sfx/ui/helpcontentpage.ui")
{
    get(m_pContentBox, "content");
    Size aSize(LogicToPixel(Size(108 , 188), MapMode(MapUnit::MapAppFont)));
    m_pContentBox->set_width_request(aSize.Width());
    m_pContentBox->set_height_request(aSize.Height());
}
 
ContentTabPage_Impl::~ContentTabPage_Impl()
{
    disposeOnce();
}
 
void ContentTabPage_Impl::dispose()
{
    m_pContentBox.clear();
    HelpTabPage_Impl::dispose();
}
 
void ContentTabPage_Impl::ActivatePage()
{
    if ( !m_pIdxWin->WasCursorLeftOrRight() )
        SetFocusOnBox();
}
 
Control* ContentTabPage_Impl::GetLastFocusControl()
{
    return m_pContentBox;
}
 
// class IndexBox_Impl ---------------------------------------------------
 
IndexBox_Impl::IndexBox_Impl(vcl::Window* pParent, WinBits nStyle)
    : ComboBox(pParent, nStyle)
{
    EnableAutocomplete(true);
    EnableUserDraw(true);
}
 
extern "C" SAL_DLLPUBLIC_EXPORT void makeIndexBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
    WinBits nWinBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
    OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
    if (!sBorder.isEmpty())
       nWinBits |= WB_BORDER;
    VclPtrInstance<IndexBox_Impl> pListBox(pParent, nWinBits);
    pListBox->EnableAutoSize(true);
    rRet = pListBox;
}
 
void IndexBox_Impl::UserDraw( const UserDrawEvent& rUDEvt )
{
    IndexEntry_Impl* pEntry = static_cast<IndexEntry_Impl*>(GetEntryData( rUDEvt.GetItemId() ));
    if ( pEntry && pEntry->m_bSubEntry )
    {
        // indent sub entries
        Point aPos( rUDEvt.GetRect().TopLeft() );
        aPos.AdjustX(8 );
        aPos.AdjustY((rUDEvt.GetRect().GetHeight() - rUDEvt.GetRenderContext()->GetTextHeight()) / 2 );
        OUString aEntry( GetEntry( rUDEvt.GetItemId() ) );
        sal_Int32 nPos = aEntry.indexOf( ';' );
        rUDEvt.GetRenderContext()->DrawText(aPos, (nPos !=-1) ? aEntry.copy(nPos + 1) : aEntry);
    }
    else
        DrawEntry( rUDEvt, false, true, true );
}
 
 
bool IndexBox_Impl::EventNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;
    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT &&
         KEY_RETURN == rNEvt.GetKeyEvent()->GetKeyCode().GetCode() )
    {
        GetDoubleClickHdl().Call( *this );
        bHandled = true;
    }
 
    return bHandled || ComboBox::EventNotify( rNEvt );
}
 
 
void IndexBox_Impl::SelectExecutableEntry()
{
    sal_Int32 nPos = GetEntryPos( GetText() );
    if ( nPos != COMBOBOX_ENTRY_NOTFOUND )
    {
        sal_Int32 nOldPos = nPos;
        OUString aEntryText;
        IndexEntry_Impl* pEntry = static_cast<IndexEntry_Impl*>(GetEntryData( nPos ));
        sal_Int32 nCount = GetEntryCount();
        while ( nPos < nCount && ( !pEntry || pEntry->m_aURL.isEmpty() ) )
        {
            pEntry = static_cast<IndexEntry_Impl*>(GetEntryData( ++nPos ));
            aEntryText = GetEntry( nPos );
        }
 
        if ( nOldPos != nPos )
            SetText( aEntryText );
    }
}
 
// class IndexTabPage_Impl -----------------------------------------------
 
IndexTabPage_Impl::IndexTabPage_Impl(vcl::Window* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
    : HelpTabPage_Impl(pParent, _pIdxWin, "HelpIndexPage",
        "sfx/ui/helpindexpage.ui")
    , aFactoryIdle("sfx2 appl IndexTabPage_Impl Factory")
    , bIsActivated(false)
{
    get(m_pIndexCB, "terms");
    Size aSize(LogicToPixel(Size(108, 97), MapMode(MapUnit::MapAppFont)));
    m_pIndexCB->set_width_request(aSize.Width());
    m_pIndexCB->set_height_request(aSize.Height());
    get(m_pOpenBtn, "display");
 
    m_pOpenBtn->SetClickHdl( LINK( this, IndexTabPage_Impl, OpenHdl ) );
    aFactoryIdle.SetInvokeHandler( LINK(this, IndexTabPage_Impl, IdleHdl ));
    aKeywordTimer.SetInvokeHandler( LINK( this, IndexTabPage_Impl, TimeoutHdl ) );
}
 
IndexTabPage_Impl::~IndexTabPage_Impl()
{
    disposeOnce();
}
 
void IndexTabPage_Impl::dispose()
{
    ClearIndex();
    m_pIndexCB.clear();
    m_pOpenBtn.clear();
    HelpTabPage_Impl::dispose();
}
 
 
namespace sfx2 {
 
    typedef std::unordered_map< OUString, int > KeywordInfo;
}
 
void IndexTabPage_Impl::InitializeIndex()
{
    WaitObject aWaitCursor( this );
 
    // By now more than 256 equal entries are not allowed
    sal_Unicode append[256];
    for(sal_Unicode & k : append)
        k =  ' ';
 
    sfx2::KeywordInfo aInfo;
    m_pIndexCB->SetUpdateMode( false );
 
    try
    {
        OUStringBuffer aURL = HELP_URL;
        aURL.append(sFactory);
        AppendConfigToken(aURL, true);
 
        Content aCnt( aURL.makeStringAndClear(), Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
        css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
        if ( xInfo->hasPropertyByName( PROPERTY_ANCHORREF ) )
        {
            css::uno::Sequence< OUString > aPropSeq( 4 );
            aPropSeq[0] = PROPERTY_KEYWORDLIST;
            aPropSeq[1] = PROPERTY_KEYWORDREF;
            aPropSeq[2] = PROPERTY_ANCHORREF;
            aPropSeq[3] = PROPERTY_TITLEREF;
 
            // abi: use one possibly remote call only
            css::uno::Sequence< css::uno::Any > aAnySeq =
                  aCnt.getPropertyValues( aPropSeq );
 
            css::uno::Sequence< OUString > aKeywordList;
            css::uno::Sequence< css::uno::Sequence< OUString > > aKeywordRefList;
            css::uno::Sequence< css::uno::Sequence< OUString > > aAnchorRefList;
            css::uno::Sequence< css::uno::Sequence< OUString > > aTitleRefList;
 
            if ( ( aAnySeq[0] >>= aKeywordList ) && ( aAnySeq[1] >>= aKeywordRefList ) &&
                 ( aAnySeq[2] >>= aAnchorRefList ) && ( aAnySeq[3] >>= aTitleRefList ) )
            {
                sal_Int32 nPos;
                int ndx,tmp;
                OUString aIndex, aTempString;
                OUStringBuffer aData( 128 );            // Capacity of up to 128 characters
                sfx2::KeywordInfo::iterator it;
 
                for ( int i = 0; i < aKeywordList.getLength(); ++i )
                {
                    // abi: Do not copy, but use references
                    const OUString& aKeywordPair = aKeywordList[i];
                    DBG_ASSERT( !aKeywordPair.isEmpty(), "invalid help index" );
                    const css::uno::Sequence< OUString >& aRefList = aKeywordRefList[i];
                    const css::uno::Sequence< OUString >& aAnchorList = aAnchorRefList[i];
                    const css::uno::Sequence< OUString >& aTitleList = aTitleRefList[i];
 
                    DBG_ASSERT( aRefList.getLength() == aAnchorList.getLength(),"reference list and title list of different length" );
 
                    const bool insert = ( ndx = aKeywordPair.indexOf( ';' ) ) != -1;
 
                    if ( insert )
                    {
                        aTempString = aKeywordPair.copy( 0, ndx );
                        if ( aIndex != aTempString )
                        {
                            aIndex = aTempString;
                            it = aInfo.emplace(aTempString, 0).first;
                            if ( (tmp = it->second++) != 0)
                                m_pIndexCB->InsertEntry(aTempString + OUString(append, tmp));
                            else
                                m_pIndexCB->InsertEntry(aTempString);                        }
                    }
                    else
                        aIndex.clear();
 
                    // Assume the token is trimmed
                    it = aInfo.emplace(aKeywordPair, 0).first;
                    if ((tmp = it->second++) != 0)
                        nPos = m_pIndexCB->InsertEntry(aKeywordPair + OUString(append, tmp));
                    else
                        nPos = m_pIndexCB->InsertEntry(aKeywordPair);
 
                    sal_uInt32 nRefListLen = aRefList.getLength();
 
                    DBG_ASSERT( aAnchorList.getLength(), "*IndexTabPage_Impl::InitializeIndex(): AnchorList is empty!" );
                    DBG_ASSERT( nRefListLen, "*IndexTabPage_Impl::InitializeIndex(): RefList is empty!" );
 
                    if ( aAnchorList.getLength() && nRefListLen )
                    {
                        if ( aAnchorList[0].getLength() > 0 )
                        {
                            aData.append( aRefList[0] ).append( '#' ).append( aAnchorList[0] );
                            m_pIndexCB->SetEntryData( nPos, new IndexEntry_Impl( aData.makeStringAndClear(), insert ) );
                        }
                        else
                            m_pIndexCB->SetEntryData( nPos, new IndexEntry_Impl( aRefList[0], insert ) );
                    }
 
                    for ( sal_uInt32 j = 1; j < nRefListLen ; ++j )
                    {
                        aData
                            .append( aKeywordPair )
                            .append( ' ' )
                            .append( '-' )
                            .append( ' ' )
                            .append( aTitleList[j] );
 
                        aTempString = aData.makeStringAndClear();
                        it = aInfo.emplace(aTempString, 0).first;
                        if ( (tmp = it->second++) != 0 )
                            nPos = m_pIndexCB->InsertEntry(aTempString + OUString(append, tmp));
                        else
                            nPos = m_pIndexCB->InsertEntry(aTempString);
 
                        if ( aAnchorList[j].getLength() > 0 )
                        {
                            aData.append( aRefList[j] ).append( '#' ).append( aAnchorList[j] );
                            m_pIndexCB->SetEntryData( nPos, new IndexEntry_Impl( aData.makeStringAndClear(), insert ) );
                        }
                        else
                            m_pIndexCB->SetEntryData( nPos, new IndexEntry_Impl( aRefList[j], insert ) );
                    }
                }
            }
        }
    }
    catch( Exception& )
    {
        OSL_FAIL( "IndexTabPage_Impl::InitializeIndex(): unexpected exception" );
    }
 
    m_pIndexCB->SetUpdateMode( true );
 
    if ( !sKeyword.isEmpty() )
        aKeywordLink.Call( *this );
}
 
void IndexTabPage_Impl::ClearIndex()
{
    const sal_Int32 nCount = m_pIndexCB->GetEntryCount();
    for ( sal_Int32 i = 0; i < nCount; ++i )
        delete static_cast<IndexEntry_Impl*>(m_pIndexCB->GetEntryData(i));
    m_pIndexCB->Clear();
}
 
IMPL_LINK_NOARG(IndexTabPage_Impl, OpenHdl, Button*, void)
{
    m_pIndexCB->GetDoubleClickHdl().Call(*m_pIndexCB);
}
 
IMPL_LINK( IndexTabPage_Impl, IdleHdl, Timer*, pIdle, void )
{
    if ( &aFactoryIdle == pIdle )
        InitializeIndex();
}
 
IMPL_LINK( IndexTabPage_Impl, TimeoutHdl, Timer*, pTimer, void)
{
    if(&aKeywordTimer == pTimer && !sKeyword.isEmpty())
        aKeywordLink.Call(*this);
}
 
void IndexTabPage_Impl::ActivatePage()
{
    if ( !bIsActivated )
    {
        bIsActivated = true;
        aFactoryIdle.Start();
    }
 
    if ( !m_pIdxWin->WasCursorLeftOrRight() )
        SetFocusOnBox();
}
 
Control* IndexTabPage_Impl::GetLastFocusControl()
{
    return m_pOpenBtn;
}
 
void IndexTabPage_Impl::SetDoubleClickHdl( const Link<ComboBox&,void>& rLink )
{
    m_pIndexCB->SetDoubleClickHdl( rLink );
}
 
void IndexTabPage_Impl::SetFactory( const OUString& rFactory )
{
    OUString sNewFactory( rFactory );
    DBG_ASSERT( !sNewFactory.isEmpty(), "empty factory" );
    bool bValid = m_pIdxWin->IsValidFactory( rFactory );
 
    if ( sFactory.isEmpty() && !bValid )
    {
        sNewFactory = SfxHelp::GetDefaultHelpModule();
        bValid = true;
    }
 
    if ( sNewFactory != sFactory && bValid )
    {
        sFactory = sNewFactory;
        ClearIndex();
        if ( bIsActivated )
            aFactoryIdle.Start();
    }
}
 
 
OUString IndexTabPage_Impl::GetSelectedEntry() const
{
    OUString aRet;
    IndexEntry_Impl* pEntry = static_cast<IndexEntry_Impl*>(m_pIndexCB->GetEntryData( m_pIndexCB->GetEntryPos( m_pIndexCB->GetText() ) ));
    if ( pEntry )
        aRet = pEntry->m_aURL;
    return aRet;
}
 
 
void IndexTabPage_Impl::SetKeyword( const OUString& rKeyword )
{
    sKeyword = rKeyword;
 
    if ( m_pIndexCB->GetEntryCount() > 0 )
        aKeywordTimer.Start();
    else if ( !bIsActivated )
        aFactoryIdle.Start();
}
 
 
bool IndexTabPage_Impl::HasKeyword() const
{
    bool bRet = false;
    if ( !sKeyword.isEmpty() )
    {
        sal_Int32 nPos = m_pIndexCB->GetEntryPos( sKeyword );
        bRet = ( nPos != LISTBOX_ENTRY_NOTFOUND );
    }
 
    return bRet;
}
 
 
bool IndexTabPage_Impl::HasKeywordIgnoreCase()
{
    bool bRet = false;
    if ( !sKeyword.isEmpty() )
    {
        sal_Int32 nEntries = m_pIndexCB->GetEntryCount();
        OUString sIndexItem;
        const vcl::I18nHelper& rI18nHelper = GetSettings().GetLocaleI18nHelper();
        for ( sal_Int32 n = 0; n < nEntries; n++)
        {
            sIndexItem = m_pIndexCB->GetEntry( n );
            if (rI18nHelper.MatchString( sIndexItem, sKeyword ))
            {
                sKeyword = sIndexItem;
                bRet = true;
            }
        }
    }
 
    return bRet;
}
 
 
void IndexTabPage_Impl::OpenKeyword()
{
    if ( !sKeyword.isEmpty() )
    {
        m_pIndexCB->SetText( sKeyword );
        m_pIndexCB->GetDoubleClickHdl().Call( *m_pIndexCB );
        sKeyword.clear();
    }
}
 
// class SearchBox_Impl --------------------------------------------------
 
extern "C" SAL_DLLPUBLIC_EXPORT void makeSearchBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap &)
{
    WinBits nWinBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_SIMPLEMODE|WB_DROPDOWN;
    VclPtrInstance<SearchBox_Impl> pComboBox(pParent, nWinBits);
    pComboBox->EnableAutoSize(true);
    rRet = pComboBox;
}
 
bool SearchBox_Impl::PreNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;
    if ( !IsInDropDown() &&
         rNEvt.GetWindow() == GetSubEdit() &&
         rNEvt.GetType() == MouseNotifyEvent::KEYINPUT &&
         KEY_RETURN == rNEvt.GetKeyEvent()->GetKeyCode().GetCode() )
    {
        aSearchLink.Call( nullptr );
        bHandled = true;
    }
    return bHandled || ComboBox::PreNotify( rNEvt );
}
 
 
void SearchBox_Impl::Select()
{
    if ( !IsTravelSelect() )
        aSearchLink.Call( nullptr );
}
 
// class SearchResultsBox_Impl -------------------------------------------
 
extern "C" SAL_DLLPUBLIC_EXPORT void makeSearchResultsBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
    WinBits nWinBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
    OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
    if (!sBorder.isEmpty())
       nWinBits |= WB_BORDER;
    VclPtrInstance<SearchResultsBox_Impl> pListBox(pParent, nWinBits);
    pListBox->EnableAutoSize(true);
    rRet = pListBox;
}
 
bool SearchResultsBox_Impl::EventNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;
    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT &&
         KEY_RETURN == rNEvt.GetKeyEvent()->GetKeyCode().GetCode() )
    {
        GetDoubleClickHdl().Call( *this );
        bHandled = true;
    }
 
    return bHandled || ListBox::EventNotify( rNEvt );
}
 
// class SearchTabPage_Impl ----------------------------------------------
 
SearchTabPage_Impl::SearchTabPage_Impl(vcl::Window* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
    : HelpTabPage_Impl(pParent, _pIdxWin, "HelpSearchPage",
        "sfx/ui/helpsearchpage.ui")
    , xBreakIterator(vcl::unohelper::CreateBreakIterator())
{
    get(m_pSearchED, "search");
    get(m_pSearchBtn, "find");
    get(m_pFullWordsCB, "completewords");
    get(m_pScopeCB, "headings");
    get(m_pResultsLB, "results");
    Size aSize(LogicToPixel(Size(128 , 30), MapMode(MapUnit::MapAppFont)));
    m_pResultsLB->set_width_request(aSize.Width());
    m_pResultsLB->set_height_request(aSize.Height());
    get(m_pOpenBtn, "display");
 
    m_pSearchED->SetSearchLink( LINK( this, SearchTabPage_Impl, SearchHdl ) );
    m_pSearchBtn->SetClickHdl(LINK( this, SearchTabPage_Impl, ClickHdl ));
    m_pSearchED->SetModifyHdl( LINK( this, SearchTabPage_Impl, ModifyHdl ) );
    m_pOpenBtn->SetClickHdl( LINK( this, SearchTabPage_Impl, OpenHdl ) );
 
    SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
    if ( aViewOpt.Exists() )
    {
        OUString aUserData;
        Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
        if ( aUserItem >>= aUserData )
        {
            sal_Int32 nIdx {0};
            bool bChecked = aUserData.getToken(0, ';', nIdx).toInt32() == 1;
            m_pFullWordsCB->Check( bChecked );
            bChecked = aUserData.getToken(0, ';', nIdx).toInt32() == 1;
            m_pScopeCB->Check( bChecked );
 
            while ( nIdx > 0 )
            {
                m_pSearchED->InsertEntry( INetURLObject::decode(
                    aUserData.getToken(0, ';', nIdx),
                    INetURLObject::DecodeMechanism::WithCharset ) );
            }
        }
    }
 
    ModifyHdl(*m_pSearchED);
}
 
SearchTabPage_Impl::~SearchTabPage_Impl()
{
    disposeOnce();
}
 
void SearchTabPage_Impl::dispose()
{
    SvtViewOptions aViewOpt( EViewType::TabPage, CONFIGNAME_SEARCHPAGE );
    OUStringBuffer aUserData;
    aUserData.append(OUString::number( m_pFullWordsCB->IsChecked() ? 1 : 0 ))
        .append(";")
        .append(OUString::number( m_pScopeCB->IsChecked() ? 1 : 0 ));
    sal_Int32 nCount = std::min( m_pSearchED->GetEntryCount(), sal_Int32(10) );  // save only 10 entries
 
    for ( sal_Int32 i = 0; i < nCount; ++i )
    {
        aUserData.append(";").append(INetURLObject::encode(
            m_pSearchED->GetEntry(i),
            INetURLObject::PART_UNO_PARAM_VALUE,
            INetURLObject::EncodeMechanism::All ));
    }
 
    Any aUserItem = makeAny( aUserData.makeStringAndClear() );
    aViewOpt.SetUserItem( USERITEM_NAME, aUserItem );
 
    m_pSearchED.clear();
    m_pSearchBtn.clear();
    m_pFullWordsCB.clear();
    m_pScopeCB.clear();
    m_pResultsLB.clear();
    m_pOpenBtn.clear();
    HelpTabPage_Impl::dispose();
}
 
 
void SearchTabPage_Impl::ClearSearchResults()
{
    const sal_Int32 nCount = m_pResultsLB->GetEntryCount();
    for ( sal_Int32 i = 0; i < nCount; ++i )
        delete static_cast<OUString*>(m_pResultsLB->GetEntryData(i));
    m_pResultsLB->Clear();
    m_pResultsLB->Update();
}
 
 
void SearchTabPage_Impl::RememberSearchText( const OUString& rSearchText )
{
    for ( sal_Int32 i = 0; i < m_pSearchED->GetEntryCount(); ++i )
    {
        if ( rSearchText == m_pSearchED->GetEntry(i) )
        {
            m_pSearchED->RemoveEntryAt(i);
            break;
        }
    }
 
    m_pSearchED->InsertEntry( rSearchText, 0 );
}
 
 
IMPL_LINK_NOARG(SearchTabPage_Impl, ClickHdl, Button*, void)
{
    SearchHdl(nullptr);
}
 
IMPL_LINK_NOARG(SearchTabPage_Impl, SearchHdl, LinkParamNone*, void)
{
    OUString aSearchText = comphelper::string::strip(m_pSearchED->GetText(), ' ');
    if ( !aSearchText.isEmpty() )
    {
        EnterWait();
        ClearSearchResults();
        RememberSearchText( aSearchText );
        OUStringBuffer aSearchURL(HELP_URL);
        aSearchURL.append(aFactory);
        aSearchURL.append(HELP_SEARCH_TAG);
        if ( !m_pFullWordsCB->IsChecked() )
            aSearchText = sfx2::PrepareSearchString( aSearchText, xBreakIterator, true );
        aSearchURL.append(aSearchText);
        AppendConfigToken(aSearchURL, false);
        if ( m_pScopeCB->IsChecked() )
            aSearchURL.append("&Scope=Heading");
        std::vector< OUString > aFactories = SfxContentHelper::GetResultSet(aSearchURL.makeStringAndClear());
        for (const OUString & rRow : aFactories)
        {
            sal_Int32 nIdx = 0;
            OUString aTitle = rRow.getToken( 0, '\t', nIdx );
            nIdx = 0;
            OUString* pURL = new OUString( rRow.getToken( 2, '\t', nIdx ) );
            const sal_Int32 nPos = m_pResultsLB->InsertEntry( aTitle );
            m_pResultsLB->SetEntryData( nPos, pURL );
        }
        LeaveWait();
 
        if ( aFactories.empty() )
        {
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                                     VclMessageType::Info, VclButtonsType::Ok,
                                                                     SfxResId(STR_INFO_NOSEARCHRESULTS)));
            xBox->run();
        }
    }
}
 
IMPL_LINK_NOARG(SearchTabPage_Impl, OpenHdl, Button*, void)
{
    m_pResultsLB->GetDoubleClickHdl().Call(*m_pResultsLB);
}
 
IMPL_LINK_NOARG(SearchTabPage_Impl, ModifyHdl, Edit&, void)
{
    OUString aSearchText = comphelper::string::strip(m_pSearchED->GetText(), ' ');
    m_pSearchBtn->Enable(!aSearchText.isEmpty());
}
 
void SearchTabPage_Impl::ActivatePage()
{
    if ( !m_pIdxWin->WasCursorLeftOrRight() )
        m_pSearchED->GrabFocus();
}
 
Control* SearchTabPage_Impl::GetLastFocusControl()
{
    return m_pOpenBtn;
}
 
void SearchTabPage_Impl::SetDoubleClickHdl( const Link<ListBox&,void>& rLink )
{
    m_pResultsLB->SetDoubleClickHdl( rLink );
}
 
 
OUString SearchTabPage_Impl::GetSelectedEntry() const
{
    OUString aRet;
    OUString* pData = static_cast<OUString*>(m_pResultsLB->GetSelectedEntryData());
    if ( pData )
        aRet = *pData;
    return aRet;
}
 
 
void SearchTabPage_Impl::ClearPage()
{
    ClearSearchResults();
    m_pSearchED->SetText( OUString() );
}
 
 
bool SearchTabPage_Impl::OpenKeyword( const OUString& rKeyword )
{
    bool bRet = false;
    m_pSearchED->SetText( rKeyword );
    SearchHdl( nullptr );
    if ( m_pResultsLB->GetEntryCount() > 0 )
    {
        // found keyword -> open it
        m_pResultsLB->SelectEntryPos(0);
        OpenHdl( nullptr );
        bRet = true;
    }
 
    return bRet;
}
 
// class BookmarksTabPage_Impl -------------------------------------------
 
void GetBookmarkEntry_Impl
(
    Sequence< PropertyValue >& aBookmarkEntry,
    OUString& rTitle,
    OUString& rURL
)
{
    for ( int i = 0; i < aBookmarkEntry.getLength(); i++ )
    {
        PropertyValue aValue = aBookmarkEntry[i];
        if ( aValue.Name == HISTORY_PROPERTYNAME_URL )
            aValue.Value >>= rURL;
        else if ( aValue.Name == HISTORY_PROPERTYNAME_TITLE )
            aValue.Value >>= rTitle;
    }
}
 
BookmarksBox_Impl::BookmarksBox_Impl(vcl::Window* pParent, WinBits nStyle)
    : ListBox(pParent, nStyle)
{
}
 
extern "C" SAL_DLLPUBLIC_EXPORT void makeBookmarksBox(VclPtr<vcl::Window> & rRet, VclPtr<vcl::Window> & pParent, VclBuilder::stringmap & rMap)
{
    WinBits nWinBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_SIMPLEMODE;
    OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
    if (!sBorder.isEmpty())
       nWinBits |= WB_BORDER;
    VclPtrInstance<BookmarksBox_Impl> pListBox(pParent, nWinBits);
    pListBox->EnableAutoSize(true);
    rRet = pListBox;
}
 
BookmarksBox_Impl::~BookmarksBox_Impl()
{
    disposeOnce();
}
 
void BookmarksBox_Impl::dispose()
{
    // save bookmarks to configuration
    SvtHistoryOptions aHistOpt;
    aHistOpt.Clear( eHELPBOOKMARKS );
    const sal_Int32 nCount = GetEntryCount();
    for ( sal_Int32 i = 0; i < nCount; ++i )
    {
        OUString aTitle = GetEntry(i);
        OUString* pURL = static_cast<OUString*>(GetEntryData(i));
        aHistOpt.AppendItem(eHELPBOOKMARKS, *pURL, "", aTitle, boost::none);
        delete pURL;
    }
    ListBox::dispose();
}
 
 
void BookmarksBox_Impl::DoAction( sal_uInt16 nAction )
{
    switch ( nAction )
    {
        case MID_OPEN :
            GetDoubleClickHdl().Call( *this );
            break;
 
        case MID_RENAME :
           {
            sal_Int32 nPos = GetSelectedEntryPos();
            if ( nPos != LISTBOX_ENTRY_NOTFOUND )
            {
                SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), true);
                aDlg.SetTitle(GetEntry(nPos));
                if (aDlg.run() == RET_OK)
                {
                    OUString* pURL = static_cast<OUString*>(GetEntryData( nPos ));
                    RemoveEntry( nPos );
                    OUString aImageURL = IMAGE_URL;
                    aImageURL += INetURLObject( *pURL ).GetHost();
                    nPos = InsertEntry( aDlg.GetTitle(), SvFileInformationManager::GetImage( INetURLObject(aImageURL) ) );
                    SetEntryData( nPos, new OUString( *pURL ) );
                    SelectEntryPos( nPos );
                    delete pURL;
                }
            }
            break;
        }
 
        case MID_DELETE :
        {
            sal_Int32 nPos = GetSelectedEntryPos();
            if ( nPos != LISTBOX_ENTRY_NOTFOUND )
            {
                RemoveEntry( nPos );
                const sal_Int32 nCount = GetEntryCount();
                if ( nCount )
                {
                    if ( nPos >= nCount )
                        nPos = nCount - 1;
                    SelectEntryPos( nPos );
                }
            }
            break;
        }
    }
}
 
 
bool BookmarksBox_Impl::EventNotify( NotifyEvent& rNEvt )
{
    bool bRet = false;
    MouseNotifyEvent nType = rNEvt.GetType();
    if ( MouseNotifyEvent::KEYINPUT == nType )
    {
        sal_uInt16 nCode = rNEvt.GetKeyEvent()->GetKeyCode().GetCode();
        if ( KEY_DELETE == nCode && GetEntryCount() > 0 )
        {
            DoAction( MID_DELETE );
            bRet = true;
        }
        else if ( KEY_RETURN == nCode )
        {
            GetDoubleClickHdl().Call( *this );
            bRet = true;
        }
    }
    else if ( MouseNotifyEvent::COMMAND == nType )
    {
        const CommandEvent* pCEvt = rNEvt.GetCommandEvent();
        if ( pCEvt->GetCommand() == CommandEventId::ContextMenu )
        {
            VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "sfx/ui/bookmarkmenu.ui", "");
            VclPtr<PopupMenu> aMenu(aBuilder.get_menu("menu"));
            sal_uInt16 nId = aMenu->Execute(this, pCEvt->GetMousePosPixel());
            if (nId != MENU_ITEM_NOTFOUND)
            {
                OString sIdent = aMenu->GetCurItemIdent();
                if (sIdent == "display")
                    DoAction(MID_OPEN);
                else if (sIdent == "rename")
                    DoAction(MID_RENAME);
                else if (sIdent == "delete")
                    DoAction(MID_DELETE);
            }
            bRet = true;
        }
    }
 
    return bRet || ListBox::EventNotify( rNEvt );
}
 
// class BookmarksTabPage_Impl -------------------------------------------
 
BookmarksTabPage_Impl::BookmarksTabPage_Impl(vcl::Window* pParent, SfxHelpIndexWindow_Impl* _pIdxWin)
    : HelpTabPage_Impl(pParent, _pIdxWin, "HelpBookmarkPage",
        "sfx/ui/helpbookmarkpage.ui")
{
    get(m_pBookmarksPB, "display");
    get(m_pBookmarksBox, "bookmarks");
    Size aSize(LogicToPixel(Size(120 , 200), MapMode(MapUnit::MapAppFont)));
    m_pBookmarksBox->set_width_request(aSize.Width());
    m_pBookmarksBox->set_height_request(aSize.Height());
 
    m_pBookmarksPB->SetClickHdl( LINK( this, BookmarksTabPage_Impl, OpenHdl ) );
 
    // load bookmarks from configuration
    Sequence< Sequence< PropertyValue > > aBookmarkSeq;
    aBookmarkSeq = SvtHistoryOptions().GetList( eHELPBOOKMARKS );
 
    OUString aTitle;
    OUString aURL;
 
    sal_uInt32 i, nCount = aBookmarkSeq.getLength();
    for ( i = 0; i < nCount; ++i )
    {
        GetBookmarkEntry_Impl( aBookmarkSeq[i], aTitle, aURL );
        AddBookmarks( aTitle, aURL );
    }
}
 
BookmarksTabPage_Impl::~BookmarksTabPage_Impl()
{
    disposeOnce();
}
 
void BookmarksTabPage_Impl::dispose()
{
    m_pBookmarksBox.clear();
    m_pBookmarksPB.clear();
    HelpTabPage_Impl::dispose();
}
 
 
IMPL_LINK_NOARG(BookmarksTabPage_Impl, OpenHdl, Button*, void)
{
    m_pBookmarksBox->GetDoubleClickHdl().Call(*m_pBookmarksBox);
}
 
void BookmarksTabPage_Impl::ActivatePage()
{
    if ( !m_pIdxWin->WasCursorLeftOrRight() )
        SetFocusOnBox();
}
 
Control* BookmarksTabPage_Impl::GetLastFocusControl()
{
    return m_pBookmarksPB;
}
 
void BookmarksTabPage_Impl::SetDoubleClickHdl( const Link<ListBox&,void>& rLink )
{
    m_pBookmarksBox->SetDoubleClickHdl(rLink);
}
 
OUString BookmarksTabPage_Impl::GetSelectedEntry() const
{
    OUString aRet;
    OUString* pData = static_cast<OUString*>(m_pBookmarksBox->GetSelectedEntryData());
    if ( pData )
        aRet = *pData;
    return aRet;
}
 
 
void BookmarksTabPage_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL )
{
    OUString aImageURL = IMAGE_URL;
    aImageURL += INetURLObject( rURL ).GetHost();
    const sal_Int32 nPos = m_pBookmarksBox->InsertEntry( rTitle, SvFileInformationManager::GetImage( INetURLObject(aImageURL) ) );
    m_pBookmarksBox->SetEntryData( nPos, new OUString( rURL ) );
}
 
OUString SfxHelpWindow_Impl::buildHelpURL(const OUString& sFactory        ,
                                          const OUString& sContent        ,
                                          const OUString& sAnchor)
{
    OUStringBuffer sHelpURL(256);
    sHelpURL.append(HELP_URL);
    sHelpURL.append(sFactory);
    sHelpURL.append(sContent);
    AppendConfigToken(sHelpURL, true/*bUseQuestionMark*/);
    if (!sAnchor.isEmpty())
        sHelpURL.append(sAnchor);
    return sHelpURL.makeStringAndClear();
}
 
void SfxHelpWindow_Impl::loadHelpContent(const OUString& sHelpURL, bool bAddToHistory)
{
    Reference< XComponentLoader > xLoader(getTextFrame(), UNO_QUERY);
    if (!xLoader.is())
        return;
 
    // If a print job runs do not open a new page
    Reference< XFrame2 >     xTextFrame      = pTextWin->getFrame();
    Reference< XController > xTextController ;
    if (xTextFrame.is())
        xTextController = xTextFrame->getController ();
    if ( xTextController.is() && !xTextController->suspend( true ) )
    {
        xTextController->suspend( false );
        return;
    }
 
    // save url to history
    if (bAddToHistory)
        pHelpInterceptor->addURL(sHelpURL);
 
    if ( !IsWait() )
        EnterWait();
    bool bSuccess = false;
// TODO implement locale fallback ... see below    while(true)
    {
        try
        {
            Reference< XComponent > xContent = xLoader->loadComponentFromURL(sHelpURL, "_self", 0, Sequence< PropertyValue >());
            if (xContent.is())
            {
                bSuccess = true;
            }
        }
        catch(const RuntimeException&)
            { throw; }
        catch(const Exception&)
            { /*break;*/ }
 
        /* TODO try next locale ...
                no further locale available? => break loop and show error page
        */
    }
    openDone(sHelpURL, bSuccess);
    if ( IsWait() )
        LeaveWait();
}
 
SfxHelpIndexWindow_Impl::SfxHelpIndexWindow_Impl(SfxHelpWindow_Impl* _pParent)
    : Window(_pParent, 0)
    , aIdle("sfx2 appl SfxHelpIndexWindow_Impl")
    , aIndexKeywordLink(LINK(this, SfxHelpIndexWindow_Impl, KeywordHdl))
    , pParentWin(_pParent)
    , pCPage(nullptr)
    , pIPage(nullptr)
    , pSPage(nullptr)
    , pBPage(nullptr)
    , bWasCursorLeftOrRight(false)
    , bIsInitDone(false)
{
    m_pUIBuilder.reset(new VclBuilder(this, getUIRootDir(), "sfx/ui/helpcontrol.ui", "HelpControl"));
    get(m_pActiveLB, "active");
    get(m_pTabCtrl, "tabcontrol");
 
    sfx2::AddToTaskPaneList( this );
 
    m_pTabCtrl->SetActivatePageHdl( LINK( this, SfxHelpIndexWindow_Impl, ActivatePageHdl ) );
 
    OString sPageId("index");
    SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN );
    if ( aViewOpt.Exists() )
        sPageId = aViewOpt.GetPageID();
    m_pTabCtrl->SetCurPageId(m_pTabCtrl->GetPageId(sPageId));
    ActivatePageHdl( m_pTabCtrl );
    m_pActiveLB->SetSelectHdl( LINK( this, SfxHelpIndexWindow_Impl, SelectHdl ) );
 
    aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, InitHdl ) );
    aIdle.Start();
 
    Show();
}
 
 
SfxHelpIndexWindow_Impl::~SfxHelpIndexWindow_Impl()
{
    disposeOnce();
}
 
void SfxHelpIndexWindow_Impl::dispose()
{
    sfx2::RemoveFromTaskPaneList( this );
 
    pCPage.disposeAndClear();
    pIPage.disposeAndClear();
    pSPage.disposeAndClear();
    pBPage.disposeAndClear();
 
    for ( sal_Int32 i = 0; i < m_pActiveLB->GetEntryCount(); ++i )
        delete static_cast<OUString*>(m_pActiveLB->GetEntryData(i));
 
    SvtViewOptions aViewOpt( EViewType::TabDialog, CONFIGNAME_INDEXWIN );
    aViewOpt.SetPageID(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
 
    disposeBuilder();
    m_pActiveLB.clear();
    m_pTabCtrl.clear();
    pParentWin.clear();
    vcl::Window::dispose();
}
 
 
void SfxHelpIndexWindow_Impl::Initialize()
{
    OUStringBuffer aHelpURL(HELP_URL);
    AppendConfigToken(aHelpURL, true);
    std::vector<OUString> aFactories = SfxContentHelper::GetResultSet(aHelpURL.makeStringAndClear());
    for (const OUString & rRow : aFactories)
    {
        sal_Int32 nIdx = 0;
        OUString aTitle = rRow.getToken( 0, '\t', nIdx );
        nIdx = 0;
        OUString aURL = rRow.getToken( 2, '\t', nIdx );
        OUString* pFactory = new OUString( INetURLObject( aURL ).GetHost() );
        const sal_Int32 nPos = m_pActiveLB->InsertEntry( aTitle );
        m_pActiveLB->SetEntryData( nPos, pFactory );
    }
 
    m_pActiveLB->SetDropDownLineCount( static_cast<sal_uInt16>(aFactories.size()) );
    if ( m_pActiveLB->GetSelectedEntryPos() == LISTBOX_ENTRY_NOTFOUND )
        SetActiveFactory();
}
 
 
void SfxHelpIndexWindow_Impl::SetActiveFactory()
{
    DBG_ASSERT( pIPage, "index page not initialized" );
    if ( !bIsInitDone && !m_pActiveLB->GetEntryCount() )
    {
        aIdle.Stop();
        InitHdl( nullptr );
    }
 
    for ( sal_Int32 i = 0; i < m_pActiveLB->GetEntryCount(); ++i )
    {
        OUString* pFactory = static_cast<OUString*>(m_pActiveLB->GetEntryData(i));
        *pFactory = pFactory->toAsciiLowerCase();
        if ( *pFactory == pIPage->GetFactory() )
        {
            if ( m_pActiveLB->GetSelectedEntryPos() != i )
            {
                m_pActiveLB->SelectEntryPos(i);
                aSelectFactoryLink.Call( nullptr );
            }
            break;
        }
    }
}
 
 
HelpTabPage_Impl* SfxHelpIndexWindow_Impl::GetCurrentPage( sal_uInt16& rCurId )
{
    rCurId = m_pTabCtrl->GetCurPageId();
    HelpTabPage_Impl* pPage = nullptr;
 
    OString sName(m_pTabCtrl->GetPageName(rCurId));
 
    if (sName == "contents")
    {
        pPage = GetContentPage();
    }
    else if (sName == "index")
    {
        pPage = GetIndexPage();
    }
    else if (sName == "find")
    {
        pPage = GetSearchPage();
    }
    else if (sName == "bookmarks")
    {
        pPage = GetBookmarksPage();
    }
 
    DBG_ASSERT( pPage, "SfxHelpIndexWindow_Impl::GetCurrentPage(): no current page" );
    return pPage;
}
 
IMPL_LINK( SfxHelpIndexWindow_Impl, ActivatePageHdl, TabControl *, pTabCtrl, void )
{
    sal_uInt16 nId = 0;
    TabPage* pPage = GetCurrentPage( nId );
    pTabCtrl->SetTabPage( nId, pPage );
}
 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectHdl, ListBox&, void)
{
    aIdle.Start();
}
 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, InitHdl, Timer *, void)
{
    bIsInitDone = true;
    Initialize();
 
    // now use the timer for selection
    aIdle.SetInvokeHandler( LINK( this, SfxHelpIndexWindow_Impl, SelectFactoryHdl ) );
    aIdle.SetPriority( TaskPriority::LOWEST );
}
 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, SelectFactoryHdl, Timer *, void)
{
    OUString* pFactory = static_cast<OUString*>(m_pActiveLB->GetSelectedEntryData());
    if ( pFactory )
    {
        SetFactory( pFactory->toAsciiLowerCase(), false );
        aSelectFactoryLink.Call( this );
    }
}
 
IMPL_LINK_NOARG(SfxHelpIndexWindow_Impl, KeywordHdl, IndexTabPage_Impl&, void)
{
    // keyword found on index?
    bool bIndex = pIPage->HasKeyword();
 
    if( !bIndex)
        bIndex = pIPage->HasKeywordIgnoreCase();
    // then set index or search page as current.
    sal_uInt16 nPageId = bIndex ? m_pTabCtrl->GetPageId("index") : m_pTabCtrl->GetPageId("find");
    if ( nPageId != m_pTabCtrl->GetCurPageId() )
    {
        m_pTabCtrl->SetCurPageId( nPageId );
        ActivatePageHdl( m_pTabCtrl );
    }
 
    // at last we open the keyword
    if ( bIndex )
        pIPage->OpenKeyword();
    else if ( !pSPage->OpenKeyword( sKeyword ) )
        pParentWin->ShowStartPage();
}
 
IMPL_LINK(SfxHelpIndexWindow_Impl, IndexTabPageDoubleClickHdl, ComboBox&, rBox, void)
{
    aPageDoubleClickLink.Call(&rBox);
}
 
void SfxHelpIndexWindow_Impl::Resize()
{
    vcl::Window *pChild = GetWindow(GetWindowType::FirstChild);
    if (!pChild)
        return;
    VclContainer::setLayoutAllocation(*pChild, Point(0,0), GetSizePixel());
}
 
Size SfxHelpIndexWindow_Impl::GetOptimalSize() const
{
    const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild);
    if (!pChild)
        return Window::GetOptimalSize();
    return VclContainer::getLayoutRequisition(*pChild);
}
 
bool SfxHelpIndexWindow_Impl::PreNotify(NotifyEvent& rNEvt)
{
    bool bDone = false;
    MouseNotifyEvent nType = rNEvt.GetType();
    if ( MouseNotifyEvent::KEYINPUT == nType && rNEvt.GetKeyEvent() )
    {
        const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode();
        sal_uInt16 nCode = rKeyCode.GetCode();
 
        if (  KEY_TAB == nCode )
        {
            // don't exit index pane with <TAB>
            sal_uInt16 nPageId = 0;
            HelpTabPage_Impl* pCurPage = GetCurrentPage( nPageId );
            Control* pControl = pCurPage->GetLastFocusControl();
            bool bShift = rKeyCode.IsShift();
            bool bCtrl = rKeyCode.IsMod1();
            if ( !bCtrl && bShift && m_pActiveLB->HasChildPathFocus() )
            {
                pControl->GrabFocus();
                bDone = true;
            }
            else if ( !bCtrl && !bShift && pControl->HasChildPathFocus() )
            {
                m_pActiveLB->GrabFocus();
                bDone = true;
            }
            else if ( bCtrl )
            {
                sal_uInt16 nPagePos = m_pTabCtrl->GetPagePos(nPageId);
                sal_uInt16 nPageCount = m_pTabCtrl->GetPageCount();
 
                ++nPageId;
                // <CTRL><TAB> moves through the pages
                if (nPagePos >= nPageCount)
                    nPagePos = 0;
 
                m_pTabCtrl->SetCurPageId(m_pTabCtrl->GetPageId(nPagePos));
                ActivatePageHdl( m_pTabCtrl );
                bDone = true;
            }
         }
        else if ( m_pTabCtrl->HasFocus() && ( KEY_LEFT == nCode || KEY_RIGHT == nCode ) )
        {
            bWasCursorLeftOrRight = true;
        }
    }
 
    return bDone || Window::PreNotify( rNEvt );
}
 
 
void SfxHelpIndexWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );
 
    if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
           ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) &&
         ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
    {
        SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) );
    }
}
 
 
void SfxHelpIndexWindow_Impl::SetDoubleClickHdl( const Link<Control*,bool>& rLink )
{
    aPageDoubleClickLink = rLink;
}
 
IMPL_LINK(SfxHelpIndexWindow_Impl, ContentTabPageDoubleClickHdl, SvTreeListBox*, p, bool)
{
    aPageDoubleClickLink.Call(p);
    return true;
}
 
IMPL_LINK(SfxHelpIndexWindow_Impl, TabPageDoubleClickHdl, ListBox&, r, void)
{
    aPageDoubleClickLink.Call(&r);
}
 
 
void SfxHelpIndexWindow_Impl::SetFactory( const OUString& rFactory, bool bActive )
{
    if ( !rFactory.isEmpty() )
    {
        GetIndexPage()->SetFactory( rFactory );
        // the index page did a check if rFactory is valid,
        // so the index page always returns a valid factory
        GetSearchPage()->SetFactory( GetIndexPage()->GetFactory() );
        if ( bActive )
            SetActiveFactory();
    }
}
 
OUString SfxHelpIndexWindow_Impl::GetSelectedEntry() const
{
    OUString sRet;
 
    OString sName(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
 
    if (sName == "contents")
    {
        sRet = pCPage->GetSelectedEntry();
    }
    else if (sName == "index")
    {
        sRet = pIPage->GetSelectedEntry();
    }
    else if (sName == "find")
    {
        sRet = pSPage->GetSelectedEntry();
    }
    else if (sName == "bookmarks")
    {
        sRet = pBPage->GetSelectedEntry();
    }
 
    return sRet;
}
 
void SfxHelpIndexWindow_Impl::AddBookmarks( const OUString& rTitle, const OUString& rURL )
{
    GetBookmarksPage()->AddBookmarks( rTitle, rURL );
}
 
 
bool SfxHelpIndexWindow_Impl::IsValidFactory( const OUString& _rFactory )
{
    bool bValid = false;
    for ( sal_Int32 i = 0; i < m_pActiveLB->GetEntryCount(); ++i )
    {
        OUString* pFactory = static_cast<OUString*>(m_pActiveLB->GetEntryData(i));
        if ( *pFactory == _rFactory )
        {
            bValid = true;
            break;
        }
    }
    return bValid;
}
 
 
void SfxHelpIndexWindow_Impl::ClearSearchPage()
{
    if ( pSPage )
        pSPage->ClearPage();
}
 
void SfxHelpIndexWindow_Impl::GrabFocusBack()
{
    OString sName(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
 
    if (sName == "contents" && pCPage)
        pCPage->SetFocusOnBox();
    else if (sName == "index" && pIPage)
        pIPage->SetFocusOnBox();
    else if (sName == "find" && pSPage)
        pSPage->SetFocusOnBox();
    else if (sName == "bookmarks" && pBPage)
        pBPage->SetFocusOnBox();
}
 
bool SfxHelpIndexWindow_Impl::HasFocusOnEdit() const
{
    bool bRet = false;
    OString sName(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
    if (sName == "index" && pIPage)
        bRet = pIPage->HasFocusOnEdit();
    else if (sName == "find" && pSPage)
        bRet = pSPage->HasFocusOnEdit();
    return bRet;
}
 
 
OUString SfxHelpIndexWindow_Impl::GetSearchText() const
{
    OUString sRet;
    OString sName(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
    if (sName == "find" && pSPage)
        sRet = pSPage->GetSearchText();
    return sRet;
}
 
bool SfxHelpIndexWindow_Impl::IsFullWordSearch() const
{
    bool bRet = false;
    OString sName(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
    if (sName == "find" && pSPage)
        bRet = pSPage->IsFullWordSearch();
    return bRet;
}
 
void SfxHelpIndexWindow_Impl::OpenKeyword( const OUString& rKeyword )
{
    sKeyword = rKeyword;
    DBG_ASSERT( pIPage, "invalid index page" );
    pIPage->SetKeyword( sKeyword );
}
 
void SfxHelpIndexWindow_Impl::SelectExecutableEntry()
{
    OString sName(m_pTabCtrl->GetPageName(m_pTabCtrl->GetCurPageId()));
    if (sName == "index" && pIPage )
        pIPage->SelectExecutableEntry();
}
 
// class TextWin_Impl ----------------------------------------------------
 
TextWin_Impl::TextWin_Impl( vcl::Window* p ) : DockingWindow( p, 0 )
{
}
 
bool TextWin_Impl::EventNotify( NotifyEvent& rNEvt )
{
    if( ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) && rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_TAB )
        return GetParent()->EventNotify( rNEvt );
    else
        return DockingWindow::EventNotify( rNEvt );
}
 
 
// remove docking area acceptor from layoutmanager, so it will not layout anything further .-)
static void lcl_disableLayoutOfFrame(const Reference< XFrame2 >& xFrame)
{
    xFrame->setLayoutManager( Reference< XLayoutManager >() );
}
 
// class SfxHelpTextWindow_Impl ------------------------------------------
 
SfxHelpTextWindow_Impl::SfxHelpTextWindow_Impl( SfxHelpWindow_Impl* pParent ) :
 
    Window( pParent, WB_CLIPCHILDREN | WB_TABSTOP | WB_DIALOGCONTROL ),
 
    aToolBox            ( VclPtr<ToolBox>::Create(this, 0) ),
    aOnStartupCB        ( VclPtr<CheckBox>::Create(this, WB_HIDE | WB_TABSTOP) ),
    aSelectIdle         ( "sfx2 appl SfxHelpTextWindow_Impl Select" ),
    aIndexOnImage       (BitmapEx(BMP_HELP_TOOLBOX_INDEX_ON)),
    aIndexOffImage      (BitmapEx(BMP_HELP_TOOLBOX_INDEX_OFF)),
    aIndexOnText        ( SfxResId( STR_HELP_BUTTON_INDEX_ON ) ),
    aIndexOffText       ( SfxResId( STR_HELP_BUTTON_INDEX_OFF ) ),
    aOnStartupText      ( SfxResId( RID_HELP_ONSTARTUP_TEXT ) ),
    pHelpWin            ( pParent ),
    pTextWin            ( VclPtr<TextWin_Impl>::Create( this ) ),
    pSrchDlg            ( nullptr ),
    nMinPos             ( 0 ),
    bIsDebug            ( false ),
    bIsIndexOn          ( false ),
    bIsInClose          ( false ),
    bIsFullWordSearch   ( false )
{
    aOnStartupCB->SetSizePixel(aOnStartupCB->PixelToLogic(Size(200, 10), MapMode(MapUnit::MapAppFont)));
 
    sfx2::AddToTaskPaneList( aToolBox.get() );
 
    xFrame = Frame::create( ::comphelper::getProcessComponentContext() );
    xFrame->initialize( VCLUnoHelper::GetInterface ( pTextWin ) );
    xFrame->setName( "OFFICE_HELP" );
    lcl_disableLayoutOfFrame(xFrame);
 
    aToolBox->SetHelpId( HID_HELP_TOOLBOX );
 
    aToolBox->InsertItem( TBI_INDEX, aIndexOffText );
    aToolBox->SetHelpId( TBI_INDEX, HID_HELP_TOOLBOXITEM_INDEX );
    aToolBox->InsertSeparator();
    aToolBox->InsertItem( TBI_BACKWARD, SfxResId( STR_HELP_BUTTON_PREV ) );
    aToolBox->SetHelpId( TBI_BACKWARD, HID_HELP_TOOLBOXITEM_BACKWARD );
    aToolBox->InsertItem( TBI_FORWARD, SfxResId( STR_HELP_BUTTON_NEXT ) );
    aToolBox->SetHelpId( TBI_FORWARD, HID_HELP_TOOLBOXITEM_FORWARD );
    aToolBox->InsertItem( TBI_START, SfxResId( STR_HELP_BUTTON_START ) );
    aToolBox->SetHelpId( TBI_START, HID_HELP_TOOLBOXITEM_START );
    aToolBox->InsertSeparator();
    aToolBox->InsertItem( TBI_PRINT, SfxResId( STR_HELP_BUTTON_PRINT ) );
    aToolBox->SetHelpId( TBI_PRINT, HID_HELP_TOOLBOXITEM_PRINT );
    aToolBox->InsertItem( TBI_BOOKMARKS, SfxResId( STR_HELP_BUTTON_ADDBOOKMARK ) );
    aToolBox->SetHelpId( TBI_BOOKMARKS, HID_HELP_TOOLBOXITEM_BOOKMARKS );
    aToolBox->InsertItem( TBI_SEARCHDIALOG, SfxResId( STR_HELP_BUTTON_SEARCHDIALOG ) );
    aToolBox->SetHelpId( TBI_SEARCHDIALOG, HID_HELP_TOOLBOXITEM_SEARCHDIALOG );
 
    InitToolBoxImages();
    aToolBox->Show();
    InitOnStartupBox();
    aOnStartupCB->SetClickHdl( LINK( this, SfxHelpTextWindow_Impl, CheckHdl ) );
 
    aSelectIdle.SetInvokeHandler( LINK( this, SfxHelpTextWindow_Impl, SelectHdl ) );
    aSelectIdle.SetPriority( TaskPriority::LOWEST );
 
    char* pEnv = getenv( "help_debug" );
    if ( pEnv )
        bIsDebug = true;
 
    SvtMiscOptions().AddListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
}
 
SfxHelpTextWindow_Impl::~SfxHelpTextWindow_Impl()
{
    disposeOnce();
}
 
void SfxHelpTextWindow_Impl::dispose()
{
    sfx2::RemoveFromTaskPaneList( aToolBox.get() );
 
    bIsInClose = true;
    SvtMiscOptions().RemoveListenerLink( LINK( this, SfxHelpTextWindow_Impl, NotifyHdl ) );
    pSrchDlg.disposeAndClear();
    aToolBox.disposeAndClear();
    aOnStartupCB.disposeAndClear();
    pHelpWin.clear();
    pTextWin.disposeAndClear();
    vcl::Window::dispose();
}
 
 
bool SfxHelpTextWindow_Impl::HasSelection() const
{
    // is there any selection in the text and not only a cursor?
    bool bRet = false;
    Reference < XTextRange > xRange = getCursor();
    if ( xRange.is() )
    {
        Reference < XText > xText = xRange->getText();
        Reference < XTextCursor > xCursor = xText->createTextCursorByRange( xRange );
        bRet = !xCursor->isCollapsed();
    }
 
    return bRet;
}
 
 
void SfxHelpTextWindow_Impl::InitToolBoxImages()
{
    bool bLarge = SvtMiscOptions().AreCurrentSymbolsLarge();
 
    aIndexOnImage  = Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_INDEX_ON) : OUString(BMP_HELP_TOOLBOX_INDEX_ON)));
    aIndexOffImage = Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_INDEX_OFF) : OUString(BMP_HELP_TOOLBOX_INDEX_OFF)));
 
    aToolBox->SetItemImage( TBI_INDEX, bIsIndexOn ? aIndexOffImage : aIndexOnImage );
 
    aToolBox->SetItemImage( TBI_BACKWARD,
                           Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_PREV) : OUString(BMP_HELP_TOOLBOX_PREV)))
    );
 
    aToolBox->SetItemImage( TBI_FORWARD,
                           Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_NEXT) : OUString(BMP_HELP_TOOLBOX_NEXT)))
    );
 
    aToolBox->SetItemImage( TBI_START,
                           Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_START) : OUString(BMP_HELP_TOOLBOX_START)))
    );
 
    aToolBox->SetItemImage( TBI_PRINT,
                           Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_PRINT) : OUString(BMP_HELP_TOOLBOX_PRINT)))
    );
 
    aToolBox->SetItemImage( TBI_BOOKMARKS,
                           Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_BOOKMARKS) : OUString(BMP_HELP_TOOLBOX_BOOKMARKS)))
    );
 
    aToolBox->SetItemImage( TBI_SEARCHDIALOG,
                           Image(BitmapEx(bLarge ? OUString(BMP_HELP_TOOLBOX_L_SEARCHDIALOG) : OUString(BMP_HELP_TOOLBOX_SEARCHDIALOG)))
    );
 
    Size aSize = aToolBox->CalcWindowSizePixel();
    aSize.AdjustHeight(TOOLBOX_OFFSET );
    aToolBox->SetPosSizePixel( Point( 0, TOOLBOX_OFFSET ), aSize );
 
    SvtMiscOptions aMiscOptions;
    if ( aMiscOptions.GetToolboxStyle() != aToolBox->GetOutStyle() )
        aToolBox->SetOutStyle( aMiscOptions.GetToolboxStyle() );
}
 
 
void SfxHelpTextWindow_Impl::InitOnStartupBox()
{
    sCurrentFactory = SfxHelp::GetCurrentModuleIdentifier();
 
    Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
    OUString sPath( PATH_OFFICE_FACTORIES );
    sPath += sCurrentFactory;
    OUString sKey( KEY_HELP_ON_OPEN );
 
    // Attention: This check boy knows two states:
    // 1) Reading of the config key fails with an exception or by getting an empty Any (!) => check box must be hidden
    // 2) We read sal_True/sal_False => check box must be shown and enabled/disabled
 
    bool bHideBox = true;
    bool bHelpAtStartup = false;
    try
    {
        xConfiguration = ConfigurationHelper::openConfig(
            xContext, PACKAGE_SETUP, EConfigurationModes::Standard );
        if ( xConfiguration.is() )
        {
            Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, sKey );
            if (aAny >>= bHelpAtStartup)
                bHideBox = false;
        }
    }
    catch( Exception& )
    {
        bHideBox = true;
    }
 
    if ( bHideBox )
        aOnStartupCB->Hide();
    else
    {
        // detect module name
        OUString sModuleName;
 
        if ( xConfiguration.is() )
        {
            OUString sTemp;
            sKey = KEY_UI_NAME;
            try
            {
                Any aAny = ConfigurationHelper::readRelativeKey( xConfiguration, sPath, sKey );
                aAny >>= sTemp;
            }
            catch( Exception& )
            {
                SAL_WARN( "sfx.appl", "SfxHelpTextWindow_Impl::InitOnStartupBox(): unexpected exception" );
            }
            sModuleName = sTemp;
        }
 
        if ( !sModuleName.isEmpty() )
        {
            // set module name in checkbox text
            OUString sText( aOnStartupText );
            sText = sText.replaceFirst( "%MODULENAME", sModuleName );
            aOnStartupCB->SetText( sText );
            // and show it
            aOnStartupCB->Show();
            // set check state
            aOnStartupCB->Check( bHelpAtStartup );
            aOnStartupCB->SaveValue();
 
            // calculate and set optimal width of the onstartup checkbox
            OUString sCBText( "XXX" );
            sCBText += aOnStartupCB->GetText();
            long nTextWidth = aOnStartupCB->GetTextWidth( sCBText );
            Size aSize = aOnStartupCB->GetSizePixel();
            aSize.setWidth( nTextWidth );
            aOnStartupCB->SetSizePixel( aSize );
            SetOnStartupBoxPosition();
        }
 
        // set position of the checkbox
        Size a3Size = LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont));
        Size aTBSize = aToolBox->GetSizePixel();
        Size aCBSize = aOnStartupCB->GetSizePixel();
        Point aPnt = aToolBox->GetPosPixel();
        aPnt.AdjustX(aTBSize.Width() + a3Size.Width() );
        aPnt.AdjustY( ( aTBSize.Height() - aCBSize.Height() ) / 2 );
        aOnStartupCB->SetPosPixel( aPnt );
        nMinPos = aPnt.X();
    }
}
 
 
void SfxHelpTextWindow_Impl::SetOnStartupBoxPosition()
{
    long nX = std::max( GetOutputSizePixel().Width() - aOnStartupCB->GetSizePixel().Width(), nMinPos );
    Point aPos = aOnStartupCB->GetPosPixel();
    aPos.setX( nX );
    aOnStartupCB->SetPosPixel( aPos );
}
 
 
Reference< XBreakIterator > const & SfxHelpTextWindow_Impl::GetBreakIterator()
{
    if ( !xBreakIterator.is() )
        xBreakIterator = vcl::unohelper::CreateBreakIterator();
    DBG_ASSERT( xBreakIterator.is(), "Could not create BreakIterator" );
    return xBreakIterator;
}
 
 
Reference< XTextRange > SfxHelpTextWindow_Impl::getCursor() const
{
    // return the current cursor
    Reference< XTextRange > xCursor;
 
    try
    {
        Reference < XSelectionSupplier > xSelSup( xFrame->getController(), UNO_QUERY );
        if ( xSelSup.is() )
        {
            Any aAny = xSelSup->getSelection();
            Reference < XIndexAccess > xSelection;
            if ( aAny >>= xSelection )
            {
                if ( xSelection->getCount() == 1 )
                {
                    aAny = xSelection->getByIndex(0);
                    aAny >>= xCursor;
                }
            }
        }
    }
    catch( Exception& )
    {
        OSL_FAIL( "SfxHelpTextWindow_Impl::getCursor(): unexpected exception" );
    }
 
    return xCursor;
}
 
 
bool SfxHelpTextWindow_Impl::isHandledKey( const vcl::KeyCode& _rKeyCode )
{
    bool bRet = false;
    sal_uInt16 nCode = _rKeyCode.GetCode();
 
    // the keys <CTRL><A> (select all), <CTRL><C> (copy),
    //          <CTRL><F> (find), <CTRL><P> (print) and <CTRL><W> (close window)
    // were handled in help
    if ( _rKeyCode.IsMod1() &&
         ( KEY_A == nCode || KEY_C == nCode || KEY_F == nCode || KEY_P == nCode || KEY_W == nCode ) )
    {
        if ( KEY_F == nCode )
            DoSearch();
        else
            bRet = true;
    }
 
    return bRet;
}
 
 
IMPL_LINK_NOARG(SfxHelpTextWindow_Impl, SelectHdl, Timer *, void)
{
    try
    {
        // select the words, which are equal to the search text of the search page
        Reference < XController > xController = xFrame->getController();
        if ( xController.is() )
        {
            // get document
            Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
            if ( xSearchable.is() )
            {
                // create descriptor, set string and find all words
                Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
                xSrchDesc->setPropertyValue( "SearchRegularExpression", makeAny( true ) );
                if ( bIsFullWordSearch )
                    xSrchDesc->setPropertyValue( "SearchWords", makeAny( true ) );
 
                OUString sSearchString = sfx2::PrepareSearchString( aSearchText, GetBreakIterator(), false );
                xSrchDesc->setSearchString( sSearchString );
                Reference< XIndexAccess > xSelection = xSearchable->findAll( xSrchDesc );
 
                // then select all found words
                Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
                if ( xSelectionSup.is() )
                {
                    xSelectionSup->select( Any(xSelection) );
                }
            }
        }
    }
    catch( Exception& )
    {
        OSL_FAIL( "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
    }
}
 
 
IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, NotifyHdl, LinkParamNone*, void )
{
    InitToolBoxImages();
    Resize();
    aToolBox->Invalidate();
}
 
 
IMPL_LINK( SfxHelpTextWindow_Impl, FindHdl, sfx2::SearchDialog&, rDlg, void )
{
    FindHdl(&rDlg);
}
void SfxHelpTextWindow_Impl::FindHdl(sfx2::SearchDialog* pDlg)
{
    bool bWrapAround = ( nullptr == pDlg );
    if ( bWrapAround )
        pDlg = pSrchDlg;
    DBG_ASSERT( pDlg, "invalid search dialog" );
    OUString sSearchText = pDlg->GetSearchText();
    try
    {
        // select the words, which are equal to the search text of the search page
        Reference < XController > xController = xFrame->getController();
        if ( xController.is() )
        {
            // get document
            Reference < XSearchable > xSearchable( xController->getModel(), UNO_QUERY );
            if ( xSearchable.is() )
            {
                // create descriptor, set string and find all words
                Reference < XSearchDescriptor > xSrchDesc = xSearchable->createSearchDescriptor();
                xSrchDesc->setPropertyValue( "SearchWords", makeAny(pDlg->IsOnlyWholeWords()) );
                xSrchDesc->setPropertyValue( "SearchCaseSensitive", makeAny(pDlg->IsMarchCase()) );
                xSrchDesc->setPropertyValue( "SearchBackwards", makeAny(pDlg->IsSearchBackwards()) );
                xSrchDesc->setSearchString( sSearchText );
                Reference< XInterface > xSelection;
                Reference< XTextRange > xCursor = getCursor();
 
                if ( xCursor.is() )
                {
                    if ( pDlg->IsSearchBackwards() )
                        xCursor = xCursor->getStart();
                    xSelection = xSearchable->findNext( xCursor, xSrchDesc );
                }
                else
                    xSelection = xSearchable->findFirst( xSrchDesc );
 
                // then select the found word
                if ( xSelection.is() )
                {
                    Reference < XSelectionSupplier > xSelectionSup( xController, UNO_QUERY );
                    if ( xSelectionSup.is() )
                    {
                        xSelectionSup->select( Any(xSelection) );
                    }
                }
                else if ( pDlg->IsWrapAround() && !bWrapAround )
                {
                    Reference < text::XTextViewCursorSupplier > xCrsrSupp( xController, uno::UNO_QUERY );
                    Reference < text::XTextViewCursor > xTVCrsr( xCrsrSupp->getViewCursor(), uno::UNO_QUERY );
                    if ( xTVCrsr.is() )
                    {
                        Reference < text::XTextDocument > xDoc( xController->getModel(), uno::UNO_QUERY );
                        Reference < text::XText > xText = xDoc->getText();
                        if ( xText.is() )
                        {
                            if ( pDlg->IsSearchBackwards() )
                                xTVCrsr->gotoRange( xText->getEnd(), false );
                            else
                                xTVCrsr->gotoRange( xText->getStart(), false );
                            FindHdl( nullptr );
                        }
                    }
                }
                else
                {
                    assert(pSrchDlg && "no search dialog");
                    std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pSrchDlg->GetFrameWeld(),
                                                              VclMessageType::Info, VclButtonsType::Ok, SfxResId(STR_INFO_NOSEARCHTEXTFOUND)));
                    xBox->run();
                    pSrchDlg->SetFocusOnEdit();
                }
            }
        }
    }
    catch( Exception& )
    {
        OSL_FAIL( "SfxHelpTextWindow_Impl::SelectHdl(): unexpected exception" );
    }
}
 
 
IMPL_LINK_NOARG( SfxHelpTextWindow_Impl, CloseHdl, LinkParamNone*, void )
{
    pSrchDlg.clear();
}
 
 
IMPL_LINK( SfxHelpTextWindow_Impl, CheckHdl, Button*, pButton, void )
{
    CheckBox* pBox = static_cast<CheckBox*>(pButton);
    if ( xConfiguration.is() )
    {
        bool bChecked = pBox->IsChecked();
        OUString sPath( PATH_OFFICE_FACTORIES );
        sPath += sCurrentFactory;
        try
        {
            ConfigurationHelper::writeRelativeKey(
                xConfiguration, sPath, KEY_HELP_ON_OPEN, makeAny( bChecked ) );
            ConfigurationHelper::flush( xConfiguration );
        }
        catch( Exception& )
        {
            SAL_WARN( "sfx.appl", "SfxHelpTextWindow_Impl::CheckHdl(): unexpected exception" );
        }
    }
}
 
 
void SfxHelpTextWindow_Impl::Resize()
{
    Size aSize = GetOutputSizePixel();
    long nToolBoxHeight = aToolBox->GetSizePixel().Height() + TOOLBOX_OFFSET;
    aSize.AdjustHeight( -nToolBoxHeight );
    pTextWin->SetPosSizePixel( Point( 0, nToolBoxHeight  ), aSize );
    SetOnStartupBoxPosition();
}
 
 
bool SfxHelpTextWindow_Impl::PreNotify( NotifyEvent& rNEvt )
{
    bool bDone = false;
    MouseNotifyEvent nType = rNEvt.GetType();
    if ( MouseNotifyEvent::COMMAND == nType && rNEvt.GetCommandEvent() )
    {
        const CommandEvent* pCmdEvt = rNEvt.GetCommandEvent();
        vcl::Window* pCmdWin = rNEvt.GetWindow();
 
        if ( pCmdEvt->GetCommand() == CommandEventId::ContextMenu && pCmdWin != this && pCmdWin != aToolBox.get() )
        {
            Point aPos;
            if ( pCmdEvt->IsMouseEvent() )
                aPos = pCmdEvt->GetMousePosPixel();
            else
                aPos = Point( pTextWin->GetPosPixel().X() + 20, 20 );
            aPos.AdjustY(pTextWin->GetPosPixel().Y() );
            ScopedVclPtrInstance<PopupMenu> aMenu;
            if ( bIsIndexOn )
                aMenu->InsertItem(TBI_INDEX, aIndexOffText, Image(BitmapEx(BMP_HELP_TOOLBOX_INDEX_OFF)));
            else
                aMenu->InsertItem(TBI_INDEX, aIndexOnText, Image(BitmapEx(BMP_HELP_TOOLBOX_INDEX_ON)));
 
            aMenu->SetHelpId( TBI_INDEX, HID_HELP_TOOLBOXITEM_INDEX );
            aMenu->InsertSeparator();
            aMenu->InsertItem( TBI_BACKWARD,
                              SfxResId( STR_HELP_BUTTON_PREV  ),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_PREV))
            );
            aMenu->SetHelpId( TBI_BACKWARD, HID_HELP_TOOLBOXITEM_BACKWARD );
            aMenu->EnableItem( TBI_BACKWARD, pHelpWin->HasHistoryPredecessor() );
            aMenu->InsertItem( TBI_FORWARD,
                              SfxResId( STR_HELP_BUTTON_NEXT ),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_NEXT))
            );
            aMenu->SetHelpId( TBI_FORWARD, HID_HELP_TOOLBOXITEM_FORWARD );
            aMenu->EnableItem( TBI_FORWARD, pHelpWin->HasHistorySuccessor() );
            aMenu->InsertItem( TBI_START,
                              SfxResId( STR_HELP_BUTTON_START ),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_START))
            );
            aMenu->SetHelpId( TBI_START, HID_HELP_TOOLBOXITEM_START );
            aMenu->InsertSeparator();
            aMenu->InsertItem( TBI_PRINT,
                              SfxResId( STR_HELP_BUTTON_PRINT ),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_PRINT))
            );
            aMenu->SetHelpId( TBI_PRINT, HID_HELP_TOOLBOXITEM_PRINT );
            aMenu->InsertItem( TBI_BOOKMARKS,
                              SfxResId( STR_HELP_BUTTON_ADDBOOKMARK ),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_BOOKMARKS))
             );
            aMenu->SetHelpId( TBI_BOOKMARKS, HID_HELP_TOOLBOXITEM_BOOKMARKS );
            aMenu->InsertItem( TBI_SEARCHDIALOG,
                              SfxResId( STR_HELP_BUTTON_SEARCHDIALOG ),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_SEARCHDIALOG))
            );
            aMenu->SetHelpId( TBI_SEARCHDIALOG, HID_HELP_TOOLBOXITEM_SEARCHDIALOG );
            aMenu->InsertSeparator();
            aMenu->InsertItem( TBI_SELECTIONMODE, SfxResId( STR_HELP_MENU_TEXT_SELECTION_MODE ) );
            aMenu->SetHelpId( TBI_SELECTIONMODE, HID_HELP_TEXT_SELECTION_MODE );
            URL aURL;
            aURL.Complete = ".uno:SelectTextMode";
            Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
            xTrans->parseStrict(aURL);
            Reference < XDispatch > xDisp = xFrame->queryDispatch( aURL, OUString(), 0 );
            if(xDisp.is())
            {
                rtl::Reference<HelpStatusListener_Impl> pStateListener =
                                        new HelpStatusListener_Impl(xDisp, aURL );
                FeatureStateEvent rEvent = pStateListener->GetStateEvent();
                bool bCheck = false;
                rEvent.State >>= bCheck;
                aMenu->CheckItem(TBI_SELECTIONMODE, bCheck);
            }
            aMenu->InsertSeparator();
            aMenu->InsertItem( TBI_COPY,
                              SfxResId(STR_HELP_MENU_TEXT_COPY),
                              Image(BitmapEx(BMP_HELP_TOOLBOX_COPY))
                );
            aMenu->SetHelpId( TBI_COPY, ".uno:Copy" );
            aMenu->EnableItem( TBI_COPY, HasSelection() );
 
            if ( bIsDebug )
            {
                aMenu->InsertSeparator();
                aMenu->InsertItem( TBI_SOURCEVIEW, SfxResId(STR_HELP_BUTTON_SOURCEVIEW) );
            }
 
            if( ! SvtMenuOptions().IsEntryHidingEnabled() )
                aMenu->SetMenuFlags( aMenu->GetMenuFlags() | MenuFlags::HideDisabledEntries );
 
            sal_uInt16 nId = aMenu->Execute( this, aPos );
            pHelpWin->DoAction( nId );
            bDone = true;
        }
    }
    else if ( MouseNotifyEvent::KEYINPUT == nType && rNEvt.GetKeyEvent() )
    {
        const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
        const vcl::KeyCode& rKeyCode = pKEvt->GetKeyCode();
        sal_uInt16 nKeyGroup = rKeyCode.GetGroup();
        sal_uInt16 nKey = rKeyCode.GetCode();
        if ( KEYGROUP_ALPHA == nKeyGroup &&  !isHandledKey( rKeyCode ) )
        {
            // do nothing disables the writer accelerators
            bDone = true;
         }
        else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
        {
            // <CTRL><F4> or <CTRL><W> -> close top frame
            pHelpWin->CloseWindow();
            bDone = true;
        }
        else if ( KEY_TAB == nKey && aOnStartupCB->HasChildPathFocus() )
        {
            aToolBox->GrabFocus();
            bDone = true;
        }
    }
 
    return bDone || Window::PreNotify( rNEvt );
}
 
 
void SfxHelpTextWindow_Impl::GetFocus()
{
    if ( !bIsInClose )
    {
        try
        {
            if( xFrame.is() )
            {
                Reference< css::awt::XWindow > xWindow = xFrame->getComponentWindow();
                if( xWindow.is() )
                    xWindow->setFocus();
            }
        }
        catch( Exception& )
        {
            SAL_WARN( "sfx.appl", "SfxHelpTextWindow_Impl::GetFocus(): unexpected exception" );
        }
    }
}
 
 
void SfxHelpTextWindow_Impl::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );
 
    if ( ( ( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) ||
           ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) ) &&
         ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) )
    {
        SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFaceColor() ) );
        InitToolBoxImages();
    }
}
 
 
void SfxHelpTextWindow_Impl::ToggleIndex( bool bOn )
{
    bIsIndexOn = bOn;
    if ( bIsIndexOn )
    {
        aToolBox->SetItemImage( TBI_INDEX, aIndexOffImage );
        aToolBox->SetItemText( TBI_INDEX, aIndexOffText );
    }
    else
    {
        aToolBox->SetItemImage( TBI_INDEX, aIndexOnImage );
        aToolBox->SetItemText( TBI_INDEX, aIndexOnText );
    }
}
 
 
void SfxHelpTextWindow_Impl::SelectSearchText( const OUString& rSearchText, bool _bIsFullWordSearch )
{
    aSearchText = rSearchText;
    bIsFullWordSearch = _bIsFullWordSearch;
    aSelectIdle.Start();
}
 
 
void SfxHelpTextWindow_Impl::SetPageStyleHeaderOff() const
{
    bool bSetOff = false;
    // set off the pagestyle header to prevent print output of the help URL
    try
    {
        Reference < XController > xController = xFrame->getController();
        Reference < XSelectionSupplier > xSelSup( xController, UNO_QUERY );
        if ( xSelSup.is() )
        {
            Reference < XIndexAccess > xSelection;
            if ( xSelSup->getSelection() >>= xSelection )
            {
                Reference < XTextRange > xRange;
                if ( xSelection->getByIndex(0) >>= xRange )
                {
                    Reference < XText > xText = xRange->getText();
                    Reference < XPropertySet > xProps( xText->createTextCursorByRange( xRange ), UNO_QUERY );
                    OUString sStyleName;
                    if ( xProps->getPropertyValue( "PageStyleName" ) >>= sStyleName )
                    {
                        Reference < XStyleFamiliesSupplier > xStyles( xController->getModel(), UNO_QUERY );
                        Reference < XNameContainer > xContainer;
                        if ( xStyles->getStyleFamilies()->getByName( "PageStyles" )
                             >>= xContainer )
                        {
                            Reference < XStyle > xStyle;
                            if ( xContainer->getByName( sStyleName ) >>= xStyle )
                            {
                                Reference < XPropertySet > xPropSet( xStyle, UNO_QUERY );
                                xPropSet->setPropertyValue( "HeaderIsOn",  makeAny( false ) );
 
                                Reference< XModifiable > xReset(xStyles, UNO_QUERY);
                                xReset->setModified(false);
                                bSetOff = true;
                            }
                        }
                    }
                }
            }
        }
    }
    catch( Exception& )
    {
        SAL_WARN( "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): unexpected exception" );
    }
 
    SAL_WARN_IF( !bSetOff, "sfx.appl", "SfxHelpTextWindow_Impl::SetPageStyleHeaderOff(): set off failed" );
}
 
 
void SfxHelpTextWindow_Impl::CloseFrame()
{
    bIsInClose = true;
    try
    {
        css::uno::Reference< css::util::XCloseable > xCloseable  ( xFrame, css::uno::UNO_QUERY );
        if (xCloseable.is())
            xCloseable->close(true);
    }
    catch( css::util::CloseVetoException& )
    {
    }
}
 
 
void SfxHelpTextWindow_Impl::DoSearch()
{
    if ( !pSrchDlg )
    {
        // create the search dialog
        pSrchDlg = VclPtr<sfx2::SearchDialog>::Create( pTextWin, "HelpSearchDialog" );
        // set handler
        pSrchDlg->SetFindHdl( LINK( this, SfxHelpTextWindow_Impl, FindHdl ) );
        pSrchDlg->SetCloseHdl( LINK( this, SfxHelpTextWindow_Impl, CloseHdl ) );
        // get selected text of the help page to set it as the search text
        Reference< XTextRange > xCursor = getCursor();
        if ( xCursor.is() )
        {
            OUString sText = xCursor->getString();
            if ( !sText.isEmpty() )
                pSrchDlg->SetSearchText( sText );
        }
        pSrchDlg->Show();
    }
}
 
// class SfxHelpWindow_Impl ----------------------------------------------
 
void SfxHelpWindow_Impl::Resize()
{
    SplitWindow::Resize();
    InitSizes();
}
 
 
void SfxHelpWindow_Impl::Split()
{
    static long nMinSplitSize = 5;
    static long nMaxSplitSize = 99 - nMinSplitSize;
 
    SplitWindow::Split();
 
    nIndexSize = GetItemSize( INDEXWIN_ID );
    nTextSize = GetItemSize( TEXTWIN_ID );
 
    bool        bMod = false;
    if( nIndexSize < nMinSplitSize )
    {
        nIndexSize = nMinSplitSize;
        nTextSize = nMaxSplitSize;
 
        bMod = true;
    }
    else if( nTextSize < nMinSplitSize )
    {
        nTextSize = nMinSplitSize;
        nIndexSize = nMaxSplitSize;
 
        bMod = true;
    }
    else
        bMod = false;
 
    if( bMod )
    {
        SetItemSize( INDEXWIN_ID, nIndexSize );
        SetItemSize( TEXTWIN_ID, nTextSize );
    }
 
    InitSizes();
}
 
void SfxHelpWindow_Impl::GetFocus()
{
    if( pTextWin )
        pTextWin->GrabFocus();
    else
        vcl::Window::GetFocus();
}
 
void SfxHelpWindow_Impl::MakeLayout()
{
    if ( nHeight > 0 && xWindow.is() )
    {
        VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow(xWindow);
 
        /* #i55528#
            Hide() / Show() will produce strange effects.
            The returned size (used later to be written back into the configuration)
            is not the right after a resize during the window is hidden.
            If this resize is done if the window is visible everything works as expected.
            Some VCL-patches could not solve this problem so I've established the
            workaround: resize the help window if it's visible .-)
        */
        css::awt::Rectangle aRect = xWindow->getPosSize();
        sal_Int32 nOldWidth = bIndex ? nCollapseWidth : nExpandWidth;
        sal_Int32 nWidth = bIndex ? nExpandWidth : nCollapseWidth;
        xWindow->setPosSize( aRect.X, aRect.Y, nWidth, nHeight, css::awt::PosSize::SIZE );
 
        if ( aRect.Width > 0 && aRect.Height > 0 )
        {
            tools::Rectangle aScreenRect = pScreenWin->GetClientWindowExtentsRelative();
            Point aNewPos = aScreenRect.TopLeft();
            sal_Int32 nDiffWidth = nOldWidth - nWidth;
            aNewPos.AdjustX(nDiffWidth );
            pScreenWin->SetPosPixel( aNewPos );
        }
        else if ( aWinPos.X() > 0 && aWinPos.Y() > 0 )
            pScreenWin->SetPosPixel( aWinPos );
    }
 
    Clear();
 
    if ( bIndex )
    {
        pIndexWin->Show();
        InsertItem( COLSET_ID, 100, SPLITWINDOW_APPEND, SPLITSET_ID, SplitWindowItemFlags::PercentSize | SplitWindowItemFlags::ColSet );
        InsertItem( INDEXWIN_ID, pIndexWin, nIndexSize, SPLITWINDOW_APPEND, COLSET_ID, SplitWindowItemFlags::PercentSize );
        InsertItem( TEXTWIN_ID, pTextWin, nTextSize, SPLITWINDOW_APPEND, COLSET_ID, SplitWindowItemFlags::PercentSize );
    }
    else
    {
        pIndexWin->Hide();
        InsertItem( COLSET_ID, 100, SPLITWINDOW_APPEND, SPLITSET_ID, SplitWindowItemFlags::PercentSize | SplitWindowItemFlags::ColSet );
        InsertItem( TEXTWIN_ID, pTextWin, 100, SPLITWINDOW_APPEND, 1, SplitWindowItemFlags::PercentSize );
    }
}
 
 
void SfxHelpWindow_Impl::InitSizes()
{
    if ( xWindow.is() )
    {
        css::awt::Rectangle aRect = xWindow->getPosSize();
        nHeight = aRect.Height;
 
        if ( bIndex )
        {
            nExpandWidth = aRect.Width;
            nCollapseWidth = nExpandWidth * nTextSize / 100;
        }
        else
        {
            nCollapseWidth = aRect.Width;
            nExpandWidth = nTextSize ? nCollapseWidth * 100 / nTextSize : 0;
        }
    }
}
 
 
void SfxHelpWindow_Impl::LoadConfig()
{
     SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
    if ( aViewOpt.Exists() )
    {
        bIndex = aViewOpt.IsVisible();
        OUString aUserData;
        Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
        OUString aTemp;
        if ( aUserItem >>= aTemp )
        {
            aUserData = aTemp;
            DBG_ASSERT( comphelper::string::getTokenCount(aUserData, ';') == 6, "invalid user data" );
            sal_Int32 nIdx = 0;
            nIndexSize = aUserData.getToken( 0, ';', nIdx ).toInt32();
            nTextSize = aUserData.getToken( 0, ';', nIdx ).toInt32();
            sal_Int32 nWidth = aUserData.getToken( 0, ';', nIdx ).toInt32();
            nHeight = aUserData.getToken( 0, ';', nIdx ).toInt32();
            aWinPos.setX( aUserData.getToken( 0, ';', nIdx ).toInt32() );
            aWinPos.setY( aUserData.getToken( 0, ';', nIdx ).toInt32() );
            if ( bIndex )
            {
                nExpandWidth = nWidth;
                nCollapseWidth = nExpandWidth * nTextSize / 100;
            }
            else if (nTextSize != 0)
            {
                nCollapseWidth = nWidth;
                nExpandWidth = nCollapseWidth * 100 / nTextSize;
            }
        }
 
        pTextWin->ToggleIndex( bIndex );
    }
}
 
 
void SfxHelpWindow_Impl::SaveConfig()
{
    SvtViewOptions aViewOpt( EViewType::Window, CONFIGNAME_HELPWIN );
    sal_Int32 nW = 0, nH = 0;
 
    if ( xWindow.is() )
    {
        css::awt::Rectangle aRect = xWindow->getPosSize();
        nW = aRect.Width;
        nH = aRect.Height;
    }
 
    aViewOpt.SetVisible( bIndex );
    OUString aUserData = OUString::number( nIndexSize );
    aUserData += ";";
    aUserData += OUString::number( nTextSize );
    aUserData += ";";
    aUserData += OUString::number( nW );
    aUserData += ";";
    aUserData += OUString::number( nH );
 
    VclPtr<vcl::Window> pScreenWin = VCLUnoHelper::GetWindow( xWindow );
    aWinPos = pScreenWin->GetWindowExtentsRelative( nullptr ).TopLeft();
    aUserData += ";";
    aUserData += OUString::number( aWinPos.X() );
    aUserData += ";";
    aUserData += OUString::number( aWinPos.Y() );
 
    aViewOpt.SetUserItem( USERITEM_NAME, makeAny( aUserData ) );
}
 
 
void SfxHelpWindow_Impl::ShowStartPage()
{
    OUString sHelpURL = SfxHelpWindow_Impl::buildHelpURL(pIndexWin->GetFactory(),
                                                                "/start",
                                                                OUString());
    loadHelpContent(sHelpURL);
}
 
 
IMPL_LINK( SfxHelpWindow_Impl, SelectHdl, ToolBox* , pToolBox, void )
{
    if ( pToolBox )
    {
        bGrabFocusToToolBox = pToolBox->HasChildPathFocus();
        DoAction( pToolBox->GetCurItemId() );
    }
}
 
 
IMPL_LINK_NOARG(SfxHelpWindow_Impl, OpenHdl, Control*, bool)
{
    pIndexWin->SelectExecutableEntry();
    OUString aEntry = pIndexWin->GetSelectedEntry();
 
    if ( aEntry.isEmpty() )
        return false;
 
    OUString sHelpURL;
 
    bool bComplete = aEntry.toAsciiLowerCase().match("vnd.sun.star.help");
 
    if (bComplete)
        sHelpURL = aEntry;
    else
    {
        OUString aId;
        OUString aAnchor = OUString('#');
        if ( comphelper::string::getTokenCount(aEntry, '#') == 2 )
        {
            aId = aEntry.getToken( 0, '#' );
            aAnchor += aEntry.getToken( 1, '#' );
        }
        else
            aId = aEntry;
 
        aEntry  = "/";
        aEntry += aId;
 
        sHelpURL = SfxHelpWindow_Impl::buildHelpURL(pIndexWin->GetFactory(),
                                                    aEntry,
                                                    aAnchor);
    }
 
    loadHelpContent(sHelpURL);
 
    return false;
}
 
 
IMPL_LINK( SfxHelpWindow_Impl, SelectFactoryHdl, SfxHelpIndexWindow_Impl* , pWin, void )
{
    if ( sTitle.isEmpty() )
        sTitle = GetParent()->GetText();
 
    OUString aNewTitle = sTitle + " - " + pIndexWin->GetActiveFactoryTitle();
 
    Reference< XTitle > xTitle(xFrame, UNO_QUERY);
    if (xTitle.is ())
        xTitle->setTitle (aNewTitle);
 
    if ( pWin )
        ShowStartPage();
    pIndexWin->ClearSearchPage();
}
 
 
IMPL_LINK( SfxHelpWindow_Impl, ChangeHdl, HelpListener_Impl&, rListener, void )
{
    SetFactory( rListener.GetFactory() );
}
 
 
void SfxHelpWindow_Impl::openDone(const OUString& sURL    ,
                                        bool         bSuccess)
{
    INetURLObject aObj( sURL );
    if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
        SetFactory( aObj.GetHost() );
    if ( IsWait() )
        LeaveWait();
    if ( bGrabFocusToToolBox )
    {
        pTextWin->GetToolBox().GrabFocus();
        bGrabFocusToToolBox = false;
    }
    else
        pIndexWin->GrabFocusBack();
    if ( bSuccess )
    {
        // set some view settings: "prevent help tips" and "helpid == 68245"
        try
        {
            Reference < XController > xController = pTextWin->getFrame()->getController();
            if ( xController.is() )
            {
                Reference < XViewSettingsSupplier > xSettings( xController, UNO_QUERY );
                Reference < XPropertySet > xViewProps = xSettings->getViewSettings();
                Reference< XPropertySetInfo > xInfo = xViewProps->getPropertySetInfo();
                xViewProps->setPropertyValue( "ShowContentTips", makeAny( false ) );
                xViewProps->setPropertyValue( "ShowGraphics", makeAny( true ) );
                xViewProps->setPropertyValue( "ShowTables", makeAny( true ) );
                xViewProps->setPropertyValue( "HelpURL", makeAny( OUString("HID:SFX2_HID_HELP_ONHELP") ) );
                OUString sProperty( "IsExecuteHyperlinks" );
                if ( xInfo->hasPropertyByName( sProperty ) )
                    xViewProps->setPropertyValue( sProperty, makeAny( true ) );
                xController->restoreViewData(Any());
            }
        }
        catch( Exception& )
        {
            OSL_FAIL( "SfxHelpWindow_Impl::OpenDoneHdl(): unexpected exception" );
        }
 
        // When the SearchPage opens the help doc, then select all words, which are equal to its text
        OUString sSearchText = comphelper::string::strip(pIndexWin->GetSearchText(), ' ');
        if ( !sSearchText.isEmpty() )
            pTextWin->SelectSearchText( sSearchText, pIndexWin->IsFullWordSearch() );
 
        // no page style header -> this prevents a print output of the URL
        pTextWin->SetPageStyleHeaderOff();
    }
}
 
 
SfxHelpWindow_Impl::SfxHelpWindow_Impl(
    const css::uno::Reference < css::frame::XFrame2 >& rFrame,
    vcl::Window* pParent ) :
 
    SplitWindow( pParent, WB_3DLOOK | WB_NOSPLITDRAW ),
 
    xFrame              ( rFrame ),
    pIndexWin           ( nullptr ),
    pTextWin            ( nullptr ),
    pHelpInterceptor    ( new HelpInterceptor_Impl() ),
    pHelpListener       ( new HelpListener_Impl( pHelpInterceptor ) ),
    nExpandWidth        ( 0 ),
    nCollapseWidth      ( 0 ),
    nHeight             ( 0 ),
    nIndexSize          ( 40 ),
    nTextSize           ( 60 ),
    bIndex              ( true ),
    bGrabFocusToToolBox ( false ),
    aWinPos             ( 0, 0 ),
    sTitle              ( pParent->GetText() )
{
    SetHelpId( HID_HELP_WINDOW );
    SetStyle( GetStyle() | WB_DIALOGCONTROL );
 
    pHelpInterceptor->InitWaiter( this );
    pIndexWin = VclPtr<SfxHelpIndexWindow_Impl>::Create( this );
    pIndexWin->SetDoubleClickHdl( LINK( this, SfxHelpWindow_Impl, OpenHdl ) );
    pIndexWin->SetSelectFactoryHdl( LINK( this, SfxHelpWindow_Impl, SelectFactoryHdl ) );
    pIndexWin->SetSizePixel(LogicToPixel(Size(120, 200), MapMode(MapUnit::MapAppFont)));
    pIndexWin->Show();
    pTextWin = VclPtr<SfxHelpTextWindow_Impl>::Create( this );
    Reference < XFrames > xFrames = rFrame->getFrames();
    xFrames->append( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
    pTextWin->SetSelectHdl( LINK( this, SfxHelpWindow_Impl, SelectHdl ) );
    pTextWin->Show();
    pHelpInterceptor->setInterception( Reference<XFrame>(pTextWin->getFrame(), UNO_QUERY_THROW) );
    pHelpListener->SetChangeHdl( LINK( this, SfxHelpWindow_Impl, ChangeHdl ) );
    LoadConfig();
}
 
 
SfxHelpWindow_Impl::~SfxHelpWindow_Impl()
{
    disposeOnce();
}
 
void SfxHelpWindow_Impl::dispose()
{
    SaveConfig();
    pIndexWin.disposeAndClear();
    pTextWin->CloseFrame();
    pTextWin.disposeAndClear();
    SplitWindow::dispose();
}
 
bool SfxHelpWindow_Impl::PreNotify( NotifyEvent& rNEvt )
{
    bool bHandled = false;
    if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
    {
        // Backward == <ALT><LEFT> or <BACKSPACE> Forward == <ALT><RIGHT>
        const vcl::KeyCode& rKeyCode = rNEvt.GetKeyEvent()->GetKeyCode();
        sal_uInt16 nKey = rKeyCode.GetCode();
        if ( ( rKeyCode.IsMod2() && ( KEY_LEFT == nKey || KEY_RIGHT == nKey ) ) ||
             ( !rKeyCode.GetModifier() && KEY_BACKSPACE == nKey && !pIndexWin->HasFocusOnEdit() ) )
        {
            DoAction( rKeyCode.GetCode() == KEY_RIGHT ? TBI_FORWARD : TBI_BACKWARD );
            bHandled = true;
        }
        else if ( rKeyCode.IsMod1() && ( KEY_F4 == nKey || KEY_W == nKey ) )
        {
            // <CTRL><F4> or <CTRL><W> -> close top frame
            CloseWindow();
            bHandled = true;
        }
    }
    return bHandled || Window::PreNotify( rNEvt );
}
 
void SfxHelpWindow_Impl::setContainerWindow( const Reference < css::awt::XWindow >& xWin )
{
    xWindow = xWin;
    MakeLayout();
}
 
void SfxHelpWindow_Impl::SetFactory( const OUString& rFactory )
{
    pIndexWin->SetFactory( rFactory, true );
}
 
 
void SfxHelpWindow_Impl::SetHelpURL( const OUString& rURL )
{
    INetURLObject aObj( rURL );
    if ( aObj.GetProtocol() == INetProtocol::VndSunStarHelp )
        SetFactory( aObj.GetHost() );
}
 
 
void SfxHelpWindow_Impl::DoAction( sal_uInt16 nActionId )
{
    switch ( nActionId )
    {
        case TBI_INDEX :
        {
            bIndex = !bIndex;
            MakeLayout();
            pTextWin->ToggleIndex( bIndex );
            break;
        }
 
        case TBI_START :
        {
            ShowStartPage();
            break;
        }
 
        case TBI_BACKWARD :
        case TBI_FORWARD :
        {
            URL aURL;
            aURL.Complete = ".uno:Backward";
            if ( TBI_FORWARD == nActionId )
                aURL.Complete = ".uno:Forward";
            Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
            xTrans->parseStrict(aURL);
            pHelpInterceptor->dispatch( aURL, Sequence < PropertyValue >() );
            break;
        }
 
        case TBI_SEARCHDIALOG :
        {
            pTextWin->DoSearch();
            break;
        }
 
        case TBI_PRINT :
        case TBI_SOURCEVIEW :
        case TBI_COPY :
        case TBI_SELECTIONMODE:
        {
            Reference < XDispatchProvider > xProv( pTextWin->getFrame(), UNO_QUERY );
            if ( xProv.is() )
            {
                URL aURL;
                if ( TBI_PRINT == nActionId )
                    aURL.Complete = ".uno:Print";
                else if ( TBI_SOURCEVIEW == nActionId )
                    aURL.Complete = ".uno:SourceView";
                else if ( TBI_COPY == nActionId )
                    aURL.Complete = ".uno:Copy";
                else if ( TBI_SELECTIONMODE == nActionId )
                    aURL.Complete = ".uno:SelectTextMode";
                else
                    aURL.Complete = ".uno:SearchDialog";
                Reference< util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
                xTrans->parseStrict(aURL);
                Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
                if ( xDisp.is() )
                    xDisp->dispatch( aURL, Sequence < PropertyValue >() );
            }
            break;
        }
 
        case TBI_BOOKMARKS :
        {
            OUString aURL = pHelpInterceptor->GetCurrentURL();
            if ( !aURL.isEmpty() )
            {
                try
                {
                    Content aCnt( aURL, Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
                    css::uno::Reference< css::beans::XPropertySetInfo > xInfo = aCnt.getProperties();
                    if ( xInfo->hasPropertyByName( PROPERTY_TITLE ) )
                    {
                        css::uno::Any aAny = aCnt.getPropertyValue( PROPERTY_TITLE );
                        OUString aValue;
                        if ( aAny >>= aValue )
                        {
                            OUString aTitle(aValue);
                            SfxAddHelpBookmarkDialog_Impl aDlg(GetFrameWeld(), false);
                            aDlg.SetTitle(aTitle);
                            if (aDlg.run() == RET_OK )
                            {
                                aTitle = aDlg.GetTitle();
                                pIndexWin->AddBookmarks( aTitle, aURL );
                            }
                        }
                    }
                }
                catch( Exception& )
                {
                    OSL_FAIL( "SfxHelpWindow_Impl::DoAction(): unexpected exception" );
                }
            }
            break;
        }
    }
}
 
 
void SfxHelpWindow_Impl::CloseWindow()
{
    try
    {
        // search for top frame
        Reference< XFramesSupplier > xCreator = getTextFrame()->getCreator();
        while ( xCreator.is() && !xCreator->isTop() )
        {
            xCreator = xCreator->getCreator();
        }
 
        // when found, close it
        if ( xCreator.is() && xCreator->isTop() )
        {
            Reference < XCloseable > xCloser( xCreator, UNO_QUERY );
            if ( xCloser.is() )
                xCloser->close( false );
        }
    }
    catch( Exception& )
    {
        SAL_WARN( "sfx.appl", "SfxHelpWindow_Impl::CloseWindow(): caught an exception" );
    }
}
 
 
void SfxHelpWindow_Impl::UpdateToolbox()
{
    pTextWin->GetToolBox().EnableItem( TBI_BACKWARD, pHelpInterceptor->HasHistoryPred() );
    pTextWin->GetToolBox().EnableItem( TBI_FORWARD, pHelpInterceptor->HasHistorySucc() );
}
 
 
bool SfxHelpWindow_Impl::HasHistoryPredecessor() const
{
    return pHelpInterceptor->HasHistoryPred();
}
 
 
bool SfxHelpWindow_Impl::HasHistorySuccessor() const
{
    return pHelpInterceptor->HasHistorySucc();
}
 
// class SfxAddHelpBookmarkDialog_Impl -----------------------------------
 
SfxAddHelpBookmarkDialog_Impl::SfxAddHelpBookmarkDialog_Impl(weld::Window* pParent, bool bRename)
    : GenericDialogController(pParent, "sfx/ui/bookmarkdialog.ui", "BookmarkDialog")
    , m_xTitleED(m_xBuilder->weld_entry("entry"))
    , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
{
    if (bRename)
        m_xDialog->set_title(m_xAltTitle->get_label());
}
 
void SfxAddHelpBookmarkDialog_Impl::SetTitle(const OUString& rTitle)
{
    m_xTitleED->set_text(rTitle);
    m_xTitleED->select_region(0, -1);
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression '1010 == nActionId' is always true.

V1019 Compound assignment expression 'aAnySeq[2] >>= aAnchorRefList' is used inside condition.

V1019 Compound assignment expression 'aAnySeq[3] >>= aTitleRefList' is used inside condition.

V1020 The function exited without calling the 'LeaveWait' function. Check lines: 1404, 1382.

V1019 Compound assignment expression 'aAnySeq[1] >>= aKeywordRefList' is used inside condition.