761 lines
26 KiB
C++
761 lines
26 KiB
C++
/* -*- 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;
|
|
}
|
|
}
|