tdf#164803 sw: Fix SwTextSizeInfo applying grid metrics without grid
[LibreOffice.git] / sw / source / uibase / misc / redlndlg.cxx
blobb2dd7fd8b4db077850c56e8c31d4c6adbfd8616d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <redline.hxx>
21 #include <tools/datetime.hxx>
22 #include <tools/lineend.hxx>
23 #include <svl/eitem.hxx>
24 #include <sfx2/viewfrm.hxx>
25 #include <sfx2/dispatch.hxx>
26 #include <svx/ctredlin.hxx>
27 #include <svx/postattr.hxx>
28 #include <utility>
29 #include <vcl/commandevent.hxx>
30 #include <swtypes.hxx>
31 #include <wrtsh.hxx>
32 #include <view.hxx>
33 #include <swmodule.hxx>
34 #include <redlndlg.hxx>
35 #include <swwait.hxx>
36 #include <uitool.hxx>
37 #include <o3tl/string_view.hxx>
39 #include <cmdid.h>
40 #include <strings.hrc>
42 // -> #111827#
43 #include <swundo.hxx>
44 #include <SwRewriter.hxx>
45 // <- #111827#
47 #include <vector>
48 #include <svx/svxdlg.hxx>
49 #include <osl/diagnose.h>
50 #include <bitmaps.hlst>
52 #include <docsh.hxx>
54 #include <IDocumentRedlineAccess.hxx>
55 #include <usrpref.hxx>
56 #include <memory>
58 SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(SwRedlineAcceptChild, FN_REDLINE_ACCEPT)
60 SwRedlineAcceptChild::SwRedlineAcceptChild(vcl::Window* _pParent,
61 sal_uInt16 nId,
62 SfxBindings* pBindings,
63 SfxChildWinInfo* pInfo)
64 : SwChildWinWrapper(_pParent, nId)
66 auto xDlg = std::make_shared<SwModelessRedlineAcceptDlg>(pBindings, this, _pParent->GetFrameWeld());
67 SetController(xDlg);
68 xDlg->Initialize(pInfo);
71 // newly initialise dialog after document switch
72 bool SwRedlineAcceptChild::ReInitDlg(SwDocShell *pDocSh)
74 bool bRet = SwChildWinWrapper::ReInitDlg(pDocSh);
75 if (bRet) // update immediately, doc switch!
76 static_cast<SwModelessRedlineAcceptDlg*>(GetController().get())->Activate();
78 return bRet;
81 SwModelessRedlineAcceptDlg::SwModelessRedlineAcceptDlg(
82 SfxBindings* _pBindings, SwChildWinWrapper* pChild, weld::Window *pParent)
83 : SfxModelessDialogController(_pBindings, pChild, pParent,
84 u"svx/ui/acceptrejectchangesdialog.ui"_ustr, u"AcceptRejectChangesDialog"_ustr)
85 , m_xContentArea(m_xBuilder->weld_container(u"container"_ustr))
86 , m_pChildWin(pChild)
88 m_xImplDlg.reset(new SwRedlineAcceptDlg(m_xDialog, m_xBuilder.get(), m_xContentArea.get()));
91 void SwModelessRedlineAcceptDlg::Activate()
93 if (mbInDestruction)
94 return;
96 SwView *pView = ::GetActiveView();
97 if (!pView) // can happen when switching to another app, when a Listbox in dialog
98 return; // had the focus previously (actually THs Bug)
100 SwDocShell *pDocSh = pView->GetDocShell();
102 if (m_pChildWin->GetOldDocShell() != pDocSh)
103 { // doc-switch
104 SwWait aWait( *pDocSh, false );
105 SwWrtShell* pSh = pView->GetWrtShellPtr();
106 if (!pSh)
107 return;
109 m_pChildWin->SetOldDocShell(pDocSh); // avoid recursion (using modified-Hdl)
111 bool bMod = pSh->IsModified();
112 SfxBoolItem aShow(FN_REDLINE_SHOW, true);
113 pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(
114 FN_REDLINE_SHOW, SfxCallMode::SYNCHRON|SfxCallMode::RECORD,
115 { &aShow });
116 if (!bMod)
117 pSh->ResetModified();
118 m_xImplDlg->Init();
119 SfxModelessDialogController::Activate();
121 return;
124 SfxModelessDialogController::Activate();
125 m_xImplDlg->Activate();
128 void SwModelessRedlineAcceptDlg::Initialize(SfxChildWinInfo* pInfo)
130 if (pInfo != nullptr)
131 m_xImplDlg->Initialize(pInfo->aExtraString);
133 SfxModelessDialogController::Initialize(pInfo);
136 void SwModelessRedlineAcceptDlg::FillInfo(SfxChildWinInfo& rInfo) const
138 SfxModelessDialogController::FillInfo(rInfo);
139 m_xImplDlg->FillInfo(rInfo.aExtraString);
142 SwModelessRedlineAcceptDlg::~SwModelessRedlineAcceptDlg()
144 mbInDestruction = true;
147 namespace
149 const SwRedlineData* lcl_get_selected_redlinedata(weld::TreeView& rTreeView)
151 std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
152 if (rTreeView.get_selected(xEntry.get()))
154 RedlinData* pRedlinData = weld::fromId<RedlinData*>(rTreeView.get_id(*xEntry));
155 if (rTreeView.get_iter_depth(*xEntry))
156 return static_cast<SwRedlineDataChild*>(pRedlinData->pData)->pChild;
157 else
158 return static_cast<SwRedlineDataParent*>(pRedlinData->pData)->pData;
160 return nullptr;
163 void lcl_reselect(weld::TreeView& rTreeView, const SwRedlineData* pSelectedEntryRedlineData)
165 if (!pSelectedEntryRedlineData)
167 rTreeView.set_cursor(-1);
168 return;
170 rTreeView.all_foreach(
171 [&rTreeView, &pSelectedEntryRedlineData](weld::TreeIter& rIter)
173 RedlinData* pRedlinData = weld::fromId<RedlinData*>(rTreeView.get_id(rIter));
174 const SwRedlineData* pRedlineData;
175 if (rTreeView.get_iter_depth(rIter))
176 pRedlineData = static_cast<SwRedlineDataChild*>(pRedlinData->pData)->pChild;
177 else
178 pRedlineData = static_cast<SwRedlineDataParent*>(pRedlinData->pData)->pData;
179 if (pRedlineData == pSelectedEntryRedlineData)
181 rTreeView.set_cursor(rIter);
182 return true;
184 return false;
189 SwRedlineAcceptDlg::SwRedlineAcceptDlg(std::shared_ptr<weld::Window> xParent, weld::Builder *pBuilder,
190 weld::Container *pContentArea, bool bAutoFormat)
191 : m_xParentDlg(std::move(xParent))
192 , m_aSelectTimer("SwRedlineAcceptDlg m_aSelectTimer")
193 , m_sInserted(SwResId(STR_REDLINE_INSERTED))
194 , m_sDeleted(SwResId(STR_REDLINE_DELETED))
195 , m_sFormated(SwResId(STR_REDLINE_FORMATTED))
196 , m_sTableChgd(SwResId(STR_REDLINE_TABLECHG))
197 , m_sFormatCollSet(SwResId(STR_REDLINE_FMTCOLLSET))
198 , m_sAutoFormat(SwResId(STR_REDLINE_AUTOFMT))
199 , m_bOnlyFormatedRedlines(false)
200 , m_bRedlnAutoFormat(bAutoFormat)
201 , m_bInhibitActivate(false)
202 , m_bHasTrackedColumn(false)
203 , m_xTabPagesCTRL(new SvxAcceptChgCtr(pContentArea))
204 , m_xPopup(pBuilder->weld_menu(u"writermenu"_ustr))
205 , m_xSortMenu(pBuilder->weld_menu(u"writersortmenu"_ustr))
207 m_pTPView = m_xTabPagesCTRL->GetViewPage();
209 m_pTable = m_pTPView->GetTableControl();
210 m_pTable->SetWriterView();
212 m_pTPView->GetSortByComboBoxControl()->set_active(4);
214 m_pTPView->SetSortByComboBoxChangedHdl(
215 LINK(this, SwRedlineAcceptDlg, SortByComboBoxChangedHdl));
217 m_pTPView->SetAcceptClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptHdl));
218 m_pTPView->SetAcceptAllClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptAllHdl));
219 m_pTPView->SetRejectClickHdl(LINK(this, SwRedlineAcceptDlg, RejectHdl));
220 m_pTPView->SetRejectAllClickHdl(LINK(this, SwRedlineAcceptDlg, RejectAllHdl));
221 m_pTPView->SetUndoClickHdl(LINK(this, SwRedlineAcceptDlg, UndoHdl));
222 //tdf#89227 default to disabled, and only enable if possible to accept/reject
223 m_pTPView->EnableAccept(false);
224 m_pTPView->EnableReject(false);
225 m_pTPView->EnableClearFormat(false);
226 m_pTPView->EnableAcceptAll(false);
227 m_pTPView->EnableRejectAll(false);
228 m_pTPView->EnableClearFormatAll(false);
230 m_xTabPagesCTRL->GetFilterPage()->SetReadyHdl(LINK(this, SwRedlineAcceptDlg, FilterChangedHdl));
232 weld::ComboBox* pActLB = m_xTabPagesCTRL->GetFilterPage()->GetLbAction();
233 pActLB->append_text(m_sInserted);
234 pActLB->append_text(m_sDeleted);
235 pActLB->append_text(m_sFormated);
236 pActLB->append_text(m_sTableChgd);
238 if (HasRedlineAutoFormat())
240 pActLB->append_text(m_sFormatCollSet);
241 pActLB->append_text(m_sAutoFormat);
242 m_pTPView->ShowUndo();
243 m_pTPView->DisableUndo(); // no UNDO events yet
246 pActLB->set_active(0);
248 weld::TreeView& rTreeView = m_pTable->GetWidget();
249 rTreeView.set_selection_mode(SelectionMode::Multiple);
251 rTreeView.connect_selection_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl));
252 rTreeView.connect_popup_menu(LINK(this, SwRedlineAcceptDlg, CommandHdl));
254 // avoid multiple selection of the same texts:
255 m_aSelectTimer.SetTimeout(100);
256 m_aSelectTimer.SetInvokeHandler(LINK(this, SwRedlineAcceptDlg, GotoHdl));
258 // we want to receive SfxHintId::SwRedlineContentAtPos
259 StartListening(*(SwModule::get()->GetView()->GetDocShell()));
262 SwRedlineAcceptDlg::~SwRedlineAcceptDlg()
266 void SwRedlineAcceptDlg::Init(SwRedlineTable::size_type nStart)
268 std::optional<SwWait> oWait;
269 SwView* pView = GetActiveView();
270 if (pView)
271 oWait.emplace(*pView->GetDocShell(), false);
272 weld::TreeView& rTreeView = m_pTable->GetWidget();
273 m_aUsedSeqNo.clear();
275 // tdf#162018 keep the selected entry selected
276 const SwRedlineData* pSelectedEntryRedlineData = lcl_get_selected_redlinedata(rTreeView);
278 // tdf#162337 tracked change selection when the Manage Changes dialog is initially opened
279 if (pView && m_bInitialSelect)
281 m_bInitialSelect = false;
282 SwWrtShell* pSh = pView->GetWrtShellPtr();
283 if (pSh)
285 const SwRangeRedline* pCurrRedline = pSh->GetCurrRedline();
286 if (pCurrRedline)
288 // Select current redline
289 SwRedlineTable::size_type nPos
290 = pSh->FindRedlineOfData(pCurrRedline->GetRedlineData());
291 pSh->GotoRedline(nPos, true);
292 pSh->SetInSelect();
294 else
296 // Select the next redline if there is one
297 pSh->AssureStdMode();
298 pCurrRedline = pSh->SelNextRedline();
300 if (pCurrRedline)
301 pSelectedEntryRedlineData = &pCurrRedline->GetRedlineData();
305 rTreeView.freeze();
306 if (nStart)
307 RemoveParents(nStart, m_RedlineParents.size() - 1);
308 else
310 rTreeView.clear();
311 m_RedlinData.clear();
312 m_RedlineChildren.clear();
313 m_RedlineParents.erase(m_RedlineParents.begin() + nStart, m_RedlineParents.end());
315 rTreeView.thaw();
317 // insert parents
318 InsertParents(nStart);
319 InitAuthors();
321 lcl_reselect(rTreeView, pSelectedEntryRedlineData);
324 void SwRedlineAcceptDlg::InitAuthors()
326 if (!m_xTabPagesCTRL)
327 return;
329 SwView *pView = ::GetActiveView();
330 if (!pView)
331 return;
332 SwWrtShell* pSh = pView->GetWrtShellPtr();
334 SvxTPFilter *pFilterPage = m_xTabPagesCTRL->GetFilterPage();
336 std::vector<OUString> aStrings;
337 OUString sOldAuthor(pFilterPage->GetSelectedAuthor());
338 pFilterPage->ClearAuthors();
340 SwRedlineTable::size_type nCount = pSh ? pSh->GetRedlineCount() : 0;
342 m_bOnlyFormatedRedlines = true;
343 bool bIsNotFormated = false;
345 // determine authors
346 for ( SwRedlineTable::size_type i = 0; i < nCount; i++)
348 const SwRangeRedline& rRedln = pSh->GetRedline(i);
350 if( m_bOnlyFormatedRedlines && RedlineType::Format != rRedln.GetType() )
351 m_bOnlyFormatedRedlines = false;
353 aStrings.push_back(rRedln.GetAuthorString());
355 for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++)
357 aStrings.push_back(rRedln.GetAuthorString(nStack));
361 std::sort(aStrings.begin(), aStrings.end());
362 aStrings.erase(std::unique(aStrings.begin(), aStrings.end()), aStrings.end());
364 for (auto const & i: aStrings)
365 pFilterPage->InsertAuthor(i);
367 if (pFilterPage->SelectAuthor(sOldAuthor) == -1 && !aStrings.empty())
368 pFilterPage->SelectAuthor(aStrings[0]);
370 weld::TreeView& rTreeView = m_pTable->GetWidget();
371 SwDocShell* pShell = pSh ? pSh->GetDoc()->GetDocShell() : nullptr;
372 bool const bEnable = pShell && !pShell->IsReadOnly()
373 && rTreeView.n_children() != 0
374 && !pSh->getIDocumentRedlineAccess().GetRedlinePassword().hasElements();
375 bool bSel = rTreeView.get_selected(nullptr);
377 rTreeView.selected_foreach([this, pSh, &bIsNotFormated](weld::TreeIter& rEntry){
378 // find the selected redline
379 // (fdo#57874: ignore, if the redline is already gone)
380 SwRedlineTable::size_type nPos = GetRedlinePos(rEntry);
381 if( nPos != SwRedlineTable::npos )
383 const SwRangeRedline& rRedln = pSh->GetRedline( nPos );
385 bIsNotFormated |= RedlineType::Format != rRedln.GetType();
387 return false;
390 m_pTPView->EnableAccept( bEnable && bSel );
391 m_pTPView->EnableReject( bEnable && bSel );
392 m_pTPView->EnableClearFormat( bEnable && !bIsNotFormated && bSel );
393 m_pTPView->EnableAcceptAll( bEnable );
394 m_pTPView->EnableRejectAll( bEnable );
395 m_pTPView->EnableClearFormatAll( bEnable &&
396 m_bOnlyFormatedRedlines );
399 const OUString & SwRedlineAcceptDlg::GetActionImage(const SwRangeRedline& rRedln, sal_uInt16 nStack,
400 bool bTableChanges, bool bRowChanges)
402 switch (rRedln.GetType(nStack))
404 case RedlineType::Insert: return bTableChanges
405 ? bRowChanges
406 ? BMP_REDLINE_ROW_INSERTION
407 : BMP_REDLINE_COL_INSERTION
408 : rRedln.IsMoved()
409 ? BMP_REDLINE_MOVED_INSERTION
410 : rRedln.IsAnnotation()
411 ? BMP_REDLINE_COMMENT_INSERTION
412 : BMP_REDLINE_INSERTED;
413 case RedlineType::Delete: return bTableChanges
414 ? bRowChanges
415 ? BMP_REDLINE_ROW_DELETION
416 : BMP_REDLINE_COL_DELETION
417 : rRedln.IsMoved()
418 ? BMP_REDLINE_MOVED_DELETION
419 : rRedln.IsAnnotation()
420 ? BMP_REDLINE_COMMENT_DELETION
421 : BMP_REDLINE_DELETED;
422 case RedlineType::Format: return BMP_REDLINE_FORMATTED;
423 case RedlineType::ParagraphFormat: return BMP_REDLINE_FORMATTED;
424 case RedlineType::Table: return BMP_REDLINE_TABLECHG;
425 case RedlineType::FmtColl: return BMP_REDLINE_FMTCOLLSET;
426 default: break;
429 return EMPTY_OUSTRING;
432 const OUString & SwRedlineAcceptDlg::GetActionText(const SwRangeRedline& rRedln, sal_uInt16 nStack)
434 switch( rRedln.GetType(nStack) )
436 case RedlineType::Insert: return m_sInserted;
437 case RedlineType::Delete: return m_sDeleted;
438 case RedlineType::Format: return m_sFormated;
439 case RedlineType::ParagraphFormat: return m_sFormated;
440 case RedlineType::Table: return m_sTableChgd;
441 case RedlineType::FmtColl: return m_sFormatCollSet;
442 default:;//prevent warning
445 return EMPTY_OUSTRING;
448 // newly initialise after activation
449 void SwRedlineAcceptDlg::Activate()
451 // prevent update if flag is set (#102547#)
452 if( m_bInhibitActivate )
453 return;
455 SwView *pView = ::GetActiveView();
456 if (!pView) // can happen when switching to another app
458 m_pTPView->EnableAccept(false);
459 m_pTPView->EnableReject(false);
460 m_pTPView->EnableClearFormat(false);
461 m_pTPView->EnableAcceptAll(false);
462 m_pTPView->EnableRejectAll(false);
463 m_pTPView->EnableClearFormatAll(false);
464 return; // had the focus previously
467 SwWait aWait( *pView->GetDocShell(), false );
469 if (pView->GetDocShell()->IsReadOnly())
471 m_pTPView->EnableAccept(false);
472 m_pTPView->EnableReject(false);
473 m_pTPView->EnableClearFormat(false);
474 m_pTPView->EnableAcceptAll(false);
475 m_pTPView->EnableRejectAll(false);
476 m_pTPView->EnableClearFormatAll(false);
477 // note: enabling is done in InitAuthors below
480 m_aUsedSeqNo.clear();
482 // did something change?
483 SwWrtShell* pSh = pView->GetWrtShellPtr();
484 if (!pSh)
485 return;
487 // tdf#162018 keep the selected entry selected
488 weld::TreeView& rTreeView = m_pTable->GetWidget();
489 const SwRedlineData* pSelectedEntryRedlineData = lcl_get_selected_redlinedata(m_pTable->GetWidget());
491 SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
493 // check the number of pointers
494 for ( SwRedlineTable::size_type i = 0; i < nCount; i++)
496 const SwRangeRedline& rRedln = pSh->GetRedline(i);
498 if (i >= m_RedlineParents.size())
500 // new entries have been appended
501 Init(i);
502 return;
505 SwRedlineDataParent *const pParent = m_RedlineParents[i].get();
506 if (&rRedln.GetRedlineData() != pParent->pData)
508 // Redline-Parents were inserted, changed or deleted
509 i = CalcDiff(i, false);
510 if (i == SwRedlineTable::npos)
512 lcl_reselect(rTreeView, pSelectedEntryRedlineData);
513 return;
515 continue;
518 const SwRedlineData *pRedlineData = rRedln.GetRedlineData().Next();
519 const SwRedlineDataChild *pBackupData = pParent->pNext;
521 if (!pRedlineData && pBackupData)
523 // Redline-Children were deleted
524 i = CalcDiff(i, true);
525 if (i == SwRedlineTable::npos)
527 lcl_reselect(rTreeView, pSelectedEntryRedlineData);
528 return;
530 continue;
532 else
534 while (pRedlineData)
536 if (!pBackupData || pRedlineData != pBackupData->pChild)
538 // Redline-Children were inserted, changed or deleted
539 i = CalcDiff(i, true);
540 if (i == SwRedlineTable::npos)
542 lcl_reselect(rTreeView, pSelectedEntryRedlineData);
543 return;
546 // here was a continue; targetted to the outer loop
547 // now a break will do, as there is nothing after it in the outer loop
548 break;
550 pBackupData = pBackupData->pNext;
551 pRedlineData = pRedlineData->Next();
556 if (nCount != m_RedlineParents.size())
558 // Redlines were deleted at the end
559 Init(nCount);
560 return;
563 // check comment
564 bool bIsShowChangesInMargin = SwModule::get()->GetUsrPref(false)->IsShowChangesInMargin();
565 for (SwRedlineTable::size_type i = 0; i < nCount; i++)
567 const SwRangeRedline& rRedln = pSh->GetRedline(i);
568 SwRedlineDataParent *const pParent = m_RedlineParents[i].get();
570 if(rRedln.GetComment() != pParent->sComment)
572 bool bShowDeletedTextAsComment = bIsShowChangesInMargin &&
573 RedlineType::Delete == rRedln.GetType() && rRedln.GetComment().isEmpty();
574 const OUString sComment = bShowDeletedTextAsComment
575 ? const_cast<SwRangeRedline&>(rRedln).GetDescr()
576 : rRedln.GetComment();
577 if (pParent->xTLBParent)
579 // update only comment
580 rTreeView.set_text(*pParent->xTLBParent, sComment.replace('\n', ' '), 3);
582 pParent->sComment = sComment;
586 InitAuthors();
588 lcl_reselect(rTreeView, pSelectedEntryRedlineData);
591 void SwRedlineAcceptDlg::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
593 if (rHint.GetId() == SfxHintId::SwRedlineContentAtPos)
595 SwView* pView = GetActiveView();
596 if (!pView)
597 return;
599 SwWrtShell* pSh = pView->GetWrtShellPtr();
600 if (!pSh)
601 return;
603 const SwRangeRedline* pRangeRedline = pSh->GetCurrRedline();
604 if (!pRangeRedline)
605 return;
607 const SwRedlineData& rRedlineData = pRangeRedline->GetRedlineData();
609 weld::TreeView& rTreeView = m_pTable->GetWidget();
610 rTreeView.all_foreach([&rTreeView, &rRedlineData](weld::TreeIter& rIter) {
611 RedlinData* pRedlinData = weld::fromId<RedlinData*>(rTreeView.get_id(rIter));
612 const SwRedlineData* pRedlineData;
613 if (rTreeView.get_iter_depth(rIter))
614 pRedlineData = static_cast<SwRedlineDataChild*>(pRedlinData->pData)->pChild;
615 else
616 pRedlineData = static_cast<SwRedlineDataParent*>(pRedlinData->pData)->pData;
617 if (pRedlineData == &rRedlineData)
619 rTreeView.set_cursor(rIter);
620 return true;
622 return false;
627 SwRedlineTable::size_type SwRedlineAcceptDlg::CalcDiff(SwRedlineTable::size_type nStart, bool bChild)
629 if (!nStart || m_bHasTrackedColumn)
631 Init();
632 return SwRedlineTable::npos;
635 weld::TreeView& rTreeView = m_pTable->GetWidget();
636 rTreeView.freeze();
637 SwView *pView = ::GetActiveView();
638 if (!pView)
639 return SwRedlineTable::npos;
641 SwWrtShell* pSh = pView->GetWrtShellPtr();
642 if (!pSh)
643 return SwRedlineTable::npos;
645 bool bHasRedlineAutoFormat = HasRedlineAutoFormat();
646 SwRedlineDataParent *const pParent = m_RedlineParents[nStart].get();
647 const SwRangeRedline& rRedln = pSh->GetRedline(nStart);
649 if (bChild) // should actually never happen, but just in case...
651 // throw away all entry's children and initialise newly
652 SwRedlineDataChild* pBackupData = const_cast<SwRedlineDataChild*>(pParent->pNext);
653 SwRedlineDataChild* pNext;
655 while (pBackupData)
657 pNext = const_cast<SwRedlineDataChild*>(pBackupData->pNext);
658 if (pBackupData->xTLBChild)
659 rTreeView.remove(*pBackupData->xTLBChild);
661 auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(),
662 [&pBackupData](const std::unique_ptr<SwRedlineDataChild>& rChildPtr) { return rChildPtr.get() == pBackupData; });
663 if (it != m_RedlineChildren.end())
664 m_RedlineChildren.erase(it);
666 pBackupData = pNext;
668 pParent->pNext = nullptr;
670 // insert new children
671 InsertChildren(pParent, rRedln, bHasRedlineAutoFormat);
673 rTreeView.thaw();
674 return nStart;
677 // have entries been deleted?
678 const SwRedlineData *pRedlineData = &rRedln.GetRedlineData();
679 for (SwRedlineTable::size_type i = nStart + 1; i < m_RedlineParents.size(); i++)
681 if (m_RedlineParents[i]->pData == pRedlineData)
683 // remove entries from nStart to i-1
684 RemoveParents(nStart, i - 1);
685 rTreeView.thaw();
686 return nStart - 1;
690 // entries been inserted?
691 SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
692 pRedlineData = m_RedlineParents[nStart]->pData;
694 for (SwRedlineTable::size_type i = nStart + 1; i < nCount; i++)
696 if (&pSh->GetRedline(i).GetRedlineData() == pRedlineData)
698 rTreeView.thaw();
699 // insert entries from nStart to i-1
700 InsertParents(nStart, i - 1);
701 return nStart - 1;
705 rTreeView.thaw();
706 Init(nStart); // adjust all entries until the end
707 return SwRedlineTable::npos;
710 void SwRedlineAcceptDlg::InsertChildren(SwRedlineDataParent *pParent, const SwRangeRedline& rRedln, bool bHasRedlineAutoFormat)
712 SwRedlineDataChild *pLastRedlineChild = nullptr;
713 const SwRedlineData *pRedlineData = &rRedln.GetRedlineData();
714 bool bAutoFormatRedline = rRedln.IsAutoFormat();
716 weld::TreeView& rTreeView = m_pTable->GetWidget();
718 OUString sAction = GetActionText(rRedln);
719 bool bValidParent = m_sFilterAction.isEmpty() || m_sFilterAction == sAction;
720 bValidParent = bValidParent && m_pTable->IsValidEntry(rRedln.GetAuthorString(), rRedln.GetTimeStamp(), rRedln.GetComment());
721 if (bHasRedlineAutoFormat)
724 if (pParent->pData->GetSeqNo())
726 std::pair<SwRedlineDataParentSortArr::const_iterator,bool> const ret
727 = m_aUsedSeqNo.insert(pParent);
728 if (ret.second) // already there
730 if (pParent->xTLBParent)
732 rTreeView.set_text(*(*ret.first)->xTLBParent, m_sAutoFormat, 0);
733 rTreeView.remove(*pParent->xTLBParent);
734 pParent->xTLBParent.reset();
736 return;
739 bValidParent = bValidParent && bAutoFormatRedline;
741 bool bValidTree = bValidParent;
743 for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++)
745 pRedlineData = pRedlineData->Next();
747 SwRedlineDataChild* pRedlineChild = new SwRedlineDataChild;
748 pRedlineChild->pChild = pRedlineData;
749 m_RedlineChildren.push_back(std::unique_ptr<SwRedlineDataChild>(pRedlineChild));
751 if ( pLastRedlineChild )
752 pLastRedlineChild->pNext = pRedlineChild;
753 else
754 pParent->pNext = pRedlineChild;
756 sAction = GetActionText(rRedln, nStack);
757 bool bValidChild = m_sFilterAction.isEmpty() || m_sFilterAction == sAction;
758 bValidChild = bValidChild && m_pTable->IsValidEntry(rRedln.GetAuthorString(nStack), rRedln.GetTimeStamp(nStack), rRedln.GetComment());
759 if (bHasRedlineAutoFormat)
760 bValidChild = bValidChild && bAutoFormatRedline;
761 bValidTree |= bValidChild;
763 if (bValidChild)
765 std::unique_ptr<RedlinData> pData(new RedlinData);
766 pData->pData = pRedlineChild;
767 pData->bDisabled = true;
769 OUString sImage(GetActionImage(rRedln, nStack));
770 const OUString& sAuthor = rRedln.GetAuthorString(nStack);
771 pData->aDateTime = rRedln.GetTimeStamp(nStack);
772 pData->eType = rRedln.GetType(nStack);
773 OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime);
774 const OUString& sComment = rRedln.GetComment(nStack);
776 std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator());
777 OUString sId(weld::toId(pData.get()));
778 rTreeView.insert(pParent->xTLBParent.get(), -1, nullptr, &sId, nullptr, nullptr,
779 false, xChild.get());
780 m_RedlinData.push_back(std::move(pData));
782 rTreeView.set_image(*xChild, sImage, -1);
783 rTreeView.set_text(*xChild, sAuthor, 1);
784 rTreeView.set_text(*xChild, sDateEntry, 2);
785 rTreeView.set_text(*xChild, sComment, 3);
787 pRedlineChild->xTLBChild = std::move(xChild);
788 if (!bValidParent)
789 rTreeView.expand_row(*pParent->xTLBParent);
791 else
792 pRedlineChild->xTLBChild.reset();
794 pLastRedlineChild = pRedlineChild;
797 if (pLastRedlineChild)
798 pLastRedlineChild->pNext = nullptr;
800 if (!bValidTree && pParent->xTLBParent)
802 rTreeView.remove(*pParent->xTLBParent);
803 pParent->xTLBParent.reset();
804 if (bHasRedlineAutoFormat)
805 m_aUsedSeqNo.erase(pParent);
809 void SwRedlineAcceptDlg::RemoveParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd)
811 SwView *pView = ::GetActiveView();
812 if (!pView)
813 return;
815 SwWrtShell* pSh = pView->GetWrtShellPtr();
816 if (!pSh)
817 return;
819 SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
821 std::vector<const weld::TreeIter*> aLBoxArr;
823 weld::TreeView& rTreeView = m_pTable->GetWidget();
825 // because of Bug of TLB that ALWAYS calls the SelectHandler at Remove:
826 rTreeView.connect_selection_changed(Link<weld::TreeView&, void>());
828 bool bChildrenRemoved = false;
829 rTreeView.thaw();
830 rTreeView.unselect_all();
832 // set the cursor after the last entry because otherwise performance problem in TLB.
833 // TLB would otherwise reset the cursor at every Remove (expensive)
834 SwRedlineTable::size_type nPos = std::min(nCount, m_RedlineParents.size());
835 weld::TreeIter *pCurEntry = nullptr;
836 while( ( pCurEntry == nullptr ) && ( nPos > 0 ) )
838 --nPos;
839 pCurEntry = m_RedlineParents[nPos]->xTLBParent.get();
842 if (pCurEntry)
843 rTreeView.set_cursor(*pCurEntry);
845 rTreeView.freeze();
847 for (SwRedlineTable::size_type i = nStart; i <= nEnd; i++)
849 if (!bChildrenRemoved && m_RedlineParents[i]->pNext)
851 SwRedlineDataChild * pChildPtr =
852 const_cast<SwRedlineDataChild*>(m_RedlineParents[i]->pNext);
853 auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(),
854 [&pChildPtr](const std::unique_ptr<SwRedlineDataChild>& rChildPtr) { return rChildPtr.get() == pChildPtr; });
855 if (it != m_RedlineChildren.end())
857 sal_uInt16 nChildren = 0;
858 while (pChildPtr)
860 pChildPtr = const_cast<SwRedlineDataChild*>(pChildPtr->pNext);
861 nChildren++;
864 m_RedlineChildren.erase(it, it + nChildren);
865 bChildrenRemoved = true;
868 weld::TreeIter *const pEntry = m_RedlineParents[i]->xTLBParent.get();
869 if (pEntry)
870 aLBoxArr.push_back(pEntry);
873 std::sort(aLBoxArr.begin(), aLBoxArr.end(), [&rTreeView](const weld::TreeIter* a, const weld::TreeIter* b) {
874 return rTreeView.iter_compare(*a, *b) == -1;
876 // clear TLB from behind
877 for (auto it = aLBoxArr.rbegin(); it != aLBoxArr.rend(); ++it)
879 const weld::TreeIter* pIter = *it;
880 rTreeView.remove(*pIter);
883 rTreeView.thaw();
884 rTreeView.connect_selection_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl));
885 // unfortunately by Remove it was selected from the TLB always again ...
886 rTreeView.unselect_all();
887 rTreeView.freeze();
889 m_RedlineParents.erase(m_RedlineParents.begin() + nStart, m_RedlineParents.begin() + nEnd + 1);
892 void SwRedlineAcceptDlg::InsertParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd)
894 SwView *pView = ::GetActiveView();
895 if (!pView)
896 return;
898 SwWrtShell* pSh = pView->GetWrtShellPtr();
899 if (!pSh)
900 return;
902 bool bHasRedlineAutoFormat = HasRedlineAutoFormat();
904 SwRedlineTable::size_type nCount = pSh->GetRedlineCount();
905 nEnd = std::min(nEnd, (nCount - 1)); // also treats nEnd=SwRedlineTable::npos (until the end)
907 // reset m_bHasTrackedColumn before searching tracked column again
908 if ( m_bHasTrackedColumn && nStart == 0 )
909 m_bHasTrackedColumn = false;
911 if (nEnd == SwRedlineTable::npos)
912 return; // no redlines in the document
914 weld::TreeView& rTreeView = m_pTable->GetWidget();
916 SwRedlineDataParent* pRedlineParent;
918 rTreeView.freeze();
919 if (m_pTable->IsSorted())
920 rTreeView.make_unsorted();
922 bool bIsShowChangesInMargin = SwModule::get()->GetUsrPref(false)->IsShowChangesInMargin();
924 // collect redlines of tracked table/row/column insertion/deletions under a single tree list
925 // item to accept/reject the table change with a single click on Accept/Reject
926 // Note: because update of the tree list depends on parent count, we don't modify
927 // m_RedlineParents, only store the 2nd and more redlines as children of the tree list
928 // item of the first redline
930 // count of items stored as children (to adjust parent position)
931 SwRedlineTable::size_type nSkipRedlines = 0;
932 // count of items of the actual table change stored as children =
933 // redlines of the change - 1 (first redline is associated to the parent tree list item)
934 SwRedlineTable::size_type nSkipRedline = 0;
936 // descriptor redline of the tracked table row/column
937 SwRedlineTable::size_type nRowChange = 0;
939 // first redlines of the tracked table rows/columns, base of the parent tree lists
940 // of the other SwRangeRedlines of the tracked table rows or columns
941 std::vector<SwRedlineTable::size_type> aTableParents;
943 // show all redlines as tree list items,
944 // redlines of a tracked table (row) insertion/deletion showed as children of a single parent
945 for (SwRedlineTable::size_type i = nStart; i <= nEnd; i++)
947 const SwRangeRedline& rRedln = pSh->GetRedline(i);
948 const SwRedlineData *pRedlineData = &rRedln.GetRedlineData();
949 // redline is a child associated to this table row/column change
950 SwRedlineTable::size_type nTableParent = SwRedlineTable::npos;
952 pRedlineParent = new SwRedlineDataParent;
953 pRedlineParent->pData = pRedlineData;
954 pRedlineParent->pNext = nullptr;
956 // handle tracked table row changes
957 const SwTableBox* pTableBox;
958 const SwTableLine* pTableLine;
959 bool bChange = false;
960 bool bRowChange = false;
961 if ( // not recognized yet as tracked table row change
962 nullptr != ( pTableBox = pSh->GetRedline(i).Start()->GetNode().GetTableBox() ) &&
963 nullptr != ( pTableLine = pTableBox->GetUpper() ) &&
964 // it's a tracked row (or column change) based on the cached row data
965 ( RedlineType::None != pTableLine->GetRedlineType() ||
966 RedlineType::None != pTableBox->GetRedlineType() ) )
968 // start redline search from the start from the tracked row/column change
969 SwRedlineTable::size_type nStartPos =
970 nRowChange > nSkipRedline ? nRowChange - nSkipRedline : 0;
971 bChange = true;
972 bRowChange = RedlineType::None != pTableLine->GetRedlineType();
973 nRowChange = bRowChange
974 ? pTableLine->UpdateTextChangesOnly(nStartPos)
975 : pTableBox->GetRedline();
976 // redline is there in a tracked table change
977 if ( SwRedlineTable::npos != nRowChange )
979 // join the consecutive deleted/inserted rows/columns under a single treebox item,
980 // if they have the same redline data (equal type, author and time stamp)
981 for (size_t j = 0; j < aTableParents.size(); j++)
983 // note: CanCombine() allows a time frame to join the changes within a short
984 // time period: this avoid of falling apart of the tracked columns inserted
985 // by several clicks
986 if ( pSh->GetRedline(nRowChange).GetRedlineData()
987 .CanCombine(pSh->GetRedline(aTableParents[j]).GetRedlineData()) )
989 nSkipRedline++;
990 nTableParent = aTableParents[j];
991 break;
996 if ( SwRedlineTable::npos == nTableParent )
998 // table redline didn't fit in the stored ones, create new parent
999 aTableParents.push_back(i);
1002 // it needs major tree update later because of tracked table columns
1003 if ( !m_bHasTrackedColumn && !bRowChange )
1005 m_bHasTrackedColumn = true;
1008 else
1010 // redline is not in a tracked table change
1011 bChange = bRowChange = false;
1015 // empty parent cache for the last table
1016 if ( !pTableBox )
1018 aTableParents.clear();
1021 bool bShowDeletedTextAsComment = bIsShowChangesInMargin &&
1022 RedlineType::Delete == rRedln.GetType() && rRedln.GetComment().isEmpty();
1023 const OUString sComment = bShowDeletedTextAsComment
1024 ? const_cast<SwRangeRedline&>(rRedln).GetDescr()
1025 : rRedln.GetComment();
1026 pRedlineParent->sComment = sComment.replace('\n', ' ');
1027 m_RedlineParents.insert(m_RedlineParents.begin() + i,
1028 std::unique_ptr<SwRedlineDataParent>(pRedlineParent));
1030 std::unique_ptr<RedlinData> pData(new RedlinData);
1031 pData->pData = pRedlineParent;
1032 pData->bDisabled = false;
1034 // use descriptor SwRangeRedline of the changed row, if needed to show
1035 // the correct redline type, author and time stamp of the tracked row change
1036 const SwRangeRedline& rChangeRedln = pSh->GetRedline(bChange ? nRowChange : i);
1038 OUString sImage = GetActionImage(rChangeRedln, 0, bChange && aTableParents.back() == i, bRowChange );
1039 OUString sAuthor = rChangeRedln.GetAuthorString(0);
1040 pData->aDateTime = rChangeRedln.GetTimeStamp(0);
1041 pData->eType = rChangeRedln.GetType(0);
1042 OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime);
1044 OUString sId = weld::toId(pData.get());
1045 std::unique_ptr<weld::TreeIter> xParent(rTreeView.make_iterator());
1047 if ( !bChange || aTableParents.back() == i )
1049 rTreeView.insert(nullptr, i - nSkipRedlines, nullptr, &sId, nullptr, nullptr, false, xParent.get());
1050 // before this was a tracked table change with more than a single redline
1051 if ( nSkipRedline > 0 )
1053 nSkipRedlines += nSkipRedline;
1054 nSkipRedline = 0;
1057 else
1059 // put 2nd or more redlines of deleted/inserted rows as children of their first redline
1060 SwRedlineDataParent *const pParent = m_RedlineParents[nTableParent].get();
1061 rTreeView.insert(pParent->xTLBParent.get(), -1, nullptr, &sId, nullptr, nullptr, false, xParent.get());
1064 m_RedlinData.push_back(std::move(pData));
1066 rTreeView.set_image(*xParent, sImage, -1);
1067 rTreeView.set_text(*xParent, sAuthor, 1);
1068 rTreeView.set_text(*xParent, sDateEntry, 2);
1069 rTreeView.set_text(*xParent, sComment, 3);
1071 pRedlineParent->xTLBParent = std::move(xParent);
1073 InsertChildren(pRedlineParent, rRedln, bHasRedlineAutoFormat);
1075 rTreeView.thaw();
1076 if (m_pTable->IsSorted())
1077 rTreeView.make_sorted();
1080 void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect, bool bAccept )
1082 SwView *pView = ::GetActiveView();
1083 if (!pView)
1084 return;
1086 SwWrtShell* pSh = pView->GetWrtShellPtr();
1087 if (!pSh)
1088 return;
1090 int nPos = -1;
1092 typedef std::vector<std::unique_ptr<weld::TreeIter>> ListBoxEntries_t;
1093 ListBoxEntries_t aRedlines;
1095 // don't activate
1096 OSL_ENSURE( !m_bInhibitActivate,
1097 "recursive call of CallAcceptReject?");
1098 m_bInhibitActivate = true;
1100 weld::TreeView& rTreeView = m_pTable->GetWidget();
1102 auto lambda = [this, pSh, bSelect, bAccept, &rTreeView, &nPos, &aRedlines](weld::TreeIter& rEntry) {
1103 if (!rTreeView.get_iter_depth(rEntry))
1105 if (bSelect && nPos == -1)
1106 nPos = rTreeView.get_iter_index_in_parent(rEntry);
1108 RedlinData *pData = weld::fromId<RedlinData*>(rTreeView.get_id(rEntry));
1110 bool bIsNotFormatted = true;
1112 // first remove only changes with insertion/deletion, if they exist
1113 // (format-only changes haven't had real rejection yet, only an
1114 // approximation: clear direct formatting, so try to warn
1115 // with the extended button label "Reject All/Clear formatting")
1116 if ( !bSelect && !bAccept && !m_bOnlyFormatedRedlines )
1118 SwRedlineTable::size_type nPosition = GetRedlinePos(rEntry);
1119 const SwRangeRedline& rRedln = pSh->GetRedline(nPosition);
1121 if( RedlineType::Format == rRedln.GetType() )
1122 bIsNotFormatted = false;
1125 if (!pData->bDisabled && bIsNotFormatted)
1126 aRedlines.emplace_back(rTreeView.make_iterator(&rEntry));
1128 return false;
1131 // collect redlines-to-be-accepted/rejected in aRedlines vector
1132 if (bSelect)
1133 rTreeView.selected_foreach(lambda);
1134 else
1135 rTreeView.all_foreach(lambda);
1137 bool (SwEditShell::*FnAccRej)( SwRedlineTable::size_type ) = &SwEditShell::AcceptRedline;
1138 if( !bAccept )
1139 FnAccRej = &SwEditShell::RejectRedline;
1141 SwWait aWait( *pSh->GetView().GetDocShell(), true );
1142 pSh->StartAction();
1144 bool bMoreRedlines( aRedlines.size() > 1 ||
1145 // single item with children, e.g. multiple redlines of a table or table row deletion/insertion
1146 ( aRedlines.size() == 1 && rTreeView.iter_n_children(*aRedlines[0]) > 0 ) );
1148 // don't add extra Undo label to a single item only with redline stack (i.e. old changes
1149 // on the same text range, stored only in OOXML)
1150 if ( bMoreRedlines && aRedlines.size() == 1 )
1152 std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator( &*aRedlines[0] ));
1153 RedlinData *pData = weld::fromId<RedlinData*>(rTreeView.get_id(*xChild));
1154 if ( pData->bDisabled )
1155 bMoreRedlines = false;
1158 if ( bMoreRedlines )
1160 OUString aTmpStr;
1162 SwRewriter aRewriter;
1163 aRewriter.AddRule(UndoArg1,
1164 OUString::number(aRedlines.size()));
1165 aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
1168 SwRewriter aRewriter;
1169 aRewriter.AddRule(UndoArg1, aTmpStr);
1171 pSh->StartUndo(bAccept? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE,
1172 &aRewriter);
1175 // accept/reject the redlines in aRedlines. The absolute
1176 // position may change during the process (e.g. when two redlines
1177 // are merged in result of another one being deleted), so the
1178 // position must be resolved late and checked before using it.
1179 // (cf #102547#)
1180 for (const auto& rRedLine : aRedlines)
1182 SwRedlineTable::size_type nPosition = GetRedlinePos( *rRedLine );
1183 if( nPosition != SwRedlineTable::npos )
1184 (pSh->*FnAccRej)( nPosition );
1186 // handle redlines of table rows, stored as children of the item associated
1187 // to the deleted/inserted table row(s)
1188 std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator( &*rRedLine ));
1189 if ( rTreeView.iter_children(*xChild) )
1191 RedlinData *pData = weld::fromId<RedlinData*>(rTreeView.get_id(*xChild));
1192 // disabled for redline stack, but not for redlines of table rows
1193 if ( !pData->bDisabled )
1197 nPosition = GetRedlinePos( *xChild );
1198 if( nPosition != SwRedlineTable::npos )
1199 (pSh->*FnAccRej)( nPosition );
1201 while ( rTreeView.iter_next_sibling(*xChild) );
1206 if ( bMoreRedlines )
1208 pSh->EndUndo();
1211 pSh->EndAction();
1213 m_bInhibitActivate = false;
1214 Activate();
1216 if (nPos != -1 && rTreeView.n_children())
1218 if (nPos >= rTreeView.n_children())
1219 nPos = rTreeView.n_children() - 1;
1220 rTreeView.select(nPos);
1221 rTreeView.scroll_to_row(nPos);
1222 rTreeView.set_cursor(nPos);
1223 SelectHdl(rTreeView);
1225 m_pTPView->EnableUndo();
1228 SwRedlineTable::size_type SwRedlineAcceptDlg::GetRedlinePos(const weld::TreeIter& rEntry)
1230 SwView* pView = GetActiveView();
1231 if (!pView)
1232 return SwRedlineTable::npos;
1234 SwWrtShell* pSh = pView->GetWrtShellPtr();
1235 if (!pSh)
1236 return SwRedlineTable::npos;
1238 weld::TreeView& rTreeView = m_pTable->GetWidget();
1239 return pSh->FindRedlineOfData( *static_cast<SwRedlineDataParent*>(weld::fromId<RedlinData*>(
1240 rTreeView.get_id(rEntry))->pData)->pData );
1243 IMPL_LINK_NOARG(SwRedlineAcceptDlg, SortByComboBoxChangedHdl, SvxTPView*, void)
1245 SwView* pView = GetActiveView();
1246 if (!pView)
1247 return;
1248 SwWait aWait(*pView->GetDocShell(), false);
1249 auto nSortMode = m_pTPView->GetSortByComboBoxControl()->get_active();
1250 if (nSortMode == 4)
1251 nSortMode = -1;
1252 m_pTable->HeaderBarClick(nSortMode);
1253 if (nSortMode == -1)
1254 Init();
1257 IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptHdl, SvxTPView*, void)
1259 CallAcceptReject( true, true );
1262 IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptAllHdl, SvxTPView*, void)
1264 CallAcceptReject( false, true );
1267 IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectHdl, SvxTPView*, void)
1269 CallAcceptReject( true, false );
1272 IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectAllHdl, SvxTPView*, void)
1274 CallAcceptReject( false, false );
1277 IMPL_LINK_NOARG(SwRedlineAcceptDlg, UndoHdl, SvxTPView*, void)
1279 if (SwView* pView = GetActiveView())
1281 pView->GetViewFrame().GetDispatcher()->
1282 Execute(SID_UNDO, SfxCallMode::SYNCHRON);
1283 const SfxPoolItemHolder aResult(pView->GetSlotState(SID_UNDO));
1284 m_pTPView->EnableUndo(aResult.is());
1287 Activate();
1290 IMPL_LINK_NOARG(SwRedlineAcceptDlg, FilterChangedHdl, SvxTPFilter*, void)
1292 SvxTPFilter *pFilterTP = m_xTabPagesCTRL->GetFilterPage();
1294 if (pFilterTP->IsAction())
1295 m_sFilterAction = pFilterTP->GetLbAction()->get_active_text();
1296 else
1297 m_sFilterAction.clear();
1299 Init();
1302 IMPL_LINK_NOARG(SwRedlineAcceptDlg, SelectHdl, weld::TreeView&, void)
1304 m_aSelectTimer.Start();
1307 IMPL_LINK_NOARG(SwRedlineAcceptDlg, GotoHdl, Timer *, void)
1309 m_aSelectTimer.Stop();
1311 SwView* pView = GetActiveView();
1312 if (!pView)
1313 return;
1315 SwWrtShell* pSh = pView->GetWrtShellPtr();
1316 if (!pSh)
1317 return;
1319 bool bIsNotFormated = false;
1320 bool bSel = false;
1322 //#98883# don't select redlines while the dialog is not focused
1323 //#107938# But not only ask pTable if it has the focus. To move
1324 // the selection to the selected redline any child of pParentDlg
1325 // may the focus.
1326 if (!m_xParentDlg || m_xParentDlg->has_toplevel_focus())
1328 weld::TreeView& rTreeView = m_pTable->GetWidget();
1329 std::unique_ptr<weld::TreeIter> xActEntry(rTreeView.make_iterator());
1330 if (rTreeView.get_selected(xActEntry.get()))
1332 pSh->StartAction();
1333 pSh->EnterStdMode();
1334 SwViewShell::SetCareDialog(m_xParentDlg);
1336 rTreeView.selected_foreach([this, pSh, &rTreeView, &xActEntry, &bIsNotFormated, &bSel](weld::TreeIter& rEntry){
1337 rTreeView.copy_iterator(rEntry, *xActEntry);
1338 if (rTreeView.get_iter_depth(rEntry))
1340 rTreeView.iter_parent(*xActEntry);
1341 if (rTreeView.is_selected(*xActEntry))
1342 return false; // don't select twice
1344 else
1345 bSel = true;
1347 // #98864# find the selected redline (ignore, if the redline is already gone)
1348 SwRedlineTable::size_type nPos = GetRedlinePos(*xActEntry);
1349 if (nPos != SwRedlineTable::npos)
1352 const SwRangeRedline& rRedln = pSh->GetRedline( nPos );
1353 bIsNotFormated |= RedlineType::Format != rRedln.GetType();
1355 if (pSh->GotoRedline(nPos, true))
1357 pSh->SetInSelect();
1358 pSh->EnterAddMode();
1362 // select all redlines of tracked table rows
1363 std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator( &*xActEntry ));
1364 if ( rTreeView.iter_children(*xChild) )
1366 RedlinData *pData = reinterpret_cast<RedlinData*>(rTreeView.get_id(*xChild).toInt64());
1367 // disabled for redline stack, but not for redlines of table rows
1368 if ( !pData->bDisabled )
1372 nPos = GetRedlinePos(*xChild);
1373 if (nPos != SwRedlineTable::npos)
1375 const SwRangeRedline& rRedln = pSh->GetRedline( nPos );
1376 bIsNotFormated |= RedlineType::Format != rRedln.GetType();
1378 if (pSh->GotoRedline(nPos, true))
1380 pSh->SetInSelect();
1381 pSh->EnterAddMode();
1385 while ( rTreeView.iter_next_sibling(*xChild) );
1388 return false;
1391 pSh->LeaveAddMode();
1392 pSh->EndAction();
1393 SwViewShell::SetCareDialog(nullptr);
1397 SwDocShell* pShell = pSh->GetDoc()->GetDocShell();
1398 bool const bEnable = pShell && !pShell->IsReadOnly()
1399 && !pSh->getIDocumentRedlineAccess().GetRedlinePassword().hasElements();
1400 m_pTPView->EnableAccept( bEnable && bSel /*&& !bReadonlySel*/ );
1401 m_pTPView->EnableReject( bEnable && bSel /*&& !bReadonlySel*/ );
1402 m_pTPView->EnableClearFormat( bEnable && bSel && !bIsNotFormated /*&& !bReadonlySel*/ );
1403 m_pTPView->EnableAcceptAll( bEnable );
1404 m_pTPView->EnableRejectAll( bEnable );
1405 m_pTPView->EnableClearFormatAll( bEnable && m_bOnlyFormatedRedlines );
1408 IMPL_LINK(SwRedlineAcceptDlg, CommandHdl, const CommandEvent&, rCEvt, bool)
1410 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
1411 return false;
1413 SwView* pView = GetActiveView();
1414 if (!pView)
1415 return false;
1417 SwWrtShell* pSh = pView->GetWrtShellPtr();
1418 if (!pSh)
1419 return false;
1421 const SwRangeRedline *pRed = nullptr;
1423 weld::TreeView& rTreeView = m_pTable->GetWidget();
1424 std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
1425 bool bEntry = rTreeView.get_selected(xEntry.get());
1426 if (bEntry)
1428 std::unique_ptr<weld::TreeIter> xTopEntry(rTreeView.make_iterator(xEntry.get()));
1430 if (rTreeView.get_iter_depth(*xTopEntry))
1431 rTreeView.iter_parent(*xTopEntry);
1433 SwRedlineTable::size_type nPos = GetRedlinePos(*xTopEntry);
1435 // disable commenting for protected areas
1436 if (nPos != SwRedlineTable::npos && (pRed = pSh->GotoRedline(nPos, true)) != nullptr)
1438 if( pSh->IsCursorPtAtEnd() )
1439 pSh->SwapPam();
1440 pSh->SetInSelect();
1444 m_xPopup->set_sensitive(u"writeredit"_ustr, bEntry && pRed &&
1445 !rTreeView.get_iter_depth(*xEntry) &&
1446 rTreeView.count_selected_rows() == 1);
1447 m_xPopup->set_sensitive(u"writersort"_ustr, rTreeView.n_children() != 0);
1448 int nColumn = rTreeView.get_sort_column();
1449 if (nColumn == -1)
1450 nColumn = 4;
1451 for (sal_Int32 i = 0; i < 5; ++i)
1452 m_xSortMenu->set_active(u"writersort" + OUString::number(i), i == nColumn);
1454 OUString sCommand = m_xPopup->popup_at_rect(&rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
1456 if (sCommand == "writeredit")
1458 if (bEntry)
1460 if (rTreeView.get_iter_depth(*xEntry))
1461 rTreeView.iter_parent(*xEntry);
1463 SwRedlineTable::size_type nPos = GetRedlinePos(*xEntry);
1465 if (nPos == SwRedlineTable::npos)
1466 return true;
1468 const SwRangeRedline &rRedline = pSh->GetRedline(nPos);
1470 OUString sComment = convertLineEnd(rRedline.GetComment(), GetSystemLineEnd());
1471 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1472 ::DialogGetRanges fnGetRange = pFact->GetDialogGetRangesFunc();
1473 SfxItemSet aSet( pSh->GetAttrPool(), fnGetRange() );
1475 aSet.Put(SvxPostItTextItem(sComment, SID_ATTR_POSTIT_TEXT));
1476 aSet.Put(SvxPostItAuthorItem(rRedline.GetAuthorString(), SID_ATTR_POSTIT_AUTHOR));
1478 aSet.Put(SvxPostItDateItem( GetAppLangDateTimeString(
1479 rRedline.GetRedlineData().GetTimeStamp() ),
1480 SID_ATTR_POSTIT_DATE ));
1482 ScopedVclPtr<AbstractSvxPostItDialog> pDlg(pFact->CreateSvxPostItDialog(&rTreeView, aSet));
1484 pDlg->HideAuthor();
1486 TranslateId pResId;
1487 switch( rRedline.GetType() )
1489 case RedlineType::Insert:
1490 pResId = STR_REDLINE_INSERTED;
1491 break;
1492 case RedlineType::Delete:
1493 pResId = STR_REDLINE_DELETED;
1494 break;
1495 case RedlineType::Format:
1496 case RedlineType::ParagraphFormat:
1497 pResId = STR_REDLINE_FORMATTED;
1498 break;
1499 case RedlineType::Table:
1500 pResId = STR_REDLINE_TABLECHG;
1501 break;
1502 default:;//prevent warning
1504 OUString sTitle(SwResId(STR_REDLINE_COMMENT));
1505 if (pResId)
1506 sTitle += SwResId(pResId);
1507 pDlg->SetText(sTitle);
1509 SwViewShell::SetCareDialog(pDlg->GetDialog());
1511 if ( pDlg->Execute() == RET_OK )
1513 const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
1514 OUString sMsg(pOutSet->Get(SID_ATTR_POSTIT_TEXT).GetValue());
1516 // insert / change comment
1517 pSh->SetRedlineComment(sMsg);
1518 rTreeView.set_text(*xEntry, sMsg.replace('\n', ' '), 3);
1521 SwViewShell::SetCareDialog(nullptr);
1522 pDlg.disposeAndClear();
1525 else if (!sCommand.isEmpty())
1527 int nSortMode = o3tl::toInt32(sCommand.subView(10));
1529 if (nSortMode == 4 && nColumn == 4)
1530 return true; // we already have it
1532 m_pTPView->GetSortByComboBoxControl()->set_active(nSortMode);
1534 if (nSortMode == 4)
1535 nSortMode = -1; // unsorted / sorted by position
1537 SwWait aWait( *pView->GetDocShell(), false );
1538 m_pTable->HeaderBarClick(nSortMode);
1539 if (nSortMode == -1)
1540 Init(); // newly fill everything
1542 return true;
1545 namespace
1547 OUString lcl_StripAcceptChgDat(OUString &rExtraString)
1549 OUString aStr;
1550 while(true)
1552 sal_Int32 nPos = rExtraString.indexOf("AcceptChgDat:");
1553 if (nPos == -1)
1554 break;
1555 // try to read the alignment string "ALIGN:(...)"; if none existing,
1556 // it's an old version
1557 sal_Int32 n1 = rExtraString.indexOf('(', nPos);
1558 if (n1 != -1)
1560 sal_Int32 n2 = rExtraString.indexOf(')', n1);
1561 if (n2 != -1)
1563 // cut out the alignment string
1564 aStr = rExtraString.copy(nPos, n2 - nPos + 1);
1565 rExtraString = rExtraString.replaceAt(nPos, n2 - nPos + 1, u"");
1566 aStr = aStr.copy(n1 - nPos + 1);
1570 return aStr;
1574 void SwRedlineAcceptDlg::Initialize(OUString& rExtraString)
1576 if (rExtraString.isEmpty())
1577 return;
1579 OUString aStr = lcl_StripAcceptChgDat(rExtraString);
1580 if (aStr.isEmpty())
1581 return;
1583 int nCount = aStr.toInt32();
1584 if (nCount <= 2)
1585 return;
1587 std::vector<int> aEndPos;
1589 for (int i = 0; i < nCount; ++i)
1591 sal_Int32 n1 = aStr.indexOf(';');
1592 aStr = aStr.copy( n1+1 );
1593 aEndPos.push_back(aStr.toInt32());
1596 bool bUseless = false;
1598 std::vector<int> aWidths;
1599 for (int i = 1; i < nCount; ++i)
1601 aWidths.push_back(aEndPos[i] - aEndPos[i - 1]);
1602 if (aWidths.back() <= 0)
1603 bUseless = true;
1606 if (!bUseless)
1608 // turn column end points back to column widths, ignoring the small
1609 // value used for the expander column
1610 weld::TreeView& rTreeView = m_pTable->GetWidget();
1611 rTreeView.set_column_fixed_widths(aWidths);
1615 void SwRedlineAcceptDlg::FillInfo(OUString &rExtraData) const
1617 //remove any old one before adding a new one
1618 lcl_StripAcceptChgDat(rExtraData);
1619 rExtraData += "AcceptChgDat:(";
1621 const int nTabCount = 4;
1623 rExtraData += OUString::number(nTabCount);
1624 rExtraData += ";";
1626 weld::TreeView& rTreeView = m_pTable->GetWidget();
1627 std::vector<int> aWidths;
1628 // turn column widths back into column end points for compatibility
1629 // with how they used to be stored, including a small value for the
1630 // expander column
1631 aWidths.push_back(rTreeView.get_checkbox_column_width());
1632 for (int i = 0; i < nTabCount - 1; ++i)
1634 int nWidth = rTreeView.get_column_width(i);
1635 assert(nWidth > 0 && "suspicious to get a value like this");
1636 aWidths.push_back(aWidths.back() + nWidth);
1639 for (auto a : aWidths)
1641 rExtraData += OUString::number(a);
1642 rExtraData += ";";
1644 rExtraData += ")";
1647 SwRedlineAcceptPanel::SwRedlineAcceptPanel(weld::Widget* pParent)
1648 : PanelLayout(pParent, u"ManageChangesPanel"_ustr, u"modules/swriter/ui/managechangessidebar.ui"_ustr)
1649 , mxContentArea(m_xBuilder->weld_container(u"content_area"_ustr))
1651 mpImplDlg.reset(new SwRedlineAcceptDlg(nullptr, m_xBuilder.get(), mxContentArea.get()));
1653 mpImplDlg->Init();
1655 // we want to receive SfxHintId::DocChanged
1656 StartListening(*(SwModule::get()->GetView()->GetDocShell()));
1659 SwRedlineAcceptPanel::~SwRedlineAcceptPanel()
1663 void SwRedlineAcceptPanel::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
1665 if (mpImplDlg && rHint.GetId() == SfxHintId::DocChanged)
1666 mpImplDlg->Activate();
1669 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */