/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // -> #111827# #include #include // <- #111827# #include #include #include #include #include #include #include #include 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(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(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 xEntry(rTreeView.make_iterator()); if (rTreeView.get_selected(xEntry.get())) { RedlinData* pRedlinData = weld::fromId(rTreeView.get_id(*xEntry)); if (rTreeView.get_iter_depth(*xEntry)) return static_cast(pRedlinData->pData)->pChild; else return static_cast(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(rTreeView.get_id(rIter)); const SwRedlineData* pRedlineData; if (rTreeView.get_iter_depth(rIter)) pRedlineData = static_cast(pRedlinData->pData)->pChild; else pRedlineData = static_cast(pRedlinData->pData)->pData; if (pRedlineData == pSelectedEntryRedlineData) { rTreeView.set_cursor(rIter); return true; } return false; }); } } SwRedlineAcceptDlg::SwRedlineAcceptDlg(std::shared_ptr 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 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 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(); bool const bEnable = pSh && !pSh->GetDoc()->GetDocShell()->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 ); } 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 OUString(); } 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 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(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(rTreeView.get_id(rIter)); const SwRedlineData* pRedlineData; if (rTreeView.get_iter_depth(rIter)) pRedlineData = static_cast(pRedlinData->pData)->pChild; else pRedlineData = static_cast(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(pParent->pNext); SwRedlineDataChild* pNext; while (pBackupData) { pNext = const_cast(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& 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 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(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 pData(new RedlinData); pData->pData = pRedlineChild; pData->bDisabled = true; OUString sImage(GetActionImage(rRedln, nStack)); OUString sAuthor = rRedln.GetAuthorString(nStack); pData->aDateTime = rRedln.GetTimeStamp(nStack); pData->eType = rRedln.GetType(nStack); OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime); OUString sComment = rRedln.GetComment(nStack); std::unique_ptr 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 aLBoxArr; weld::TreeView& rTreeView = m_pTable->GetWidget(); // because of Bug of TLB that ALWAYS calls the SelectHandler at Remove: rTreeView.connect_changed(Link()); 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(m_RedlineParents[i]->pNext); auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(), [&pChildPtr](const std::unique_ptr& rChildPtr) { return rChildPtr.get() == pChildPtr; }); if (it != m_RedlineChildren.end()) { sal_uInt16 nChildren = 0; while (pChildPtr) { pChildPtr = const_cast(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 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(rRedln).GetDescr() : rRedln.GetComment(); pRedlineParent->sComment = sComment.replace('\n', ' '); m_RedlineParents.insert(m_RedlineParents.begin() + i, std::unique_ptr(pRedlineParent)); std::unique_ptr 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 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> 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(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 xChild(rTreeView.make_iterator( &*aRedlines[0] )); RedlinData *pData = weld::fromId(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 xChild(rTreeView.make_iterator( &*rRedLine )); if ( rTreeView.iter_children(*xChild) ) { RedlinData *pData = weld::fromId(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(weld::fromId( 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 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 xChild(rTreeView.make_iterator( &*xActEntry )); if ( rTreeView.iter_children(*xChild) ) { RedlinData *pData = reinterpret_cast(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); } } bool const bEnable = !pSh->GetDoc()->GetDocShell()->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 xEntry(rTreeView.make_iterator()); bool bEntry = rTreeView.get_selected(xEntry.get()); if (bEntry) { std::unique_ptr 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 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 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 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 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: */