/* -*- 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 <comphelper/lok.hxx>
#include <rtl/ustrbuf.hxx>
#include <sfx2/request.hxx>
#include <svl/stritem.hxx>
#include <unotools/viewoptions.hxx>
#include <vcl/weld.hxx>
#include <o3tl/string_view.hxx>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/text/XBookmarksSupplier.hpp>
#include <officecfg/Office/Common.hxx>

#include <swabstdlg.hxx>
#include <swuiexp.hxx>
#include <view.hxx>
#include <wrtsh.hxx>
#include <cmdid.h>
#include <bookmark.hxx>
#include <docsh.hxx>
#include <ndtxt.hxx>
#include <strings.hrc>
#include <IDocumentSettingAccess.hxx>
#include <unotxdoc.hxx>

using namespace ::com::sun::star;

const char BookmarkTable::s_cSeparator(';');

// callback to modify EditBox
IMPL_LINK_NOARG(SwInsertBookmarkDlg, ModifyHdl, weld::Entry&, void)
{
    ValidateBookmarks();
    m_xBookmarksBox->unselect_all();
    // if a string has been pasted from the clipboard then
    // there may be illegal characters in the box
    // sanitization
    OUString sTmp = m_xEditBox->get_text();
    const sal_Int32 nLen = sTmp.getLength();
    for (sal_Int32 i = 0; i < BookmarkTable::aForbiddenChars.getLength(); i++)
    {
        sTmp = sTmp.replaceAll(OUStringChar(BookmarkTable::aForbiddenChars.getStr()[i]), "");
    }
    const bool bHasForbiddenChars = sTmp.getLength() != nLen;
    m_xForbiddenChars->set_visible(bHasForbiddenChars);
    if (bHasForbiddenChars)
        m_xEditBox->set_message_type(weld::EntryMessageType::Error);
    else
        m_xEditBox->set_message_type(weld::EntryMessageType::Normal);

    sal_Int32 nSelectedEntries = 0;
    sal_Int32 nEntries = 0;
    sal_Int32 nTokenIndex = 0;
    while (!sTmp.isEmpty() && nTokenIndex >= 0)
    {
        OUString aToken = sTmp.getToken(0, BookmarkTable::s_cSeparator, nTokenIndex);
        if (m_xBookmarksBox->GetBookmarkByName(aToken))
        {
            m_xBookmarksBox->SelectByName(aToken);
            nSelectedEntries++;
        }
        nEntries++;
    }

    // allow to add new bookmark only if one name provided and it's not taken
    m_xInsertBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 0 && !bHasForbiddenChars
                                && !m_bAreProtected);

    // allow to delete only if all bookmarks are recognized
    m_xDeleteBtn->set_sensitive(nEntries > 0 && nSelectedEntries == nEntries && !m_bAreProtected);
    m_xGotoBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1);
    m_xEditTextBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1);
    m_xRenameBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1 && !m_bAreProtected);
}

// callback to delete a text mark
IMPL_LINK_NOARG(SwInsertBookmarkDlg, DeleteHdl, weld::Button&, void)
{
    if (!ValidateBookmarks())
        return;

    int nSelectedRows(0);

    m_xBookmarksBox->selected_foreach([this, &nSelectedRows](weld::TreeIter& rEntry) {
        // remove from model
        sw::mark::MarkBase* pBookmark
            = weld::fromId<sw::mark::MarkBase*>(m_xBookmarksBox->get_id(rEntry));
        SwMarkName sRemoved = pBookmark->GetName();
        IDocumentMarkAccess* const pMarkAccess = m_rSh.getIDocumentMarkAccess();
        pMarkAccess->deleteMark(pMarkAccess->findMark(sRemoved), false);
        SfxRequest aReq(m_rSh.GetView().GetViewFrame(), FN_DELETE_BOOKMARK);
        aReq.AppendItem(SfxStringItem(FN_DELETE_BOOKMARK, sRemoved.toString()));
        aReq.Done();
        std::erase(m_aTableBookmarks, std::make_pair(pBookmark, sRemoved));

        ++nSelectedRows;

        return false;
    });

    if (!nSelectedRows)
        return;

    // remove from BookmarkTable
    m_xBookmarksBox->remove_selection();

    ValidateBookmarks();

    m_xDeleteBtn->set_sensitive(false);
    m_xGotoBtn->set_sensitive(false);
    m_xEditTextBtn->set_sensitive(false);
    m_xRenameBtn->set_sensitive(false);
    m_xInsertBtn->set_sensitive(false);
}

// callback to a goto button
IMPL_LINK_NOARG(SwInsertBookmarkDlg, GotoHdl, weld::Button&, void) { GotoSelectedBookmark(); }

IMPL_LINK_NOARG(SwInsertBookmarkDlg, DoubleClickHdl, weld::TreeView&, bool)
{
    GotoSelectedBookmark();
    return true;
}

IMPL_LINK_NOARG(SwInsertBookmarkDlg, SelectionChangedHdl, weld::TreeView&, void)
{
    if (!ValidateBookmarks())
        return;
    // this event should fired only if we change selection by clicking on BookmarkTable entry
    if (!m_xBookmarksBox->has_focus())
        return;

    SelectionChanged();
}

void SwInsertBookmarkDlg::SelectionChanged()
{
    OUStringBuffer sEditBoxText;
    int nSelectedRows = 0;
    m_xBookmarksBox->selected_foreach(
        [this, &sEditBoxText, &nSelectedRows](weld::TreeIter& rEntry) {
            sw::mark::MarkBase* pBookmark
                = weld::fromId<sw::mark::MarkBase*>(m_xBookmarksBox->get_id(rEntry));
            const SwMarkName& sEntryName = pBookmark->GetName();
            if (!sEditBoxText.isEmpty())
                sEditBoxText.append(";");
            sEditBoxText.append(sEntryName.toString());
            ++nSelectedRows;
            return false;
        });
    if (nSelectedRows)
    {
        m_xInsertBtn->set_sensitive(false);
        m_xGotoBtn->set_sensitive(nSelectedRows == 1);
        m_xEditTextBtn->set_sensitive(nSelectedRows == 1);
        m_xRenameBtn->set_sensitive(nSelectedRows == 1 && !m_bAreProtected);
        m_xDeleteBtn->set_sensitive(!m_bAreProtected);
        m_xEditBox->set_text(sEditBoxText.makeStringAndClear());
    }
    else
    {
        m_xInsertBtn->set_sensitive(!m_bAreProtected);
        m_xGotoBtn->set_sensitive(false);
        m_xEditTextBtn->set_sensitive(false);
        m_xRenameBtn->set_sensitive(false);
        m_xDeleteBtn->set_sensitive(false);
    }
}

IMPL_LINK_NOARG(SwInsertBookmarkDlg, EditTextHdl, weld::Button&, void)
{
    if (!ValidateBookmarks())
        return;
    auto pSelected = m_xBookmarksBox->get_selected();
    if (!pSelected)
        return;

    m_xBookmarksBox->start_editing(*pSelected);
}

IMPL_LINK_NOARG(SwInsertBookmarkDlg, RenameHdl, weld::Button&, void)
{
    if (!ValidateBookmarks())
        return;
    auto xSelected = m_xBookmarksBox->get_selected();
    if (!xSelected)
        return;

    sw::mark::MarkBase* pBookmark
        = weld::fromId<sw::mark::MarkBase*>(m_xBookmarksBox->get_id(*xSelected));
    rtl::Reference<SwXTextDocument> xModel = m_rSh.GetView().GetDocShell()->GetBaseModel();
    uno::Reference<container::XNameAccess> xNameAccess = xModel->getBookmarks();
    uno::Any aObj = xNameAccess->getByName(pBookmark->GetName().toString());
    uno::Reference<uno::XInterface> xTmp;
    aObj >>= xTmp;
    uno::Reference<container::XNamed> xNamed(xTmp, uno::UNO_QUERY);
    SwAbstractDialogFactory& rFact = swui::GetFactory();
    VclPtr<AbstractSwRenameXNamedDlg> pDlg(
        rFact.CreateSwRenameXNamedDlg(m_xDialog.get(), xNamed, xNameAccess));
    pDlg->SetForbiddenChars(BookmarkTable::aForbiddenChars
                            + OUStringChar(BookmarkTable::s_cSeparator));

    pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult) {
        if (nResult == RET_OK)
        {
            ValidateBookmarks();
            m_xDeleteBtn->set_sensitive(false);
            m_xGotoBtn->set_sensitive(false);
            m_xEditTextBtn->set_sensitive(false);
            m_xRenameBtn->set_sensitive(false);
            m_xInsertBtn->set_sensitive(false);
        }
        pDlg->disposeOnce();
    });
}

// callback to an insert button. Inserts a new text mark to the current position.
IMPL_LINK_NOARG(SwInsertBookmarkDlg, InsertHdl, weld::Button&, void)
{
    OUString sBookmark = m_xEditBox->get_text();
    m_rSh.SetBookmark2(vcl::KeyCode(), SwMarkName(sBookmark), m_xHideCB->get_active(),
                       m_xConditionED->get_text());

    m_xDialog->response(RET_OK);
}

IMPL_LINK(SwInsertBookmarkDlg, ChangeHideHdl, weld::Toggleable&, rBox, void)
{
    bool bHide = rBox.get_active();
    m_xConditionED->set_sensitive(bHide);
    m_xConditionFT->set_sensitive(bHide);
}

IMPL_LINK(SwInsertBookmarkDlg, EditingHdl, weld::TreeIter const&, rIter, bool)
{
    sw::mark::MarkBase const* const pBookmark(
        weld::fromId<sw::mark::MarkBase*>(m_xBookmarksBox->get_id(rIter)));
    assert(pBookmark);
    return pBookmark->IsExpanded()
           && pBookmark->GetMarkPos().GetNode() == pBookmark->GetOtherMarkPos().GetNode()
           && !m_xBookmarksBox->get_text(rIter).endsWith(u"…");
}

IMPL_LINK(SwInsertBookmarkDlg, EditedHdl, weld::TreeView::iter_string const&, rIterString, bool)
{
    sw::mark::MarkBase const* const pBookmark(
        weld::fromId<sw::mark::MarkBase*>(m_xBookmarksBox->get_id(rIterString.first)));
    assert(pBookmark);
    bool bRet(false);
    if (pBookmark->GetMarkPos() != pBookmark->GetOtherMarkPos())
    {
        if (pBookmark->GetMarkPos().GetNode() != pBookmark->GetOtherMarkPos().GetNode())
        {
            return false; // don't allow editing if it spans multiple nodes
        }
        m_rSh.Push();
        m_rSh.GotoMark(pBookmark);
        // GetSelText only works for 1 paragraph, but it's checked above
        if (m_rSh.GetSelText() != rIterString.second)
        {
            bRet = m_rSh.Replace(rIterString.second, false);
        }
        m_rSh.Pop(SwEditShell::PopMode::DeleteCurrent);
    }
    else if (pBookmark->IsExpanded() && !rIterString.second.isEmpty())
    { // SwEditShell::Replace does nothing for empty selection
        m_rSh.Insert(rIterString.second);
        bRet = true;
    }
    return bRet;
}

void SwInsertBookmarkDlg::GotoSelectedBookmark()
{
    if (!ValidateBookmarks())
        return;
    // if no entries selected we can't jump anywhere
    // shouldn't be needed as we disable GoTo button when jump is not possible
    auto xSelected = m_xBookmarksBox->get_selected();
    if (!xSelected)
        return;

    sw::mark::MarkBase* pBookmark
        = weld::fromId<sw::mark::MarkBase*>(m_xBookmarksBox->get_id(*xSelected));

    m_rSh.EnterStdMode();
    m_rSh.GotoMark(pBookmark);
}

bool SwInsertBookmarkDlg::ValidateBookmarks()
{
    if (HaveBookmarksChanged())
    {
        PopulateTable();
        m_xEditBox->set_text(u""_ustr);
        return false;
    }
    return true;
}

bool SwInsertBookmarkDlg::HaveBookmarksChanged()
{
    IDocumentMarkAccess* const pMarkAccess = m_rSh.getIDocumentMarkAccess();
    if (pMarkAccess->getBookmarksCount() != m_nLastBookmarksCount)
        return true;

    auto aListIter = m_aTableBookmarks.begin();
    for (auto ppBookmark = pMarkAccess->getBookmarksBegin();
         ppBookmark != pMarkAccess->getBookmarksEnd(); ++ppBookmark)
    {
        if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark))
        {
            // more bookmarks then expected
            if (aListIter == m_aTableBookmarks.end())
                return true;
            if (aListIter->first != *ppBookmark || aListIter->second != (*ppBookmark)->GetName())
                return true;
            ++aListIter;
        }
    }
    // less bookmarks then expected
    return aListIter != m_aTableBookmarks.end();
}

void SwInsertBookmarkDlg::PopulateTable()
{
    m_aTableBookmarks.clear();
    m_xBookmarksBox->clear();

    int nSortColumn = m_xBookmarksBox->get_sort_column();
    if (nSortColumn != -1)
        m_xBookmarksBox->make_unsorted();

    m_rSh.StartAllAction();
    IDocumentMarkAccess* const pMarkAccess = m_rSh.getIDocumentMarkAccess();
    for (auto ppBookmark = pMarkAccess->getBookmarksBegin();
         ppBookmark != pMarkAccess->getBookmarksEnd(); ++ppBookmark)
    {
        if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark))
        {
            m_xBookmarksBox->InsertBookmark(m_rSh, *ppBookmark);
            m_aTableBookmarks.emplace_back(*ppBookmark, (*ppBookmark)->GetName());
        }
    }
    m_rSh.EndAllAction();

    if (nSortColumn != -1)
        m_xBookmarksBox->make_sorted();

    m_nLastBookmarksCount = pMarkAccess->getBookmarksCount();
}

SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS,
                                         OUString const* const pSelected)
    : SfxDialogController(pParent, u"modules/swriter/ui/insertbookmark.ui"_ustr,
                          u"InsertBookmarkDialog"_ustr)
    , m_rSh(rS)
    , m_nLastBookmarksCount(0)
    , m_bSorted(false)
    , m_xEditBox(m_xBuilder->weld_entry(u"name"_ustr))
    , m_xInsertBtn(m_xBuilder->weld_button(u"insert"_ustr))
    , m_xDeleteBtn(m_xBuilder->weld_button(u"delete"_ustr))
    , m_xGotoBtn(m_xBuilder->weld_button(u"goto"_ustr))
    , m_xEditTextBtn(m_xBuilder->weld_button(u"edittext"_ustr))
    , m_xRenameBtn(m_xBuilder->weld_button(u"rename"_ustr))
    , m_xHideCB(m_xBuilder->weld_check_button(u"hide"_ustr))
    , m_xConditionFT(m_xBuilder->weld_label(u"condlabel"_ustr))
    , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry(u"withcond"_ustr)))
    , m_xBookmarksBox(new BookmarkTable(m_xBuilder->weld_tree_view(u"bookmarks"_ustr)))
    , m_xForbiddenChars(m_xBuilder->weld_label(u"lbForbiddenChars"_ustr))
{
    m_xBookmarksBox->connect_changed(LINK(this, SwInsertBookmarkDlg, SelectionChangedHdl));
    m_xBookmarksBox->connect_row_activated(LINK(this, SwInsertBookmarkDlg, DoubleClickHdl));
    m_xBookmarksBox->connect_column_clicked(LINK(this, SwInsertBookmarkDlg, HeaderBarClick));
    m_xBookmarksBox->connect_editing(LINK(this, SwInsertBookmarkDlg, EditingHdl),
                                     LINK(this, SwInsertBookmarkDlg, EditedHdl));
    m_xEditBox->connect_changed(LINK(this, SwInsertBookmarkDlg, ModifyHdl));
    m_xInsertBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, InsertHdl));
    m_xDeleteBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, DeleteHdl));
    m_xGotoBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, GotoHdl));
    m_xEditTextBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, EditTextHdl));
    m_xRenameBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, RenameHdl));
    m_xHideCB->connect_toggled(LINK(this, SwInsertBookmarkDlg, ChangeHideHdl));

    m_xDeleteBtn->set_sensitive(false);
    m_xGotoBtn->set_sensitive(false);
    m_xEditTextBtn->set_sensitive(false);
    m_xRenameBtn->set_sensitive(false);

    // Clicking the "edit text" button is not yet supported in JSDialog
    // to edit this we need to send back 'editend' events from editing via another trigger - presumably double-clicking
    m_xEditTextBtn->set_visible(!comphelper::LibreOfficeKit::isActive());

    // select 3rd column, otherwise it'll pick 1st one
    m_xBookmarksBox->set_column_editables({ false, false, true, false, false });

    PopulateTable();

    m_xEditBox->set_text(m_xBookmarksBox->GetNameProposal());
    m_xEditBox->set_position(-1);

    m_xForbiddenChars->set_label(SwResId(STR_BOOKMARK_FORBIDDENCHARS) + " "
                                 + BookmarkTable::aForbiddenChars);
    m_xForbiddenChars->set_visible(false);

    if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
    {
        m_xHideCB->set_visible(false);
        m_xConditionFT->set_visible(false);
        m_xConditionED->set_visible(false);
    }

    m_bAreProtected = m_rSh.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS);

    // disabled until "Hide" flag is not checked
    m_xConditionED->set_sensitive(false);
    m_xConditionFT->set_sensitive(false);

    // restore dialog size
    SvtViewOptions aDlgOpt(EViewType::Dialog, u"BookmarkDialog"_ustr);
    if (aDlgOpt.Exists())
        m_xDialog->set_window_state(aDlgOpt.GetWindowState());

    if (pSelected)
    {
        if (m_xBookmarksBox->SelectByName(*pSelected))
        {
            SelectionChanged();
            // which is better, focus on a button or focus on the table row?
            // as long as editing doesn't work via the TreeView with VCL
            // widgets, better on button.
            m_xEditTextBtn->grab_focus();
        }
    }
}

SwInsertBookmarkDlg::~SwInsertBookmarkDlg()
{
    // tdf#146261 - Remember size of bookmark dialog
    SvtViewOptions aDlgOpt(EViewType::Dialog, u"BookmarkDialog"_ustr);
    OUString sWindowState = m_xDialog->get_window_state(vcl::WindowDataMask::PosSize);
    aDlgOpt.SetWindowState(sWindowState);
}

IMPL_LINK(SwInsertBookmarkDlg, HeaderBarClick, int, nColumn, void)
{
    if (!m_bSorted)
    {
        m_xBookmarksBox->make_sorted();
        m_bSorted = true;
    }

    bool bSortAtoZ = m_xBookmarksBox->get_sort_order();

    //set new arrow positions in headerbar
    if (nColumn == m_xBookmarksBox->get_sort_column())
    {
        bSortAtoZ = !bSortAtoZ;
        m_xBookmarksBox->set_sort_order(bSortAtoZ);
    }
    else
    {
        int nOldSortColumn = m_xBookmarksBox->get_sort_column();
        if (nOldSortColumn != -1)
            m_xBookmarksBox->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
        m_xBookmarksBox->set_sort_column(nColumn);
    }

    if (nColumn != -1)
    {
        //sort lists
        m_xBookmarksBox->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
    }
}

BookmarkTable::BookmarkTable(std::unique_ptr<weld::TreeView> xControl)
    : m_xControl(std::move(xControl))
{
    m_xControl->set_size_request(-1, m_xControl->get_height_rows(8));
    m_xControl->set_column_fixed_widths({ 40, 110, 150, 160 });
    m_xControl->set_selection_mode(SelectionMode::Multiple);
}

std::unique_ptr<weld::TreeIter> BookmarkTable::get_selected() const
{
    std::unique_ptr<weld::TreeIter> xIter(m_xControl->make_iterator());
    if (!m_xControl->get_selected(xIter.get()))
        xIter.reset();
    return xIter;
}

void BookmarkTable::InsertBookmark(SwWrtShell& rSh, sw::mark::MarkBase* const pMark)
{
    sw::mark::Bookmark* pBookmark = dynamic_cast<sw::mark::Bookmark*>(pMark);
    assert(pBookmark);

    OUString sBookmarkNodeText;
    static const sal_Int32 nMaxTextLen = 50;

    if (pBookmark->IsExpanded())
    {
        rSh.Push();
        rSh.GotoMark(pBookmark);
        rSh.GetSelectedText(sBookmarkNodeText, ParaBreakType::ToBlank);
        rSh.Pop(SwEditShell::PopMode::DeleteCurrent);
    }
    if (nMaxTextLen < sBookmarkNodeText.getLength())
    {
        sBookmarkNodeText = sBookmarkNodeText.subView(0, nMaxTextLen);
        ;
        sBookmarkNodeText += u"…";
    }

    const OUString& sHideCondition = pBookmark->GetHideCondition();
    const SwMarkName& sName = pBookmark->GetName();
    OUString sHidden
        = (pBookmark->IsHidden() || !sHideCondition.isEmpty() ||
           // tdf#150955 add "hidden" status to the imported OOXML _Toc and _Ref bookmarks
           // to allow separating custom bookmarks by sorting based on their Hidden status.
           // Note: this "hidden" means here only that these bookmarks haven't got
           // visible bookmark formatting aids (gray I-shape or brackets), otherwise
           // their anchor are still visible.
           sName.toString().startsWith("_Toc") || sName.toString().startsWith("_Ref"))
              ? SwResId(STR_BOOKMARK_YES)
              : SwResId(STR_BOOKMARK_NO);

    OUString sPageNum = OUString::number(SwPaM(pMark->GetMarkStart()).GetPageNum());
    int nRow = m_xControl->n_children();
    m_xControl->append(weld::toId(pMark), sPageNum);
    m_xControl->set_text(nRow, sName.toString(), 1);
    m_xControl->set_text(nRow, sBookmarkNodeText, 2);
    m_xControl->set_text(nRow, sHidden, 3);
    m_xControl->set_text(nRow, sHideCondition, 4);
}

std::unique_ptr<weld::TreeIter> BookmarkTable::GetRowByBookmarkName(const OUString& sName)
{
    std::unique_ptr<weld::TreeIter> xRet;
    m_xControl->all_foreach([this, &sName, &xRet](weld::TreeIter& rEntry) {
        sw::mark::MarkBase* pBookmark
            = weld::fromId<sw::mark::MarkBase*>(m_xControl->get_id(rEntry));
        if (pBookmark->GetName() == sName)
        {
            xRet = m_xControl->make_iterator(&rEntry);
            return true;
        }
        return false;
    });
    return xRet;
}

sw::mark::MarkBase* BookmarkTable::GetBookmarkByName(const OUString& sName)
{
    auto xEntry = GetRowByBookmarkName(sName);
    if (!xEntry)
        return nullptr;

    return weld::fromId<sw::mark::MarkBase*>(m_xControl->get_id(*xEntry));
}

bool BookmarkTable::SelectByName(const OUString& sName)
{
    auto xEntry = GetRowByBookmarkName(sName);
    if (!xEntry)
        return false;
    select(*xEntry);
    return true;
}

OUString BookmarkTable::GetNameProposal() const
{
    OUString sDefaultBookmarkName = SwResId(STR_BOOKMARK_DEF_NAME);
    sal_Int32 nHighestBookmarkId = 0;
    for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i)
    {
        sw::mark::MarkBase* pBookmark = weld::fromId<sw::mark::MarkBase*>(m_xControl->get_id(i));
        const SwMarkName& sName = pBookmark->GetName();
        sal_Int32 nIndex = 0;
        if (o3tl::getToken(sName.toString(), 0, ' ', nIndex) == sDefaultBookmarkName)
        {
            sal_Int32 nCurrBookmarkId
                = o3tl::toInt32(o3tl::getToken(sName.toString(), 0, ' ', nIndex));
            nHighestBookmarkId = std::max<sal_Int32>(nHighestBookmarkId, nCurrBookmarkId);
        }
    }
    return sDefaultBookmarkName + " " + OUString::number(nHighestBookmarkId + 1);
}

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