Files
odfstudio/sc/source/ui/operation/InsertThreadedCommentOperation.cxx
T
Xisco Fauli 7003eb6fc6 fix headers
Change-Id: Ia2732cc041367bc113654bc424ea6621f3961e9f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211724
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2026-10-05 08:52:04 +02:00

169 lines
6.3 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/.
*/
#include <operation/InsertThreadedCommentOperation.hxx>
#include <comphelper/hash.hxx>
#include <rtl/string.hxx>
#include <svx/svdocapt.hxx>
#include <tools/Guid.hxx>
#include <tools/datetime.hxx>
#include <unotools/datetime.hxx>
#include <unotools/useroptions.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <drwlayer.hxx>
#include <editable.hxx>
#include <postit.hxx>
#include <scresid.hxx>
#include <strings.hrc>
#include <undocell.hxx>
namespace sc
{
InsertThreadedCommentOperation::InsertThreadedCommentOperation(ScDocShell& rDocShell,
const ScAddress& rPos,
const OUString& rText,
const OUString* pAuthor, bool bApi)
: Operation(OperationType::InsertThreadedComment, false, bApi)
, mrDocShell(rDocShell)
, maPos(rPos)
, maText(rText)
, moAuthor(pAuthor ? std::optional<OUString>(*pAuthor) : std::nullopt)
{
}
bool InsertThreadedCommentOperation::runImplementation()
{
ScDocShellModificator aModificator(mrDocShell);
ScDocument& rDoc = mrDocShell.GetDocument();
ScAddress aPos = convertAddress(maPos);
ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(
rDoc, aPos.Tab(), aPos.Col(), aPos.Row(), aPos.Col(), aPos.Row());
if (!aTester.IsEditable())
{
if (!mbApi)
mrDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
SfxUndoManager* pUndoMgr
= (pDrawLayer && rDoc.IsUndoEnabled()) ? mrDocShell.GetUndoManager() : nullptr;
// Rescue the existing note (if any) for undo. Threaded data lives on ScPostIt
// outside ScNoteData, so capture it separately.
ScNoteData aOldData;
std::unique_ptr<ScThreadedCommentData> pOldThreadedData;
std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote(aPos);
sal_uInt32 nNoteId = 0;
if (pOldNote)
{
nNoteId = pOldNote->GetId();
// ensure existing caption object before draw undo tracking starts
pOldNote->GetOrCreateCaption(aPos);
aOldData = pOldNote->GetNoteData();
if (const ScThreadedCommentData* pOldThreaded = pOldNote->GetThreadedCommentData())
pOldThreadedData = std::make_unique<ScThreadedCommentData>(*pOldThreaded);
}
if (pUndoMgr)
pDrawLayer->BeginCalcUndo(false);
// delete the old note (creates drawing undo action for the caption object)
bool hadOldNote(pOldNote);
pOldNote.reset();
// create new note (creates drawing undo action for the new caption object)
ScNoteData aNewData;
std::unique_ptr<ScThreadedCommentData> pNewThreadedData;
ScPostIt* pNewNote = ScNoteUtil::CreateNoteFromString(rDoc, aPos, maText, false, true, nNoteId);
if (pNewNote)
{
OUString aAuthor = moAuthor.value_or(OUString());
if (aAuthor.isEmpty())
aAuthor = SvtUserOptions().GetFullName();
if (aAuthor.isEmpty())
aAuthor = ScResId(STR_CHG_UNKNOWN_AUTHOR);
// Resolve the person entry; create one with a stable UUIDv5 derived from
// the author name so the same user gets the same person-id across documents.
OUString aPersonId;
for (const auto& rPerson : rDoc.GetPersonList())
{
if (rPerson.maDisplayName == aAuthor)
{
aPersonId = rPerson.maId;
break;
}
}
if (aPersonId.isEmpty())
{
// Namespace string is wire-stable; changing it remaps every existing
// document's person-ids and breaks author matching on reopen.
OString aInput = "LibreOffice:ThreadedCommentPerson:"
+ OUStringToOString(aAuthor, RTL_TEXTENCODING_UTF8);
::std::vector<sal_uInt8> aDigest{ ::comphelper::Hash::calculateHash(
aInput.getStr(), aInput.getLength(), ::comphelper::HashType::SHA1) };
aDigest[6] = (aDigest[6] & 0x0F) | 0x50; // version 5
aDigest[8] = (aDigest[8] & 0x3F) | 0x80; // variant RFC 4122
tools::Guid aPersonGuid(aDigest.data());
ScPersonData aPerson;
aPerson.maId = aPersonGuid.getOUString();
aPerson.maDisplayName = aAuthor;
aPerson.maUserId = aAuthor;
aPerson.maProviderId = u"None"_ustr;
aPersonId = aPerson.maId;
rDoc.AddPerson(aPerson);
}
auto pThreaded = std::make_unique<ScThreadedCommentData>();
pThreaded->maRoot.maGuid = tools::Guid(tools::Guid::Generate).getOUString();
pThreaded->maRoot.maDateTime = utl::toISO8601(DateTime(DateTime::SYSTEM).GetUNODateTime());
pThreaded->maRoot.maPersonId = aPersonId;
pThreaded->maRoot.maText = maText;
pNewNote->SetAuthor(aAuthor);
pNewNote->SetThreadedCommentData(std::move(pThreaded));
aNewData = pNewNote->GetNoteData();
if (const ScThreadedCommentData* pNewThreaded = pNewNote->GetThreadedCommentData())
pNewThreadedData = std::make_unique<ScThreadedCommentData>(*pNewThreaded);
}
if (pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption))
pUndoMgr->AddUndoAction(std::make_unique<ScUndoReplaceNote>(
mrDocShell, aPos, std::move(aOldData), std::move(aNewData), pDrawLayer->GetCalcUndo(),
std::move(pOldThreadedData), std::move(pNewThreadedData)));
mrDocShell.PostPaintCell(aPos);
rDoc.SetStreamValid(aPos.Tab(), false);
syncSheetViews();
aModificator.SetDocumentModified();
if (pNewNote)
{
ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify
: LOKCommentNotificationType::Add,
rDoc, aPos, pNewNote);
}
return true;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */