/* -*- 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 .
*/
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;
// 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(*(SwModule::get()->GetView()->GetDocShell()));
}
// 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();
}
}
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 EnableControls below
}
m_aUsedSeqNo.clear();
// did something change?
SwWrtShell* pSh = pView->GetWrtShellPtr(); if (!pSh) return;
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 = SwModule::get()->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 (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;
}
}
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;
}
// 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_selection_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl)); // unfortunately by Remove it was selected from the TLB always again ...
rTreeView.unselect_all();
rTreeView.freeze();
// 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();
}
// 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);
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());
}
// 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); if (nPosition != SwRedlineTable::npos)
{ const SwRangeRedline& rRedln = pSh->GetRedline(nPosition);
if (RedlineType::Format == rRedln.GetType())
bIsNotFormatted = false;
}
}
if (!pData->bDisabled && bIsNotFormatted)
aRedlines.emplace_back(rTreeView.make_iterator(&rEntry));
} returnfalse;
};
// collect redlines-to-be-accepted/rejected in aRedlines vector if (bSelect)
rTreeView.selected_foreach(lambda); else
rTreeView.all_foreach(lambda);
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;
}
// 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 (constauto& rRedLine : aRedlines)
{
SwRedlineTable::size_type nPosition = GetRedlinePos( *rRedLine ); if( nPosition != SwRedlineTable::npos )
(pSh->*FnAccRej)( nPosition, /*bDirect=*/true );
// 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, /*bDirect=*/true );
} while ( rTreeView.iter_next_sibling(*xChild) );
}
}
}
SwView* pView = GetActiveView(); if (!pView) return;
SwWrtShell* pSh = pView->GetWrtShellPtr(); if (!pSh) return;
//#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](weld::TreeIter& rEntry){
rTreeView.copy_iterator(rEntry, *xActEntry); if (rTreeView.get_iter_depth(rEntry))
{
rTreeView.iter_parent(*xActEntry); if (rTreeView.is_selected(*xActEntry)) returnfalse; // don't select twice
}
// #98864# find the selected redline (ignore, if the redline is already gone)
SwRedlineTable::size_type nPos = GetRedlinePos(*xActEntry); if (nPos != SwRedlineTable::npos)
{ 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)
{ if (pSh->GotoRedline(nPos, true))
{
pSh->SetInSelect();
pSh->EnterAddMode();
}
}
} while ( rTreeView.iter_next_sibling(*xChild) );
}
} returnfalse;
});
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
} returntrue;
}
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:(";
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 += ")";
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.