Change-Id: Ia2732cc041367bc113654bc424ea6621f3961e9f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211724 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
169 lines
6.3 KiB
C++
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: */
|