Add a comment to clarify what kind of inputs the class handles
[LibreOffice.git] / sw / source / uibase / docvw / PostItMgr.cxx
blob5e3ccb5857ef7fb1554bd17d5e5d2093dc5bd881
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 <config_wasm_strip.h>
22 #include <boost/property_tree/json_parser.hpp>
24 #include <PostItMgr.hxx>
25 #include <postithelper.hxx>
27 #include <AnnotationWin.hxx>
28 #include "frmsidebarwincontainer.hxx"
29 #include <accmap.hxx>
31 #include <SidebarWindowsConsts.hxx>
32 #include "AnchorOverlayObject.hxx"
33 #include "ShadowOverlayObject.hxx"
35 #include <utility>
36 #include <vcl/svapp.hxx>
37 #include <vcl/outdev.hxx>
38 #include <vcl/settings.hxx>
40 #include <chrdlgmodes.hxx>
41 #include <viewopt.hxx>
42 #include <view.hxx>
43 #include <docsh.hxx>
44 #include <wrtsh.hxx>
45 #include <doc.hxx>
46 #include <IDocumentSettingAccess.hxx>
47 #include <IDocumentFieldsAccess.hxx>
48 #include <docstyle.hxx>
49 #include <fldbas.hxx>
50 #include <fmtfld.hxx>
51 #include <docufld.hxx>
52 #include <edtwin.hxx>
53 #include <txtfld.hxx>
54 #include <txtannotationfld.hxx>
55 #include <rootfrm.hxx>
56 #include <SwRewriter.hxx>
57 #include <tools/color.hxx>
58 #include <unotools/datetime.hxx>
60 #include <swmodule.hxx>
61 #include <strings.hrc>
62 #include <cmdid.h>
64 #include <sfx2/request.hxx>
65 #include <sfx2/event.hxx>
66 #include <svl/srchitem.hxx>
68 #include <svl/languageoptions.hxx>
69 #include <svl/hint.hxx>
71 #include <svx/svdview.hxx>
72 #include <editeng/eeitem.hxx>
73 #include <editeng/langitem.hxx>
74 #include <editeng/outliner.hxx>
75 #include <editeng/outlobj.hxx>
77 #include <comphelper/lok.hxx>
78 #include <comphelper/string.hxx>
79 #include <officecfg/Office/Writer.hxx>
80 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
82 #include <annotsh.hxx>
83 #include <swabstdlg.hxx>
84 #include <pagefrm.hxx>
85 #include <officecfg/Office/Common.hxx>
87 #include <memory>
89 // distance between Anchor Y and initial note position
90 #define POSTIT_INITIAL_ANCHOR_DISTANCE 20
91 //distance between two postits
92 #define POSTIT_SPACE_BETWEEN 8
93 #define POSTIT_MINIMUMSIZE_WITH_META 60
94 #define POSTIT_SCROLL_SIDEBAR_HEIGHT 20
96 // if we layout more often we stop, this should never happen
97 #define MAX_LOOP_COUNT 50
99 using namespace sw::sidebarwindows;
100 using namespace sw::annotation;
102 namespace {
104 enum class CommentNotificationType { Add, Remove, Modify, Resolve, RedlinedDeletion };
106 bool comp_pos(const std::unique_ptr<SwAnnotationItem>& a, const std::unique_ptr<SwAnnotationItem>& b)
108 // sort by anchor position
109 SwPosition aPosAnchorA = a->GetAnchorPosition();
110 SwPosition aPosAnchorB = b->GetAnchorPosition();
112 bool aAnchorAInFooter = false;
113 bool aAnchorBInFooter = false;
115 // is the anchor placed in Footnote or the Footer?
116 if( aPosAnchorA.GetNode().FindFootnoteStartNode() || aPosAnchorA.GetNode().FindFooterStartNode() )
117 aAnchorAInFooter = true;
118 if( aPosAnchorB.GetNode().FindFootnoteStartNode() || aPosAnchorB.GetNode().FindFooterStartNode() )
119 aAnchorBInFooter = true;
121 // fdo#34800
122 // if AnchorA is in footnote, and AnchorB isn't
123 // we do not want to change over the position
124 if( aAnchorAInFooter && !aAnchorBInFooter )
125 return false;
126 // if aAnchorA is not placed in a footnote, and aAnchorB is
127 // force a change over
128 else if( !aAnchorAInFooter && aAnchorBInFooter )
129 return true;
130 // If neither or both are in the footer, compare the positions.
131 // Since footnotes are in Inserts section of nodes array and footers
132 // in Autotext section, all footnotes precede any footers so no need
133 // to check that.
134 else
135 return aPosAnchorA < aPosAnchorB;
138 /// Emits LOK notification about one addition/removal/change of a comment
139 void lcl_CommentNotification(const SwView* pView, const CommentNotificationType nType, const SwAnnotationItem* pItem, const sal_uInt32 nPostItId)
141 if (!comphelper::LibreOfficeKit::isActive())
142 return;
144 boost::property_tree::ptree aAnnotation;
145 aAnnotation.put("action", (nType == CommentNotificationType::Add ? "Add" :
146 (nType == CommentNotificationType::Remove ? "Remove" :
147 (nType == CommentNotificationType::Modify ? "Modify" :
148 (nType == CommentNotificationType::RedlinedDeletion ? "RedlinedDeletion" :
149 (nType == CommentNotificationType::Resolve ? "Resolve" : "???"))))));
151 aAnnotation.put("id", nPostItId);
152 if (nType != CommentNotificationType::Remove && pItem != nullptr)
154 sw::annotation::SwAnnotationWin* pWin = pItem->mpPostIt.get();
156 const SwPostItField* pField = pWin->GetPostItField();
157 const SwRect& aRect = pWin->GetAnchorRect();
158 tools::Rectangle aSVRect(aRect.Pos().getX(),
159 aRect.Pos().getY(),
160 aRect.Pos().getX() + aRect.SSize().Width(),
161 aRect.Pos().getY() + aRect.SSize().Height());
163 if (!pItem->maLayoutInfo.mPositionFromCommentAnchor)
165 // Comments on frames: anchor position is the corner position, not the whole frame.
166 aSVRect.SetSize(Size(0, 0));
169 std::vector<OString> aRects;
170 for (const basegfx::B2DRange& aRange : pWin->GetAnnotationTextRanges())
172 const SwRect rect(aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), aRange.getHeight());
173 aRects.push_back(rect.SVRect().toString());
175 const OString sRects = comphelper::string::join("; ", aRects);
177 aAnnotation.put("id", pField->GetPostItId());
178 aAnnotation.put("parentId", pField->GetParentPostItId());
179 aAnnotation.put("author", pField->GetPar1().toUtf8().getStr());
180 // Note, for just plain text we could use "text" populated by pField->GetPar2()
181 aAnnotation.put("html", pWin->GetSimpleHtml());
182 aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false");
183 aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime()));
184 aAnnotation.put("anchorPos", aSVRect.toString());
185 aAnnotation.put("textRange", sRects.getStr());
186 aAnnotation.put("layoutStatus", pItem->mLayoutStatus);
188 if (nType == CommentNotificationType::Remove && comphelper::LibreOfficeKit::isActive())
190 // Redline author is basically the author which has made the modification rather than author of the comments
191 // This is important to know who removed the comment
192 aAnnotation.put("author", SwModule::get()->GetRedlineAuthor(SwModule::get()->GetRedlineAuthor()));
195 boost::property_tree::ptree aTree;
196 aTree.add_child("comment", aAnnotation);
197 std::stringstream aStream;
198 boost::property_tree::write_json(aStream, aTree);
199 std::string aPayload = aStream.str();
201 if (pView)
203 pView->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, OString(aPayload));
207 class FilterFunctor
209 public:
210 virtual bool operator()(const SwFormatField* pField) const = 0;
211 virtual ~FilterFunctor() {}
214 class IsPostitField : public FilterFunctor
216 public:
217 bool operator()(const SwFormatField* pField) const override
219 return pField->GetField()->GetTyp()->Which() == SwFieldIds::Postit;
223 class IsPostitFieldWithAuthorOf : public FilterFunctor
225 OUString m_sAuthor;
226 public:
227 explicit IsPostitFieldWithAuthorOf(OUString aAuthor)
228 : m_sAuthor(std::move(aAuthor))
231 bool operator()(const SwFormatField* pField) const override
233 if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
234 return false;
235 return static_cast<const SwPostItField*>(pField->GetField())->GetPar1() == m_sAuthor;
239 class IsPostitFieldWithPostitId : public FilterFunctor
241 sal_uInt32 m_nPostItId;
242 public:
243 explicit IsPostitFieldWithPostitId(sal_uInt32 nPostItId)
244 : m_nPostItId(nPostItId)
247 bool operator()(const SwFormatField* pField) const override
249 if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
250 return false;
251 return static_cast<const SwPostItField*>(pField->GetField())->GetPostItId() == m_nPostItId;
255 class IsFieldNotDeleted : public FilterFunctor
257 private:
258 IDocumentRedlineAccess const& m_rIDRA;
259 FilterFunctor const& m_rNext;
261 public:
262 IsFieldNotDeleted(IDocumentRedlineAccess const& rIDRA,
263 const FilterFunctor & rNext)
264 : m_rIDRA(rIDRA)
265 , m_rNext(rNext)
268 bool operator()(const SwFormatField* pField) const override
270 if (!m_rNext(pField))
271 return false;
272 if (!pField->GetTextField())
273 return false;
274 return !sw::IsFieldDeletedInModel(m_rIDRA, *pField->GetTextField());
278 //Manages the passed in vector by automatically removing entries if they are deleted
279 //and automatically adding entries if they appear in the document and match the
280 //functor.
282 //This will completely refill in the case of a "anonymous" NULL pField stating
283 //rather unhelpfully that "something changed" so you may process the same
284 //Fields more than once.
285 class FieldDocWatchingStack : public SfxListener
287 std::vector<std::unique_ptr<SwAnnotationItem>>& m_aSidebarItems;
288 std::vector<const SwFormatField*> m_aFormatFields;
289 SwDocShell& m_rDocShell;
290 FilterFunctor& m_rFilter;
292 virtual void Notify(SfxBroadcaster&, const SfxHint& rHint) override
294 if ( rHint.GetId() != SfxHintId::SwFormatField )
295 return;
296 const SwFormatFieldHint* pHint = static_cast<const SwFormatFieldHint*>(&rHint);
298 bool bAllInvalidated = false;
299 if (pHint->Which() == SwFormatFieldHintWhich::REMOVED)
301 const SwFormatField* pField = pHint->GetField();
302 bAllInvalidated = pField == nullptr;
303 if (!bAllInvalidated && m_rFilter(pField))
305 EndListening(const_cast<SwFormatField&>(*pField));
306 std::erase(m_aFormatFields, pField);
309 else if (pHint->Which() == SwFormatFieldHintWhich::INSERTED)
311 const SwFormatField* pField = pHint->GetField();
312 bAllInvalidated = pField == nullptr;
313 if (!bAllInvalidated && m_rFilter(pField))
315 StartListening(const_cast<SwFormatField&>(*pField));
316 m_aFormatFields.push_back(pField);
320 if (bAllInvalidated)
321 FillVector();
323 return;
326 public:
327 FieldDocWatchingStack(std::vector<std::unique_ptr<SwAnnotationItem>>& in, SwDocShell &rDocShell, FilterFunctor& rFilter)
328 : m_aSidebarItems(in)
329 , m_rDocShell(rDocShell)
330 , m_rFilter(rFilter)
332 FillVector();
333 StartListening(m_rDocShell);
335 void FillVector()
337 EndListeningToAllFields();
338 m_aFormatFields.clear();
339 m_aFormatFields.reserve(m_aSidebarItems.size());
340 for (auto const& p : m_aSidebarItems)
342 const SwFormatField& rField = p->GetFormatField();
343 if (!m_rFilter(&rField))
344 continue;
345 StartListening(const_cast<SwFormatField&>(rField));
346 m_aFormatFields.push_back(&rField);
349 void EndListeningToAllFields()
351 for (auto const& pField : m_aFormatFields)
353 EndListening(const_cast<SwFormatField&>(*pField));
356 virtual ~FieldDocWatchingStack() override
358 EndListeningToAllFields();
359 EndListening(m_rDocShell);
361 const SwFormatField* pop()
363 if (m_aFormatFields.empty())
364 return nullptr;
365 const SwFormatField* p = m_aFormatFields.back();
366 EndListening(const_cast<SwFormatField&>(*p));
367 m_aFormatFields.pop_back();
368 return p;
372 } // anonymous namespace
374 SwPostItMgr::SwPostItMgr(SwView* pView)
375 : mpView(pView)
376 , mpWrtShell(mpView->GetDocShell()->GetWrtShell())
377 , mpEditWin(&mpView->GetEditWin())
378 , mnEventId(nullptr)
379 , mbWaitingForCalcRects(false)
380 , mpActivePostIt(nullptr)
381 , mbLayout(false)
382 , mbLayoutHeight(0)
383 , mbLayouting(false)
384 , mbReadOnly(mpView->GetDocShell()->IsReadOnly())
385 , mbDeleteNote(true)
387 if(!mpView->GetDrawView() )
388 mpView->GetWrtShell().MakeDrawView();
390 //make sure we get the colour yellow always, even if not the first one of comments or redlining
391 SwModule::get()->GetRedlineAuthor();
393 // collect all PostIts and redline comments that exist after loading the document
394 // don't check for existence for any of them, don't focus them
395 AddPostIts(false,false);
396 /* this code can be used once we want redline comments in the Sidebar
397 AddRedlineComments(false,false);
399 // we want to receive stuff like SfxHintId::DocChanged
400 StartListening(*mpView->GetDocShell());
401 // listen to stylesheet pool to update on stylesheet rename,
402 // as EditTextObject references styles by name.
403 SfxStyleSheetBasePool* pStyleSheetPool = mpView->GetDocShell()->GetStyleSheetPool();
404 if (pStyleSheetPool)
405 StartListening(*static_cast<SwDocStyleSheetPool*>(pStyleSheetPool)->GetEEStyleSheetPool());
406 if (!mvPostItFields.empty())
408 mbWaitingForCalcRects = true;
409 mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
413 SwPostItMgr::~SwPostItMgr()
415 if ( mnEventId )
416 Application::RemoveUserEvent( mnEventId );
417 // forget about all our Sidebar windows
418 RemoveSidebarWin();
419 EndListeningAll();
421 mPages.clear();
424 bool SwPostItMgr::CheckForRemovedPostIts()
426 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
427 bool bRemoved = false;
428 auto it = mvPostItFields.begin();
429 while(it != mvPostItFields.end())
431 if (!(*it)->UseElement(*mpWrtShell->GetLayout(), rIDRA))
433 EndListening(const_cast<SfxBroadcaster&>(*(*it)->GetBroadcaster()));
435 if((*it)->mpPostIt && (*it)->mpPostIt->GetPostItField())
436 lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, (*it)->mpPostIt->GetPostItField()->GetPostItId());
438 std::unique_ptr<SwAnnotationItem> p = std::move(*it);
439 it = mvPostItFields.erase(it);
440 if (GetActiveSidebarWin() == p->mpPostIt)
441 SetActiveSidebarWin(nullptr);
442 p->mpPostIt.disposeAndClear();
444 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
446 const SwPostItField* pPostItField = static_cast<const SwPostItField*>(p->GetFormatField().GetField());
447 lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, pPostItField->GetPostItId());
450 bRemoved = true;
452 else
453 ++it;
456 if ( !bRemoved )
457 return false;
459 // make sure that no deleted items remain in page lists
460 // todo: only remove deleted ones?!
461 if ( mvPostItFields.empty() )
463 PreparePageContainer();
464 PrepareView();
466 else
468 // if postits are there make sure that page lists are not empty
469 // otherwise sudden paints can cause pain (in BorderOverPageBorder)
470 CalcRects();
473 return true;
476 SwAnnotationItem* SwPostItMgr::InsertItem(SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus)
478 if (bCheckExistence)
480 for (auto const& postItField : mvPostItFields)
482 if ( postItField->GetBroadcaster() == pItem )
483 return nullptr;
486 mbLayout = bFocus;
488 SwAnnotationItem* pAnnotationItem = nullptr;
489 if (auto pSwFormatField = dynamic_cast< SwFormatField *>( pItem ))
491 IsPostitField isPostitField;
492 if (!isPostitField(pSwFormatField))
493 return nullptr;
494 mvPostItFields.push_back(std::make_unique<SwAnnotationItem>(*pSwFormatField, bFocus));
495 pAnnotationItem = mvPostItFields.back().get();
497 assert(dynamic_cast< const SwFormatField *>( pItem ) && "Mgr::InsertItem: seems like new stuff was added");
498 StartListening(*pItem);
499 return pAnnotationItem;
502 sw::annotation::SwAnnotationWin* SwPostItMgr::GetRemovedAnnotationWin( const SfxBroadcaster* pBroadcast )
504 auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
505 [&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; });
506 if (i != mvPostItFields.end())
508 return (*i)->mpPostIt;
510 return nullptr;
513 void SwPostItMgr::RemoveItem( SfxBroadcaster* pBroadcast )
515 EndListening(*pBroadcast);
516 auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
517 [&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; });
518 if (i != mvPostItFields.end())
520 std::unique_ptr<SwAnnotationItem> p = std::move(*i);
521 // tdf#120487 remove from list before dispose, so comment window
522 // won't be recreated due to the entry still in the list if focus
523 // transferring from the pPostIt triggers relayout of postits
524 // tdf#133348 remove from list before calling SetActiveSidebarWin
525 // so GetNextPostIt won't deal with mvPostItFields containing empty unique_ptr
526 mvPostItFields.erase(i);
527 if (GetActiveSidebarWin() == p->mpPostIt)
528 SetActiveSidebarWin(nullptr);
529 p->mpPostIt.disposeAndClear();
531 mbLayout = true;
532 PrepareView();
535 void SwPostItMgr::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
537 if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
539 const SfxEventHint& rSfxEventHint = static_cast<const SfxEventHint&>(rHint);
540 if (rSfxEventHint.GetEventId() == SfxEventHintId::SwEventLayoutFinished)
542 if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
544 mbWaitingForCalcRects = true;
545 mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
549 else if ( rHint.GetId() == SfxHintId::SwFormatField )
551 const SwFormatFieldHint * pFormatHint = static_cast<const SwFormatFieldHint*>(&rHint);
552 SwFormatField* pField = const_cast <SwFormatField*>( pFormatHint->GetField() );
553 switch ( pFormatHint->Which() )
555 case SwFormatFieldHintWhich::INSERTED :
557 if (!pField)
559 AddPostIts();
560 break;
562 // get field to be inserted from hint
563 if ( pField->IsFieldInDoc() )
565 bool bEmpty = !HasNotes();
566 SwAnnotationItem* pItem = InsertItem( pField, true, false );
568 if (bEmpty && !mvPostItFields.empty())
569 PrepareView(true);
571 // True until the layout of this post it finishes
572 if (pItem)
573 pItem->mbPendingLayout = true;
575 else
577 OSL_FAIL("Inserted field not in document!" );
579 break;
581 case SwFormatFieldHintWhich::REMOVED:
582 case SwFormatFieldHintWhich::REDLINED_DELETION:
584 if (mbDeleteNote)
586 if (!pField)
588 const bool bWasRemoved = CheckForRemovedPostIts();
589 // tdf#143643 ensure relayout on undo of insert comment
590 if (bWasRemoved)
591 mbLayout = true;
592 break;
594 this->Broadcast(rHint);
595 RemoveItem(pField);
597 // If LOK has disabled tiled annotations, emit annotation callbacks
598 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
600 SwPostItField* pPostItField = static_cast<SwPostItField*>(pField->GetField());
601 auto type = pFormatHint->Which() == SwFormatFieldHintWhich::REMOVED ? CommentNotificationType::Remove: CommentNotificationType::RedlinedDeletion;
602 lcl_CommentNotification(mpView, type, nullptr, pPostItField->GetPostItId());
605 break;
607 case SwFormatFieldHintWhich::FOCUS:
609 if (pFormatHint->GetView()== mpView)
610 Focus(rBC);
611 break;
613 case SwFormatFieldHintWhich::CHANGED:
614 case SwFormatFieldHintWhich::RESOLVED:
616 SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC);
617 for (auto const& postItField : mvPostItFields)
619 if ( pFormatField == postItField->GetBroadcaster() )
621 if (postItField->mpPostIt)
623 postItField->mpPostIt->SetPostItText();
624 mbLayout = true;
625 this->Forward(rBC, rHint);
628 // If LOK has disabled tiled annotations, emit annotation callbacks
629 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
631 if(SwFormatFieldHintWhich::CHANGED == pFormatHint->Which())
632 lcl_CommentNotification(mpView, CommentNotificationType::Modify, postItField.get(), 0);
633 else
634 lcl_CommentNotification(mpView, CommentNotificationType::Resolve, postItField.get(), 0);
636 break;
639 break;
643 else if ( rHint.GetId() == SfxHintId::StyleSheetModifiedExtended )
645 const SfxStyleSheetModifiedHint * pStyleHint = static_cast<const SfxStyleSheetModifiedHint*>(&rHint);
646 for (const auto& postItField : mvPostItFields)
648 auto pField = static_cast<SwPostItField*>(postItField->GetFormatField().GetField());
649 pField->ChangeStyleSheetName(pStyleHint->GetOldName(), pStyleHint->GetStyleSheet());
652 else
654 SfxHintId nId = rHint.GetId();
655 switch ( nId )
657 case SfxHintId::ModeChanged:
659 if ( mbReadOnly != mpView->GetDocShell()->IsReadOnly() )
661 mbReadOnly = !mbReadOnly;
662 SetReadOnlyState();
663 mbLayout = true;
665 break;
667 case SfxHintId::DocChanged:
669 if ( mpView->GetDocShell() == &rBC )
671 if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
673 mbWaitingForCalcRects = true;
674 mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
677 break;
679 case SfxHintId::LanguageChanged:
681 SetSpellChecking();
682 break;
684 case SfxHintId::SwSplitNodeOperation:
686 // if we are in a SplitNode/Cut operation, do not delete note and then add again, as this will flicker
687 mbDeleteNote = !mbDeleteNote;
688 break;
690 case SfxHintId::Dying:
692 if ( mpView->GetDocShell() != &rBC )
694 // field to be removed is the broadcaster
695 OSL_FAIL("Notification for removed SwFormatField was not sent!");
696 RemoveItem(&rBC);
698 break;
700 default: break;
705 void SwPostItMgr::Focus(const SfxBroadcaster& rBC)
707 if (!mpWrtShell->GetViewOptions()->IsPostIts())
709 SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_NOTES);
710 mpView->ExecViewOptions(aRequest);
713 for (auto const& postItField : mvPostItFields)
715 // field to get the focus is the broadcaster
716 if ( &rBC == postItField->GetBroadcaster() )
718 if (postItField->mpPostIt)
720 if (postItField->mpPostIt->IsResolved() &&
721 !mpWrtShell->GetViewOptions()->IsResolvedPostIts())
723 SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_RESOLVED_NOTES);
724 mpView->ExecViewOptions(aRequest);
726 postItField->mpPostIt->GrabFocus();
727 MakeVisible(postItField->mpPostIt);
729 else
731 // when the layout algorithm starts, this postit is created and receives focus
732 postItField->mbFocus = true;
738 bool SwPostItMgr::CalcRects()
740 if ( mnEventId )
742 // if CalcRects() was forced and an event is still pending: remove it
743 // it is superfluous and also may cause reentrance problems if triggered while layouting
744 Application::RemoveUserEvent( mnEventId );
745 mnEventId = nullptr;
748 bool bChange = false;
749 bool bRepair = false;
750 PreparePageContainer();
751 if ( !mvPostItFields.empty() )
753 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
754 for (auto const& pItem : mvPostItFields)
756 if (!pItem->UseElement(*mpWrtShell->GetLayout(), rIDRA))
758 OSL_FAIL("PostIt is not in doc or other wrong use");
759 bRepair = true;
760 continue;
762 const SwRect aOldAnchorRect( pItem->maLayoutInfo.mPosition );
763 const SwPostItHelper::SwLayoutStatus eOldLayoutStatus = pItem->mLayoutStatus;
764 const SwNodeOffset nOldStartNodeIdx( pItem->maLayoutInfo.mnStartNodeIdx );
765 const sal_Int32 nOldStartContent( pItem->maLayoutInfo.mnStartContent );
767 // update layout information
768 const SwTextAnnotationField* pTextAnnotationField =
769 dynamic_cast< const SwTextAnnotationField* >( pItem->GetFormatField().GetTextField() );
770 const ::sw::mark::MarkBase* pAnnotationMark =
771 pTextAnnotationField != nullptr ? pTextAnnotationField->GetAnnotationMark() : nullptr;
772 if ( pAnnotationMark != nullptr )
774 pItem->mLayoutStatus =
775 SwPostItHelper::getLayoutInfos(
776 pItem->maLayoutInfo,
777 pItem->GetAnchorPosition(),
778 pAnnotationMark );
780 else
782 pItem->mLayoutStatus =
783 SwPostItHelper::getLayoutInfos( pItem->maLayoutInfo, pItem->GetAnchorPosition() );
786 bChange = bChange
787 || pItem->maLayoutInfo.mPosition != aOldAnchorRect
788 || pItem->mLayoutStatus != eOldLayoutStatus
789 || pItem->maLayoutInfo.mnStartNodeIdx != nOldStartNodeIdx
790 || pItem->maLayoutInfo.mnStartContent != nOldStartContent;
793 // show notes in right order in navigator
794 //prevent Anchors during layout to overlap, e.g. when moving a frame
795 if (mvPostItFields.size()>1 )
796 std::stable_sort(mvPostItFields.begin(), mvPostItFields.end(), comp_pos);
798 // sort the items into the right page vector, so layout can be done by page
799 for (auto const& pItem : mvPostItFields)
801 if( SwPostItHelper::INVISIBLE == pItem->mLayoutStatus )
803 if (pItem->mpPostIt)
804 pItem->mpPostIt->HideNote();
805 continue;
808 if( SwPostItHelper::HIDDEN == pItem->mLayoutStatus )
810 if (!mpWrtShell->GetViewOptions()->IsShowHiddenChar())
812 if (pItem->mpPostIt)
813 pItem->mpPostIt->HideNote();
814 continue;
818 const tools::ULong aPageNum = pItem->maLayoutInfo.mnPageNumber;
819 if (aPageNum > mPages.size())
821 const tools::ULong nNumberOfPages = mPages.size();
822 mPages.reserve(aPageNum);
823 for (tools::ULong j=0; j<aPageNum - nNumberOfPages; ++j)
824 mPages.emplace_back( new SwPostItPageItem());
826 mPages[aPageNum-1]->mvSidebarItems.push_back(pItem.get());
827 mPages[aPageNum-1]->mPageRect = pItem->maLayoutInfo.mPageFrame;
828 mPages[aPageNum-1]->eSidebarPosition = pItem->maLayoutInfo.meSidebarPosition;
831 if (!bChange && mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))
833 tools::Long nLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
834 if( nLayoutHeight > mbLayoutHeight )
836 if (mPages[0]->bScrollbar || HasScrollbars())
837 bChange = true;
839 else if( nLayoutHeight < mbLayoutHeight )
841 if (mPages[0]->bScrollbar || !BorderOverPageBorder(1))
842 bChange = true;
847 if ( bRepair )
848 CheckForRemovedPostIts();
850 mbLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
851 mbWaitingForCalcRects = false;
852 return bChange;
855 bool SwPostItMgr::HasScrollbars() const
857 for (auto const& postItField : mvPostItFields)
859 if (postItField->mbShow && postItField->mpPostIt && postItField->mpPostIt->HasScrollbar())
860 return true;
862 return false;
865 void SwPostItMgr::PreparePageContainer()
867 // we do not just delete the SwPostItPageItem, so offset/scrollbar is not lost
868 tools::Long lPageSize = mpWrtShell->GetNumPages();
869 tools::Long lContainerSize = mPages.size();
871 if (lContainerSize < lPageSize)
873 mPages.reserve(lPageSize);
874 for (tools::Long i=0; i<lPageSize - lContainerSize;i++)
875 mPages.emplace_back( new SwPostItPageItem());
877 else if (lContainerSize > lPageSize)
879 for (int i=mPages.size()-1; i >= lPageSize;--i)
881 mPages.pop_back();
884 // only clear the list, DO NOT delete the objects itself
885 for (auto const& page : mPages)
887 page->mvSidebarItems.clear();
888 if (mvPostItFields.empty())
889 page->bScrollbar = false;
893 VclPtr<SwAnnotationWin> SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationItem& rItem)
895 VclPtr<SwAnnotationWin> pPostIt = rItem.mpPostIt;
896 if (!pPostIt)
898 pPostIt = rItem.GetSidebarWindow( mpView->GetEditWin(),
899 *this );
900 pPostIt->InitControls();
901 pPostIt->SetReadonly(mbReadOnly);
902 rItem.mpPostIt = pPostIt;
903 if (mpAnswer)
905 if (pPostIt->GetPostItField()->GetParentPostItId() != 0) //do we really have another note in front of this one
907 pPostIt->InitAnswer(*mpAnswer);
909 mpAnswer.reset();
912 return rItem.mpPostIt;
915 void SwPostItMgr::LayoutPostIts()
917 const bool bLoKitActive = comphelper::LibreOfficeKit::isActive();
918 const bool bTiledAnnotations = comphelper::LibreOfficeKit::isTiledAnnotations();
919 const bool bShowNotes = ShowNotes();
921 const bool bEnableMapMode = bLoKitActive && !mpEditWin->IsMapModeEnabled();
922 if (bEnableMapMode)
923 mpEditWin->EnableMapMode();
925 if ( !mvPostItFields.empty() && !mbWaitingForCalcRects )
927 mbLayouting = true;
929 //loop over all pages and do the layout
930 // - create SwPostIt if necessary
931 // - place SwPostIts on their initial position
932 // - calculate necessary height for all PostIts together
933 bool bUpdate = false;
934 for (std::unique_ptr<SwPostItPageItem>& pPage : mPages)
936 // only layout if there are notes on this page
937 if (!pPage->mvSidebarItems.empty())
939 std::vector<SwAnnotationWin*> aVisiblePostItList;
940 tools::ULong lNeededHeight = 0;
942 for (auto const& pItem : pPage->mvSidebarItems)
944 if (pItem->mbShow)
946 VclPtr<SwAnnotationWin> pPostIt = GetOrCreateAnnotationWindow(*pItem);
948 pPostIt->SetChangeTracking(
949 pItem->mLayoutStatus,
950 GetColorAnchor(pItem->maLayoutInfo.mRedlineAuthor));
951 pPostIt->SetSidebarPosition(pPage->eSidebarPosition);
953 if (pPostIt->GetPostItField()->GetParentPostItId() != 0)
954 pPostIt->SetFollow(true);
956 tools::Long aPostItHeight = 0;
957 if (bShowNotes)
959 tools::Long mlPageBorder = 0;
960 tools::Long mlPageEnd = 0;
962 if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
964 // x value for notes positioning
965 mlPageBorder = mpEditWin->LogicToPixel( Point( pPage->mPageRect.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true);
966 //bending point
967 mlPageEnd =
968 mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
969 ? pItem->maLayoutInfo.mPagePrtArea.Left()
970 : pPage->mPageRect.Left() + 350;
972 else if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
974 // x value for notes positioning
975 mlPageBorder = mpEditWin->LogicToPixel( Point(pPage->mPageRect.Right(), 0)).X() + GetSidebarBorderWidth(true);
976 //bending point
977 mlPageEnd =
978 mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
979 ? pItem->maLayoutInfo.mPagePrtArea.Right() :
980 pPage->mPageRect.Right() - 350;
983 tools::Long Y = mpEditWin->LogicToPixel( Point(0,pItem->maLayoutInfo.mPosition.Bottom())).Y();
985 aPostItHeight = ( pPostIt->GetPostItTextHeight() < pPostIt->GetMinimumSizeWithoutMeta()
986 ? pPostIt->GetMinimumSizeWithoutMeta()
987 : pPostIt->GetPostItTextHeight() )
988 + pPostIt->GetMetaHeight();
989 pPostIt->SetPosSizePixelRect( mlPageBorder ,
990 Y - GetInitialAnchorDistance(),
991 GetSidebarWidth(true),
992 aPostItHeight,
993 mlPageEnd );
996 pPostIt->SetAnchorRect(pItem->maLayoutInfo.mPosition);
998 pPostIt->ChangeSidebarItem( *pItem );
1000 if (pItem->mbFocus)
1002 mbLayout = true;
1003 pPostIt->GrabFocus();
1004 pItem->mbFocus = false;
1006 // only the visible postits are used for the final layout
1007 aVisiblePostItList.push_back(pPostIt);
1008 if (bShowNotes)
1009 lNeededHeight += pPostIt->IsFollow() ? aPostItHeight : aPostItHeight+GetSpaceBetween();
1011 else // we don't want to see it
1013 VclPtr<SwAnnotationWin> pPostIt = pItem->mpPostIt;
1014 if (pPostIt)
1015 pPostIt->HideNote();
1017 SwFormatField* pFormatField = &(pItem->GetFormatField());
1018 SwFormatFieldHintWhich nWhich = SwFormatFieldHintWhich::INSERTED;
1019 this->Broadcast(SwFormatFieldHint(pFormatField, nWhich, mpView));
1022 if (!aVisiblePostItList.empty() && ShowNotes())
1024 bool bOldScrollbar = pPage->bScrollbar;
1025 pPage->bScrollbar = LayoutByPage(aVisiblePostItList, pPage->mPageRect.SVRect(), lNeededHeight);
1026 if (!pPage->bScrollbar)
1028 pPage->lOffset = 0;
1030 else if (sal_Int32 nScrollSize = GetScrollSize())
1032 //when we changed our zoom level, the offset value can be too big, so let's check for the largest possible zoom value
1033 tools::Long aAvailableHeight = mpEditWin->LogicToPixel(Size(0,pPage->mPageRect.Height())).Height() - 2 * GetSidebarScrollerHeight();
1034 tools::Long lOffset = -1 * nScrollSize * (aVisiblePostItList.size() - aAvailableHeight / nScrollSize);
1035 if (pPage->lOffset < lOffset)
1036 pPage->lOffset = lOffset;
1038 bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
1039 const tools::Long aSidebarheight = pPage->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
1041 TODO
1042 - enlarge all notes till GetNextBorder(), as we resized to average value before
1044 //let's hide the ones which overlap the page
1045 for (auto const& visiblePostIt : aVisiblePostItList)
1047 if (pPage->lOffset != 0)
1048 visiblePostIt->TranslateTopPosition(pPage->lOffset);
1050 bool bBottom = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y()+visiblePostIt->VirtualSize().Height())).Y() <= (pPage->mPageRect.Bottom()-aSidebarheight);
1051 bool bTop = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() >= (pPage->mPageRect.Top()+aSidebarheight);
1052 if ( bBottom && bTop )
1054 // When tiled rendering, make sure that only the
1055 // view that has the comment focus emits callbacks,
1056 // so the editing view jumps to the comment, but
1057 // not the others.
1058 bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting();
1059 if (!bTiledPainting)
1060 // No focus -> disable callbacks.
1061 comphelper::LibreOfficeKit::setTiledPainting(!visiblePostIt->HasChildPathFocus());
1062 visiblePostIt->ShowNote();
1063 if (!bTiledPainting)
1064 comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting);
1066 else
1068 if (mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() < (pPage->mPageRect.Top()+aSidebarheight))
1070 if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
1071 visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Left(),
1072 pPage->mPageRect.Top()));
1073 else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
1074 visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Right(),
1075 pPage->mPageRect.Top()));
1077 else
1079 if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
1080 visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Left(),
1081 pPage->mPageRect.Bottom()));
1082 else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
1083 visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Right(),
1084 pPage->mPageRect.Bottom()));
1086 OSL_ENSURE(pPage->bScrollbar,"SwPostItMgr::LayoutByPage(): note overlaps, but bScrollbar is not true");
1090 else
1092 for (auto const& visiblePostIt : aVisiblePostItList)
1094 visiblePostIt->SetPosAndSize();
1097 bool bOldScrollbar = pPage->bScrollbar;
1098 pPage->bScrollbar = false;
1099 bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
1102 for (auto const& visiblePostIt : aVisiblePostItList)
1104 if (bLoKitActive && !bTiledAnnotations)
1106 if (visiblePostIt->GetSidebarItem().mbPendingLayout && visiblePostIt->GetSidebarItem().mLayoutStatus != SwPostItHelper::DELETED)
1107 lcl_CommentNotification(mpView, CommentNotificationType::Add, &visiblePostIt->GetSidebarItem(), 0);
1108 else if (visiblePostIt->IsAnchorRectChanged())
1110 lcl_CommentNotification(mpView, CommentNotificationType::Modify, &visiblePostIt->GetSidebarItem(), 0);
1111 visiblePostIt->ResetAnchorRectChanged();
1115 // Layout for this post it finished now
1116 visiblePostIt->GetSidebarItem().mbPendingLayout = false;
1119 else
1121 if (pPage->bScrollbar)
1122 bUpdate = true;
1123 pPage->bScrollbar = false;
1127 if (!bShowNotes)
1128 { // we do not want to see the notes anymore -> Options-Writer-View-Notes
1129 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1130 bool bRepair = false;
1131 for (auto const& postItField : mvPostItFields)
1133 if (!postItField->UseElement(*mpWrtShell->GetLayout(), rIDRA))
1135 OSL_FAIL("PostIt is not in doc!");
1136 bRepair = true;
1137 continue;
1140 if (postItField->mpPostIt)
1142 postItField->mpPostIt->HideNote();
1143 if (postItField->mpPostIt->HasChildPathFocus())
1145 SetActiveSidebarWin(nullptr);
1146 postItField->mpPostIt->GrabFocusToDocument();
1151 if ( bRepair )
1152 CheckForRemovedPostIts();
1155 // notes scrollbar is otherwise not drawn correctly for some cases
1156 // scrollbar area is enough
1157 if (bUpdate)
1158 mpEditWin->Invalidate(); /*This is a super expensive relayout and render of the entire page*/
1160 mbLayouting = false;
1163 if (bEnableMapMode)
1164 mpEditWin->EnableMapMode(false);
1167 bool SwPostItMgr::BorderOverPageBorder(tools::ULong aPage) const
1169 if ( mPages[aPage-1]->mvSidebarItems.empty() )
1171 OSL_FAIL("Notes SidePane painted but no rects and page lists calculated!");
1172 return false;
1175 auto aItem = mPages[aPage-1]->mvSidebarItems.end();
1176 --aItem;
1177 OSL_ENSURE ((*aItem)->mpPostIt,"BorderOverPageBorder: NULL postIt, should never happen");
1178 if ((*aItem)->mpPostIt)
1180 const tools::Long aSidebarheight = mPages[aPage-1]->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
1181 const tools::Long aEndValue = mpEditWin->PixelToLogic(Point(0,(*aItem)->mpPostIt->GetPosPixel().Y()+(*aItem)->mpPostIt->GetSizePixel().Height())).Y();
1182 return aEndValue <= mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight;
1184 else
1185 return false;
1188 void SwPostItMgr::DrawNotesForPage(OutputDevice *pOutDev, sal_uInt32 nPage)
1190 assert(nPage < mPages.size());
1191 if (nPage >= mPages.size())
1192 return;
1193 for (auto const& pItem : mPages[nPage]->mvSidebarItems)
1195 SwAnnotationWin* pPostIt = pItem->mpPostIt;
1196 if (!pPostIt)
1197 continue;
1198 Point aPoint(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
1199 pPostIt->DrawForPage(pOutDev, aPoint);
1203 void SwPostItMgr::PaintTile(OutputDevice& rRenderContext)
1205 for (const std::unique_ptr<SwAnnotationItem>& pItem : mvPostItFields)
1207 SwAnnotationWin* pPostIt = pItem->mpPostIt;
1208 if (!pPostIt)
1209 continue;
1211 bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
1212 mpEditWin->EnableMapMode();
1213 rRenderContext.Push(vcl::PushFlags::MAPMODE);
1214 Point aOffset(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
1215 MapMode aMapMode(rRenderContext.GetMapMode());
1216 aMapMode.SetOrigin(aMapMode.GetOrigin() + aOffset);
1217 rRenderContext.SetMapMode(aMapMode);
1218 Size aSize(rRenderContext.PixelToLogic(pPostIt->GetSizePixel()));
1219 tools::Rectangle aRectangle(Point(0, 0), aSize);
1221 pPostIt->PaintTile(rRenderContext, aRectangle);
1223 rRenderContext.Pop();
1224 if (bEnableMapMode)
1225 mpEditWin->EnableMapMode(false);
1229 void SwPostItMgr::Scroll(const tools::Long lScroll,const tools::ULong aPage)
1231 OSL_ENSURE((lScroll % GetScrollSize() )==0,"SwPostItMgr::Scroll: scrolling by wrong value");
1232 // do not scroll more than necessary up or down
1233 if ( ((mPages[aPage-1]->lOffset == 0) && (lScroll>0)) || ( BorderOverPageBorder(aPage) && (lScroll<0)) )
1234 return;
1236 const bool bOldUp = ArrowEnabled(KEY_PAGEUP,aPage);
1237 const bool bOldDown = ArrowEnabled(KEY_PAGEDOWN,aPage);
1238 const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
1239 for (auto const& item : mPages[aPage-1]->mvSidebarItems)
1241 SwAnnotationWin* pPostIt = item->mpPostIt;
1242 // if this is an answer, we should take the normal position and not the real, slightly moved position
1243 pPostIt->SetVirtualPosSize(pPostIt->GetPosPixel(),pPostIt->GetSizePixel());
1244 pPostIt->TranslateTopPosition(lScroll);
1246 if (item->mbShow)
1248 bool bBottom = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y()+pPostIt->VirtualSize().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
1249 bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
1250 if ( bBottom && bTop)
1252 pPostIt->ShowNote();
1254 else
1256 if ( mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() < (mPages[aPage-1]->mPageRect.Top()+aSidebarheight))
1258 if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
1259 pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Top()));
1260 else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
1261 pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Top()));
1263 else
1265 if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
1266 pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Bottom()));
1267 else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
1268 pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Bottom()));
1273 mPages[aPage-1]->lOffset += lScroll;
1274 if ( (bOldUp != ArrowEnabled(KEY_PAGEUP,aPage)) ||(bOldDown != ArrowEnabled(KEY_PAGEDOWN,aPage)) )
1276 mpEditWin->Invalidate(GetBottomScrollRect(aPage));
1277 mpEditWin->Invalidate(GetTopScrollRect(aPage));
1281 void SwPostItMgr::AutoScroll(const SwAnnotationWin* pPostIt,const tools::ULong aPage )
1283 // otherwise all notes are visible
1284 if (!mPages[aPage-1]->bScrollbar)
1285 return;
1287 const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
1288 const bool bBottom = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
1289 const bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
1290 if ( !(bBottom && bTop))
1292 const tools::Long aDiff = bBottom ? mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Top() + aSidebarheight)).Y() - pPostIt->GetPosPixel().Y() :
1293 mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Bottom() - aSidebarheight)).Y() - (pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height());
1294 // this just adds the missing value to get the next a* GetScrollSize() after aDiff
1295 // e.g aDiff= 61 POSTIT_SCROLL=50 --> lScroll = 100
1296 const auto nScrollSize = GetScrollSize();
1297 assert(nScrollSize);
1298 const tools::Long lScroll = bBottom ? (aDiff + ( nScrollSize - (aDiff % nScrollSize))) : (aDiff - (nScrollSize + (aDiff % nScrollSize)));
1299 Scroll(lScroll, aPage);
1303 void SwPostItMgr::MakeVisible(const SwAnnotationWin* pPostIt )
1305 tools::Long aPage = -1;
1306 // we don't know the page yet, let's find it ourselves
1307 std::vector<SwPostItPageItem*>::size_type n=0;
1308 for (auto const& page : mPages)
1310 for (auto const& item : page->mvSidebarItems)
1312 if (item->mpPostIt==pPostIt)
1314 aPage = n+1;
1315 break;
1318 ++n;
1320 if (aPage!=-1)
1321 AutoScroll(pPostIt,aPage);
1322 tools::Rectangle aNoteRect (Point(pPostIt->GetPosPixel().X(),pPostIt->GetPosPixel().Y()-5),pPostIt->GetSizePixel());
1323 if (!aNoteRect.IsEmpty())
1324 mpWrtShell->MakeVisible(SwRect(mpEditWin->PixelToLogic(aNoteRect)));
1327 bool SwPostItMgr::ArrowEnabled(sal_uInt16 aDirection,tools::ULong aPage) const
1329 switch (aDirection)
1331 case KEY_PAGEUP:
1333 return (mPages[aPage-1]->lOffset != 0);
1335 case KEY_PAGEDOWN:
1337 return (!BorderOverPageBorder(aPage));
1339 default: return false;
1343 Color SwPostItMgr::GetArrowColor(sal_uInt16 aDirection,tools::ULong aPage) const
1345 if (ArrowEnabled(aDirection,aPage))
1347 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1348 return COL_WHITE;
1349 else
1350 return COL_NOTES_SIDEPANE_ARROW_ENABLED;
1352 else
1354 return COL_NOTES_SIDEPANE_ARROW_DISABLED;
1358 bool SwPostItMgr::LayoutByPage(std::vector<SwAnnotationWin*> &aVisiblePostItList, const tools::Rectangle& rBorder, tools::Long lNeededHeight)
1360 /*** General layout idea:***/
1361 // - if we have space left, we always move the current one up,
1362 // otherwise the next one down
1363 // - first all notes are resized
1364 // - then the real layout starts
1366 //rBorder is the page rect
1367 const tools::Rectangle aBorder = mpEditWin->LogicToPixel(rBorder);
1368 tools::Long lTopBorder = aBorder.Top() + 5;
1369 tools::Long lBottomBorder = aBorder.Bottom() - 5;
1370 const tools::Long lVisibleHeight = lBottomBorder - lTopBorder; //aBorder.GetHeight() ;
1371 const size_t nPostItListSize = aVisiblePostItList.size();
1372 tools::Long lTranslatePos = 0;
1373 bool bScrollbars = false;
1375 // do all necessary resizings
1376 if (nPostItListSize > 0 && lVisibleHeight < lNeededHeight)
1378 // ok, now we have to really resize and adding scrollbars
1379 const tools::Long lAverageHeight = (lVisibleHeight - nPostItListSize*GetSpaceBetween()) / nPostItListSize;
1380 if (lAverageHeight<GetMinimumSizeWithMeta())
1382 bScrollbars = true;
1383 lTopBorder += GetSidebarScrollerHeight() + 10;
1384 lBottomBorder -= (GetSidebarScrollerHeight() + 10);
1385 for (auto const& visiblePostIt : aVisiblePostItList)
1386 visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),visiblePostIt->GetMinimumSizeWithMeta()));
1388 else
1390 for (auto const& visiblePostIt : aVisiblePostItList)
1392 if ( visiblePostIt->VirtualSize().getHeight() > lAverageHeight)
1393 visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),lAverageHeight));
1398 //start the real layout so nothing overlaps anymore
1399 if (aVisiblePostItList.size()>1)
1401 int loop = 0;
1402 bool bDone = false;
1403 // if no window is moved anymore we are finished
1404 while (!bDone)
1406 loop++;
1407 bDone = true;
1408 tools::Long lSpaceUsed = lTopBorder + GetSpaceBetween();
1409 for(auto i = aVisiblePostItList.begin(); i != aVisiblePostItList.end() ; ++i)
1411 auto aNextPostIt = i;
1412 ++aNextPostIt;
1414 if (aNextPostIt != aVisiblePostItList.end())
1416 lTranslatePos = ( (*i)->VirtualPos().Y() + (*i)->VirtualSize().Height()) - (*aNextPostIt)->VirtualPos().Y();
1417 if (lTranslatePos > 0) // note windows overlaps the next one
1419 // we are not done yet, loop at least once more
1420 bDone = false;
1421 // if there is space left, move the current note up
1422 // it could also happen that there is no space left for the first note due to a scrollbar
1423 // then we also jump into, so we move the current one up and the next one down
1424 if ( (lSpaceUsed <= (*i)->VirtualPos().Y()) || (i==aVisiblePostItList.begin()))
1426 // we have space left, so let's move the current one up
1427 if ( ((*i)->VirtualPos().Y()- lTranslatePos - GetSpaceBetween()) > lTopBorder)
1429 if ((*aNextPostIt)->IsFollow())
1430 (*i)->TranslateTopPosition(-1*(lTranslatePos+ANCHORLINE_WIDTH));
1431 else
1432 (*i)->TranslateTopPosition(-1*(lTranslatePos+GetSpaceBetween()));
1434 else
1436 tools::Long lMoveUp = (*i)->VirtualPos().Y() - lTopBorder;
1437 (*i)->TranslateTopPosition(-1* lMoveUp);
1438 if ((*aNextPostIt)->IsFollow())
1439 (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+ANCHORLINE_WIDTH) - lMoveUp);
1440 else
1441 (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+GetSpaceBetween()) - lMoveUp);
1444 else
1446 // no space left, left move the next one down
1447 if ((*aNextPostIt)->IsFollow())
1448 (*aNextPostIt)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH);
1449 else
1450 (*aNextPostIt)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
1453 else
1455 // the first one could overlap the topborder instead of a second note
1456 if (i==aVisiblePostItList.begin())
1458 tools::Long lMoveDown = lTopBorder - (*i)->VirtualPos().Y();
1459 if (lMoveDown>0)
1461 bDone = false;
1462 (*i)->TranslateTopPosition( lMoveDown);
1466 if ( (*aNextPostIt)->IsFollow() )
1467 lSpaceUsed += (*i)->VirtualSize().Height() + ANCHORLINE_WIDTH;
1468 else
1469 lSpaceUsed += (*i)->VirtualSize().Height() + GetSpaceBetween();
1471 else
1473 //(*i) is the last visible item
1474 auto aPrevPostIt = i;
1475 --aPrevPostIt;
1476 lTranslatePos = ( (*aPrevPostIt)->VirtualPos().Y() + (*aPrevPostIt)->VirtualSize().Height() ) - (*i)->VirtualPos().Y();
1477 if (lTranslatePos > 0)
1479 bDone = false;
1480 if ( ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()+lTranslatePos) < lBottomBorder)
1482 if ( (*i)->IsFollow() )
1483 (*i)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH);
1484 else
1485 (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
1487 else
1489 (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()) );
1492 else
1494 // note does not overlap, but we might be over the lower border
1495 // only do this if there are no scrollbars, otherwise notes are supposed to overlap the border
1496 if (!bScrollbars && ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height() > lBottomBorder) )
1498 bDone = false;
1499 (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()));
1504 // security check so we don't loop forever
1505 if (loop>MAX_LOOP_COUNT)
1507 OSL_FAIL("PostItMgr::Layout(): We are looping forever");
1508 break;
1512 else
1514 // only one left, make sure it is not hidden at the top or bottom
1515 auto i = aVisiblePostItList.begin();
1516 lTranslatePos = lTopBorder - (*i)->VirtualPos().Y();
1517 if (lTranslatePos>0)
1519 (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
1521 lTranslatePos = lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height());
1522 if (lTranslatePos<0)
1524 (*i)->TranslateTopPosition(lTranslatePos);
1527 return bScrollbars;
1530 std::vector<SwFormatField*> SwPostItMgr::UpdatePostItsParentInfo()
1532 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1533 SwFieldType* pType = mpView->GetDocShell()->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(),false);
1534 std::vector<SwFormatField*> vFormatFields;
1535 pType->CollectPostIts(vFormatFields, rIDRA, mpWrtShell->GetLayout()->IsHideRedlines());
1537 for (std::vector<SwFormatField*>::iterator i = vFormatFields.begin(); i != vFormatFields.end(); i++)
1539 SwPostItField *pChildPostIt = static_cast<SwPostItField*>((*i)->GetField());
1541 if (pChildPostIt->GetParentId() != 0 || !pChildPostIt->GetParentName().isEmpty())
1543 for (std::vector<SwFormatField*>::iterator j = vFormatFields.begin(); j != vFormatFields.end(); j++)
1545 SwPostItField *pParentPostIt = static_cast<SwPostItField*>((*j)->GetField());
1546 if (pChildPostIt->GetParentId() != 0 && pParentPostIt->GetParaId() == pChildPostIt->GetParentId())
1548 pChildPostIt->SetParentPostItId(pParentPostIt->GetPostItId());
1549 pChildPostIt->SetParentName(pParentPostIt->GetName());
1551 else if (!pParentPostIt->GetName().isEmpty() && pParentPostIt->GetName() == pChildPostIt->GetParentName())
1553 pChildPostIt->SetParentPostItId(pParentPostIt->GetPostItId());
1554 pChildPostIt->SetParentName(pParentPostIt->GetName());
1559 return vFormatFields;
1563 void SwPostItMgr::AddPostIts(const bool bCheckExistence, const bool bFocus)
1565 const bool bEmpty = mvPostItFields.empty();
1566 std::vector<SwFormatField*> vFormatFields = UpdatePostItsParentInfo();
1568 for(auto pFormatField : vFormatFields)
1569 InsertItem(pFormatField, bCheckExistence, bFocus);
1570 // if we just added the first one we have to update the view for centering
1571 if (bEmpty && !mvPostItFields.empty())
1572 PrepareView(true);
1575 void SwPostItMgr::RemoveSidebarWin()
1577 for (auto& postItField : mvPostItFields)
1579 EndListening( *const_cast<SfxBroadcaster*>(postItField->GetBroadcaster()) );
1580 postItField->mpPostIt.disposeAndClear();
1581 postItField.reset();
1583 mvPostItFields.clear();
1585 // all postits removed, no items should be left in pages
1586 PreparePageContainer();
1589 static bool ConfirmDeleteAll(SwView& pView, const OUString& sText)
1591 const bool bAsk = officecfg::Office::Common::Misc::QueryDeleteAllComments::get();
1592 bool bConfirm = true;
1593 if (bAsk)
1595 VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
1596 auto pDlg
1597 = pFact->CreateQueryDialog(pView.GetFrameWeld(),
1598 SwResId(STR_QUERY_DELALLCOMMENTS_TITLE), sText, "", true);
1599 sal_Int32 nResult = pDlg->Execute();
1600 if (pDlg->ShowAgain() == false)
1602 std::shared_ptr<comphelper::ConfigurationChanges> xChanges(
1603 comphelper::ConfigurationChanges::create());
1604 officecfg::Office::Common::Misc::QueryDeleteAllComments::set(false, xChanges);
1605 xChanges->commit();
1607 bConfirm = (nResult == RET_YES);
1608 pDlg->disposeOnce();
1610 return bConfirm;
1613 // copy to new vector, otherwise RemoveItem would operate and delete stuff on mvPostItFields as well
1614 // RemoveItem will clean up the core field and visible postit if necessary
1615 // we cannot just delete everything as before, as postits could move into change tracking
1616 void SwPostItMgr::Delete(const OUString& rAuthor)
1618 OUString sQuestion = SwResId(STR_QUERY_DELALLCOMMENTSAUTHOR_QUESTION);
1619 sQuestion = sQuestion.replaceAll("%AUTHOR", rAuthor);
1620 if (!ConfirmDeleteAll(mpWrtShell->GetView(), sQuestion))
1621 return;
1623 mpWrtShell->StartAllAction();
1624 if (HasActiveSidebarWin() && (GetActiveSidebarWin()->GetAuthor() == rAuthor))
1626 SetActiveSidebarWin(nullptr);
1628 SwRewriter aRewriter;
1629 aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_AUTHOR_NOTES) + rAuthor);
1630 mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
1632 IsPostitFieldWithAuthorOf aFilter(rAuthor);
1633 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1634 IsFieldNotDeleted aFilter2(rIDRA, aFilter);
1635 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2);
1636 while (const SwFormatField* pField = aStack.pop())
1638 if (mpWrtShell->GotoField(*pField))
1639 mpWrtShell->DelRight();
1641 mpWrtShell->EndUndo();
1642 PrepareView();
1643 mpWrtShell->EndAllAction();
1644 mbLayout = true;
1645 CalcRects();
1646 LayoutPostIts();
1649 void SwPostItMgr::Delete(sal_uInt32 nPostItId)
1651 mpWrtShell->StartAllAction();
1652 if (HasActiveSidebarWin() &&
1653 mpActivePostIt->GetPostItField()->GetPostItId() == nPostItId)
1655 SetActiveSidebarWin(nullptr);
1657 SwRewriter aRewriter;
1658 aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1659 mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
1661 IsPostitFieldWithPostitId aFilter(nPostItId);
1662 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1663 IsFieldNotDeleted aFilter2(rIDRA, aFilter);
1664 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2);
1665 const SwFormatField* pField = aStack.pop();
1666 if (pField && mpWrtShell->GotoField(*pField))
1667 mpWrtShell->DelRight();
1668 mpWrtShell->EndUndo();
1669 PrepareView();
1670 mpWrtShell->EndAllAction();
1671 mbLayout = true;
1672 CalcRects();
1673 LayoutPostIts();
1676 void SwPostItMgr::DeleteCommentThread(sal_uInt32 nPostItId)
1678 mpWrtShell->StartAllAction();
1680 SwRewriter aRewriter;
1681 aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1683 // We have no undo ID at the moment.
1685 IsPostitFieldWithPostitId aFilter(nPostItId);
1686 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1687 const SwFormatField* pField = aStack.pop();
1688 // pField now contains our AnnotationWin object
1689 if (pField) {
1690 SwAnnotationWin* pWin = GetSidebarWin(pField);
1691 pWin->DeleteThread();
1693 PrepareView();
1694 mpWrtShell->EndAllAction();
1695 mbLayout = true;
1696 CalcRects();
1697 LayoutPostIts();
1700 void SwPostItMgr::ToggleResolved(sal_uInt32 nPostItId)
1702 mpWrtShell->StartAllAction();
1704 SwRewriter aRewriter;
1705 aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1707 // We have no undo ID at the moment.
1709 IsPostitFieldWithPostitId aFilter(nPostItId);
1710 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1711 const SwFormatField* pField = aStack.pop();
1712 // pField now contains our AnnotationWin object
1713 if (pField) {
1714 SwAnnotationWin* pWin = GetSidebarWin(pField);
1715 pWin->ToggleResolved();
1718 PrepareView();
1719 mpWrtShell->EndAllAction();
1720 mbLayout = true;
1721 CalcRects();
1722 LayoutPostIts();
1725 void SwPostItMgr::ToggleResolvedForThread(sal_uInt32 nPostItId)
1727 mpWrtShell->StartAllAction();
1729 SwRewriter aRewriter;
1730 aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1732 // We have no undo ID at the moment.
1734 IsPostitFieldWithPostitId aFilter(nPostItId);
1735 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1736 const SwFormatField* pField = aStack.pop();
1737 // pField now contains our AnnotationWin object
1738 if (pField) {
1739 SwAnnotationWin* pWin = GetSidebarWin(pField);
1740 pWin->ToggleResolvedForThread();
1743 PrepareView();
1744 mpWrtShell->EndAllAction();
1745 mbLayout = true;
1746 CalcRects();
1747 LayoutPostIts();
1751 void SwPostItMgr::Delete()
1753 if (!ConfirmDeleteAll(mpWrtShell->GetView(), SwResId(STR_QUERY_DELALLCOMMENTS_QUESTION)))
1754 return;
1756 mpWrtShell->StartAllAction();
1757 SetActiveSidebarWin(nullptr);
1758 SwRewriter aRewriter;
1759 aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_ALL_NOTES) );
1760 mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
1762 IsPostitField aFilter;
1763 IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1764 IsFieldNotDeleted aFilter2(rIDRA, aFilter);
1765 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(),
1766 aFilter2);
1767 while (const SwFormatField* pField = aStack.pop())
1769 if (mpWrtShell->GotoField(*pField))
1770 mpWrtShell->DelRight();
1773 mpWrtShell->EndUndo();
1774 PrepareView();
1775 mpWrtShell->EndAllAction();
1776 mbLayout = true;
1777 CalcRects();
1778 LayoutPostIts();
1781 void SwPostItMgr::PromoteToRoot(sal_uInt32 nPostItId)
1783 mpWrtShell->StartAllAction();
1785 SwRewriter aRewriter;
1786 aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1788 // We have no undo ID at the moment.
1790 IsPostitFieldWithPostitId aFilter(nPostItId);
1791 FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1792 const SwFormatField* pField = aStack.pop();
1793 // pField now contains our AnnotationWin object
1794 if (pField)
1796 SwAnnotationWin* pWin = GetSidebarWin(pField);
1797 pWin->SetAsRoot();
1799 PrepareView();
1800 mpWrtShell->EndAllAction();
1801 mbLayout = true;
1802 CalcRects();
1803 LayoutPostIts();
1806 void SwPostItMgr::MoveSubthreadToRoot(const sw::annotation::SwAnnotationWin* pNewRoot)
1808 std::vector<std::unique_ptr<SwAnnotationItem>>::iterator first, middle, last;
1809 first = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
1810 [&pNewRoot](const std::unique_ptr<SwAnnotationItem>& pField) {
1811 return pField->mpPostIt == pNewRoot;
1813 if (first == mvPostItFields.end())
1814 return;
1815 std::set<int> aPostItIds;
1816 aPostItIds.insert(pNewRoot->GetPostItField()->GetPostItId());
1817 middle = first + 1;
1818 while (middle != mvPostItFields.end()
1819 && aPostItIds.contains((*middle)->mpPostIt->GetPostItField()->GetParentPostItId()))
1821 aPostItIds.insert((*middle)->mpPostIt->GetPostItField()->GetPostItId());
1822 ++middle;
1824 if (middle == mvPostItFields.end())
1825 return;
1826 last = middle;
1827 while (last != mvPostItFields.end()
1828 && (*last)->mpPostIt->GetPostItField()->GetParentPostItId() != 0)
1829 ++last;
1830 if (last == middle)
1831 return;
1832 std::rotate(first, middle, last);
1833 CalcRects();
1834 LayoutPostIts();
1837 void SwPostItMgr::ExecuteFormatAllDialog(SwView& rView)
1839 if (mvPostItFields.empty())
1840 return;
1841 sw::annotation::SwAnnotationWin *pOrigActiveWin = GetActiveSidebarWin();
1842 sw::annotation::SwAnnotationWin *pWin = pOrigActiveWin;
1843 if (!pWin)
1845 for (auto const& postItField : mvPostItFields)
1847 pWin = postItField->mpPostIt;
1848 if (pWin)
1849 break;
1852 if (!pWin)
1853 return;
1854 SetActiveSidebarWin(pWin);
1855 OutlinerView* pOLV = pWin->GetOutlinerView();
1856 SfxItemSet aEditAttr(pOLV->GetAttribs());
1857 SfxItemPool* pPool(SwAnnotationShell::GetAnnotationPool(rView));
1858 auto xDlgAttr = std::make_shared<SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END>>(*pPool);
1859 xDlgAttr->Put(aEditAttr);
1860 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
1861 VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwCharDlg(rView.GetFrameWeld(), rView, *xDlgAttr, SwCharDlgMode::Ann));
1862 pDlg->StartExecuteAsync(
1863 [this, pDlg, xDlgAttr=std::move(xDlgAttr), pOrigActiveWin] (sal_Int32 nResult)->void
1865 if (nResult == RET_OK)
1867 auto aNewAttr = *xDlgAttr;
1868 aNewAttr.Put(*pDlg->GetOutputItemSet());
1869 FormatAll(aNewAttr);
1871 pDlg->disposeOnce();
1872 SetActiveSidebarWin(pOrigActiveWin);
1877 void SwPostItMgr::FormatAll(const SfxItemSet &rNewAttr)
1879 mpWrtShell->StartAllAction();
1880 SwRewriter aRewriter;
1881 aRewriter.AddRule(UndoArg1, SwResId(STR_FORMAT_ALL_NOTES) );
1882 mpWrtShell->StartUndo( SwUndoId::INSATTR, &aRewriter );
1884 for (auto const& postItField : mvPostItFields)
1886 if (!postItField->mpPostIt)
1887 continue;
1888 OutlinerView* pOLV = postItField->mpPostIt->GetOutlinerView();
1889 //save old selection
1890 ESelection aOrigSel(pOLV->GetSelection());
1891 //select all
1892 Outliner *pOutliner = pOLV->GetOutliner();
1893 if (pOutliner)
1895 sal_Int32 nParaCount = pOutliner->GetParagraphCount();
1896 if (nParaCount > 0)
1897 pOLV->SelectRange(0, nParaCount);
1899 //set new char properties
1900 pOLV->SetAttribs(rNewAttr);
1901 //restore old selection
1902 pOLV->SetSelection(aOrigSel);
1903 // tdf#91596 store updated formatting in SwField
1904 postItField->mpPostIt->UpdateData();
1907 mpWrtShell->EndUndo();
1908 PrepareView();
1909 mpWrtShell->EndAllAction();
1910 mbLayout = true;
1911 CalcRects();
1912 LayoutPostIts();
1915 void SwPostItMgr::Hide( std::u16string_view rAuthor )
1917 for (auto const& postItField : mvPostItFields)
1919 if ( postItField->mpPostIt && (postItField->mpPostIt->GetAuthor() == rAuthor) )
1921 postItField->mbShow = false;
1922 postItField->mpPostIt->HideNote();
1926 LayoutPostIts();
1929 void SwPostItMgr::Hide()
1931 for (auto const& postItField : mvPostItFields)
1933 postItField->mbShow = false;
1934 if (postItField->mpPostIt)
1935 postItField->mpPostIt->HideNote();
1939 SwAnnotationWin* SwPostItMgr::GetSidebarWin( const SfxBroadcaster* pBroadcaster) const
1941 for (auto const& postItField : mvPostItFields)
1943 if ( postItField->GetBroadcaster() == pBroadcaster)
1944 return postItField->mpPostIt;
1946 return nullptr;
1949 sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const SwPostItField* pField) const
1951 for (auto const& postItField : mvPostItFields)
1953 if ( postItField->GetFormatField().GetField() == pField )
1954 return postItField->mpPostIt.get();
1956 return nullptr;
1959 sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const sal_uInt32 nPostItId) const
1961 for (auto const& postItField : mvPostItFields)
1963 if ( static_cast<const SwPostItField*>(postItField->GetFormatField().GetField())->GetPostItId() == nPostItId )
1964 return postItField->mpPostIt.get();
1966 return nullptr;
1969 SwPostItField* SwPostItMgr::GetLatestPostItField()
1971 return static_cast<SwPostItField*>(mvPostItFields.back()->GetFormatField().GetField());
1974 sw::annotation::SwAnnotationWin* SwPostItMgr::GetOrCreateAnnotationWindowForLatestPostItField()
1976 return GetOrCreateAnnotationWindow(*mvPostItFields.back());
1979 SwAnnotationWin* SwPostItMgr::GetNextPostIt( sal_uInt16 aDirection,
1980 SwAnnotationWin* aPostIt )
1982 if (mvPostItFields.size()>1)
1984 auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
1985 [&aPostIt](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->mpPostIt == aPostIt; });
1986 if (i == mvPostItFields.end())
1987 return nullptr;
1989 auto iNextPostIt = i;
1990 if (aDirection == KEY_PAGEUP)
1992 if ( iNextPostIt == mvPostItFields.begin() )
1994 return nullptr;
1996 --iNextPostIt;
1998 else
2000 ++iNextPostIt;
2001 if ( iNextPostIt == mvPostItFields.end() )
2003 return nullptr;
2006 // let's quit, we are back at the beginning
2007 if ( (*iNextPostIt)->mpPostIt == aPostIt)
2008 return nullptr;
2009 return (*iNextPostIt)->mpPostIt;
2011 else
2012 return nullptr;
2015 tools::Long SwPostItMgr::GetNextBorder()
2017 for (auto const& pPage : mPages)
2019 for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
2021 if ((*b)->mpPostIt == mpActivePostIt)
2023 auto aNext = b;
2024 ++aNext;
2025 bool bFollow = (aNext != pPage->mvSidebarItems.end()) && (*aNext)->mpPostIt->IsFollow();
2026 if ( pPage->bScrollbar || bFollow )
2028 return -1;
2030 else
2032 //if this is the last item, return the bottom border otherwise the next item
2033 if (aNext == pPage->mvSidebarItems.end())
2034 return mpEditWin->LogicToPixel(Point(0,pPage->mPageRect.Bottom())).Y() - GetSpaceBetween();
2035 else
2036 return (*aNext)->mpPostIt->GetPosPixel().Y() - GetSpaceBetween();
2042 OSL_FAIL("SwPostItMgr::GetNextBorder(): We have to find a next border here");
2043 return -1;
2046 void SwPostItMgr::SetShadowState(const SwPostItField* pField,bool bCursor)
2048 if (pField)
2050 if (pField !=mShadowState.mpShadowField)
2052 if (mShadowState.mpShadowField)
2054 // reset old one if still alive
2055 // TODO: does not work properly if mouse and cursor was set
2056 sw::annotation::SwAnnotationWin* pOldPostIt =
2057 GetAnnotationWin(mShadowState.mpShadowField);
2058 if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT))
2059 pOldPostIt->SetViewState(ViewState::NORMAL);
2061 //set new one, if it is not currently edited
2062 sw::annotation::SwAnnotationWin* pNewPostIt = GetAnnotationWin(pField);
2063 if (pNewPostIt && pNewPostIt->Shadow() && (pNewPostIt->Shadow()->GetShadowState() != SS_EDIT))
2065 pNewPostIt->SetViewState(ViewState::VIEW);
2066 //remember our new field
2067 mShadowState.mpShadowField = pField;
2068 mShadowState.bCursor = false;
2069 mShadowState.bMouse = false;
2072 if (bCursor)
2073 mShadowState.bCursor = true;
2074 else
2075 mShadowState.bMouse = true;
2077 else
2079 if (mShadowState.mpShadowField)
2081 if (bCursor)
2082 mShadowState.bCursor = false;
2083 else
2084 mShadowState.bMouse = false;
2085 if (!mShadowState.bCursor && !mShadowState.bMouse)
2087 // reset old one if still alive
2088 sw::annotation::SwAnnotationWin* pOldPostIt = GetAnnotationWin(mShadowState.mpShadowField);
2089 if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT))
2091 pOldPostIt->SetViewState(ViewState::NORMAL);
2092 mShadowState.mpShadowField = nullptr;
2099 void SwPostItMgr::PrepareView(bool bIgnoreCount)
2101 if (!HasNotes() || bIgnoreCount)
2103 mpWrtShell->StartAllAction();
2104 SwRootFrame* pLayout = mpWrtShell->GetLayout();
2105 if ( pLayout )
2106 SwPostItHelper::setSidebarChanged( pLayout,
2107 mpWrtShell->getIDocumentSettingAccess().get( DocumentSettingId::BROWSE_MODE ) );
2108 mpWrtShell->EndAllAction();
2112 bool SwPostItMgr::ShowScrollbar(const tools::ULong aPage) const
2114 if (mPages.size() > aPage-1)
2115 return (mPages[aPage-1]->bScrollbar && !mbWaitingForCalcRects);
2116 else
2117 return false;
2120 bool SwPostItMgr::IsHit(const Point& aPointPixel)
2122 if (!HasNotes() || !ShowNotes())
2123 return false;
2125 const Point aPoint = mpEditWin->PixelToLogic(aPointPixel);
2126 tools::Rectangle aRect(GetSidebarRect(aPoint));
2127 if (!aRect.Contains(aPoint))
2128 return false;
2130 // we hit the note's sidebar
2131 // let's now test for the arrow area
2132 SwRect aPageFrame;
2133 const tools::ULong nPageNum
2134 = SwPostItHelper::getPageInfo(aPageFrame, mpWrtShell->GetLayout(), aPoint);
2135 if (!nPageNum)
2136 return false;
2137 if (mPages[nPageNum - 1]->bScrollbar)
2138 return ScrollbarHit(nPageNum, aPoint);
2139 return false;
2142 vcl::Window* SwPostItMgr::IsHitSidebarWindow(const Point& rPointLogic)
2144 vcl::Window* pRet = nullptr;
2146 if (HasNotes() && ShowNotes())
2148 bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
2149 if (bEnableMapMode)
2150 mpEditWin->EnableMapMode();
2152 for (const std::unique_ptr<SwAnnotationItem>& pItem : mvPostItFields)
2154 SwAnnotationWin* pPostIt = pItem->mpPostIt;
2155 if (!pPostIt)
2156 continue;
2158 if (pPostIt->IsHitWindow(rPointLogic))
2160 pRet = pPostIt;
2161 break;
2165 if (bEnableMapMode)
2166 mpEditWin->EnableMapMode(false);
2169 return pRet;
2172 tools::Rectangle SwPostItMgr::GetSidebarRect(const Point& rPointLogic)
2174 const SwRootFrame* pLayout = mpWrtShell->GetLayout();
2175 SwRect aPageFrame;
2176 const tools::ULong nPageNum = SwPostItHelper::getPageInfo(aPageFrame, pLayout, rPointLogic);
2177 if (!nPageNum)
2178 return tools::Rectangle();
2180 return GetSidebarPos(rPointLogic) == sw::sidebarwindows::SidebarPosition::LEFT
2181 ? tools::Rectangle(
2182 Point(aPageFrame.Left() - GetSidebarWidth() - GetSidebarBorderWidth(),
2183 aPageFrame.Top()),
2184 Size(GetSidebarWidth(), aPageFrame.Height()))
2185 : tools::Rectangle(
2186 Point(aPageFrame.Right() + GetSidebarBorderWidth(), aPageFrame.Top()),
2187 Size(GetSidebarWidth(), aPageFrame.Height()));
2190 bool SwPostItMgr::IsHitSidebarDragArea(const Point& rPointPx)
2192 if (!HasNotes() || !ShowNotes())
2193 return false;
2195 const Point aPointLogic = mpEditWin->PixelToLogic(rPointPx);
2196 sw::sidebarwindows::SidebarPosition eSidebarPosition = GetSidebarPos(aPointLogic);
2197 if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::NONE)
2198 return false;
2200 tools::Rectangle aDragArea(GetSidebarRect(aPointLogic));
2201 aDragArea.SetTop(aPointLogic.Y());
2202 if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
2203 aDragArea.SetPos(Point(aDragArea.Right() - 50, aDragArea.Top()));
2204 else
2205 aDragArea.SetPos(Point(aDragArea.Left() - 50, aDragArea.Top()));
2207 Size aS(aDragArea.GetSize());
2208 aS.setWidth(100);
2209 aDragArea.SetSize(aS);
2210 return aDragArea.Contains(aPointLogic);
2213 tools::Rectangle SwPostItMgr::GetBottomScrollRect(const tools::ULong aPage) const
2215 SwRect aPageRect = mPages[aPage-1]->mPageRect;
2216 Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2217 ? Point(aPageRect.Left() - GetSidebarWidth() - GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
2218 : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
2219 Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
2220 return tools::Rectangle(aPointBottom,aSize);
2223 tools::Rectangle SwPostItMgr::GetTopScrollRect(const tools::ULong aPage) const
2225 SwRect aPageRect = mPages[aPage-1]->mPageRect;
2226 Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2227 ? Point(aPageRect.Left() - GetSidebarWidth() -GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height())
2228 : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height());
2229 Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
2230 return tools::Rectangle(aPointTop,aSize);
2233 //IMPORTANT: if you change the rects here, also change SwPageFrame::PaintNotesSidebar()
2234 bool SwPostItMgr::ScrollbarHit(const tools::ULong aPage,const Point &aPoint)
2236 SwRect aPageRect = mPages[aPage-1]->mPageRect;
2237 Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2238 ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
2239 : Point(aPageRect.Right() + GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
2241 Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2242 ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height())
2243 : Point(aPageRect.Right()+GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height());
2245 tools::Rectangle aRectBottom(GetBottomScrollRect(aPage));
2246 tools::Rectangle aRectTop(GetTopScrollRect(aPage));
2248 if (aRectBottom.Contains(aPoint))
2250 if (aPoint.X() < tools::Long((aPointBottom.X() + GetSidebarWidth()/3)))
2251 Scroll( GetScrollSize(),aPage);
2252 else
2253 Scroll( -1*GetScrollSize(), aPage);
2254 return true;
2256 else if (aRectTop.Contains(aPoint))
2258 if (aPoint.X() < tools::Long((aPointTop.X() + GetSidebarWidth()/3*2)))
2259 Scroll(GetScrollSize(), aPage);
2260 else
2261 Scroll(-1*GetScrollSize(), aPage);
2262 return true;
2264 return false;
2267 void SwPostItMgr::CorrectPositions()
2269 if ( mbWaitingForCalcRects || mbLayouting || mvPostItFields.empty() )
2270 return;
2272 // find first valid note
2273 SwAnnotationWin *pFirstPostIt = nullptr;
2274 for (auto const& postItField : mvPostItFields)
2276 pFirstPostIt = postItField->mpPostIt;
2277 if (pFirstPostIt)
2278 break;
2281 //if we have not found a valid note, forget about it and leave
2282 if (!pFirstPostIt)
2283 return;
2285 // yeah, I know, if this is a left page it could be wrong, but finding the page and the note is probably not even faster than just doing it
2286 // check, if anchor overlay object exists.
2287 const tools::Long aAnchorX = pFirstPostIt->Anchor()
2288 ? mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(pFirstPostIt->Anchor()->GetSixthPosition().getX()),0)).X()
2289 : 0;
2290 const tools::Long aAnchorY = pFirstPostIt->Anchor()
2291 ? mpEditWin->LogicToPixel( Point(0,static_cast<tools::Long>(pFirstPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1
2292 : 0;
2293 if (Point(aAnchorX,aAnchorY) == pFirstPostIt->GetPosPixel())
2294 return;
2296 tools::Long aAnchorPosX = 0;
2297 tools::Long aAnchorPosY = 0;
2298 for (const std::unique_ptr<SwPostItPageItem>& pPage : mPages)
2300 for (auto const& item : pPage->mvSidebarItems)
2302 // check, if anchor overlay object exists.
2303 if ( item->mbShow && item->mpPostIt && item->mpPostIt->Anchor() )
2305 aAnchorPosX = pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2306 ? mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSeventhPosition().getX()),0)).X()
2307 : mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSixthPosition().getX()),0)).X();
2308 aAnchorPosY = mpEditWin->LogicToPixel( Point(0,static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1;
2309 item->mpPostIt->SetPosPixel(Point(aAnchorPosX,aAnchorPosY));
2315 bool SwPostItMgr::ShowNotes() const
2317 // we only want to see notes if Options - Writer - View - Notes is ticked
2318 return mpWrtShell->GetViewOptions()->IsPostIts();
2321 bool SwPostItMgr::HasNotes() const
2323 return !mvPostItFields.empty();
2326 void SwPostItMgr::SetSidebarWidth(const Point& rPointLogic)
2328 tools::Rectangle nSidebarRect = GetSidebarRect(rPointLogic);
2329 if (nSidebarRect.IsEmpty())
2330 return;
2332 sw::sidebarwindows::SidebarPosition eSidebarPosition = GetSidebarPos(rPointLogic);
2333 if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::NONE)
2334 return;
2336 // Calculate the width to be applied in logic units
2337 tools::Long nLogicWidth;
2338 if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
2339 nLogicWidth = rPointLogic.X() - nSidebarRect.Left();
2340 else
2341 nLogicWidth = nSidebarRect.Right() - rPointLogic.X();
2343 // The zoom level is conveniently used as reference to define the minimum width
2344 const sal_uInt16 nZoom = mpWrtShell->GetViewOptions()->GetZoom();
2345 double nFactor = static_cast<double>(mpEditWin->LogicToPixel(Point(nLogicWidth, 0)).X())
2346 / static_cast<double>(nZoom);
2347 // The width may vary from 1x to 8x the zoom factor
2348 nFactor = std::clamp(nFactor, 1.0, 8.0);
2349 std::shared_ptr<comphelper::ConfigurationChanges> xChanges(
2350 comphelper::ConfigurationChanges::create());
2351 officecfg::Office::Writer::Notes::DisplayWidthFactor::set(nFactor, xChanges);
2352 xChanges->commit();
2354 // tdf#159146 After resizing the sidebar the layout and the ruler needs to be updated
2355 mpWrtShell->InvalidateLayout(true);
2356 mpView->GetHRuler().Invalidate();
2357 mpView->InvalidateRulerPos();
2359 LayoutPostIts();
2362 tools::ULong SwPostItMgr::GetSidebarWidth(bool bPx) const
2364 bool bEnableMapMode = !mpWrtShell->GetOut()->IsMapModeEnabled();
2365 sal_uInt16 nZoom = mpWrtShell->GetViewOptions()->GetZoom();
2366 if (comphelper::LibreOfficeKit::isActive() && !bEnableMapMode)
2368 // The output device is the tile and contains the real wanted scale factor.
2369 double fScaleX = double(mpWrtShell->GetOut()->GetMapMode().GetScaleX());
2370 nZoom = fScaleX * 100;
2372 tools::ULong aWidth = static_cast<tools::ULong>(
2373 nZoom * officecfg::Office::Writer::Notes::DisplayWidthFactor::get());
2375 if (bPx)
2376 return aWidth;
2377 else
2379 if (bEnableMapMode)
2380 // The output device is the window.
2381 mpWrtShell->GetOut()->EnableMapMode();
2382 tools::Long nRet = mpWrtShell->GetOut()->PixelToLogic(Size(aWidth, 0)).Width();
2383 if (bEnableMapMode)
2384 mpWrtShell->GetOut()->EnableMapMode(false);
2385 return nRet;
2389 tools::ULong SwPostItMgr::GetSidebarBorderWidth(bool bPx) const
2391 if (bPx)
2392 return 2;
2393 else
2394 return mpWrtShell->GetOut()->PixelToLogic(Size(2,0)).Width();
2397 Color SwPostItMgr::GetColorDark(std::size_t aAuthorIndex)
2399 Color aColor = GetColorAnchor(aAuthorIndex);
2400 svtools::ColorConfig aColorConfig;
2401 const Color aBgColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
2402 if (aBgColor.IsDark())
2403 aColor.DecreaseLuminance(80);
2404 else
2405 aColor.IncreaseLuminance(150);
2406 return aColor;
2409 Color SwPostItMgr::GetColorLight(std::size_t aAuthorIndex)
2411 Color aColor = GetColorAnchor(aAuthorIndex);
2412 svtools::ColorConfig aColorConfig;
2413 const Color aBgColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
2414 if (aBgColor.IsDark())
2415 aColor.DecreaseLuminance(130);
2416 else
2417 aColor.IncreaseLuminance(200);
2418 return aColor;
2421 Color SwPostItMgr::GetColorAnchor(std::size_t aAuthorIndex)
2423 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
2425 svtools::ColorConfig aColorConfig;
2426 switch (aAuthorIndex % 9)
2428 case 0: return aColorConfig.GetColorValue(svtools::AUTHOR1).nColor;
2429 case 1: return aColorConfig.GetColorValue(svtools::AUTHOR2).nColor;
2430 case 2: return aColorConfig.GetColorValue(svtools::AUTHOR3).nColor;
2431 case 3: return aColorConfig.GetColorValue(svtools::AUTHOR4).nColor;
2432 case 4: return aColorConfig.GetColorValue(svtools::AUTHOR5).nColor;
2433 case 5: return aColorConfig.GetColorValue(svtools::AUTHOR6).nColor;
2434 case 6: return aColorConfig.GetColorValue(svtools::AUTHOR7).nColor;
2435 case 7: return aColorConfig.GetColorValue(svtools::AUTHOR8).nColor;
2436 case 8: return aColorConfig.GetColorValue(svtools::AUTHOR9).nColor;
2440 return COL_WHITE;
2443 void SwPostItMgr::SetActiveSidebarWin( SwAnnotationWin* p)
2445 if ( p == mpActivePostIt )
2446 return;
2448 // we need the temp variable so we can set mpActivePostIt before we call DeactivatePostIt
2449 // therefore we get a new layout in DOCCHANGED when switching from postit to document,
2450 // otherwise, GetActivePostIt() would still hold our old postit
2451 SwAnnotationWin* pActive = mpActivePostIt;
2452 mpActivePostIt = p;
2453 if (pActive)
2455 pActive->DeactivatePostIt();
2456 mShadowState.mpShadowField = nullptr;
2458 if (mpActivePostIt)
2460 mpActivePostIt->GotoPos();
2461 mpView->AttrChangedNotify(nullptr);
2462 mpActivePostIt->ActivatePostIt();
2466 IMPL_LINK_NOARG( SwPostItMgr, CalcHdl, void*, void )
2468 mnEventId = nullptr;
2469 if ( mbLayouting )
2471 OSL_FAIL("Reentrance problem in Layout Manager!");
2472 mbWaitingForCalcRects = false;
2473 return;
2476 // do not change order, even if it would seem so in the first place, we need the calcrects always
2477 if (CalcRects() || mbLayout)
2479 mbLayout = false;
2480 LayoutPostIts();
2484 void SwPostItMgr::Rescale()
2486 for (auto const& postItField : mvPostItFields)
2487 if ( postItField->mpPostIt )
2488 postItField->mpPostIt->Rescale();
2491 sal_Int32 SwPostItMgr::GetInitialAnchorDistance() const
2493 const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2494 return sal_Int32(POSTIT_INITIAL_ANCHOR_DISTANCE * f);
2497 sal_Int32 SwPostItMgr::GetSpaceBetween() const
2499 const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2500 return sal_Int32(POSTIT_SPACE_BETWEEN * f);
2503 sal_Int32 SwPostItMgr::GetScrollSize() const
2505 const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2506 return sal_Int32((POSTIT_SPACE_BETWEEN + POSTIT_MINIMUMSIZE_WITH_META) * f);
2509 sal_Int32 SwPostItMgr::GetMinimumSizeWithMeta() const
2511 const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2512 return sal_Int32(POSTIT_MINIMUMSIZE_WITH_META * f);
2515 sal_Int32 SwPostItMgr::GetSidebarScrollerHeight() const
2517 const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2518 return sal_Int32(POSTIT_SCROLL_SIDEBAR_HEIGHT * f);
2521 void SwPostItMgr::SetSpellChecking()
2523 for (auto const& postItField : mvPostItFields)
2524 if ( postItField->mpPostIt )
2525 postItField->mpPostIt->SetSpellChecking();
2528 void SwPostItMgr::SetReadOnlyState()
2530 for (auto const& postItField : mvPostItFields)
2531 if ( postItField->mpPostIt )
2532 postItField->mpPostIt->SetReadonly( mbReadOnly );
2535 void SwPostItMgr::CheckMetaText()
2537 for (auto const& postItField : mvPostItFields)
2538 if ( postItField->mpPostIt )
2539 postItField->mpPostIt->CheckMetaText();
2542 void SwPostItMgr::UpdateColors()
2544 for (auto const& postItField : mvPostItFields)
2545 if ( postItField->mpPostIt )
2547 postItField->mpPostIt->UpdateColors();
2548 postItField->mpPostIt->Invalidate();
2552 sal_uInt16 SwPostItMgr::Replace(SvxSearchItem const * pItem)
2554 SwAnnotationWin* pWin = GetActiveSidebarWin();
2555 sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( *pItem );
2556 if (!aResult)
2557 SetActiveSidebarWin(nullptr);
2558 return aResult;
2561 sal_uInt16 SwPostItMgr::FinishSearchReplace(const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward)
2563 SwAnnotationWin* pWin = GetActiveSidebarWin();
2564 SvxSearchItem aItem(SID_SEARCH_ITEM );
2565 aItem.SetSearchOptions(rSearchOptions);
2566 aItem.SetBackward(!bSrchForward);
2567 sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem );
2568 if (!aResult)
2569 SetActiveSidebarWin(nullptr);
2570 return aResult;
2573 sal_uInt16 SwPostItMgr::SearchReplace(const SwFormatField &pField, const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward)
2575 sal_uInt16 aResult = 0;
2576 SwAnnotationWin* pWin = GetSidebarWin(&pField);
2577 if (pWin)
2579 ESelection aOldSelection = pWin->GetOutlinerView()->GetSelection();
2580 if (bSrchForward)
2581 pWin->GetOutlinerView()->SetSelection(ESelection(0, 0));
2582 else
2583 pWin->GetOutlinerView()->SetSelection(ESelection::AtEnd());
2584 SvxSearchItem aItem(SID_SEARCH_ITEM );
2585 aItem.SetSearchOptions(rSearchOptions);
2586 aItem.SetBackward(!bSrchForward);
2587 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem );
2588 if (!aResult)
2589 pWin->GetOutlinerView()->SetSelection(aOldSelection);
2590 else
2592 SetActiveSidebarWin(pWin);
2593 MakeVisible(pWin);
2596 return aResult;
2599 void SwPostItMgr::AssureStdModeAtShell()
2601 mpWrtShell->AssureStdMode();
2604 bool SwPostItMgr::HasActiveSidebarWin() const
2606 return mpActivePostIt != nullptr;
2609 bool SwPostItMgr::HasActiveAnnotationWin() const
2611 return HasActiveSidebarWin() &&
2612 mpActivePostIt != nullptr;
2615 void SwPostItMgr::GrabFocusOnActiveSidebarWin()
2617 if ( HasActiveSidebarWin() )
2619 mpActivePostIt->GrabFocus();
2623 void SwPostItMgr::UpdateDataOnActiveSidebarWin()
2625 if ( HasActiveSidebarWin() )
2627 mpActivePostIt->UpdateData();
2631 void SwPostItMgr::DeleteActiveSidebarWin()
2633 if ( HasActiveSidebarWin() )
2635 mpActivePostIt->Delete();
2639 void SwPostItMgr::HideActiveSidebarWin()
2641 if ( HasActiveSidebarWin() )
2643 mpActivePostIt->Hide();
2647 void SwPostItMgr::ToggleInsModeOnActiveSidebarWin()
2649 if ( HasActiveSidebarWin() )
2651 mpActivePostIt->ToggleInsMode();
2655 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2656 void SwPostItMgr::ConnectSidebarWinToFrame( const SwFrame& rFrame,
2657 const SwFormatField& rFormatField,
2658 SwAnnotationWin& rSidebarWin )
2660 if ( mpFrameSidebarWinContainer == nullptr )
2662 mpFrameSidebarWinContainer.reset(new SwFrameSidebarWinContainer());
2665 const bool bInserted = mpFrameSidebarWinContainer->insert( rFrame, rFormatField, rSidebarWin );
2666 if ( bInserted &&
2667 mpWrtShell->GetAccessibleMap() )
2669 mpWrtShell->GetAccessibleMap()->InvalidatePosOrSize( nullptr, nullptr, &rSidebarWin, SwRect() );
2673 void SwPostItMgr::DisconnectSidebarWinFromFrame( const SwFrame& rFrame,
2674 SwAnnotationWin& rSidebarWin )
2676 if ( mpFrameSidebarWinContainer != nullptr )
2678 const bool bRemoved = mpFrameSidebarWinContainer->remove( rFrame, rSidebarWin );
2679 if ( bRemoved &&
2680 mpWrtShell->GetAccessibleMap() )
2682 mpWrtShell->GetAccessibleMap()->A11yDispose( nullptr, nullptr, &rSidebarWin );
2686 #endif // ENABLE_WASM_STRIP_ACCESSIBILITY
2688 bool SwPostItMgr::HasFrameConnectedSidebarWins( const SwFrame& rFrame )
2690 bool bRet( false );
2692 if ( mpFrameSidebarWinContainer != nullptr )
2694 bRet = !mpFrameSidebarWinContainer->empty( rFrame );
2697 return bRet;
2700 vcl::Window* SwPostItMgr::GetSidebarWinForFrameByIndex( const SwFrame& rFrame,
2701 const sal_Int32 nIndex )
2703 vcl::Window* pSidebarWin( nullptr );
2705 if ( mpFrameSidebarWinContainer != nullptr )
2707 pSidebarWin = mpFrameSidebarWinContainer->get( rFrame, nIndex );
2710 return pSidebarWin;
2713 void SwPostItMgr::GetAllSidebarWinForFrame( const SwFrame& rFrame,
2714 std::vector< vcl::Window* >* pChildren )
2716 if ( mpFrameSidebarWinContainer != nullptr )
2718 mpFrameSidebarWinContainer->getAll( rFrame, pChildren );
2722 void SwPostItMgr::ShowHideResolvedNotes(bool visible) {
2723 for (auto const& pPage : mPages)
2725 for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
2727 if ((*b)->mpPostIt->IsResolved())
2729 (*b)->mpPostIt->SetResolved(true);
2730 (*b)->mpPostIt->GetSidebarItem().mbShow = visible;
2734 LayoutPostIts();
2737 void SwPostItMgr::UpdateResolvedStatus(const sw::annotation::SwAnnotationWin* topNote) {
2738 // Given the topmost note as an argument, scans over all notes and sets the
2739 // 'resolved' state of each descendant of the top notes to the resolved state
2740 // of the top note.
2741 bool resolved = topNote->IsResolved();
2742 for (auto const& pPage : mPages)
2744 for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
2746 if((*b)->mpPostIt->GetTopReplyNote() == topNote) {
2747 (*b)->mpPostIt->SetResolved(resolved);
2753 sw::sidebarwindows::SidebarPosition SwPostItMgr::GetSidebarPos(const Point& rPointLogic)
2755 if (const SwRootFrame* pLayout = mpWrtShell->GetLayout())
2757 const SwPageFrame* pPageFrame = pLayout->GetPageAtPos(rPointLogic, nullptr, true);
2758 if (pPageFrame)
2759 return pPageFrame->SidebarPosition();
2761 return sw::sidebarwindows::SidebarPosition::NONE;
2764 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */