/* -*- 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 <redline.hxx>
#include <tools/datetime.hxx>
#include <tools/lineend.hxx>
#include <svl/eitem.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/dispatch.hxx>
#include <svx/ctredlin.hxx>
#include <svx/postattr.hxx>
#include <utility>
#include <vcl/commandevent.hxx>
#include <swtypes.hxx>
#include <wrtsh.hxx>
#include <view.hxx>
#include <swmodule.hxx>
#include <redlndlg.hxx>
#include <swwait.hxx>
#include <uitool.hxx>
#include <o3tl/string_view.hxx>
#include <cmdid.h>
#include <strings.hrc>
// -> #111827#
#include <swundo.hxx>
#include <SwRewriter.hxx>
// <- #111827#
#include <vector>
#include <svx/svxdlg.hxx>
#include <osl/diagnose.h>
#include <bitmaps.hlst>
#include <docsh.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <usrpref.hxx>
#include <memory>
SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(SwRedlineAcceptChild, FN_REDLINE_ACCEPT)
SwRedlineAcceptChild::SwRedlineAcceptChild(vcl::Window* _pParent,
sal_uInt16 nId,
SfxBindings* pBindings,
SfxChildWinInfo* pInfo)
: SwChildWinWrapper(_pParent, nId)
{
auto xDlg = std::make_shared<SwModelessRedlineAcceptDlg>(pBindings, this, _pParent->GetFrameWeld());
SetController(xDlg);
xDlg->Initialize(pInfo);
}
// newly initialise dialog after document switch
bool SwRedlineAcceptChild::ReInitDlg(SwDocShell *pDocSh)
{
bool bRet = SwChildWinWrapper::ReInitDlg(pDocSh);
if (bRet) // update immediately, doc switch!
static_cast<SwModelessRedlineAcceptDlg*>(GetController().get())->Activate();
return bRet;
}
SwModelessRedlineAcceptDlg::SwModelessRedlineAcceptDlg(
SfxBindings* _pBindings, SwChildWinWrapper* pChild, weld::Window *pParent)
: SfxModelessDialogController(_pBindings, pChild, pParent,
u"svx/ui/acceptrejectchangesdialog.ui"_ustr, u"AcceptRejectChangesDialog"_ustr)
, m_xContentArea(m_xBuilder->weld_container(u"container"_ustr))
, m_pChildWin(pChild)
{
m_xImplDlg.reset(new SwRedlineAcceptDlg(m_xDialog, m_xBuilder.get(), m_xContentArea.get()));
}
void SwModelessRedlineAcceptDlg::Activate()
{
if (mbInDestruction)
return;
SwView *pView = ::GetActiveView();
if (!pView) // can happen when switching to another app, when a Listbox in dialog
return; // had the focus previously (actually THs Bug)
SwDocShell *pDocSh = pView->GetDocShell();
if (m_pChildWin->GetOldDocShell() != pDocSh)
{ // doc-switch
SwWait aWait( *pDocSh, false );
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
m_pChildWin->SetOldDocShell(pDocSh); // avoid recursion (using modified-Hdl)
bool bMod = pSh->IsModified();
SfxBoolItem aShow(FN_REDLINE_SHOW, true);
pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(
FN_REDLINE_SHOW, SfxCallMode::SYNCHRON|SfxCallMode::RECORD,
{ &aShow });
if (!bMod)
pSh->ResetModified();
m_xImplDlg->Init();
SfxModelessDialogController::Activate();
return;
}
SfxModelessDialogController::Activate();
m_xImplDlg->Activate();
}
void SwModelessRedlineAcceptDlg::Initialize(SfxChildWinInfo* pInfo)
{
if (pInfo != nullptr)
m_xImplDlg->Initialize(pInfo->aExtraString);
SfxModelessDialogController::Initialize(pInfo);
}
void SwModelessRedlineAcceptDlg::FillInfo(SfxChildWinInfo& rInfo) const
{
SfxModelessDialogController::FillInfo(rInfo);
m_xImplDlg->FillInfo(rInfo.aExtraString);
}
SwModelessRedlineAcceptDlg::~SwModelessRedlineAcceptDlg()
{
mbInDestruction = true;
}
namespace
{
const SwRedlineData* lcl_get_selected_redlinedata(weld::TreeView& rTreeView)
{
std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
if (rTreeView.get_selected(xEntry.get()))
{
RedlinData* pRedlinData = weld::fromId<RedlinData*>(rTreeView.get_id(*xEntry));
if (rTreeView.get_iter_depth(*xEntry))
return static_cast<SwRedlineDataChild*>(pRedlinData->pData)->pChild;
else
return static_cast<SwRedlineDataParent*>(pRedlinData->pData)->pData;
}
return nullptr;
}
void lcl_reselect(weld::TreeView& rTreeView, const SwRedlineData* pSelectedEntryRedlineData)
{
if (!pSelectedEntryRedlineData)
{
rTreeView.set_cursor(-1);
return;
}
rTreeView.all_foreach(
[&rTreeView, &pSelectedEntryRedlineData](weld::TreeIter& rIter)
{
RedlinData* pRedlinData = weld::fromId<RedlinData*>(rTreeView.get_id(rIter));
const SwRedlineData* pRedlineData;
if (rTreeView.get_iter_depth(rIter))
pRedlineData = static_cast<SwRedlineDataChild*>(pRedlinData->pData)->pChild;
else
pRedlineData = static_cast<SwRedlineDataParent*>(pRedlinData->pData)->pData;
if (pRedlineData == pSelectedEntryRedlineData)
{
rTreeView.set_cursor(rIter);
return true;
}
return false;
});
}
}
SwRedlineAcceptDlg::SwRedlineAcceptDlg(std::shared_ptr<weld::Window> xParent, weld::Builder *pBuilder,
weld::Container *pContentArea, bool bAutoFormat)
: m_xParentDlg(std::move(xParent))
, m_aSelectTimer("SwRedlineAcceptDlg m_aSelectTimer")
, m_sInserted(SwResId(STR_REDLINE_INSERTED))
, m_sDeleted(SwResId(STR_REDLINE_DELETED))
, m_sFormated(SwResId(STR_REDLINE_FORMATTED))
, m_sTableChgd(SwResId(STR_REDLINE_TABLECHG))
, m_sFormatCollSet(SwResId(STR_REDLINE_FMTCOLLSET))
, m_sAutoFormat(SwResId(STR_REDLINE_AUTOFMT))
, m_bOnlyFormatedRedlines(false)
, m_bRedlnAutoFormat(bAutoFormat)
, m_bInhibitActivate(false)
, m_bHasTrackedColumn(false)
, m_xTabPagesCTRL(new SvxAcceptChgCtr(pContentArea))
, m_xPopup(pBuilder->weld_menu(u"writermenu"_ustr))
, m_xSortMenu(pBuilder->weld_menu(u"writersortmenu"_ustr))
{
m_pTPView = m_xTabPagesCTRL->GetViewPage();
m_pTable = m_pTPView->GetTableControl();
m_pTable->SetWriterView();
m_pTPView->GetSortByComboBoxControl()->set_active(4);
m_pTPView->SetSortByComboBoxChangedHdl(
LINK(this, SwRedlineAcceptDlg, SortByComboBoxChangedHdl));
m_pTPView->SetAcceptClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptHdl));
m_pTPView->SetAcceptAllClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptAllHdl));
m_pTPView->SetRejectClickHdl(LINK(this, SwRedlineAcceptDlg, RejectHdl));
m_pTPView->SetRejectAllClickHdl(LINK(this, SwRedlineAcceptDlg, RejectAllHdl));
m_pTPView->SetUndoClickHdl(LINK(this, SwRedlineAcceptDlg, UndoHdl));
//tdf#89227 default to disabled, and only enable if possible to accept/reject
m_pTPView->EnableAccept(false);
m_pTPView->EnableReject(false);
m_pTPView->EnableClearFormat(false);
m_pTPView->EnableAcceptAll(false);
m_pTPView->EnableRejectAll(false);
m_pTPView->EnableClearFormatAll(false);
m_xTabPagesCTRL->GetFilterPage()->SetReadyHdl(LINK(this, SwRedlineAcceptDlg, FilterChangedHdl));
weld::ComboBox* pActLB = m_xTabPagesCTRL->GetFilterPage()->GetLbAction();
pActLB->append_text(m_sInserted);
pActLB->append_text(m_sDeleted);
pActLB->append_text(m_sFormated);
pActLB->append_text(m_sTableChgd);
if (HasRedlineAutoFormat())
{
pActLB->append_text(m_sFormatCollSet);
pActLB->append_text(m_sAutoFormat);
m_pTPView->ShowUndo();
m_pTPView->DisableUndo(); // no UNDO events yet
}
pActLB->set_active(0);
weld::TreeView& rTreeView = m_pTable->GetWidget();
rTreeView.set_selection_mode(SelectionMode::Multiple);
rTreeView.connect_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl));
rTreeView.connect_popup_menu(LINK(this, SwRedlineAcceptDlg, CommandHdl));
// avoid multiple selection of the same texts:
m_aSelectTimer.SetTimeout(100);
m_aSelectTimer.SetInvokeHandler(LINK(this, SwRedlineAcceptDlg, GotoHdl));
// we want to receive SfxHintId::SwRedlineContentAtPos
StartListening(*(SW_MOD()->GetView()->GetDocShell()));
}
SwRedlineAcceptDlg::~SwRedlineAcceptDlg()
{
}
void SwRedlineAcceptDlg::Init(SwRedlineTable::size_type nStart)
{
std::optional<SwWait> oWait;
SwView* pView = GetActiveView();
if (pView)
oWait.emplace(*pView->GetDocShell(), false);
weld::TreeView& rTreeView = m_pTable->GetWidget();
m_aUsedSeqNo.clear();
// tdf#162018 keep the selected entry selected
const SwRedlineData* pSelectedEntryRedlineData = lcl_get_selected_redlinedata(rTreeView);
// tdf#162337 tracked change selection when the Manage Changes dialog is initially opened
if (pView && m_bInitialSelect)
{
m_bInitialSelect = false;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (pSh)
{
const SwRangeRedline* pCurrRedline = pSh->GetCurrRedline();
if (pCurrRedline)
{
// Select current redline
SwRedlineTable::size_type nPos
= pSh->FindRedlineOfData(pCurrRedline->GetRedlineData());
pSh->GotoRedline(nPos, true);
pSh->SetInSelect();
}
else
{
// Select the next redline if there is one
pSh->AssureStdMode();
pCurrRedline = pSh->SelNextRedline();
}
if (pCurrRedline)
pSelectedEntryRedlineData = &pCurrRedline->GetRedlineData();
}
}
rTreeView.freeze();
if (nStart)
RemoveParents(nStart, m_RedlineParents.size() - 1);
else
{
rTreeView.clear();
m_RedlinData.clear();
m_RedlineChildren.clear();
m_RedlineParents.erase(m_RedlineParents.begin() + nStart, m_RedlineParents.end());
}
rTreeView.thaw();
// insert parents
InsertParents(nStart);
InitAuthors();
lcl_reselect(rTreeView, pSelectedEntryRedlineData);
}
void SwRedlineAcceptDlg::InitAuthors()
{
if (!m_xTabPagesCTRL)
return;
SwView *pView = ::GetActiveView();
if (!pView)
return;
SwWrtShell* pSh = pView->GetWrtShellPtr();
SvxTPFilter *pFilterPage = m_xTabPagesCTRL->GetFilterPage();
std::vector<OUString> aStrings;
OUString sOldAuthor(pFilterPage->GetSelectedAuthor());
pFilterPage->ClearAuthors();
SwRedlineTable::size_type nCount = pSh ? pSh->GetRedlineCount() : 0;
m_bOnlyFormatedRedlines = true;
bool bIsNotFormated = false;
// determine authors
for ( SwRedlineTable::size_type i = 0; i < nCount; i++)
{
const SwRangeRedline& rRedln = pSh->GetRedline(i);
if( m_bOnlyFormatedRedlines && RedlineType::Format != rRedln.GetType() )
m_bOnlyFormatedRedlines = false;
aStrings.push_back(rRedln.GetAuthorString());
for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++)
{
aStrings.push_back(rRedln.GetAuthorString(nStack));
}
}
std::sort(aStrings.begin(), aStrings.end());
aStrings.erase(std::unique(aStrings.begin(), aStrings.end()), aStrings.end());
for (auto const & i: aStrings)
pFilterPage->InsertAuthor(i);
if (pFilterPage->SelectAuthor(sOldAuthor) == -1 && !aStrings.empty())
pFilterPage->SelectAuthor(aStrings[0]);
weld::TreeView& rTreeView = m_pTable->GetWidget();
SwDocShell* pShell = pSh ? pSh->GetDoc()->GetDocShell() : nullptr;
bool const bEnable = pShell && !pShell->IsReadOnly()
&& rTreeView.n_children() != 0
&& !pSh->getIDocumentRedlineAccess().GetRedlinePassword().hasElements();
bool bSel = rTreeView.get_selected(nullptr);
rTreeView.selected_foreach([this, pSh, &bIsNotFormated](weld::TreeIter& rEntry){
// find the selected redline
// (fdo#57874: ignore, if the redline is already gone)
SwRedlineTable::size_type nPos = GetRedlinePos(rEntry);
if( nPos != SwRedlineTable::npos )
{
const SwRangeRedline& rRedln = pSh->GetRedline( nPos );
bIsNotFormated |= RedlineType::Format != rRedln.GetType();
}
return false;
});
m_pTPView->EnableAccept( bEnable && bSel );
m_pTPView->EnableReject( bEnable && bSel );
m_pTPView->EnableClearFormat( bEnable && !bIsNotFormated && bSel );
m_pTPView->EnableAcceptAll( bEnable );
m_pTPView->EnableRejectAll( bEnable );
m_pTPView->EnableClearFormatAll( bEnable &&
m_bOnlyFormatedRedlines );
}
const OUString & SwRedlineAcceptDlg::GetActionImage(const SwRangeRedline& rRedln, sal_uInt16 nStack,
bool bTableChanges, bool bRowChanges)
{
switch (rRedln.GetType(nStack))
{
case RedlineType::Insert: return bTableChanges
? bRowChanges
? BMP_REDLINE_ROW_INSERTION
: BMP_REDLINE_COL_INSERTION
: rRedln.IsMoved()
? BMP_REDLINE_MOVED_INSERTION
: rRedln.IsAnnotation()
? BMP_REDLINE_COMMENT_INSERTION
: BMP_REDLINE_INSERTED;
case RedlineType::Delete: return bTableChanges
? bRowChanges
? BMP_REDLINE_ROW_DELETION
: BMP_REDLINE_COL_DELETION
: rRedln.IsMoved()
? BMP_REDLINE_MOVED_DELETION
: rRedln.IsAnnotation()
? BMP_REDLINE_COMMENT_DELETION
: BMP_REDLINE_DELETED;
case RedlineType::Format: return BMP_REDLINE_FORMATTED;
case RedlineType::ParagraphFormat: return BMP_REDLINE_FORMATTED;
case RedlineType::Table: return BMP_REDLINE_TABLECHG;
case RedlineType::FmtColl: return BMP_REDLINE_FMTCOLLSET;
default: break;
}
return EMPTY_OUSTRING;
}
const OUString & SwRedlineAcceptDlg::GetActionText(const SwRangeRedline& rRedln, sal_uInt16 nStack)
{
switch( rRedln.GetType(nStack) )
{
case RedlineType::Insert: return m_sInserted;
case RedlineType::Delete: return m_sDeleted;
case RedlineType::Format: return m_sFormated;
case RedlineType::ParagraphFormat: return m_sFormated;
case RedlineType::Table: return m_sTableChgd;
case RedlineType::FmtColl: return m_sFormatCollSet;
default:;//prevent warning
}
return EMPTY_OUSTRING;
}
// newly initialise after activation
void SwRedlineAcceptDlg::Activate()
{
// prevent update if flag is set (#102547#)
if( m_bInhibitActivate )
return;
SwView *pView = ::GetActiveView();
if (!pView) // can happen when switching to another app
{
m_pTPView->EnableAccept(false);
m_pTPView->EnableReject(false);
m_pTPView->EnableClearFormat(false);
m_pTPView->EnableAcceptAll(false);
m_pTPView->EnableRejectAll(false);
m_pTPView->EnableClearFormatAll(false);
return; // had the focus previously
}
SwWait aWait( *pView->GetDocShell(), false );
if (pView->GetDocShell()->IsReadOnly())
{
m_pTPView->EnableAccept(false);
m_pTPView->EnableReject(false);
m_pTPView->EnableClearFormat(false);
m_pTPView->EnableAcceptAll(false);
m_pTPView->EnableRejectAll(false);
m_pTPView->EnableClearFormatAll(false);
// note: enabling is done in InitAuthors below
}
m_aUsedSeqNo.clear();
// did something change?
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
// tdf#162018 keep the selected entry selected
weld::TreeView& rTreeView = m_pTable->GetWidget();
const SwRedlineData* pSelectedEntryRedlineData = lcl_get_selected_redlinedata(m_pTable->GetWidget());
SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
// check the number of pointers
for ( SwRedlineTable::size_type i = 0; i < nCount; i++)
{
const SwRangeRedline& rRedln = pSh->GetRedline(i);
if (i >= m_RedlineParents.size())
{
// new entries have been appended
Init(i);
return;
}
SwRedlineDataParent *const pParent = m_RedlineParents[i].get();
if (&rRedln.GetRedlineData() != pParent->pData)
{
// Redline-Parents were inserted, changed or deleted
i = CalcDiff(i, false);
if (i == SwRedlineTable::npos)
{
lcl_reselect(rTreeView, pSelectedEntryRedlineData);
return;
}
continue;
}
const SwRedlineData *pRedlineData = rRedln.GetRedlineData().Next();
const SwRedlineDataChild *pBackupData = pParent->pNext;
if (!pRedlineData && pBackupData)
{
// Redline-Children were deleted
i = CalcDiff(i, true);
if (i == SwRedlineTable::npos)
{
lcl_reselect(rTreeView, pSelectedEntryRedlineData);
return;
}
continue;
}
else
{
while (pRedlineData)
{
if (!pBackupData || pRedlineData != pBackupData->pChild)
{
// Redline-Children were inserted, changed or deleted
i = CalcDiff(i, true);
if (i == SwRedlineTable::npos)
{
lcl_reselect(rTreeView, pSelectedEntryRedlineData);
return;
}
// here was a continue; targetted to the outer loop
// now a break will do, as there is nothing after it in the outer loop
break;
}
pBackupData = pBackupData->pNext;
pRedlineData = pRedlineData->Next();
}
}
}
if (nCount != m_RedlineParents.size())
{
// Redlines were deleted at the end
Init(nCount);
return;
}
// check comment
bool bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
for (SwRedlineTable::size_type i = 0; i < nCount; i++)
{
const SwRangeRedline& rRedln = pSh->GetRedline(i);
SwRedlineDataParent *const pParent = m_RedlineParents[i].get();
if(rRedln.GetComment() != pParent->sComment)
{
bool bShowDeletedTextAsComment = bIsShowChangesInMargin &&
RedlineType::Delete == rRedln.GetType() && rRedln.GetComment().isEmpty();
const OUString sComment = bShowDeletedTextAsComment
? const_cast<SwRangeRedline&>(rRedln).GetDescr()
: rRedln.GetComment();
if (pParent->xTLBParent)
{
// update only comment
rTreeView.set_text(*pParent->xTLBParent, sComment.replace('\n', ' '), 3);
}
pParent->sComment = sComment;
}
}
InitAuthors();
lcl_reselect(rTreeView, pSelectedEntryRedlineData);
}
void SwRedlineAcceptDlg::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
{
if (rHint.GetId() == SfxHintId::SwRedlineContentAtPos)
{
SwView* pView = GetActiveView();
if (!pView)
return;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
const SwRangeRedline* pRangeRedline = pSh->GetCurrRedline();
if (!pRangeRedline)
return;
const SwRedlineData& rRedlineData = pRangeRedline->GetRedlineData();
weld::TreeView& rTreeView = m_pTable->GetWidget();
rTreeView.all_foreach([&rTreeView, &rRedlineData](weld::TreeIter& rIter) {
RedlinData* pRedlinData = weld::fromId<RedlinData*>(rTreeView.get_id(rIter));
const SwRedlineData* pRedlineData;
if (rTreeView.get_iter_depth(rIter))
pRedlineData = static_cast<SwRedlineDataChild*>(pRedlinData->pData)->pChild;
else
pRedlineData = static_cast<SwRedlineDataParent*>(pRedlinData->pData)->pData;
if (pRedlineData == &rRedlineData)
{
rTreeView.set_cursor(rIter);
return true;
}
return false;
});
}
}
SwRedlineTable::size_type SwRedlineAcceptDlg::CalcDiff(SwRedlineTable::size_type nStart, bool bChild)
{
if (!nStart || m_bHasTrackedColumn)
{
Init();
return SwRedlineTable::npos;
}
weld::TreeView& rTreeView = m_pTable->GetWidget();
rTreeView.freeze();
SwView *pView = ::GetActiveView();
if (!pView)
return SwRedlineTable::npos;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return SwRedlineTable::npos;
bool bHasRedlineAutoFormat = HasRedlineAutoFormat();
SwRedlineDataParent *const pParent = m_RedlineParents[nStart].get();
const SwRangeRedline& rRedln = pSh->GetRedline(nStart);
if (bChild) // should actually never happen, but just in case...
{
// throw away all entry's children and initialise newly
SwRedlineDataChild* pBackupData = const_cast<SwRedlineDataChild*>(pParent->pNext);
SwRedlineDataChild* pNext;
while (pBackupData)
{
pNext = const_cast<SwRedlineDataChild*>(pBackupData->pNext);
if (pBackupData->xTLBChild)
rTreeView.remove(*pBackupData->xTLBChild);
auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(),
[&pBackupData](const std::unique_ptr<SwRedlineDataChild>& rChildPtr) { return rChildPtr.get() == pBackupData; });
if (it != m_RedlineChildren.end())
m_RedlineChildren.erase(it);
pBackupData = pNext;
}
pParent->pNext = nullptr;
// insert new children
InsertChildren(pParent, rRedln, bHasRedlineAutoFormat);
rTreeView.thaw();
return nStart;
}
// have entries been deleted?
const SwRedlineData *pRedlineData = &rRedln.GetRedlineData();
for (SwRedlineTable::size_type i = nStart + 1; i < m_RedlineParents.size(); i++)
{
if (m_RedlineParents[i]->pData == pRedlineData)
{
// remove entries from nStart to i-1
RemoveParents(nStart, i - 1);
rTreeView.thaw();
return nStart - 1;
}
}
// entries been inserted?
SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
pRedlineData = m_RedlineParents[nStart]->pData;
for (SwRedlineTable::size_type i = nStart + 1; i < nCount; i++)
{
if (&pSh->GetRedline(i).GetRedlineData() == pRedlineData)
{
rTreeView.thaw();
// insert entries from nStart to i-1
InsertParents(nStart, i - 1);
return nStart - 1;
}
}
rTreeView.thaw();
Init(nStart); // adjust all entries until the end
return SwRedlineTable::npos;
}
void SwRedlineAcceptDlg::InsertChildren(SwRedlineDataParent *pParent, const SwRangeRedline& rRedln, bool bHasRedlineAutoFormat)
{
SwRedlineDataChild *pLastRedlineChild = nullptr;
const SwRedlineData *pRedlineData = &rRedln.GetRedlineData();
bool bAutoFormatRedline = rRedln.IsAutoFormat();
weld::TreeView& rTreeView = m_pTable->GetWidget();
OUString sAction = GetActionText(rRedln);
bool bValidParent = m_sFilterAction.isEmpty() || m_sFilterAction == sAction;
bValidParent = bValidParent && m_pTable->IsValidEntry(rRedln.GetAuthorString(), rRedln.GetTimeStamp(), rRedln.GetComment());
if (bHasRedlineAutoFormat)
{
if (pParent->pData->GetSeqNo())
{
std::pair<SwRedlineDataParentSortArr::const_iterator,bool> const ret
= m_aUsedSeqNo.insert(pParent);
if (ret.second) // already there
{
if (pParent->xTLBParent)
{
rTreeView.set_text(*(*ret.first)->xTLBParent, m_sAutoFormat, 0);
rTreeView.remove(*pParent->xTLBParent);
pParent->xTLBParent.reset();
}
return;
}
}
bValidParent = bValidParent && bAutoFormatRedline;
}
bool bValidTree = bValidParent;
for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++)
{
pRedlineData = pRedlineData->Next();
SwRedlineDataChild* pRedlineChild = new SwRedlineDataChild;
pRedlineChild->pChild = pRedlineData;
m_RedlineChildren.push_back(std::unique_ptr<SwRedlineDataChild>(pRedlineChild));
if ( pLastRedlineChild )
pLastRedlineChild->pNext = pRedlineChild;
else
pParent->pNext = pRedlineChild;
sAction = GetActionText(rRedln, nStack);
bool bValidChild = m_sFilterAction.isEmpty() || m_sFilterAction == sAction;
bValidChild = bValidChild && m_pTable->IsValidEntry(rRedln.GetAuthorString(nStack), rRedln.GetTimeStamp(nStack), rRedln.GetComment());
if (bHasRedlineAutoFormat)
bValidChild = bValidChild && bAutoFormatRedline;
bValidTree |= bValidChild;
if (bValidChild)
{
std::unique_ptr<RedlinData> pData(new RedlinData);
pData->pData = pRedlineChild;
pData->bDisabled = true;
OUString sImage(GetActionImage(rRedln, nStack));
const OUString& sAuthor = rRedln.GetAuthorString(nStack);
pData->aDateTime = rRedln.GetTimeStamp(nStack);
pData->eType = rRedln.GetType(nStack);
OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime);
const OUString& sComment = rRedln.GetComment(nStack);
std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator());
OUString sId(weld::toId(pData.get()));
rTreeView.insert(pParent->xTLBParent.get(), -1, nullptr, &sId, nullptr, nullptr,
false, xChild.get());
m_RedlinData.push_back(std::move(pData));
rTreeView.set_image(*xChild, sImage, -1);
rTreeView.set_text(*xChild, sAuthor, 1);
rTreeView.set_text(*xChild, sDateEntry, 2);
rTreeView.set_text(*xChild, sComment, 3);
pRedlineChild->xTLBChild = std::move(xChild);
if (!bValidParent)
rTreeView.expand_row(*pParent->xTLBParent);
}
else
pRedlineChild->xTLBChild.reset();
pLastRedlineChild = pRedlineChild;
}
if (pLastRedlineChild)
pLastRedlineChild->pNext = nullptr;
if (!bValidTree && pParent->xTLBParent)
{
rTreeView.remove(*pParent->xTLBParent);
pParent->xTLBParent.reset();
if (bHasRedlineAutoFormat)
m_aUsedSeqNo.erase(pParent);
}
}
void SwRedlineAcceptDlg::RemoveParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd)
{
SwView *pView = ::GetActiveView();
if (!pView)
return;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
std::vector<const weld::TreeIter*> aLBoxArr;
weld::TreeView& rTreeView = m_pTable->GetWidget();
// because of Bug of TLB that ALWAYS calls the SelectHandler at Remove:
rTreeView.connect_changed(Link<weld::TreeView&,void>());
bool bChildrenRemoved = false;
rTreeView.thaw();
rTreeView.unselect_all();
// set the cursor after the last entry because otherwise performance problem in TLB.
// TLB would otherwise reset the cursor at every Remove (expensive)
SwRedlineTable::size_type nPos = std::min(nCount, m_RedlineParents.size());
weld::TreeIter *pCurEntry = nullptr;
while( ( pCurEntry == nullptr ) && ( nPos > 0 ) )
{
--nPos;
pCurEntry = m_RedlineParents[nPos]->xTLBParent.get();
}
if (pCurEntry)
rTreeView.set_cursor(*pCurEntry);
rTreeView.freeze();
for (SwRedlineTable::size_type i = nStart; i <= nEnd; i++)
{
if (!bChildrenRemoved && m_RedlineParents[i]->pNext)
{
SwRedlineDataChild * pChildPtr =
const_cast<SwRedlineDataChild*>(m_RedlineParents[i]->pNext);
auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(),
[&pChildPtr](const std::unique_ptr<SwRedlineDataChild>& rChildPtr) { return rChildPtr.get() == pChildPtr; });
if (it != m_RedlineChildren.end())
{
sal_uInt16 nChildren = 0;
while (pChildPtr)
{
pChildPtr = const_cast<SwRedlineDataChild*>(pChildPtr->pNext);
nChildren++;
}
m_RedlineChildren.erase(it, it + nChildren);
bChildrenRemoved = true;
}
}
weld::TreeIter *const pEntry = m_RedlineParents[i]->xTLBParent.get();
if (pEntry)
aLBoxArr.push_back(pEntry);
}
std::sort(aLBoxArr.begin(), aLBoxArr.end(), [&rTreeView](const weld::TreeIter* a, const weld::TreeIter* b) {
return rTreeView.iter_compare(*a, *b) == -1;
});
// clear TLB from behind
for (auto it = aLBoxArr.rbegin(); it != aLBoxArr.rend(); ++it)
{
const weld::TreeIter* pIter = *it;
rTreeView.remove(*pIter);
}
rTreeView.thaw();
rTreeView.connect_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl));
// unfortunately by Remove it was selected from the TLB always again ...
rTreeView.unselect_all();
rTreeView.freeze();
m_RedlineParents.erase(m_RedlineParents.begin() + nStart, m_RedlineParents.begin() + nEnd + 1);
}
void SwRedlineAcceptDlg::InsertParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd)
{
SwView *pView = ::GetActiveView();
if (!pView)
return;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
bool bHasRedlineAutoFormat = HasRedlineAutoFormat();
SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
nEnd = std::min(nEnd, (nCount - 1)); // also treats nEnd=SwRedlineTable::npos (until the end)
// reset m_bHasTrackedColumn before searching tracked column again
if ( m_bHasTrackedColumn && nStart == 0 )
m_bHasTrackedColumn = false;
if (nEnd == SwRedlineTable::npos)
return; // no redlines in the document
weld::TreeView& rTreeView = m_pTable->GetWidget();
SwRedlineDataParent* pRedlineParent;
rTreeView.freeze();
if (m_pTable->IsSorted())
rTreeView.make_unsorted();
bool bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
// collect redlines of tracked table/row/column insertion/deletions under a single tree list
// item to accept/reject the table change with a single click on Accept/Reject
// Note: because update of the tree list depends on parent count, we don't modify
// m_RedlineParents, only store the 2nd and more redlines as children of the tree list
// item of the first redline
// count of items stored as children (to adjust parent position)
SwRedlineTable::size_type nSkipRedlines = 0;
// count of items of the actual table change stored as children =
// redlines of the change - 1 (first redline is associated to the parent tree list item)
SwRedlineTable::size_type nSkipRedline = 0;
// descriptor redline of the tracked table row/column
SwRedlineTable::size_type nRowChange = 0;
// first redlines of the tracked table rows/columns, base of the parent tree lists
// of the other SwRangeRedlines of the tracked table rows or columns
std::vector<SwRedlineTable::size_type> aTableParents;
// show all redlines as tree list items,
// redlines of a tracked table (row) insertion/deletion showed as children of a single parent
for (SwRedlineTable::size_type i = nStart; i <= nEnd; i++)
{
const SwRangeRedline& rRedln = pSh->GetRedline(i);
const SwRedlineData *pRedlineData = &rRedln.GetRedlineData();
// redline is a child associated to this table row/column change
SwRedlineTable::size_type nTableParent = SwRedlineTable::npos;
pRedlineParent = new SwRedlineDataParent;
pRedlineParent->pData = pRedlineData;
pRedlineParent->pNext = nullptr;
// handle tracked table row changes
const SwTableBox* pTableBox;
const SwTableLine* pTableLine;
bool bChange = false;
bool bRowChange = false;
if ( // not recognized yet as tracked table row change
nullptr != ( pTableBox = pSh->GetRedline(i).Start()->GetNode().GetTableBox() ) &&
nullptr != ( pTableLine = pTableBox->GetUpper() ) &&
// it's a tracked row (or column change) based on the cached row data
( RedlineType::None != pTableLine->GetRedlineType() ||
RedlineType::None != pTableBox->GetRedlineType() ) )
{
// start redline search from the start from the tracked row/column change
SwRedlineTable::size_type nStartPos =
nRowChange > nSkipRedline ? nRowChange - nSkipRedline : 0;
bChange = true;
bRowChange = RedlineType::None != pTableLine->GetRedlineType();
nRowChange = bRowChange
? pTableLine->UpdateTextChangesOnly(nStartPos)
: pTableBox->GetRedline();
// redline is there in a tracked table change
if ( SwRedlineTable::npos != nRowChange )
{
// join the consecutive deleted/inserted rows/columns under a single treebox item,
// if they have the same redline data (equal type, author and time stamp)
for (size_t j = 0; j < aTableParents.size(); j++)
{
// note: CanCombine() allows a time frame to join the changes within a short
// time period: this avoid of falling apart of the tracked columns inserted
// by several clicks
if ( pSh->GetRedline(nRowChange).GetRedlineData()
.CanCombine(pSh->GetRedline(aTableParents[j]).GetRedlineData()) )
{
nSkipRedline++;
nTableParent = aTableParents[j];
break;
}
}
if ( SwRedlineTable::npos == nTableParent )
{
// table redline didn't fit in the stored ones, create new parent
aTableParents.push_back(i);
}
// it needs major tree update later because of tracked table columns
if ( !m_bHasTrackedColumn && !bRowChange )
{
m_bHasTrackedColumn = true;
}
}
else
{
// redline is not in a tracked table change
bChange = bRowChange = false;
}
}
// empty parent cache for the last table
if ( !pTableBox )
{
aTableParents.clear();
}
bool bShowDeletedTextAsComment = bIsShowChangesInMargin &&
RedlineType::Delete == rRedln.GetType() && rRedln.GetComment().isEmpty();
const OUString sComment = bShowDeletedTextAsComment
? const_cast<SwRangeRedline&>(rRedln).GetDescr()
: rRedln.GetComment();
pRedlineParent->sComment = sComment.replace('\n', ' ');
m_RedlineParents.insert(m_RedlineParents.begin() + i,
std::unique_ptr<SwRedlineDataParent>(pRedlineParent));
std::unique_ptr<RedlinData> pData(new RedlinData);
pData->pData = pRedlineParent;
pData->bDisabled = false;
// use descriptor SwRangeRedline of the changed row, if needed to show
// the correct redline type, author and time stamp of the tracked row change
const SwRangeRedline& rChangeRedln = pSh->GetRedline(bChange ? nRowChange : i);
OUString sImage = GetActionImage(rChangeRedln, 0, bChange && aTableParents.back() == i, bRowChange );
OUString sAuthor = rChangeRedln.GetAuthorString(0);
pData->aDateTime = rChangeRedln.GetTimeStamp(0);
pData->eType = rChangeRedln.GetType(0);
OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime);
OUString sId = weld::toId(pData.get());
std::unique_ptr<weld::TreeIter> xParent(rTreeView.make_iterator());
if ( !bChange || aTableParents.back() == i )
{
rTreeView.insert(nullptr, i - nSkipRedlines, nullptr, &sId, nullptr, nullptr, false, xParent.get());
// before this was a tracked table change with more than a single redline
if ( nSkipRedline > 0 )
{
nSkipRedlines += nSkipRedline;
nSkipRedline = 0;
}
}
else
{
// put 2nd or more redlines of deleted/inserted rows as children of their first redline
SwRedlineDataParent *const pParent = m_RedlineParents[nTableParent].get();
rTreeView.insert(pParent->xTLBParent.get(), -1, nullptr, &sId, nullptr, nullptr, false, xParent.get());
}
m_RedlinData.push_back(std::move(pData));
rTreeView.set_image(*xParent, sImage, -1);
rTreeView.set_text(*xParent, sAuthor, 1);
rTreeView.set_text(*xParent, sDateEntry, 2);
rTreeView.set_text(*xParent, sComment, 3);
pRedlineParent->xTLBParent = std::move(xParent);
InsertChildren(pRedlineParent, rRedln, bHasRedlineAutoFormat);
}
rTreeView.thaw();
if (m_pTable->IsSorted())
rTreeView.make_sorted();
}
void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect, bool bAccept )
{
SwView *pView = ::GetActiveView();
if (!pView)
return;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
int nPos = -1;
typedef std::vector<std::unique_ptr<weld::TreeIter>> ListBoxEntries_t;
ListBoxEntries_t aRedlines;
// don't activate
OSL_ENSURE( !m_bInhibitActivate,
"recursive call of CallAcceptReject?");
m_bInhibitActivate = true;
weld::TreeView& rTreeView = m_pTable->GetWidget();
auto lambda = [this, pSh, bSelect, bAccept, &rTreeView, &nPos, &aRedlines](weld::TreeIter& rEntry) {
if (!rTreeView.get_iter_depth(rEntry))
{
if (bSelect && nPos == -1)
nPos = rTreeView.get_iter_index_in_parent(rEntry);
RedlinData *pData = weld::fromId<RedlinData*>(rTreeView.get_id(rEntry));
bool bIsNotFormatted = true;
// first remove only changes with insertion/deletion, if they exist
// (format-only changes haven't had real rejection yet, only an
// approximation: clear direct formatting, so try to warn
// with the extended button label "Reject All/Clear formatting")
if ( !bSelect && !bAccept && !m_bOnlyFormatedRedlines )
{
SwRedlineTable::size_type nPosition = GetRedlinePos(rEntry);
const SwRangeRedline& rRedln = pSh->GetRedline(nPosition);
if( RedlineType::Format == rRedln.GetType() )
bIsNotFormatted = false;
}
if (!pData->bDisabled && bIsNotFormatted)
aRedlines.emplace_back(rTreeView.make_iterator(&rEntry));
}
return false;
};
// collect redlines-to-be-accepted/rejected in aRedlines vector
if (bSelect)
rTreeView.selected_foreach(lambda);
else
rTreeView.all_foreach(lambda);
bool (SwEditShell::*FnAccRej)( SwRedlineTable::size_type ) = &SwEditShell::AcceptRedline;
if( !bAccept )
FnAccRej = &SwEditShell::RejectRedline;
SwWait aWait( *pSh->GetView().GetDocShell(), true );
pSh->StartAction();
bool bMoreRedlines( aRedlines.size() > 1 ||
// single item with children, e.g. multiple redlines of a table or table row deletion/insertion
( aRedlines.size() == 1 && rTreeView.iter_n_children(*aRedlines[0]) > 0 ) );
// don't add extra Undo label to a single item only with redline stack (i.e. old changes
// on the same text range, stored only in OOXML)
if ( bMoreRedlines && aRedlines.size() == 1 )
{
std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator( &*aRedlines[0] ));
RedlinData *pData = weld::fromId<RedlinData*>(rTreeView.get_id(*xChild));
if ( pData->bDisabled )
bMoreRedlines = false;
}
if ( bMoreRedlines )
{
OUString aTmpStr;
{
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1,
OUString::number(aRedlines.size()));
aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
}
SwRewriter aRewriter;
aRewriter.AddRule(UndoArg1, aTmpStr);
pSh->StartUndo(bAccept? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE,
&aRewriter);
}
// accept/reject the redlines in aRedlines. The absolute
// position may change during the process (e.g. when two redlines
// are merged in result of another one being deleted), so the
// position must be resolved late and checked before using it.
// (cf #102547#)
for (const auto& rRedLine : aRedlines)
{
SwRedlineTable::size_type nPosition = GetRedlinePos( *rRedLine );
if( nPosition != SwRedlineTable::npos )
(pSh->*FnAccRej)( nPosition );
// handle redlines of table rows, stored as children of the item associated
// to the deleted/inserted table row(s)
std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator( &*rRedLine ));
if ( rTreeView.iter_children(*xChild) )
{
RedlinData *pData = weld::fromId<RedlinData*>(rTreeView.get_id(*xChild));
// disabled for redline stack, but not for redlines of table rows
if ( !pData->bDisabled )
{
do
{
nPosition = GetRedlinePos( *xChild );
if( nPosition != SwRedlineTable::npos )
(pSh->*FnAccRej)( nPosition );
}
while ( rTreeView.iter_next_sibling(*xChild) );
}
}
}
if ( bMoreRedlines )
{
pSh->EndUndo();
}
pSh->EndAction();
m_bInhibitActivate = false;
Activate();
if (nPos != -1 && rTreeView.n_children())
{
if (nPos >= rTreeView.n_children())
nPos = rTreeView.n_children() - 1;
rTreeView.select(nPos);
rTreeView.scroll_to_row(nPos);
rTreeView.set_cursor(nPos);
SelectHdl(rTreeView);
}
m_pTPView->EnableUndo();
}
SwRedlineTable::size_type SwRedlineAcceptDlg::GetRedlinePos(const weld::TreeIter& rEntry)
{
SwView* pView = GetActiveView();
if (!pView)
return SwRedlineTable::npos;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return SwRedlineTable::npos;
weld::TreeView& rTreeView = m_pTable->GetWidget();
return pSh->FindRedlineOfData( *static_cast<SwRedlineDataParent*>(weld::fromId<RedlinData*>(
rTreeView.get_id(rEntry))->pData)->pData );
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, SortByComboBoxChangedHdl, SvxTPView*, void)
{
SwView* pView = GetActiveView();
if (!pView)
return;
SwWait aWait(*pView->GetDocShell(), false);
auto nSortMode = m_pTPView->GetSortByComboBoxControl()->get_active();
if (nSortMode == 4)
nSortMode = -1;
m_pTable->HeaderBarClick(nSortMode);
if (nSortMode == -1)
Init();
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptHdl, SvxTPView*, void)
{
CallAcceptReject( true, true );
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptAllHdl, SvxTPView*, void)
{
CallAcceptReject( false, true );
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectHdl, SvxTPView*, void)
{
CallAcceptReject( true, false );
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectAllHdl, SvxTPView*, void)
{
CallAcceptReject( false, false );
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, UndoHdl, SvxTPView*, void)
{
if (SwView* pView = GetActiveView())
{
pView->GetViewFrame().GetDispatcher()->
Execute(SID_UNDO, SfxCallMode::SYNCHRON);
const SfxPoolItemHolder aResult(pView->GetSlotState(SID_UNDO));
m_pTPView->EnableUndo(aResult.is());
}
Activate();
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, FilterChangedHdl, SvxTPFilter*, void)
{
SvxTPFilter *pFilterTP = m_xTabPagesCTRL->GetFilterPage();
if (pFilterTP->IsAction())
m_sFilterAction = pFilterTP->GetLbAction()->get_active_text();
else
m_sFilterAction.clear();
Init();
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, SelectHdl, weld::TreeView&, void)
{
m_aSelectTimer.Start();
}
IMPL_LINK_NOARG(SwRedlineAcceptDlg, GotoHdl, Timer *, void)
{
m_aSelectTimer.Stop();
SwView* pView = GetActiveView();
if (!pView)
return;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return;
bool bIsNotFormated = false;
bool bSel = false;
//#98883# don't select redlines while the dialog is not focused
//#107938# But not only ask pTable if it has the focus. To move
// the selection to the selected redline any child of pParentDlg
// may the focus.
if (!m_xParentDlg || m_xParentDlg->has_toplevel_focus())
{
weld::TreeView& rTreeView = m_pTable->GetWidget();
std::unique_ptr<weld::TreeIter> xActEntry(rTreeView.make_iterator());
if (rTreeView.get_selected(xActEntry.get()))
{
pSh->StartAction();
pSh->EnterStdMode();
SwViewShell::SetCareDialog(m_xParentDlg);
rTreeView.selected_foreach([this, pSh, &rTreeView, &xActEntry, &bIsNotFormated, &bSel](weld::TreeIter& rEntry){
rTreeView.copy_iterator(rEntry, *xActEntry);
if (rTreeView.get_iter_depth(rEntry))
{
rTreeView.iter_parent(*xActEntry);
if (rTreeView.is_selected(*xActEntry))
return false; // don't select twice
}
else
bSel = true;
// #98864# find the selected redline (ignore, if the redline is already gone)
SwRedlineTable::size_type nPos = GetRedlinePos(*xActEntry);
if (nPos != SwRedlineTable::npos)
{
const SwRangeRedline& rRedln = pSh->GetRedline( nPos );
bIsNotFormated |= RedlineType::Format != rRedln.GetType();
if (pSh->GotoRedline(nPos, true))
{
pSh->SetInSelect();
pSh->EnterAddMode();
}
}
// select all redlines of tracked table rows
std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator( &*xActEntry ));
if ( rTreeView.iter_children(*xChild) )
{
RedlinData *pData = reinterpret_cast<RedlinData*>(rTreeView.get_id(*xChild).toInt64());
// disabled for redline stack, but not for redlines of table rows
if ( !pData->bDisabled )
{
do
{
nPos = GetRedlinePos(*xChild);
if (nPos != SwRedlineTable::npos)
{
const SwRangeRedline& rRedln = pSh->GetRedline( nPos );
bIsNotFormated |= RedlineType::Format != rRedln.GetType();
if (pSh->GotoRedline(nPos, true))
{
pSh->SetInSelect();
pSh->EnterAddMode();
}
}
}
while ( rTreeView.iter_next_sibling(*xChild) );
}
}
return false;
});
pSh->LeaveAddMode();
pSh->EndAction();
SwViewShell::SetCareDialog(nullptr);
}
}
SwDocShell* pShell = pSh ? pSh->GetDoc()->GetDocShell() : nullptr;
bool const bEnable = pShell && !pShell->IsReadOnly()
&& !pSh->getIDocumentRedlineAccess().GetRedlinePassword().hasElements();
m_pTPView->EnableAccept( bEnable && bSel /*&& !bReadonlySel*/ );
m_pTPView->EnableReject( bEnable && bSel /*&& !bReadonlySel*/ );
m_pTPView->EnableClearFormat( bEnable && bSel && !bIsNotFormated /*&& !bReadonlySel*/ );
m_pTPView->EnableAcceptAll( bEnable );
m_pTPView->EnableRejectAll( bEnable );
m_pTPView->EnableClearFormatAll( bEnable && m_bOnlyFormatedRedlines );
}
IMPL_LINK(SwRedlineAcceptDlg, CommandHdl, const CommandEvent&, rCEvt, bool)
{
if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
return false;
SwView* pView = GetActiveView();
if (!pView)
return false;
SwWrtShell* pSh = pView->GetWrtShellPtr();
if (!pSh)
return false;
const SwRangeRedline *pRed = nullptr;
weld::TreeView& rTreeView = m_pTable->GetWidget();
std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
bool bEntry = rTreeView.get_selected(xEntry.get());
if (bEntry)
{
std::unique_ptr<weld::TreeIter> xTopEntry(rTreeView.make_iterator(xEntry.get()));
if (rTreeView.get_iter_depth(*xTopEntry))
rTreeView.iter_parent(*xTopEntry);
SwRedlineTable::size_type nPos = GetRedlinePos(*xTopEntry);
// disable commenting for protected areas
if (nPos != SwRedlineTable::npos && (pRed = pSh->GotoRedline(nPos, true)) != nullptr)
{
if( pSh->IsCursorPtAtEnd() )
pSh->SwapPam();
pSh->SetInSelect();
}
}
m_xPopup->set_sensitive(u"writeredit"_ustr, bEntry && pRed &&
!rTreeView.get_iter_depth(*xEntry) &&
rTreeView.count_selected_rows() == 1);
m_xPopup->set_sensitive(u"writersort"_ustr, rTreeView.n_children() != 0);
int nColumn = rTreeView.get_sort_column();
if (nColumn == -1)
nColumn = 4;
for (sal_Int32 i = 0; i < 5; ++i)
m_xSortMenu->set_active(u"writersort" + OUString::number(i), i == nColumn);
OUString sCommand = m_xPopup->popup_at_rect(&rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
if (sCommand == "writeredit")
{
if (bEntry)
{
if (rTreeView.get_iter_depth(*xEntry))
rTreeView.iter_parent(*xEntry);
SwRedlineTable::size_type nPos = GetRedlinePos(*xEntry);
if (nPos == SwRedlineTable::npos)
return true;
const SwRangeRedline &rRedline = pSh->GetRedline(nPos);
OUString sComment = convertLineEnd(rRedline.GetComment(), GetSystemLineEnd());
SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
::DialogGetRanges fnGetRange = pFact->GetDialogGetRangesFunc();
SfxItemSet aSet( pSh->GetAttrPool(), fnGetRange() );
aSet.Put(SvxPostItTextItem(sComment, SID_ATTR_POSTIT_TEXT));
aSet.Put(SvxPostItAuthorItem(rRedline.GetAuthorString(), SID_ATTR_POSTIT_AUTHOR));
aSet.Put(SvxPostItDateItem( GetAppLangDateTimeString(
rRedline.GetRedlineData().GetTimeStamp() ),
SID_ATTR_POSTIT_DATE ));
ScopedVclPtr<AbstractSvxPostItDialog> pDlg(pFact->CreateSvxPostItDialog(&rTreeView, aSet));
pDlg->HideAuthor();
TranslateId pResId;
switch( rRedline.GetType() )
{
case RedlineType::Insert:
pResId = STR_REDLINE_INSERTED;
break;
case RedlineType::Delete:
pResId = STR_REDLINE_DELETED;
break;
case RedlineType::Format:
case RedlineType::ParagraphFormat:
pResId = STR_REDLINE_FORMATTED;
break;
case RedlineType::Table:
pResId = STR_REDLINE_TABLECHG;
break;
default:;//prevent warning
}
OUString sTitle(SwResId(STR_REDLINE_COMMENT));
if (pResId)
sTitle += SwResId(pResId);
pDlg->SetText(sTitle);
SwViewShell::SetCareDialog(pDlg->GetDialog());
if ( pDlg->Execute() == RET_OK )
{
const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
OUString sMsg(pOutSet->Get(SID_ATTR_POSTIT_TEXT).GetValue());
// insert / change comment
pSh->SetRedlineComment(sMsg);
rTreeView.set_text(*xEntry, sMsg.replace('\n', ' '), 3);
}
SwViewShell::SetCareDialog(nullptr);
pDlg.disposeAndClear();
}
}
else if (!sCommand.isEmpty())
{
int nSortMode = o3tl::toInt32(sCommand.subView(10));
if (nSortMode == 4 && nColumn == 4)
return true; // we already have it
m_pTPView->GetSortByComboBoxControl()->set_active(nSortMode);
if (nSortMode == 4)
nSortMode = -1; // unsorted / sorted by position
SwWait aWait( *pView->GetDocShell(), false );
m_pTable->HeaderBarClick(nSortMode);
if (nSortMode == -1)
Init(); // newly fill everything
}
return true;
}
namespace
{
OUString lcl_StripAcceptChgDat(OUString &rExtraString)
{
OUString aStr;
while(true)
{
sal_Int32 nPos = rExtraString.indexOf("AcceptChgDat:");
if (nPos == -1)
break;
// try to read the alignment string "ALIGN:(...)"; if none existing,
// it's an old version
sal_Int32 n1 = rExtraString.indexOf('(', nPos);
if (n1 != -1)
{
sal_Int32 n2 = rExtraString.indexOf(')', n1);
if (n2 != -1)
{
// cut out the alignment string
aStr = rExtraString.copy(nPos, n2 - nPos + 1);
rExtraString = rExtraString.replaceAt(nPos, n2 - nPos + 1, u"");
aStr = aStr.copy(n1 - nPos + 1);
}
}
}
return aStr;
}
}
void SwRedlineAcceptDlg::Initialize(OUString& rExtraString)
{
if (rExtraString.isEmpty())
return;
OUString aStr = lcl_StripAcceptChgDat(rExtraString);
if (aStr.isEmpty())
return;
int nCount = aStr.toInt32();
if (nCount <= 2)
return;
std::vector<int> aEndPos;
for (int i = 0; i < nCount; ++i)
{
sal_Int32 n1 = aStr.indexOf(';');
aStr = aStr.copy( n1+1 );
aEndPos.push_back(aStr.toInt32());
}
bool bUseless = false;
std::vector<int> aWidths;
for (int i = 1; i < nCount; ++i)
{
aWidths.push_back(aEndPos[i] - aEndPos[i - 1]);
if (aWidths.back() <= 0)
bUseless = true;
}
if (!bUseless)
{
// turn column end points back to column widths, ignoring the small
// value used for the expander column
weld::TreeView& rTreeView = m_pTable->GetWidget();
rTreeView.set_column_fixed_widths(aWidths);
}
}
void SwRedlineAcceptDlg::FillInfo(OUString &rExtraData) const
{
//remove any old one before adding a new one
lcl_StripAcceptChgDat(rExtraData);
rExtraData += "AcceptChgDat:(";
const int nTabCount = 4;
rExtraData += OUString::number(nTabCount);
rExtraData += ";";
weld::TreeView& rTreeView = m_pTable->GetWidget();
std::vector<int> aWidths;
// turn column widths back into column end points for compatibility
// with how they used to be stored, including a small value for the
// expander column
aWidths.push_back(rTreeView.get_checkbox_column_width());
for (int i = 0; i < nTabCount - 1; ++i)
{
int nWidth = rTreeView.get_column_width(i);
assert(nWidth > 0 && "suspicious to get a value like this");
aWidths.push_back(aWidths.back() + nWidth);
}
for (auto a : aWidths)
{
rExtraData += OUString::number(a);
rExtraData += ";";
}
rExtraData += ")";
}
SwRedlineAcceptPanel::SwRedlineAcceptPanel(weld::Widget* pParent)
: PanelLayout(pParent, u"ManageChangesPanel"_ustr, u"modules/swriter/ui/managechangessidebar.ui"_ustr)
, mxContentArea(m_xBuilder->weld_container(u"content_area"_ustr))
{
mpImplDlg.reset(new SwRedlineAcceptDlg(nullptr, m_xBuilder.get(), mxContentArea.get()));
mpImplDlg->Init();
// we want to receive SfxHintId::DocChanged
StartListening(*(SW_MOD()->GetView()->GetDocShell()));
}
SwRedlineAcceptPanel::~SwRedlineAcceptPanel()
{
}
void SwRedlineAcceptPanel::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
{
if (mpImplDlg && rHint.GetId() == SfxHintId::DocChanged)
mpImplDlg->Activate();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'ExecuteList' is required to be utilized.
↑ V530 The return value of function 'Execute' is required to be utilized.
↑ V530 The return value of function 'lcl_StripAcceptChgDat' is required to be utilized.
↑ V595 The 'pSh' pointer was utilized before it was verified against nullptr. Check lines: 1392, 1397.
↑ V657 It's odd that this function always returns one and the same value.
↑ V1004 The 'pSh' pointer was used unsafely after it was verified against nullptr. Check lines: 340, 374.
↑ V1004 The 'pSh' pointer was used unsafely after it was verified against nullptr. Check lines: 1397, 1399.