/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * 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 <sal/config.h>
#include <sfx2/bindings.hxx>
 
#include <PostItMgr.hxx>
#include <postithelper.hxx>
#include <AnnotationWin.hxx>
#include <fmtfld.hxx>
#include <docufld.hxx>
#include <txtfld.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <MarkManager.hxx>
#include <ndtxt.hxx>
#include <swmodule.hxx>
#include <vcl/svapp.hxx>
#include <rtl/ustring.hxx>
#include <xmloff/xmlmetae.hxx>
#include <tools/datetimeutils.hxx>
#include <swtypes.hxx>
#include <view.hxx>
#include <wrtsh.hxx>
#include <tools/date.hxx>
#include <tools/datetime.hxx>
#include <tools/time.hxx>
#include <unotools/localedatawrapper.hxx>
#include <unotools/syslocale.hxx>
#include <tools/link.hxx>
#include <editeng/outliner.hxx>
#include <editeng/editeng.hxx>
#include <svtools/ctrlbox.hxx>
#include <vcl/event.hxx>
 
#include <strings.hrc>
#include <cmdid.h>
 
#include "CommentsPanel.hxx"
#include <annotationmark.hxx>
#include <pam.hxx>
 
#include <com/sun/star/lang/IllegalArgumentException.hpp>
 
namespace sw::sidebar
{
Comment::Comment(weld::Container* pParent, CommentsPanel& rCommentsPanel)
    : mxBuilder(Application::CreateBuilder(pParent, "modules/swriter/ui/commentwidget.ui"))
    , mxContainer(mxBuilder->weld_container("Comment"))
    , mxExpander(mxBuilder->weld_expander("expander"))
    , mxAuthor(mxBuilder->weld_label("authorlabel"))
    , mxDate(mxBuilder->weld_label("datelabel"))
    , mxTime(mxBuilder->weld_label("timelabel"))
    , mxReply(mxBuilder->weld_button("replybutton"))
    , mxResolve(mxBuilder->weld_check_button("resolvebutton"))
    , mxTextView(mxBuilder->weld_text_view("textview"))
    , mrCommentsPanel(rCommentsPanel)
    , maDate(Date::EMPTY)
    , maTime(tools::Time::EMPTY)
    , mbResolved(false)
{
    mxTextView->set_editable(false);
    mxTextView->set_tooltip_text(SwResId(STR_COMMENT_VIEW_MODE));
    mxTextView->connect_focus_out(LINK(this, Comment, OnFocusOut));
    mxResolve->connect_toggled(LINK(this, Comment, ResolveClicked));
    mxReply->connect_clicked(LINK(this, Comment, ReplyClicked));
    mxExpander->connect_mouse_press(LINK(this, Comment, ContextMenuHdl));
}
 
IMPL_LINK(Comment, ContextMenuHdl, const MouseEvent&, rMEvt, bool)
{
    if (rMEvt.IsRight())
    {
        std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(
            mxExpander.get(), u"modules/swriter/ui/commentcontextmenu.ui"_ustr));
        std::unique_ptr<weld::Menu> xPopMenu(xBuilder->weld_menu(u"contextmenu"_ustr));
        Point aPos = rMEvt.GetPosPixel();
        OUString sId
            = xPopMenu->popup_at_rect(mxExpander.get(), tools::Rectangle(aPos, Size(1, 1)));
 
        if (sId == "edit")
        {
            this->makeEditable();
            getTextView()->set_tooltip_text(SwResId(STR_COMMENT_EDIT_MODE));
        }
        else if (sId == "reply")
            mrCommentsPanel.ReplyComment(this);
        else if (sId == "delete")
            mrCommentsPanel.DeleteComment(this);
        else if (sId == "toggle_resolved")
            mrCommentsPanel.ToggleResolved(this);
        else if (sId == "delete_thread")
            mrCommentsPanel.DeleteThread(this);
        else if (sId == "resolve_thread")
            mrCommentsPanel.ResolveThread(this);
        return true;
    }
    return false;
}
 
OUString CommentsPanel::getReferenceText(SwTextNode* pTextNode, sw::mark::AnnotationMark* pMark)
{
    if (!pMark)
        return OUString();
    const SwPosition& rStart = pMark->GetMarkStart();
    const SwPosition& rEnd = pMark->GetMarkEnd();
    OUString sReferenceText = pTextNode->GetText().copy(
        rStart.GetContentIndex(), rEnd.GetContentIndex() - rStart.GetContentIndex() - 1);
    return sReferenceText;
}
 
Comment::~Comment() {}
 
void Comment::InitControls(const SwPostItField* pPostItField)
{
    if (!pPostItField)
        return;
    msText = pPostItField->GetText();
    msAuthor = pPostItField->GetPar1();
    maDate = Date(pPostItField->GetDateTime());
    maTime = tools::Time(pPostItField->GetDateTime());
    mbResolved = pPostItField->GetResolved();
 
    OUString sDate = sw::sidebar::CommentsPanel::FormatDate(maDate);
    OUString sTime = sw::sidebar::CommentsPanel::FormatTime(maTime);
    if (mxDate->get_label() != sDate)
    {
        mxDate->set_label(sDate);
    }
    if (mxTime->get_label() != sTime)
    {
        mxTime->set_label(sTime);
    }
 
    mxAuthor->set_label(msAuthor);
    mxAuthor->set_tooltip_text(msAuthor);
    mxResolve->set_active(mbResolved);
    mxTextView->set_text(msText);
}
 
IMPL_LINK_NOARG(Comment, OnFocusOut, weld::Widget&, void)
{
    mrCommentsPanel.EditComment(this);
    getTextView()->set_tooltip_text(SwResId(STR_COMMENT_VIEW_MODE));
}
 
IMPL_LINK_NOARG(Comment, ResolveClicked, weld::Toggleable&, void)
{
    mrCommentsPanel.ToggleResolved(this);
    mrCommentsPanel.ShowResolvedHdl(mrCommentsPanel.getShowResolved());
}
 
IMPL_LINK_NOARG(Comment, ReplyClicked, weld::Button&, void) { mrCommentsPanel.ReplyComment(this); }
 
Thread::Thread(weld::Container* pParent)
    : mxBuilder(Application::CreateBuilder(pParent, "modules/swriter/ui/commentsthread.ui"))
    , mxContainer(mxBuilder->weld_container("Thread"))
    , mxExpander(mxBuilder->weld_expander("expander"))
    , mxCommentBox(mxBuilder->weld_box("comments_box"))
{
    // mxContainer->set_size_request(-1, mxContainer->get_preferred_size().Height());
}
 
Thread::~Thread() {}
 
std::unique_ptr<PanelLayout> CommentsPanel::Create(weld::Widget* pParent)
{
    if (pParent == nullptr)
        throw css::lang::IllegalArgumentException("no parent window given to CommentsPanel::Create",
                                                  nullptr, 0);
    return std::make_unique<CommentsPanel>(pParent);
}
 
CommentsPanel::CommentsPanel(weld::Widget* pParent)
    : PanelLayout(pParent, "CommentsPanel", "modules/swriter/ui/commentspanel.ui")
    , mpDoc(nullptr)
    , mpPostItMgr(nullptr)
    , mxFilterAuthor(m_xBuilder->weld_combo_box("filter_author"))
    , mxFilterDate(new SvtCalendarBox(m_xBuilder->weld_menu_button("filter_date"), true))
    , mxResetDate(m_xBuilder->weld_button("reset"))
    , mxShowTime(m_xBuilder->weld_check_button("show_time"))
    , mxShowResolved(m_xBuilder->weld_check_button("show_resolved"))
    , mxShowReference(m_xBuilder->weld_check_button("show_reference"))
    , mxSortbyPosition(m_xBuilder->weld_radio_button("sortby_position"))
    , mxSortbyTime(m_xBuilder->weld_radio_button("sortby_time"))
    , mxThreadsContainer(m_xBuilder->weld_box("comment_threads"))
{
    mxFilterAuthor->connect_changed(LINK(this, CommentsPanel, FilterByAuthor));
    mxFilterDate->connect_activated(LINK(this, CommentsPanel, FilterByDate));
    mxResetDate->connect_clicked(LINK(this, CommentsPanel, ResetDate));
    mxShowTime->connect_toggled(LINK(this, CommentsPanel, ShowTimeHdl));
    mxShowResolved->connect_toggled(LINK(this, CommentsPanel, ShowResolvedHdl));
    mxSortbyPosition->connect_toggled(LINK(this, CommentsPanel, SortHdl));
    mxSortbyTime->connect_toggled(LINK(this, CommentsPanel, SortHdl));
 
    SwView* pView = GetActiveView();
    if (!pView)
        return;
    SwWrtShell& rSh = pView->GetWrtShell();
    mpPostItMgr = rSh.GetPostItMgr();
    mpDoc = rSh.GetDoc();
    populateComments();
 
    StartListening(*mpPostItMgr);
}
 
IMPL_LINK_NOARG(CommentsPanel, SortHdl, weld::Toggleable&, void) { populateComments(); }
 
void CommentsPanel::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{
    if (rHint.GetId() == SfxHintId::SwFormatField)
    {
        const SwFormatFieldHint* pFormatHint = static_cast<const SwFormatFieldHint*>(&rHint);
        const SwFormatField* pField = pFormatHint->GetField();
        switch (pFormatHint->Which())
        {
            case SwFormatFieldHintWhich::INSERTED:
            {
                if (!pField)
                {
                    break;
                }
                // get field to be inserted from hint
                if (pField->IsFieldInDoc())
                {
                    addComment(pField);
                }
                else
                {
                    OSL_FAIL("Inserted field not in document!");
                }
                break;
            }
            case SwFormatFieldHintWhich::REMOVED:
            case SwFormatFieldHintWhich::REDLINED_DELETION:
            {
                sw::annotation::SwAnnotationWin* pAnnotationWin
                    = mpPostItMgr->GetRemovedAnnotationWin(pField);
                sal_uInt32 nId = getPostItId(pAnnotationWin);
                deleteComment(nId);
                break;
            }
            case SwFormatFieldHintWhich::FOCUS:
            {
                break;
            }
            case SwFormatFieldHintWhich::CHANGED:
            case SwFormatFieldHintWhich::RESOLVED:
            {
                SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC);
                SwPostItField* pPostItField = static_cast<SwPostItField*>(pFormatField->GetField());
                sw::annotation::SwAnnotationWin* pAnnotationWin
                    = mpPostItMgr->GetAnnotationWin(pPostItField);
                setResolvedStatus(pAnnotationWin);
                break;
            }
        }
    }
}
 
OUString CommentsPanel::FormatDate(Date& rDate)
{
    const SvtSysLocale aSysLocale;
    const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData();
    OUString sMeta;
    if (rDate.IsValidAndGregorian())
    {
        sMeta = rLocalData.getDate(rDate);
    }
    else
    {
        sMeta = SwResId(STR_NODATE);
    }
    return sMeta;
}
 
OUString CommentsPanel::FormatTime(tools::Time& rTime)
{
    const SvtSysLocale aSysLocale;
    const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData();
    OUString sMeta;
    if (rTime.GetTime() != 0)
    {
        sMeta = " " + rLocalData.getTime(rTime, false);
    }
    return sMeta;
}
 
sw::annotation::SwAnnotationWin* CommentsPanel::getRootCommentWin(const SwFormatField* pFormatField)
{
    if (!pFormatField)
        return nullptr;
    const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pFormatField->GetField());
    sw::annotation::SwAnnotationWin* pAnnotationWin = mpPostItMgr->GetAnnotationWin(pPostItField);
    if (!pAnnotationWin)
        return nullptr;
    sw::annotation::SwAnnotationWin* pRootNote = pAnnotationWin->GetTopReplyNote();
    return pRootNote;
}
 
sal_uInt32 CommentsPanel::getPostItId(sw::annotation::SwAnnotationWin* pAnnotationWin)
{
    const SwPostItField* pField = pAnnotationWin->GetPostItField();
    return pField->GetPostItId();
}
 
sw::annotation::SwAnnotationWin* CommentsPanel::getAnnotationWin(Comment* pComment)
{
    sal_uInt32 nPostItId = 0;
    for (auto & [ rId, rComment ] : mpCommentsMap)
    {
        if (rComment.get() != pComment)
        {
            continue;
        }
        nPostItId = rId;
        break;
    }
    auto& vPostItFields = mpPostItMgr->GetPostItFields();
    SwPostItField* pPostItField = nullptr;
    for (auto& pItem : vPostItFields)
    {
        SwFormatField* pFormatField = &pItem->GetFormatField();
        SwPostItField* pField = static_cast<SwPostItField*>(pFormatField->GetField());
        if (pField->GetPostItId() == nPostItId)
        {
            pPostItField = pField;
            break;
        }
    }
    return mpPostItMgr->GetAnnotationWin(pPostItField);
}
 
bool CommentsPanel::comp_dateTime(SwFormatField* a, SwFormatField* b)
{
    SwPostItField* pA = static_cast<SwPostItField*>(a->GetField());
    SwPostItField* pB = static_cast<SwPostItField*>(b->GetField());
 
    Date aDateA(pA->GetDateTime());
    tools::Time aTimeA(pA->GetDateTime());
    Date aDateB(pB->GetDateTime());
    tools::Time aTimeB(pB->GetDateTime());
 
    OUString sDateTimeA = FormatTime(aTimeA) + " " + FormatDate(aDateA);
    OUString sDateTimeB = FormatTime(aTimeB) + " " + FormatDate(aDateB);
 
    return sDateTimeA > sDateTimeB;
}
 
SwPosition CommentsPanel::getAnchorPosition(SwFormatField* pField)
{
    SwTextField* pTextField = pField->GetTextField();
    SwTextNode* pTextNode = pTextField->GetpTextNode();
 
    SwPosition aPos(*pTextNode, pTextField->GetStart());
    return aPos;
}
 
bool CommentsPanel::comp_position(SwFormatField* a, SwFormatField* b)
{
    SwPosition aPosA = getAnchorPosition(a);
    SwPosition aPosB = getAnchorPosition(b);
 
    return aPosA < aPosB;
}
 
void CommentsPanel::populateComments()
{
    if (!mpCommentsMap.empty())
    {
        for (auto it = mpCommentsMap.begin(); it != mpCommentsMap.end();)
        {
            sal_uInt32 nId = it->first;
            it++;
            deleteComment(nId);
        }
    }
 
    if (!mpPostItMgr)
        return;
    std::vector<SwFormatField*> vFormatFields = mpPostItMgr->UpdatePostItsParentInfo();
    if (mxSortbyTime->get_active())
    {
        std::sort(vFormatFields.begin(), vFormatFields.end(),
                  [](SwFormatField* a, SwFormatField* b) {
                      return sw::sidebar::CommentsPanel::comp_dateTime(a, b);
                  });
    }
    else
    {
        std::stable_sort(vFormatFields.begin(), vFormatFields.end(),
                         [](SwFormatField* a, SwFormatField* b) {
                             return sw::sidebar::CommentsPanel::comp_position(a, b);
                         });
    }
 
    for (auto pFormatField : vFormatFields)
    {
        sw::annotation::SwAnnotationWin* pRootNote = getRootCommentWin(pFormatField);
        if (!pRootNote)
            continue;
        sal_uInt32 nRootId = getPostItId(pRootNote);
 
        if (mpThreadsMap.contains(nRootId))
        {
            if (mxSortbyPosition->get_active())
                continue;
            else
            {
                auto pThread = mpThreadsMap[nRootId].get();
                SwPostItField* pPostItField = static_cast<SwPostItField*>(pFormatField->GetField());
                sal_uInt32 nId = pPostItField->GetPostItId();
                auto pComment = std::make_unique<Comment>(pThread->getCommentBoxWidget(), *this);
                pThread->getCommentBoxWidget()->reorder_child(pComment->get_widget(),
                                                              pThread->mnComments++);
                pComment->InitControls(pPostItField);
                mpAuthorSet.insert(pComment->GetAuthor());
                mpCommentsMap[nId] = std::move(pComment);
                continue;
            }
        }
 
        auto pThread = std::make_unique<Thread>(mxThreadsContainer.get());
        mxThreadsContainer->reorder_child(pThread->get_widget(), mnThreads++);
 
        if (mxSortbyPosition->get_active())
        {
            for (sw::annotation::SwAnnotationWin* pCurrent = pRootNote;;)
            {
                sal_uInt32 nId = getPostItId(pCurrent);
                auto pComment = std::make_unique<Comment>(pThread->getCommentBoxWidget(), *this);
                pThread->getCommentBoxWidget()->reorder_child(pComment->get_widget(),
                                                              pThread->mnComments++);
                pComment->InitControls(pCurrent->GetPostItField());
                mpAuthorSet.insert(pComment->GetAuthor());
                mpCommentsMap[nId] = std::move(pComment);
                sw::annotation::SwAnnotationWin* next
                    = mpPostItMgr->GetNextPostIt(KEY_PAGEDOWN, pCurrent);
                if (!next || next->GetTopReplyNote() != pRootNote)
                    break;
                pCurrent = next;
            }
        }
        else
        {
            SwPostItField* pPostItField = static_cast<SwPostItField*>(pFormatField->GetField());
            sal_uInt32 nId = pPostItField->GetPostItId();
            auto pComment = std::make_unique<Comment>(pThread->getCommentBoxWidget(), *this);
            pThread->getCommentBoxWidget()->reorder_child(pComment->get_widget(),
                                                          pThread->mnComments++);
            pComment->InitControls(pPostItField);
            mpAuthorSet.insert(pComment->GetAuthor());
            mpCommentsMap[nId] = std::move(pComment);
        }
        mpThreadsMap[nRootId] = std::move(pThread);
        setReferenceText(nRootId);
    }
    populateAuthorComboBox();
}
 
void CommentsPanel::addComment(const SwFormatField* pField)
{
    // Get id of the note
    const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField->GetField());
    sal_uInt32 nNoteId = pPostItField->GetPostItId();
 
    if (mpCommentsMap.contains(nNoteId))
        return;
 
    // Get id of the root note
    sw::annotation::SwAnnotationWin* pRootNote = getRootCommentWin(pField);
    if (!pRootNote)
        return;
    sal_uInt32 nRootId = getPostItId(pRootNote);
 
    sw::annotation::SwAnnotationWin* pNote
        = mpPostItMgr->GetAnnotationWin(static_cast<const SwPostItField*>(pField->GetField()));
    // If comment is added to an existing thread
    if (mpThreadsMap.contains(nRootId))
    {
        auto& pThread = mpThreadsMap[nRootId];
        auto pComment = std::make_unique<Comment>(pThread->getCommentBoxWidget(), *this);
        pThread->getCommentBoxWidget()->reorder_child(pComment->get_widget(),
                                                      pThread->mnComments++);
        pComment->InitControls(pNote->GetPostItField());
        pComment->getTextView()->set_tooltip_text(SwResId(STR_COMMENT_EDIT_MODE));
        mpAuthorSet.insert(pComment->GetAuthor());
        mpCommentsMap[nNoteId] = std::move(pComment);
    }
    // If a new thread is created
    else
    {
        auto pThread = std::make_unique<Thread>(mxThreadsContainer.get());
        mxThreadsContainer->reorder_child(pThread->get_widget(), mnThreads++);
        auto pComment = std::make_unique<Comment>(pThread->getCommentBoxWidget(), *this);
        pThread->getCommentBoxWidget()->reorder_child(pComment->get_widget(),
                                                      pThread->mnComments++);
        mpThreadsMap[nRootId] = std::move(pThread);
        setReferenceText(nRootId);
        pComment->InitControls(pNote->GetPostItField());
        pComment->getTextView()->set_tooltip_text(SwResId(STR_COMMENT_EDIT_MODE));
        mpAuthorSet.insert(pComment->GetAuthor());
        mpCommentsMap[nNoteId] = std::move(pComment);
    }
    populateComments();
}
 
void CommentsPanel::deleteComment(sal_uInt32 nId)
{
    sw::annotation::SwAnnotationWin* pAnnotationWin = getAnnotationWin(mpCommentsMap[nId].get());
    SwFormatField* pFormatField = pAnnotationWin->GetFormatField();
    sw::annotation::SwAnnotationWin* pRootNote = getRootCommentWin(pFormatField);
    // // If the root comment is deleted, the new root comment of the thread should be the next comment in the thread
    // // but due to a bug `getRootCommentWin` returns root comment of some other/random thread so we completely lose
    // // access to the current thread.
    // if (mpThreadsMap.find(nId) != mpThreadsMap.end())
    // {
    //     pRootNote = mpThreadsMap[nId];
    // }
    // else
    // {
    //     pRootNote = getRootCommentWin(pFormatField);
    // }
 
    sal_uInt32 nRootId = getPostItId(pRootNote);
 
    if (mpThreadsMap.find(nRootId) == mpThreadsMap.end())
    {
        SAL_WARN("sw",
                 "Comments Panel is unable to delete comment: Referenced thread does not exist!");
        return;
    }
    auto& pComment = mpCommentsMap[nId];
    auto& pThread = mpThreadsMap[nRootId];
    if (!pComment)
    {
        SAL_WARN("sw",
                 "Comments Panel is unable to delete comment: Referenced comment does not exist!");
        return;
    }
 
    pThread->getCommentBoxWidget()->move(pComment->get_widget(), nullptr);
    mpCommentsMap.erase(nId);
 
    // If the last comment in the thread is deleted, delete the thread
    if (--pThread->mnComments == 0)
    {
        mxThreadsContainer->move(pThread->get_widget(), nullptr);
        if (mpThreadsMap.contains(nRootId))
            mpThreadsMap.erase(nRootId);
        mnThreads--;
    }
}
 
void CommentsPanel::setResolvedStatus(sw::annotation::SwAnnotationWin* pAnnotationWin)
{
    sal_uInt32 nId = getPostItId(pAnnotationWin);
    if (mpCommentsMap.find(nId) == mpCommentsMap.end())
        return;
    auto& pComment = mpCommentsMap[nId];
    if (!pComment)
        return;
    SwPostItField* pPostItField = const_cast<SwPostItField*>(pAnnotationWin->GetPostItField());
    if (pPostItField->GetResolved() == pComment->mbResolved)
    {
        editComment(pPostItField, pComment.get());
        return;
    }
    pComment->mbResolved = pPostItField->GetResolved();
    pComment->mxResolve->set_active(pComment->mbResolved);
}
 
void CommentsPanel::editComment(SwPostItField* pPostItField, Comment* pComment)
{
    const OUString& sText = pPostItField->GetText();
    pComment->SetCommentText(sText);
    pComment->mxTextView->set_text(sText);
}
 
void CommentsPanel::setReferenceText(sal_uInt32 nRootId)
{
    Thread* pThread = mpThreadsMap[nRootId].get();
    Comment* pRootComment = mpCommentsMap[nRootId].get();
    sw::annotation::SwAnnotationWin* pRootNote = getAnnotationWin(pRootComment);
    SwFormatField* pField = pRootNote->GetFormatField();
    const SwPosition pAnchor = getAnchorPosition(pField);
    SwNodeIndex aIdx(pAnchor.nNode);
    SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
    sw::mark::MarkManager& rMarkManager = mpDoc->GetMarkManager();
    sw::mark::AnnotationMark* pMark = rMarkManager.getAnnotationMarkFor(pAnchor);
    OUString sText = getReferenceText(pTextNode, pMark);
    pThread->getExpander()->set_label(sText);
}
 
void CommentsPanel::EditComment(Comment* pComment)
{
    if (!pComment)
        return;
    if (!pComment->mxTextView->get_editable())
        return;
    const OUString sText = pComment->mxTextView->get_text();
 
    sw::annotation::SwAnnotationWin* pWin = getAnnotationWin(pComment);
    Outliner* pOutliner = pWin->GetOutliner();
    pOutliner->Clear();
    pOutliner->SetText(sText, pOutliner->GetParagraph(0));
    pComment->mxTextView->set_editable(false);
}
 
void CommentsPanel::ToggleResolved(Comment* pComment)
{
    if (!pComment)
        return;
    sw::annotation::SwAnnotationWin* pWin = getAnnotationWin(pComment);
    pWin->ToggleResolved();
}
 
void CommentsPanel::ReplyComment(Comment* pComment)
{
    if (!pComment)
        return;
    sw::annotation::SwAnnotationWin* pWin = getAnnotationWin(pComment);
    pWin->ExecuteCommand(FN_REPLY);
}
 
void CommentsPanel::DeleteComment(Comment* pComment)
{
    if (!pComment)
        return;
    sw::annotation::SwAnnotationWin* pWin = getAnnotationWin(pComment);
    pWin->ExecuteCommand(FN_DELETE_COMMENT);
}
 
void CommentsPanel::DeleteThread(Comment* pComment)
{
    if (!pComment)
        return;
    sw::annotation::SwAnnotationWin* pWin = getAnnotationWin(pComment);
    pWin->ExecuteCommand(FN_DELETE_COMMENT_THREAD);
}
 
void CommentsPanel::ResolveThread(Comment* pComment)
{
    if (!pComment)
        return;
    sw::annotation::SwAnnotationWin* pWin = getAnnotationWin(pComment);
    pWin->ExecuteCommand(FN_RESOLVE_NOTE_THREAD);
}
 
void CommentsPanel::populateAuthorComboBox()
{
    mxFilterAuthor->clear();
    if (mpAuthorSet.empty())
        return;
    mxFilterAuthor->append_text("All");
    for (const OUString& rAuthor : mpAuthorSet)
    {
        mxFilterAuthor->append_text(rAuthor);
    }
    mxFilterAuthor->set_active_text("All");
}
 
IMPL_LINK_NOARG(CommentsPanel, FilterByAuthor, weld::ComboBox&, void)
{
    OUString sAuthor = mxFilterAuthor->get_active_text();
    if (sAuthor == "All")
    {
        for (auto & [ nId, pComment ] : mpCommentsMap)
        {
            if (mbDateSelected && mxFilterDate->get_date() != pComment->GetDate())
                continue;
            pComment->get_widget()->set_visible(true);
        }
    }
    else
    {
        for (auto & [ nId, pComment ] : mpCommentsMap)
        {
            if (sAuthor == pComment->GetAuthor())
            {
                if (mbDateSelected && mxFilterDate->get_date() != pComment->GetDate())
                    continue;
                pComment->get_widget()->set_visible(true);
            }
            else
            {
                pComment->get_widget()->set_visible(false);
            }
        }
    }
}
 
IMPL_LINK_NOARG(CommentsPanel, FilterByDate, SvtCalendarBox&, void)
{
    mbDateSelected = true;
    Date aDate(mxFilterDate->get_date());
    for (auto & [ nId, pComment ] : mpCommentsMap)
    {
        if (aDate == pComment->GetDate())
        {
            pComment->get_widget()->set_visible(true);
        }
        else
        {
            pComment->get_widget()->set_visible(false);
        }
    }
    FilterByAuthor(*mxFilterAuthor);
}
 
IMPL_LINK_NOARG(CommentsPanel, ResetDate, weld::Button&, void)
{
    mbDateSelected = false;
    FilterByAuthor(*mxFilterAuthor);
}
 
IMPL_LINK_NOARG(CommentsPanel, ShowTimeHdl, weld::Toggleable&, void)
{
    bool bShowTime = mxShowTime->get_active();
    for (auto & [ rId, pComment ] : mpCommentsMap)
    {
        pComment->mxTime->set_visible(bShowTime);
    }
}
 
IMPL_LINK_NOARG(CommentsPanel, ShowResolvedHdl, weld::Toggleable&, void)
{
    bool bShowResolved = mxShowResolved->get_active();
    for (auto & [ rId, pComment ] : mpCommentsMap)
    {
        if (pComment->mxResolve->get_active())
        {
            pComment->get_widget()->set_visible(bShowResolved);
        }
    }
}
 
CommentsPanel::~CommentsPanel() {}
 
void CommentsPanel::NotifyItemUpdate(const sal_uInt16 /*nSid*/, const SfxItemState /* eState */,
                                     const SfxPoolItem* pState)
{
    if (!pState) //disposed
        return;
}
}

V522 There might be dereferencing of a potential null pointer 'pFormatField'.